/** * 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 General Public License, version 3.0 (GPLv3) * (the "License"); you may not use this file except in compliance * with the License. * * You should have received a copy of the GNU General Public License, * version 3.0 (GPLv3) along with this library; if not, see * <http://www.gnu.org/licenses/>. * * 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.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import org.jumpmind.db.sql.ISqlRowMapper; import org.jumpmind.db.sql.Row; import org.jumpmind.symmetric.common.Constants; import org.jumpmind.symmetric.common.ParameterConstants; import org.jumpmind.symmetric.db.ISymmetricDialect; import org.jumpmind.symmetric.model.Channel; import org.jumpmind.symmetric.model.ChannelMap; import org.jumpmind.symmetric.model.Node; import org.jumpmind.symmetric.model.NodeChannel; import org.jumpmind.symmetric.model.NodeGroup; import org.jumpmind.symmetric.model.NodeGroupChannelWindow; import org.jumpmind.symmetric.model.NodeGroupLink; import org.jumpmind.symmetric.model.NodeGroupLinkAction; import org.jumpmind.symmetric.service.IConfigurationService; import org.jumpmind.symmetric.service.INodeService; import org.jumpmind.symmetric.service.IParameterService; /** * @see IConfigurationService */ public class ConfigurationService extends AbstractService implements IConfigurationService { private INodeService nodeService; private Map<String, List<NodeChannel>> nodeChannelCache; private Map<String, Channel> channelsCache; private List<NodeGroupLink> nodeGroupLinksCache; private long channelCacheTime; private long nodeChannelCacheTime; private long nodeGroupLinkCacheTime; private List<Channel> defaultChannels; private Date lastUpdateTime; public ConfigurationService(IParameterService parameterService, ISymmetricDialect dialect, INodeService nodeService) { super(parameterService, dialect); this.nodeService = nodeService; this.defaultChannels = new ArrayList<Channel>(); this.defaultChannels .add(new Channel(Constants.CHANNEL_CONFIG, 0, 2000, 100, true, 0, true)); this.defaultChannels.add(new Channel(Constants.CHANNEL_RELOAD, 1, 1, 1, true, 0, false, true, false)); this.defaultChannels.add(new Channel(Constants.CHANNEL_HEARTBEAT, 2, 100, 100, true, 0, false)); this.defaultChannels.add(new Channel(Constants.CHANNEL_DEFAULT, 99999, 1000, 100, true, 0, false)); this.defaultChannels.add(new Channel(Constants.CHANNEL_DYNAMIC, 99999, 1000, 100, true, 0, false)); if (parameterService.is(ParameterConstants.FILE_SYNC_ENABLE)) { this.defaultChannels.add(new Channel(Constants.CHANNEL_FILESYNC, 3, 100, 100, true, 0, false, "nontransactional", false, true)); this.defaultChannels.add(new Channel(Constants.CHANNEL_FILESYNC_RELOAD, 1, 100, 100, true, 0, false, "nontransactional", true, true)); } setSqlMap(new ConfigurationServiceSqlMap(symmetricDialect.getPlatform(), createSqlReplacementTokens())); } public boolean isMasterToMaster() { boolean masterToMaster = false; Node me = nodeService.findIdentity(); if (me != null) { masterToMaster = getNodeGroupLinkFor(me.getNodeGroupId(), me.getNodeGroupId(), false) != null; } return masterToMaster; } public boolean refreshFromDatabase() { Date date1 = sqlTemplate.queryForObject(getSql("selectMaxChannelLastUpdateTime"), Date.class); Date date2 = sqlTemplate.queryForObject(getSql("selectMaxNodeGroupLastUpdateTime"), Date.class); Date date3 = sqlTemplate.queryForObject(getSql("selectMaxNodeGroupLinkLastUpdateTime"), Date.class); Date date = maxDate(date1, date2, date3); if (date != null) { if (lastUpdateTime == null || lastUpdateTime.before(date)) { if (lastUpdateTime != null) { log.info("Newer channel or group settings were detected"); } lastUpdateTime = date; clearCache(); return true; } } return false; } public void saveNodeGroupLink(NodeGroupLink link) { if (!doesNodeGroupExist(link.getSourceNodeGroupId())) { saveNodeGroup(new NodeGroup(link.getSourceNodeGroupId())); } if (!doesNodeGroupExist(link.getTargetNodeGroupId())) { saveNodeGroup(new NodeGroup(link.getTargetNodeGroupId())); } link.setLastUpdateTime(new Date()); if (sqlTemplate.update(getSql("updateNodeGroupLinkSql"), link.getDataEventAction().name(), link.isSyncConfigEnabled() ? 1 : 0, link.getLastUpdateTime(), link.getLastUpdateBy(), link.getSourceNodeGroupId(), link.getTargetNodeGroupId()) == 0) { link.setCreateTime(new Date()); sqlTemplate.update(getSql("insertNodeGroupLinkSql"), link.getDataEventAction().name(), link.getSourceNodeGroupId(), link.getTargetNodeGroupId(), link.isSyncConfigEnabled() ? 1 : 0, link.getLastUpdateTime(), link.getLastUpdateBy(), link.getCreateTime()); } } public boolean doesNodeGroupExist(String nodeGroupId) { boolean exists = false; List<NodeGroup> groups = getNodeGroups(); for (NodeGroup nodeGroup : groups) { exists |= nodeGroup.getNodeGroupId().equals(nodeGroupId); } return exists; } public void saveNodeGroup(NodeGroup group) { group.setLastUpdateTime(new Date()); if (sqlTemplate.update(getSql("updateNodeGroupSql"), group.getDescription(), group.getLastUpdateTime(), group.getLastUpdateBy(), group.getNodeGroupId()) == 0) { group.setCreateTime(new Date()); sqlTemplate.update(getSql("insertNodeGroupSql"), group.getDescription(), group.getNodeGroupId(), group.getLastUpdateTime(), group.getLastUpdateBy(), group.getCreateTime()); } } public void deleteNodeGroup(String nodeGroupId) { sqlTemplate.update(getSql("deleteNodeGroupSql"), nodeGroupId); } public void deleteNodeGroupLink(NodeGroupLink link) { sqlTemplate.update(getSql("deleteNodeGroupLinkSql"), link.getSourceNodeGroupId(), link.getTargetNodeGroupId()); } public List<NodeGroup> getNodeGroups() { return sqlTemplate.query(getSql("selectNodeGroupsSql"), new NodeGroupMapper()); } public List<NodeGroupLink> getNodeGroupLinks(boolean refreshCache) { if (refreshCache) { nodeGroupLinkCacheTime = 0; } long cacheTimeoutInMs = parameterService .getLong(ParameterConstants.CACHE_TIMEOUT_NODE_GROUP_LINK_IN_MS); List<NodeGroupLink> links = nodeGroupLinksCache; if (System.currentTimeMillis() - nodeGroupLinkCacheTime >= cacheTimeoutInMs || links == null) { synchronized (this) { links = nodeGroupLinksCache; if (System.currentTimeMillis() - nodeGroupLinkCacheTime >= cacheTimeoutInMs || links == null) { links = sqlTemplate.query(getSql("groupsLinksSql"), new NodeGroupLinkMapper()); nodeGroupLinksCache = links; nodeGroupLinkCacheTime = System.currentTimeMillis(); } } } return links; } public List<NodeGroupLink> getNodeGroupLinksFor(String sourceNodeGroupId, boolean refreshCache) { List<NodeGroupLink> links = getNodeGroupLinks(refreshCache); List<NodeGroupLink> target = new ArrayList<NodeGroupLink>(links.size()); for (NodeGroupLink nodeGroupLink : links) { if (nodeGroupLink.getSourceNodeGroupId().equals(sourceNodeGroupId)) { target.add(nodeGroupLink); } } return target; } public NodeGroupLink getNodeGroupLinkFor(String sourceNodeGroupId, String targetNodeGroupId, boolean refreshCache) { List<NodeGroupLink> links = getNodeGroupLinks(refreshCache); for (NodeGroupLink nodeGroupLink : links) { if (nodeGroupLink.getTargetNodeGroupId().equals(targetNodeGroupId) && nodeGroupLink.getSourceNodeGroupId().equals(sourceNodeGroupId)) { return nodeGroupLink; } } return null; } public boolean isChannelInUse(String channelId) { return sqlTemplate.queryForInt(getSql("isChannelInUseSql"), channelId) > 0; } public void saveChannel(Channel channel, boolean reloadChannels) { channel.setLastUpdateTime(new Date()); if (0 == sqlTemplate.update( getSql("updateChannelSql"), new Object[] { channel.getProcessingOrder(), channel.getMaxBatchSize(), channel.getMaxBatchToSend(), channel.getMaxDataToRoute(), channel.isUseOldDataToRoute() ? 1 : 0, channel.isUseRowDataToRoute() ? 1 : 0, channel.isUsePkDataToRoute() ? 1 : 0, channel.isContainsBigLob() ? 1 : 0, channel.isEnabled() ? 1 : 0, channel.getBatchAlgorithm(), channel.getExtractPeriodMillis(), channel.getDataLoaderType(), channel.getLastUpdateTime(), channel.getLastUpdateBy(), channel.isReloadFlag() ? 1 : 0, channel.isFileSyncFlag() ? 1 : 0, channel.getChannelId() })) { channel.setCreateTime(new Date()); sqlTemplate.update( getSql("insertChannelSql"), new Object[] { channel.getChannelId(), channel.getProcessingOrder(), channel.getMaxBatchSize(), channel.getMaxBatchToSend(), channel.getMaxDataToRoute(), channel.isUseOldDataToRoute() ? 1 : 0, channel.isUseRowDataToRoute() ? 1 : 0, channel.isUsePkDataToRoute() ? 1 : 0, channel.isContainsBigLob() ? 1 : 0, channel.isEnabled() ? 1 : 0, channel.getBatchAlgorithm(), channel.getExtractPeriodMillis(), channel.getDataLoaderType(), channel.getLastUpdateTime(), channel.getLastUpdateBy(), channel.getCreateTime(), channel.isReloadFlag() ? 1 : 0, channel.isFileSyncFlag() ? 1 : 0, }); } if (reloadChannels) { clearCache(); } } public void saveChannel(NodeChannel nodeChannel, boolean reloadChannels) { saveChannel(nodeChannel.getChannel(), reloadChannels); } public void saveNodeChannel(NodeChannel nodeChannel, boolean reloadChannels) { saveChannel(nodeChannel.getChannel(), false); saveNodeChannelControl(nodeChannel, reloadChannels); } public void saveNodeChannelControl(NodeChannel nodeChannel, boolean reloadChannels) { if (0 == sqlTemplate.update( getSql("updateNodeChannelControlSql"), new Object[] { nodeChannel.isSuspendEnabled() ? 1 : 0, nodeChannel.isIgnoreEnabled() ? 1 : 0, nodeChannel.getLastExtractTime(), nodeChannel.getNodeId(), nodeChannel.getChannelId() })) { sqlTemplate .update(getSql("insertNodeChannelControlSql"), new Object[] { nodeChannel.getNodeId(), nodeChannel.getChannelId(), nodeChannel.isSuspendEnabled() ? 1 : 0, nodeChannel.isIgnoreEnabled() ? 1 : 0, nodeChannel.getLastExtractTime() }); } if (reloadChannels) { clearCache(); } } public void deleteChannel(Channel channel) { sqlTemplate.update(getSql("deleteNodeChannelSql"), new Object[] { channel.getChannelId() }); sqlTemplate.update(getSql("deleteChannelSql"), new Object[] { channel.getChannelId() }); clearCache(); } public NodeChannel getNodeChannel(String channelId, boolean refreshExtractMillis) { return getNodeChannel(channelId, nodeService.findIdentityNodeId(), refreshExtractMillis); } public NodeChannel getNodeChannel(String channelId, String nodeId, boolean refreshExtractMillis) { List<NodeChannel> channels = getNodeChannels(nodeId, refreshExtractMillis); for (NodeChannel nodeChannel : channels) { if (nodeChannel.getChannelId().equals(channelId)) { return nodeChannel; } } return null; } public List<NodeChannel> getNodeChannels(boolean refreshExtractMillis) { return getNodeChannels(nodeService.findIdentityNodeId(), refreshExtractMillis); } public List<NodeChannel> getNodeChannels(final String nodeId, boolean refreshExtractMillis) { boolean loaded = false; long channelCacheTimeoutInMs = parameterService .getLong(ParameterConstants.CACHE_TIMEOUT_CHANNEL_IN_MS); List<NodeChannel> nodeChannels = nodeChannelCache != null ? nodeChannelCache.get(nodeId) : null; if (System.currentTimeMillis() - nodeChannelCacheTime >= channelCacheTimeoutInMs || nodeChannels == null) { synchronized (this) { if (System.currentTimeMillis() - nodeChannelCacheTime >= channelCacheTimeoutInMs || nodeChannelCache == null || nodeChannelCache.get(nodeId) == null || nodeChannels == null) { if (System.currentTimeMillis() - nodeChannelCacheTime >= channelCacheTimeoutInMs || nodeChannelCache == null) { nodeChannelCache = new HashMap<String, List<NodeChannel>>(); nodeChannelCacheTime = System.currentTimeMillis(); } if (nodeId != null) { nodeChannels = sqlTemplate.query(getSql("selectNodeChannelsSql"), new ISqlRowMapper<NodeChannel>() { public NodeChannel mapRow(Row row) { NodeChannel nodeChannel = new NodeChannel(); nodeChannel.setChannelId(row.getString("channel_id")); nodeChannel.setNodeId(nodeId); nodeChannel.setIgnoreEnabled(row .getBoolean("ignore_enabled")); nodeChannel.setSuspendEnabled(row .getBoolean("suspend_enabled")); nodeChannel.setProcessingOrder(row .getInt("processing_order")); nodeChannel.setMaxBatchSize(row.getInt("max_batch_size")); nodeChannel.setEnabled(row.getBoolean("enabled")); nodeChannel.setMaxBatchToSend(row .getInt("max_batch_to_send")); nodeChannel.setMaxDataToRoute(row .getInt("max_data_to_route")); nodeChannel.setUseOldDataToRoute(row .getBoolean("use_old_data_to_route")); nodeChannel.setUseRowDataToRoute(row .getBoolean("use_row_data_to_route")); nodeChannel.setUsePkDataToRoute(row .getBoolean("use_pk_data_to_route")); nodeChannel.setContainsBigLob(row .getBoolean("contains_big_lob")); nodeChannel.setBatchAlgorithm(row .getString("batch_algorithm")); nodeChannel.setLastExtractTime(row .getDateTime("last_extract_time")); nodeChannel.setExtractPeriodMillis(row .getLong("extract_period_millis")); nodeChannel.setDataLoaderType(row .getString("data_loader_type")); nodeChannel.setCreateTime(row.getDateTime("create_time")); nodeChannel.setLastUpdateBy(row.getString("last_update_by")); nodeChannel.setLastUpdateTime(row .getDateTime("last_update_time")); nodeChannel.setFileSyncFlag(row .getBoolean("file_sync_flag")); nodeChannel.setReloadFlag(row.getBoolean("reload_flag")); return nodeChannel; }; }, nodeId); nodeChannelCache.put(nodeId, nodeChannels); loaded = true; } else { nodeChannels = new ArrayList<NodeChannel>(0); } } } } if (!loaded && refreshExtractMillis) { /* * need to read last extracted time from database regardless of * whether we used the cache or not. locate the nodes in the cache, * and update it. */ final Map<String, NodeChannel> nodeChannelsMap = new HashMap<String, NodeChannel>(); for (NodeChannel nc : nodeChannels) { nodeChannelsMap.put(nc.getChannelId(), nc); } sqlTemplate.query(getSql("selectNodeChannelControlLastExtractTimeSql"), new ISqlRowMapper<Object>() { public Object mapRow(Row row) { String channelId = row.getString("channel_id"); Date extractTime = row.getDateTime("last_extract_time"); NodeChannel nodeChannel = nodeChannelsMap.get(channelId); if (nodeChannel != null) { nodeChannel.setLastExtractTime(extractTime); } return nodeChannelsMap; }; }, nodeId); } return nodeChannels; } public void clearCache() { synchronized (this) { nodeChannelCache = null; channelsCache = null; nodeGroupLinksCache = null; } } public NodeGroupLinkAction getDataEventActionByGroupLinkId(String sourceGroupId, String targetGroupId) { String code = (String) sqlTemplate.queryForObject(getSql("selectDataEventActionsByIdSql"), String.class, sourceGroupId, targetGroupId); return NodeGroupLinkAction.fromCode(code); } public void initDefaultChannels() { if (defaultChannels != null) { clearCache(); List<NodeChannel> channels = getNodeChannels(false); for (Channel defaultChannel : defaultChannels) { Channel channel = defaultChannel.findInList(channels); if (channel == null) { log.info("Auto-configuring {} channel", defaultChannel.getChannelId()); saveChannel(defaultChannel, true); } else if (channel.getChannelId().equals(Constants.CHANNEL_RELOAD) && !channel.isReloadFlag()) { log.info("Setting reload flag on reload channel"); channel.setReloadFlag(true); saveChannel(channel, true); } else if (channel.getChannelId().equals(Constants.CHANNEL_FILESYNC) && !channel.isFileSyncFlag()) { log.info("Setting file sync flag on file sync channel"); channel.setFileSyncFlag(true); saveChannel(channel, true); } else if (channel.getChannelId().equals(Constants.CHANNEL_FILESYNC_RELOAD) && (!channel.isFileSyncFlag() || !channel.isReloadFlag())) { log.info("Setting reload and file sync flag on file sync reload channel"); channel.setFileSyncFlag(true); saveChannel(channel, true); } else { log.debug("No need to create channel {}. It already exists", defaultChannel.getChannelId()); } } clearCache(); } } public List<NodeGroupChannelWindow> getNodeGroupChannelWindows(String nodeGroupId, String channelId) { return (List<NodeGroupChannelWindow>) sqlTemplate.query( getSql("selectNodeGroupChannelWindowSql"), new NodeGroupChannelWindowMapper(), nodeGroupId, channelId); } public ChannelMap getSuspendIgnoreChannelLists(final String nodeId) { ChannelMap map = new ChannelMap(); List<NodeChannel> ncs = getNodeChannels(nodeId, true); if (ncs != null) { for (NodeChannel nc : ncs) { if (nc.isSuspendEnabled()) { map.addSuspendChannels(nc.getChannelId()); } if (nc.isIgnoreEnabled()) { map.addIgnoreChannels(nc.getChannelId()); } } } return map; } public List<Channel> getFileSyncChannels() { List<Channel> list = new ArrayList<Channel>(getChannels(false).values()); Iterator<Channel> it = list.iterator(); while (it.hasNext()) { Channel channel = it.next(); if (!channel.isFileSyncFlag()) { it.remove(); } } return list; } public Map<String, Channel> getChannels(boolean refreshCache) { long channelCacheTimeoutInMs = parameterService.getLong( ParameterConstants.CACHE_TIMEOUT_CHANNEL_IN_MS, 60000); Map<String, Channel> channels = channelsCache; if (System.currentTimeMillis() - channelCacheTime >= channelCacheTimeoutInMs || channels == null || refreshCache) { synchronized (this) { channels = channelsCache; if (System.currentTimeMillis() - channelCacheTime >= channelCacheTimeoutInMs || channels == null || refreshCache) { channels = new HashMap<String, Channel>(); List<Channel> list = sqlTemplate.query(getSql("selectChannelsSql"), new ISqlRowMapper<Channel>() { public Channel mapRow(Row row) { Channel channel = new Channel(); channel.setChannelId(row.getString("channel_id")); channel.setProcessingOrder(row.getInt("processing_order")); channel.setMaxBatchSize(row.getInt("max_batch_size")); channel.setEnabled(row.getBoolean("enabled")); channel.setMaxBatchToSend(row.getInt("max_batch_to_send")); channel.setMaxDataToRoute(row.getInt("max_data_to_route")); channel.setUseOldDataToRoute(row .getBoolean("use_old_data_to_route")); channel.setUseRowDataToRoute(row .getBoolean("use_row_data_to_route")); channel.setUsePkDataToRoute(row .getBoolean("use_pk_data_to_route")); channel.setContainsBigLob(row.getBoolean("contains_big_lob")); channel.setBatchAlgorithm(row.getString("batch_algorithm")); channel.setExtractPeriodMillis(row .getLong("extract_period_millis")); channel.setDataLoaderType(row.getString("data_loader_type")); channel.setCreateTime(row.getDateTime("create_time")); channel.setLastUpdateBy(row.getString("last_update_by")); channel.setLastUpdateTime(row.getDateTime("last_update_time")); channel.setReloadFlag(row.getBoolean("reload_flag")); channel.setFileSyncFlag(row.getBoolean("file_sync_flag")); return channel; } }); for (Channel channel : list) { channels.put(channel.getChannelId(), channel); } channelsCache = channels; channelCacheTime = System.currentTimeMillis(); } } } return channels; } public Channel getChannel(String channelId) { NodeChannel nodeChannel = getNodeChannel(channelId, false); if (nodeChannel != null) { return nodeChannel.getChannel(); } else { return null; } } public ChannelMap getSuspendIgnoreChannelLists() { return getSuspendIgnoreChannelLists(nodeService.findIdentityNodeId()); } class NodeGroupChannelWindowMapper implements ISqlRowMapper<NodeGroupChannelWindow> { public NodeGroupChannelWindow mapRow(Row row) { NodeGroupChannelWindow window = new NodeGroupChannelWindow(); window.setNodeGroupId(row.getString("node_group_id")); window.setChannelId(row.getString("channel_id")); window.setStartTime(row.getTime("start_time")); window.setEndTime(row.getTime("end_time")); window.setEnabled(row.getBoolean("enabled")); return window; } } class NodeGroupLinkMapper implements ISqlRowMapper<NodeGroupLink> { public NodeGroupLink mapRow(Row row) { NodeGroupLink link = new NodeGroupLink(); link.setSourceNodeGroupId(row.getString("source_node_group_id")); link.setTargetNodeGroupId(row.getString("target_node_group_id")); link.setDataEventAction(NodeGroupLinkAction.fromCode(row.getString("data_event_action"))); link.setSyncConfigEnabled(row.getBoolean("sync_config_enabled")); link.setCreateTime(row.getDateTime("create_time")); link.setLastUpdateBy(row.getString("last_update_by")); link.setLastUpdateTime(row.getDateTime("last_update_time")); return link; } } class NodeGroupMapper implements ISqlRowMapper<NodeGroup> { public NodeGroup mapRow(Row row) { NodeGroup group = new NodeGroup(); group.setNodeGroupId(row.getString("node_group_id")); group.setDescription(row.getString("description")); group.setCreateTime(row.getDateTime("create_time")); group.setLastUpdateBy(row.getString("last_update_by")); group.setLastUpdateTime(row.getDateTime("last_update_time")); return group; } } public Map<String, String> getRegistrationRedirectMap() { return this.sqlTemplate.queryForMap(getSql("getRegistrationRedirectSql"), "registrant_external_id", "registration_node_id"); } @Override public void updateLastExtractTime(NodeChannel channel) { sqlTemplate.update(getSql("updateNodeChannelLastExtractTime"), channel.getLastExtractTime(), channel.getChannelId(), channel.getNodeId()); } }