0001: /*
0002: * This file is part of the WfMOpen project.
0003: * Copyright (C) 2001-2003 Danet GmbH (www.danet.de), GS-AN.
0004: * All rights reserved.
0005: *
0006: * This program is free software; you can redistribute it and/or modify
0007: * it under the terms of the GNU General Public License as published by
0008: * the Free Software Foundation; either version 2 of the License, or
0009: * (at your option) any later version.
0010: *
0011: * This program is distributed in the hope that it will be useful,
0012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
0013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
0014: * GNU General Public License for more details.
0015: *
0016: * You should have received a copy of the GNU General Public License
0017: * along with this program; if not, write to the Free Software
0018: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
0019: *
0020: * $Id: ProcDefValidator.java,v 1.13 2007/05/03 21:58:19 mlipp Exp $
0021: *
0022: * $Log: ProcDefValidator.java,v $
0023: * Revision 1.13 2007/05/03 21:58:19 mlipp
0024: * Internal refactoring for making better use of local EJBs.
0025: *
0026: * Revision 1.12 2007/02/27 14:34:16 drmlipp
0027: * Some refactoring to reduce cyclic dependencies.
0028: *
0029: * Revision 1.11 2006/11/19 21:53:47 mlipp
0030: * Finished support for native Java types.
0031: *
0032: * Revision 1.10 2006/11/19 11:16:35 mlipp
0033: * Made ExternalReference an interface.
0034: *
0035: * Revision 1.9 2006/11/17 21:40:36 mlipp
0036: * Added Java type tests.
0037: *
0038: * Revision 1.8 2006/11/17 16:16:12 drmlipp
0039: * Started support for Java native types.
0040: *
0041: * Revision 1.7 2006/09/29 12:32:08 drmlipp
0042: * Consistently using WfMOpen as projct name now.
0043: *
0044: * Revision 1.6 2006/03/08 14:46:43 drmlipp
0045: * Synchronized with 1.3.3p5.
0046: *
0047: * Revision 1.5 2005/11/09 16:14:01 drmlipp
0048: * Fixed jaxen problems.
0049: *
0050: * Revision 1.4 2005/08/23 14:11:56 drmlipp
0051: * Synchronized with 1.3.1p5.
0052: *
0053: * Revision 1.1.1.5.6.3 2005/08/23 13:27:26 drmlipp
0054: * Fixed problem with not dinstinguished process local application
0055: * declarations.
0056: *
0057: * Revision 1.3 2005/04/22 15:11:02 drmlipp
0058: * Merged changes from 1.3 branch up to 1.3p15.
0059: *
0060: * Revision 1.1.1.5.6.2 2005/04/19 09:32:17 drmlipp
0061: * Moved warning about non-standard behaviour to proper place and made
0062: * extended attribute specification more flexible.
0063: *
0064: * Revision 1.2 2005/02/04 14:25:26 drmlipp
0065: * Synchronized with 1.3rc2.
0066: *
0067: * Revision 1.1.1.5.6.1 2005/02/03 13:02:46 drmlipp
0068: * Moved warning about indexed formal parameters to process definition
0069: * import.
0070: *
0071: * Revision 1.1.1.5 2004/08/18 15:17:38 drmlipp
0072: * Update to 1.2
0073: *
0074: * Revision 1.52 2004/03/25 14:41:47 lipp
0075: * Added possibility to specify actual parameters as XML.
0076: *
0077: * Revision 1.51 2004/03/18 12:53:00 lipp
0078: * Support for XML as actual parameter.
0079: *
0080: * Revision 1.50 2004/01/19 12:30:58 lipp
0081: * SchemaType now supported properly.
0082: *
0083: * Revision 1.49 2003/09/20 19:24:48 lipp
0084: * Cannot really verify duration conditions.
0085: *
0086: * Revision 1.48 2003/09/19 15:19:10 lipp
0087: * Using proper parser now.
0088: *
0089: * Revision 1.47 2003/09/17 10:39:52 lipp
0090: * Reduced number of client side classes.
0091: *
0092: * Revision 1.46 2003/09/08 15:37:18 lipp
0093: * Introduced deadline definition and suspend tracking.
0094: *
0095: * Revision 1.45 2003/07/11 10:49:02 huaiyang
0096: * Fix the tip error with the error key.
0097: *
0098: * Revision 1.44 2003/07/11 10:30:07 huaiyang
0099: * Error messages further improved.
0100: *
0101: * Revision 1.43 2003/07/10 12:43:30 huaiyang
0102: * improve the error info.
0103: *
0104: * Revision 1.42 2003/07/04 10:56:14 huaiyang
0105: * Performer can be expression in form of DataField in type PERFORMER.
0106: *
0107: * Revision 1.41 2003/06/27 08:51:45 lipp
0108: * Fixed copyright/license information.
0109: *
0110: * Revision 1.40 2003/06/22 20:24:14 lipp
0111: * Fixed argument type checking.
0112: *
0113: * Revision 1.39 2003/06/04 07:47:13 huaiyang
0114: * refactor the method of valiadateSubFlow.
0115: *
0116: * Revision 1.38 2003/06/03 15:36:43 huaiyang
0117: * Fix the error and add the validating of tool param type.
0118: *
0119: * Revision 1.37 2003/06/03 13:36:11 lipp
0120: * Disabled faulty test.
0121: *
0122: * Revision 1.36 2003/06/03 11:27:56 huaiyang
0123: * validate the type of formal- and actualParameters of subflow.
0124: *
0125: * Revision 1.35 2003/05/28 15:11:34 lipp
0126: * Removed excessive warning on emtpy activity sets.
0127: *
0128: * Revision 1.34 2003/05/28 11:16:07 lipp
0129: * Fixed message text.
0130: *
0131: * Revision 1.33 2003/05/28 07:51:26 huaiyang
0132: * Modify the algorithm to check loop activities.
0133: *
0134: * Revision 1.32 2003/05/26 14:31:58 huaiyang
0135: * add the check of loop activities.
0136: *
0137: * Revision 1.31 2003/05/26 12:42:03 huaiyang
0138: * add the validation of script.
0139: *
0140: * Revision 1.30 2003/05/20 12:04:54 huaiyang
0141: * new error message for not matched mode of formal- and actualParam.
0142: *
0143: * Revision 1.29 2003/05/15 13:50:19 huaiyang
0144: * Check if actual parameters is defined as formal parameters.
0145: *
0146: * Revision 1.28 2003/05/15 13:14:45 huaiyang
0147: * Check if the mode of formal and actual parameters matched.
0148: *
0149: * Revision 1.27 2003/05/13 08:37:15 huaiyang
0150: * add the check of OUT- and INOUT- actual parameter in calling
0151: * subflow.
0152: *
0153: * Revision 1.26 2003/05/12 10:39:02 lipp
0154: * Fixed actual paramter check.
0155: *
0156: * Revision 1.25 2003/05/12 09:13:43 huaiyang
0157: * add the validating of priority of process and activity.
0158: *
0159: * Revision 1.24 2003/05/08 14:54:23 huaiyang
0160: * add the checking of empty activity set.
0161: *
0162: * Revision 1.23 2003/05/08 13:36:41 huaiyang
0163: * function of validate subflow added.
0164: *
0165: * Revision 1.22 2003/05/06 13:21:30 lipp
0166: * Resolved cyclic dependency.
0167: *
0168: * Revision 1.21 2003/04/24 19:47:50 lipp
0169: * Removed dependency between general util and workflow api.
0170: *
0171: * Revision 1.20 2003/04/24 15:08:14 lipp
0172: * Reading ApplicationDefinitiosn from SAX now.
0173: *
0174: * Revision 1.19 2003/03/31 16:50:28 huaiyang
0175: * Logging using common-logging.
0176: *
0177: * Revision 1.18 2003/03/28 14:42:35 lipp
0178: * Moved some code to XPDLUtil.
0179: *
0180: * Revision 1.17 2003/03/28 12:44:08 lipp
0181: * Moved XPDL related constants to XPDLUtil.
0182: *
0183: * Revision 1.16 2003/03/27 10:45:37 lipp
0184: * Renamed activity implementation classes for clarity.
0185: *
0186: * Revision 1.15 2003/03/25 17:16:08 lipp
0187: * Fixed test.
0188: *
0189: * Revision 1.14 2003/03/07 14:11:10 huaiyang
0190: * optimize the initialization of all the xpath.
0191: *
0192: * Revision 1.13 2003/03/07 08:01:02 huaiyang
0193: * use workflow.util.CollectingErrorHandler to collect errors.
0194: *
0195: * Revision 1.12 2003/03/04 13:44:03 huaiyang
0196: * add the method of validateBlockActivity.
0197: *
0198: * Revision 1.11 2002/12/19 21:37:43 lipp
0199: * Reorganized interfaces.
0200: *
0201: * Revision 1.10 2002/12/03 13:49:16 huaiyang
0202: * Add the check of otherwise condition.
0203: *
0204: * Revision 1.9 2002/11/25 09:28:29 huaiyang
0205: * Bug with checking double application Id fixed.
0206: *
0207: * Revision 1.8 2002/11/13 09:14:56 huaiyang
0208: * Remove obsolete debug message.
0209: *
0210: * Revision 1.7 2002/11/12 14:15:36 huaiyang
0211: * Validate the transition restrictions of activity.
0212: *
0213: * Revision 1.6 2002/11/06 12:14:48 huaiyang
0214: * Add the method of validateExtendedAttributes.
0215: *
0216: * Revision 1.5 2002/11/04 10:56:59 huaiyang
0217: * Added the validating of transition for activitySet.
0218: *
0219: * Revision 1.4 2002/11/01 13:09:13 huaiyang
0220: * Bug with validating activityset fixed.
0221: *
0222: * Revision 1.3 2002/11/01 12:21:30 huaiyang
0223: * Validating of activity set added.
0224: *
0225: * Revision 1.2 2002/10/06 20:14:38 lipp
0226: * Updated argument handling.
0227: *
0228: * Revision 1.1 2002/09/27 12:44:55 lipp
0229: * Moved validation to a separate class.
0230: *
0231: */
0232: package de.danet.an.workflow.domain;
0233:
0234: import java.io.IOException;
0235: import java.io.Serializable;
0236: import java.io.StringReader;
0237:
0238: import java.util.ArrayList;
0239: import java.util.Collection;
0240: import java.util.Collections;
0241: import java.util.HashMap;
0242: import java.util.Iterator;
0243: import java.util.List;
0244: import java.util.Map;
0245: import java.util.Set;
0246:
0247: import javax.xml.parsers.ParserConfigurationException;
0248: import javax.xml.parsers.SAXParser;
0249: import javax.xml.parsers.SAXParserFactory;
0250:
0251: import org.jaxen.JaxenException;
0252: import org.jaxen.XPath;
0253: import org.jaxen.jdom.JDOMXPath;
0254: import org.jdom.Document;
0255: import org.jdom.Element;
0256: import org.jdom.JDOMException;
0257: import org.jdom.Namespace;
0258: import org.jdom.output.SAXOutputter;
0259: import org.xml.sax.ContentHandler;
0260: import org.xml.sax.InputSource;
0261: import org.xml.sax.SAXException;
0262: import org.xml.sax.XMLReader;
0263:
0264: import de.danet.an.util.sax.HandlerStack;
0265:
0266: import de.danet.an.workflow.util.CollectingErrorHandler;
0267: import de.danet.an.workflow.util.SAXEventBufferImpl;
0268: import de.danet.an.workflow.util.XPDLUtil;
0269:
0270: import de.danet.an.workflow.api.ExternalReference;
0271: import de.danet.an.workflow.api.FormalParameter;
0272: import de.danet.an.workflow.api.PrioritizedMessage;
0273:
0274: import de.danet.an.workflow.internalapi.ExtExecutionObjectLocal;
0275:
0276: /**
0277: * This class performes validation methods for a process defintion
0278: * package.
0279: *
0280: * @author <a href="mailto:lipp@danet.de"></a>
0281: * @version $Revision: 1.13 $
0282: */
0283: public class ProcDefValidator {
0284: /**
0285: * logger of this class.
0286: */
0287: static final org.apache.commons.logging.Log logger = org.apache.commons.logging.LogFactory
0288: .getLog(ProcDefValidator.class);
0289:
0290: // The defined data fields
0291: private transient Map procdataMap = null;
0292: private transient List dataFieldsList = null;
0293:
0294: // The defined applications
0295: private transient Map applicationDefs = null;
0296:
0297: // the bundle base for all the error key.
0298: private String bundleBase = "ImportMessages";
0299:
0300: // all the xpath used in the validation
0301: private XPath processPath = null;
0302: private XPath actSetPath = null;
0303: private XPath actPath = null;
0304: private XPath alltranrefsPath = null;
0305: private XPath splitXPath = null;
0306: private XPath tranrefsPath = null;
0307: private XPath transPath = null;
0308: private XPath toolPath = null;
0309: private XPath subFlowPath = null;
0310: private XPath partPath = null;
0311: private XPath scriptPath = null;
0312: private XPath procpartPath = null;
0313: private XPath procApplPath = null;
0314: private XPath applPath = null;
0315: private XPath toolAgentClassPath = null;
0316: private XPath extAttrPath = null;
0317: private XPath procExtAttrPath = null;
0318: private XPath dataFieldPath = null;
0319: private XPath procDataFieldPath = null;
0320: private XPath dataTypePath = null;
0321: private XPath procFormalParamsPath = null;
0322: private XPath procPriorityPath = null;
0323: private XPath actualParamsPath = null;
0324: private XPath formalParamsPath = null;
0325: private XPath indexedParamsPath = null;
0326:
0327: /**
0328: * This class is used to save the value and data type of the proc
0329: * data.
0330: */
0331: private class ProcData {
0332: public Object value;
0333: public Element dataType;
0334:
0335: public ProcData(Object value, Element dataType) {
0336: this .value = value;
0337: this .dataType = dataType;
0338: }
0339: }
0340:
0341: /**
0342: * This class is used to save the ActualParameters and its responding
0343: * data type of the tool.
0344: */
0345: private class ToolInfo {
0346: public String[] actualParams;
0347:
0348: public ToolInfo(String[] actualParams) {
0349: this .actualParams = actualParams;
0350: }
0351: }
0352:
0353: /**
0354: * Validate a process definition package.
0355: * @param doc the JDOM <code>Document</code> to check.
0356: * @param eh the <code>CollectingErrorHandler</code> to which
0357: * error messages should be added.
0358: * @exception JDOMException if an error occurs
0359: * @exception JaxenException if an error occurs
0360: */
0361: public void validate(Document doc, CollectingErrorHandler eh)
0362: throws JDOMException, JaxenException {
0363: // initialize all the xpath
0364: init();
0365: // validate it
0366: validateHeaderDefinition(doc, eh);
0367: validateProcessDefinition(doc, eh);
0368: }
0369:
0370: private void init() throws JDOMException, JaxenException {
0371: processPath = buildJDOMXPath("/xpdl:Package/xpdl:WorkflowProcesses/xpdl:WorkflowProcess");
0372: actSetPath = buildJDOMXPath("xpdl:ActivitySets/xpdl:ActivitySet");
0373: actPath = buildJDOMXPath("xpdl:Activities/xpdl:Activity");
0374: tranrefsPath = buildJDOMXPath("xpdl:TransitionRefs/xpdl:TransitionRef");
0375: alltranrefsPath = buildJDOMXPath("descendant::xpdl:TransitionRefs/xpdl:TransitionRef");
0376: splitXPath = buildJDOMXPath("xpdl:TransitionRestrictions/xpdl:TransitionRestriction"
0377: + "/xpdl:Split[@Type='XOR']");
0378: transPath = buildJDOMXPath("xpdl:Transitions/xpdl:Transition");
0379: toolPath = buildJDOMXPath("xpdl:Implementation/xpdl:Tool");
0380: subFlowPath = buildJDOMXPath("xpdl:Implementation/xpdl:SubFlow");
0381: partPath = buildJDOMXPath("/xpdl:Package/xpdl:Participants/xpdl:Participant");
0382: scriptPath = buildJDOMXPath("/xpdl:Package/xpdl:Script");
0383: procpartPath = buildJDOMXPath("xpdl:Participants/xpdl:Participant");
0384: applPath = buildJDOMXPath("/xpdl:Package/xpdl:Applications/xpdl:Application");
0385: procApplPath = buildJDOMXPath("xpdl:Applications/xpdl:Application");
0386: toolAgentClassPath = buildJDOMXPath("xpdl:ExtendedAttributes"
0387: + "/xpdl:ExtendedAttribute[@Name=\"Implementation\"]"
0388: + "/vx:ToolAgent/@Class");
0389: extAttrPath = buildJDOMXPath("/xpdl:Package/xpdl:ExtendedAttributes");
0390: procExtAttrPath = buildJDOMXPath("xpdl:ExtendedAttributes");
0391: dataFieldPath = buildJDOMXPath("/xpdl:Package/xpdl:DataFields/xpdl:DataField");
0392: procDataFieldPath = buildJDOMXPath("xpdl:DataFields/xpdl:DataField");
0393: dataTypePath = buildJDOMXPath("xpdl:DataType/xpdl:BasicType");
0394: procFormalParamsPath = buildJDOMXPath("/xpdl:Package/xpdl:WorkflowProcesses/xpdl:WorkflowProcess"
0395: + "/xpdl:FormalParameters/xpdl:FormalParameter");
0396: procPriorityPath = buildJDOMXPath("xpdl:ProcessHeader/xpdl:Priority");
0397: actualParamsPath = buildJDOMXPath("xpdl:ActualParameters/xpdl:ActualParameter");
0398: formalParamsPath = buildJDOMXPath("xpdl:FormalParameters/xpdl:FormalParameter");
0399: indexedParamsPath = buildJDOMXPath("xpdl:FormalParameters/xpdl:FormalParameter/@Index");
0400: }
0401:
0402: private XPath buildJDOMXPath(String pathString)
0403: throws JDOMException, JaxenException {
0404: XPath path = new JDOMXPath(pathString);
0405: path.addNamespace("xpdl", XPDLUtil.XPDL_NS);
0406: path.addNamespace("vx", XPDLUtil.XPDL_EXTN_NS);
0407: return path;
0408: }
0409:
0410: private void validateHeaderDefinition(Document doc,
0411: CollectingErrorHandler eh) throws JDOMException,
0412: JaxenException {
0413: validateScript(doc, eh);
0414: validateApplDefs(doc, eh);
0415: validateExtendedAttributes(doc, null, eh);
0416: }
0417:
0418: private void validateScript(Document doc, CollectingErrorHandler eh)
0419: throws JDOMException, JaxenException {
0420: // supported scripts and its version
0421: String javascript = "text/javascript", jsversion = "1.5";
0422: String ecmascript = "text/ecmascript", esversion = "3rd Edition";
0423: // parse
0424: Element script = (Element) scriptPath.selectSingleNode(doc);
0425: if (script == null) {
0426: return;
0427: }
0428: String type = script.getAttributeValue("Type");
0429: String version = script.getAttributeValue("Version");
0430: if (version == null) {
0431: if (!type.equals(javascript) && !type.equals(ecmascript)) {
0432: String[] errdatas = { type };
0433: eh.add(new PrioritizedMessage(
0434: PrioritizedMessage.Priority.ERROR, bundleBase
0435: + "#package.script.type.unsupported",
0436: errdatas));
0437: }
0438: return;
0439: } else {
0440: if ((type.equals(javascript) && (version.equals(jsversion)))
0441: || (type.equals(ecmascript) && (version
0442: .equals(esversion)))) {
0443: return;
0444: } else {
0445: String[] errdts = { type, version };
0446: eh
0447: .add(new PrioritizedMessage(
0448: PrioritizedMessage.Priority.ERROR,
0449: bundleBase
0450: + "#package.script.version.unsupported",
0451: errdts));
0452: }
0453: }
0454: }
0455:
0456: private boolean validateProcessDefinition(Document doc,
0457: CollectingErrorHandler eh) throws JDOMException,
0458: JaxenException {
0459: boolean faultless = true;
0460: List processIds = new ArrayList();
0461: Iterator processListIterator = processPath.selectNodes(doc)
0462: .iterator();
0463: while (processListIterator.hasNext()) {
0464: Element process = (Element) processListIterator.next();
0465: String id = process.getAttributeValue("Id");
0466: //validatePriority
0467: validatePriority(id, (Element) procPriorityPath
0468: .selectSingleNode(process),
0469: "procdef.header.priority.notallowed", id, eh);
0470: //validateUniqueProcessId
0471: processIds.add(id);
0472: //validateUniqueActivityId
0473: validateData(doc, process, eh);
0474: validateActivities(doc, process, eh);
0475: //validateUniqueTransitionId
0476: validateTransition(process, eh);
0477: //validateExtendedAttributes
0478: validateExtendedAttributes(null, process, eh);
0479: }
0480: String duplicate = findDuplicate(processIds);
0481: if (duplicate != null) {
0482: faultless = false;
0483: String[] errdatas = { duplicate };
0484: eh.add(new PrioritizedMessage(
0485: PrioritizedMessage.Priority.ERROR, bundleBase
0486: + "#procdef.process.ununique", errdatas));
0487: }
0488: return faultless;
0489: }
0490:
0491: private void validateData(Document doc, Element process,
0492: CollectingErrorHandler eh) throws JaxenException {
0493: Map pd = procdatas(doc, process, eh);
0494: for (Iterator i = pd.entrySet().iterator(); i.hasNext();) {
0495: Map.Entry e = (Map.Entry) i.next();
0496: Object type = XPDLUtil.extractDataType(((ProcData) e
0497: .getValue()).dataType);
0498: if ((type instanceof ExternalReference)
0499: && XPDLUtil.isJavaType((ExternalReference) type)) {
0500: String[] errdatas = { ((ExternalReference) type)
0501: .location().getPath() };
0502: try {
0503: Class jType = XPDLUtil
0504: .getJavaType((ExternalReference) type);
0505: if (!(jType.isInterface() || Serializable.class
0506: .isAssignableFrom(jType))) {
0507: eh
0508: .add(new PrioritizedMessage(
0509: PrioritizedMessage.Priority.ERROR,
0510: bundleBase
0511: + "#procdef.data.notSerializable",
0512: errdatas));
0513: }
0514: } catch (ClassNotFoundException ee) {
0515: eh.add(new PrioritizedMessage(
0516: PrioritizedMessage.Priority.ERROR,
0517: bundleBase
0518: + "#procdef.data.invalidJavaType",
0519: errdatas));
0520: }
0521: }
0522: }
0523:
0524: }
0525:
0526: private void validateActivities(Document doc, Element process,
0527: CollectingErrorHandler eh) throws JDOMException,
0528: JaxenException {
0529: List activitySetList = actSetPath.selectNodes(process);
0530: // validateUniqueActivityId
0531: doValidateActivities(doc, process,
0532: actPath.selectNodes(process), activitySetList, null,
0533: false, eh);
0534: // validateUniqueActivityId in ActivitySet
0535: Iterator activitySetIterator = activitySetList.iterator();
0536: while (activitySetIterator.hasNext()) {
0537: Element actSetElem = (Element) activitySetIterator.next();
0538: List acts = actPath.selectNodes(actSetElem);
0539: if (acts.size() == 0) {
0540: // empty activity set
0541: String actSetId = actSetElem.getAttributeValue("Id");
0542: String processId = process.getAttributeValue("Id");
0543: String[] errDatas = { processId, actSetId };
0544: eh.add(new PrioritizedMessage(
0545: PrioritizedMessage.Priority.ERROR, bundleBase
0546: + "#procdef.process.activityset.empty",
0547: errDatas));
0548: return;
0549: } else {
0550: doValidateActivities(doc, process, acts,
0551: activitySetList, actSetElem, true, eh);
0552: }
0553: }
0554: }
0555:
0556: // Validate activity of a process or what included in activitySet. If
0557: // isIncludedInActivitySet is set to true, then activitySet Element must be
0558: // given.
0559: private void doValidateActivities(Document doc, Element process,
0560: Collection acts, List activitySetList, Element actSetElem,
0561: boolean isIncludedInActivitySet, CollectingErrorHandler eh)
0562: throws JDOMException, JaxenException {
0563: Namespace namespace = Namespace.getNamespace("xpdl",
0564: XPDLUtil.XPDL_NS);
0565: List activityIds = new ArrayList();
0566: List participantIdList = validateParticipants(doc, process, eh);
0567: String processId = process.getAttributeValue("Id");
0568: // Validate existence of the referenced transitionRef
0569: List transitionIds = getTransitionIds(process, actSetElem,
0570: isIncludedInActivitySet);
0571: Iterator activitiesIterator = acts.iterator();
0572: while (activitiesIterator.hasNext()) {
0573: Element activity = (Element) activitiesIterator.next();
0574: String id = activity.getAttributeValue("Id");
0575: //validatePriority
0576: validatePriority(processId, (Element) activity.getChild(
0577: "Priority", namespace),
0578: "procdef.activity.priority.notallowed", id, eh);
0579: validateDeadlines(processId, activity, eh);
0580: //validateId
0581: activityIds.add(id);
0582: // validate the included BlockActivity.
0583: validateBlockActivity(processId, activitySetList,
0584: actSetElem, activity, eh);
0585: // collects all transition ref id in activity.
0586: Iterator transRefIterator = alltranrefsPath.selectNodes(
0587: activity).iterator();
0588: while (transRefIterator.hasNext()) {
0589: Element transRefElem = (Element) transRefIterator
0590: .next();
0591: String transRefId = transRefElem
0592: .getAttributeValue("Id");
0593: if (!transitionIds.contains(transRefId)) {
0594: eh.add(new PrioritizedMessage(
0595: PrioritizedMessage.Priority.ERROR,
0596: bundleBase
0597: + "#procdef.transitionid.notfound",
0598: new Object[] { transRefId, processId }));
0599: }
0600: }
0601: validateTranstionRestrictions(process, actSetElem,
0602: activity, isIncludedInActivitySet, eh);
0603: validateImplementation(doc, process, activity, eh);
0604: //validate if the referenced participant defined and unique.
0605: validatePerformer(processId, activity, participantIdList,
0606: eh);
0607: }
0608: String duplicate = findDuplicate(activityIds);
0609: if (duplicate != null) {
0610: String errorKey = null;
0611: String activitySetId = null;
0612: if (isIncludedInActivitySet) {
0613: errorKey = "procdef.activityset.activity.ununique";
0614: activitySetId = actSetElem.getAttributeValue("Id");
0615: } else {
0616: errorKey = "procdef.activity.ununique";
0617: }
0618: String[] errdatas = { duplicate, processId, activitySetId };
0619: eh.add(new PrioritizedMessage(
0620: PrioritizedMessage.Priority.ERROR, bundleBase + "#"
0621: + errorKey, errdatas));
0622: }
0623: }
0624:
0625: private void validateDeadlines(String processId, Element actEl,
0626: CollectingErrorHandler eh) throws JDOMException {
0627: Namespace namespace = Namespace.getNamespace("xpdl",
0628: XPDLUtil.XPDL_NS);
0629: List dls = actEl.getChildren("Deadline", namespace);
0630: for (Iterator i = dls.iterator(); i.hasNext();) {
0631: Element dl = (Element) i.next();
0632: String exep = dl.getChildText("ExceptionName", namespace);
0633: if (exep == null || exep.length() == 0) {
0634: eh
0635: .add(new PrioritizedMessage(
0636: PrioritizedMessage.Priority.ERROR,
0637: bundleBase
0638: + "#procdef.activity.deadline.noException",
0639: new Object[] { processId,
0640: actEl.getAttributeValue("Id") }));
0641: }
0642: String cond = dl.getChildText("DeadlineCondition",
0643: namespace);
0644: if (cond == null || cond.length() == 0) {
0645: eh
0646: .add(new PrioritizedMessage(
0647: PrioritizedMessage.Priority.ERROR,
0648: bundleBase
0649: + "#procdef.activity.deadline.noCondition",
0650: new Object[] { processId,
0651: actEl.getAttributeValue("Id") }));
0652: continue;
0653: }
0654: }
0655: }
0656:
0657: private void validateBlockActivity(String processId,
0658: List activitySetList, Element actSetElement,
0659: Element activity, CollectingErrorHandler eh)
0660: throws JDOMException, JaxenException {
0661: Namespace namespace = Namespace.getNamespace("xpdl",
0662: XPDLUtil.XPDL_NS);
0663: Element blockActivity = activity.getChild("BlockActivity",
0664: namespace);
0665: if (blockActivity == null) {
0666: // no block activity defined in this activity
0667: return;
0668: }
0669: String blockActivityId = blockActivity
0670: .getAttributeValue("BlockId");
0671: String activityId = activity.getAttributeValue("Id");
0672: String[] errdatas = { activityId, blockActivityId, processId };
0673: if ((actSetElement != null)
0674: && blockActivityId.equals(actSetElement
0675: .getAttributeValue("Id"))) {
0676: // the given activity included his super activity set as block
0677: // activity.
0678: eh
0679: .add(new PrioritizedMessage(
0680: PrioritizedMessage.Priority.ERROR,
0681: bundleBase
0682: + "#procdef.activityset.activity.includeitself",
0683: errdatas));
0684: return;
0685: }
0686: if (activitySetList != null) {
0687: boolean validActivitySet = false;
0688: Iterator i = activitySetList.iterator();
0689: while (i.hasNext()) {
0690: Element actSet = (Element) i.next();
0691: if (actSet.getAttributeValue("Id").equals(
0692: blockActivityId)) {
0693: validActivitySet = true;
0694: break;
0695: }
0696: }
0697: if (!validActivitySet) {
0698: // included BlockActivity not found in the defined
0699: // activity set
0700: eh
0701: .add(new PrioritizedMessage(
0702: PrioritizedMessage.Priority.ERROR,
0703: bundleBase
0704: + "#procdef.activity.activityset.invalid",
0705: errdatas));
0706: return;
0707: }
0708: }
0709: }
0710:
0711: private void validateTranstionRestrictions(Element process,
0712: Element actSet, Element activity,
0713: boolean isIncludedInActivitySet, CollectingErrorHandler eh)
0714: throws JDOMException, JaxenException {
0715: List transRefIds = new ArrayList();
0716: Element toBeSelectedNode = isIncludedInActivitySet ? actSet
0717: : process;
0718: String toBeSelectedNodeId = toBeSelectedNode
0719: .getAttributeValue("Id");
0720: String actId = activity.getAttributeValue("Id");
0721: Namespace namespace = Namespace.getNamespace("xpdl",
0722: XPDLUtil.XPDL_NS);
0723: Element splitElement = (Element) splitXPath
0724: .selectSingleNode(activity);
0725: if (splitElement == null) {
0726: return;
0727: }
0728: List transRefList = tranrefsPath.selectNodes(splitElement);
0729: String[] errdatas = { actId, toBeSelectedNodeId };
0730: if (transRefList.size() == 0) {
0731: eh
0732: .add(new PrioritizedMessage(
0733: PrioritizedMessage.Priority.ERROR,
0734: bundleBase
0735: + "#procdef.activity.split.transition.notdefined",
0736: new Object[] { actId, toBeSelectedNodeId }));
0737: return;
0738: }
0739: Iterator transRefIterator = transRefList.iterator();
0740: while (transRefIterator.hasNext()) {
0741: Element transRef = (Element) transRefIterator.next();
0742: transRefIds.add(transRef.getAttributeValue("Id"));
0743: }
0744: // find out all transitions with the fromAct same as the actId.
0745: String notCompleteErrKey = isIncludedInActivitySet ? "#procdef.activityset.split.transition.notcomplete"
0746: : "#procdef.activity.split.transition.notcomplete";
0747: List transList = transPath.selectNodes(toBeSelectedNode);
0748: Iterator transIterator = transList.iterator();
0749: while (transIterator.hasNext()) {
0750: Element transElem = (Element) transIterator.next();
0751: String fromId = transElem.getAttributeValue("From");
0752: String transId = transElem.getAttributeValue("Id");
0753: if (fromId.equals(actId) && !transRefIds.contains(transId)) {
0754: eh.add(new PrioritizedMessage(
0755: PrioritizedMessage.Priority.ERROR, bundleBase
0756: + notCompleteErrKey, new Object[] {
0757: actId, transId, toBeSelectedNodeId }));
0758: }
0759: }
0760: // Find out if any referenced transition with the condition of
0761: // OTHERWISE is listed at the end of TransitionRestrictions of
0762: // Activiity; if not, needs to be sorted and throws warning.
0763: validateTransitionRestrictionsCondition(transRefList,
0764: transList, eh);
0765: }
0766:
0767: // Find out if any referenced transition with the condition of
0768: // OTHERWISE is listed at the end of TransitionRestrictions of
0769: // Activiity; if not, needs to be sorted and throws warning.
0770: private void validateTransitionRestrictionsCondition(
0771: List transRefList, List transList, CollectingErrorHandler eh) {
0772: int index = 0;
0773: boolean foundIt = false;
0774: Element transRef = null;
0775: for (Iterator i = transRefList.iterator(); i.hasNext(); index++) {
0776: transRef = (Element) i.next();
0777: Element condition = condition(transList, transRef
0778: .getAttributeValue("Id"));
0779: if (condition == null) {
0780: continue;
0781: }
0782: if (condition.getAttributeValue("Type") != null
0783: && condition.getAttributeValue("Type").equals(
0784: "OTHERWISE")) {
0785: foundIt = true;
0786: break;
0787: }
0788: }
0789: if (!foundIt || (index == transRefList.size() - 1)) {
0790: return;
0791: }
0792: eh.add(new PrioritizedMessage(PrioritizedMessage.Priority.WARN,
0793: bundleBase + "#procdef.transition.otherwise.notatend"));
0794: }
0795:
0796: private Element condition(List transList, String transId) {
0797: Namespace namespace = Namespace.getNamespace("xpdl",
0798: XPDLUtil.XPDL_NS);
0799: Element condition = null;
0800: for (Iterator i = transList.iterator(); i.hasNext();) {
0801: Element tran = (Element) i.next();
0802: if (tran.getAttributeValue("Id").equals(transId)) {
0803: condition = tran.getChild("Condition", namespace);
0804: break;
0805: }
0806: }
0807: return condition;
0808: }
0809:
0810: private boolean validateImplementation(Document doc,
0811: Element process, Element activity, CollectingErrorHandler eh)
0812: throws JDOMException, JaxenException {
0813: if (toolPath.selectNodes(activity).size() != 0) {
0814: // Tool as Implementation
0815: return validateTools(doc, process, activity, eh);
0816: } else if (subFlowPath.selectNodes(activity).size() != 0) {
0817: // SubFlow as Implementation
0818: return validateSubFlow(doc, process, activity, eh);
0819: } else {
0820: return true;
0821: }
0822: }
0823:
0824: private boolean validateTools(Document doc, Element process,
0825: Element activity, CollectingErrorHandler eh)
0826: throws JDOMException, JaxenException {
0827: // initialize the lists to be compared
0828: List formalParamsDataTypeList = new ArrayList();
0829: Set applicationIds = applicationDefs.keySet();
0830: String processId = process.getAttributeValue("Id");
0831: String activityId = activity.getAttributeValue("Id");
0832: boolean faultless = true;
0833: // Map of tool id and its actual parameters.
0834: Map tools = new HashMap();
0835: Iterator toolIterator = toolPath.selectNodes(activity)
0836: .iterator();
0837: while (toolIterator.hasNext()) {
0838: Element toolElem = (Element) toolIterator.next();
0839: String toolId = toolElem.getAttributeValue("Id");
0840: if (toolId != null) {
0841: List apList = new ArrayList();
0842: Iterator apsIt = actualParamsPath.selectNodes(toolElem)
0843: .iterator();
0844: while (apsIt.hasNext()) {
0845: String ap = ((Element) apsIt.next()).getTextTrim();
0846: apList.add(ap);
0847: }
0848: tools.put(toolId, new ToolInfo((String[]) apList
0849: .toArray(new String[apList.size()])));
0850: // Validate existence of the referenced ToolId
0851: if (!applicationIds.contains(toolId)
0852: && !(applicationIds.contains(new PSK(processId,
0853: toolId)))) {
0854: faultless = false;
0855: eh.add(new PrioritizedMessage(
0856: PrioritizedMessage.Priority.ERROR,
0857: bundleBase + "#procdef.tool.notfound",
0858: new Object[] { toolId, activityId,
0859: processId }));
0860: }
0861: }
0862: }
0863:
0864: Iterator toolsIterator = tools.keySet().iterator();
0865: while (toolsIterator.hasNext()) {
0866: String tid = (String) toolsIterator.next();
0867: String[] actualParams = ((ToolInfo) tools.get(tid)).actualParams;
0868: // reference to process local tool?
0869: ApplicationDefinition applDef = (ApplicationDefinition) applicationDefs
0870: .get(new PSK(processId, tid));
0871: // reference to package level tool?
0872: if (applDef == null) {
0873: applDef = (ApplicationDefinition) applicationDefs
0874: .get(tid);
0875: }
0876: // the referenced tool is invalid.
0877: if (applDef == null) {
0878: return false;
0879: }
0880: FormalParameter[] formalParams = applDef.formalParameters();
0881: // check the match of formal parameters and actual parameters
0882: if (actualParams.length != formalParams.length) {
0883: eh
0884: .add(new PrioritizedMessage(
0885: PrioritizedMessage.Priority.ERROR,
0886: bundleBase
0887: + "#procdef.toolimpl.params.notmatched",
0888: new Object[] { tid, activityId,
0889: processId }));
0890: return false;
0891: }
0892: // verify the actual parameters have been defined in data fields.
0893: for (int i = 0; i < actualParams.length; i++) {
0894: String actParam = actualParams[i];
0895: if (procdataMap.containsKey(actParam)) {
0896: boolean compat = true;
0897: if (formalParams[i].mode().equals(
0898: FormalParameter.Mode.IN)
0899: || formalParams[i].mode().equals(
0900: FormalParameter.Mode.INOUT)) {
0901: compat = typeCompatible(
0902: formalParams[i].type(),
0903: XPDLUtil
0904: .extractDataType(((ProcData) procdataMap
0905: .get(actParam)).dataType));
0906: }
0907: if (formalParams[i].mode().equals(
0908: FormalParameter.Mode.OUT)
0909: || formalParams[i].mode().equals(
0910: FormalParameter.Mode.INOUT)) {
0911: compat = typeCompatible(
0912: XPDLUtil
0913: .extractDataType(((ProcData) procdataMap
0914: .get(actParam)).dataType),
0915: formalParams[i].type());
0916: }
0917: if (!compat) {
0918: eh
0919: .add(new PrioritizedMessage(
0920: PrioritizedMessage.Priority.ERROR,
0921: bundleBase
0922: + "#procdef.toolimpl.paramsdatatype.notmatched",
0923: new Object[] { tid, activityId,
0924: processId, actParam }));
0925: return false;
0926: }
0927: } else {
0928: if (formalParams[i].mode() != FormalParameter.Mode.IN) {
0929: eh
0930: .add(new PrioritizedMessage(
0931: PrioritizedMessage.Priority.ERROR,
0932: bundleBase
0933: + "#procdef.toolimpl.params.datanotfound",
0934: new Object[] { actParam, tid,
0935: activityId, processId }));
0936: return false;
0937: }
0938: if (XPDLUtil.isXMLType(formalParams[i].type())) {
0939: String res = notXMLOrValid(actParam);
0940: if (res != null) {
0941: eh
0942: .add(new PrioritizedMessage(
0943: PrioritizedMessage.Priority.ERROR,
0944: bundleBase
0945: + "#procdef.toolimpl.params.invalidXML",
0946: new Object[] { actParam,
0947: tid, activityId,
0948: processId, res }));
0949: return false;
0950: }
0951: }
0952: }
0953: }
0954: }
0955: return faultless;
0956: }
0957:
0958: private boolean validateSubFlow(Document doc, Element process,
0959: Element activity, CollectingErrorHandler eh)
0960: throws JDOMException, JaxenException {
0961: Namespace xpdlns = Namespace.getNamespace(XPDLUtil.XPDL_NS);
0962: Map processMap = processMap(doc);
0963: String activityId = activity.getAttributeValue("Id");
0964: String processId = process.getAttributeValue("Id");
0965: Element subFlowElem = (Element) subFlowPath
0966: .selectSingleNode(activity);
0967: if (subFlowElem == null) {
0968: return true;
0969: }
0970: String subFlowId = subFlowElem.getAttributeValue("Id");
0971: // check if this subflow is defined.
0972: if (!processMap.keySet().contains(subFlowId)) {
0973: eh.add(new PrioritizedMessage(
0974: PrioritizedMessage.Priority.ERROR, bundleBase
0975: + "#procdef.activity.subflow.notfound",
0976: new Object[] { activityId, subFlowId, processId }));
0977: return false;
0978: }
0979: // check if the formal params of the called process in the type of
0980: // OUT and INOUT reference the datafields or formal params in the
0981: // calling process.
0982: // find out all the actual parameters of the calling process.
0983: List actualParams = new ArrayList();
0984: Iterator actualParamsIt = actualParamsPath.selectNodes(
0985: subFlowElem).iterator();
0986: while (actualParamsIt.hasNext()) {
0987: String apm = ((Element) actualParamsIt.next()).getText();
0988: actualParams.add(apm);
0989: }
0990: // Map of id and its mode
0991: Map callerFpMap = new HashMap();
0992: List formalParamsList = formalParamsPath.selectNodes(process);
0993: Iterator formalParamsIt = formalParamsList.iterator();
0994: while (formalParamsIt.hasNext()) {
0995: Element fm = (Element) formalParamsIt.next();
0996: String fmId = fm.getAttributeValue("Id");
0997: callerFpMap.put(fmId, fm.getAttributeValue("Mode"));
0998: }
0999: // find out all the formal parameters of the called process.
1000: Element subFlowProc = (Element) processMap.get(subFlowId);
1001: List subFlowFormalParams = formalParamsPath
1002: .selectNodes(subFlowProc);
1003: if (subFlowFormalParams.size() != actualParams.size()) {
1004: eh
1005: .add(new PrioritizedMessage(
1006: PrioritizedMessage.Priority.ERROR,
1007: bundleBase
1008: + "#procdef.activity.subflow.params.notmatched",
1009: new Object[] { activityId, subFlowId,
1010: processId }));
1011: return false;
1012: }
1013: int index = 0;
1014: for (Iterator formParamIter = subFlowFormalParams.iterator(); formParamIter
1015: .hasNext(); index++) {
1016: Element fpElem = (Element) formParamIter.next();
1017: FormalParameter fp = new FormalParameter(fpElem
1018: .getAttributeValue("Id"), Integer.toString(index),
1019: FormalParameter.Mode.fromString(fpElem
1020: .getAttributeValue("Mode")), XPDLUtil
1021: .extractDataType(fpElem.getChild(
1022: "DataType", xpdlns)));
1023: String actParam = ((String) actualParams.get(index)).trim();
1024: if (callerFpMap.containsKey(actParam)) {
1025: // check the mode of the formal parameter of the calling
1026: // process
1027: String callerParamMode = (String) callerFpMap
1028: .get(actParam);
1029: if (callerParamMode.indexOf(fp.mode().toString()) == -1) {
1030: eh
1031: .add(new PrioritizedMessage(
1032: PrioritizedMessage.Priority.ERROR,
1033: bundleBase
1034: + "#procdef.activity.subflow.parammode.notmatched",
1035: new Object[] { subFlowId, actParam,
1036: callerParamMode,
1037: fp.mode().toString(),
1038: activityId, processId }));
1039: return false;
1040: }
1041: }
1042: if (procdataMap.containsKey(actParam)) {
1043: boolean compat = true;
1044: if (fp.mode().equals(FormalParameter.Mode.IN)
1045: || fp.mode().equals(FormalParameter.Mode.INOUT)) {
1046: compat = typeCompatible(fp.type(), XPDLUtil
1047: .extractDataType(((ProcData) procdataMap
1048: .get(actParam)).dataType));
1049: }
1050: if (fp.mode().equals(FormalParameter.Mode.OUT)
1051: || fp.mode().equals(FormalParameter.Mode.INOUT)) {
1052: compat = compat
1053: && typeCompatible(
1054: XPDLUtil
1055: .extractDataType(((ProcData) procdataMap
1056: .get(actParam)).dataType),
1057: fp.type());
1058: }
1059: if (!compat) {
1060: String[] errDatas = { subFlowId, activityId,
1061: processId };
1062: eh.add(new PrioritizedMessage(
1063: PrioritizedMessage.Priority.ERROR,
1064: bundleBase + "#procdef.activity.subflow"
1065: + ".paramdatatype.notmatched",
1066: errDatas));
1067: return false;
1068: }
1069: } else {
1070: if (fp.mode() != FormalParameter.Mode.IN) {
1071: eh
1072: .add(new PrioritizedMessage(
1073: PrioritizedMessage.Priority.ERROR,
1074: bundleBase
1075: + "#procdef.activity.subflow.datanotfound",
1076: new Object[] { subFlowId, actParam,
1077: activityId, processId }));
1078: return false;
1079: }
1080: if (XPDLUtil.isXMLType(fp.type())) {
1081: String res = notXMLOrValid(actParam);
1082: if (res != null) {
1083: eh
1084: .add(new PrioritizedMessage(
1085: PrioritizedMessage.Priority.ERROR,
1086: bundleBase
1087: + "#procdef.subflow.params.invalidXML",
1088: new Object[] { subFlowId,
1089: actParam, activityId,
1090: processId, res }));
1091: return false;
1092: }
1093: }
1094: }
1095: }
1096: return true;
1097: }
1098:
1099: private String notXMLOrValid(String text) {
1100: String src = text.trim();
1101: if (src.length() == 0 || src.charAt(0) != '<') {
1102: return null;
1103: }
1104: if (src.startsWith("<>") && src.endsWith("</>")) {
1105: src = src.substring(2, src.length() - 3);
1106: }
1107: try {
1108: SAXParserFactory pf = SAXParserFactory.newInstance();
1109: pf.setValidating(false);
1110: SAXParser parser = pf.newSAXParser();
1111: XMLReader reader = parser.getXMLReader();
1112: // Parse the file
1113: InputSource inSrc = new InputSource(new StringReader(
1114: "<temporary-root>" + src + "</temporary-root>"));
1115: reader.parse(inSrc);
1116: } catch (SAXException e) {
1117: return e.getMessage();
1118: } catch (IOException e) {
1119: return e.getMessage();
1120: } catch (ParserConfigurationException e) {
1121: String s = "Cannot read XPDL: " + e.getMessage();
1122: logger.fatal(s, e);
1123: throw new IllegalStateException(s);
1124: }
1125: return null;
1126: }
1127:
1128: private boolean typeCompatible(Object aType, Object bType) {
1129: if (XPDLUtil.isXMLType(aType) && XPDLUtil.isXMLType(bType)) {
1130: return true;
1131: } else if ((aType instanceof ExternalReference)
1132: && (bType instanceof ExternalReference)) {
1133: if (XPDLUtil.isJavaType((ExternalReference) aType) != XPDLUtil
1134: .isJavaType((ExternalReference) bType)) {
1135: return false;
1136: }
1137: if (XPDLUtil.isJavaType((ExternalReference) aType)) {
1138: try {
1139: Class aJType = XPDLUtil
1140: .getJavaType((ExternalReference) aType);
1141: Class bJType = XPDLUtil
1142: .getJavaType((ExternalReference) bType);
1143: return aJType.isAssignableFrom(bJType);
1144: } catch (ClassNotFoundException e) {
1145: logger.error("Class no longer loadable: "
1146: + e.getMessage());
1147: }
1148: return false;
1149: }
1150: // for the time being, assume all other external references
1151: // to be compatible
1152: return true;
1153: } else if (aType.equals(bType)) {
1154: // Basic type
1155: return true;
1156: } else {
1157: logger.debug(aType + " is not compatible with " + bType);
1158: return false;
1159: }
1160: }
1161:
1162: private boolean validatePerformer(String processId,
1163: Element activity, List participantIdList,
1164: CollectingErrorHandler eh) throws JDOMException,
1165: JaxenException {
1166: boolean faultless = true;
1167: Namespace namespace = Namespace.getNamespace("xpdl",
1168: XPDLUtil.XPDL_NS);
1169: String performer = activity
1170: .getChildText("Performer", namespace);
1171: String id = activity.getAttributeValue("Id");
1172: if ((performer != null)
1173: && !participantIdList.contains(performer)) {
1174: // check if this performer is definiert in the data fields
1175: faultless = false;
1176: for (Iterator i = dataFieldsList.iterator(); i.hasNext();) {
1177: Element elem = (Element) i.next();
1178: String elemId = elem.getAttributeValue("Id");
1179: Element dte = elem.getChild("DataType", namespace);
1180: if (elemId.equals(performer)) {
1181: Element dtb = dte.getChild("BasicType", namespace);
1182: if ((dtb != null)
1183: && ("PERFORMER".equals(dtb
1184: .getAttributeValue("Type")))) {
1185: return true;
1186: }
1187: }
1188: }
1189: eh.add(new PrioritizedMessage(
1190: PrioritizedMessage.Priority.ERROR, bundleBase
1191: + "#procdef.performer.notfound",
1192: new Object[] { performer, id, processId }));
1193: }
1194: return faultless;
1195: }
1196:
1197: private List validateParticipants(Document doc, Element process,
1198: CollectingErrorHandler eh) throws JDOMException,
1199: JaxenException {
1200: // some jaxen versions return an immutable list
1201: List partiList = new ArrayList(partPath.selectNodes(doc));
1202: partiList.addAll(procpartPath.selectNodes(process));
1203: String processId = process.getAttributeValue("Id");
1204: List partiIdList = new ArrayList();
1205: Iterator partiInterator = partiList.iterator();
1206: while (partiInterator.hasNext()) {
1207: Element pd = (Element) partiInterator.next();
1208: partiIdList.add(pd.getAttributeValue("Id"));
1209: }
1210: String duplicate = findDuplicate(partiIdList);
1211: if (duplicate != null) {
1212: String[] errdatas = { duplicate };
1213: eh
1214: .add(new PrioritizedMessage(
1215: PrioritizedMessage.Priority.ERROR,
1216: bundleBase
1217: + "#procdef.participant.ununique",
1218: errdatas));
1219: }
1220: return partiIdList;
1221: }
1222:
1223: private void validateTransition(Element process,
1224: CollectingErrorHandler eh) throws JDOMException,
1225: JaxenException {
1226: // Validate unique transition id and loop activies
1227: doValidateTransition(process, transPath.selectNodes(process),
1228: null, false, eh);
1229: // validate unique transition id and loop activies in ActivitySet
1230: Iterator activitySetIterator = actSetPath.selectNodes(process)
1231: .iterator();
1232: while (activitySetIterator.hasNext()) {
1233: Element actSetElem = (Element) activitySetIterator.next();
1234: doValidateTransition(process, transPath
1235: .selectNodes(actSetElem), actSetElem, true, eh);
1236: }
1237: }
1238:
1239: // Validate transition of a process or what included in activitySet. If
1240: // isIncludedInActivitySet is set to true, then activitySet Element must be
1241: // given.
1242: private boolean doValidateTransition(Element process,
1243: Collection trans, Element actSetElem,
1244: boolean isIncludedInActivitySet, CollectingErrorHandler eh)
1245: throws JDOMException, JaxenException {
1246: boolean faultless = true;
1247: String actSetElemId = null;
1248: if (isIncludedInActivitySet) {
1249: actSetElemId = actSetElem.getAttributeValue("Id");
1250: }
1251: List activityIds = getActivityIds(process, actSetElem,
1252: isIncludedInActivitySet);
1253: List transitionIds = new ArrayList();
1254: List transitionTos = new ArrayList();
1255: String processId = process.getAttributeValue("Id");
1256: Iterator activitiesIterator = trans.iterator();
1257: while (activitiesIterator.hasNext()) {
1258: Element transition = (Element) activitiesIterator.next();
1259: String transitionId = transition.getAttributeValue("Id");
1260: transitionIds.add(transitionId);
1261: String transitionFrom = transition
1262: .getAttributeValue("From");
1263: String transitionTo = transition.getAttributeValue("To");
1264: transitionTos.add(transitionTo);
1265: String errTransition = null;
1266: // Validate existence of the referenced activityId
1267: if (!activityIds.contains(transitionFrom)) {
1268: errTransition = transitionFrom;
1269: } else if (!activityIds.contains(transitionTo)) {
1270: errTransition = transitionTo;
1271: }
1272: if (errTransition != null) {
1273: faultless = false;
1274: String errKey1 = "#procdef.transition.activityid.notfound";
1275: if (isIncludedInActivitySet) {
1276: errKey1 = "#procdef.activityset.transition.activityid.notfound";
1277: }
1278: eh.add(new PrioritizedMessage(
1279: PrioritizedMessage.Priority.ERROR, bundleBase
1280: + errKey1, new Object[] {
1281: errTransition, transitionId, processId,
1282: actSetElemId }));
1283: }
1284: }
1285: String duplicate = findDuplicate(transitionIds);
1286: String[] errdatas = { duplicate, processId, actSetElemId };
1287: if (duplicate != null) {
1288: faultless = false;
1289: String errKey = "#procdef.transition.ununique";
1290: if (isIncludedInActivitySet) {
1291: errKey = "#procdef.activityset.transition.ununique";
1292: }
1293: eh.add(new PrioritizedMessage(
1294: PrioritizedMessage.Priority.ERROR, bundleBase
1295: + errKey, errdatas));
1296: }
1297: int activityCount = activityIds.size();
1298: // Validate loop activities
1299: if (activityCount > 0) {
1300: activityIds.removeAll(transitionTos);
1301: if (activityIds.size() == 0) {
1302: faultless = false;
1303: eh.add(new PrioritizedMessage(
1304: PrioritizedMessage.Priority.WARN, bundleBase
1305: + "#procdef.transition.noentry",
1306: errdatas));
1307: }
1308: }
1309: return faultless;
1310: }
1311:
1312: /**
1313: * Extracts all the applications definition in the package and
1314: * in this process and use it to generate a Map of ids and
1315: * <code>ApplicationDefinition</code>.
1316: * @return a Map of ids and <code>ApplicationDefinition</code>.
1317: */
1318: private void validateApplDefs(Document doc,
1319: CollectingErrorHandler eh) throws JaxenException {
1320: applicationDefs = new HashMap();
1321:
1322: // package level definitions
1323: List applList = applPath.selectNodes(doc);
1324: List appIdList = new ArrayList();
1325: for (Iterator itr = applList.iterator(); itr.hasNext();) {
1326: Element appl = (Element) itr.next();
1327: validateApplDef(eh, appIdList, appl, null);
1328: }
1329: String duplicate = findDuplicate(appIdList);
1330: if (duplicate != null) {
1331: String[] errdatas = { duplicate };
1332: eh
1333: .add(new PrioritizedMessage(
1334: PrioritizedMessage.Priority.ERROR,
1335: bundleBase
1336: + "#procdef.application.ununique",
1337: errdatas));
1338: }
1339:
1340: // process level definitons
1341: List processes = processes(doc);
1342: for (Iterator i = processes.iterator(); i.hasNext();) {
1343: Element proc = (Element) i.next();
1344: String procId = proc.getAttributeValue("Id");
1345: // some versions of jaxen return an immutable list
1346: List procApplList = new ArrayList(procApplPath
1347: .selectNodes(proc));
1348: procApplList.addAll(applList);
1349: appIdList = new ArrayList();
1350: for (Iterator itr = procApplList.iterator(); itr.hasNext();) {
1351: Element appl = (Element) itr.next();
1352: validateApplDef(eh, appIdList, appl, procId);
1353: }
1354: duplicate = findDuplicate(appIdList);
1355: if (duplicate != null) {
1356: String[] errdatas = { duplicate };
1357: eh.add(new PrioritizedMessage(
1358: PrioritizedMessage.Priority.ERROR, bundleBase
1359: + "#procdef.application.ununique",
1360: errdatas));
1361: }
1362: }
1363: }
1364:
1365: /**
1366: * @param eh
1367: * @param appIdList
1368: * @param appl
1369: * @throws JaxenException
1370: */
1371: private void validateApplDef(CollectingErrorHandler eh,
1372: List appIdList, Element appl, String procId)
1373: throws JaxenException {
1374: String tac = toolAgentClassPath.stringValueOf(appl);
1375: if (tac == null || tac.length() == 0) {
1376: eh.add(new PrioritizedMessage(
1377: PrioritizedMessage.Priority.ERROR, bundleBase
1378: + "#procdef.application.toolAgentClass",
1379: new String[] { appl.getAttributeValue("Id") }));
1380: }
1381: try {
1382: ApplicationDefinition ad = new ApplicationDefinition();
1383: subtreeToSAX(appl, ad.saxInitializer());
1384: applicationDefs.put(procId == null ? (Object) ad.id()
1385: : new PSK(procId, ad.id()), ad);
1386: appIdList.add(ad.id());
1387: List indexedParams = indexedParamsPath.selectNodes(appl);
1388: if (indexedParams.size() > 0) {
1389: String[] errdatas = { ad.id() };
1390: eh
1391: .add(new PrioritizedMessage(
1392: PrioritizedMessage.Priority.WARN,
1393: bundleBase
1394: + "#procdef.application.indexedParameter",
1395: new String[] { ad.id() }));
1396: }
1397: } catch (IllegalArgumentException ex) {
1398: eh
1399: .add(new PrioritizedMessage(
1400: PrioritizedMessage.Priority.ERROR, ex
1401: .getMessage()));
1402: }
1403: }
1404:
1405: /**
1406: * Extracts all the applications definition in the package and
1407: * in this process and use it to generate a Map of ids and
1408: * <code>ApplicationDefinition</code>.
1409: * @return a Map of ids and <code>ApplicationDefinition</code>.
1410: */
1411: private void validateExtendedAttributes(Document doc,
1412: Element process, CollectingErrorHandler eh)
1413: throws JaxenException {
1414: Element extendedAttrs = null;
1415: if (doc != null) {
1416: extendedAttrs = (Element) extAttrPath.selectSingleNode(doc);
1417: } else if (process != null) {
1418: extendedAttrs = (Element) procExtAttrPath
1419: .selectSingleNode(process);
1420: }
1421: if (extendedAttrs == null) {
1422: return;
1423: }
1424: Namespace namespace = Namespace.getNamespace(XPDLUtil.XPDL_NS);
1425: List extendedAttrList = extendedAttrs.getChildren(
1426: "ExtendedAttribute", namespace);
1427: Iterator iterator = extendedAttrList.iterator();
1428: while (iterator.hasNext()) {
1429: Element extendedAttr = (Element) iterator.next();
1430: String extendedAttrName = extendedAttr
1431: .getAttributeValue("Name");
1432: // Check if the extendedAttribute is in MANUAL/AUTOMATIC/COMPLETED.
1433: if (extendedAttrName.equals("RemoveClosedProcess")) {
1434: String val = extendedAttr.getAttributeValue("Value");
1435: if (val == null) {
1436: val = extendedAttr.getTextTrim();
1437: }
1438: if (!val.equals("AUTOMATIC") && !val.equals("MANUAL")
1439: && !val.equals("COMPLETED")) {
1440: eh
1441: .add(new PrioritizedMessage(
1442: PrioritizedMessage.Priority.ERROR,
1443: bundleBase
1444: + "#procdef.extendedattr.value.notallowed",
1445: new String[] { extendedAttrName,
1446: val }));
1447: }
1448: } else if (extendedAttrName.equals("AuditEventSelection")) {
1449: String val = extendedAttr.getAttributeValue("Value");
1450: if (val == null) {
1451: val = extendedAttr.getTextTrim();
1452: }
1453: if (!val.equals("AllEvents")
1454: && !val.equals("StateEventsOnly")
1455: && !val.equals("ProcessClosedEventsOnly")
1456: && !val.equals("NoEvents")) {
1457: eh
1458: .add(new PrioritizedMessage(
1459: PrioritizedMessage.Priority.ERROR,
1460: bundleBase
1461: + "#procdef.extendedattr.value.notallowed",
1462: new String[] { extendedAttrName,
1463: val }));
1464: }
1465: if (process == null) {
1466: eh.add(new PrioritizedMessage(
1467: PrioritizedMessage.Priority.WARN,
1468: bundleBase
1469: + "#package.nonstandard.auditing"));
1470: } else {
1471: eh.add(new PrioritizedMessage(
1472: PrioritizedMessage.Priority.WARN,
1473: bundleBase
1474: + "#process.nonstandard.auditing",
1475: new String[] { process
1476: .getAttributeValue("Id") }));
1477: }
1478: } else if (extendedAttrName.equals("StoreAuditEvents")) {
1479: String val = extendedAttr.getAttributeValue("Value");
1480: if (val == null) {
1481: val = extendedAttr.getTextTrim();
1482: }
1483: if (!val.equalsIgnoreCase("true")) {
1484: if (process == null) {
1485: eh
1486: .add(new PrioritizedMessage(
1487: PrioritizedMessage.Priority.WARN,
1488: bundleBase
1489: + "#package.nonstandard.logging"));
1490: } else {
1491: eh
1492: .add(new PrioritizedMessage(
1493: PrioritizedMessage.Priority.WARN,
1494: bundleBase
1495: + "#process.nonstandard.logging",
1496: new String[] { process
1497: .getAttributeValue("Id") }));
1498: }
1499: }
1500: }
1501: }
1502: }
1503:
1504: private void validatePriority(String processId, Element priority,
1505: String errorKey, String errorText, CollectingErrorHandler eh)
1506: throws JDOMException {
1507: if (priority == null) {
1508: return;
1509: }
1510: String priorityStr = null;
1511: try {
1512: priorityStr = priority.getText();
1513: if (priorityStr == null) {
1514: return;
1515: } else {
1516: priorityStr = priorityStr.trim();
1517: }
1518: ExtExecutionObjectLocal.Priority.fromInt(Integer
1519: .parseInt(priorityStr.trim()));
1520: } catch (Exception e) {
1521: String[] errdatas = { errorText, priorityStr, processId };
1522: eh.add(new PrioritizedMessage(
1523: PrioritizedMessage.Priority.ERROR, bundleBase + "#"
1524: + errorKey, errdatas));
1525: }
1526: }
1527:
1528: private List getActivityIds(Element process, Element actSet,
1529: boolean isIncludedInActivitySet) throws JDOMException,
1530: JaxenException {
1531: List activityIds = new ArrayList();
1532: Element toBeSelectedNode = isIncludedInActivitySet ? actSet
1533: : process;
1534: Iterator activitiesIterator = actPath.selectNodes(
1535: toBeSelectedNode).iterator();
1536: while (activitiesIterator.hasNext()) {
1537: Element activity = (Element) activitiesIterator.next();
1538: activityIds.add(activity.getAttributeValue("Id"));
1539: }
1540: return activityIds;
1541: }
1542:
1543: private List getTransitionIds(Element process, Element actSet,
1544: boolean isIncludedInActivitySet) throws JDOMException,
1545: JaxenException {
1546: List transitionIds = new ArrayList();
1547: Element toBeSelectedNode = isIncludedInActivitySet ? actSet
1548: : process;
1549: Iterator transIterator = transPath
1550: .selectNodes(toBeSelectedNode).iterator();
1551: while (transIterator.hasNext()) {
1552: Element transElem = (Element) transIterator.next();
1553: transitionIds.add(transElem.getAttributeValue("Id"));
1554: }
1555: return transitionIds;
1556: }
1557:
1558: // Get all the data fields defined in the package header and
1559: // in the given process.
1560: private Map procdatas(Document doc, Element process,
1561: CollectingErrorHandler eh) throws JaxenException {
1562: procdataMap = new HashMap();
1563: List docDataFields = dataFieldPath.selectNodes(doc);
1564: List procDataFields = procDataFieldPath.selectNodes(process);
1565: if (dataFieldsList == null) {
1566: dataFieldsList = new ArrayList();
1567: dataFieldsList.addAll(docDataFields);
1568: dataFieldsList.addAll(procDataFields);
1569: }
1570: // determine all DataFields defined in the package header
1571: extractDataFields(docDataFields, procdataMap, eh);
1572: // determine all DataFields defined in the process
1573: extractDataFields(procDataFields, procdataMap, eh);
1574: // add formal parameters
1575: Namespace xpdlns = Namespace.getNamespace(XPDLUtil.XPDL_NS);
1576: Iterator iterator = procFormalParamsPath.selectNodes(doc)
1577: .iterator();
1578: while (iterator.hasNext()) {
1579: Element elem = (Element) iterator.next();
1580: String id = elem.getAttributeValue("Id");
1581: procdataMap.put(id, new ProcData(null, elem.getChild(
1582: "DataType", xpdlns)));
1583: }
1584: return procdataMap;
1585: }
1586:
1587: /**
1588: * Extract the defined data fields and put them in the procDataMap.
1589: * @param dataFieldsList list of data field.
1590: * @param procDataMap map with extracted data/value.
1591: */
1592: private void extractDataFields(List dataFieldsList,
1593: Map procDataMap, CollectingErrorHandler eh)
1594: throws JaxenException {
1595: Namespace xpdlns = Namespace.getNamespace(XPDLUtil.XPDL_NS);
1596: Iterator iterator = dataFieldsList.iterator();
1597: while (iterator.hasNext()) {
1598: Element elem = (Element) iterator.next();
1599: String id = elem.getAttributeValue("Id");
1600: Element dte = elem.getChild("DataType", xpdlns);
1601: Element ve = elem.getChild("InitialValue", xpdlns);
1602: try {
1603: procdataMap.put(id, new ProcData(XPDLUtil.extractValue(
1604: dte, ve), dte));
1605: } catch (IllegalArgumentException e) {
1606: eh.add(new PrioritizedMessage(
1607: PrioritizedMessage.Priority.ERROR, bundleBase
1608: + "#procdef.data.cannotInit",
1609: new Object[] { id, e.getMessage() }));
1610: continue;
1611: }
1612: }
1613: }
1614:
1615: /**
1616: * Returns the Map of the Ids and element of all the process of the whole
1617: * xpdl doc.
1618: */
1619: private Map processMap(Document doc) throws JaxenException {
1620: Map processMap = new HashMap();
1621: for (Iterator i = processes(doc).iterator(); i.hasNext();) {
1622: Element proc = (Element) i.next();
1623: processMap.put(proc.getAttributeValue("Id"), proc);
1624: }
1625: return processMap;
1626: }
1627:
1628: /**
1629: * Returns the list of all process elements of the whole xpdl doc.
1630: */
1631: private List processes(Document doc) throws JaxenException {
1632: return processPath.selectNodes(doc);
1633: }
1634:
1635: /**
1636: * Used to find the duplicate in the list. If found, returns the duplicate
1637: * in String; if not, returns null;
1638: */
1639: private String findDuplicate(List ids) {
1640: if (ids.size() < 2) {
1641: return null;
1642: }
1643: Collections.sort(ids);
1644: for (int i = 1; i < ids.size(); i++) {
1645: if (((String) ids.get(i)).equals((String) ids.get(i - 1))) {
1646: return (String) ids.get(i);
1647: }
1648: }
1649: return null;
1650: }
1651:
1652: private void subtreeToSAX(Element e, ContentHandler h) {
1653: try {
1654: SAXEventBufferImpl buf = new SAXEventBufferImpl();
1655: SAXOutputter out = new SAXOutputter(buf);
1656: out.output(new Document((Element) e.clone()));
1657: HandlerStack hs = new HandlerStack(h);
1658: buf.emit(hs.contentHandler());
1659: } catch (JDOMException ex) {
1660: throw new IllegalArgumentException(ex.getMessage());
1661: } catch (SAXException ex) {
1662: throw new IllegalArgumentException(ex.getMessage());
1663: }
1664: }
1665:
1666: // Process Scoped Key
1667: private class PSK {
1668: private String processId;
1669: private String key;
1670:
1671: public PSK(String pId, String key) {
1672: processId = pId;
1673: this .key = key;
1674: }
1675:
1676: /**
1677: * Describe <code>equals</code> method here.
1678: *
1679: * @param object an <code>Object</code> value
1680: * @return a <code>boolean</code> value
1681: */
1682: public boolean equals(Object object) {
1683: if (!(object instanceof PSK)) {
1684: return false;
1685: }
1686: PSK other = (PSK) object;
1687: return processId.equals(other.processId)
1688: && key.equals(other.key);
1689: }
1690:
1691: /**
1692: * Describe <code>hashCode</code> method here.
1693: *
1694: * @return an <code>int</code> value
1695: */
1696: public int hashCode() {
1697: return processId.hashCode() ^ key.hashCode();
1698: }
1699: }
1700: }
|