/** * 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.io.data.transform; import static org.jumpmind.symmetric.common.Constants.DATA_CONTEXT_ENGINE; import static org.jumpmind.symmetric.common.Constants.DATA_CONTEXT_SOURCE_NODE; import static org.jumpmind.symmetric.common.Constants.DATA_CONTEXT_SOURCE_NODE_EXTERNAL_ID; import static org.jumpmind.symmetric.common.Constants.DATA_CONTEXT_SOURCE_NODE_GROUP_ID; import static org.jumpmind.symmetric.common.Constants.DATA_CONTEXT_SOURCE_NODE_ID; import static org.jumpmind.symmetric.common.Constants.DATA_CONTEXT_TARGET_NODE; import static org.jumpmind.symmetric.common.Constants.DATA_CONTEXT_TARGET_NODE_EXTERNAL_ID; import static org.jumpmind.symmetric.common.Constants.DATA_CONTEXT_TARGET_NODE_GROUP_ID; import static org.jumpmind.symmetric.common.Constants.DATA_CONTEXT_TARGET_NODE_ID; import java.util.HashMap; import java.util.Map; import org.apache.commons.lang.StringUtils; import org.jumpmind.db.platform.IDatabasePlatform; import org.jumpmind.extension.IBuiltInExtensionPoint; import org.jumpmind.symmetric.common.Constants; import org.jumpmind.symmetric.common.ParameterConstants; import org.jumpmind.symmetric.io.data.DataContext; import org.jumpmind.symmetric.model.Data; import org.jumpmind.symmetric.service.IParameterService; import org.jumpmind.util.Context; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import bsh.Interpreter; import bsh.TargetError; public class BshColumnTransform implements ISingleValueColumnTransform, IBuiltInExtensionPoint { protected final Logger log = LoggerFactory.getLogger(getClass()); final String INTERPRETER_KEY = String.format("%d.BshInterpreter", hashCode()); public static final String NAME = "bsh"; IParameterService parameterService; /* * Static context object used to maintain objects in memory for reference between BSH transforms. */ private static Map<String, Object> bshContext = new HashMap<String, Object>(); public BshColumnTransform(IParameterService parameterService) { this.parameterService = parameterService; } public String getName() { return NAME; } public boolean isExtractColumnTransform() { return true; } public boolean isLoadColumnTransform() { return true; } public String transform(IDatabasePlatform platform, DataContext context, TransformColumn column, TransformedData data, Map<String, String> sourceValues, String newValue, String oldValue) throws IgnoreColumnException, IgnoreRowException { try { Interpreter interpreter = getInterpreter(context); interpreter.set("currentValue", newValue); interpreter.set("oldValue", oldValue); interpreter.set("channelId", context.getBatch().getChannelId()); interpreter.set("includeOn", column.getIncludeOn()); interpreter.set("sourceDmlType", data.getSourceDmlType()); interpreter.set("sourceDmlTypeString", data.getSourceDmlType().toString()); interpreter.set("transformedData", data); interpreter.set("transformColumn", column); Data csvData = (Data)context.get(Constants.DATA_CONTEXT_CURRENT_CSV_DATA); if (csvData != null && csvData.getTriggerHistory() != null) { interpreter.set("sourceSchemaName", csvData.getTriggerHistory().getSourceSchemaName()); interpreter.set("sourceCatalogName", csvData.getTriggerHistory().getSourceCatalogName()); interpreter.set("sourceTableName", csvData.getTriggerHistory().getSourceTableName()); } for (String columnName : sourceValues.keySet()) { interpreter.set(columnName.toUpperCase(), sourceValues.get(columnName)); interpreter.set(columnName, sourceValues.get(columnName)); } String transformExpression = column.getTransformExpression(); String globalScript = parameterService.getString(ParameterConstants.BSH_TRANSFORM_GLOBAL_SCRIPT); String methodName = String.format("transform_%d()", Math.abs(transformExpression.hashCode() + (globalScript == null ? 0 : globalScript.hashCode()))); if (context.get(methodName) == null) { interpreter.set("log", log); interpreter.set("sqlTemplate", platform.getSqlTemplate()); interpreter.set("context", context); interpreter.set("bshContext", bshContext); interpreter.set(DATA_CONTEXT_ENGINE, context.get(DATA_CONTEXT_ENGINE)); interpreter.set(DATA_CONTEXT_TARGET_NODE, context.get(DATA_CONTEXT_TARGET_NODE)); interpreter.set(DATA_CONTEXT_TARGET_NODE_ID, context.get(DATA_CONTEXT_TARGET_NODE_ID)); interpreter.set(DATA_CONTEXT_TARGET_NODE_GROUP_ID, context.get(DATA_CONTEXT_TARGET_NODE_GROUP_ID)); interpreter.set(DATA_CONTEXT_TARGET_NODE_EXTERNAL_ID, context.get(DATA_CONTEXT_TARGET_NODE_EXTERNAL_ID)); interpreter.set(DATA_CONTEXT_SOURCE_NODE, context.get(DATA_CONTEXT_SOURCE_NODE)); interpreter.set(DATA_CONTEXT_SOURCE_NODE_ID, context.get(DATA_CONTEXT_SOURCE_NODE_ID)); interpreter.set(DATA_CONTEXT_SOURCE_NODE_GROUP_ID, context.get(DATA_CONTEXT_SOURCE_NODE_GROUP_ID)); interpreter.set(DATA_CONTEXT_SOURCE_NODE_EXTERNAL_ID, context.get(DATA_CONTEXT_SOURCE_NODE_EXTERNAL_ID)); if (StringUtils.isNotBlank(globalScript)) { interpreter.eval(globalScript); } interpreter.eval(String.format("%s {\n%s\n}", methodName, transformExpression)); context.put(methodName, Boolean.TRUE); } Object result = interpreter.eval(methodName); if (csvData != null && csvData.getTriggerHistory() != null) { interpreter.unset("sourceSchemaName"); interpreter.unset("sourceCatalogName"); interpreter.unset("sourceTableName"); } for (String columnName : sourceValues.keySet()) { interpreter.unset(columnName.toUpperCase()); interpreter.unset(columnName); } if (result != null) { return result.toString(); } else { return null; } } catch (TargetError evalEx) { Throwable ex = evalEx.getTarget(); if (ex instanceof IgnoreColumnException) { throw (IgnoreColumnException) ex; } else if (ex instanceof IgnoreRowException) { throw (IgnoreRowException) ex; } else { throw new TransformColumnException(String.format("Beanshell script error on line %d for target column %s on transform %s", evalEx.getErrorLineNumber(), column.getTargetColumnName(), column.getTransformId()), ex); } } catch (Exception ex) { if (ex instanceof IgnoreColumnException) { throw (IgnoreColumnException) ex; } else if (ex instanceof IgnoreRowException) { throw (IgnoreRowException) ex; } else { log.error(String.format("Beanshell script error for target column %s on transform %s", column.getTargetColumnName(), column.getTransformId()), ex); throw new TransformColumnException(ex); } } } protected Interpreter getInterpreter(Context context) { Interpreter interpreter = (Interpreter) context.get(INTERPRETER_KEY); if (interpreter == null) { interpreter = new Interpreter(); context.put(INTERPRETER_KEY, interpreter); } return interpreter; } }