All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jumpmind.symmetric.service.impl.TriggerRouterService Maven / Gradle / Ivy

Go to download

SymmetricDS is an open source database synchronization solution. It is platform-independent, web-enabled, and database-agnostic. SymmetricDS was first built to replicate changes between 'retail store' databases and ad centralized 'corporate' database.

The newest version!
/*
 * Licensed to JumpMind Inc under one or more contributor 
 * license agreements.  See the NOTICE file distributed
 * with this work for additional information regarding 
 * copyright ownership.  JumpMind Inc licenses this file
 * to you under the GNU Lesser General Public License (the
 * "License"); you may not use this file except in compliance
 * with the License. 
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, see           
 * .
 * 
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License. 
 */


package org.jumpmind.symmetric.service.impl;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.TreeSet;

import org.apache.commons.lang.StringUtils;
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.config.TriggerSelector;
import org.jumpmind.symmetric.ddl.model.Table;
import org.jumpmind.symmetric.model.DataEventType;
import org.jumpmind.symmetric.model.NodeGroupLink;
import org.jumpmind.symmetric.model.NodeGroupLinkAction;
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.service.ClusterConstants;
import org.jumpmind.symmetric.service.IClusterService;
import org.jumpmind.symmetric.service.IConfigurationService;
import org.jumpmind.symmetric.service.ITriggerRouterService;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.jdbc.core.RowMapper;

/**
 * 
 */
public class TriggerRouterService extends AbstractService implements ITriggerRouterService {

    private Map> rootConfigChannelTableNames;

    private Map> rootConfigChannelInitialLoadSelect;

    private IClusterService clusterService;

    private IConfigurationService configurationService;

    private Map triggerRouterCacheByNodeGroupId = new HashMap();
    
    private long triggerRouterCacheTime;

    private List triggerCreationListeners;

    private TriggerFailureListener failureListener = new TriggerFailureListener();

    /**
     * Cache the history for performance. History never changes and does not
     * grow big so this should be OK.
     */
    private HashMap historyMap = new HashMap();

    public TriggerRouterService() {
        this.addTriggerCreationListeners(this.failureListener);
    }

    public void inactivateTriggerHistory(TriggerHistory history) {
        jdbcTemplate.update(getSql("inactivateTriggerHistorySql"), new Object[] { history
                .getTriggerHistoryId() });
    }

    public Map getHistoryRecords() {
        final Map retMap = new HashMap();
        jdbcTemplate.query(getSql("allTriggerHistSql"), new TriggerHistoryMapper(retMap));
        return retMap;
    }

    protected boolean isTriggerNameInUse(String triggerId, String triggerName) {
        return jdbcTemplate.queryForInt(getSql("selectTriggerNameInUseSql"), triggerName,
                triggerName, triggerName, triggerId) > 0;
    }

    public TriggerHistory findTriggerHistory(String sourceTableName) {
        final Map retMap = new HashMap();
        jdbcTemplate.query(getSql("allTriggerHistSql","triggerHistBySourceTableWhereSql"), new Object[] { sourceTableName },
                new int[] { Types.VARCHAR }, new TriggerHistoryMapper(retMap));
        if (retMap.size() > 0) {
            return retMap.values().iterator().next();
        } else {
            return null;
        }
    }

    public TriggerHistory getTriggerHistory(int histId) {
        TriggerHistory history = historyMap.get(histId);
        if (history == null && histId >= 0) {
            try {
                history = (TriggerHistory) jdbcTemplate.queryForObject(getSql("triggerHistSql"),
                        new Object[] { histId }, new TriggerHistoryMapper());
                historyMap.put(histId, history);
            } catch (EmptyResultDataAccessException ex) {
            }
        }
        return history;
    }

    public TriggerHistory getNewestTriggerHistoryForTrigger(String triggerId) {
        try {
            return (TriggerHistory) jdbcTemplate.queryForObject(getSql("latestTriggerHistSql"),
                    new Object[] { triggerId }, new TriggerHistoryMapper());
        } catch (EmptyResultDataAccessException ex) {
            return null;
        }
    }

    /**
     * Get a list of trigger histories that need to be inactivated.
     */
    protected List getActiveTriggerHistoriesForInactivation() {
        List hists = jdbcTemplate.query(getSql("allTriggerHistSql",
                "inactiveTriggerHistoryWhereSql"), new Object[] {parameterService.getNodeGroupId()}, new TriggerHistoryMapper());
        for (Iterator iterator = hists.iterator(); iterator.hasNext();) {
            TriggerHistory triggerHistory = iterator.next();
            if (triggerHistory.getSourceTableName().toLowerCase().startsWith(tablePrefix + "_")) {
                iterator.remove();
            }
        }
        return hists;
    }
    
    private String getNewestVersionOfRootConfigChannelTableNames() {
        TreeSet ordered = new TreeSet(rootConfigChannelTableNames.keySet());
        return ordered.last();
    }
    
