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

de.viadee.bpm.vPAV.processing.ProcessVariableReader Maven / Gradle / Ivy

Go to download

The tool checks Camunda projects for consistency and discovers errors in process-driven applications. Called as a JUnit test, it discovers esp. inconsistencies of a given BPMN model in the classpath and the sourcecode of an underlying java project, such as a delegate reference to a non-existing java class or a non-existing Spring bean.

There is a newer version: 3.0.8
Show newest version
/**
 * BSD 3-Clause License
 *
 * Copyright © 2018, viadee Unternehmensberatung AG
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * * Redistributions of source code must retain the above copyright notice, this
 *   list of conditions and the following disclaimer.
 *
 * * Redistributions in binary form must reproduce the above copyright notice,
 *   this list of conditions and the following disclaimer in the documentation
 *   and/or other materials provided with the distribution.
 *
 * * Neither the name of the copyright holder nor the names of its
 *   contributors may be used to endorse or promote products derived from
 *   this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package de.viadee.bpm.vPAV.processing;

import de.viadee.bpm.vPAV.BpmnScanner;
import de.viadee.bpm.vPAV.FileScanner;
import de.viadee.bpm.vPAV.RuntimeConfig;
import de.viadee.bpm.vPAV.constants.BpmnConstants;
import de.viadee.bpm.vPAV.constants.ConfigConstants;
import de.viadee.bpm.vPAV.processing.model.data.*;
import org.camunda.bpm.engine.impl.juel.Builder;
import org.camunda.bpm.engine.impl.juel.IdentifierNode;
import org.camunda.bpm.engine.impl.juel.Tree;
import org.camunda.bpm.engine.impl.juel.TreeBuilder;
import org.camunda.bpm.model.bpmn.Query;
import org.camunda.bpm.model.bpmn.impl.BpmnModelConstants;
import org.camunda.bpm.model.bpmn.instance.*;
import org.camunda.bpm.model.bpmn.instance.camunda.*;
import org.camunda.bpm.model.dmn.Dmn;
import org.camunda.bpm.model.dmn.DmnModelInstance;
import org.camunda.bpm.model.dmn.instance.Decision;
import org.camunda.bpm.model.dmn.instance.InputExpression;
import org.camunda.bpm.model.dmn.instance.Output;
import org.camunda.bpm.model.dmn.instance.Text;
import org.camunda.bpm.model.xml.instance.ModelElementInstance;

import javax.el.ELException;
import java.io.InputStream;
import java.util.*;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * search process variables for an bpmn element
 *
 */
public final class ProcessVariableReader {

	private final Map decisionRefToPathMap;

	private final BpmnScanner bpmnScanner;

	public static final Logger LOGGER = Logger.getLogger(ProcessVariableReader.class.getName());

	public ProcessVariableReader(final Map decisionRefToPathMap, BpmnScanner scanner) {
		this.decisionRefToPathMap = decisionRefToPathMap;
		this.bpmnScanner = scanner;
	}

	/**
	 * Examining an bpmn element for variables
	 * 
	 * @param context
	 *            JavaReaderContext (static vs regex)
	 * @param fileScanner
	 *            FileScanner
	 * @param element
	 *            BpmnElement
	 * @param processVariables
	 *            process variable operation
	 * @return returns processVariables
	 */
	public LinkedHashMap getVariablesFromElement(final JavaReaderContext context,
			final FileScanner fileScanner, final BpmnElement element,
			final LinkedHashMap processVariables) {

		// 1) Search variables in task
		processVariables.putAll(getVariablesFromTask(context, fileScanner, element));
		// 2) Search variables in sequence flow
		processVariables.putAll(searchVariablesFromSequenceFlow(context, fileScanner, element));
		// 3) Search variables in ExtensionElements
		processVariables.putAll(searchExtensionsElements(context, fileScanner, element));
		// 4) Search variables in In/Output Parameters
		processVariables.putAll(getVariablesFromParameters(element));
		// 5) Search variables in Signal and Messagenames
		processVariables.putAll(getVariablesFromNames(element));

		return processVariables;
	}

