View Javadoc

1   /*
2    * SymmetricDS is an open source database synchronization solution.
3    *   
4    * Copyright (C) Eric Long <erilong@users.sourceforge.net>,
5    *               Chris Henson <chenson42@users.sourceforge.net>
6    *
7    * This library is free software; you can redistribute it and/or
8    * modify it under the terms of the GNU Lesser General Public
9    * License as published by the Free Software Foundation; either
10   * version 3 of the License, or (at your option) any later version.
11   *
12   * This library is distributed in the hope that it will be useful,
13   * but WITHOUT ANY WARRANTY; without even the implied warranty of
14   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15   * Lesser General Public License for more details.
16   *
17   * You should have received a copy of the GNU Lesser General Public
18   * License along with this library; if not, see
19   * <http://www.gnu.org/licenses/>.
20   */
21  
22  package org.jumpmind.symmetric.service.impl;
23  
24  import java.io.IOException;
25  import java.io.OutputStream;
26  import java.net.ConnectException;
27  import java.sql.Types;
28  
29  import org.apache.commons.lang.time.DateUtils;
30  import org.apache.commons.logging.Log;
31  import org.apache.commons.logging.LogFactory;
32  import org.jumpmind.symmetric.common.ParameterConstants;
33  import org.jumpmind.symmetric.db.IDbDialect;
34  import org.jumpmind.symmetric.model.Node;
35  import org.jumpmind.symmetric.model.NodeSecurity;
36  import org.jumpmind.symmetric.service.IClusterService;
37  import org.jumpmind.symmetric.service.IConfigurationService;
38  import org.jumpmind.symmetric.service.IDataExtractorService;
39  import org.jumpmind.symmetric.service.IDataLoaderService;
40  import org.jumpmind.symmetric.service.IDataService;
41  import org.jumpmind.symmetric.service.INodeService;
42  import org.jumpmind.symmetric.service.IRegistrationService;
43  import org.jumpmind.symmetric.transport.ITransportManager;
44  import org.jumpmind.symmetric.util.RandomTimeSlot;
45  
46  // TODO: NodeService already does all this DML. Should use NodeService or move
47  // methods to there.
48  public class RegistrationService extends AbstractService implements IRegistrationService {
49  
50      protected static final Log logger = LogFactory.getLog(RegistrationService.class);
51  
52      private INodeService nodeService;
53  
54      private IDataExtractorService dataExtractorService;
55  
56      private IConfigurationService configurationService;
57  
58      private IClusterService clusterService;
59  
60      private IDataService dataService;
61  
62      private IDataLoaderService dataLoaderService;
63  
64      private ITransportManager transportManager;
65  
66      private IDbDialect dbDialect;
67  
68      /***
69       * Register a node for the given domain name and domain ID if the
70       * registration is open.
71       * @param isRequestedRegistration An indicator that registration has been requested by the remote client
72       */
73      public boolean registerNode(Node node, OutputStream out, boolean isRequestedRegistration) throws IOException {
74          if (!configurationService.isRegistrationServer()) {
75              // registration is not allowed until this node has an initial load
76              NodeSecurity security = nodeService.findNodeSecurity(nodeService.findIdentity().getNodeId());
77              if (security == null || security.getInitialLoadTime() == null) {
78                  logger.warn("Registration is not allowed until this node has an initial load");
79                  return false;
80              }
81          }
82          String nodeId = findNodeToRegister(node.getNodeGroupId(), node.getExternalId());
83          if (nodeId == null && parameterService.is(ParameterConstants.AUTO_REGISTER_ENABLED)) {
84              Node existingNode = nodeService.findNodeByExternalId(node.getNodeGroupId(), node.getExternalId());
85              if (existingNode != null) {
86                  nodeId = existingNode.getNodeId();
87              } else {
88                  openRegistration(node.getNodeGroupId(), node.getExternalId());
89                  nodeId = findNodeToRegister(node.getNodeGroupId(), node.getExternalId());
90              }
91          }
92          if (nodeId == null) {
93              return false;
94          }
95          node.setNodeId(nodeId);
96          jdbcTemplate.update(getSql("registerNodeSecuritySql"), new Object[] { node.getNodeId() });
97          jdbcTemplate.update(getSql("registerNodeSql"), new Object[] { node.getSyncURL(), node.getSchemaVersion(),
98                  node.getDatabaseType(), node.getDatabaseVersion(), node.getSymmetricVersion(), node.getNodeId() },
99                  new int[] { Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR });
100         boolean success = writeConfiguration(node, out);
101         if (success && parameterService.is(ParameterConstants.AUTO_RELOAD_ENABLED)) {
102             // only send automatic initial load once or if the client is really re-registering
103             NodeSecurity security = nodeService.findNodeSecurity(node.getNodeId());
104             if ((security != null && security.getInitialLoadTime() == null) || isRequestedRegistration) {
105                 dataService.reloadNode(node.getNodeId());
106             }
107         }
108         return success;
109     }
110 
111     private String findNodeToRegister(String nodeGroupId, String externald) {
112         return (String) jdbcTemplate.queryForObject(getSql("findNodeToRegisterSql"), new Object[] { nodeGroupId,
113                 externald }, String.class);
114     }
115 
116     private void sleepBeforeRegistrationRetry() {
117         try {
118             RandomTimeSlot randomSleepTimeSlot = new RandomTimeSlot(parameterService
119                     .getString(ParameterConstants.EXTERNAL_ID), 60);
120             long sleepTimeInMs = DateUtils.MILLIS_PER_SECOND * randomSleepTimeSlot.getRandomValueSeededByDomainId();
121             logger.warn("Could not register.  Sleeping for " + sleepTimeInMs + "ms before attempting again.");
122             Thread.sleep(sleepTimeInMs);
123         } catch (InterruptedException e) {
124         }
125     }
126 
127     public boolean isRegisteredWithServer() {
128         return nodeService.findIdentity() != null;
129     }
130 
131     public void registerWithServer() {
132         boolean registered = isRegisteredWithServer();
133         // If we cannot contact the server to register, we simply must wait and
134         // try again.
135         while (!registered) {
136             try {
137                 logger.info("Attempting to register with " + parameterService.getRegistrationUrl());
138                 registered = dataLoaderService.loadData(transportManager.getRegisterTransport(new Node(
139                         this.parameterService, dbDialect)));
140             } catch (ConnectException e) {
141                 logger.warn("Connection failed while registering.");
142             } catch (Exception e) {
143                 logger.error(e, e);
144             }
145 
146             if (!registered) {
147                 sleepBeforeRegistrationRetry();
148             } else {
149                 Node node = nodeService.findIdentity();
150                 if (node != null) {
151                     logger.info("Successfully registered node [id=" + node.getNodeId() + "]");
152                 } else {
153                     logger.error("Node registration is unavailable");
154                 }
155             }
156         }
157     }
158 
159     /***
160      * Synchronize node configuration.
161      */
162     @Deprecated
163     protected boolean writeConfiguration(Node node, OutputStream out) throws IOException {
164         dataExtractorService.extractConfigurationStandalone(node, out);
165         return true;
166     }
167 
168     /***
169      * Re-open registration for a single node that already exists in the
170      * database. A new password is generated and the registration_enabled flag
171      * is turned on. The next node to try registering for this node group and
172      * external ID will be given this information.
173      */
174     public void reOpenRegistration(String nodeId) {
175         String password = nodeService.generatePassword();
176         Node node = nodeService.findNode(nodeId);
177         if (node != null) {
178             int updateCount = jdbcTemplate.update(getSql("reopenRegistrationSql"), new Object[] { password, nodeId });
179             if (updateCount == 0) {
180                 // if the update count was 0, then we probably have a row in the
181                 // node table, but not in node security.
182                 // lets go ahead and try to insert into node security.
183                 jdbcTemplate.update(getSql("openRegistrationNodeSecuritySql"), new Object[] { nodeId, password });
184             } 
185         } else {
186             logger.warn("There was no row with a node id of " + nodeId + " to 'reopen' registration for.");
187         }
188     }
189 
190     /***
191      * Open registration for a single new node given a node group (f.e.,
192      * "STORE") and external ID (f.e., "00001"). The unique node ID and password
193      * are generated and stored in the node and node_security tables with the
194      * registration_enabled flag turned on. The next node to try registering for
195      * this node group and external ID will be given this information.
196      */
197     public void openRegistration(String nodeGroup, String externalId) {
198         Node me = nodeService.findIdentity();
199         String nodeId = nodeService.generateNodeId(nodeGroup, externalId);
200         String password = nodeService.generatePassword();
201         jdbcTemplate.update(getSql("openRegistrationNodeSql"), new Object[] { nodeId, nodeGroup, externalId, me.getNodeId() });
202         jdbcTemplate.update(getSql("openRegistrationNodeSecuritySql"), new Object[] { nodeId, password, me.getNodeId() });
203         clusterService.initLockTableForNode(nodeService.findNode(nodeId));
204         logger.info("Just opened registration for external id of " + externalId + " and a node group of " + nodeGroup);
205     }
206 
207     public void setNodeService(INodeService nodeService) {
208         this.nodeService = nodeService;
209     }
210 
211     public void setDataExtractorService(IDataExtractorService dataExtractorService) {
212         this.dataExtractorService = dataExtractorService;
213     }
214 
215     public void setConfigurationService(IConfigurationService configurationService) {
216         this.configurationService = configurationService;
217     }
218 
219     public void setClusterService(IClusterService clusterService) {
220         this.clusterService = clusterService;
221     }
222 
223     public void setDataService(IDataService dataService) {
224         this.dataService = dataService;
225     }
226 
227     public boolean isAutoRegistration() {
228         return parameterService.is(ParameterConstants.AUTO_REGISTER_ENABLED);
229     }
230 
231     public void setDataLoaderService(IDataLoaderService dataLoaderService) {
232         this.dataLoaderService = dataLoaderService;
233     }
234 
235     public void setTransportManager(ITransportManager transportManager) {
236         this.transportManager = transportManager;
237     }
238 
239     public void setDbDialect(IDbDialect dbDialect) {
240         this.dbDialect = dbDialect;
241     }
242 
243 }