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

com.centurylink.mdw.services.process.BaseActivity Maven / Gradle / Ivy

There is a newer version: 6.1.39
Show newest version
/*
 * Copyright (C) 2017 CenturyLink, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * 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 com.centurylink.mdw.services.process;

import com.centurylink.mdw.activity.ActivityException;
import com.centurylink.mdw.activity.types.GeneralActivity;
import com.centurylink.mdw.activity.types.SuspendableActivity;
import com.centurylink.mdw.app.ApplicationContext;
import com.centurylink.mdw.cache.asset.PackageCache;
import com.centurylink.mdw.config.PropertyException;
import com.centurylink.mdw.config.PropertyManager;
import com.centurylink.mdw.constant.OwnerType;
import com.centurylink.mdw.dataaccess.DataAccessException;
import com.centurylink.mdw.model.Attributes;
import com.centurylink.mdw.model.event.EventType;
import com.centurylink.mdw.model.task.TaskAction;
import com.centurylink.mdw.model.variable.Document;
import com.centurylink.mdw.model.variable.DocumentReference;
import com.centurylink.mdw.model.variable.Variable;
import com.centurylink.mdw.model.variable.VariableInstance;
import com.centurylink.mdw.model.workflow.Package;
import com.centurylink.mdw.model.workflow.Process;
import com.centurylink.mdw.model.workflow.*;
import com.centurylink.mdw.model.workflow.WorkStatus.InternalLogMessage;
import com.centurylink.mdw.monitor.ActivityMonitor;
import com.centurylink.mdw.monitor.MonitorRegistry;
import com.centurylink.mdw.monitor.OfflineMonitor;
import com.centurylink.mdw.pkg.PackageClassLoader;
import com.centurylink.mdw.script.*;
import com.centurylink.mdw.service.data.activity.ImplementorCache;
import com.centurylink.mdw.service.data.process.EngineDataAccess;
import com.centurylink.mdw.service.data.process.EngineDataAccessCache;
import com.centurylink.mdw.service.data.process.ProcessCache;
import com.centurylink.mdw.services.OfflineMonitorTrigger;
import com.centurylink.mdw.util.TransactionWrapper;
import com.centurylink.mdw.util.log.LoggerUtil;
import com.centurylink.mdw.util.log.StandardLogger;
import com.centurylink.mdw.util.timer.TrackingTimer;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Base class that implements the Controlled Activity.
 * All the controlled activities should extend this class.
 */
public abstract class BaseActivity implements GeneralActivity {

    /**
     * Replaced during execution by ActivityLogger.
     */
    protected StandardLogger logger = LoggerUtil.getStandardLogger();
    /**
     * Use this logger for activity output to make it appear in the activity_log.
     */
    protected StandardLogger getLogger() { return logger; }

    public static final String JAVASCRIPT = "JavaScript";
    public static final String GROOVY = "Groovy";
    public static final String JAVA_EL = "javax.el";
    public static final String OUTPUTDOCS = "Output Documents";
    public static final String DISABLED = "disabled";

    private Long workTransitionInstanceId;
    private String returnCode;
    private String returnMessage;
    private List parameters;
    private Attributes attributes;
    private ProcessExecutor engine;
    private TrackingTimer timer;
    private String[] outputDocuments;
    private Activity activityDef;
    private ProcessInstance processInstance;
    private ActivityInstance activityInstance;
    private ActivityRuntimeContext _runtimeContext;
    private Package pkg;

    /**
     * Repopulates variable values in case they've changed during execution.
     */
    public ActivityRuntimeContext getRuntimeContext() {
        for (VariableInstance var : getParameters()) {
            try {
                _runtimeContext.getValues().put(var.getName(), getVariableValue(var.getName()));
            }
            catch (ActivityException ex) {
                logger.error("Error populating " + var.getName(), ex);
            }
        }
        return _runtimeContext;
    }