	/**
	 * 
	 * Retrieve process variables from names
	 * 
	 * @param element
	 *            BpmnElement
	 * @return processVariables returns processVariables
	 */
	private LinkedHashMap getVariablesFromNames(final BpmnElement element) {
		final LinkedHashMap processVariables = new LinkedHashMap();
		final BaseElement baseElement = element.getBaseElement();
		final BpmnModelElementInstance scopeElement = baseElement.getScope();

		String scopeElementId = null;
		if (scopeElement != null) {
			scopeElementId = scopeElement.getAttributeValue(BpmnConstants.ATTR_ID);
		}

		ArrayList signalRefs = bpmnScanner.getSignalRefs(element.getBaseElement().getId());
		ArrayList messagesRefs = bpmnScanner.getMessageRefs(element.getBaseElement().getId());

		ArrayList signalVariables = getSignalVariables(signalRefs, element);
		ArrayList messageVariables = getMessageVariables(messagesRefs, element);

		for (String variable : messageVariables) {
			processVariables.put(variable,
					new ProcessVariableOperation(variable, element, ElementChapter.General,
							KnownElementFieldType.Message, element.getProcessdefinition(), VariableOperation.READ,
							scopeElementId));
		}

		for (String variable : signalVariables) {
			processVariables.put(variable,
					new ProcessVariableOperation(variable, element, ElementChapter.General,
							KnownElementFieldType.Signal, element.getProcessdefinition(), VariableOperation.READ,
							scopeElementId));
		}

		return processVariables;
	}

	/**
	 * 
	 * Retrieves variables from signal
	 * 
	 * @param signalRefs
	 *            List of signal references
	 * @param element
	 *            BpmnElement
	 * @return variables
	 */
	private ArrayList getSignalVariables(ArrayList signalRefs, BpmnElement element) {
		ArrayList names = new ArrayList();

		for (String signalID : signalRefs) {
			names.add(bpmnScanner.getSignalName(signalID));
		}

		ArrayList variables = new ArrayList();
		for (String signalName : names) {
			variables.addAll(checkExpressionForReadVariable(signalName, element));
		}

		return variables;
	}

	/**
	 * 
	 * Retrieves variables from message
	 * 
	 * @param messageRefs
	 *            List of message references
	 * @param element
	 *            BpmnElement
	 * @return variables
	 */
	private ArrayList getMessageVariables(ArrayList messageRefs, BpmnElement element) {
		ArrayList names = new ArrayList();

		for (String messageID : messageRefs) {
			names.add(bpmnScanner.getMessageName(messageID));
		}

		ArrayList variables = new ArrayList();
		for (String messageName : names) {
			variables.addAll(checkExpressionForReadVariable(messageName, element));
		}

		return variables;
	}

	/**
	 * Analyze Output Parameters for variables
	 *
	 * @param element
	 *            BpmnElement
	 * @return Map of ProcessVariable
	 *
	 */
	private LinkedHashMap getVariablesFromParameters(final BpmnElement element) {
		final LinkedHashMap processVariables = new LinkedHashMap();
		final BaseElement baseElement = element.getBaseElement();
		final BpmnModelElementInstance scopeElement = baseElement.getScope();

		String scopeElementId = null;
		if (scopeElement != null) {
			scopeElementId = scopeElement.getAttributeValue(BpmnConstants.ATTR_ID);
		}

		ArrayList outVar = bpmnScanner.getOutputVariables(element.getBaseElement().getId());
		ArrayList inVar = bpmnScanner.getInputVariables(element.getBaseElement().getId());
		ArrayList varValues = bpmnScanner.getInOutputVariablesValue(element.getBaseElement().getId());

		// save output variables
		for (String name : outVar) {
			processVariables.put(name,
					new ProcessVariableOperation(name, element, ElementChapter.InputOutput,
							KnownElementFieldType.OutputParameter, element.getProcessdefinition(),
							VariableOperation.WRITE, scopeElementId));
		}

		ArrayList varValueClean = new ArrayList();
		for (String expression : varValues) {
			varValueClean.addAll(checkExpressionForReadVariable(expression, element));
		}
		varValueClean.removeAll(inVar);

		// add all processVariables to List
		for (String var : varValueClean) {
			processVariables.put(var,
					new ProcessVariableOperation(var, element, ElementChapter.InputOutput,
							KnownElementFieldType.OutputParameter, element.getProcessdefinition(),
							VariableOperation.READ, scopeElementId));
		}

		for (String name : outVar)
			processVariables.put(name,
					new ProcessVariableOperation(name, element, ElementChapter.InputOutput,
							KnownElementFieldType.OutputParameter, element.getProcessdefinition(),
							VariableOperation.WRITE, scopeElementId));

		return processVariables;
	}

