/**
* Licensed to JumpMind Inc under one or more contributor
* license agreements. See the NOTICE file distributed
* with this work for additional information regarding
* copyright ownership. JumpMind Inc licenses this file
* to you under the GNU General Public License, version 3.0 (GPLv3)
* (the "License"); you may not use this file except in compliance
* with the License.
*
* You should have received a copy of the GNU General Public License,
* version 3.0 (GPLv3) along with this library; if not, see
* <http://www.gnu.org/licenses/>.
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jumpmind.symmetric.service.impl;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang.StringUtils;
import org.jumpmind.db.model.Table;
import org.jumpmind.db.sql.ISqlRowMapper;
import org.jumpmind.db.sql.Row;
import org.jumpmind.symmetric.common.ParameterConstants;
import org.jumpmind.symmetric.db.ISymmetricDialect;
import org.jumpmind.symmetric.model.LoadFilter;
import org.jumpmind.symmetric.model.LoadFilter.LoadFilterType;
import org.jumpmind.symmetric.model.NodeGroupLink;
import org.jumpmind.symmetric.service.IConfigurationService;
import org.jumpmind.symmetric.service.ILoadFilterService;
import org.jumpmind.symmetric.service.IParameterService;
import org.jumpmind.util.FormatUtils;
public class LoadFilterService extends AbstractService implements ILoadFilterService {
private Map<NodeGroupLink, Map<LoadFilterType, Map<String, List<LoadFilter>>>> loadFilterCacheByNodeGroupLink;
private long lastCacheTimeInMs;
private Date lastUpdateTime;
private IConfigurationService configurationService;
public LoadFilterService(IParameterService parameterService,
ISymmetricDialect symmetricDialect, IConfigurationService configurationService) {
super(parameterService, symmetricDialect);
this.configurationService = configurationService;
setSqlMap(new LoadFilterServiceSqlMap(symmetricDialect.getPlatform(),
createSqlReplacementTokens()));
}
public Map<LoadFilterType, Map<String, List<LoadFilter>>> findLoadFiltersFor(NodeGroupLink nodeGroupLink,
boolean useCache) {
// get the cache timeout
long cacheTimeoutInMs = parameterService
.getLong(ParameterConstants.CACHE_TIMEOUT_LOAD_FILTER_IN_MS);
// if the cache is expired or the caller doesn't want to use the cache,
// pull the data and refresh the cache
synchronized (this) {
if (System.currentTimeMillis() - lastCacheTimeInMs >= cacheTimeoutInMs
|| loadFilterCacheByNodeGroupLink == null || useCache == false) {
refreshCache();
}
}
if (loadFilterCacheByNodeGroupLink != null) {
Map<LoadFilterType, Map<String, List<LoadFilter>>> loadFilters = loadFilterCacheByNodeGroupLink
.get(nodeGroupLink);
return loadFilters;
}
return null;
}
protected void refreshCache() {
// get the cache timeout
long cacheTimeoutInMs = parameterService
.getLong(ParameterConstants.CACHE_TIMEOUT_LOAD_FILTER_IN_MS);
synchronized (this) {
if (System.currentTimeMillis() - lastCacheTimeInMs >= cacheTimeoutInMs
|| loadFilterCacheByNodeGroupLink == null) {
loadFilterCacheByNodeGroupLink = new HashMap<NodeGroupLink, Map<LoadFilterType, Map<String, List<LoadFilter>>>>();
List<LoadFilterNodeGroupLink> loadFilters = getLoadFiltersFromDB();
boolean ignoreCase = this.parameterService
.is(ParameterConstants.DB_METADATA_IGNORE_CASE);
for (LoadFilterNodeGroupLink loadFilter : loadFilters) {
NodeGroupLink nodeGroupLink = loadFilter.getNodeGroupLink();
if (nodeGroupLink != null) {
Map<LoadFilterType, Map<String, List<LoadFilter>>> loadFiltersByType= loadFilterCacheByNodeGroupLink
.get(nodeGroupLink);
if (loadFiltersByType == null) {
loadFiltersByType = new HashMap<LoadFilterType, Map<String, List<LoadFilter>>>();
loadFilterCacheByNodeGroupLink.put(nodeGroupLink, loadFiltersByType);
}
Map<String, List<LoadFilter>> loadFiltersByTable = loadFiltersByType.get(loadFilter.getLoadFilterType());
if (loadFiltersByTable == null) {
loadFiltersByTable = new HashMap<String, List<LoadFilter>>();
loadFiltersByType.put(loadFilter.getLoadFilterType(), loadFiltersByTable);
}
String tableName = loadFilter.getTargetTableName();
if (StringUtils.isBlank(tableName)) {
tableName = FormatUtils.WILDCARD;
} else if (ignoreCase) {
tableName = tableName.toUpperCase();
}
String schemaName = loadFilter.getTargetSchemaName();
if (StringUtils.isBlank(schemaName)) {
schemaName = FormatUtils.WILDCARD;
} else if (ignoreCase) {
schemaName = schemaName.toUpperCase();
}
String catalogName = loadFilter.getTargetCatalogName();
if (StringUtils.isBlank(catalogName)) {
catalogName = FormatUtils.WILDCARD;
} else if (ignoreCase) {
catalogName = catalogName.toUpperCase();
}
String qualifiedName = Table.getFullyQualifiedTableName(
catalogName, schemaName, tableName);
List<LoadFilter> loadFiltersForTable = loadFiltersByTable.get(qualifiedName);
if (loadFiltersForTable == null) {
loadFiltersForTable = new ArrayList<LoadFilter>();
loadFiltersByTable.put(qualifiedName, loadFiltersForTable);
}
loadFiltersForTable.add(loadFilter);
}
}
lastCacheTimeInMs = System.currentTimeMillis();
}
}
}
private List<LoadFilterNodeGroupLink> getLoadFiltersFromDB() {
return sqlTemplate.query(getSql("selectLoadFilterTable"), new LoadFilterMapper());
}
class LoadFilterMapper implements ISqlRowMapper<LoadFilterNodeGroupLink> {
public LoadFilterNodeGroupLink mapRow(Row rs) {
LoadFilterNodeGroupLink loadFilter = new LoadFilterNodeGroupLink();
loadFilter.setLoadFilterId(rs.getString("load_filter_id"));
loadFilter.setNodeGroupLink(configurationService.getNodeGroupLinkFor(
rs.getString("source_node_group_id"), rs.getString("target_node_group_id"), false));
loadFilter.setTargetCatalogName(rs.getString("target_catalog_name"));
loadFilter.setTargetSchemaName(rs.getString("target_schema_name"));
loadFilter.setTargetTableName(rs.getString("target_table_name"));
loadFilter.setFilterOnInsert(rs.getBoolean("filter_on_insert"));
loadFilter.setFilterOnUpdate(rs.getBoolean("filter_on_update"));
loadFilter.setFilterOnDelete(rs.getBoolean("filter_on_delete"));
loadFilter.setBeforeWriteScript(rs.getString("before_write_script"));
loadFilter.setAfterWriteScript(rs.getString("after_write_script"));
loadFilter.setBatchCompleteScript(rs.getString("batch_complete_script"));
loadFilter.setBatchCommitScript(rs.getString("batch_commit_script"));
loadFilter.setBatchRollbackScript(rs.getString("batch_rollback_script"));
loadFilter.setHandleErrorScript(rs.getString("handle_error_script"));
loadFilter.setCreateTime(rs.getDateTime("create_time"));
loadFilter.setLastUpdateBy(rs.getString("last_update_by"));
loadFilter.setLastUpdateTime(rs.getDateTime("last_update_time"));
loadFilter.setLoadFilterOrder(rs.getInt("load_filter_order"));
loadFilter.setFailOnError(rs.getBoolean("fail_on_error"));
try {
loadFilter.setLoadFilterType(LoadFilter.LoadFilterType.valueOf(rs.getString(
"load_filter_type").toUpperCase()));
} catch (RuntimeException ex) {
log.warn(
"Invalid value provided for load_filter_type of '{}.' Valid values are: {}",
rs.getString("load_filter_type"),
Arrays.toString(LoadFilter.LoadFilterType.values()));
throw ex;
}
return loadFilter;
}
}
public List<LoadFilterNodeGroupLink> getLoadFilterNodeGroupLinks() {
return getLoadFiltersFromDB();
}
public void saveLoadFilter(LoadFilterNodeGroupLink loadFilter) {
loadFilter.setLastUpdateTime(new Date());
Object[] args = { loadFilter.getAfterWriteScript(), loadFilter.getBatchCommitScript(),
loadFilter.getBatchCompleteScript(), loadFilter.getBatchRollbackScript(),
loadFilter.getBeforeWriteScript(), loadFilter.getHandleErrorScript(),
loadFilter.getLoadFilterOrder(),
loadFilter.getLoadFilterType().name(),
loadFilter.getNodeGroupLink().getSourceNodeGroupId(),
loadFilter.getNodeGroupLink().getTargetNodeGroupId(),
loadFilter.getTargetCatalogName(), loadFilter.getTargetSchemaName(),
loadFilter.getTargetTableName(), loadFilter.isFilterOnInsert() ? 1 : 0,
loadFilter.isFilterOnUpdate() ? 1 : 0, loadFilter.isFilterOnDelete() ? 1 : 0,
loadFilter.isFailOnError() ? 1 : 0, loadFilter.getLastUpdateBy(),
loadFilter.getLastUpdateTime(), loadFilter.getLoadFilterId() };
if (sqlTemplate.update(getSql("updateLoadFilterSql"), args) == 0) {
sqlTemplate.update(getSql("insertLoadFilterSql"), args);
}
clearCache();
}
public void deleteLoadFilter(String loadFilterId) {
sqlTemplate.update(getSql("deleteLoadFilterSql"), loadFilterId);
clearCache();
}
public boolean refreshFromDatabase() {
Date date = sqlTemplate.queryForObject(getSql("selectMaxLastUpdateTime"), Date.class);
if (date != null) {
if (lastUpdateTime == null || lastUpdateTime.before(date)) {
if (lastUpdateTime != null) {
log.info("Newer filter settings were detected");
}
lastUpdateTime = date;
clearCache();
return true;
}
}
return false;
}
public void clearCache() {
synchronized (this) {
this.loadFilterCacheByNodeGroupLink = null;
}
}
public static class LoadFilterNodeGroupLink extends LoadFilter {
private static final long serialVersionUID = 1L;
protected NodeGroupLink nodeGroupLink;
public void setNodeGroupLink(NodeGroupLink nodeGroupLink) {
this.nodeGroupLink = nodeGroupLink;
}
public NodeGroupLink getNodeGroupLink() {
return nodeGroupLink;
}
}
}