    /**
     * This version is used by the new engine (ProcessExecuter).
     * @param parameters variable instances of the process instance,
     *    or of the parent process instance when this is in an embedded process
     */
    void prepare(Activity actVO, ProcessInstance pi, ActivityInstance ai, List parameters,
            Long transInstId, TrackingTimer timer, ProcessExecutor engine) {
        try {
            if (timer != null)
                timer.start("Prepare Activity");
            this.engine = engine;
            this.processInstance = pi;
            this.activityDef = actVO;
            this.activityInstance = ai;
            this.workTransitionInstanceId = transInstId;
            this.parameters = parameters;
            this.attributes = actVO.getAttributes();
            this.timer = timer;
            try {
                pkg = PackageCache.getPackage(getMainProcessDefinition().getPackageName());
                ActivityImplementor implementor = ImplementorCache.get(activityDef.getImplementor());
                String category = implementor == null ? GeneralActivity.class.getName() : implementor.getCategory();
                StandardLogger activityLogger = LoggerUtil.getStandardLogger(getClass().getName());
                _runtimeContext = new ActivityRuntimeContext(activityLogger, pkg, getProcessDefinition(), processInstance,
                        getPerformanceLevel(), getEngine().isInService(), activityDef, category, activityInstance,
                        this instanceof SuspendableActivity);
                if (!(logger instanceof ActivityLogger))
                    logger = new ActivityLogger(_runtimeContext);
                for (VariableInstance var : getParameters()) {
                    try {
                        _runtimeContext.getValues().put(var.getName(), getVariableValue(var.getName()));
                    }
                    catch (ActivityException ex) {
                        logger.error("Error populating " + var.getName(), ex);
                    }
                }
            }
            catch (NullPointerException ex) {
                logger.error(ex.getMessage(), ex);
            }
        }
        finally {
            if (timer != null)
                timer.stopAndLogTiming();
        }
    }

    /**
     * This version is used to initialize adapter activities to be called
     * from API (not from runtime engine) -- mainly for unit testing
     */
    public void prepare(ActivityRuntimeContext runtimeContext) {
        if (!(logger instanceof ActivityLogger))
            logger = new ActivityLogger(runtimeContext);

        EngineDataAccess edao = EngineDataAccessCache.getInstance(true, 9);
        // InternalMessenger msgBroker = MessengerFactory.newInternalMessenger();
        engine = new ProcessExecutor(edao, null, false);

        this.processDefinition = this.mainProcessDefinition = runtimeContext.getProcess();
        this.processInstance = runtimeContext.getProcessInstance();
        this.pkg = runtimeContext.getPackage();
        this._runtimeContext = runtimeContext;
        this.activityDef = runtimeContext.getActivity();
        this.activityInstance = runtimeContext.getActivityInstance();

        if (runtimeContext.getAttributes() != null) {
            attributes = new Attributes();
            for (String attrName : runtimeContext.getAttributes().keySet())
                attributes.put(attrName, runtimeContext.getAttribute(attrName));
        }

        if (runtimeContext.getValues() != null) {
            parameters = new ArrayList<>();
            for (String varName : runtimeContext.getValues().keySet())
                parameters.add(new VariableInstance(varName, runtimeContext.getValues().get(varName)));
        }
    }

    /**
     * Provides a hook to allow activity implementors to initialize themselves.
     * @param runtimeContext the activity runtime context
     */
    protected void initialize(ActivityRuntimeContext runtimeContext) throws ActivityException {
        // TODO consider funneling all client runtime info access
    }

    protected VariableInstance getVariableInstance(String pName) {
        for (int i = 0; i < parameters.size(); i++) {
            if (pName.equalsIgnoreCase(parameters.get(i).getName())) {
                return parameters.get(i);
            }
        }
        return null;
    }

    void execute(ProcessExecutor engine) throws ActivityException {
        this.engine = engine;
        if (isDisabled()) {
            loginfo("Skipping disabled activity: " + getActivityName());
        }
        else {
            try {
                initialize(_runtimeContext);
                Object ret = execute(_runtimeContext);
                if (ret != null)
                    setReturnCode(String.valueOf(ret));
            }
            finally
            {
                if (Thread.currentThread().getContextClassLoader() instanceof PackageClassLoader)
                    ApplicationContext.resetContextClassLoader();
            }
        }
    }

    void executeTimed(ProcessExecutor engine) throws ActivityException {
        try {
            if (timer != null)
                timer.start("Execute Activity");
            execute(engine);
        }
        finally {
            if (timer != null)
                timer.stopAndLogTiming();
        }
    }