	/**
	 * Analyze bpmn extension elements for variables
	 *
	 * @param context
	 *            JavaReaderContext
	 * @param fileScanner
	 *            FileScanner
	 * @param element
	 *            BpmnElement
	 * @return variables
	 */
	private LinkedHashMap searchExtensionsElements(final JavaReaderContext context,
			final FileScanner fileScanner, final BpmnElement element) {

		final LinkedHashMap processVariables = new LinkedHashMap();
		final BaseElement baseElement = element.getBaseElement();
		final BpmnModelElementInstance scopeElement = baseElement.getScope();
		String scopeElementId = null;
		if (scopeElement != null) {
			scopeElementId = scopeElement.getAttributeValue(BpmnConstants.ATTR_ID);
		}
		final ExtensionElements extensionElements = baseElement.getExtensionElements();
		if (extensionElements != null) {
			// 1) Search in Execution Listeners
			processVariables.putAll(getVariablesFromExecutionListener(context, fileScanner, element, extensionElements,
					scopeElementId));

			// 2) Search in Task Listeners
			processVariables.putAll(
					getVariablesFromTaskListener(context, fileScanner, element, extensionElements, scopeElementId));

			// 3) Search in Form Data
			processVariables.putAll(getVariablesFromFormData(element, extensionElements, scopeElementId));

			// 4) Search in Input/Output-Associations (Call Activities)
			processVariables.putAll(searchVariablesInInputOutputExtensions(element, extensionElements, scopeElementId));
		}

		return processVariables;
	}

	/**
	 * Get process variables from execution listeners
	 * 
	 * @param context
	 *            JavaReaderContext
	 * @param fileScanner
	 *            FileScanner
	 * @param element
	 *            Current BPMN Element
	 * @param extensionElements
	 *            Extension elements (e.g. Listeners)
	 * @param scopeId
	 *            Scope ID
	 * @return variables
	 */
	private LinkedHashMap getVariablesFromExecutionListener(
			final JavaReaderContext context, final FileScanner fileScanner, final BpmnElement element,
			final ExtensionElements extensionElements, final String scopeId) {

		final LinkedHashMap processVariables = new LinkedHashMap();
		List listenerList = extensionElements.getElementsQuery()
				.filterByType(CamundaExecutionListener.class).list();
		for (final CamundaExecutionListener listener : listenerList) {
			final String l_expression = listener.getCamundaExpression();
			if (l_expression != null) {
				processVariables.putAll(findVariablesInExpression(context, fileScanner, l_expression, element,
						ElementChapter.ExecutionListener, KnownElementFieldType.Expression, scopeId));
			}
			final String l_delegateExpression = listener.getCamundaDelegateExpression();
			if (l_delegateExpression != null) {
				processVariables.putAll(findVariablesInExpression(context, fileScanner, l_delegateExpression, element,
						ElementChapter.ExecutionListener, KnownElementFieldType.DelegateExpression, scopeId));
			}
			final String l_class = listener.getCamundaClass();
			if (l_class != null) {
				LOGGER.warning("Entered getVariablesFromJavaDelegate from getVariablesFromExecutionListener for "
						+ element.getBaseElement().getAttributeValue("name"));
				processVariables.putAll(context.readJavaDelegate(fileScanner, listener.getCamundaClass(), element,
						ElementChapter.ExecutionListener, KnownElementFieldType.Class, scopeId));
			}
			final CamundaScript script = listener.getCamundaScript();
			if (script != null && script.getCamundaScriptFormat() != null
					&& script.getCamundaScriptFormat().equals(ConfigConstants.GROOVY)) {
				// inline script or external file?
				final String inlineScript = script.getTextContent();
				if (inlineScript != null && inlineScript.trim().length() > 0) {
					processVariables.putAll(
							ResourceFileReader.searchProcessVariablesInCode(element, ElementChapter.ExecutionListener,
									KnownElementFieldType.InlineScript, null, scopeId, inlineScript));
				} else {
					final String resourcePath = script.getCamundaResource();
					if (resourcePath != null) {
						processVariables.putAll(getVariablesFromGroovyScript(resourcePath, element,
								ElementChapter.ExecutionListener, KnownElementFieldType.ExternalScript, scopeId));
					}
				}
			}
		}
		return processVariables;
	}

