/** * 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.route; import java.sql.Types; import java.util.Date; import java.util.HashMap; import java.util.Map; import java.util.Set; import org.jumpmind.db.model.Column; import org.jumpmind.db.model.Table; import org.jumpmind.db.platform.DatabaseInfo; import org.jumpmind.db.platform.IDatabasePlatform; import org.jumpmind.db.sql.DmlStatement; import org.jumpmind.db.sql.DmlStatement.DmlType; import org.jumpmind.db.sql.ISqlTemplate; import org.jumpmind.extension.IBuiltInExtensionPoint; import org.jumpmind.symmetric.ISymmetricEngine; import org.jumpmind.symmetric.common.ParameterConstants; import org.jumpmind.symmetric.io.data.DataEventType; import org.jumpmind.symmetric.model.DataMetaData; import org.jumpmind.symmetric.model.Node; import org.jumpmind.symmetric.model.TriggerHistory; import org.jumpmind.symmetric.model.TriggerRouter; import org.jumpmind.symmetric.service.IParameterService; import org.jumpmind.util.FormatUtils; public class AuditTableDataRouter extends AbstractDataRouter implements IBuiltInExtensionPoint { private static final String COLUMN_AUDIT_EVENT = "AUDIT_EVENT"; private static final String COLUMN_AUDIT_TIME = "AUDIT_TIME"; private static final String COLUMN_AUDIT_ID = "AUDIT_ID"; private ISymmetricEngine engine; private Map<String, Table> auditTables = new HashMap<String, Table>(); public AuditTableDataRouter(ISymmetricEngine engine) { this.engine = engine; } public Set<String> routeToNodes(SimpleRouterContext context, DataMetaData dataMetaData, Set<Node> nodes, boolean initialLoad, boolean initialLoadSelectUsed, TriggerRouter triggerRouter) { DataEventType eventType = dataMetaData.getData().getDataEventType(); if (eventType == DataEventType.INSERT || eventType == DataEventType.UPDATE || eventType == DataEventType.DELETE) { IParameterService parameterService = engine.getParameterService(); IDatabasePlatform platform = engine.getDatabasePlatform(); TriggerHistory triggerHistory = dataMetaData.getTriggerHistory(); Table table = dataMetaData.getTable().copyAndFilterColumns( triggerHistory.getParsedColumnNames(), triggerHistory.getParsedPkColumnNames(), true); String tableName = table.getFullyQualifiedTableName(); Table auditTable = auditTables.get(tableName); if (auditTable == null) { auditTable = toAuditTable(table); auditTables.put(tableName, auditTable); if (parameterService.is(ParameterConstants.AUTO_CONFIGURE_DATABASE)) { platform.alterTables(true, auditTable); } } DatabaseInfo dbInfo = platform.getDatabaseInfo(); String auditTableName = auditTable.getQualifiedTableName(dbInfo.getDelimiterToken(), dbInfo.getCatalogSeparator(), dbInfo.getSchemaSeparator()); ISqlTemplate template = platform.getSqlTemplate(); Map<String, Object> values = null; if (eventType != DataEventType.DELETE) { values = new HashMap<String, Object>(getNewDataAsObject(null, dataMetaData, engine.getSymmetricDialect(), false)); } else { values = new HashMap<String, Object>(getOldDataAsObject(null, dataMetaData, engine.getSymmetricDialect(), false)); } Long sequence = (Long) context.get(auditTableName); if (sequence == null) { sequence = 1l + template.queryForLong(String.format("select max(%s) from %s", auditTable.getColumnWithName(COLUMN_AUDIT_ID).getName(), auditTableName)); } else { sequence = 1l + sequence; } context.put(auditTable.getName(), sequence); values.put(auditTable.getColumnWithName(COLUMN_AUDIT_ID).getName(), sequence); values.put(auditTable.getColumnWithName(COLUMN_AUDIT_TIME).getName(), new Date()); values.put(auditTable.getColumnWithName(COLUMN_AUDIT_EVENT).getName(), eventType.getCode()); DmlStatement statement = platform.createDmlStatement(DmlType.INSERT, auditTable, null); int[] types = statement.getTypes(); Object[] args = statement.getValueArray(values); String sql = statement.getSql(); template.update(sql, args, types); } return null; } protected Table toAuditTable(Table table) { Table auditTable = table.copy(); String tableName = table.getName(); if (!FormatUtils.isMixedCase(tableName)) { tableName = tableName.toUpperCase(); } auditTable.setName(String.format("%s_AUDIT", tableName)); Column[] columns = auditTable.getColumns(); auditTable.removeAllColumns(); auditTable.addColumn(new Column(COLUMN_AUDIT_ID, true, Types.BIGINT, 0, 0)); auditTable.addColumn(new Column(COLUMN_AUDIT_TIME, false, Types.TIMESTAMP, 0, 0)); auditTable.addColumn(new Column(COLUMN_AUDIT_EVENT, false, Types.CHAR, 1, 0)); for (Column column : columns) { column.setRequired(false); column.setPrimaryKey(false); column.setAutoIncrement(false); auditTable.addColumn(column); } auditTable.removeAllForeignKeys(); auditTable.removeAllIndices(); engine.getDatabasePlatform().alterCaseToMatchDatabaseDefaultCase(auditTable); return auditTable; } }