    /**
     * Executes the workflow activity.
     * This method is the main method that subclasses need to override.
     * The implementation in the default implementation does nothing.
     */
    public Object execute(ActivityRuntimeContext runtimeContext) throws ActivityException {
        // compatibility dictates that the default implementation delegate to execute()
        execute();
        return runtimeContext.getCompletionCode();
    }

    /**
     * Return the activity instance ID
     * @return activity instance ID
     */
    protected Long getActivityInstanceId() {
        return activityInstance.getId();
    }

    protected ActivityInstance getActivityInstance() {
        return activityInstance;
    }

    /**
     * Return the owner type of the process instance.
     * The owner is typically an external event or another process
     * instance, but can be other things as well.
     * @return the owner type
     */
    protected String getProcessInstanceOwner() {
        return this.processInstance.getOwner();
    }

    /**
     * Return the ID of the owner of the process instance.
     * The owner is typically an external event or another process
     * instance, but can be other things as well.
     * @return the process instance owner ID.
     */
    protected Long getProcessInstanceOwnerId() {
        return this.processInstance.getOwnerId();
    }

    /**
     * Return the process instance ID
     * @return process instance ID
     */
    protected Long getProcessInstanceId() {
        return processInstance.getId();
    }

    /**
     * This method is used internally by MDW.
     * @return the ID of the transition instance leading to the activity
     *      instance.
     */
    protected Long getWorkTransitionInstanceId() {
        return workTransitionInstanceId;
    }

    /**
     * Return the activity (definition) ID
     * @return activity ID
     */
    protected Long getActivityId() {
        return this.activityDef.getId();
    }

    /**
     * Return the name of the activity (definition).
     * @return name of the activity
     */
    protected String getActivityName() {
        return this.activityDef.getName();
    }

    /**
     * Return the process (definition) ID
     * @return process ID
     */
    protected Long getProcessId() {
        return this.processInstance.getProcessId();
    }


    /**
     * Return the master request ID.
     * @return master request ID
     */
    protected String getMasterRequestId() {
        return this.processInstance.getMasterRequestId();
    }

    /**
     * Return the return code, a.k.a completion code
     * @return the return code.
     */
    protected String getReturnCode() {
        return this.returnCode;
    }
    protected void setReturnMessage(String pMessage) {
        this.returnMessage = pMessage;
    }
    protected String getReturnMessage() {
        return this.returnMessage;
    }
    protected void setReturnCode(String code) {
        this.returnCode = code;
    }

    protected void setProcessInstanceCompletionCode(String code)
            throws ActivityException {
        try {
            if (code!=null) {
                if (code.equals(TaskAction.CANCEL)) code = EventType.EVENTNAME_ABORT;
                else if (code.equals(TaskAction.ABORT))
                    code = EventType.EVENTNAME_ABORT + ":process";
                else if (code.equals(TaskAction.COMPLETE)) code = EventType.EVENTNAME_FINISH;
                else if (code.equals(TaskAction.RETRY )) code = EventType.EVENTNAME_START;
                else code = EventType.EVENTNAME_FINISH + ":" + code;
            }
            engine.setProcessInstanceCompletionCode(getProcessInstanceId(), code);
        } catch (Exception ex) {
            logger.error(ex.getMessage(), ex);
            throw new ActivityException(0, ex.getMessage(),ex);
        }
    }

    protected ProcessInstance getProcessInstance() {
        return this.processInstance;
    }

    private Process processDefinition;
    protected Process getProcessDefinition() {
        if (processDefinition == null) {
            if (processInstance.getInstanceDefinitionId() > 0L)
                processDefinition = ProcessCache.getInstanceDefinition(processInstance.getProcessId(), processInstance.getInstanceDefinitionId());
            if (processDefinition == null) {
                try {
                    processDefinition = ProcessCache.getProcess(processInstance.getProcessId());
                } catch (IOException ex) {
                    logger.error("Error loading process definition for instance " + processInstance.getId(), ex);
                }
            }
            if (processInstance.isEmbedded() && processDefinition != null)
                processDefinition = processDefinition.getSubProcess(new Long(processInstance.getComment()));
        }
        return processDefinition;
    }