	/**
	 * Get process variables from task listeners
	 *
	 *  @param context JavaReaderContext
	 * 
	 * @param fileScanner
	 *            FileScanner
	 * @param element
	 *            BpmnElement
	 * @param extensionElements
	 *            ExtensionElements
	 * @param scopeId
	 *            ScopeId
	 * @return variables
	 */
	private LinkedHashMap getVariablesFromTaskListener(
			final JavaReaderContext context, final FileScanner fileScanner, final BpmnElement element,
			final ExtensionElements extensionElements, final String scopeId) {

		final LinkedHashMap processVariables = new LinkedHashMap();
		List listenerList = extensionElements.getElementsQuery()
				.filterByType(CamundaTaskListener.class).list();
		for (final CamundaTaskListener listener : listenerList) {
			final String l_expression = listener.getCamundaExpression();
			if (l_expression != null) {
				processVariables.putAll(findVariablesInExpression(context, fileScanner, l_expression, element,
						ElementChapter.TaskListener, KnownElementFieldType.Expression, scopeId));
			}
			final String l_delegateExpression = listener.getCamundaDelegateExpression();
			if (l_delegateExpression != null) {
				processVariables.putAll(findVariablesInExpression(context, fileScanner, l_delegateExpression, element,
						ElementChapter.TaskListener, KnownElementFieldType.DelegateExpression, scopeId));
			}
			processVariables
					.putAll(new JavaReaderRegex().getVariablesFromJavaDelegate(fileScanner, listener.getCamundaClass(),
							element, ElementChapter.TaskListener, KnownElementFieldType.Class, scopeId));

			final CamundaScript script = listener.getCamundaScript();
			if (script != null && script.getCamundaScriptFormat() != null
					&& script.getCamundaScriptFormat().equals(ConfigConstants.GROOVY)) {
				// inline script or external file?
				final String inlineScript = script.getTextContent();
				if (inlineScript != null && inlineScript.trim().length() > 0) {
					processVariables.putAll(
							ResourceFileReader.searchProcessVariablesInCode(element, ElementChapter.TaskListener,
									KnownElementFieldType.InlineScript, null, scopeId, inlineScript));
				} else {
					final String resourcePath = script.getCamundaResource();
					if (resourcePath != null) {
						processVariables.putAll(getVariablesFromGroovyScript(resourcePath, element,
								ElementChapter.TaskListener, KnownElementFieldType.ExternalScript, scopeId));
					}
				}
			}
		}

		return processVariables;
	}

	/**
	 * Get process variables from form fields (user tasks)
	 *
	 * @param element
	 *            BpmnElement
	 * @param extensionElements
	 *            ExtensionElements
	 * @param scopeElementId
	 *            ScopeElementId
	 * @return variables
	 */
	private LinkedHashMap getVariablesFromFormData(final BpmnElement element,
			final ExtensionElements extensionElements, final String scopeElementId) {

		final LinkedHashMap processVariables = new LinkedHashMap();

		final Query formDataQuery = extensionElements.getElementsQuery()
				.filterByType(CamundaFormData.class);
		if (formDataQuery.count() > 0) {
			final CamundaFormData formData = formDataQuery.singleResult();
			if (formData != null) {
				final Collection formFields = formData.getCamundaFormFields();
				for (final CamundaFormField field : formFields) {
					processVariables.put(field.getCamundaId(),
							new ProcessVariableOperation(field.getCamundaId(), element, ElementChapter.FormData,
									KnownElementFieldType.FormField, null, VariableOperation.WRITE, scopeElementId));
				}
			}
		}

		return processVariables;
	}

	/**
	 * Get process variables from camunda input/output associations (call
	 * activities)
	 *
	 * @param element
	 *            BpmnElement
	 * @param extensionElements
	 *            ExtensionElements
	 * @param scopeId
	 *            ScopeId
	 * @return variables
	 */
	private LinkedHashMap searchVariablesInInputOutputExtensions(
			final BpmnElement element, final ExtensionElements extensionElements, final String scopeId) {

		final LinkedHashMap processVariables = new LinkedHashMap();

		final BaseElement baseElement = element.getBaseElement();

		if (baseElement instanceof CallActivity) {
			final List inputAssociations = extensionElements.getElementsQuery().filterByType(CamundaIn.class)
					.list();
			for (final CamundaIn inputAssociation : inputAssociations) {
				final String source = inputAssociation.getCamundaSource();
				if (source != null && !source.isEmpty()) {
					processVariables.put(source, new ProcessVariableOperation(source, element, ElementChapter.InputData,
							KnownElementFieldType.CamundaIn, null, VariableOperation.READ, scopeId));
				}
			}
			final List outputAssociations = extensionElements.getElementsQuery()
					.filterByType(CamundaOut.class).list();
			for (final CamundaOut outputAssociation : outputAssociations) {
				final String target = outputAssociation.getCamundaTarget();
				if (target != null && !target.isEmpty()) {
					processVariables.put(target,
							new ProcessVariableOperation(target, element, ElementChapter.OutputData,
									KnownElementFieldType.CamundaOut, null, VariableOperation.WRITE, scopeId));
				}
			}
		}

		return processVariables;
	}

