/** * 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 static org.apache.commons.lang.StringUtils.isBlank; import java.sql.Types; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.UUID; import org.apache.commons.lang.StringUtils; import org.jumpmind.db.model.Column; import org.jumpmind.db.model.Database; import org.jumpmind.db.model.Table; import org.jumpmind.db.sql.ISqlRowMapper; import org.jumpmind.db.sql.Row; import org.jumpmind.properties.TypedProperties; import org.jumpmind.symmetric.ISymmetricEngine; import org.jumpmind.symmetric.Version; import org.jumpmind.symmetric.common.Constants; import org.jumpmind.symmetric.common.ParameterConstants; import org.jumpmind.symmetric.common.TableConstants; import org.jumpmind.symmetric.config.ITriggerCreationListener; import org.jumpmind.symmetric.config.TriggerFailureListener; import org.jumpmind.symmetric.io.data.DataEventType; import org.jumpmind.symmetric.model.Channel; import org.jumpmind.symmetric.model.NodeGroupLink; import org.jumpmind.symmetric.model.NodeSecurity; import org.jumpmind.symmetric.model.Router; import org.jumpmind.symmetric.model.Trigger; import org.jumpmind.symmetric.model.TriggerHistory; import org.jumpmind.symmetric.model.TriggerReBuildReason; import org.jumpmind.symmetric.model.TriggerRouter; import org.jumpmind.symmetric.route.ConfigurationChangedDataRouter; import org.jumpmind.symmetric.route.FileSyncDataRouter; import org.jumpmind.symmetric.service.ClusterConstants; import org.jumpmind.symmetric.service.IClusterService; import org.jumpmind.symmetric.service.IConfigurationService; import org.jumpmind.symmetric.service.IExtensionService; import org.jumpmind.symmetric.service.IGroupletService; import org.jumpmind.symmetric.service.INodeService; import org.jumpmind.symmetric.service.ISequenceService; import org.jumpmind.symmetric.service.ITriggerRouterService; import org.jumpmind.symmetric.statistic.IStatisticManager; import org.jumpmind.util.FormatUtils; import org.jumpmind.util.KeyedCache; import org.jumpmind.util.KeyedCache.IRefreshCache; /** * @see ITriggerRouterService */ public class TriggerRouterService extends AbstractService implements ITriggerRouterService { private IClusterService clusterService; private IConfigurationService configurationService; private ISequenceService sequenceService; private IExtensionService extensionService; private Map<Boolean, KeyedCache<String, Router>> routersCache = new HashMap<Boolean, KeyedCache<String,Router>>(); private Map<Boolean, KeyedCache<String, Trigger>> triggersCache = new HashMap<Boolean, KeyedCache<String,Trigger>>();; private Map<String, TriggerRoutersCache> triggerRouterCacheByNodeGroupId = new HashMap<String, TriggerRoutersCache>(); private Map<Boolean, KeyedCache<Integer, TriggerRouter>> triggerRoutersCache = new HashMap<Boolean, KeyedCache<Integer,TriggerRouter>>(); private long triggerRouterPerNodeCacheTime; private TriggerFailureListener failureListener = new TriggerFailureListener(); private IStatisticManager statisticManager; private IGroupletService groupletService; private INodeService nodeService; private List<String> extraConfigTables = new ArrayList<String>(); private Date lastUpdateTime; private Object cacheLock = new Object(); /** * Cache the history for performance. History never changes and does not * grow big so this should be OK. */ private HashMap<Integer, TriggerHistory> historyMap = new HashMap<Integer, TriggerHistory>(); public TriggerRouterService(ISymmetricEngine engine) { super(engine.getParameterService(), engine.getSymmetricDialect()); this.clusterService = engine.getClusterService(); this.configurationService = engine.getConfigurationService(); this.statisticManager = engine.getStatisticManager(); this.groupletService = engine.getGroupletService(); this.nodeService = engine.getNodeService(); this.sequenceService = engine.getSequenceService(); this.extensionService = engine.getExtensionService(); engine.getExtensionService().addExtensionPoint(failureListener); setSqlMap(new TriggerRouterServiceSqlMap(symmetricDialect.getPlatform(), createSqlReplacementTokens())); } public boolean refreshFromDatabase() { Date date1 = sqlTemplate.queryForObject(getSql("selectMaxTriggerLastUpdateTime"), Date.class); Date date2 = sqlTemplate.queryForObject(getSql("selectMaxRouterLastUpdateTime"), Date.class); Date date3 = sqlTemplate.queryForObject(getSql("selectMaxTriggerRouterLastUpdateTime"), Date.class); Date date = maxDate(date1, date2, date3); if (date != null) { if (lastUpdateTime == null || lastUpdateTime.before(date)) { if (lastUpdateTime != null) { log.info("Newer trigger router settings were detected"); } lastUpdateTime = date; clearCache(); return true; } } return false; } public List<Trigger> getTriggers(boolean substituteParameters) { return sqlTemplate.query("select " + getSql("selectTriggersColumnList", "selectTriggersSql"), new TriggerMapper(substituteParameters)); } public boolean isTriggerBeingUsed(String triggerId) { return sqlTemplate.queryForInt(getSql("countTriggerRoutersByTriggerIdSql"), triggerId) > 0; } public boolean doesTriggerExist(String triggerId) { return sqlTemplate.queryForInt(getSql("countTriggerByTriggerIdSql"), triggerId) > 0; } public boolean doesTriggerExistForTable(String tableName) { return sqlTemplate.queryForInt(getSql("countTriggerByTableNameSql"), tableName) > 0; } public void deleteTrigger(Trigger trigger) { sqlTemplate.update(getSql("deleteTriggerSql"), (Object) trigger.getTriggerId()); } public void dropTriggers() { List<TriggerHistory> activeHistories = getActiveTriggerHistories(); for (TriggerHistory history : activeHistories) { if (!TableConstants.getTables(symmetricDialect.getTablePrefix()).contains(history.getSourceTableName())) { dropTriggers(history, (StringBuilder) null); } } } public void dropTriggers(Set<String> tables) { List<TriggerHistory> activeHistories = null; for (String table : tables) { if (doesTriggerExistForTable(table)) { activeHistories = this.getActiveTriggerHistories(table); for (TriggerHistory history : activeHistories) { dropTriggers(history, (StringBuilder) null); } } } } protected void deleteTriggerHistory(TriggerHistory history) { sqlTemplate.update(getSql("deleteTriggerHistorySql"), history.getTriggerHistoryId()); } public void createTriggersOnChannelForTables(String channelId, String catalogName, String schemaName, List<String> tables, String lastUpdateBy) { createTriggersOnChannelForTablesWithReturn(channelId, catalogName, schemaName, tables, lastUpdateBy); } public List<Trigger> createTriggersOnChannelForTablesWithReturn(String channelId, String catalogName, String schemaName, List<String> tables, String lastUpdateBy) { List<Trigger> createdTriggers = new ArrayList<Trigger>(); List<Trigger> existingTriggers = getTriggers(false); for (String table : tables) { Trigger trigger = new Trigger(); trigger.setChannelId(channelId); trigger.setSourceCatalogName(catalogName); trigger.setSourceSchemaName(schemaName); trigger.setSourceTableName(table); String triggerId = table; if (table.length() > 50) { triggerId = table.substring(0, 13) + "_" + UUID.randomUUID().toString(); } boolean uniqueNameCreated = false; int suffix = 0; while (!uniqueNameCreated) { String triggerIdPriorToCheck = triggerId; for (Trigger existingTrigger : existingTriggers) { if (triggerId.equals(existingTrigger.getTriggerId())) { String suffixString = "_" + suffix; if (suffix == 0) { triggerId = triggerId + suffixString; } else { triggerId = triggerId.substring(0, triggerId.length() - ("_" + (suffix - 1)).length()) + suffixString; } suffix++; } } if (triggerId.equals(triggerIdPriorToCheck)) { uniqueNameCreated = true; } } trigger.setTriggerId(triggerId); trigger.setLastUpdateBy(lastUpdateBy); trigger.setLastUpdateTime(new Date()); trigger.setCreateTime(new Date()); saveTrigger(trigger); createdTriggers.add(trigger); } return createdTriggers; } public Collection<Trigger> findMatchingTriggers(List<Trigger> triggers, String catalog, String schema, String table) { Set<Trigger> matches = new HashSet<Trigger>(); for (Trigger trigger : triggers) { boolean catalogMatches = trigger.isSourceCatalogNameWildCarded() || (catalog == null && trigger.getSourceCatalogName() == null) || (StringUtils.isBlank(trigger.getSourceCatalogName()) && StringUtils.isNotBlank(catalog) && catalog.equals(platform .getDefaultCatalog())) || (StringUtils.isNotBlank(catalog) && catalog.equals(trigger.getSourceCatalogName())); boolean schemaMatches = trigger.isSourceSchemaNameWildCarded() || (schema == null && trigger.getSourceSchemaName() == null) || (StringUtils.isBlank(trigger.getSourceSchemaName()) && StringUtils.isNotBlank(schema) && schema.equals(platform .getDefaultSchema())) || (StringUtils.isNotBlank(schema) && schema.equals(trigger.getSourceSchemaName())); boolean tableMatches = trigger.isSourceTableNameWildCarded() || table.equalsIgnoreCase(trigger.getSourceTableName()); if (catalogMatches && schemaMatches && tableMatches) { matches.add(trigger); } } return matches; } public void inactivateTriggerHistory(TriggerHistory history) { sqlTemplate.update(getSql("inactivateTriggerHistorySql"), new Object[] { history.getErrorMessage(), history.getTriggerHistoryId() }, new int[] { Types.VARCHAR, Types.INTEGER }); } public Map<Long, TriggerHistory> getHistoryRecords() { final Map<Long, TriggerHistory> retMap = new HashMap<Long, TriggerHistory>(); sqlTemplate.query(getSql("allTriggerHistSql"), new TriggerHistoryMapper(retMap)); return retMap; } protected boolean isTriggerNameInUse(List<TriggerHistory> activeTriggerHistories, String triggerId, String triggerName) { for (TriggerHistory triggerHistory : activeTriggerHistories) { if (!triggerHistory.getTriggerId().equals(triggerId) && ((triggerHistory.getNameForDeleteTrigger() != null && triggerHistory.getNameForDeleteTrigger().equals(triggerName)) || (triggerHistory.getNameForInsertTrigger() != null && triggerHistory.getNameForInsertTrigger().equals( triggerName)) || (triggerHistory.getNameForUpdateTrigger() != null && triggerHistory .getNameForUpdateTrigger().equals(triggerName)))) { return true; } } return false; } public TriggerHistory findTriggerHistory(String catalogName, String schemaName, String tableName) { List<TriggerHistory> list = findTriggerHistories(catalogName, schemaName, tableName); return list.size() > 0 ? list.get(0) : null; } public List<TriggerHistory> findTriggerHistories(String catalogName, String schemaName, String tableName) { List<TriggerHistory> listToReturn = new ArrayList<TriggerHistory>(); List<TriggerHistory> triggerHistories = getActiveTriggerHistories(); if (triggerHistories != null && triggerHistories.size() > 0) { for (TriggerHistory triggerHistory : triggerHistories) { boolean matches = true; if (StringUtils.isNotBlank(catalogName)) { matches = catalogName.equals(triggerHistory.getSourceCatalogName()); } if (matches && StringUtils.isNotBlank(schemaName)) { matches = schemaName.equals(triggerHistory.getSourceSchemaName()); } if (matches && StringUtils.isNotBlank(tableName)) { boolean ignoreCase = parameterService.is(ParameterConstants.DB_METADATA_IGNORE_CASE) && !FormatUtils.isMixedCase(tableName); matches = ignoreCase ? triggerHistory.getSourceTableName().equalsIgnoreCase(tableName) : triggerHistory .getSourceTableName().equals(tableName); } if (matches) { listToReturn.add(triggerHistory); } } } return listToReturn; } public TriggerHistory getTriggerHistory(int histId) { TriggerHistory history = historyMap.get(histId); if (history == null && histId >= 0) { history = (TriggerHistory) sqlTemplate.queryForObject(getSql("triggerHistSql"), new TriggerHistoryMapper(), histId); historyMap.put(histId, history); } return history; } public List<TriggerHistory> getActiveTriggerHistories(Trigger trigger) { List<TriggerHistory> active = getActiveTriggerHistories(); List<TriggerHistory> list = new ArrayList<TriggerHistory>(); for (TriggerHistory triggerHistory : active) { if (triggerHistory.getTriggerId().equals(trigger.getTriggerId())) { list.add(triggerHistory); } } return list; } public TriggerHistory getNewestTriggerHistoryForTrigger(String triggerId, String catalogName, String schemaName, String tableName) { List<TriggerHistory> triggerHistories = sqlTemplate.query(getSql("latestTriggerHistSql"), new TriggerHistoryMapper(), triggerId, tableName); for (TriggerHistory triggerHistory : triggerHistories) { if ((StringUtils.isBlank(catalogName) && StringUtils.isBlank(triggerHistory.getSourceCatalogName())) || (StringUtils.isNotBlank(catalogName) && catalogName.equals(triggerHistory.getSourceCatalogName()))) { if ((StringUtils.isBlank(schemaName) && StringUtils.isBlank(triggerHistory.getSourceSchemaName())) || (StringUtils.isNotBlank(schemaName) && schemaName.equals(triggerHistory.getSourceSchemaName()))) { return triggerHistory; } } } return null; } @SuppressWarnings("unchecked") @Override public List<TriggerHistory> getActiveTriggerHistoriesFromCache() { return new ArrayList<TriggerHistory>(historyMap != null ? historyMap.values() : Collections.EMPTY_LIST); } /** * Get a list of trigger histories that are currently active */ public List<TriggerHistory> getActiveTriggerHistories() { List<TriggerHistory> histories = sqlTemplate.query(getSql("allTriggerHistSql", "activeTriggerHistSql"), new TriggerHistoryMapper()); for (TriggerHistory triggerHistory : histories) { historyMap.put(triggerHistory.getTriggerHistoryId(), triggerHistory); } return histories; } public List<TriggerHistory> getActiveTriggerHistories(String tableName) { return sqlTemplate.query(getSql("allTriggerHistSql", "triggerHistBySourceTableWhereSql"), new TriggerHistoryMapper(), tableName); } protected List<Trigger> buildTriggersForSymmetricTables(String version, String... tablesToExclude) { List<Trigger> triggers = new ArrayList<Trigger>(); List<String> tables = new ArrayList<String>(TableConstants.getConfigTables(symmetricDialect.getTablePrefix())); if (extraConfigTables != null) { for (String extraTable : extraConfigTables) { tables.add(extraTable); } } List<Trigger> definedTriggers = getTriggers(false); for (Trigger trigger : definedTriggers) { if (tables.remove(trigger.getSourceTableName())) { logOnce(String.format("Not generating virtual triggers for %s because there is a user defined trigger already defined", trigger.getSourceTableName())); } } if (tablesToExclude != null) { for (String tableToExclude : tablesToExclude) { String tablename = TableConstants.getTableName(tablePrefix, tableToExclude); if (!tables.remove(tablename)) { if (!tables.remove(tablename.toUpperCase())) { tables.remove(tablename.toLowerCase()); } } } } for (String tableName : tables) { Trigger trigger = buildTriggerForSymmetricTable(tableName); triggers.add(trigger); } return triggers; } protected Trigger buildTriggerForSymmetricTable(String tableName) { boolean syncChanges = !TableConstants.getTablesThatDoNotSync(tablePrefix).contains(tableName) && parameterService.is(ParameterConstants.AUTO_SYNC_CONFIGURATION); boolean syncOnIncoming = !configurationService.isMasterToMaster() && (parameterService.is(ParameterConstants.AUTO_SYNC_CONFIGURATION_ON_INCOMING, true) || tableName.equals(TableConstants .getTableName(tablePrefix, TableConstants.SYM_TABLE_RELOAD_REQUEST))); Trigger trigger = new Trigger(); trigger.setTriggerId(tableName); trigger.setSyncOnDelete(syncChanges); trigger.setSyncOnInsert(syncChanges); trigger.setSyncOnUpdate(syncChanges); trigger.setSyncOnIncomingBatch(syncOnIncoming); trigger.setSourceTableName(tableName); trigger.setUseCaptureOldData(false); if (TableConstants.getTableName(tablePrefix, TableConstants.SYM_NODE_HOST).equals(tableName)) { trigger.setChannelId(Constants.CHANNEL_HEARTBEAT); } else if (TableConstants.getTableName(tablePrefix, TableConstants.SYM_FILE_SNAPSHOT).equals(tableName)) { trigger.setChannelId(Constants.CHANNEL_DYNAMIC); trigger.setChannelExpression("$(curTriggerValue).$(curColumnPrefix)" + platform.alterCaseToMatchDatabaseDefaultCase("channel_id")); trigger.setReloadChannelId(Constants.CHANNEL_FILESYNC_RELOAD); trigger.setUseCaptureOldData(true); trigger.setSyncOnIncomingBatch(false); boolean syncEnabled = parameterService.is(ParameterConstants.FILE_SYNC_ENABLE); trigger.setSyncOnInsert(syncEnabled); trigger.setSyncOnUpdate(syncEnabled); // Changed to false because of // issues with the traffic // file trigger.setSyncOnDelete(false); } else { trigger.setChannelId(Constants.CHANNEL_CONFIG); } if (!TableConstants.getTableName(tablePrefix, TableConstants.SYM_NODE_HOST).equals(tableName) && !TableConstants.getTableName(tablePrefix, TableConstants.SYM_NODE).equals(tableName) && !TableConstants.getTableName(tablePrefix, TableConstants.SYM_NODE_SECURITY).equals(tableName) && !TableConstants.getTableName(tablePrefix, TableConstants.SYM_TABLE_RELOAD_REQUEST).equals(tableName)) { trigger.setUseCaptureLobs(true); } // little trick to force the rebuild of SymmetricDS triggers every time // there is a new version of SymmetricDS trigger.setLastUpdateTime(new Date(Version.version().hashCode())); return trigger; } public List<TriggerRouter> buildTriggerRoutersForSymmetricTables(String version, NodeGroupLink nodeGroupLink, String... tablesToExclude) { int initialLoadOrder = 1; List<Trigger> triggers = buildTriggersForSymmetricTables(version, tablesToExclude); List<TriggerRouter> triggerRouters = new ArrayList<TriggerRouter>(triggers.size()); for (int j = 0; j < triggers.size(); j++) { Trigger trigger = triggers.get(j); TriggerRouter triggerRouter = buildTriggerRoutersForSymmetricTables(version, trigger, nodeGroupLink); triggerRouter.setInitialLoadOrder(initialLoadOrder++); triggerRouters.add(triggerRouter); } return triggerRouters; } public String buildSymmetricTableRouterId(String triggerId, String sourceNodeGroupId, String targetNodeGroupId) { return replaceCharsToShortenName(String.format("%s_%s_2_%s", triggerId, sourceNodeGroupId, targetNodeGroupId)); } protected TriggerRouter buildTriggerRoutersForSymmetricTables(String version, Trigger trigger, NodeGroupLink nodeGroupLink) { TriggerRouter triggerRouter = new TriggerRouter(); triggerRouter.setTrigger(trigger); Router router = triggerRouter.getRouter(); router.setRouterId(buildSymmetricTableRouterId(trigger.getTriggerId(), nodeGroupLink.getSourceNodeGroupId(), nodeGroupLink.getTargetNodeGroupId())); if (TableConstants.getTableName(tablePrefix, TableConstants.SYM_FILE_SNAPSHOT).equals(trigger.getSourceTableName())) { router.setRouterType(FileSyncDataRouter.ROUTER_TYPE); } else { router.setRouterType(ConfigurationChangedDataRouter.ROUTER_TYPE); } router.setNodeGroupLink(nodeGroupLink); router.setLastUpdateTime(trigger.getLastUpdateTime()); triggerRouter.setLastUpdateTime(trigger.getLastUpdateTime()); return triggerRouter; } @Override public Set<TriggerRouter> getTriggerRouterForTableForCurrentNode(boolean substituteParameters, String catalogName, String schemaName, String tableName, boolean refreshCache) { return getTriggerRouterForTableForCurrentNode(substituteParameters, null, catalogName, schemaName, tableName, refreshCache); } public Set<TriggerRouter> getTriggerRouterForTableForCurrentNode(boolean substituteParameters, NodeGroupLink link, String catalogName, String schemaName, String tableName, boolean refreshCache) { TriggerRoutersCache cache = getTriggerRoutersCacheForCurrentNode(refreshCache); Collection<List<TriggerRouter>> triggerRouters = cache.triggerRoutersByTriggerId.values(); HashSet<TriggerRouter> returnList = new HashSet<TriggerRouter>(); for (List<TriggerRouter> list : triggerRouters) { for (TriggerRouter triggerRouter : list) { if (isMatch(link, triggerRouter) && isMatch(catalogName, schemaName, tableName, triggerRouter.getTrigger())) { returnList.add(triggerRouter); } } } return returnList; } protected boolean isMatch(NodeGroupLink link, TriggerRouter router) { if (link != null && router != null && router.getRouter() != null) { return link.getSourceNodeGroupId().equals(router.getRouter().getNodeGroupLink().getSourceNodeGroupId()) && link.getTargetNodeGroupId().equals(router.getRouter().getNodeGroupLink().getTargetNodeGroupId()); } else { return true; } } protected boolean isMatch(String catalogName, String schemaName, String tableName, Trigger trigger) { if (!StringUtils.isBlank(tableName) && !tableName.equals(trigger.getSourceTableName())) { return false; } else if (StringUtils.isBlank(tableName) && !StringUtils.isBlank(trigger.getSourceTableName())) { return false; } else if (!StringUtils.isBlank(catalogName) && !catalogName.equals(trigger.getSourceCatalogName())) { return false; } else if (StringUtils.isBlank(catalogName) && !StringUtils.isBlank(trigger.getSourceCatalogName())) { return false; } else if (!StringUtils.isBlank(schemaName) && !schemaName.equals(trigger.getSourceSchemaName())) { return false; } else if (StringUtils.isBlank(schemaName) && !StringUtils.isBlank(trigger.getSourceSchemaName())) { return false; } else { return true; } } /** * Create a list of {@link TriggerRouter} for the SymmetricDS tables that * should have triggers created for them on the current node. */ protected List<TriggerRouter> getConfigurationTablesTriggerRoutersForCurrentNode(String sourceNodeGroupId) { List<TriggerRouter> triggerRouters = new ArrayList<TriggerRouter>(); List<NodeGroupLink> links = configurationService.getNodeGroupLinksFor(sourceNodeGroupId, false); for (NodeGroupLink nodeGroupLink : links) { triggerRouters.addAll(buildTriggerRoutersForSymmetricTables(Version.version(), nodeGroupLink)); } return triggerRouters; } protected void mergeInConfigurationTablesTriggerRoutersForCurrentNode(String sourceNodeGroupId, List<TriggerRouter> configuredInDatabase) { List<TriggerRouter> virtualConfigTriggers = getConfigurationTablesTriggerRoutersForCurrentNode(sourceNodeGroupId); for (TriggerRouter trigger : virtualConfigTriggers) { if (trigger.getRouter().getNodeGroupLink().getSourceNodeGroupId().equalsIgnoreCase(sourceNodeGroupId) && !doesTriggerRouterExistInList(configuredInDatabase, trigger)) { configuredInDatabase.add(trigger); } } } protected boolean doesTriggerRouterExistInList(List<TriggerRouter> triggerRouters, TriggerRouter triggerRouter) { for (TriggerRouter checkMe : triggerRouters) { if (checkMe.isSame(triggerRouter)) { return true; } } return false; } public TriggerRouter getTriggerRouterForCurrentNode(String triggerId, String routerId, boolean refreshCache) { TriggerRouter triggerRouter = null; List<TriggerRouter> triggerRouters = getTriggerRoutersForCurrentNode(refreshCache).get(triggerId); if (triggerRouters != null) { for (TriggerRouter testTriggerRouter : triggerRouters) { if (ConfigurationChangedDataRouter.ROUTER_TYPE.equals(testTriggerRouter.getRouter().getRouterType()) || testTriggerRouter.getRouter().getRouterId().equals(routerId) || routerId.equals(Constants.UNKNOWN_ROUTER_ID)) { triggerRouter = testTriggerRouter; break; } } } if (triggerRouter == null) { log.warn("Could not find trigger router [{}:{}] in list {}", new Object[] { triggerId, routerId, triggerRouters == null ? 0 : triggerRouters.toString() }); } return triggerRouter; } public Map<String, List<TriggerRouter>> getTriggerRoutersForCurrentNode(boolean refreshCache) { return getTriggerRoutersCacheForCurrentNode(refreshCache).triggerRoutersByTriggerId; } public List<Trigger> getTriggersForCurrentNode(boolean refreshCache) { Map<String, List<TriggerRouter>> triggerRouters = getTriggerRoutersForCurrentNode(refreshCache); List<Trigger> triggers = new ArrayList<Trigger>(triggerRouters.size()); for (List<TriggerRouter> list : triggerRouters.values()) { if (list.size() > 0) { triggers.add(list.get(0).getTrigger()); } } return triggers; } protected TriggerRoutersCache getTriggerRoutersCacheForCurrentNode(boolean refreshCache) { String myNodeGroupId = parameterService.getNodeGroupId(); long triggerRouterCacheTimeoutInMs = parameterService.getLong(ParameterConstants.CACHE_TIMEOUT_TRIGGER_ROUTER_IN_MS); TriggerRoutersCache cache = triggerRouterCacheByNodeGroupId == null ? null : triggerRouterCacheByNodeGroupId.get(myNodeGroupId); if (cache == null || refreshCache || System.currentTimeMillis() - this.triggerRouterPerNodeCacheTime > triggerRouterCacheTimeoutInMs) { synchronized (cacheLock) { this.triggerRouterPerNodeCacheTime = System.currentTimeMillis(); Map<String, TriggerRoutersCache> newTriggerRouterCacheByNodeGroupId = new HashMap<String, TriggerRoutersCache>(); List<TriggerRouter> triggerRouters = getAllTriggerRoutersForCurrentNode(myNodeGroupId); Map<String, List<TriggerRouter>> triggerRoutersByTriggerId = new HashMap<String, List<TriggerRouter>>(triggerRouters.size()); Map<String, Router> routers = new HashMap<String, Router>(triggerRouters.size()); for (TriggerRouter triggerRouter : triggerRouters) { if (triggerRouter.isEnabled()) { boolean sourceEnabled = groupletService.isSourceEnabled(triggerRouter); if (sourceEnabled) { String triggerId = triggerRouter.getTrigger().getTriggerId(); List<TriggerRouter> list = triggerRoutersByTriggerId.get(triggerId); if (list == null) { list = new ArrayList<TriggerRouter>(); triggerRoutersByTriggerId.put(triggerId, list); } list.add(triggerRouter); routers.put(triggerRouter.getRouter().getRouterId(), triggerRouter.getRouter()); } } } newTriggerRouterCacheByNodeGroupId.put(myNodeGroupId, new TriggerRoutersCache(triggerRoutersByTriggerId, routers)); this.triggerRouterCacheByNodeGroupId = newTriggerRouterCacheByNodeGroupId; cache = triggerRouterCacheByNodeGroupId == null ? null : triggerRouterCacheByNodeGroupId.get(myNodeGroupId); } } return cache; } /** * @see ITriggerRouterService#getActiveRouterByIdForCurrentNode(String, * boolean) */ public Router getActiveRouterByIdForCurrentNode(String routerId, boolean refreshCache) { return getTriggerRoutersCacheForCurrentNode(refreshCache).routersByRouterId.get(routerId); } /** * @see ITriggerRouterService#getRoutersByGroupLink(NodeGroupLink) */ public List<Router> getRoutersByGroupLink(NodeGroupLink link) { return sqlTemplate.query(getSql("select", "selectRoutersColumnList", "selectRouterByNodeGroupLinkWhereSql"), new RouterMapper(true, configurationService.getNodeGroupLinks(false)), link.getSourceNodeGroupId(), link.getTargetNodeGroupId()); } public Trigger getTriggerById(boolean substituteParameters, String triggerId) { return getTriggerById(substituteParameters, triggerId, true); } public Trigger getTriggerById(final boolean substituteParameters, String triggerId, boolean refreshCache) { Trigger trigger = null; KeyedCache<String, Trigger> cache = triggersCache.get(substituteParameters); if (cache == null) { synchronized (cacheLock) { if (cache == null) { cache = new KeyedCache<String, Trigger>(parameterService.getLong(ParameterConstants.CACHE_TIMEOUT_TRIGGER_ROUTER_IN_MS), new IRefreshCache<String, Trigger>() { @Override public LinkedHashMap<String, Trigger> refresh() { List<Trigger> triggers = new ArrayList<Trigger>(getTriggers(substituteParameters)); triggers.addAll(buildTriggersForSymmetricTables(Version.version())); LinkedHashMap<String, Trigger> cache = new LinkedHashMap<String, Trigger>(triggers.size()); for (Trigger t : triggers) { cache.put(t.getTriggerId(), t); } return cache; } }); triggersCache.put(substituteParameters, cache); } } } trigger = cache.find(triggerId, refreshCache); if (trigger == null && !refreshCache) { trigger = cache.find(triggerId, true); } return trigger; } public Router getRouterById(boolean substituteParameters, String routerId) { return getRouterById(substituteParameters, routerId, true); } public Router getRouterById(final boolean substituteParameters, String routerId, boolean refreshCache) { Router router = null; KeyedCache<String, Router> cache = routersCache.get(substituteParameters); if (cache == null) { synchronized (cacheLock) { if (cache == null) { cache = new KeyedCache<String, Router>(parameterService.getLong(ParameterConstants.CACHE_TIMEOUT_TRIGGER_ROUTER_IN_MS), new IRefreshCache<String, Router>() { @Override public LinkedHashMap<String, Router> refresh() { List<Router> routers = getRouters(substituteParameters); LinkedHashMap<String, Router> cache = new LinkedHashMap<String, Router>(routers.size()); for (Router router : routers) { cache.put(router.getRouterId(), router); } return cache; } }); routersCache.put(substituteParameters, cache); } } } router = cache.find(routerId, refreshCache); if (router == null && !refreshCache) { router = cache.find(routerId, true); } return router; } public List<Router> getRouters(boolean substituteParameters) { return sqlTemplate.query(getSql("select ", "selectRoutersColumnList", "selectRoutersSql"), new RouterMapper(substituteParameters, configurationService.getNodeGroupLinks(false))); } private String getTriggerRouterSql(String sql) { return getSql("select ", "selectTriggerRoutersColumnList", "selectTriggerRoutersSql", sql); } public List<TriggerRouter> getTriggerRouters(final boolean substituteParameters, boolean refreshCache) { KeyedCache<Integer, TriggerRouter> cache = triggerRoutersCache.get(substituteParameters); if (cache == null) { synchronized (cacheLock) { if (cache == null) { cache = new KeyedCache<Integer, TriggerRouter>(parameterService.getLong(ParameterConstants.CACHE_TIMEOUT_TRIGGER_ROUTER_IN_MS), new IRefreshCache<Integer, TriggerRouter>() { @Override public LinkedHashMap<Integer, TriggerRouter> refresh() { List<TriggerRouter> triggerRouters = enhanceTriggerRouters(substituteParameters, sqlTemplate.query(getTriggerRouterSql(null), new TriggerRouterMapper())); LinkedHashMap<Integer, TriggerRouter> cache = new LinkedHashMap<Integer, TriggerRouter>(triggerRouters.size()); for (TriggerRouter triggerRouter : triggerRouters) { cache.put(triggerRouter.hashCode(), triggerRouter); } return cache; } }); triggerRoutersCache.put(substituteParameters, cache); } } } return cache.getAll(refreshCache); } public List<TriggerRouter> getAllTriggerRoutersForCurrentNode(String sourceNodeGroupId) { List<TriggerRouter> triggerRouters = enhanceTriggerRouters(true, sqlTemplate.query(getTriggerRouterSql("activeTriggersForSourceNodeGroupSql"), new TriggerRouterMapper(), sourceNodeGroupId)); mergeInConfigurationTablesTriggerRoutersForCurrentNode(sourceNodeGroupId, triggerRouters); return triggerRouters; } public List<TriggerRouter> getAllTriggerRoutersForReloadForCurrentNode(String sourceNodeGroupId, String targetNodeGroupId) { return enhanceTriggerRouters(true, sqlTemplate.query(getTriggerRouterSql("activeTriggersForReloadSql"), new TriggerRouterMapper(), sourceNodeGroupId, targetNodeGroupId, Constants.CHANNEL_CONFIG)); } public TriggerRouter findTriggerRouterById(boolean substituteParameters, String triggerId, String routerId) { List<TriggerRouter> configs = (List<TriggerRouter>) sqlTemplate.query(getTriggerRouterSql("selectTriggerRouterSql"), new TriggerRouterMapper(), triggerId, routerId); if (configs.size() > 0) { TriggerRouter triggerRouter = configs.get(0); triggerRouter.setRouter(getRouterById(substituteParameters, triggerRouter.getRouter().getRouterId())); triggerRouter.setTrigger(getTriggerById(substituteParameters, triggerRouter.getTrigger().getTriggerId())); return triggerRouter; } else { return null; } } private List<TriggerRouter> enhanceTriggerRouters(boolean substituteParameters, List<TriggerRouter> triggerRouters) { HashMap<String, Router> routersById = new HashMap<String, Router>(); for (Router router : getRouters(substituteParameters)) { routersById.put(router.getRouterId().trim().toUpperCase(), router); } HashMap<String, Trigger> triggersById = new HashMap<String, Trigger>(); for (Trigger trigger : getTriggers(substituteParameters)) { triggersById.put(trigger.getTriggerId().trim().toUpperCase(), trigger); } for (TriggerRouter triggerRouter : triggerRouters) { triggerRouter.setTrigger(triggersById.get(triggerRouter.getTrigger().getTriggerId().trim().toUpperCase())); triggerRouter.setRouter(routersById.get(triggerRouter.getRouter().getRouterId().trim().toUpperCase())); } return triggerRouters; } public void insert(TriggerHistory newHistRecord) { newHistRecord.setTriggerHistoryId((int) sequenceService.nextVal(Constants.SEQUENCE_TRIGGER_HIST)); historyMap.put(newHistRecord.getTriggerHistoryId(), newHistRecord); sqlTemplate.update( getSql("insertTriggerHistorySql"), new Object[] { newHistRecord.getTriggerHistoryId(), newHistRecord.getTriggerId(), newHistRecord.getSourceTableName(), newHistRecord.getTableHash(), newHistRecord.getCreateTime(), newHistRecord.getColumnNames(), newHistRecord.getPkColumnNames(), newHistRecord.getLastTriggerBuildReason().getCode(), newHistRecord.getNameForDeleteTrigger(), newHistRecord.getNameForInsertTrigger(), newHistRecord.getNameForUpdateTrigger(), newHistRecord.getSourceSchemaName(), newHistRecord.getSourceCatalogName(), newHistRecord.getTriggerRowHash(), newHistRecord.getTriggerTemplateHash(), newHistRecord.getErrorMessage() }, new int[] { Types.INTEGER, Types.VARCHAR, Types.VARCHAR, Types.BIGINT, Types.TIMESTAMP, Types.VARCHAR, Types.VARCHAR, Types.CHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.BIGINT, Types.BIGINT, Types.VARCHAR }); } @Override public void deleteTriggerRouter(String triggerId, String routerId) { sqlTemplate.update(getSql("deleteTriggerRouterSql"), triggerId, routerId); clearCache(); } public void deleteTriggerRouter(TriggerRouter triggerRouter) { sqlTemplate.update(getSql("deleteTriggerRouterSql"), (Object) triggerRouter.getTrigger().getTriggerId(), triggerRouter.getRouter() .getRouterId()); clearCache(); } public void saveTriggerRouter(TriggerRouter triggerRouter) { saveTriggerRouter(triggerRouter, false); } public void saveTriggerRouter(TriggerRouter triggerRouter, boolean updateTriggerRouterTableOnly) { if (!updateTriggerRouterTableOnly) { saveTrigger(triggerRouter.getTrigger()); saveRouter(triggerRouter.getRouter()); } triggerRouter.setLastUpdateTime(new Date()); if (0 == sqlTemplate .update(getSql("updateTriggerRouterSql"), new Object[] { triggerRouter.getInitialLoadOrder(), triggerRouter.getInitialLoadSelect(), triggerRouter.getInitialLoadDeleteStmt(), triggerRouter.getInitialLoadBatchCount(), triggerRouter.isPingBackEnabled() ? 1 : 0, triggerRouter.getLastUpdateBy(), triggerRouter.getLastUpdateTime(), triggerRouter.isEnabled() ? 1 : 0, triggerRouter.getTrigger().getTriggerId(), triggerRouter.getRouter().getRouterId() }, new int[] { Types.NUMERIC, Types.VARCHAR, Types.VARCHAR, Types.INTEGER, Types.SMALLINT, Types.VARCHAR, Types.TIMESTAMP, Types.SMALLINT, Types.VARCHAR, Types.VARCHAR })) { triggerRouter.setCreateTime(triggerRouter.getLastUpdateTime()); sqlTemplate.update( getSql("insertTriggerRouterSql"), new Object[] { triggerRouter.getInitialLoadOrder(), triggerRouter.getInitialLoadSelect(), triggerRouter.getInitialLoadDeleteStmt(), triggerRouter.getInitialLoadBatchCount(), triggerRouter.isPingBackEnabled() ? 1 : 0, triggerRouter.getCreateTime(), triggerRouter.getLastUpdateBy(), triggerRouter.getLastUpdateTime(), triggerRouter.isEnabled() ? 1 : 0, triggerRouter.getTrigger().getTriggerId(), triggerRouter.getRouter().getRouterId() }, new int[] { Types.NUMERIC, Types.VARCHAR, Types.VARCHAR, Types.INTEGER, Types.SMALLINT, Types.TIMESTAMP, Types.VARCHAR, Types.TIMESTAMP, Types.SMALLINT, Types.VARCHAR, Types.VARCHAR }); } clearCache(); } protected void resetTriggerRouterCacheByNodeGroupId() { triggerRouterPerNodeCacheTime = 0; } public void saveRouter(Router router) { router.setLastUpdateTime(new Date()); router.nullOutBlankFields(); if (0 == sqlTemplate.update( getSql("updateRouterSql"), new Object[] { router.getTargetCatalogName(), router.getTargetSchemaName(), router.getTargetTableName(), router.getNodeGroupLink().getSourceNodeGroupId(), router.getNodeGroupLink().getTargetNodeGroupId(), router.getRouterType(), router.getRouterExpression(), router.isSyncOnUpdate() ? 1 : 0, router.isSyncOnInsert() ? 1 : 0, router.isSyncOnDelete() ? 1 : 0, router.isUseSourceCatalogSchema() ? 1 : 0, router.getLastUpdateBy(), router.getLastUpdateTime(), router.getRouterId() }, new int[] { Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.SMALLINT, Types.SMALLINT, Types.SMALLINT, Types.SMALLINT, Types.VARCHAR, Types.TIMESTAMP, Types.VARCHAR })) { router.setCreateTime(router.getLastUpdateTime()); sqlTemplate.update( getSql("insertRouterSql"), new Object[] { router.getTargetCatalogName(), router.getTargetSchemaName(), router.getTargetTableName(), router.getNodeGroupLink().getSourceNodeGroupId(), router.getNodeGroupLink().getTargetNodeGroupId(), router.getRouterType(), router.getRouterExpression(), router.isSyncOnUpdate() ? 1 : 0, router.isSyncOnInsert() ? 1 : 0, router.isSyncOnDelete() ? 1 : 0, router.isUseSourceCatalogSchema() ? 1 : 0, router.getCreateTime(), router.getLastUpdateBy(), router.getLastUpdateTime(), router.getRouterId() }, new int[] { Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.SMALLINT, Types.SMALLINT, Types.SMALLINT, Types.SMALLINT, Types.TIMESTAMP, Types.VARCHAR, Types.TIMESTAMP, Types.VARCHAR }); } clearCache(); } public boolean isRouterBeingUsed(String routerId) { return sqlTemplate.queryForInt(getSql("countTriggerRoutersByRouterIdSql"), routerId) > 0; } public void deleteRouter(Router router) { if (router != null) { sqlTemplate.update(getSql("deleteRouterSql"), (Object) router.getRouterId()); } } public void saveTrigger(Trigger trigger) { trigger.setLastUpdateTime(new Date()); trigger.nullOutBlankFields(); if (0 == sqlTemplate.update( getSql("updateTriggerSql"), new Object[] { trigger.getSourceCatalogName(), trigger.getSourceSchemaName(), trigger.getSourceTableName(), trigger.getChannelId(), trigger.getReloadChannelId(), trigger.isSyncOnUpdate() ? 1 : 0, trigger.isSyncOnInsert() ? 1 : 0, trigger.isSyncOnDelete() ? 1 : 0, trigger.isSyncOnIncomingBatch() ? 1 : 0, trigger.isUseStreamLobs() ? 1 : 0, trigger.isUseCaptureLobs() ? 1 : 0, trigger.isUseCaptureOldData() ? 1 : 0, trigger.isUseHandleKeyUpdates() ? 1 : 0, trigger.getNameForUpdateTrigger(), trigger.getNameForInsertTrigger(), trigger.getNameForDeleteTrigger(), trigger.getSyncOnUpdateCondition(), trigger.getSyncOnInsertCondition(), trigger.getSyncOnDeleteCondition(), trigger.getCustomOnUpdateText(), trigger.getCustomOnInsertText(), trigger.getCustomOnDeleteText(), trigger.getTxIdExpression(), trigger.getExcludedColumnNames(), trigger.getSyncKeyNames(), trigger.getLastUpdateBy(), trigger.getLastUpdateTime(), trigger.getExternalSelect(), trigger.getChannelExpression(), trigger.getTriggerId() }, new int[] { Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.SMALLINT, Types.SMALLINT, Types.SMALLINT, Types.SMALLINT, Types.SMALLINT, Types.SMALLINT, Types.SMALLINT, Types.SMALLINT, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.TIMESTAMP, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR })) { trigger.setCreateTime(trigger.getLastUpdateTime()); sqlTemplate.update( getSql("insertTriggerSql"), new Object[] { trigger.getSourceCatalogName(), trigger.getSourceSchemaName(), trigger.getSourceTableName(), trigger.getChannelId(), trigger.getReloadChannelId(), trigger.isSyncOnUpdate() ? 1 : 0, trigger.isSyncOnInsert() ? 1 : 0, trigger.isSyncOnDelete() ? 1 : 0, trigger.isSyncOnIncomingBatch() ? 1 : 0, trigger.isUseStreamLobs() ? 1 : 0, trigger.isUseCaptureLobs() ? 1 : 0, trigger.isUseCaptureOldData() ? 1 : 0, trigger.isUseHandleKeyUpdates() ? 1 : 0, trigger.getNameForUpdateTrigger(), trigger.getNameForInsertTrigger(), trigger.getNameForDeleteTrigger(), trigger.getSyncOnUpdateCondition(), trigger.getSyncOnInsertCondition(), trigger.getSyncOnDeleteCondition(), trigger.getCustomOnUpdateText(), trigger.getCustomOnInsertText(), trigger.getCustomOnDeleteText(), trigger.getTxIdExpression(), trigger.getExcludedColumnNames(), trigger.getSyncKeyNames(), trigger.getCreateTime(), trigger.getLastUpdateBy(), trigger.getLastUpdateTime(), trigger.getExternalSelect(), trigger.getChannelExpression(), trigger.getTriggerId() }, new int[] { Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.SMALLINT, Types.SMALLINT, Types.SMALLINT, Types.SMALLINT, Types.SMALLINT, Types.SMALLINT, Types.SMALLINT, Types.SMALLINT, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.TIMESTAMP, Types.VARCHAR, Types.TIMESTAMP, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR }); } clearCache(); } public void syncTriggers() { syncTriggers(false); } public void syncTriggers(boolean force) { syncTriggers((StringBuilder) null, force); } public void syncTriggers(StringBuilder sqlBuffer, boolean force) { if ((parameterService.is(ParameterConstants.AUTO_SYNC_TRIGGERS) || isCalledFromSymmetricAdminTool())) { synchronized (this) { if (clusterService.lock(ClusterConstants.SYNCTRIGGERS)) { try { String additionalMessage = ""; if (isCalledFromSymmetricAdminTool() && !parameterService.is(ParameterConstants.AUTO_SYNC_TRIGGERS)) { additionalMessage = " " + ParameterConstants.AUTO_SYNC_TRIGGERS + " is set to false, but the sync triggers process will run so that needed changes can be written to a file so they can be applied manually. Once all of the triggers have been successfully applied this process should not show triggers being created"; } log.info("Synchronizing triggers{}", additionalMessage); // make sure all tables are freshly read in platform.resetCachedTableModel(); clearCache(); // make sure channels are read from the database configurationService.clearCache(); List<Trigger> triggersForCurrentNode = getTriggersForCurrentNode(true); boolean createTriggersForTables = false; String nodeId = nodeService.findIdentityNodeId(); if (StringUtils.isNotBlank(nodeId)) { NodeSecurity nodeSecurity = nodeService.findNodeSecurity(nodeId); if (nodeSecurity != null && (nodeSecurity.isInitialLoadEnabled() || nodeSecurity.getInitialLoadTime() == null)) { createTriggersForTables = parameterService.is(ParameterConstants.TRIGGER_CREATE_BEFORE_INITIAL_LOAD); if (!createTriggersForTables) { log.info("Trigger creation has been disabled by " + ParameterConstants.TRIGGER_CREATE_BEFORE_INITIAL_LOAD + " because an initial load is in progress or has not yet been requested"); } } else { createTriggersForTables = true; } } if (!createTriggersForTables) { triggersForCurrentNode.clear(); } List<TriggerHistory> activeTriggerHistories = getActiveTriggerHistories(); inactivateTriggers(triggersForCurrentNode, sqlBuffer, activeTriggerHistories); updateOrCreateDatabaseTriggers(triggersForCurrentNode, sqlBuffer, force, true, activeTriggerHistories, true); resetTriggerRouterCacheByNodeGroupId(); } finally { clusterService.unlock(ClusterConstants.SYNCTRIGGERS); log.info("Done synchronizing triggers"); } } else { log.info("Sync triggers was locked by the cluster service"); } } } else { log.info("Not synchronizing triggers. {} is set to false", ParameterConstants.AUTO_SYNC_TRIGGERS); } } public void clearCache() { synchronized (cacheLock) { this.triggerRouterPerNodeCacheTime = 0; List<KeyedCache<?, ?>> caches = new ArrayList<KeyedCache<?,?>>(); caches.addAll(triggerRoutersCache.values()); caches.addAll(triggersCache.values()); caches.addAll(routersCache.values()); for (KeyedCache<?, ?> keyedCache : caches) { keyedCache.clear(); } } } protected Set<String> getTriggerIdsFrom(List<Trigger> triggersThatShouldBeActive) { Set<String> triggerIds = new HashSet<String>(triggersThatShouldBeActive.size()); for (Trigger trigger : triggersThatShouldBeActive) { triggerIds.add(trigger.getTriggerId()); } return triggerIds; } protected Trigger getTriggerFromList(String triggerId, List<Trigger> triggersThatShouldBeActive) { for (Trigger trigger : triggersThatShouldBeActive) { if (trigger.getTriggerId().equals(triggerId)) { return trigger; } } return null; } protected void inactivateTriggers(List<Trigger> triggersThatShouldBeActive, StringBuilder sqlBuffer, List<TriggerHistory> activeTriggerHistories) { boolean ignoreCase = this.parameterService.is(ParameterConstants.DB_METADATA_IGNORE_CASE); Map<String, Set<Table>> tablesByTriggerId = new HashMap<String, Set<Table>>(); for (TriggerHistory history : activeTriggerHistories) { boolean removeTrigger = false; Set<Table> tables = tablesByTriggerId.get(history.getTriggerId()); Trigger trigger = getTriggerFromList(history.getTriggerId(), triggersThatShouldBeActive); if (tables == null && trigger != null) { tables = getTablesForTrigger(trigger, triggersThatShouldBeActive, false); tablesByTriggerId.put(trigger.getTriggerId(), tables); } if (tables == null || tables.size() == 0 || trigger == null) { removeTrigger = true; } else { boolean foundTable = false; for (Table table : tables) { boolean matchesCatalog = isEqual( trigger.isSourceCatalogNameWildCarded() ? table.getCatalog() : trigger.getSourceCatalogName(), history.getSourceCatalogName(), ignoreCase); boolean matchesSchema = isEqual( trigger.isSourceSchemaNameWildCarded() ? table.getSchema() : trigger.getSourceSchemaName(), history.getSourceSchemaName(), ignoreCase); boolean matchesTable = isEqual(trigger.isSourceTableNameWildCarded() ? table.getName() : trigger.getSourceTableName(), history.getSourceTableName(), ignoreCase); foundTable |= matchesCatalog && matchesSchema && matchesTable; } if (!foundTable) { removeTrigger = true; } } if (removeTrigger) { log.info("About to remove triggers for inactivated table: {}", history.getFullyQualifiedSourceTableName()); dropTriggers(history, sqlBuffer); } } } protected boolean isEqual(String one, String two, boolean ignoreCase) { if (ignoreCase) { return StringUtils.equalsIgnoreCase(one, two); } else { return StringUtils.equals(one, two); } } public void dropTriggers(TriggerHistory history) { dropTriggers(history, null); } protected void dropTriggers(TriggerHistory history, StringBuilder sqlBuffer) { if (StringUtils.isNotBlank(history.getNameForInsertTrigger())) { symmetricDialect.removeTrigger(sqlBuffer, history.getSourceCatalogName(), history.getSourceSchemaName(), history.getNameForInsertTrigger(), history.getSourceTableName()); } if (StringUtils.isNotBlank(history.getNameForDeleteTrigger())) { symmetricDialect.removeTrigger(sqlBuffer, history.getSourceCatalogName(), history.getSourceSchemaName(), history.getNameForDeleteTrigger(), history.getSourceTableName()); } if (StringUtils.isNotBlank(history.getNameForUpdateTrigger())) { symmetricDialect.removeTrigger(sqlBuffer, history.getSourceCatalogName(), history.getSourceSchemaName(), history.getNameForUpdateTrigger(), history.getSourceTableName()); } if (parameterService.is(ParameterConstants.AUTO_SYNC_TRIGGERS)) { for (ITriggerCreationListener l : extensionService.getExtensionPointList(ITriggerCreationListener.class)) { l.triggerInactivated(null, history); } } boolean triggerExists = symmetricDialect.doesTriggerExist(history.getSourceCatalogName(), history.getSourceSchemaName(), history.getSourceTableName(), history.getNameForInsertTrigger()); triggerExists |= symmetricDialect.doesTriggerExist(history.getSourceCatalogName(), history.getSourceSchemaName(), history.getSourceTableName(), history.getNameForUpdateTrigger()); triggerExists |= symmetricDialect.doesTriggerExist(history.getSourceCatalogName(), history.getSourceSchemaName(), history.getSourceTableName(), history.getNameForDeleteTrigger()); if (triggerExists) { log.warn( "There are triggers that have been marked as inactive. Please remove triggers represented by trigger_id={} and trigger_hist_id={}", history.getTriggerId(), history.getTriggerHistoryId()); } else { inactivateTriggerHistory(history); } } protected List<TriggerRouter> toList(Collection<List<TriggerRouter>> source) { ArrayList<TriggerRouter> list = new ArrayList<TriggerRouter>(); for (List<TriggerRouter> triggerRouters : source) { for (TriggerRouter triggerRouter : triggerRouters) { list.add(triggerRouter); } } return list; } protected Set<Table> getTablesForTrigger(Trigger trigger, List<Trigger> triggers, boolean useTableCache) { Set<Table> tables = new HashSet<Table>(); try { boolean ignoreCase = this.parameterService.is(ParameterConstants.DB_METADATA_IGNORE_CASE); List<String> catalogNames = new ArrayList<String>(); if (trigger.isSourceCatalogNameWildCarded()) { List<String> all = platform.getDdlReader().getCatalogNames(); for (String catalogName : all) { if (trigger.matchesCatalogName(catalogName, ignoreCase)) { catalogNames.add(catalogName); } } if (catalogNames.size() == 0) { catalogNames.add(null); } } else { if (isBlank(trigger.getSourceCatalogName())) { catalogNames.add(platform.getDefaultCatalog()); } else { catalogNames.add(trigger.getSourceCatalogName()); } } for (String catalogName : catalogNames) { List<String> schemaNames = new ArrayList<String>(); if (trigger.isSourceSchemaNameWildCarded()) { List<String> all = platform.getDdlReader().getSchemaNames(catalogName); for (String schemaName : all) { if (trigger.matchesSchemaName(schemaName, ignoreCase)) { schemaNames.add(schemaName); } } if (schemaNames.size() == 0) { schemaNames.add(null); } } else { if (isBlank(trigger.getSourceSchemaName())) { schemaNames.add(platform.getDefaultSchema()); } else { schemaNames.add(trigger.getSourceSchemaName()); } } for (String schemaName : schemaNames) { if (trigger.isSourceTableNameWildCarded()) { Database database = symmetricDialect.getPlatform().readDatabase(catalogName, schemaName, new String[] { "TABLE" }); Table[] tableArray = database.getTables(); for (Table table : tableArray) { if (trigger.matches(table, catalogName, schemaName, ignoreCase) && !containsExactMatchForSourceTableName(table, triggers, ignoreCase) && !table.getName().toLowerCase().startsWith(tablePrefix)) { tables.add(table); } } } else { Table table = symmetricDialect.getPlatform().getTableFromCache(catalogName, schemaName, trigger.getSourceTableName(), !useTableCache); if (table != null) { tables.add(table); } } } } } catch (Exception ex) { log.error(String.format("Failed to retrieve tables for trigger with id of %s", trigger.getTriggerId()), ex); } return tables; } private boolean containsExactMatchForSourceTableName(Table table, List<Trigger> triggers, boolean ignoreCase) { for (Trigger trigger : triggers) { String sourceCatalogName = trigger.getSourceCatalogName() != null ? trigger.getSourceCatalogName() : platform.getDefaultCatalog(); String sourceSchemaName = trigger.getSourceSchemaName() != null ? trigger.getSourceSchemaName() : platform.getDefaultSchema(); if (trigger.getSourceTableName().equals(table.getName()) && (sourceCatalogName == null || sourceCatalogName.equals(table.getCatalog())) && (sourceSchemaName == null || sourceSchemaName.equals(table.getSchema()))) { return true; } else if (ignoreCase && trigger.getSourceTableName().equalsIgnoreCase(table.getName()) && (sourceCatalogName == null || sourceCatalogName.equalsIgnoreCase(table.getCatalog())) && (sourceSchemaName == null || sourceSchemaName.equalsIgnoreCase(table.getSchema()))) { return true; } } return false; } public void syncTriggers(Table table, boolean force) { boolean ignoreCase = this.parameterService.is(ParameterConstants.DB_METADATA_IGNORE_CASE); /* Re-lookup just in case the table was just altered */ platform.resetCachedTableModel(); table = platform.getTableFromCache(table.getCatalog(), table.getSchema(), table.getName(), true); List<Trigger> triggersForCurrentNode = getTriggersForCurrentNode(true); List<TriggerHistory> activeTriggerHistories = getActiveTriggerHistories(); for (Trigger trigger : triggersForCurrentNode) { if (trigger.matches(table, platform.getDefaultCatalog(), platform.getDefaultSchema(), ignoreCase)) { log.info("Synchronizing triggers for {}", table.getFullyQualifiedTableName()); updateOrCreateDatabaseTriggers(trigger, table, null, force, true, activeTriggerHistories); log.info("Done synchronizing triggers for {}", table.getFullyQualifiedTableName()); } } } protected void updateOrCreateDatabaseTriggers(List<Trigger> triggers, StringBuilder sqlBuffer, boolean force, boolean verifyInDatabase, List<TriggerHistory> activeTriggerHistories, boolean useTableCache) { for (Trigger trigger : triggers) { updateOrCreateDatabaseTrigger(trigger, triggers, sqlBuffer, force, verifyInDatabase, activeTriggerHistories, useTableCache); } } protected void updateOrCreateDatabaseTrigger(Trigger trigger, List<Trigger> triggers, StringBuilder sqlBuffer, boolean force, boolean verifyInDatabase, List<TriggerHistory> activeTriggerHistories, boolean useTableCache) { Set<Table> tables = getTablesForTrigger(trigger, triggers, useTableCache); if (tables != null && tables.size() > 0) { for (Table table : tables) { updateOrCreateDatabaseTriggers(trigger, table, sqlBuffer, force, verifyInDatabase, activeTriggerHistories); } } else { log.warn("Could not find any database tables matching '{}' in the datasource that is configured", trigger.qualifiedSourceTableName()); for (ITriggerCreationListener l : extensionService.getExtensionPointList(ITriggerCreationListener.class)) { l.tableDoesNotExist(trigger); } } } public void syncTrigger(String triggerId, ITriggerCreationListener listener) { clearCache(); Trigger trigger = getTriggerById(true, triggerId, false); StringBuilder sqlBuffer = new StringBuilder(); try { if (listener != null) { extensionService.addExtensionPoint(listener); } List<Trigger> listOfTriggers = getTriggersForCurrentNode(false); List<TriggerHistory> allHistories = getActiveTriggerHistories(); for (TriggerHistory triggerHistory : allHistories) { if (triggerHistory.getTriggerId().equals(triggerId)) { dropTriggers(triggerHistory, sqlBuffer); } } if (listOfTriggers.contains(trigger)) { updateOrCreateDatabaseTrigger(trigger, listOfTriggers, sqlBuffer, true, false, allHistories, false); } } finally { if (listener != null) { extensionService.removeExtensionPoint(listener); } } } protected void updateOrCreateDatabaseTriggers(Trigger trigger, Table table, StringBuilder sqlBuffer, boolean force, boolean verifyInDatabase, List<TriggerHistory> activeTriggerHistories) { TriggerHistory newestHistory = null; TriggerReBuildReason reason = TriggerReBuildReason.NEW_TRIGGERS; String errorMessage = null; if (verifyInDatabase) { Channel channel = configurationService.getChannel(trigger.getChannelId()); if (channel == null) { errorMessage = String .format("Trigger %s had an unrecognized channel_id of '%s'. Please check to make sure the channel exists. Creating trigger on the '%s' channel", trigger.getTriggerId(), trigger.getChannelId(), Constants.CHANNEL_DEFAULT); log.error(errorMessage); trigger.setChannelId(Constants.CHANNEL_DEFAULT); } } try { boolean foundPk = false; Column[] columns = trigger.filterExcludedColumns(table.getColumns()); for (Column column : columns) { foundPk |= column.isPrimaryKey(); } if (!foundPk) { table = platform.makeAllColumnsPrimaryKeys(table); } TriggerHistory latestHistoryBeforeRebuild = getNewestTriggerHistoryForTrigger(trigger.getTriggerId(), trigger.isSourceCatalogNameWildCarded() ? table.getCatalog() : trigger.getSourceCatalogName(), trigger.isSourceSchemaNameWildCarded() ? table.getSchema() : trigger.getSourceSchemaName(), trigger.isSourceTableNameWildCarded() ? table.getName() : trigger.getSourceTableName()); boolean forceRebuildOfTriggers = false; if (latestHistoryBeforeRebuild == null) { reason = TriggerReBuildReason.NEW_TRIGGERS; forceRebuildOfTriggers = true; } else if (table.calculateTableHashcode() != latestHistoryBeforeRebuild.getTableHash()) { reason = TriggerReBuildReason.TABLE_SCHEMA_CHANGED; forceRebuildOfTriggers = true; } else if (trigger.hasChangedSinceLastTriggerBuild(latestHistoryBeforeRebuild.getCreateTime()) || trigger.toHashedValue() != latestHistoryBeforeRebuild.getTriggerRowHash()) { reason = TriggerReBuildReason.TABLE_SYNC_CONFIGURATION_CHANGED; forceRebuildOfTriggers = true; } else if (symmetricDialect.getTriggerTemplate().toHashedValue() != latestHistoryBeforeRebuild.getTriggerTemplateHash()) { reason = TriggerReBuildReason.TRIGGER_TEMPLATE_CHANGED; forceRebuildOfTriggers = true; } else if (force) { reason = TriggerReBuildReason.FORCED; forceRebuildOfTriggers = true; } boolean supportsTriggers = symmetricDialect.getPlatform().getDatabaseInfo().isTriggersSupported(); newestHistory = rebuildTriggerIfNecessary(sqlBuffer, forceRebuildOfTriggers, trigger, DataEventType.INSERT, reason, latestHistoryBeforeRebuild, null, trigger.isSyncOnInsert() && supportsTriggers, table, activeTriggerHistories); newestHistory = rebuildTriggerIfNecessary(sqlBuffer, forceRebuildOfTriggers, trigger, DataEventType.UPDATE, reason, latestHistoryBeforeRebuild, newestHistory, trigger.isSyncOnUpdate() && supportsTriggers, table, activeTriggerHistories); newestHistory = rebuildTriggerIfNecessary(sqlBuffer, forceRebuildOfTriggers, trigger, DataEventType.DELETE, reason, latestHistoryBeforeRebuild, newestHistory, trigger.isSyncOnDelete() && supportsTriggers, table, activeTriggerHistories); if (latestHistoryBeforeRebuild != null && newestHistory != null) { inactivateTriggerHistory(latestHistoryBeforeRebuild); } if (newestHistory != null) { activeTriggerHistories.add(newestHistory); newestHistory.setErrorMessage(errorMessage); if (parameterService.is(ParameterConstants.AUTO_SYNC_TRIGGERS)) { for (ITriggerCreationListener l : extensionService.getExtensionPointList(ITriggerCreationListener.class)) { l.triggerCreated(trigger, newestHistory); } } } } catch (Exception ex) { log.error(String.format("Failed to create triggers for %s", trigger.qualifiedSourceTableName()), ex); if (newestHistory != null) { // Make sure all the triggers are removed from the // table symmetricDialect.removeTrigger(null, trigger.getSourceCatalogName(), trigger.getSourceSchemaName(), newestHistory.getNameForInsertTrigger(), trigger.getSourceTableName()); symmetricDialect.removeTrigger(null, trigger.getSourceCatalogName(), trigger.getSourceSchemaName(), newestHistory.getNameForUpdateTrigger(), trigger.getSourceTableName()); symmetricDialect.removeTrigger(null, trigger.getSourceCatalogName(), trigger.getSourceSchemaName(), newestHistory.getNameForDeleteTrigger(), trigger.getSourceTableName()); } for (ITriggerCreationListener l : extensionService.getExtensionPointList(ITriggerCreationListener.class)) { l.triggerFailed(trigger, ex); } } } protected TriggerHistory rebuildTriggerIfNecessary(StringBuilder sqlBuffer, boolean forceRebuild, Trigger trigger, DataEventType dmlType, TriggerReBuildReason reason, TriggerHistory oldhist, TriggerHistory hist, boolean triggerIsActive, Table table, List<TriggerHistory> activeTriggerHistories) { boolean triggerExists = false; boolean triggerRemoved = false; TriggerHistory newTriggerHist = new TriggerHistory(table, trigger, symmetricDialect.getTriggerTemplate(), reason); int maxTriggerNameLength = symmetricDialect.getMaxTriggerNameLength(); if (trigger.isSyncOnInsert()) { newTriggerHist.setNameForInsertTrigger(getTriggerName(DataEventType.INSERT, maxTriggerNameLength, trigger, table, activeTriggerHistories).toUpperCase()); } if (trigger.isSyncOnUpdate()) { newTriggerHist.setNameForUpdateTrigger(getTriggerName(DataEventType.UPDATE, maxTriggerNameLength, trigger, table, activeTriggerHistories).toUpperCase()); } if (trigger.isSyncOnDelete()) { newTriggerHist.setNameForDeleteTrigger(getTriggerName(DataEventType.DELETE, maxTriggerNameLength, trigger, table, activeTriggerHistories).toUpperCase()); } String oldTriggerName = null; String oldSourceSchema = null; String oldCatalogName = null; if (oldhist != null) { oldTriggerName = oldhist.getTriggerNameForDmlType(dmlType); oldSourceSchema = oldhist.getSourceSchemaName(); oldCatalogName = oldhist.getSourceCatalogName(); triggerExists = symmetricDialect.doesTriggerExist(oldCatalogName, oldSourceSchema, oldhist.getSourceTableName(), oldTriggerName); } else { // We had no trigger_hist row, lets validate that the trigger as // defined in the trigger row data does not exist as well. oldTriggerName = newTriggerHist.getTriggerNameForDmlType(dmlType); oldSourceSchema = table.getSchema(); oldCatalogName = table.getCatalog(); if (StringUtils.isNotBlank(oldTriggerName)) { triggerExists = symmetricDialect.doesTriggerExist(oldCatalogName, oldSourceSchema, table.getName(), oldTriggerName); } } if (!triggerExists && forceRebuild) { reason = TriggerReBuildReason.TRIGGERS_MISSING; } if ((forceRebuild || !triggerIsActive) && triggerExists) { symmetricDialect.removeTrigger(sqlBuffer, oldCatalogName, oldSourceSchema, oldTriggerName, trigger.isSourceTableNameWildCarded() ? table.getName() : trigger.getSourceTableName()); triggerExists = false; triggerRemoved = true; } boolean isDeadTrigger = !trigger.isSyncOnInsert() && !trigger.isSyncOnUpdate() && !trigger.isSyncOnDelete(); if (hist == null && (oldhist == null || (!triggerExists && triggerIsActive) || (isDeadTrigger && forceRebuild))) { insert(newTriggerHist); hist = getNewestTriggerHistoryForTrigger(trigger.getTriggerId(), trigger.isSourceCatalogNameWildCarded() ? table.getCatalog() : trigger.getSourceCatalogName(), trigger.isSourceSchemaNameWildCarded() ? table.getSchema() : trigger.getSourceSchemaName(), trigger.isSourceTableNameWildCarded() ? table.getName() : trigger.getSourceTableName()); } try { if (!triggerExists && triggerIsActive) { symmetricDialect.createTrigger(sqlBuffer, dmlType, trigger, hist, configurationService.getChannel(trigger.getChannelId()), tablePrefix, table); if (triggerRemoved) { statisticManager.incrementTriggersRebuiltCount(1); } else { statisticManager.incrementTriggersCreatedCount(1); } } else if (triggerRemoved) { statisticManager.incrementTriggersRemovedCount(1); } } catch (RuntimeException ex) { if (hist != null) { log.info("Cleaning up trigger hist row of {} after failing to create the associated trigger", hist.getTriggerHistoryId()); hist.setErrorMessage(ex.getMessage()); inactivateTriggerHistory(hist); } throw ex; } return hist; } protected static String replaceCharsToShortenName(String triggerName) { return triggerName.replaceAll("[^a-zA-Z0-9_]|[a|e|i|o|u|A|E|I|O|U]", ""); } protected String getTriggerName(DataEventType dml, int maxTriggerNameLength, Trigger trigger, Table table, List<TriggerHistory> activeTriggerHistories) { String triggerName = null; switch (dml) { case INSERT: if (!StringUtils.isBlank(trigger.getNameForInsertTrigger())) { triggerName = trigger.getNameForInsertTrigger(); } break; case UPDATE: if (!StringUtils.isBlank(trigger.getNameForUpdateTrigger())) { triggerName = trigger.getNameForUpdateTrigger(); } break; case DELETE: if (!StringUtils.isBlank(trigger.getNameForDeleteTrigger())) { triggerName = trigger.getNameForDeleteTrigger(); } break; default: break; } if (StringUtils.isBlank(triggerName)) { String triggerPrefix1 = tablePrefix + "_"; String triggerSuffix1 = "on_" + dml.getCode().toLowerCase() + "_for_"; String triggerSuffix2 = replaceCharsToShortenName(trigger.getTriggerId()); if (trigger.isSourceTableNameWildCarded()) { triggerSuffix2 = replaceCharsToShortenName(table.getName()); } String triggerSuffix3 = replaceCharsToShortenName("_" + parameterService.getNodeGroupId()); triggerName = triggerPrefix1 + triggerSuffix1 + triggerSuffix2 + triggerSuffix3; // use the node group id as part of the trigger if we can because it // helps uniquely identify the trigger in embedded databases. In // hsqldb we choose the // correct connection based on the presence of a table that is named // for the trigger. // If the trigger isn't unique across all databases, then we can // choose the wrong connection. if (triggerName.length() > maxTriggerNameLength && maxTriggerNameLength > 0) { triggerName = triggerPrefix1 + triggerSuffix1 + triggerSuffix2; } } triggerName = triggerName.toUpperCase(); if (triggerName.length() > maxTriggerNameLength && maxTriggerNameLength > 0) { triggerName = triggerName.substring(0, maxTriggerNameLength - 1); log.debug( "We just truncated the trigger name for the {} trigger id={}. You might want to consider manually providing a name for the trigger that is less than {} characters long", new Object[] { dml.name().toLowerCase(), trigger.getTriggerId(), maxTriggerNameLength }); } int duplicateCount = 0; while (isTriggerNameInUse(activeTriggerHistories, trigger.getTriggerId(), triggerName)) { duplicateCount++; String duplicateSuffix = Integer.toString(duplicateCount); if (triggerName.length() + duplicateSuffix.length() > maxTriggerNameLength) { triggerName = triggerName.substring(0, triggerName.length() - duplicateSuffix.length()) + duplicateSuffix; } else { triggerName = triggerName + duplicateSuffix; } } return triggerName; } class TriggerHistoryMapper implements ISqlRowMapper<TriggerHistory> { Map<Long, TriggerHistory> retMap = null; TriggerHistoryMapper() { } TriggerHistoryMapper(Map<Long, TriggerHistory> map) { this.retMap = map; } public TriggerHistory mapRow(Row rs) { TriggerHistory hist = new TriggerHistory(); hist.setTriggerHistoryId(rs.getInt("trigger_hist_id")); hist.setTriggerId(rs.getString("trigger_id")); hist.setSourceTableName(rs.getString("source_table_name")); hist.setTableHash(rs.getInt("table_hash")); hist.setCreateTime(rs.getDateTime("create_time")); hist.setPkColumnNames(rs.getString("pk_column_names")); hist.setColumnNames(rs.getString("column_names")); hist.setLastTriggerBuildReason(TriggerReBuildReason.fromCode(rs.getString("last_trigger_build_reason"))); hist.setNameForDeleteTrigger(rs.getString("name_for_delete_trigger")); hist.setNameForInsertTrigger(rs.getString("name_for_insert_trigger")); hist.setNameForUpdateTrigger(rs.getString("name_for_update_trigger")); hist.setSourceSchemaName(rs.getString("source_schema_name")); hist.setSourceCatalogName(rs.getString("source_catalog_name")); hist.setTriggerRowHash(rs.getLong("trigger_row_hash")); hist.setTriggerTemplateHash(rs.getLong("trigger_template_hash")); hist.setErrorMessage(rs.getString("error_message")); if (this.retMap != null) { this.retMap.put((long) hist.getTriggerHistoryId(), hist); } return hist; } } class RouterMapper implements ISqlRowMapper<Router> { List<NodeGroupLink> nodeGroupLinks; boolean substituteParameters; public RouterMapper(boolean substituteParameters, List<NodeGroupLink> nodeGroupLinks) { this.nodeGroupLinks = nodeGroupLinks; this.substituteParameters = substituteParameters; } private NodeGroupLink getNodeGroupLink(String sourceNodeGroupId, String targetNodeGroupId) { for (NodeGroupLink nodeGroupLink : nodeGroupLinks) { if (nodeGroupLink.getSourceNodeGroupId().equals(sourceNodeGroupId) && nodeGroupLink.getTargetNodeGroupId().equals(targetNodeGroupId)) { return nodeGroupLink; } } return null; } public Router mapRow(Row rs) { Router router = new Router(); router.setSyncOnInsert(rs.getBoolean("r_sync_on_insert")); router.setSyncOnUpdate(rs.getBoolean("r_sync_on_update")); router.setSyncOnDelete(rs.getBoolean("r_sync_on_delete")); router.setTargetCatalogName(rs.getString("target_catalog_name")); router.setNodeGroupLink(getNodeGroupLink(rs.getString("source_node_group_id"), rs.getString("target_node_group_id"))); router.setTargetSchemaName(rs.getString("target_schema_name")); router.setTargetTableName(rs.getString("target_table_name")); String condition = rs.getString("router_expression"); if (!StringUtils.isBlank(condition)) { router.setRouterExpression(condition); } router.setRouterType(rs.getString("router_type")); router.setRouterId(rs.getString("router_id")); router.setUseSourceCatalogSchema(rs.getBoolean("use_source_catalog_schema")); router.setCreateTime(rs.getDateTime("r_create_time")); router.setLastUpdateTime(rs.getDateTime("r_last_update_time")); router.setLastUpdateBy(rs.getString("r_last_update_by")); if (substituteParameters) { TypedProperties parameters = parameterService.getAllParameters(); router.setTargetCatalogName(FormatUtils.replaceTokens(router.getTargetCatalogName(), parameters, true)); router.setTargetSchemaName(FormatUtils.replaceTokens(router.getTargetSchemaName(), parameters, true)); router.setTargetTableName(FormatUtils.replaceTokens(router.getTargetTableName(), parameters, true)); } return router; } } class TriggerMapper implements ISqlRowMapper<Trigger> { boolean substituteParameters; public TriggerMapper(boolean substituteParameters) { this.substituteParameters = substituteParameters; } public Trigger mapRow(Row rs) { Trigger trigger = new Trigger(); trigger.setTriggerId(rs.getString("trigger_id")); trigger.setChannelId(rs.getString("channel_id")); trigger.setReloadChannelId(rs.getString("reload_channel_id")); trigger.setSourceTableName(rs.getString("source_table_name")); trigger.setSyncOnInsert(rs.getBoolean("sync_on_insert")); trigger.setSyncOnUpdate(rs.getBoolean("sync_on_update")); trigger.setSyncOnDelete(rs.getBoolean("sync_on_delete")); trigger.setSyncOnIncomingBatch(rs.getBoolean("sync_on_incoming_batch")); trigger.setUseStreamLobs(rs.getBoolean("use_stream_lobs")); trigger.setUseCaptureLobs(rs.getBoolean("use_capture_lobs")); trigger.setUseCaptureOldData(rs.getBoolean("use_capture_old_data")); trigger.setUseHandleKeyUpdates(rs.getBoolean("use_handle_key_updates")); trigger.setNameForDeleteTrigger(rs.getString("name_for_delete_trigger")); trigger.setNameForInsertTrigger(rs.getString("name_for_insert_trigger")); trigger.setNameForUpdateTrigger(rs.getString("name_for_update_trigger")); String schema = rs.getString("source_schema_name"); trigger.setSourceSchemaName(schema); String catalog = rs.getString("source_catalog_name"); trigger.setSourceCatalogName(catalog); String condition = rs.getString("sync_on_insert_condition"); if (!StringUtils.isBlank(condition)) { trigger.setSyncOnInsertCondition(condition); } condition = rs.getString("sync_on_update_condition"); if (!StringUtils.isBlank(condition)) { trigger.setSyncOnUpdateCondition(condition); } condition = rs.getString("sync_on_delete_condition"); if (!StringUtils.isBlank(condition)) { trigger.setSyncOnDeleteCondition(condition); } String text = rs.getString("custom_on_insert_text"); if (!StringUtils.isBlank(text)) { trigger.setCustomOnInsertText(text); } text = rs.getString("custom_on_update_text"); if (!StringUtils.isBlank(text)) { trigger.setCustomOnUpdateText(text); } text = rs.getString("custom_on_delete_text"); if (!StringUtils.isBlank(text)) { trigger.setCustomOnDeleteText(text); } condition = rs.getString("external_select"); if (!StringUtils.isBlank(condition)) { trigger.setExternalSelect(condition); } trigger.setChannelExpression(rs.getString("channel_expression")); trigger.setTxIdExpression(rs.getString("tx_id_expression")); trigger.setCreateTime(rs.getDateTime("t_create_time")); trigger.setLastUpdateTime(rs.getDateTime("t_last_update_time")); trigger.setLastUpdateBy(rs.getString("t_last_update_by")); trigger.setExcludedColumnNames(rs.getString("excluded_column_names")); trigger.setSyncKeyNames(rs.getString("sync_key_names")); if (substituteParameters) { TypedProperties parameters = parameterService.getAllParameters(); trigger.setSourceCatalogName(FormatUtils.replaceTokens(trigger.getSourceCatalogName(), parameters, true)); trigger.setSourceSchemaName(FormatUtils.replaceTokens(trigger.getSourceSchemaName(), parameters, true)); trigger.setSourceTableName(FormatUtils.replaceTokens(trigger.getSourceTableName(), parameters, true)); } return trigger; } } class TriggerRouterMapper implements ISqlRowMapper<TriggerRouter> { public TriggerRouterMapper() { } public TriggerRouter mapRow(Row rs) { TriggerRouter triggerRouter = new TriggerRouter(); Trigger trigger = new Trigger(); trigger.setTriggerId(rs.getString("trigger_id")); triggerRouter.setTrigger(trigger); Router router = new Router(); router.setRouterId(rs.getString("router_id")); triggerRouter.setRouter(router); triggerRouter.setCreateTime(rs.getDateTime("create_time")); triggerRouter.setLastUpdateTime(rs.getDateTime("last_update_time")); triggerRouter.setLastUpdateBy(rs.getString("last_update_by")); triggerRouter.setInitialLoadOrder(rs.getInt("initial_load_order")); triggerRouter.setInitialLoadSelect(rs.getString("initial_load_select")); triggerRouter.setInitialLoadBatchCount(rs.getInt("initial_load_batch_count")); triggerRouter.setEnabled(rs.getBoolean("enabled")); triggerRouter.setInitialLoadDeleteStmt(rs.getString("initial_load_delete_stmt")); triggerRouter.setPingBackEnabled(rs.getBoolean("ping_back_enabled")); return triggerRouter; } } public void addExtraConfigTable(String table) { if (this.extraConfigTables == null) { this.extraConfigTables = new ArrayList<String>(); } this.extraConfigTables.add(table); } public Map<Trigger, Exception> getFailedTriggers() { return this.failureListener.getFailures(); } public TriggerHistory findTriggerHistoryForGenericSync() { String triggerTableName = TableConstants.getTableName(tablePrefix, TableConstants.SYM_NODE); TriggerHistory history = findTriggerHistory(null, null, triggerTableName.toUpperCase()); if (history == null) { history = findTriggerHistory(null, null, triggerTableName); } return history; } public Map<Integer, List<TriggerRouter>> fillTriggerRoutersByHistIdAndSortHist(String sourceNodeGroupId, String targetNodeGroupId, List<TriggerHistory> triggerHistories) { List<TriggerRouter> triggerRouters = new ArrayList<TriggerRouter>(getAllTriggerRoutersForReloadForCurrentNode(sourceNodeGroupId, targetNodeGroupId)); final Map<Integer, List<TriggerRouter>> triggerRoutersByHistoryId = new HashMap<Integer, List<TriggerRouter>>(triggerHistories.size()); for (TriggerHistory triggerHistory : triggerHistories) { List<TriggerRouter> triggerRoutersForTriggerHistory = new ArrayList<TriggerRouter>(); triggerRoutersByHistoryId.put(triggerHistory.getTriggerHistoryId(), triggerRoutersForTriggerHistory); String triggerId = triggerHistory.getTriggerId(); for (TriggerRouter triggerRouter : triggerRouters) { if (triggerRouter.getTrigger().getTriggerId().equals(triggerId)) { triggerRoutersForTriggerHistory.add(triggerRouter); } } } final List<Table> sortedTables = getSortedTablesFor(triggerHistories); Comparator<TriggerHistory> comparator = new Comparator<TriggerHistory>() { public int compare(TriggerHistory o1, TriggerHistory o2) { List<TriggerRouter> triggerRoutersForTriggerHist1 = triggerRoutersByHistoryId.get(o1.getTriggerHistoryId()); int intialLoadOrder1 = 0; for (TriggerRouter triggerRouter1 : triggerRoutersForTriggerHist1) { if (triggerRouter1.getInitialLoadOrder() > intialLoadOrder1) { intialLoadOrder1 = triggerRouter1.getInitialLoadOrder(); } } List<TriggerRouter> triggerRoutersForTriggerHist2 = triggerRoutersByHistoryId.get(o2.getTriggerHistoryId()); int intialLoadOrder2 = 0; for (TriggerRouter triggerRouter2 : triggerRoutersForTriggerHist2) { if (triggerRouter2.getInitialLoadOrder() > intialLoadOrder2) { intialLoadOrder2 = triggerRouter2.getInitialLoadOrder(); } } if (intialLoadOrder1 < intialLoadOrder2) { return -1; } else if (intialLoadOrder1 > intialLoadOrder2) { return 1; } Table table1 = platform .getTableFromCache(o1.getSourceCatalogName(), o1.getSourceSchemaName(), o1.getSourceTableName(), false); Table table2 = platform .getTableFromCache(o2.getSourceCatalogName(), o2.getSourceSchemaName(), o2.getSourceTableName(), false); return new Integer(sortedTables.indexOf(table1)).compareTo(new Integer(sortedTables.indexOf(table2))); }; }; Collections.sort(triggerHistories, comparator); return triggerRoutersByHistoryId; } protected List<Table> getSortedTablesFor(List<TriggerHistory> histories) { List<Table> tables = new ArrayList<Table>(histories.size()); for (TriggerHistory triggerHistory : histories) { Table table = platform.getTableFromCache(triggerHistory.getSourceCatalogName(), triggerHistory.getSourceSchemaName(), triggerHistory.getSourceTableName(), false); if (table != null) { tables.add(table); } } return Database.sortByForeignKeys(tables); } class TriggerRoutersCache { public TriggerRoutersCache(Map<String, List<TriggerRouter>> triggerRoutersByTriggerId, Map<String, Router> routersByRouterId) { this.triggerRoutersByTriggerId = triggerRoutersByTriggerId; this.routersByRouterId = routersByRouterId; } Map<String, List<TriggerRouter>> triggerRoutersByTriggerId = new HashMap<String, List<TriggerRouter>>(); Map<String, Router> routersByRouterId = new HashMap<String, Router>(); } }