    private Process mainProcessDefinition;
    protected Process getMainProcessDefinition() {
        if (mainProcessDefinition == null) {
            if (processInstance.getInstanceDefinitionId() > 0L)
                mainProcessDefinition = ProcessCache.getInstanceDefinition(processInstance.getProcessId(), processInstance.getInstanceDefinitionId());
            if (mainProcessDefinition == null) {
                try {
                    mainProcessDefinition = ProcessCache.getProcess(processInstance.getProcessId());
                } catch (IOException ex) {
                    logger.error("Error loading main definition for process instance " + processInstance.getId(), ex);
                }
            }
        }
        return mainProcessDefinition;
    }

    /**
     * @deprecated use {@link #getValue(String)}
     */
    @Deprecated
    protected Object getParameterValue(String name) {
        VariableInstance var = getVariableInstance(name);
        return var == null ? null : var.getData(getPackage());
    }

    /**
     * @deprecated {@link #getValue(String)}
     */
    @Deprecated
    protected String getParameterStringValue(String name) {
        VariableInstance var = getVariableInstance(name);
        return var == null ? null : var.getStringValue(getPackage());
    }

    /**
     * Get the variable instance ID from its name.
     * Despite the name of the method, the method works for
     * all variables, not just parameters of the process instances.
     * @param name variable name
     * @return the variable instance ID
     */
    protected Long getParameterId(String name) {
        for (int i = 0; i < parameters.size(); i++) {
            if (name.equalsIgnoreCase(parameters.get(i).getName())) {
                return parameters.get(i).getId();
            }
        }
        return null;
    }