	/**
	 * Get process variables from sequence flow conditions
	 *
	 * @param context
	 *            JavaReaderContext
	 * @param fileScanner
	 *            FileScanner
	 * @param element
	 *            BpmnElement
	 * @return variables
	 */
	private LinkedHashMap searchVariablesFromSequenceFlow(
			final JavaReaderContext context, final FileScanner fileScanner, final BpmnElement element) {

		final LinkedHashMap variables = new LinkedHashMap();
		final BaseElement baseElement = element.getBaseElement();
		if (baseElement instanceof SequenceFlow) {
			final SequenceFlow flow = (SequenceFlow) baseElement;
			BpmnModelElementInstance scopeElement = flow.getScope();
			String scopeId = null;
			if (scopeElement != null) {
				scopeId = scopeElement.getAttributeValue(BpmnConstants.ATTR_ID);
			}
			final ConditionExpression expression = flow.getConditionExpression();
			if (expression != null) {
				if (expression.getLanguage() != null && expression.getLanguage().equals(ConfigConstants.GROOVY)) {
					// inline script or external file?
					final String inlineScript = expression.getTextContent();
					if (inlineScript != null && inlineScript.trim().length() > 0) {
						variables
								.putAll(ResourceFileReader.searchProcessVariablesInCode(element, ElementChapter.Details,
										KnownElementFieldType.InlineScript, scopeId, null, inlineScript));
					} else {
						final String resourcePath = expression.getCamundaResource();
						if (resourcePath != null) {
							variables.putAll(getVariablesFromGroovyScript(resourcePath, element, ElementChapter.Details,
									KnownElementFieldType.ExternalScript, scopeId));
						}
					}
				} else {
					if (expression.getTextContent().trim().length() > 0) {
						variables.putAll(findVariablesInExpression(context, fileScanner, expression.getTextContent(),
								element, ElementChapter.Details, KnownElementFieldType.Expression, scopeId));
					}
				}
			}
		}
		return variables;
	}

	/**
	 * Analyse all types of tasks for process variables
	 *
	 * @param context
	 *            JavaReaderContext
	 * @param fileScanner
	 *            FileScanner
	 * @param element
	 *            BpmnElement
	 * @return variables
	 */
	private Map getVariablesFromTask(final JavaReaderContext context,
			final FileScanner fileScanner, final BpmnElement element) {

		final Map processVariables = new HashMap();

		final BaseElement baseElement = element.getBaseElement();
		BpmnModelElementInstance scopeElement = baseElement.getScope();

		String scopeId = null;
		if (scopeElement != null) {
			scopeId = scopeElement.getAttributeValue(BpmnConstants.ATTR_ID);
		}
		if (baseElement instanceof ServiceTask || baseElement instanceof SendTask
				|| baseElement instanceof BusinessRuleTask) {
			final String t_expression = baseElement.getAttributeValueNs(BpmnModelConstants.CAMUNDA_NS,
					BpmnConstants.ATTR_EX);
			if (t_expression != null) {

				processVariables.putAll(findVariablesInExpression(context, fileScanner, t_expression, element,
						ElementChapter.Details, KnownElementFieldType.Expression, scopeId));
			}

			final String t_delegateExpression = baseElement.getAttributeValueNs(BpmnModelConstants.CAMUNDA_NS,
					BpmnConstants.ATTR_DEL);
			if (t_delegateExpression != null) {
				processVariables.putAll(findVariablesInExpression(context, fileScanner, t_delegateExpression, element,
						ElementChapter.Details, KnownElementFieldType.DelegateExpression, scopeId));
			}

			final ArrayList t_fieldInjectionExpressions = bpmnScanner
					.getFieldInjectionExpression(baseElement.getId());
			if (t_fieldInjectionExpressions != null && !t_fieldInjectionExpressions.isEmpty()) {
				for (String t_fieldInjectionExpression : t_fieldInjectionExpressions)
					processVariables.putAll(findVariablesInExpression(context, fileScanner, t_fieldInjectionExpression,
							element, ElementChapter.FieldInjections, KnownElementFieldType.Expression, scopeId));
			}

			final String t_resultVariable = baseElement.getAttributeValueNs(BpmnModelConstants.CAMUNDA_NS,
					BpmnConstants.RESULT_VARIABLE);
			if (t_resultVariable != null && t_resultVariable.trim().length() > 0) {
				processVariables.put(t_resultVariable,
						new ProcessVariableOperation(t_resultVariable, element, ElementChapter.Details,
								KnownElementFieldType.ResultVariable, null, VariableOperation.WRITE, scopeId));
			}

			if (baseElement.getAttributeValueNs(BpmnModelConstants.CAMUNDA_NS, BpmnConstants.ATTR_CLASS) != null) {
				processVariables.putAll(context.readJavaDelegate(fileScanner,
						baseElement.getAttributeValueNs(BpmnModelConstants.CAMUNDA_NS, BpmnConstants.ATTR_CLASS),
						element, ElementChapter.Details, KnownElementFieldType.Class, scopeId));
			}

			if (baseElement instanceof BusinessRuleTask) {
				final String t_decisionRef = baseElement.getAttributeValueNs(BpmnModelConstants.CAMUNDA_NS,
						BpmnConstants.DECISIONREF);
				if (t_decisionRef != null && t_decisionRef.trim().length() > 0 && decisionRefToPathMap != null) {
					final String fileName = decisionRefToPathMap.get(t_decisionRef);
					if (fileName != null) {
						processVariables.putAll(readDmnFile(t_decisionRef, fileName, element, ElementChapter.Details,
								KnownElementFieldType.DMN, scopeId));
					}
				}
			}

		} else if (baseElement instanceof UserTask) {
			final UserTask userTask = (UserTask) baseElement;
			final String assignee = userTask.getCamundaAssignee();
			if (assignee != null)
				processVariables.putAll(findVariablesInExpression(context, fileScanner, assignee, element,
						ElementChapter.Details, KnownElementFieldType.Assignee, scopeId));
			final String candidateUsers = userTask.getCamundaCandidateUsers();
			if (candidateUsers != null)
				processVariables.putAll(findVariablesInExpression(context, fileScanner, candidateUsers, element,
						ElementChapter.Details, KnownElementFieldType.CandidateUsers, scopeId));
			final String candidateGroups = userTask.getCamundaCandidateGroups();
			if (candidateGroups != null)
				processVariables.putAll(findVariablesInExpression(context, fileScanner, candidateGroups, element,
						ElementChapter.Details, KnownElementFieldType.CandidateGroups, scopeId));
			final String dueDate = userTask.getCamundaDueDate();
			if (dueDate != null)
				processVariables.putAll(findVariablesInExpression(context, fileScanner, dueDate, element,
						ElementChapter.Details, KnownElementFieldType.DueDate, scopeId));
			final String followUpDate = userTask.getCamundaFollowUpDate();
			if (followUpDate != null)
				processVariables.putAll(findVariablesInExpression(context, fileScanner, followUpDate, element,
						ElementChapter.Details, KnownElementFieldType.FollowUpDate, scopeId));

		} else if (baseElement instanceof ScriptTask) {
			// Examine script task for process variables
			final ScriptTask scriptTask = (ScriptTask) baseElement;
			if (scriptTask.getScriptFormat() != null && scriptTask.getScriptFormat().equals(ConfigConstants.GROOVY)) {
				// inline script or external file?
				final Script script = scriptTask.getScript();
				if (script != null && script.getTextContent() != null && script.getTextContent().trim().length() > 0) {
					processVariables
							.putAll(ResourceFileReader.searchProcessVariablesInCode(element, ElementChapter.Details,
									KnownElementFieldType.InlineScript, null, scopeId, script.getTextContent()));
				} else {
					final String resourcePath = scriptTask.getCamundaResource();
					if (resourcePath != null) {
						processVariables.putAll(getVariablesFromGroovyScript(resourcePath, element,
								ElementChapter.Details, KnownElementFieldType.ExternalScript, scopeId));
					}
				}
			}
			String resultVariable = scriptTask.getCamundaResultVariable();
			if (resultVariable != null && resultVariable.trim().length() > 0) {
				processVariables.put(resultVariable,
						new ProcessVariableOperation(resultVariable, element, ElementChapter.Details,
								KnownElementFieldType.ResultVariable, null, VariableOperation.WRITE, scopeId));
			}
		} else if (baseElement instanceof CallActivity) {
			final CallActivity callActivity = (CallActivity) baseElement;
			final String calledElement = callActivity.getCalledElement();
			if (calledElement != null && calledElement.trim().length() > 0) {
				processVariables.putAll(findVariablesInExpression(context, fileScanner, calledElement, element,
						ElementChapter.Details, KnownElementFieldType.CalledElement, scopeId));
			}
			final String caseRef = callActivity.getAttributeValueNs(BpmnModelConstants.CAMUNDA_NS,
					BpmnConstants.CASEREF);
			if (caseRef != null && caseRef.trim().length() > 0) {
				processVariables.putAll(findVariablesInExpression(context, fileScanner, caseRef, element,
						ElementChapter.Details, KnownElementFieldType.CaseRef, scopeId));
			}
		}

		// Check multi instance attributes
		processVariables.putAll(searchVariablesInMultiInstanceTask(context, fileScanner, element));

		return processVariables;
	}