    private String getMajorVersion(String version) {
        String majorVersion = Integer.toString(Version.parseVersion(version)[0]);        
        List tables = rootConfigChannelTableNames.get(majorVersion);
        if (tables == null) {
            String newestVersion = getNewestVersionOfRootConfigChannelTableNames();
            log.warn("TriggersDefaultVersionWarning", newestVersion, majorVersion);
            majorVersion = newestVersion;
        }
        return majorVersion;
    }

    public List getTriggerRoutersForRegistration(String version,
            String sourceGroupId, String targetGroupId) {
        int initialLoadOrder = 1;
        String majorVersion = getMajorVersion(version);
        List tables = rootConfigChannelTableNames.get(majorVersion);
        List triggers = new ArrayList(tables.size());
        for (int j = 0; j < tables.size(); j++) {
            String tableName = tables.get(j);
            boolean syncChanges = !TableConstants.getNodeTablesAsSet(tablePrefix).contains(
                    tableName);
            TriggerRouter trigger = buildRegistrationTriggerRouter(version, tableName, syncChanges,
                    sourceGroupId, targetGroupId);
            trigger.setInitialLoadOrder(initialLoadOrder++);
            String initialLoadSelect = rootConfigChannelInitialLoadSelect.get(majorVersion).get(
                    tableName);
            trigger.setInitialLoadSelect(initialLoadSelect);
            if (tableName.equalsIgnoreCase(TableConstants.getTableName(tablePrefix,
                    TableConstants.SYM_TRIGGER))) {
                trigger.getRouter().setRouterType("trigger");
            }
            triggers.add(trigger);
        }
        return triggers;
    }

    protected TriggerRouter buildRegistrationTriggerRouter(String version, String tableName,
            boolean syncChanges, String sourceGroupId, String targetGroupId) {
        String majorVersion = getMajorVersion(version);
        boolean autoSyncConfig = parameterService.is(ParameterConstants.AUTO_SYNC_CONFIGURATION);

        TriggerRouter triggerRouter = new TriggerRouter();
        triggerRouter.setInitialLoadSelect(rootConfigChannelInitialLoadSelect.get(majorVersion)
                .get(tableName));

        Trigger trigger = triggerRouter.getTrigger();
        trigger.setTriggerId(Integer.toString(Math.abs(tableName.hashCode())));
        trigger.setSyncOnDelete(syncChanges && autoSyncConfig);
        trigger.setSyncOnInsert(syncChanges && autoSyncConfig);
        trigger.setSyncOnUpdate(syncChanges && autoSyncConfig);
        trigger.setSyncOnIncomingBatch(true);
        trigger.setSourceTableName(tableName);
        trigger.setChannelId(Constants.CHANNEL_CONFIG);

        Router router = triggerRouter.getRouter();
        router.setSourceNodeGroupId(sourceGroupId);
        router.setTargetNodeGroupId(targetGroupId);

        // 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()));
        router.setLastUpdateTime(trigger.getLastUpdateTime());
        triggerRouter.setLastUpdateTime(trigger.getLastUpdateTime());

        return triggerRouter;
    }

    private String getTriggerRouterSql(String sql) {
        return getSql("selectTriggerRouterPrefixSql", sql);
    }
    
    public TriggerRouter getTriggerRouterForTableForCurrentNode(String catalogName, String schemaName, String tableName, boolean refreshCache) {
        return getTriggerRouterForTableForCurrentNode(null, catalogName, schemaName, tableName, refreshCache);
    }
    
    public TriggerRouter getTriggerRouterForTableForCurrentNode(NodeGroupLink link, String catalogName, String schemaName, String tableName, boolean refreshCache) {
        TriggerRoutersCache cache = getTriggerRoutersCacheForCurrentNode(refreshCache);
        Collection> triggerRouters = cache.triggerRoutersByTriggerId.values();
        for (List list : triggerRouters) {
            for (TriggerRouter triggerRouter : list) {                
                if (isMatch(link, triggerRouter) && 
                        isMatch(catalogName, schemaName, tableName, triggerRouter.getTrigger())) {
                    return triggerRouter;
                }
            }
        }
        return null;
    }
    
    protected boolean isMatch(NodeGroupLink link, TriggerRouter router) {
        if (link != null && router != null && router.getRouter() != null) {
            return link.getSourceNodeGroupId().equals(router.getRouter().getSourceNodeGroupId()) && link.getTargetNodeGroupId().equals(router.getRouter().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 getConfigurationTablesTriggerRoutersForCurrentNode(
            String sourceNodeGroupId) {
        List triggers = new ArrayList();
        List links = configurationService.getGroupLinksFor(sourceNodeGroupId);
        for (NodeGroupLink nodeGroupLink : links) {
            if (nodeGroupLink.getDataEventAction().equals(NodeGroupLinkAction.W)) {
                triggers.addAll(getTriggerRoutersForRegistration(Version.version(), nodeGroupLink
                        .getSourceNodeGroupId(), nodeGroupLink.getTargetNodeGroupId()));
            } else if (nodeGroupLink.getDataEventAction().equals(NodeGroupLinkAction.P)) {

                triggers.add(buildRegistrationTriggerRouter(Version.version(), TableConstants
                        .getTableName(tablePrefix, TableConstants.SYM_NODE), false, nodeGroupLink
                        .getSourceNodeGroupId(), nodeGroupLink.getTargetNodeGroupId()));
                log.debug("TriggerHistCreating", TableConstants.getTableName(tablePrefix,
                        TableConstants.SYM_NODE));

                triggers
                        .add(buildRegistrationTriggerRouter(Version.version(), TableConstants
                                .getTableName(tablePrefix, TableConstants.SYM_NODE_HOST), true,
                                nodeGroupLink.getSourceNodeGroupId(), nodeGroupLink
                                        .getTargetNodeGroupId()));
                log.debug("TriggerHistCreating", TableConstants.getTableName(tablePrefix,
                        TableConstants.SYM_NODE_HOST));

            } else {
                log.warn("TriggerConfigurationCreatingFailed", sourceNodeGroupId, nodeGroupLink
                        .getDataEventAction());
            }
        }
        return triggers;
    }

    protected void mergeInConfigurationTablesTriggerRoutersForCurrentNode(String sourceNodeGroupId,
            List configuredInDatabase) {
        List virtualConfigTriggers = getConfigurationTablesTriggerRoutersForCurrentNode(sourceNodeGroupId);
        for (TriggerRouter trigger : virtualConfigTriggers) {
            if (trigger.getRouter().getSourceNodeGroupId().equalsIgnoreCase(sourceNodeGroupId)
                    && !doesTriggerRouterExistInList(configuredInDatabase, trigger)) {
                configuredInDatabase.add(trigger);
            }
        }
    }
    
    protected boolean doesTriggerRouterExistInList(List triggerRouters,
            TriggerRouter triggerRouter) {
        for (TriggerRouter checkMe : triggerRouters) {
            if (checkMe.isSame(triggerRouter)) {
                return true;
            }
        }
        return false;
    }

    public Map> getTriggerRoutersForCurrentNode(
            boolean refreshCache) {
        return getTriggerRoutersCacheForCurrentNode(refreshCache).triggerRoutersByTriggerId;
    }

    protected TriggerRoutersCache getTriggerRoutersCacheForCurrentNode(
            boolean refreshCache) {
        String myNodeGroupId = parameterService.getNodeGroupId();
        long triggerRouterCacheTimeoutInMs = parameterService.getLong(ParameterConstants.CACHE_TIMEOUT_TRIGGER_ROUTER_IN_MS);
        if (System.currentTimeMillis()-this.triggerRouterCacheTime > triggerRouterCacheTimeoutInMs) {
           resetTriggerRouterCacheByNodeGroupId();
        }
        if (!triggerRouterCacheByNodeGroupId.containsKey(myNodeGroupId) || refreshCache ) {
            synchronized (this) {
                List triggerRouters = getAllTriggerRoutersForCurrentNode(myNodeGroupId);
                Map> triggerRoutersByTriggerId = new HashMap>(
                        triggerRouters.size());
                Map routers = new HashMap(triggerRouters.size());
                for (TriggerRouter triggerRouter : triggerRouters) {
                    String triggerId = triggerRouter.getTrigger().getTriggerId();
                    List list = triggerRoutersByTriggerId.get(triggerId);
                    if (list == null) {
                        list = new ArrayList();
                        triggerRoutersByTriggerId.put(triggerId, list);
                    }
                    list.add(triggerRouter);
                    routers.put(triggerRouter.getRouter().getRouterId(), triggerRouter.getRouter());
                }

                triggerRouterCacheByNodeGroupId.put(myNodeGroupId, new TriggerRoutersCache(
                        triggerRoutersByTriggerId, routers));
            }
        }
        return triggerRouterCacheByNodeGroupId.get(myNodeGroupId);
    }

    /**
     * @see ITriggerRouterService#getActiveRouterByIdForCurrentNode(String, boolean)
     */
    public Router getActiveRouterByIdForCurrentNode(String routerId, boolean refreshCache) {
        return getTriggerRoutersCacheForCurrentNode(refreshCache).routersByRouterId
                .get(routerId);
    }
    
    public Router getRouterById(String routerId) {
        try {
            return jdbcTemplate.queryForObject(getSql("selectRouterSql"), new RouterMapper(), routerId);
        } catch (EmptyResultDataAccessException ex) {
            return null;
        }
    }

    public List getAllTriggerRoutersForCurrentNode(String sourceNodeGroupId) {
        List triggers = (List) jdbcTemplate.query(
                getTriggerRouterSql("activeTriggersForSourceNodeGroupSql"),
                new Object[] { sourceNodeGroupId }, new TriggerRouterMapper());
        mergeInConfigurationTablesTriggerRoutersForCurrentNode(sourceNodeGroupId, triggers);
        return triggers;
    }

    public List getAllTriggerRoutersForReloadForCurrentNode(
            String sourceNodeGroupId, String targetNodeGroupId) {
        return (List) jdbcTemplate.query(getTriggerRouterSql("activeTriggersForReloadSql"), new Object[] { sourceNodeGroupId,
                targetNodeGroupId, Constants.CHANNEL_CONFIG }, new TriggerRouterMapper());
    }

    public TriggerRouter findTriggerRouterById(String triggerId, String routerId) {
        List configs = (List) jdbcTemplate.query(
                getTriggerRouterSql("selectTriggerRouterSql"), new Object[] {
                        triggerId, routerId }, new TriggerRouterMapper());
        if (configs.size() > 0) {
            return configs.get(0);
        } else {
            return null;
        }
    }

    public Trigger getTriggerById(String triggerId) {
        List triggers = (List) jdbcTemplate.query(
                getTriggerRouterSql("selectTriggerByIdSql"),
                new Object[] { triggerId }, new TriggerRouterMapper());
        if (triggers.size() > 0) {
            return triggers.get(0).getTrigger();
        } else {
            return null;
        }
    }

    public Map> getTriggerRoutersByChannel(String nodeGroupId) {
        final Map> retMap = new HashMap>();
        jdbcTemplate.query(getTriggerRouterSql("selectGroupTriggersSql"),
                new Object[] { nodeGroupId }, new TriggerRouterMapper() {
                    public TriggerRouter mapRow(java.sql.ResultSet rs, int arg1)
                            throws java.sql.SQLException {
                        TriggerRouter config = (TriggerRouter) super.mapRow(rs, arg1);
                        List list = retMap.get(config.getTrigger().getChannelId());
                        if (list == null) {
                            list = new ArrayList();
                            retMap.put(config.getTrigger().getChannelId(), list);
                        }
                        list.add(config);
                        return config;
                    };
                });
        return retMap;
    }

    public void insert(TriggerHistory newHistRecord) {
        jdbcTemplate.update(getSql("insertTriggerHistorySql"), new Object[] {
                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() },
                new int[] { 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 });
    }

    public void saveTriggerRouter(TriggerRouter triggerRouter) {
        saveTrigger(triggerRouter.getTrigger());
        saveRouter(triggerRouter.getRouter());
        triggerRouter.setLastUpdateTime(new Date());
        if (0 == jdbcTemplate.update(getSql("updateTriggerRouterSql"),
                new Object[] { triggerRouter.getInitialLoadOrder(),
                        triggerRouter.getInitialLoadSelect(), 
                        triggerRouter.isPingBackEnabled() ? 1 : 0, 
                        triggerRouter.getLastUpdateBy(),
                        triggerRouter.getLastUpdateTime(),
                        triggerRouter.getTrigger().getTriggerId(),
                        triggerRouter.getRouter().getRouterId() },
                new int[] { Types.INTEGER, Types.VARCHAR, Types.SMALLINT, Types.VARCHAR, Types.TIMESTAMP,
                        Types.VARCHAR, Types.VARCHAR })) {
            triggerRouter.setCreateTime(triggerRouter.getLastUpdateTime());
            jdbcTemplate.update(getSql("insertTriggerRouterSql"), new Object[] {
                    triggerRouter.getInitialLoadOrder(), triggerRouter.getInitialLoadSelect(),
                    triggerRouter.isPingBackEnabled() ? 1 : 0,
                    triggerRouter.getCreateTime(), triggerRouter.getLastUpdateBy(),
                    triggerRouter.getLastUpdateTime(), triggerRouter.getTrigger().getTriggerId(),
                    triggerRouter.getRouter().getRouterId() }, new int[] { Types.INTEGER,
                    Types.VARCHAR, Types.SMALLINT, Types.TIMESTAMP, Types.VARCHAR, Types.TIMESTAMP, Types.VARCHAR,
                    Types.VARCHAR });
        }
    }
    
    synchronized protected void resetTriggerRouterCacheByNodeGroupId() {
        triggerRouterCacheByNodeGroupId.clear();
        triggerRouterCacheTime = System.currentTimeMillis();
    }

    public void saveRouter(Router router) {
        router.setLastUpdateTime(new Date());
        if (0 == jdbcTemplate.update(getSql("updateRouterSql"), new Object[] {
                router.getTargetCatalogName(), router.getTargetSchemaName(),
                router.getTargetTableName(), router.getSourceNodeGroupId(),
                router.getTargetNodeGroupId(), router.getRouterType(),
                router.getRouterExpression(), router.isSyncOnUpdate() ? 1 : 0,
                router.isSyncOnInsert() ? 1 : 0, router.isSyncOnDelete() ? 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.VARCHAR, Types.TIMESTAMP,
                        Types.VARCHAR })) {
            router.setCreateTime(router.getLastUpdateTime());
            jdbcTemplate.update(getSql("insertRouterSql"), new Object[] {
                    router.getTargetCatalogName(), router.getTargetSchemaName(),
                    router.getTargetTableName(), router.getSourceNodeGroupId(),
                    router.getTargetNodeGroupId(), router.getRouterType(),
                    router.getRouterExpression(), router.isSyncOnUpdate() ? 1 : 0,
                    router.isSyncOnInsert() ? 1 : 0, router.isSyncOnDelete() ? 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.TIMESTAMP, Types.VARCHAR,
                    Types.TIMESTAMP, Types.VARCHAR });
        }
        resetTriggerRouterCacheByNodeGroupId();
    }

    public void saveTrigger(Trigger trigger) {
        trigger.setLastUpdateTime(new Date());
        if (0 == jdbcTemplate.update(getSql("updateTriggerSql"), new Object[] {
                trigger.getSourceCatalogName(), trigger.getSourceSchemaName(),
                trigger.getSourceTableName(), trigger.getChannelId(),
                trigger.isSyncOnUpdate() ? 1 : 0, trigger.isSyncOnInsert() ? 1 : 0,
                trigger.isSyncOnDelete() ? 1 : 0, trigger.isSyncOnIncomingBatch() ? 1 : 0,
                trigger.getNameForUpdateTrigger(), trigger.getNameForInsertTrigger(),
                trigger.getNameForDeleteTrigger(), trigger.getSyncOnUpdateCondition(),
                trigger.getSyncOnInsertCondition(), trigger.getSyncOnDeleteCondition(),
                trigger.getTxIdExpression(), trigger.getExcludedColumnNames(),
                trigger.getLastUpdateBy(), trigger.getLastUpdateTime(),
                trigger.getExternalSelect(), trigger.getTriggerId() }, new int[] { Types.VARCHAR,
                Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, 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.TIMESTAMP, Types.VARCHAR, Types.VARCHAR })) {
            trigger.setCreateTime(trigger.getLastUpdateTime());
            jdbcTemplate.update(getSql("insertTriggerSql"), new Object[] {
                    trigger.getSourceCatalogName(), trigger.getSourceSchemaName(),
                    trigger.getSourceTableName(), trigger.getChannelId(),
                    trigger.isSyncOnUpdate() ? 1 : 0, trigger.isSyncOnInsert() ? 1 : 0,
                    trigger.isSyncOnDelete() ? 1 : 0, trigger.isSyncOnIncomingBatch() ? 1 : 0,
                    trigger.getNameForUpdateTrigger(), trigger.getNameForInsertTrigger(),
                    trigger.getNameForDeleteTrigger(), trigger.getSyncOnUpdateCondition(),
                    trigger.getSyncOnInsertCondition(), trigger.getSyncOnDeleteCondition(),
                    trigger.getTxIdExpression(), trigger.getExcludedColumnNames(),
                    trigger.getCreateTime(), trigger.getLastUpdateBy(),
                    trigger.getLastUpdateTime(), trigger.getExternalSelect(),
                    trigger.getTriggerId() }, new int[] { Types.VARCHAR, Types.VARCHAR,
                    Types.VARCHAR, Types.VARCHAR, 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.TIMESTAMP,
                    Types.VARCHAR, Types.TIMESTAMP, Types.VARCHAR, Types.VARCHAR });
        }
    }

    public void syncTriggers() {
        syncTriggers(null, false);
    }

    public void syncTriggers(StringBuilder sqlBuffer, boolean gen_always) {
        if (clusterService.lock(ClusterConstants.SYNCTRIGGERS)) {
            synchronized (this) {
                try {
                    log.info("TriggersSynchronizing");
                    // make sure channels are read from the database
                    configurationService.reloadChannels();
                    inactivateTriggers(sqlBuffer);
                    updateOrCreateDatabaseTriggers(sqlBuffer, gen_always);
                    resetTriggerRouterCacheByNodeGroupId();
                } finally {
                    clusterService.unlock(ClusterConstants.SYNCTRIGGERS);
                    log.info("TriggersSynchronized");
                }
            }
        } else {
            log.info("TriggersSynchronizingFailedLock");
        }
    }

    protected void inactivateTriggers(StringBuilder sqlBuffer) {
        List triggers = getActiveTriggerHistoriesForInactivation();
        for (TriggerHistory history : triggers) {
            log.info("TriggersRemoving", history.getSourceTableName());
            dbDialect.removeTrigger(sqlBuffer, history.getSourceCatalogName(), history
                    .getSourceSchemaName(), history.getNameForInsertTrigger(), history
                    .getSourceTableName(), history);
            dbDialect.removeTrigger(sqlBuffer, history.getSourceCatalogName(), history
                    .getSourceSchemaName(), history.getNameForDeleteTrigger(), history
                    .getSourceTableName(), history);
            dbDialect.removeTrigger(sqlBuffer, history.getSourceCatalogName(), history
                    .getSourceSchemaName(), history.getNameForUpdateTrigger(), history
                    .getSourceTableName(), history);

            if (parameterService.is(ParameterConstants.AUTO_SYNC_TRIGGERS)) {
                if (this.triggerCreationListeners != null) {
                    for (ITriggerCreationListener l : this.triggerCreationListeners) {
                        l.triggerInactivated(null, history);
                    }
                }
            }

            boolean triggerExists = dbDialect.doesTriggerExist(history.getSourceCatalogName(),
                    history.getSourceSchemaName(), history.getSourceTableName(), history
                            .getNameForInsertTrigger());
            triggerExists |= dbDialect.doesTriggerExist(history.getSourceCatalogName(), history
                    .getSourceSchemaName(), history.getSourceTableName(), history
                    .getNameForUpdateTrigger());
            triggerExists |= dbDialect.doesTriggerExist(history.getSourceCatalogName(), history
                    .getSourceSchemaName(), history.getSourceTableName(), history
                    .getNameForDeleteTrigger());
            if (triggerExists) {
                log.warn("TriggersRemovingFailed", history.getTriggerId(), history
                        .getTriggerHistoryId());
            } else {
                inactivateTriggerHistory(history);
            }
        }
    }

    protected List toList(Collection> source) {
        ArrayList list = new ArrayList();
        for (List triggerRouters : source) {
            for (TriggerRouter triggerRouter : triggerRouters) {
                list.add(triggerRouter);
            }
        }
        return list;
    }

    protected void updateOrCreateDatabaseTriggers(StringBuilder sqlBuffer, boolean gen_always) {
        List triggers = new TriggerSelector(toList(getTriggerRoutersForCurrentNode(
                true).values()))
                .select();

        for (Trigger trigger : triggers) {

            try {

                TriggerReBuildReason reason = TriggerReBuildReason.NEW_TRIGGERS;

                Table table = dbDialect.getTable(trigger.getSourceCatalogName(), trigger
                        .getSourceSchemaName(), trigger.getSourceTableName(), false);

                if (table != null) {
                    TriggerHistory latestHistoryBeforeRebuild = getNewestTriggerHistoryForTrigger(trigger
                            .getTriggerId());

                    boolean forceRebuildOfTriggers = false;
                    if (latestHistoryBeforeRebuild == null) {
                        reason = TriggerReBuildReason.NEW_TRIGGERS;
                        forceRebuildOfTriggers = true;

                    } else if (TriggerHistory.calculateTableHashFor(table) != 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 (gen_always) {
                        reason = TriggerReBuildReason.FORCED;
                        forceRebuildOfTriggers = true;
                    }

                    TriggerHistory newestHistory = rebuildTriggerIfNecessary(sqlBuffer,
                            forceRebuildOfTriggers, trigger, DataEventType.INSERT, reason,
                            latestHistoryBeforeRebuild, null, trigger.isSyncOnInsert(), table);

                    newestHistory = rebuildTriggerIfNecessary(sqlBuffer, forceRebuildOfTriggers,
                            trigger, DataEventType.UPDATE, reason, latestHistoryBeforeRebuild,
                            newestHistory, trigger.isSyncOnUpdate(), table);

                    newestHistory = rebuildTriggerIfNecessary(sqlBuffer, forceRebuildOfTriggers,
                            trigger, DataEventType.DELETE, reason, latestHistoryBeforeRebuild,
                            newestHistory, trigger.isSyncOnDelete(), table);

                    if (latestHistoryBeforeRebuild != null && newestHistory != null) {
                        inactivateTriggerHistory(latestHistoryBeforeRebuild);
                    }

                    if (newestHistory != null) {
                        if (parameterService.is(ParameterConstants.AUTO_SYNC_TRIGGERS)) {
                            if (this.triggerCreationListeners != null) {
                                for (ITriggerCreationListener l : this.triggerCreationListeners) {
                                    l.triggerCreated(trigger, newestHistory);
                                }
                            }
                        }
                    }

                } else {
                    log.error("TriggerTableMissing", trigger.qualifiedSourceTableName());

                    if (this.triggerCreationListeners != null) {
                        for (ITriggerCreationListener l : this.triggerCreationListeners) {
                            l.tableDoesNotExist(trigger);
                        }
                    }
                }
            } catch (Exception ex) {
                log.error("TriggerSynchronizingFailed", ex, trigger.qualifiedSourceTableName());
                if (this.triggerCreationListeners != null) {
                    for (ITriggerCreationListener l : this.triggerCreationListeners) {
                        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) {

        boolean triggerExists = false;

        TriggerHistory newTriggerHist = new TriggerHistory(table, trigger, reason);
        int maxTriggerNameLength = dbDialect.getMaxTriggerNameLength();

        newTriggerHist.setNameForInsertTrigger(getTriggerName(DataEventType.INSERT,
                maxTriggerNameLength, trigger).toUpperCase());
        newTriggerHist.setNameForUpdateTrigger(getTriggerName(DataEventType.UPDATE,
                maxTriggerNameLength, trigger).toUpperCase());
        newTriggerHist.setNameForDeleteTrigger(getTriggerName(DataEventType.DELETE,
                maxTriggerNameLength, trigger).toUpperCase());

        String oldTriggerName = null;
        String oldSourceSchema = null;
        String oldCatalogName = null;
        if (oldhist != null) {
            oldTriggerName = oldhist.getTriggerNameForDmlType(dmlType);
            oldSourceSchema = oldhist.getSourceSchemaName();
            oldCatalogName = oldhist.getSourceCatalogName();
            triggerExists = dbDialect.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 = trigger.getSourceSchemaName();
            oldCatalogName = trigger.getSourceCatalogName();
            triggerExists = dbDialect.doesTriggerExist(oldCatalogName, oldSourceSchema, trigger
                    .getSourceTableName(), oldTriggerName);
        }

        if (!triggerExists && forceRebuild) {
            reason = TriggerReBuildReason.TRIGGERS_MISSING;
        }

        if ((forceRebuild || !triggerIsActive) && triggerExists) {
            dbDialect.removeTrigger(sqlBuffer, oldCatalogName, oldSourceSchema, oldTriggerName,
                    trigger.getSourceTableName(), oldhist);
            triggerExists = false;
        }

        boolean isDeadTrigger = !trigger.isSyncOnInsert() && !trigger.isSyncOnUpdate()
                && !trigger.isSyncOnDelete();

        if (hist == null
                && (oldhist == null || (!triggerExists && triggerIsActive) || (isDeadTrigger && forceRebuild))) {
            insert(newTriggerHist);
            hist = getNewestTriggerHistoryForTrigger(trigger.getTriggerId());
        }

        if (!triggerExists && triggerIsActive) {
            dbDialect.createTrigger(sqlBuffer, dmlType, trigger, hist, tablePrefix, table);
        }

        return hist;
    }
    
    protected String replaceCharsForTriggerName(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) {

        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;
        }

        if (StringUtils.isBlank(triggerName)) {
            String triggerPrefix1 = tablePrefix + "_";
            String triggerSuffix1 = "on_" + dml.getCode().toLowerCase() + "_for_";
            String triggerSuffix2 = replaceCharsForTriggerName(trigger.getTriggerId());
            String triggerSuffix3 = replaceCharsForTriggerName("_" + 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) {
            int duplicateCount = 0;
            do {
                if (duplicateCount == 0) {
                    triggerName = triggerName.substring(0, maxTriggerNameLength - 1);
                } else {
                    String duplicateSuffix = Integer.toString(duplicateCount);
                    triggerName = triggerName.substring(0, triggerName.length()
                            - duplicateSuffix.length())
                            + duplicateSuffix;
                }
                duplicateCount++;
            } while (isTriggerNameInUse(trigger.getTriggerId(), triggerName));

            log.debug("TriggerNameTruncated", dml.name().toLowerCase(), trigger.getTriggerId(),
                    maxTriggerNameLength);
        }
        return triggerName;
    }

    class NodeGroupLinkMapper implements RowMapper {
        public NodeGroupLink mapRow(ResultSet rs, int num) throws SQLException {
            NodeGroupLink node_groupTarget = new NodeGroupLink();
            node_groupTarget.setSourceNodeGroupId(rs.getString(1));
            node_groupTarget.setTargetNodeGroupId(rs.getString(2));
            node_groupTarget.setDataEventAction(NodeGroupLinkAction.fromCode(rs.getString(3)));
            return node_groupTarget;
        }
    }

    class TriggerHistoryMapper implements RowMapper {
        Map retMap = null;

        TriggerHistoryMapper() {
        }

        TriggerHistoryMapper(Map map) {
            this.retMap = map;
        }

        public TriggerHistory mapRow(ResultSet rs, int i) throws SQLException {
            TriggerHistory hist = new TriggerHistory();
            hist.setTriggerHistoryId(rs.getInt(1));
            hist.setTriggerId(rs.getString(2));
            hist.setSourceTableName(rs.getString(3));
            hist.setTableHash(rs.getInt(4));
            hist.setCreateTime(rs.getTimestamp(5));
            hist.setPkColumnNames(rs.getString(6));
            hist.setColumnNames(rs.getString(7));
            hist.setLastTriggerBuildReason(TriggerReBuildReason.fromCode(rs.getString(8)));
            hist.setNameForDeleteTrigger(rs.getString(9));
            hist.setNameForInsertTrigger(rs.getString(10));
            hist.setNameForUpdateTrigger(rs.getString(11));
            hist.setSourceSchemaName(rs.getString(12));
            hist.setSourceCatalogName(rs.getString(13));
            hist.setTriggerRowHash(rs.getLong(14));
            if (this.retMap != null) {
                this.retMap.put((long) hist.getTriggerHistoryId(), hist);
            }
            return hist;
        }
    }
    
    class RouterMapper implements RowMapper {        
        public Router mapRow(ResultSet rs, int rowNum) throws SQLException {
            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.setSourceNodeGroupId(rs.getString("source_node_group_id"));
            router.setTargetSchemaName(rs.getString("target_schema_name"));
            router.setTargetTableName(rs.getString("target_table_name"));
            router.setTargetNodeGroupId(rs.getString("target_node_group_id"));

            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.setCreateTime(rs.getTimestamp("r_create_time"));
            router.setLastUpdateTime(rs.getTimestamp("r_last_update_time"));
            router.setLastUpdateBy(rs.getString("r_last_update_by"));
            return router;
        }
    }

    class TriggerRouterMapper implements RowMapper {
        public TriggerRouter mapRow(java.sql.ResultSet rs, int arg1) throws java.sql.SQLException {
            TriggerRouter trig = new TriggerRouter();
            trig.getTrigger().setTriggerId(rs.getString("trigger_id"));
            trig.getTrigger().setChannelId(rs.getString("channel_id"));
            trig.getTrigger().setSourceTableName(rs.getString("source_table_name"));
            trig.getTrigger().setSyncOnInsert(rs.getBoolean("sync_on_insert"));
            trig.getTrigger().setSyncOnUpdate(rs.getBoolean("sync_on_update"));
            trig.getTrigger().setSyncOnDelete(rs.getBoolean("sync_on_delete"));
            trig.getTrigger().setSyncOnIncomingBatch(rs.getBoolean("sync_on_incoming_batch"));
            trig.getTrigger().setNameForDeleteTrigger(rs.getString("name_for_delete_trigger"));
            trig.getTrigger().setNameForInsertTrigger(rs.getString("name_for_insert_trigger"));
            trig.getTrigger().setNameForUpdateTrigger(rs.getString("name_for_update_trigger"));
            String schema = rs.getString("source_schema_name");
            trig.getTrigger().setSourceSchemaName(schema);
            String catalog = rs.getString("source_catalog_name");
            trig.getTrigger().setSourceCatalogName(catalog);
            String condition = rs.getString("sync_on_insert_condition");
            if (!StringUtils.isBlank(condition)) {
                trig.getTrigger().setSyncOnInsertCondition(condition);
            }
            condition = rs.getString("sync_on_update_condition");
            if (!StringUtils.isBlank(condition)) {
                trig.getTrigger().setSyncOnUpdateCondition(condition);
            }

            condition = rs.getString("sync_on_delete_condition");
            if (!StringUtils.isBlank(condition)) {
                trig.getTrigger().setSyncOnDeleteCondition(condition);
            }

            condition = rs.getString("external_select");
            if (!StringUtils.isBlank(condition)) {
                trig.getTrigger().setExternalSelect(condition);
            }

            trig.getTrigger().setTxIdExpression(rs.getString("tx_id_expression"));
            trig.getTrigger().setCreateTime(rs.getTimestamp("t_create_time"));
            trig.getTrigger().setLastUpdateTime(rs.getTimestamp("t_last_update_time"));
            trig.getTrigger().setLastUpdateBy(rs.getString("t_last_update_by"));
            trig.getTrigger().setExcludedColumnNames(rs.getString("excluded_column_names"));

            RouterMapper mapper = new RouterMapper();
            trig.setRouter(mapper.mapRow(rs, arg1));

            trig.setCreateTime(rs.getTimestamp("create_time"));
            trig.setLastUpdateTime(rs.getTimestamp("last_update_time"));
            trig.setLastUpdateBy(rs.getString("last_update_by"));
            trig.setInitialLoadOrder(rs.getInt("initial_load_order"));
            trig.setInitialLoadSelect(rs.getString("initial_load_select"));
            trig.setPingBackEnabled(rs.getBoolean("ping_back_enabled"));

            return trig;
        }
    }

    public void setRootConfigChannelTableNames(Map> configChannelTableNames) {
        this.rootConfigChannelTableNames = configChannelTableNames;
    }

    public void setRootConfigChannelInitialLoadSelect(
            Map> rootConfigChannelInitialLoadSelect) {
        this.rootConfigChannelInitialLoadSelect = rootConfigChannelInitialLoadSelect;
    }

    public void setClusterService(IClusterService clusterService) {
        this.clusterService = clusterService;
    }

    public void setConfigurationService(IConfigurationService configurationService) {
        this.configurationService = configurationService;
    }

    public void setTriggerCreationListeners(
            List autoTriggerCreationListeners) {
        if (triggerCreationListeners != null) {
            for (ITriggerCreationListener l : triggerCreationListeners) {
                addTriggerCreationListeners(l);
            }
        }
    }

    public void addTriggerCreationListeners(ITriggerCreationListener l) {
        if (this.triggerCreationListeners == null) {
            this.triggerCreationListeners = new ArrayList();
        }
        this.triggerCreationListeners.add(l);
    }

    public Map getFailedTriggers() {
        return this.failureListener.getFailures();
    }

    class TriggerRoutersCache {

        public TriggerRoutersCache(Map> triggerRoutersByTriggerId,
                Map routersByRouterId) {
            this.triggerRoutersByTriggerId = triggerRoutersByTriggerId;
            this.routersByRouterId = routersByRouterId;
        }

        Map> triggerRoutersByTriggerId = new HashMap>();
        Map routersByRouterId = new HashMap();
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy