org.jumpmind.symmetric.service.impl.NodeService Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of symmetric-ds Show documentation
Show all versions of symmetric-ds Show documentation
SymmetricDS is an open source database synchronization solution. It is platform-independent,
web-enabled, and database-agnostic. SymmetricDS was first built to replicate changes between 'retail store'
databases and ad centralized 'corporate' database.
The newest version!
/*
* Licensed to JumpMind Inc under one or more contributor
* license agreements. See the NOTICE file distributed
* with this work for additional information regarding
* copyright ownership. JumpMind Inc licenses this file
* to you under the GNU Lesser General Public License (the
* "License"); you may not use this file except in compliance
* with the License.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see
* .
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jumpmind.symmetric.service.impl;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.lang.StringUtils;
import org.jumpmind.symmetric.common.ParameterConstants;
import org.jumpmind.symmetric.config.INodeIdGenerator;
import org.jumpmind.symmetric.ext.IOfflineServerListener;
import org.jumpmind.symmetric.model.Node;
import org.jumpmind.symmetric.model.NodeGroupLinkAction;
import org.jumpmind.symmetric.model.NodeHost;
import org.jumpmind.symmetric.model.NodeSecurity;
import org.jumpmind.symmetric.model.NodeStatus;
import org.jumpmind.symmetric.security.INodePasswordFilter;
import org.jumpmind.symmetric.service.INodeService;
import org.jumpmind.symmetric.util.AppUtils;
import org.springframework.dao.CannotAcquireLockException;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.jdbc.core.ResultSetExtractor;
import org.springframework.jdbc.core.RowMapper;
public class NodeService extends AbstractService implements INodeService {
private Node cachedNodeIdentity;
private Map securityCache;
private long securityCacheTime;
private INodeIdGenerator nodeIdGenerator;
private INodePasswordFilter nodePasswordFilter;
private NodeHost nodeHostForCurrentNode = null;
private long offlineNodeDetectionMinutes;
private List offlineServerListeners;
public String findSymmetricVersion() {
try {
return (String) jdbcTemplate.queryForObject(getSql("findSymmetricVersionSql"), String.class);
} catch (EmptyResultDataAccessException ex) {
return null;
}
}
public String findIdentityNodeId() {
Node node = findIdentity();
return node != null ? node.getNodeId() : null;
}
public Collection findEnabledNodesFromNodeGroup(String nodeGroupId) {
return jdbcTemplate.query(getSql("selectNodePrefixSql","findEnabledNodesFromNodeGroupSql"), new Object[] { nodeGroupId }, new NodeRowMapper());
}
public Set findNodesThatOriginatedFromNodeId(String originalNodeId) {
Set all = new HashSet();
List list = jdbcTemplate.query(getSql("selectNodePrefixSql","findNodesCreatedByMeSql"), new Object[] { originalNodeId }, new NodeRowMapper());
if (list.size() > 0) {
all.addAll(list);
for (Node node : list) {
all.addAll(findNodesThatOriginatedFromNodeId(node.getNodeId()));
}
}
return all;
}
/**
* Lookup a node in the database, which contains information for syncing
* with it.
*/
public Node findNode(String id) {
List list = jdbcTemplate.query(getSql("selectNodePrefixSql","findNodeSql"),
new Object[] { id }, new NodeRowMapper());
return (Node) getFirstEntry(list);
}
public Node findNodeByExternalId(String nodeGroupId, String externalId) {
List list = jdbcTemplate.query(getSql("selectNodePrefixSql","findNodeByExternalIdSql"),
new Object[] { nodeGroupId, externalId }, new NodeRowMapper());
return (Node) getFirstEntry(list);
}
public void ignoreNodeChannelForExternalId(boolean enabled, String channelId, String nodeGroupId, String externalId) {
Node node = findNodeByExternalId(nodeGroupId, externalId);
if (jdbcTemplate.update(getSql("nodeChannelControlIgnoreSql"), new Object[] { enabled ? 1 : 0,
node.getNodeId(), channelId }) == 0) {
jdbcTemplate.update(getSql("insertNodeChannelControlSql"), new Object[] { node.getNodeId(), channelId,
enabled ? 1 : 0, 0 });
}
}
public boolean isRegistrationEnabled(String nodeId) {
NodeSecurity nodeSecurity = findNodeSecurity(nodeId);
if (nodeSecurity != null) {
return nodeSecurity.isRegistrationEnabled();
}
return false;
}
/**
* Lookup a node_security in the database, which contains private
* information used to authenticate.
*/
public NodeSecurity findNodeSecurity(String id) {
return findNodeSecurity(id, false);
}
public void updateNodeHostForCurrentNode() {
if (nodeHostForCurrentNode == null) {
nodeHostForCurrentNode = new NodeHost(findIdentityNodeId());
}
nodeHostForCurrentNode.refresh();
Object[] params = new Object[] {
nodeHostForCurrentNode.getIpAddress(),
nodeHostForCurrentNode.getOsUser(),
nodeHostForCurrentNode.getOsName(),
nodeHostForCurrentNode.getOsArch(),
nodeHostForCurrentNode.getOsVersion(),
nodeHostForCurrentNode.getAvailableProcessors(),
nodeHostForCurrentNode.getFreeMemoryBytes(),
nodeHostForCurrentNode.getTotalMemoryBytes(),
nodeHostForCurrentNode.getMaxMemoryBytes(),
nodeHostForCurrentNode.getJavaVersion(),
nodeHostForCurrentNode.getJavaVendor(),
nodeHostForCurrentNode.getSymmetricVersion(),
nodeHostForCurrentNode.getTimezoneOffset(),
nodeHostForCurrentNode.getHeartbeatTime(),
nodeHostForCurrentNode.getLastRestartTime(),
nodeHostForCurrentNode.getNodeId(),
nodeHostForCurrentNode.getHostName()
};
if (jdbcTemplate.update(getSql("updateNodeHostSql"), params) == 0) {
jdbcTemplate.update(getSql("insertNodeHostSql"), params);
}
}
public NodeSecurity findNodeSecurity(String nodeId, boolean createIfNotFound) {
try {
if (nodeId != null) {
List list = jdbcTemplate.query(getSql("findNodeSecuritySql"), new Object[] { nodeId },
new int[] { Types.VARCHAR }, new NodeSecurityRowMapper());
NodeSecurity security = (NodeSecurity) getFirstEntry(list);
if (security == null && createIfNotFound) {
insertNodeSecurity(nodeId);
security = findNodeSecurity(nodeId, false);
}
return security;
} else {
log.debug("FindNodeSecurityNodeNull");
return null;
}
} catch (DataIntegrityViolationException ex) {
log.error("NodeSecurityMissing", nodeId);
throw ex;
}
}
public void deleteNodeSecurity(String nodeId) {
jdbcTemplate.update(getSql("deleteNodeSecuritySql"), new Object[] { nodeId });
}
public void insertNodeSecurity(String id) {
flushNodeAuthorizedCache();
String password = nodeIdGenerator.generatePassword(this, new Node(id, null, null));
password = filterPasswordOnSaveIfNeeded(password);
jdbcTemplate.update(getSql("insertNodeSecuritySql"), new Object[] { id, password, findIdentity().getNodeId() });
}
public void insertNodeIdentity(String nodeId) {
jdbcTemplate.update(getSql("insertNodeIdentitySql"), nodeId);
}
public void deleteIdentity() {
jdbcTemplate.execute(getSql("deleteNodeIdentitySql"));
cachedNodeIdentity = null;
}
public void insertNode(String nodeId, String nodeGroupdId, String externalId, String createdAtNodeId) {
jdbcTemplate.update(getSql("insertNodeSql"), new Object[] { nodeId, nodeGroupdId,
externalId, createdAtNodeId, AppUtils.getTimezoneOffset() });
}
public void insertNodeGroup(String groupId, String description) {
if (jdbcTemplate.queryForInt(getSql("doesNodeGroupExistSql"), groupId) == 0) {
jdbcTemplate.update(getSql("insertNodeGroupSql"), description, groupId);
}
}
public boolean updateNode(Node node) {
boolean updated = jdbcTemplate.update(getSql("updateNodeSql"), new Object[] { node.getNodeGroupId(),
node.getExternalId(), node.getDatabaseType(), node.getDatabaseVersion(), node.getSchemaVersion(),
node.getSymmetricVersion(), node.getSyncUrl(), node.getHeartbeatTime(), node.isSyncEnabled() ? 1 : 0,
node.getTimezoneOffset(), node.getBatchToSendCount(), node.getBatchInErrorCount(), node.getCreatedAtNodeId(), node.getNodeId() }, new int[] { Types.VARCHAR,
Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR,
Types.TIMESTAMP, Types.INTEGER, Types.VARCHAR, Types.INTEGER, Types.INTEGER, Types.VARCHAR, Types.VARCHAR }) == 1;
return updated;
}
protected T getFirstEntry(List list) {
if (list != null && list.size() > 0) {
return list.get(0);
}
return null;
}
public Map findAllNodeSecurity(boolean useCache) {
long maxSecurityCacheTime = parameterService
.getLong(ParameterConstants.CACHE_TIMEOUT_NODE_SECURITY_IN_MS);
Map all = securityCache;
if (all == null || System.currentTimeMillis() - securityCacheTime >= maxSecurityCacheTime
|| securityCacheTime == 0 || !useCache) {
all = (Map) jdbcTemplate.query(
getSql("findAllNodeSecuritySql"), new NodeSecurityResultSetExtractor());
securityCache = all;
securityCacheTime = System.currentTimeMillis();
}
return all;
}
/**
* Check that the given node and password match in the node_security table.
* A node must authenticate before it's allowed to sync data.
*/
public boolean isNodeAuthorized(String nodeId, String password) {
Map nodeSecurities = findAllNodeSecurity(true);
NodeSecurity nodeSecurity = nodeSecurities.get(nodeId);
if (nodeSecurity != null
&& ((nodeSecurity.getNodePassword() != null && !nodeSecurity.getNodePassword().equals("") && nodeSecurity
.getNodePassword().equals(password)) || nodeSecurity.isRegistrationEnabled())) {
return true;
}
return false;
}
public void flushNodeAuthorizedCache() {
securityCacheTime = 0;
}
public Node findIdentity() {
return findIdentity(true);
}
public Node findIdentity(boolean useCache) {
if (cachedNodeIdentity == null || useCache == false) {
List list = jdbcTemplate.query(getSql("selectNodePrefixSql","findNodeIdentitySql"),
new NodeRowMapper());
cachedNodeIdentity = (Node) getFirstEntry(list);
}
return cachedNodeIdentity;
}
public List findNodesToPull() {
return findSourceNodesFor(NodeGroupLinkAction.W);
}
public List findNodesToPushTo() {
return findTargetNodesFor(NodeGroupLinkAction.P);
}
public List findSourceNodesFor(NodeGroupLinkAction eventAction) {
Node node = findIdentity();
if (node != null) {
return jdbcTemplate.query(getSql("selectNodePrefixSql","findNodesWhoTargetMeSql"), new Object[] {
node.getNodeGroupId(), eventAction.name() }, new NodeRowMapper());
} else {
return Collections.emptyList();
}
}
public List findTargetNodesFor(NodeGroupLinkAction eventAction) {
Node node = findIdentity();
if (node != null) {
return jdbcTemplate.query(getSql("selectNodePrefixSql","findNodesWhoITargetSql"), new Object[] {
node.getNodeGroupId(), eventAction.name() }, new NodeRowMapper());
} else {
return Collections.emptyList();
}
}
public boolean updateNodeSecurity(NodeSecurity security) {
flushNodeAuthorizedCache();
security.setNodePassword(filterPasswordOnSaveIfNeeded(security.getNodePassword()));
return jdbcTemplate.update(getSql("updateNodeSecuritySql"), new Object[] { security.getNodePassword(),
security.isRegistrationEnabled() ? 1 : 0, security.getRegistrationTime(),
security.isInitialLoadEnabled() ? 1 : 0, security.getInitialLoadTime(), security.getCreatedAtNodeId(),
security.getNodeId() }, new int[] { Types.VARCHAR, Types.INTEGER, Types.TIMESTAMP, Types.INTEGER,
Types.TIMESTAMP, Types.VARCHAR, Types.VARCHAR }) == 1;
}
public boolean setInitialLoadEnabled(String nodeId, boolean initialLoadEnabled) {
NodeSecurity nodeSecurity = findNodeSecurity(nodeId, true);
if (nodeSecurity != null) {
nodeSecurity.setInitialLoadEnabled(initialLoadEnabled);
if (initialLoadEnabled) {
nodeSecurity.setInitialLoadTime(null);
} else {
nodeSecurity.setInitialLoadTime(new Date());
}
return updateNodeSecurity(nodeSecurity);
}
return false;
}
class NodeRowMapper implements RowMapper {
public Node mapRow(ResultSet rs, int num) throws SQLException {
Node node = new Node();
node.setNodeId(rs.getString(1));
node.setNodeGroupId(rs.getString(2));
node.setExternalId(rs.getString(3));
node.setSyncEnabled(rs.getBoolean(4));
node.setSyncUrl(rs.getString(5));
node.setSchemaVersion(rs.getString(6));
node.setDatabaseType(rs.getString(7));
node.setDatabaseVersion(rs.getString(8));
node.setSymmetricVersion(rs.getString(9));
node.setCreatedAtNodeId(rs.getString(10));
node.setHeartbeatTime(rs.getTimestamp(11));
node.setTimezoneOffset(rs.getString(12));
node.setBatchToSendCount(rs.getInt(13));
node.setBatchInErrorCount(rs.getInt(14));
return node;
}
}
class NodeSecurityRowMapper implements RowMapper {
public NodeSecurity mapRow(ResultSet rs, int num) throws SQLException {
NodeSecurity nodeSecurity = new NodeSecurity();
nodeSecurity.setNodeId(rs.getString(1));
nodeSecurity.setNodePassword(filterPasswordOnRenderIfNeeded(rs.getString(2)));
nodeSecurity.setRegistrationEnabled(rs.getBoolean(3));
nodeSecurity.setRegistrationTime(rs.getTimestamp(4));
nodeSecurity.setInitialLoadEnabled(rs.getBoolean(5));
nodeSecurity.setInitialLoadTime(rs.getTimestamp(6));
nodeSecurity.setCreatedAtNodeId(rs.getString(7));
return nodeSecurity;
}
}
class NodeSecurityResultSetExtractor implements ResultSetExtractor