	/**
	 * Examine multi instance tasks for process variables
	 *
	 * @param context
	 *            JavaReaderContext
	 * @param fileScanner
	 *            FileScanner
	 * @param element
	 *            BpmnElement
	 * @return variables
	 */
	private Map searchVariablesInMultiInstanceTask(final JavaReaderContext context,
			final FileScanner fileScanner, final BpmnElement element) {

		final Map processVariables = new HashMap();

		final BaseElement baseElement = element.getBaseElement();
		BpmnModelElementInstance scopeElement = baseElement.getScope();
		String scopeId = null;
		if (scopeElement != null) {
			scopeId = scopeElement.getAttributeValue(BpmnConstants.ATTR_ID);
		}
		final ModelElementInstance loopCharacteristics = baseElement
				.getUniqueChildElementByType(LoopCharacteristics.class);
		if (loopCharacteristics != null) {
			final String collectionName = loopCharacteristics.getAttributeValueNs(BpmnModelConstants.CAMUNDA_NS,
					BpmnConstants.COLLECTION);
			if (collectionName != null && collectionName.trim().length() > 0) {
				processVariables.put(collectionName,
						new ProcessVariableOperation(collectionName, element, ElementChapter.MultiInstance,
								KnownElementFieldType.CollectionElement, null, VariableOperation.READ, scopeId));
			}
			final String elementVariable = loopCharacteristics.getAttributeValueNs(BpmnModelConstants.CAMUNDA_NS,
					BpmnConstants.ELEMENT_VARIABLE);
			if (elementVariable != null && elementVariable.trim().length() > 0) {
				processVariables.put(elementVariable,
						new ProcessVariableOperation(elementVariable, element, ElementChapter.MultiInstance,
								KnownElementFieldType.ElementVariable, null, VariableOperation.READ, scopeId));
			}
			final ModelElementInstance loopCardinality = loopCharacteristics
					.getUniqueChildElementByType(LoopCardinality.class);
			if (loopCardinality != null) {
				final String cardinality = loopCardinality.getTextContent();
				if (cardinality != null && cardinality.trim().length() > 0) {
					processVariables.putAll(findVariablesInExpression(context, fileScanner, cardinality, element,
							ElementChapter.MultiInstance, KnownElementFieldType.LoopCardinality, scopeId));
				}
			}
			final ModelElementInstance completionCondition = loopCharacteristics
					.getUniqueChildElementByType(CompletionCondition.class);
			if (completionCondition != null) {
				final String completionConditionExpression = completionCondition.getTextContent();
				if (completionConditionExpression != null && completionConditionExpression.trim().length() > 0) {
					processVariables.putAll(
							findVariablesInExpression(context, fileScanner, completionConditionExpression, element,
									ElementChapter.MultiInstance, KnownElementFieldType.CompletionCondition, scopeId));
				}
			}
		}
		return processVariables;
	}

	/**
	 * Checks an external groovy script for process variables (read/write).
	 *
	 * @param groovyFile
	 *            Groovy File
	 * @param element
	 *            BpmnElement
	 * @param chapter
	 *            ElementChapter
	 * @param fieldType
	 *            KnownElementFieldType
	 * @param scopeId
	 *            ScopeId
	 * @return variables
	 */
	private Map getVariablesFromGroovyScript(final String groovyFile,
			final BpmnElement element, final ElementChapter chapter, final KnownElementFieldType fieldType,
			final String scopeId) {

		final Map variables = ResourceFileReader.readResourceFile(groovyFile, element,
				chapter, fieldType, scopeId);
		return variables;
	}

	/**
	 * Scans a dmn file for process variables
	 *
	 * @param decisionId
	 *            DecisionId
	 * @param fileName
	 *            File Name
	 * @param element
	 *            BpmnElement
	 * @param chapter
	 *            ElementChapter
	 * @param fieldType
	 *            KnownElementFieldType
	 * @param scopeId
	 *            ScopeID
	 * @return
	 */
	private Map readDmnFile(final String decisionId, final String fileName,
			final BpmnElement element, final ElementChapter chapter, final KnownElementFieldType fieldType,
			final String scopeId) {

		final Map variables = new HashMap();

		if (fileName != null && fileName.trim().length() > 0) {
			final InputStream resource = RuntimeConfig.getInstance().getClassLoader().getResourceAsStream(fileName);
			if (resource != null) {
				// parse dmn model
				final DmnModelInstance modelInstance = Dmn.readModelFromStream(resource);
				final Decision decision = modelInstance.getModelElementById(decisionId);
				final Collection inputExpressions = decision.getModelInstance()
						.getModelElementsByType(InputExpression.class);
				for (final InputExpression inputExpression : inputExpressions) {
					final Text variable = inputExpression.getText();
					variables.put(variable.getTextContent(), new ProcessVariableOperation(variable.getTextContent(),
							element, chapter, fieldType, fileName, VariableOperation.READ, scopeId));
				}
				final Collection outputs = decision.getModelInstance().getModelElementsByType(Output.class);
				for (final Output output : outputs) {
					final String variable = output.getName();
					variables.put(variable, new ProcessVariableOperation(variable, element, chapter, fieldType,
							fileName, VariableOperation.WRITE, scopeId));
				}
			}
		}
		return variables;
	}

	/**
	 * Examine JUEL expressions for variables
	 *
	 * @param context
	 *            JavaReaderContext
	 * @param fileScanner
	 *            FileScanner
	 * @param expression
	 *            Expression
	 * @param element
	 *            BpmnElement
	 * @param chapter
	 *            ElementChapter
	 * @param fieldType
	 *            KnownElementFieldType
	 * @param scopeId
	 *            ScopeId
	 * @return variables
	 * @throws ProcessingException
	 */
	private LinkedHashMap findVariablesInExpression(final JavaReaderContext context,
			final FileScanner fileScanner, final String expression, final BpmnElement element,
			final ElementChapter chapter, final KnownElementFieldType fieldType, final String scopeId) {
		final LinkedHashMap variables = new LinkedHashMap();

		// HOTFIX: Catch pattern like below to avoid crash of TreeBuilder
		// ${dateTime().plusWeeks(1).toDate()}
		final Pattern pattern = Pattern.compile("\\$\\{(\\w)*\\(.*\\)\\}");

		Matcher matcher = pattern.matcher(expression);

		if (matcher.matches()) {
			return variables;
		}

		try {
			// remove object name from method calls, otherwise the method arguments could
			// not be found
			final String filteredExpression = expression.replaceAll("[\\w]+\\.", "");
			final TreeBuilder treeBuilder = new Builder();
			final Tree tree = treeBuilder.build(filteredExpression);

			final Iterable identifierNodes = tree.getIdentifierNodes();
			for (final IdentifierNode node : identifierNodes) {
				// checks, if found variable is a bean
				final String className = isBean(node.getName());
				if (className != null) {
					// read variables in class file (bean)
					variables.putAll(
							context.readJavaDelegate(fileScanner, className, element, chapter, fieldType, scopeId));
				} else {
					// save variable
					variables.put(node.getName(), new ProcessVariableOperation(node.getName(), element, chapter,
							fieldType, null, VariableOperation.READ, scopeId));
				}
			}
			// extract written variables
			variables.putAll(ResourceFileReader.searchWrittenProcessVariablesInCode(element, chapter, fieldType, null,
					scopeId, expression));
			// extract deleted variables
			variables.putAll(ResourceFileReader.searchRemovedProcessVariablesInCode(element, chapter, fieldType, null,
					scopeId, expression));
		} catch (final ELException e) {
			throw new ProcessingException("EL expression " + expression + " in " + element.getProcessdefinition()
					+ ", element ID: " + element.getBaseElement().getId() + ", Type: " + fieldType.getDescription()
					+ " couldn't be parsed", e);
		}

		return variables;
	}

	/**
	 * Checks an expression for read variable
	 * 
	 * @param expression
	 *            Expression
	 * @param element
	 *            BpmnElement
	 * @return variables
	 */
	private ArrayList checkExpressionForReadVariable(final String expression, final BpmnElement element) {
		final ArrayList variables = new ArrayList();
		try {
			// remove object name from method calls, otherwise the method arguments could
			// not be found
			final String filteredExpression = expression.replaceAll("[\\w]+\\.", "");
			final TreeBuilder treeBuilder = new Builder();
			final Tree tree = treeBuilder.build(filteredExpression);

			final Iterable identifierNodes = tree.getIdentifierNodes();
			for (final IdentifierNode node : identifierNodes) {
				// checks, if found variable is a bean
				if (isBean(node.getName()) == null) {
					variables.add(node.getName());
				}
			}
		} catch (final ELException e) {
			throw new ProcessingException("EL expression " + expression + " in " + element.getProcessdefinition()
					+ ", element ID: " + element.getBaseElement().getId() + ", Type: "
					+ KnownElementFieldType.Expression + " couldn't be parsed", e);
		}

		return variables;
	}

	/**
	 * Checks a variable being a bean
	 *
	 * @param variable
	 * @return classpath to bean definition
	 */
	private String isBean(final String variable) {
		if (RuntimeConfig.getInstance().getBeanMapping() != null) {
			return RuntimeConfig.getInstance().getBeanMapping().get(variable);
		}
		return null;
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy