Java tutorial
/* * Copyright (c) 2004-2012 The YAWL Foundation. All rights reserved. * The YAWL Foundation is a collaboration of individuals and * organisations who are committed to improving workflow technology. * * This file is part of YAWL. YAWL is free software: you can * redistribute it and/or modify it under the terms of the GNU Lesser * General Public License as published by the Free Software Foundation. * * YAWL is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General * Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with YAWL. If not, see <http://www.gnu.org/licenses/>. */ package org.yawlfoundation.yawl.elements; import net.sf.saxon.s9api.SaxonApiException; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.jdom2.Document; import org.jdom2.Element; import org.yawlfoundation.yawl.elements.data.YParameter; import org.yawlfoundation.yawl.elements.data.YVariable; import org.yawlfoundation.yawl.elements.data.external.AbstractExternalDBGateway; import org.yawlfoundation.yawl.elements.data.external.ExternalDBGatewayFactory; import org.yawlfoundation.yawl.elements.e2wfoj.E2WFOJNet; import org.yawlfoundation.yawl.elements.predicate.PredicateEvaluatorCache; import org.yawlfoundation.yawl.elements.state.YIdentifier; import org.yawlfoundation.yawl.elements.state.YInternalCondition; import org.yawlfoundation.yawl.engine.*; import org.yawlfoundation.yawl.engine.time.YTimerVariable; import org.yawlfoundation.yawl.engine.time.YWorkItemTimer; import org.yawlfoundation.yawl.exceptions.*; import org.yawlfoundation.yawl.logging.YLogDataItemList; import org.yawlfoundation.yawl.schema.YDataValidator; import org.yawlfoundation.yawl.util.JDOMUtil; import org.yawlfoundation.yawl.util.SaxonUtil; import org.yawlfoundation.yawl.util.StringUtil; import org.yawlfoundation.yawl.util.YVerificationHandler; import java.net.URL; import java.util.*; /** * A superclass of any type of task in the YAWL language. * * @author Lachlan Aldred * @author Michael Adams (v2.0 and later) */ public abstract class YTask extends YExternalNetElement { //class members private static final Random _random = new Random(new Date().getTime()); public static final int _AND = 95; public static final int _OR = 103; public static final int _XOR = 126; //internal state nodes protected YIdentifier _i; protected YInternalCondition _mi_active = new YInternalCondition(YInternalCondition._mi_active, this); protected YInternalCondition _mi_entered = new YInternalCondition(YInternalCondition._mi_entered, this); protected YInternalCondition _mi_complete = new YInternalCondition(YInternalCondition._mi_complete, this); protected YInternalCondition _mi_executing = new YInternalCondition(YInternalCondition._mi_executing, this); //private attributes private int _splitType; private int _joinType; protected YMultiInstanceAttributes _multiInstAttr; private Set<YExternalNetElement> _removeSet = new HashSet<YExternalNetElement>(); protected final Map<String, String> _dataMappingsForTaskStarting = new HashMap<String, String>(); //[key=ParamName, value=query] private final Map<String, String> _dataMappingsForTaskCompletion = new HashMap<String, String>(); //[key=query, value=NetVarName] protected final Map<String, String> _dataMappingsForTaskEnablement = new HashMap<String, String>(); //[key=ParamName, value=query] protected YDecomposition _decompositionPrototype; // input data storage private final Map<YIdentifier, Element> _caseToDataMap = new HashMap<YIdentifier, Element>(); private Iterator _multiInstanceSpecificParamsIterator; private Map<String, Element> _localVariableNameToReplaceableOutputData; private Document _groupedMultiInstanceOutputData; // Reset net association private E2WFOJNet _resetNet; // process configuration private String configuration = null; private String defaultConfiguration = null; private Element configurationElement = null; private Element defaultConfigurationElement = null; // task resourcing mappings private String _resourcingXML = null; // populated externally (eg. the editor) private Element _resourcingSpec = null; // populated on spec parse // optional timer params [name, value] private YTimerParameters _timerParams; private YTimerVariable _timerVariable; // optional URI to a custom form (rather than inbuilt dynamic form) private URL _customFormURL; // optional user-defined data items for logging with task instance events private YLogDataItemList _inputLogDataItems; private YLogDataItemList _outputLogDataItems; private static final Logger logger = LogManager.getLogger(YTask.class); /** * AJH: Extensions to cater for task level XML attributes. * <p/> * Encoded list of standard XML attributes used on atomic tasks */ private static final String STANDARD_TASK_ATTRIBUTES = "/id/type/skipOutboundSchemaValidation"; private static final String PERFORM_OUTBOUND_SCHEMA_VALIDATION = "skipOutboundSchemaValidation"; private boolean _skipOutboundSchemaChecks = false; // False by default public void setI(YIdentifier i) { this._i = i; } /** * Constructor * * @param id * @param splitType * @param joinType * @param container */ public YTask(String id, int joinType, int splitType, YNet container) { super(id, container); _splitType = splitType; _joinType = joinType; } public E2WFOJNet getResetNet() { return _resetNet; } public void setResetNet(E2WFOJNet net) { _resetNet = net; } public int getSplitType() { return _splitType; } public int getJoinType() { return _joinType; } public void setSplitType(int splitType) { _splitType = splitType; } public void setJoinType(int joinType) { _joinType = joinType; } public boolean isMultiInstance() { return _multiInstAttr != null && _multiInstAttr.isMultiInstance(); } public String getPredicate(YExternalNetElement netElement) { YFlow flow = getPostsetFlow(netElement); return flow.getXpathPredicate(); } public YMultiInstanceAttributes getMultiInstanceAttributes() { return _multiInstAttr; } public void setUpMultipleInstanceAttributes(String minInstanceQuery, String maxInstanceQuery, String thresholdQuery, String creationMode) { _multiInstAttr = new YMultiInstanceAttributes(this, minInstanceQuery, maxInstanceQuery, thresholdQuery, creationMode); } public void setMultiInstanceInputDataMappings(String remoteVariableName, String inputProcessingExpression) { _multiInstAttr.setMIFormalInputParam(remoteVariableName); _multiInstAttr.setUniqueInputMISplittingQuery(inputProcessingExpression); } public void setMultiInstanceOutputDataMappings(String remoteOutputQuery, String aggregationQuery) { _multiInstAttr.setMIFormalOutputQuery(remoteOutputQuery); _multiInstAttr.setUniqueOutputMIJoiningQuery(aggregationQuery); } public Collection<String> getParamNamesForTaskCompletion() { return _dataMappingsForTaskCompletion.values(); } protected void checkXQuery(String xQuery, String param, YVerificationHandler handler) { if (!StringUtil.isNullOrEmpty(xQuery)) { if (ExternalDBGatewayFactory.isExternalDBMappingExpression(xQuery)) { checkExternalMapping(xQuery, handler); } else { try { SaxonUtil.compileXQuery(xQuery); } catch (SaxonApiException e) { handler.error(this, this + " [id= " + this.getID() + "] the XQuery could not be successfully" + " parsed [" + e.getMessage() + "]"); } } } else handler.error(this, this + " [id= " + this.getID() + "] the XQuery for param [" + param + "] cannot be equal to null or the empty string."); } protected void checkExternalMapping(String query, YVerificationHandler handler) { AbstractExternalDBGateway dbClass = ExternalDBGatewayFactory.getInstance(query); if (dbClass == null) { handler.error(this, this + "(id= " + this.getID() + ") the mapping could not be successfully" + " parsed. External DB Class '" + query + "' was not found."); } } protected Set<String> getParamNamesForTaskEnablement() { return new HashSet<String>(_dataMappingsForTaskEnablement.keySet()); } protected Set<String> getParamNamesForTaskStarting() { return new HashSet<String>(_dataMappingsForTaskStarting.keySet()); } private Set<String> getQueriesForTaskCompletion() { return _dataMappingsForTaskCompletion.keySet(); } public Map<String, String> getDataMappingsForTaskStarting() { return _dataMappingsForTaskStarting; } public Map<String, String> getDataMappingsForTaskCompletion() { return _dataMappingsForTaskCompletion; } public Set<YExternalNetElement> getRemoveSet() { if (_removeSet != null) { return new HashSet<YExternalNetElement>(_removeSet); } return null; } public void addRemovesTokensFrom(List<YExternalNetElement> removeSet) { _removeSet.addAll(removeSet); //Need to add the task to the CancelledBySet as well for (YExternalNetElement element : removeSet) { element.addToCancelledBySet(this); } } // Added for reduction rules - need to use id to check for equal! public void removeFromRemoveSet(YExternalNetElement e) { if (e != null) { _removeSet.remove(e); e.removeFromCancelledBySet(this); } } public synchronized List<YIdentifier> t_fire(YPersistenceManager pmgr) throws YStateException, YDataStateException, YQueryException, YPersistenceException { YIdentifier id = getI(); if (!t_enabled(id)) { throw new YStateException(this + " cannot fire due to not being enabled"); } _i = id; _i.addLocation(pmgr, this); long numToSpawn = determineHowManyInstancesToCreate(); List<YIdentifier> childIdentifiers = new Vector<YIdentifier>(); for (int i = 0; i < numToSpawn; i++) { YIdentifier childID = createFiredIdentifier(pmgr); try { prepareDataForInstanceStarting(childID); } catch (Exception e) { //if there was a problem firing the task then roll back the case. rollbackFired(childID, pmgr); if (e instanceof YDataStateException) throw (YDataStateException) e; else if (e instanceof YStateException) throw (YStateException) e; else if (e instanceof YQueryException) throw (YQueryException) e; } childIdentifiers.add(childID); } prepareDataDocsForTaskOutput(); // contract: all task presetElements are conditions switch (_joinType) { case YTask._AND: for (YExternalNetElement preSetElement : getPresetElements()) { ((YConditionInterface) preSetElement).removeOne(pmgr); } break; case YTask._OR: for (YExternalNetElement preSetElement : getPresetElements()) { YConditionInterface condition = ((YConditionInterface) preSetElement); if (condition.containsIdentifier()) condition.removeOne(pmgr); } break; case YTask._XOR: List<YExternalNetElement> conditions = new Vector<YExternalNetElement>(getPresetElements()); boolean done = false; do { int i = Math.abs(_random.nextInt()) % conditions.size(); YConditionInterface condition = (YConditionInterface) conditions.get(i); if (condition.containsIdentifier()) { condition.removeOne(pmgr); done = true; } } while (!done); break; } return childIdentifiers; } /*changed to public for persistence*/ public void prepareDataDocsForTaskOutput() { if (null == getDecompositionPrototype()) { return; } _groupedMultiInstanceOutputData = new Document(); _groupedMultiInstanceOutputData .setRootElement(new Element(getDecompositionPrototype().getRootDataElementName())); _localVariableNameToReplaceableOutputData = new HashMap<String, Element>(); } public synchronized YIdentifier t_add(YPersistenceManager pmgr, YIdentifier siblingWithPermission, Element newInstanceData) throws YDataStateException, YStateException, YQueryException, YPersistenceException { if (!YMultiInstanceAttributes.CREATION_MODE_DYNAMIC.equals(_multiInstAttr.getCreationMode())) { throw new RuntimeException(this + " does not allow dynamic instance creation."); } if (t_addEnabled(siblingWithPermission)) { List<Element> newData = new Vector<Element>(); newData.add(newInstanceData); _multiInstanceSpecificParamsIterator = newData.iterator(); YIdentifier newInstance = createFiredIdentifier(pmgr); prepareDataForInstanceStarting(newInstance); return newInstance; } return null; } public boolean t_addEnabled(YIdentifier identifier) { return t_isBusy() && isMultiInstance() && YMultiInstanceAttributes.CREATION_MODE_DYNAMIC.equals(_multiInstAttr.getCreationMode()) && _mi_executing.contains(identifier) && _mi_active.getIdentifiers().size() < _multiInstAttr.getMaxInstances(); } private long determineHowManyInstancesToCreate() throws YDataStateException, YQueryException { if (!isMultiInstance()) { return 1; } int max = _multiInstAttr.getMaxInstances(); int min = _multiInstAttr.getMinInstances(); String queryString = getPreSplittingMIQuery(); Element dataToSplit = evaluateTreeQuery(queryString, _net.getInternalDataDocument()); if (dataToSplit == null) { throw new YDataQueryException(queryString, dataToSplit, this.getID(), "No data available for MI splitting at task start"); } generateBeginReport1(); List multiInstanceList = evaluateListQuery(_multiInstAttr.getMISplittingQuery(), dataToSplit); int listSize = multiInstanceList.size(); if (listSize > max || listSize < min) { throw new YDataQueryException(_multiInstAttr.getMISplittingQuery(), dataToSplit, this.getID(), String.format( "The number of instances produced by MI split (%d) is %s than " + "the %s instance bound specified (%d).", listSize, (listSize > max ? "more" : "less"), (listSize > max ? "maximum" : "minimum"), (listSize > max ? max : min))); } _multiInstanceSpecificParamsIterator = multiInstanceList.iterator(); return listSize; } private void generateBeginReport1() { logger.debug("\n\nYTask::firing"); logger.debug("\ttaskID = {}", getID()); } public String getPreSplittingMIQuery() { String miVarNameInDecomposition = _multiInstAttr.getMIFormalInputParam(); return miVarNameInDecomposition != null ? _dataMappingsForTaskStarting.get(miVarNameInDecomposition) : null; } public synchronized boolean t_isExitEnabled() { return t_isBusy() && ((_mi_active.getIdentifiers().containsAll(_mi_complete.getIdentifiers()) && _mi_complete.getIdentifiers().containsAll(_mi_active.getIdentifiers())) || (_mi_complete.getIdentifiers().size() >= _multiInstAttr.getThreshold())); } public synchronized boolean t_complete(YPersistenceManager pmgr, YIdentifier childID, Document decompositionOutputData) throws YDataStateException, YStateException, YQueryException, YPersistenceException { if (t_isBusy()) { YSpecification spec = _net.getSpecification(); YDataValidator validator = spec.getDataValidator(); validateOutputs(validator, decompositionOutputData); for (String query : getQueriesForTaskCompletion()) { if (ExternalDBGatewayFactory.isExternalDBMappingExpression(query)) { AbstractExternalDBGateway gateway = ExternalDBGatewayFactory.getInstance(query); updateExternalFromTaskCompletion(gateway, query, decompositionOutputData); continue; } String localVarThatQueryResultGetsAppliedTo = getMIOutputAssignmentVar(query); Element queryResultElement = evaluateTreeQuery(query, decompositionOutputData); //debugging method call generateCompletingReport1(query, decompositionOutputData, queryResultElement); if (queryResultElement == null) { throw new YDataQueryException(query, queryResultElement, null, "The result of the output query (" + query + ") is null"); } // handle empty complex type flag elements if (queryResultElement.getContentSize() == 0) { handleEmptyComplexTypeFlagOutput(decompositionOutputData, queryResultElement, query, localVarThatQueryResultGetsAppliedTo); } if (query.equals(getPreJoiningMIQuery())) { _groupedMultiInstanceOutputData.getRootElement().addContent(queryResultElement.clone()); } else { _localVariableNameToReplaceableOutputData.put(localVarThatQueryResultGetsAppliedTo, queryResultElement); } //Now we check that the resulting transformation produced data according //to the net variable's type. if (spec.getSchemaVersion().isSchemaValidating() && (!query.equals(getPreJoiningMIQuery()))) { YVariable var = _net.getLocalOrInputVariable(localVarThatQueryResultGetsAppliedTo); try { Element tempRoot = new Element(_decompositionPrototype.getID()); tempRoot.addContent(queryResultElement.clone()); /** * MF: Skip schema checking if we have an empty XQuery result to allow us to effectively blank-out * a net variable. */ if ((queryResultElement.getChildren().size() != 0 || (queryResultElement.getContent().size() != 0))) { validator.validate(var, tempRoot, getID()); } } catch (YDataValidationException e) { YDataStateException f = new YDataStateException(query, decompositionOutputData.getRootElement(), validator.getSchema(), queryResultElement, e.getErrors(), getID(), "BAD PROCESS DEFINITION. Data extraction failed schema validation at task completion."); f.setStackTrace(e.getStackTrace()); throw f; } generateCompletingReport2(queryResultElement, localVarThatQueryResultGetsAppliedTo, query, decompositionOutputData); } } _mi_executing.removeOne(pmgr, childID); _mi_complete.add(pmgr, childID); if (t_isExitEnabled()) { t_exit(pmgr); return true; } return false; } else { throw new RuntimeException("This task [" + (getName() != null ? getName() : getID()) + "] is not active, and therefore cannot be completed."); } } private void addDefaultValuesAsRequired(Document dataDoc) { if (dataDoc == null) return; Element dataElem = dataDoc.getRootElement(); for (YParameter param : _decompositionPrototype.getOutputParameters().values()) { String defaultValue = param.getDefaultValue(); if (!StringUtil.isNullOrEmpty(defaultValue)) { Element paramData = dataElem.getChild(param.getPreferredName()); // if there's an element, but no value, add the default if (paramData != null) { if (StringUtil.isNullOrEmpty(paramData.getText())) { paramData.setText(defaultValue); } } // else if there's no element at all, add it with the default value else { Element defElem = JDOMUtil .stringToElement(StringUtil.wrap(defaultValue, param.getPreferredName())); defElem.setNamespace(dataElem.getNamespace()); dataElem.addContent(Math.min(dataElem.getContentSize(), param.getOrdering()), defElem.detach()); } } } } private void handleEmptyComplexTypeFlagOutput(Document outputDataDoc, Element queryResult, String query, String localVarName) { YVariable localVar = _net.getLocalOrInputVariable(localVarName); if (localVar.isEmptyTyped()) { // extract the xpath part of the xquery and use it to check whether the // optional element was included in the output data. If so, add an 'internal' // attribute to remember the flag was set (and not just an empty xquery // mapping). It will be used when the local var is next used as a source mapping. if (getElementForXQuery(outputDataDoc, query) != null) { queryResult.setAttribute("__emptyComplexTypeFlag__", "true"); } } } private Element getElementForXQuery(Document outputDataDoc, String query) { String innerQuery = StringUtil.unwrap(query); String xpath = innerQuery.substring(innerQuery.indexOf('/'), innerQuery.lastIndexOf('/')); return JDOMUtil.selectElement(outputDataDoc, xpath); } private void validateOutputs(YDataValidator validator, Document decompositionOutputData) throws YDataValidationException { YSpecification spec = _net.getSpecification(); // if the specification is beta 4 or greater then do validation. if ((!spec.getSchemaVersion().usesSimpleRootData()) && (null != getDecompositionPrototype())) { // fix any output vars with missing values that have default values defined addDefaultValuesAsRequired(decompositionOutputData); validator.validate(_decompositionPrototype.getOutputParameters().values(), decompositionOutputData.getRootElement(), getID()); } } private void updateExternalFromTaskCompletion(AbstractExternalDBGateway gateway, String query, Document outputData) { String paramName = query.split(":")[2]; Element data = outputData.getRootElement().getChild(paramName); Element netData = _net.getInternalDataDocument().getRootElement(); gateway.updateFromTaskCompletion(paramName, data, netData); } private static void generateCompletingReport2(Element resultElem, String forNetVar, String query, Document data) { if (logger.isDebugEnabled()) { logger.debug( "\n\nYTask::t_completing " + "\n\tstatus: transforming output for net" + "\n\tforNetVar = {}\n\tquery = {}" + "\n\tover data = {}\n\tresulting data = {}", forNetVar, query, JDOMUtil.documentToString(data), JDOMUtil.elementToString(resultElem)); } } private void generateCompletingReport1(String query, Document rawDecompositionData, Element queryResultElement) { if (logger.isDebugEnabled()) { StringBuilder debug = new StringBuilder("\n\n\nYTask::completing\n\tTaskID = "); debug.append(getID()).append("\n\tquery ").append(query); if (query.equals(getPreJoiningMIQuery())) { debug.append("\tquery = [").append(query).append("] is pre-joining MI query."); } debug.append("\n\trawDecompositionData = "); debug.append(JDOMUtil.documentToString(rawDecompositionData)); debug.append("\n\tresult = "); debug.append(JDOMUtil.elementToString(queryResultElement)); logger.debug(debug); } } public String getMIOutputAssignmentVar(String query) { return _dataMappingsForTaskCompletion.get(query); } private String getPreJoiningMIQuery() { return _multiInstAttr != null ? _multiInstAttr.getMIFormalOutputQuery() : null; } public synchronized void t_start(YPersistenceManager pmgr, YIdentifier child) throws YDataStateException, YPersistenceException, YQueryException, YStateException { if (t_isBusy()) { startOne(pmgr, child); } } private YIdentifier getI() { for (YExternalNetElement element : getPresetElements()) { YConditionInterface condition = (YConditionInterface) element; if (condition.containsIdentifier()) { return condition.getIdentifiers().get(0); } } return null; } private synchronized void t_exit(YPersistenceManager pmgr) throws YDataStateException, YStateException, YQueryException, YPersistenceException { if (!t_isExitEnabled()) { throw new RuntimeException(this + "_exit() is not enabled."); } performDataAssignmentsAccordingToOutputExpressions(pmgr); if (getTimerVariable() != null) { getTimerVariable().setState(YWorkItemTimer.State.closed); } YIdentifier i = _i; if (this instanceof YCompositeTask) { cancel(pmgr); } //remove tokens from cancellation set for (YExternalNetElement netElement : _removeSet) { if (netElement instanceof YTask) { ((YTask) netElement).cancel(pmgr); } else if (netElement instanceof YCondition) { ((YCondition) netElement).removeAll(pmgr); } } purgeLocations(pmgr); switch (_splitType) { case YTask._AND: doAndSplit(pmgr, i); break; case YTask._OR: doOrSplit(pmgr, i); break; case YTask._XOR: doXORSplit(pmgr, i); break; } i.removeLocation(pmgr, this); _caseToDataMap.remove(i); if (logger.isDebugEnabled()) { logger.debug("YTask::{}.exit() caseID({}) " + "_parentDecomposition.getInternalDataDocument() = {}", getID(), _i, JDOMUtil.documentToString(_net.getInternalDataDocument())); } _i = null; } private void performDataAssignmentsAccordingToOutputExpressions(YPersistenceManager pmgr) throws YDataStateException, YQueryException, YPersistenceException { if (null == getDecompositionPrototype()) { return; } if (logger.isDebugEnabled()) generateExitReport1(); for (String localVariableName : _localVariableNameToReplaceableOutputData.keySet()) { Element queryResult = _localVariableNameToReplaceableOutputData.get(localVariableName); //todo check that queryResult is valid instance of variable type _net.addData(pmgr, queryResult); } if (this.isMultiInstance() && _multiInstAttr.getMIJoiningQuery() != null) { Element result; result = evaluateTreeQuery(_multiInstAttr.getMIJoiningQuery(), _groupedMultiInstanceOutputData); if (_net.getSpecification().getSchemaVersion().isSchemaValidating()) { //if betaversion > beta3 then validate the results of the aggregation query String uniqueInstanceOutputQuery = _multiInstAttr.getMIFormalOutputQuery(); String localVarThatQueryResultGetsAppliedTo = _dataMappingsForTaskCompletion .get(uniqueInstanceOutputQuery); YVariable var = _net.getLocalOrInputVariable(localVarThatQueryResultGetsAppliedTo); Element tempRoot = new Element(_decompositionPrototype.getID()); tempRoot.addContent(result.clone()); try { _net.getSpecification().getDataValidator().validate(var, tempRoot, getID()); } catch (YDataValidationException e) { YDataStateException f = new YDataStateException(_multiInstAttr.getMIJoiningQuery(), _groupedMultiInstanceOutputData.getRootElement(), _net.getSpecification().getDataValidator().getSchema(), result, e.getErrors(), getID(), "BAD PROCESS DEFINITION. " + "Data extraction failed schema validation at task completion."); f.setStackTrace(e.getStackTrace()); throw f; } } if (logger.isDebugEnabled()) generateExitReports2(_multiInstAttr.getMIJoiningQuery(), _groupedMultiInstanceOutputData, result); _net.addData(pmgr, result); if (logger.isDebugEnabled()) generateExitReports3(); } } private void generateExitReports3() { logger.debug("\tresulting net data = {}", JDOMUtil.documentToString(_net.getInternalDataDocument())); } private void generateExitReports2(String miJoiningQuery, Document groupedOutputData, Element result) { logger.debug("\tmi JoiningQuery = {}", miJoiningQuery); logger.debug("\tmi groupedOutputData = {}", JDOMUtil.documentToString(groupedOutputData)); logger.debug("\tmi result = {}", JDOMUtil.elementToString(result)); } private void generateExitReport1() { // pre: debug is enabled logger.debug("\n\nYTask::exit()"); logger.debug("\tgetID = {}", getID()); for (Element queryResult : _localVariableNameToReplaceableOutputData.values()) { logger.debug("\tqueryResult = {}", JDOMUtil.elementToString(queryResult)); } } private Set<String> getLocalVariablesForTaskCompletion() { Set<String> localVars = new HashSet<String>(); for (String query : _dataMappingsForTaskCompletion.keySet()) { if (!ExternalDBGatewayFactory.isExternalDBMappingExpression(query)) { localVars.add(_dataMappingsForTaskCompletion.get(query)); } } return localVars; } private void doXORSplit(YPersistenceManager pmgr, YIdentifier tokenToSend) throws YQueryException, YPersistenceException { if (logger.isDebugEnabled()) { logger.debug("Evaluating XQueries against Net: {}", JDOMUtil.documentToString(_net.getInternalDataDocument())); } // get & sort the flows according to their evaluation ordering, // and with the default flow occurring last. List<YFlow> flows = new ArrayList<YFlow>(getPostsetFlows()); Collections.sort(flows); for (YFlow flow : flows) { if (flow.isDefaultFlow()) { // last flow reached - default logger.debug("Following default path."); ((YCondition) flow.getNextElement()).add(pmgr, tokenToSend); return; } if (evaluateSplitQuery(flow.getXpathPredicate(), tokenToSend)) { ((YCondition) flow.getNextElement()).add(pmgr, tokenToSend); return; } } } private void doOrSplit(YPersistenceManager pmgr, YIdentifier tokenToSend) throws YQueryException, YPersistenceException { boolean noTokensOutput = true; if (logger.isDebugEnabled()) { logger.debug("Evaluating XQueries against Net: " + JDOMUtil.documentToString(_net.getInternalDataDocument())); } // get & sort the flows according to their evaluation ordering, // and with the default flow occurring last. List<YFlow> flows = new ArrayList<YFlow>(getPostsetFlows()); Collections.sort(flows); for (YFlow flow : flows) { if (evaluateSplitQuery(flow.getXpathPredicate(), tokenToSend)) { ((YCondition) flow.getNextElement()).add(pmgr, tokenToSend); noTokensOutput = false; } if (flow.isDefaultFlow() && noTokensOutput) { ((YCondition) flow.getNextElement()).add(pmgr, tokenToSend); } } } private void doAndSplit(YPersistenceManager pmgr, YIdentifier tokenToSend) throws YPersistenceException { if (tokenToSend != null) { for (YExternalNetElement element : getPostsetElements()) { ((YCondition) element).add(pmgr, tokenToSend); } } else throw new RuntimeException("token is equal to null = " + tokenToSend); } private boolean evaluateSplitQuery(String query, YIdentifier tokenToSend) throws YQueryException { // check for timer predicates first if (isTimerPredicate(query)) { return evaluateTimerPredicate(query, tokenToSend); } // next check for plugin evaluator expressions and have the evaluators replace // them with simple values query = PredicateEvaluatorCache.process(getDecompositionPrototype(), query, tokenToSend); // now we have a standard query - check if it evaluates to true String xquery = "boolean(" + query + ")"; try { logger.debug("Evaluating XQuery: " + xquery); String result = SaxonUtil.evaluateQuery(xquery, _net.getInternalDataDocument()); if (result != null) { if (result.equalsIgnoreCase("true")) { logger.debug("XQuery evaluated TRUE."); return true; } else if (result.equalsIgnoreCase("false")) { logger.debug("XQuery evaluated FALSE."); return false; } } // either result is null or result is not a boolean string logger.error("Evaluated XQuery did not return a singular boolean result."); throw new YQueryException( "Evaluated XQuery did not return a singular " + "boolean result. Evaluated: '" + xquery + "'"); } catch (SaxonApiException e) { logger.error("Invalid XQuery expression (" + xquery + ").", e); throw new YQueryException("Invalid XQuery expression (" + xquery + ")."); } } private boolean isTimerPredicate(String predicate) { return predicate.trim().startsWith("timer("); } private boolean evaluateTimerPredicate(String predicate, YIdentifier token) throws YQueryException { YNetRunner runner = getNetRunnerRepository().get(token); if (runner != null) { return runner.evaluateTimerPredicate(predicate); } else throw new YQueryException("Unable to determine current timer status for " + "predicate: " + predicate); } protected YNetRunnerRepository getNetRunnerRepository() { return YEngine.getInstance().getNetRunnerRepository(); } protected YWorkItemRepository getWorkItemRepository() { return YEngine.getInstance().getWorkItemRepository(); } public synchronized boolean t_enabled(YIdentifier id) { if (_i != null) return false; // busy tasks are never enabled switch (_joinType) { case YTask._AND: for (YExternalNetElement condition : getPresetElements()) { if (!((YCondition) condition).containsIdentifier()) { return false; } } return true; case YTask._OR: return _net.orJoinEnabled(this, id); case YTask._XOR: for (YExternalNetElement condition : getPresetElements()) { if (((YCondition) condition).containsIdentifier()) { return true; } } return false; default: return false; } } public Object clone() throws CloneNotSupportedException { YTask copy = (YTask) super.clone(); copy._mi_active = new YInternalCondition(YInternalCondition._mi_active, copy); copy._mi_complete = new YInternalCondition(YInternalCondition._mi_complete, copy); copy._mi_entered = new YInternalCondition(YInternalCondition._mi_entered, copy); copy._mi_executing = new YInternalCondition(YInternalCondition._mi_executing, copy); copy._removeSet = new HashSet<YExternalNetElement>(); for (YExternalNetElement elem : _removeSet) { YExternalNetElement elemsClone = copy._net.getNetElement(elem.getID()); if (elemsClone == null) { elemsClone = (YExternalNetElement) elem.clone(); } copy._removeSet.add(elemsClone); } if (this.isMultiInstance()) { copy._multiInstAttr = (YMultiInstanceAttributes) _multiInstAttr.clone(); copy._multiInstAttr.setTask(copy); } return copy; } protected abstract void startOne(YPersistenceManager pmgr, YIdentifier id) throws YDataStateException, YPersistenceException, YQueryException, YStateException; protected YIdentifier createFiredIdentifier(YPersistenceManager pmgr) throws YPersistenceException { YIdentifier childCaseID = _i.createChild(pmgr); _mi_active.add(pmgr, childCaseID); _mi_entered.add(pmgr, childCaseID); return childCaseID; } public void prepareDataForInstanceStarting(YIdentifier childInstanceID) throws YDataStateException, YStateException, YQueryException { logger.debug("--> prepareDataForInstanceStarting" + childInstanceID); if (null != getDecompositionPrototype()) { _caseToDataMap.put(childInstanceID, getStartingDataSnapshot()); } logger.debug("<-- prepareDataForInstanceStarting"); } public Element getStartingDataSnapshot() throws YDataStateException, YStateException, YQueryException { logger.debug("--> getStartingDataSnapshot"); if (null == getDecompositionPrototype()) return null; Element dataForChildCase = produceDataRootElement(); List<YParameter> inputParams = new ArrayList<YParameter>( _decompositionPrototype.getInputParameters().values()); Collections.sort(inputParams); for (YParameter parameter : inputParams) { String inputParamName = parameter.getPreferredName(); String expression = _dataMappingsForTaskStarting.get(inputParamName); if (this.isMultiInstance() && inputParamName.equals(_multiInstAttr.getMIFormalInputParam())) { if (_multiInstanceSpecificParamsIterator == null) continue; Element specificMIData = (Element) _multiInstanceSpecificParamsIterator.next(); if (specificMIData != null) { if (YEngine.getInstance().generateUIMetaData()) { // Add in attributes for input parameter specificMIData.setAttributes(parameter.getAttributes().toJDOM()); } dataForChildCase.addContent(specificMIData.detach()); } } else { Element result = ExternalDBGatewayFactory.isExternalDBMappingExpression(expression) ? performExternalDataExtraction(expression, parameter) : performDataExtraction(expression, parameter); if (result != null) { if (YEngine.getInstance().generateUIMetaData()) { result.setAttributes(parameter.getAttributes().toJDOM()); } dataForChildCase.addContent(result.clone()); } } } if (YEngine.getInstance().generateUIMetaData()) { /** * AJH: Add in task level attributes for specifcation to XMLdoclet pass-thru. * Note that we skip processing of the YAWL standard task attributes as we only * pass-thru the additional (user interface hints) attributes. */ for (String attrName : getDecompositionPrototype().getAttributes().keySet()) { String attrValue = getDecompositionPrototype().getAttributes().get(attrName); if (!STANDARD_TASK_ATTRIBUTES.contains("/" + attrName + "/")) { dataForChildCase.setAttribute(attrName, attrValue); } } } logger.debug("<-- getStartingDataSnapshot"); return dataForChildCase; } protected Element performDataExtraction(String expression, YParameter inputParam) throws YDataStateException, YQueryException { Element result = evaluateTreeQuery(expression, _net.getInternalDataDocument()); // if the param id of empty complex type flag type, don't return the query result // as input data if the flag is not currently set if (inputParam.isEmptyTyped()) { if (!isPopulatedEmptyTypeFlag(expression)) return null; } // else if we have an empty element and the element is not mandatory, don't pass // the element out to the task as input data. else if ((!inputParam.isRequired()) && (result.getChildren().size() == 0) && (result.getContentSize() == 0)) { return null; } /** * AJH: Allow option to inhibit schema validation for outbound data. * Ideally need to support this at task level. */ if (_net.getSpecification().getSchemaVersion().isSchemaValidating() && (!skipOutboundSchemaChecks())) { performSchemaValidationOverExtractionResult(expression, inputParam, result); } return result; } protected boolean isPopulatedEmptyTypeFlag(String expression) { Element elem = getElementForXQuery(_net.getInternalDataDocument(), expression); return (elem != null) && (elem.getAttribute("__emptyComplexTypeFlag__") != null); } protected Element performExternalDataExtraction(String expression, YParameter inputParam) throws YStateException, YDataStateException { Element result = null; if (ExternalDBGatewayFactory.isExternalDBMappingExpression(expression)) { AbstractExternalDBGateway extractor = ExternalDBGatewayFactory.getInstance(expression); if (extractor != null) { Element netData = _net.getInternalDataDocument().getRootElement(); result = extractor.populateTaskParameter(this, inputParam, netData); } } if (result != null) { if (_net.getSpecification().getSchemaVersion().isSchemaValidating()) { if (!skipOutboundSchemaChecks()) { // remove any dynamic attributes for schema checking Element resultSansAttributes = JDOMUtil.stripAttributes((Element) result.clone()); performSchemaValidationOverExtractionResult(expression, inputParam, resultSansAttributes); } } } else { throw new YStateException("External data pull failure."); } return result; } protected void performSchemaValidationOverExtractionResult(String expression, YParameter param, Element result) throws YDataStateException { Element tempRoot = new Element(_decompositionPrototype.getID()); try { tempRoot.addContent(result.clone()); _net.getSpecification().getDataValidator().validate(param, tempRoot, getID()); } catch (YDataValidationException e) { YDataStateException f = new YDataStateException(expression, _net.getInternalDataDocument().getRootElement(), _net.getSpecification().getDataValidator().getSchema(), tempRoot, e.getErrors(), getID(), "BAD PROCESS DEFINITION. " + "Data extraction failed schema validation at task starting."); f.setStackTrace(e.getStackTrace()); throw f; } } protected Element produceDataRootElement() { return new Element(getDecompositionPrototype().getRootDataElementName()); } protected Element evaluateTreeQuery(String query, Document document) throws YQueryException { try { logger.debug("Evaluating XQuery: " + query); return SaxonUtil.evaluateTreeQuery(query, document); } catch (SaxonApiException e) { YQueryException qe = new YQueryException("Something Wrong with Process Specification:\n" + "The engine failed to parse an invalid query.\n" + "Please check task:\n\t" + "id[ " + getID() + " ]\n\t" + "query: \n\t" + query + ".\n" + "Message from parser: [" + e.getMessage() + "]"); qe.setStackTrace(e.getStackTrace()); throw qe; } } private List evaluateListQuery(String query, Element element) throws YQueryException { try { logger.debug("Evaluating XQuery: " + query); return SaxonUtil.evaluateListQuery(query, element); } catch (SaxonApiException e) { YQueryException de = new YQueryException(e.getMessage()); de.setStackTrace(e.getStackTrace()); throw de; } } public Element getData(YIdentifier childInstanceID) { return _caseToDataMap.get(childInstanceID); } public synchronized boolean t_isBusy() { return _i != null; } public synchronized void cancel(YPersistenceManager pmgr) throws YPersistenceException { purgeLocations(pmgr); if (_i != null) { _i.removeLocation(pmgr, this); _caseToDataMap.remove(_i); _i = null; } } public synchronized void rollbackFired(YIdentifier childID, YPersistenceManager pmgr) throws YPersistenceException { _mi_active.removeAll(pmgr); _mi_entered.removeAll(pmgr); _i.removeChild(childID); _i.removeLocation(pmgr, this); _i = null; } private void purgeLocations(YPersistenceManager pmgr) throws YPersistenceException { _mi_active.removeAll(pmgr); _mi_complete.removeAll(pmgr); _mi_entered.removeAll(pmgr); _mi_executing.removeAll(pmgr); } public YInternalCondition getMIActive() { return _mi_active; } public YInternalCondition getMIEntered() { return _mi_entered; } public YInternalCondition getMIComplete() { return _mi_complete; } public YInternalCondition getMIExecuting() { return _mi_executing; } public List<YInternalCondition> getAllInternalConditions() { List<YInternalCondition> icList = new Vector<YInternalCondition>(); icList.add(_mi_active); icList.add(_mi_entered); icList.add(_mi_complete); icList.add(_mi_executing); return icList; } /** * The input must be map of [key="variableName", value="expression"] * * @param map */ public void setDataMappingsForTaskStarting(Map<String, String> map) { _dataMappingsForTaskStarting.putAll(map); } /** * The input must be map of [key="expression", value="variableName"] * * @param map */ public void setDataMappingsForTaskCompletion(Map<String, String> map) { _dataMappingsForTaskCompletion.putAll(map); } public String toXML() { StringBuilder xml = new StringBuilder(); xml.append("<task id=\"").append(this.getID()).append("\""); if (this.isMultiInstance()) { xml.append(" xsi:type=\"MultipleInstanceExternalTaskFactsType\""); xml.append(" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""); } xml.append(">"); xml.append(super.toXML()); String joinType = decoratorTypeToString(_joinType); xml.append("<join code=\"").append(joinType).append("\"/>"); String splitType = decoratorTypeToString(_splitType); xml.append("<split code=\"").append(splitType).append("\"/>"); // adds process configuration information if (getDefaultConfiguration() != null) { xml.append(getDefaultConfiguration()); } if (getConfiguration() != null) { xml.append(getConfiguration()); } StringBuilder removeTokensFromFlow = new StringBuilder(); List<YExternalNetElement> removeList = new ArrayList<YExternalNetElement>(_removeSet); Collections.sort(removeList); for (YExternalNetElement netElement : removeList) { boolean implicitElement = false; if (netElement instanceof YCondition) { YCondition maybeImplicit = (YCondition) netElement; if (maybeImplicit.isImplicit()) { removeTokensFromFlow.append("<removesTokensFromFlow>"); YExternalNetElement pre = maybeImplicit.getPresetElements().iterator().next(); removeTokensFromFlow.append("<flowSource id=\"").append(pre.getID()).append("\"/>"); YExternalNetElement post = maybeImplicit.getPostsetElements().iterator().next(); removeTokensFromFlow.append("<flowDestination id=\"").append(post.getID()).append("\"/>"); removeTokensFromFlow.append("</removesTokensFromFlow>"); implicitElement = true; } } if (!implicitElement) { xml.append("<removesTokens id=\"").append(netElement.getID()).append("\"/>"); } } xml.append(removeTokensFromFlow); if (_dataMappingsForTaskStarting.size() > 0) { if (!(this.isMultiInstance() && _dataMappingsForTaskStarting.size() == 1)) { xml.append("<startingMappings>"); for (String mapsTo : _dataMappingsForTaskStarting.keySet()) { String expression = _dataMappingsForTaskStarting.get(mapsTo); if (!isMultiInstance() || !getPreSplittingMIQuery().equals(expression)) { xml.append(writeExpressionMapping(expression, mapsTo)); } } xml.append("</startingMappings>"); } } if (_dataMappingsForTaskCompletion.size() > 0) { if (!(this.isMultiInstance() && _dataMappingsForTaskCompletion.size() == 1)) { xml.append("<completedMappings>"); for (String expression : _dataMappingsForTaskCompletion.keySet()) { String mapsTo = _dataMappingsForTaskCompletion.get(expression); if (!isMultiInstance() || !_multiInstAttr.getMIFormalOutputQuery().equals(expression)) { xml.append(writeExpressionMapping(expression, mapsTo)); } } xml.append("</completedMappings>"); } } if (_dataMappingsForTaskEnablement.size() > 0) { xml.append("<enablementMappings>"); for (String mapsTo : _dataMappingsForTaskEnablement.keySet()) { String expression = _dataMappingsForTaskEnablement.get(mapsTo); xml.append(writeExpressionMapping(expression, mapsTo)); } xml.append("</enablementMappings>"); } if (_timerParams != null) { xml.append(_timerParams.toXML()); } if (_resourcingXML != null) { xml.append(_resourcingXML); } else if (_resourcingSpec != null) { xml.append(JDOMUtil.elementToString(_resourcingSpec)); } if (_customFormURL != null) { xml.append(StringUtil.wrap(_customFormURL.toString(), "customForm")); } if (_decompositionPrototype != null) { xml.append("<decomposesTo id=\"").append(_decompositionPrototype.getID()).append("\"/>"); } if (isMultiInstance()) { xml.append(_multiInstAttr.toXML()); } if (_inputLogDataItems != null) { xml.append(StringUtil.wrap(_inputLogDataItems.toXML(), "inputLogData")); } if (_outputLogDataItems != null) { xml.append(StringUtil.wrap(_outputLogDataItems.toXML(), "outputLogData")); } xml.append("</task>"); return xml.toString(); } private String decoratorTypeToString(int decType) { switch (decType) { case _AND: return "and"; case _OR: return "or"; case _XOR: return "xor"; } return "invalid"; } private String writeExpressionMapping(String expression, String mapsTo) { StringBuilder xml = new StringBuilder("<mapping><expression query=\""); xml.append(JDOMUtil.encodeEscapes(expression).replace("\n", "
")).append("\"/>").append("<mapsTo>") .append(mapsTo).append("</mapsTo></mapping>"); return xml.toString(); } public YDecomposition getDecompositionPrototype() { return _decompositionPrototype; } public void setDecompositionPrototype(YDecomposition decomposition) { _decompositionPrototype = decomposition; /** * AJH: Check if this task is to perform outbound schema validation. This is currently configured * via the tasks UI MetaData. */ if (decomposition != null) { String attrVal = decomposition.getAttributes().get(PERFORM_OUTBOUND_SCHEMA_VALIDATION); if ("TRUE".equalsIgnoreCase(attrVal)) { setSkipOutboundSchemaChecks(true); } } } /** * Connects the query to a decomposition enablement parameter. * * @param query a query applied to the net enablement variable in the net * containing this task. * @param paramName the enablement decomposition parameter to which to apply the result. */ public void setDataBindingForEnablementParam(String query, String paramName) { _dataMappingsForTaskEnablement.put(paramName, query); } /** * Returns the query to a decomposition enablement parameter. * * @param paramName the decomposition enablement variable. * @return the data binding query for that parameter. */ public String getDataBindingForEnablementParam(String paramName) { return _dataMappingsForTaskEnablement.get(paramName); } /** * Connects the query to a decomposition parameter. * * @param query a query applied to the net variables in the net containing * this task. * @param paramName the decomposition parameter to which to apply the result. */ public void setDataBindingForInputParam(String query, String paramName) { _dataMappingsForTaskStarting.put(paramName, query); } /** * Returns the query to a decomposition input parameter. * * @param paramName the decomposition input parameter. * @return the data binding query for that parameter. */ public String getDataBindingForInputParam(String paramName) { return _dataMappingsForTaskStarting.get(paramName); } /** * Binds an output expression of a decomposition to a net variable. * * @param query the ouptut expression belonging to the tasks decomposition * @param netVarName the net scope variable to which to apply the result. */ public void setDataBindingForOutputExpression(String query, String netVarName) { _dataMappingsForTaskCompletion.put(query, netVarName); } /** * Returns the query to a decomposition output parameter. * * @param paramName the decomposition output parameter. * @return the data binding query for that parameter. */ public String getDataBindingForOutputParam(String paramName) { for (String outputParameterQuery : _dataMappingsForTaskCompletion.keySet()) { String outputParameter = _dataMappingsForTaskCompletion.get(outputParameterQuery); if (paramName.equals(outputParameter)) { return outputParameterQuery; } } return null; } public String getInformation() { try { YAWLServiceGateway gateway = (YAWLServiceGateway) getDecompositionPrototype(); StringBuilder result = new StringBuilder(); result.append("<taskInfo>"); YSpecification ySpec = _net.getSpecification(); result.append("<specification>"); result.append(StringUtil.wrap(ySpec.getID(), "id")); result.append(StringUtil.wrap(ySpec.getSpecVersion(), "version")); result.append(StringUtil.wrap(ySpec.getURI(), "uri")); result.append("</specification>"); result.append("<taskID>"); result.append(getID()); result.append("</taskID>"); result.append("<taskName>"); result.append(_name != null ? _name : _decompositionPrototype.getID()); result.append("</taskName>"); if (_documentation != null) { result.append("<taskDocumentation>"); result.append(_documentation); result.append("</taskDocumentation>"); } if (_decompositionPrototype != null) { result.append("<decompositionID>"); result.append(_decompositionPrototype.getID()); result.append("</decompositionID>"); result.append("<attributes>"); result.append(_decompositionPrototype.getAttributes().toXMLElements()); result.append("</attributes>"); YAWLServiceGateway wsgw = (YAWLServiceGateway) _decompositionPrototype; YAWLServiceReference ys = wsgw.getYawlService(); if (ys != null) { result.append("<yawlService>"); String ysID = ys.getURI(); result.append("<id>"); result.append(ysID); result.append("</id>"); result.append("</yawlService>"); } } result.append("<params>"); if (isMultiInstance()) { result.append("<formalInputParam>").append(getMultiInstanceAttributes().getMIFormalInputParam()) .append("</formalInputParam>"); } for (YParameter parameter : gateway.getInputParameters().values()) { result.append(parameter.toSummaryXML()); } for (YParameter parameter : gateway.getOutputParameters().values()) { result.append(parameter.toSummaryXML()); } result.append("</params>"); if (_customFormURL != null) { result.append(StringUtil.wrap(_customFormURL.toExternalForm(), "customform")); } else { result.append("<customform/>"); } result.append("</taskInfo>"); return result.toString(); } catch (ClassCastException e) { e.printStackTrace(); return null; } } /** * Gets the version of the specification. * * @return the specification version. */ public String getSpecVersion() { return _net.getSpecification().getSpecVersion(); } public YLogDataItemList get_inputLogDataItems() { return _inputLogDataItems; } public void set_inputLogDataItems(YLogDataItemList _inputLogDataItems) { this._inputLogDataItems = _inputLogDataItems; } public YLogDataItemList get_outputLogDataItems() { return _outputLogDataItems; } public void set_outputLogDataItems(YLogDataItemList _outputLogDataItems) { this._outputLogDataItems = _outputLogDataItems; } //###################################################################################### //########################### BEGIN VERIFICATION CODE ################################ //###################################################################################### public void verify(YVerificationHandler handler) { super.verify(handler); if (!(_splitType == _AND || _splitType == _OR || _splitType == _XOR)) { handler.error(this, this + " has an incorrect value for split type"); } if (!(_joinType == _AND || _joinType == _OR || _joinType == _XOR)) { handler.error(this, this + " has an incorrect value for join type"); } if (_splitType == _OR || _splitType == _XOR) { int defaultCount = 0; List<YFlow> postsetFlows = new ArrayList<YFlow>(getPostsetFlows()); Collections.sort(postsetFlows); int lastOrdering = Integer.MIN_VALUE; for (YFlow flow : postsetFlows) { if (flow.getEvalOrdering() != null) { int thisOrdering = flow.getEvalOrdering(); if (thisOrdering == lastOrdering) { handler.error(this, this + " no two elements may possess the same " + "ordering (" + flow + ") for the same task."); } lastOrdering = thisOrdering; } if (flow.isDefaultFlow()) { defaultCount++; } } if (defaultCount != 1) { handler.error(this, this + " the postset of any OR/XOR split must have" + " exactly one default flow (not " + defaultCount + ")"); } } if (_multiInstAttr != null) { _multiInstAttr.verify(handler); } for (YExternalNetElement element : _removeSet) { if (element == null) { handler.error(this, this + " refers to a non existent element in its remove set."); } else if (!element._net.equals(_net)) { handler.error(this, this + " and " + element + " must be contained in the same net." + " (container " + _net + " & " + element._net + ")"); } } if (_decompositionPrototype != null) { checkParameterMappings(handler); } else { if (_dataMappingsForTaskStarting.size() > 0) { handler.error(this, "Syntax error for " + this + " to have startingMappings and no decomposition."); } if (_dataMappingsForTaskCompletion.size() > 0) { handler.error(this, "Syntax error for " + this + " to have completionMappings and no decomposition."); } } } private void checkParameterMappings(YVerificationHandler handler) { checkInputParameterMappings(handler); checkForDuplicateParameterMappings(handler); checkOutputParameterMappings(handler); } private void checkOutputParameterMappings(YVerificationHandler handler) { if (_net._specification.getSchemaVersion().usesSimpleRootData()) { checkOutputParamsPreBeta4(handler); } //check that each output query has valid syntax for (String nextQuery : _dataMappingsForTaskCompletion.keySet()) { String netVarNam = _dataMappingsForTaskCompletion.get(nextQuery); checkXQuery(nextQuery, netVarNam, handler); } //check that non existent local variables are not assigned output. for (String localVarName : getLocalVariablesForTaskCompletion()) { if (_net.getLocalVariables().get(localVarName) == null && _net.getInputParameters().get(localVarName) == null) { handler.error(this, "The task (id= " + getID() + ") claims to assign its " + "output to a net variable named (" + localVarName + "). " + "However the containing net does not have " + "such a variable."); } } } private void checkForDuplicateParameterMappings(YVerificationHandler handler) { //catch the case where several expressions map to the same decomposition input param //The only case where the schema misses this is where the multi-instance input //is the same as one the regular variable mappings int numOfUniqueParamsMappedTo = new HashSet<String>(_dataMappingsForTaskStarting.values()).size(); int numParams = _dataMappingsForTaskStarting.size(); if (numOfUniqueParamsMappedTo != numParams) { handler.error(this, "An input parameter is used twice. The task (id=" + getID() + ") " + "uses the same parameter through its multi-instance input " + "and its regular input."); } //check that the MI data output extract process does not map to a net variable that is //already mapped to by the said task. //The only case where the schema misses this is where the multi-instance output //is applied to the same net variable as one of regular outputs. int numOfUniqueNetVarsMappedTo = new HashSet<String>(_dataMappingsForTaskCompletion.values()).size(); numParams = _dataMappingsForTaskCompletion.size(); if (numOfUniqueNetVarsMappedTo != numParams) { handler.error(this, "An output parameter is used twice. The task (id=" + getID() + ") " + "uses the same parameter through its multi-instance output " + "and its regular output."); } } private void checkOutputParamsPreBeta4(YVerificationHandler handler) { //check there is link to each to each output param(query). Set<String> outputQueriesAtDecomposition = _decompositionPrototype.getOutputQueries(); Set<String> outputQueriesAtTask = getQueriesForTaskCompletion(); for (String query : outputQueriesAtDecomposition) { if (!outputQueriesAtTask.contains(query)) { handler.error(this, this + " there exists an output" + " query (" + query + ") in " + _decompositionPrototype + " that is" + " not mapped to by this Task."); } } for (String query : outputQueriesAtTask) { if (!outputQueriesAtDecomposition.contains(query)) { handler.error(this, this + " there exists an output" + " query (" + query + ") in this Task that has no " + "corresponding mapping at its decomposition(" + _decompositionPrototype + ")."); } } } private void checkInputParameterMappings(YVerificationHandler handler) { //check that there is a link to each inputParam Set<String> inputParamNamesAtTask = getParamNamesForTaskStarting(); //check that task input var maps to decomp input var for (String paramName : _decompositionPrototype.getInputParameterNames()) { String query = _dataMappingsForTaskStarting.get(paramName); checkXQuery(query, paramName, handler); if (!inputParamNamesAtTask.contains(paramName)) { handler.error(this, "The task (id= " + this.getID() + ")" + " needs to be connected with the input parameter (" + paramName + ")" + " of decomposition (" + _decompositionPrototype + ")."); } } } /** * Indicates if schema validation is to be performed when starting the task. * * @return whether or not to skip validation on task starting. */ protected boolean skipOutboundSchemaChecks() { return _skipOutboundSchemaChecks; } /** * Defines if schema validation is to be performed when starting the task. * * @param performOutboundSchemaChecks */ private void setSkipOutboundSchemaChecks(boolean performOutboundSchemaChecks) { _skipOutboundSchemaChecks = performOutboundSchemaChecks; } public String getResourcingXML() { return _resourcingXML; } public void setResourcingXML(String xml) { _resourcingXML = xml; setResourcingSpecs(JDOMUtil.stringToElement(xml)); } public Element getResourcingSpecs() { return _resourcingSpec; } public void setResourcingSpecs(Element resSpec) { _resourcingSpec = resSpec; } public void setCustomFormURI(URL formURL) { _customFormURL = formURL; } public URL getCustomFormURL() { return _customFormURL; } /** * TIMER SETTINGS ** */ public void setTimerParameters(YTimerParameters timerParameters) { _timerParams = timerParameters; _timerVariable = (_timerParams != null) ? new YTimerVariable(this) : null; } public YTimerVariable getTimerVariable() { return _timerVariable; } public YTimerParameters getTimerParameters() { return _timerParams; } // process configuration setters & getters public String getConfiguration() { return configuration; } public void setConfiguration(String config) { configuration = config; } public String getDefaultConfiguration() { return defaultConfiguration; } public void setDefaultConfiguration(String defaultConfig) { defaultConfiguration = defaultConfig; } public Element getConfigurationElement() { return configurationElement; } public void setConfigurationElement(Element configElement) { configurationElement = configElement; } public Element getDefaultConfigurationElement() { return defaultConfigurationElement; } public void setDefaultConfigurationElement(Element defaultConfigElement) { defaultConfigurationElement = defaultConfigElement; } }