    /**
     * Get the variable instance ID from its name.
     * Despite the name of the method, the method works for
     * all variables, not just parameters of the process instances.
     * @param name variable name
     * @return the variable instance ID
     */
    protected String getParameterType(String name) {
        for (VariableInstance varinst : parameters) {
            if (varinst.getName().equals(name)) return varinst.getType();
        }
        Process procdef = getMainProcessDefinition();
        List vs = procdef.getVariables();
        String varName;
        for (int i=0; i getParameters() {
        return parameters;
    }

    @Deprecated
    protected void setParameterValues(Map map)
            throws ActivityException {
        for (String name : map.keySet()) {
            setParameterValue(name, map.get(name));
        }
    }

    /**
     * Set the value of the variable instance.
     * @param name variable name
     * @param value variable value; the value must not be null or
     *    empty string, which will cause database not-null constraint
     *    violation
     * @return variable instance ID
     */
    protected Long setParameterValue(String name, Object value)
            throws ActivityException {
        Long varInstId;
        try {
            VariableInstance varInst = this.getVariableInstance(name);
            if (varInst != null) {
                varInstId = varInst.getId();
                varInst.setData(value);
                engine.updateVariableInstance(varInst, getPackage());
            } else {
                varInst = engine.createVariableInstance(processInstance, name, value);  // This also adds it to ProcessInstance
                varInstId = varInst.getId();
                if (!parameters.contains(varInst))  // Should already be there when added to ProcessInstance
                    parameters.add(varInst);    // This adds to ProcessInstanceVO as well - do not think this ever executes
            }
        } catch (Exception ex) {
            logger.error(ex.getMessage(), ex);
            throw new ActivityException(0, ex.getMessage(),ex);
        }
        return varInstId;
    }

    /**
     * Set the value of the variable as a document reference to the given
     * document. If the variable is already bound to a document reference,
     * the method updates the content of the referred document.
     * The method will throw an exception if the document reference points to
     * a remote document.
     * @param name name
     * @param variableType variable type
     * @param value this is the document
     */
    protected DocumentReference setParameterValueAsDocument(String name, String variableType, Object value)
            throws ActivityException {
        DocumentReference docref = (DocumentReference)getParameterValue(name);
        if (docref == null) {
            docref = createDocument(variableType, value, OwnerType.VARIABLE_INSTANCE, 0L);
            Long varInstId = setParameterValue(name, docref);
            updateDocumentInfo(docref, value.getClass().getName(), OwnerType.VARIABLE_INSTANCE, varInstId);
        } else {
            updateDocumentContent(docref, value, variableType);
            if (!value.getClass().getName().equals(variableType))
                updateDocumentInfo(docref, value.getClass().getName(), null, null);
        }
        return docref;
    }

    /**
     * Method that returns the external event instance data
     *
     * @param externalEventInstId instance id
     * @return EventDetails as String
     */
    protected String getExternalEventInstanceDetails(Long externalEventInstId)
            throws ActivityException {
        DocumentReference docref = new DocumentReference(externalEventInstId);
        Document docvo;
        try {
            docvo = engine.getDocument(docref, false);
        } catch (Exception ex) {
            logger.error(ex.getMessage(), ex);
            throw new ActivityException(ex.getMessage(), ex);
        }
        return docvo==null?null:docvo.getContent(getPackage());
    }

    protected Attributes getAttributes() {
        return attributes;
    }

    /**
     * Get the value of the attribute with the given name
     * @param name attribute name
     * @return the attribute value
     */
    protected String getAttributeValue(String name) {
        return attributes.get(name);
    }

    @Deprecated
    protected String getVariableValueSmart(String name) throws PropertyException {
        return getValueSmart(name,name);
    }

    /**
     * Same as getAttributeValue() except performing translations in
     * the following cases:
     * 
    *
  • If the value starts with “prop:”, treat it as a property * specification and return the property value. * The property specification has the syntax * “prop:property-group/property-name”.
  • *
  • If the value starts with a dollar sign followed by an identifier, * it is treated as a variable name and the method returns the variable instance * value as the result.
  • *
  • If the value starts with string:, * the rest of the value is returned without interpretation. * This may be needed when the literal string contains special * characters that may be misinterpreted as expression.
  • *
  • If the value starts with magic:, * the rest of the value is considered an expression * in Magic Box language. It evaluates the expression and return the result. * If it fails to evaluate, the method throws a PropertyException.
  • *
  • If the value contains { and }, and the text in between is a variable * (a dollar sign followed by variable name), then the value is * obtained by replacing the place holders by corresponding variable values
  • *
  • If the value looks like a Magic expression (if it contains dollar sign * or colon), evaluate the expression and return the result. * If it fails to evaluate, return the original value (instead * of throwing an exception in * the case when the value starts with magic:).
  • *
  • If none of above applies, return the value as it is.
  • *
* * @param name name * @return attribute value (literal or translated). * @throws PropertyException if a translation rule is applied * and translation fails. */ protected String getAttributeValueSmart(String name) { return getValueSmart(attributes.get(name), "A:" + name); } /** * @deprecated {@link #getValue(String)} */ @Deprecated protected String getValueSmart(String value, String tag) { if (value==null) return null; if (value.startsWith("prop:")) { value = this.getProperty(value.substring(5)); } else if (valueIsVariable(value)) { Object valueObj = this.getParameterValue(value.substring(1).trim()); value = valueObj == null ? null : valueObj.toString(); } else if (value.startsWith("string:")) { value = value.substring(7); } else if (value.startsWith("groovy:") || value.startsWith("g:") || value.startsWith("javascript:") || value.startsWith("js:")) { String name = ScriptNaming.getValidName(getActivityName() + "_" + getActivityId() + "_" + tag); Object obj = evaluateExpression(name, value.startsWith("j") ? JAVASCRIPT : GROOVY, value.substring(value.indexOf(':') + 1)); value = obj == null ? null : obj.toString(); } else if (valueIsPlaceHolder(value)) { value = this.translatePlaceHolder(value); } else if (valueIsJavaExpression(value)) { Object obj = evaluateExpression(tag, JAVA_EL, value); value = obj == null ? null : obj.toString(); } return value == null ? null : value.trim(); } /** * General-purpose expression evaluation. * Language is one of Groovy, JavaScript or (optionally) Kotlin. * Variables for this activity instance are bound. */ protected Object evaluateExpression(String name, String language, String expression) throws ExecutionException { if (getPerformanceLevel() < 9) _runtimeContext.setLogPersister(ActivityLogger::persist); try { if (JAVA_EL.equals(language)) { return _runtimeContext.evaluate(expression); } ScriptEvaluator evaluator = getScriptEvaluator(name, language); Process processVO = getMainProcessDefinition(); List varVOs = processVO.getVariables(); Map bindings = new HashMap<>(); bindings.put("runtimeContext", _runtimeContext); for (Variable varVO: varVOs) { Object value = getParameterValue(varVO.getName()); if (value instanceof DocumentReference) { DocumentReference docref = (DocumentReference) value; value = getDocument(docref, varVO.getType()); } bindings.put(varVO.getName(), value); } return evaluator.evaluate(expression, bindings); } catch (ActivityException ex) { throw new ExecutionException(ex.getMessage(), ex); } } protected ScriptEvaluator getScriptEvaluator(String name, String language) throws ExecutionException { if (language == null) throw new NullPointerException("Missing script evaluator language"); ScriptEvaluator evaluator = ExecutorRegistry.getInstance().getEvaluator(language); evaluator.setName(name); return evaluator; } /** * The method checks if a string is of the form of a variable reference, * i.e. a dollar sign followed by an identifier. * This is typically used to check if an attribute specifies a variable. * @param v value * @return true if the argument is a dollar sign followed by an identifier */ protected boolean valueIsVariable(String v) { if (v==null || v.length()<2) return false; if (v.charAt(0)!='$') return false; for (int i=1; i= 0 ? v.indexOf("#{") : v.indexOf("${"); return ((start != -1) && (start < v.indexOf('}'))); } private boolean valueIsPlaceHolder(String v) { int n = v.length(); for (int i=0; i updates = null; // Document variables are up-to-date as of activity start. // Since there is no guaranteed order to the multiple monitors at this point, there cannot be an expectation to keep // the runtimeContext process variables map up-to-date from one monitor to the next. // TODO Implement a way to determine priority/order when having multiple monitors if (monitor.isOffline()) { @SuppressWarnings("unchecked") OfflineMonitor activityOfflineMonitor = (OfflineMonitor) monitor; new OfflineMonitorTrigger<>(activityOfflineMonitor, _runtimeContext).fire(logMessage); } else { if (logMessage == InternalLogMessage.ACTIVITY_START) { updates = monitor.onStart(_runtimeContext); } else if (logMessage == InternalLogMessage.ACTIVITY_EXECUTE) { String compCode = monitor.onExecute(_runtimeContext); if (compCode != null) { loginfo("Activity short-circuited by monitor: " + monitor.getClass().getName() + " with code: " + compCode); return compCode; } } else if (logMessage == InternalLogMessage.ACTIVITY_COMPLETE) { updates = monitor.onFinish(_runtimeContext); } else if (logMessage == InternalLogMessage.ACTIVITY_FAIL) { monitor.onError(_runtimeContext); } else if (logMessage == InternalLogMessage.ACTIVITY_SUSPEND) { monitor.onSuspend(_runtimeContext); } } if (updates != null) { for (String varName : updates.keySet()) { loginfo("Variable: " + varName + " updated by ActivityMonitor: " + monitor.getClass().getName()); setVariableValue(varName, updates.get(varName)); } } } } catch (Exception ex) { logexception(ex.getMessage(), ex); } return null; } public void logInfo(String message) { _runtimeContext.logInfo(message); } /** * @deprecated use {@link #logInfo(String)} */ @Deprecated public void loginfo(String message) { logInfo(message); } public void logDebug(String message) { _runtimeContext.logDebug(message); } /** * @deprecated use {@link #logDebug(String)} */ @Deprecated public void logdebug(String message) { logDebug(message); } public void logWarn(String message) { _runtimeContext.logWarn(message); } /** * @deprecated use {@link #logWarn(String)} */ @Deprecated public void logwarn(String message) { logWarn(message); } public void logError(String message) { _runtimeContext.logError(message); } /** * @deprecated use {@link #logError(String)} */ @Deprecated public void logsevere(String message) { logError(message); } public void logError(String msg, Throwable t) { _runtimeContext.logError(msg, t); } /** * @deprecated use {@link #logError(String, Throwable)} */ @Deprecated public void logexception(String msg, Exception e) { logError(msg, e); } public boolean isLogInfoEnabled() { return _runtimeContext.isLogInfoEnabled(); } public boolean isLogDebugEnabled() { return _runtimeContext.isLogDebugEnabled(); } protected Integer lockActivityInstance() throws ActivityException { try { return engine.lockActivityInstance(this.getActivityInstanceId()); } catch (Exception e) { throw new ActivityException(-1, "Failed to lock activity instance", e); } } protected Integer lockProcessInstance() throws ActivityException { try { return engine.lockProcessInstance(this.getProcessInstanceId()); } catch (Exception e) { throw new ActivityException(-1, "Failed to lock process instance", e); } } TrackingTimer getTimer() { return timer; } protected ProcessExecutor getEngine() { return engine; } /** * Executes a script, passing additional bindings to be made available to the script. * Script should return a value for the result code if the default (null is not desired). * * @param script - the script content * @param language - built-in support for Groovy, and JavaScript (default is Groovy) */ protected Object executeScript(String script, String language, Map addlBindings, String qualifier) throws ActivityException { if (getPerformanceLevel() < 9) _runtimeContext.setLogPersister(ActivityLogger::persist); outputDocuments = attributes.containsKey(OUTPUTDOCS) ? attributes.getList(OUTPUTDOCS).toArray(new String[0]) : new String[0]; Object retObj; try { Process process = getMainProcessDefinition(); List vars = process.getVariables(); Map bindings = new HashMap<>(); for (Variable var: vars) { try { bindings.put(var.getName(), getVariableValue(var.getName())); } catch (ActivityException ex) { logger.error("Error populating " + var.getName(), ex); } } bindings.put("runtimeContext", _runtimeContext); if (addlBindings != null) { bindings.putAll(addlBindings); } ScriptExecutor executor = getScriptExecutor(language, qualifier); retObj = executor.execute(script, bindings); for (Variable var : vars) { String variableName = var.getName(); Object bindValue = bindings.get(variableName); String varType = var.getType(); Object value = bindValue; if (varType.equals("java.lang.String") && value != null) value = value.toString(); // convert to string setVariableValue(variableName, varType, value); } } catch (Exception ex) { throw new ActivityException(-1, ex.getMessage(), ex); } return retObj; } protected ScriptExecutor getScriptExecutor(String language, String qualifier) throws PropertyException { if (language == null) throw new NullPointerException("Missing script executor language"); ScriptExecutor executor = ExecutorRegistry.getInstance().getExecutor(language); executor.setName(getScriptExecClassName(qualifier)); return executor; } protected String getScriptExecClassName(String qualifier) { String name = getProcessDefinition().getLabel() + "_" + getActivityName() + "_" + getActivityId(); if (qualifier != null) name += "_" + qualifier; return ScriptNaming.getValidName(name); } /** * @deprecated {@link #getValue(String)} */ @Deprecated protected Object getVariableValue(String varName) throws ActivityException { VariableInstance var = getVariableInstance(varName); if (var == null) return null; Object value = var.getData(getPackage()); if (var.isDocument(pkg)) { DocumentReference docref = (DocumentReference)value; if (isOutputDocument(varName)) value = getDocumentForUpdate(docref, var.getType()); else value = getDocument(docref, var.getType()); } return value; } /** * Convenience method to set variable value regardless of type. */ protected void setVariableValue(String varName, Object value) throws ActivityException { Variable variable = getProcessDefinition().getVariable(varName); if (variable == null) throw new ActivityException("No such variable defined for process: " + varName); String varType = variable.getType(); if (getPackage().getTranslator(varType).isDocumentReferenceVariable()) setParameterValueAsDocument(varName, varType, value); else setParameterValue(varName, value); } /** * Convenience method that sets a variable value, including document content. * Meant to be used by Script, Dynamic Java and other activities where it is * not known whether the value of a document has changed after execution. * Note: to update a document value through this method it must be included in getOutputDocuments(). * @param varName variable name * @param varType variable type * @param value new value to set */ protected void setVariableValue(String varName, String varType, Object value) throws ActivityException { if (value == null) return; if (pkg.getTranslator(varType).isDocumentReferenceVariable()) { try { boolean isOutputDoc = isOutputDocument(varName); boolean doUpdate = isOutputDoc; // don't check in production (or for cache-only perf level since old values are not retained) if (!ApplicationContext.isProduction() && (getPerformanceLevel() < 5)) { boolean changed = hasDocumentValueChanged(varName, varType, value); if (changed){ if (!isOutputDoc) { String msg = "*** WARNING: Attempt to change value of non-output document '" + varName + "'"; if (Object.class.getName().equals(varType)) msg += ". Make sure to implement equals()."; logwarn(msg); } } else { doUpdate = false; } } if (doUpdate) { setParameterValueAsDocument(varName, varType, value); } } catch (DataAccessException ex) { throw new ActivityException(ex.getMessage(), ex); } } else { this.setParameterValue(varName, value); } } public int getPerformanceLevel() { return getEngine().getPerformanceLevel(); } public String[] getOutputDocuments() { return outputDocuments; } public void setOutputDocuments(String[] outputDocs) { this.outputDocuments = outputDocs; } protected boolean isOutputDocument(String variableName) { if (outputDocuments == null) return false; for (String outputDoc : outputDocuments) { if (outputDoc.equals(variableName)) return true; } return false; } private boolean hasDocumentValueChanged(String varName, String varType, Object newValue) throws DataAccessException { DocumentReference docRef = (DocumentReference) getParameterValue(varName); if (docRef == null) return newValue != null; Document doc = getEngine().loadDocument(docRef, false); if (doc == null || doc.getContent(getPackage()) == null) return newValue != null; if (newValue == null) return true; // we already know old value is not null String docType = newValue.getClass().getName(); String oldString = doc.getContent(getPackage()); Object oldObject = getPackage().getObjectValue(varType, oldString, true, docType); if (doc.getType().equals(Object.class.getName())) return !newValue.equals(oldObject); // general comparison involves reserializing since round-trip results are not guaranteed oldString = getPackage().getStringValue(varType, oldObject, true); String newString = getPackage().getStringValue(varType, newValue, true); return !newString.equals(oldString); } public TransactionWrapper startTransaction() throws ActivityException { try { return engine.startTransaction(); } catch (DataAccessException e) { throw new ActivityException(0, e.getMessage(), e); } } public void stopTransaction(TransactionWrapper transaction) throws ActivityException { try { engine.stopTransaction(transaction); } catch (DataAccessException e) { throw new ActivityException(0, e.getMessage(), e); } } protected boolean isDisabled() throws ActivityException { try { return "true".equalsIgnoreCase(getAttributeValueSmart(DISABLED)); } catch (PropertyException ex) { throw new ActivityException(ex.getMessage(), ex); } } /** * Get a runtime value. * @param name can be a variable name or expression */ protected Object getValue(String name) throws ActivityException { if (ProcessRuntimeContext.isExpression(name)) { return getRuntimeContext().evaluate(name); } else { return getVariableValue(name); } } protected Map getValues() { return _runtimeContext.getValues(); } /** * Set a runtime value. * @param name can be a variable name or expression * @param value the value to set */ protected void setValue(String name, Object value) throws ActivityException { if (ProcessRuntimeContext.isExpression(name)) { // create or update document variable referenced by expression ActivityRuntimeContext runtimeContext = getRuntimeContext(); runtimeContext.set(name, value); String rootVar = name.substring(2, name.indexOf('.')); Variable doc = runtimeContext.getProcess().getVariable(rootVar); String stringValue = runtimeContext.getPackage().getStringValue(doc.getType(), runtimeContext.evaluate("${" + rootVar + "}")); setParameterValueAsDocument(rootVar, doc.getType(), stringValue); } else { setVariableValue(name, value); } } public Object getRequiredVariableValue(String name) throws ActivityException { if (getMainProcessDefinition().getVariable(name) == null) throw new ActivityException("Missing process variable: " + name); return getVariableValue(name); } protected String getAttribute(String name) { String v = getAttributeValueSmart(name); return v == null || v.isEmpty() ? null : v; } protected boolean getAttribute(String name, Boolean defaultValue) { String v = getAttribute(name); return v == null ? defaultValue : v.equalsIgnoreCase("true"); } protected int getAttribute(String name, Integer defaultValue) { String v = getAttribute(name); return v == null ? defaultValue : Integer.parseInt(v); } protected String getAttribute(String name, String defaultValue) { String v = getAttribute(name); return v == null ? defaultValue : v; } protected String getRequiredAttribute(String name) throws ActivityException { String v = getAttribute(name); if (v == null) throw new ActivityException("Missing required attribute: " + name); return v; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy