/*
* This file is part of the WfMOpen project.
* Copyright (C) 2001-2003 Danet GmbH (www.danet.de), GS-AN.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* $Id: DefaultProcessDefinition.java 2942 2009-02-18 23:12:12Z mlipp $
*/
package de.danet.an.workflow.domain;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.sax.SAXTransformerFactory;
import javax.xml.transform.sax.TransformerHandler;
import javax.xml.transform.stream.StreamResult;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXHandler;
import org.jdom.output.SAXOutputter;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import de.danet.an.util.sax.DelegatingHandler;
import de.danet.an.util.sax.HandlerStack;
import de.danet.an.util.sax.StackedHandler;
import de.danet.an.util.sax.XmlnsUrisPatcher;
import de.danet.an.workflow.util.SAXEventBufferImpl;
import de.danet.an.workflow.util.XPDLUtil;
import de.danet.an.workflow.omgcore.ProcessDataInfo;
import de.danet.an.workflow.api.Application;
import de.danet.an.workflow.api.FormalParameter;
import de.danet.an.workflow.api.ImportException;
import de.danet.an.workflow.api.InvalidIdException;
import de.danet.an.workflow.api.Participant;
import de.danet.an.workflow.api.ProcessDefinition;
import de.danet.an.workflow.api.SAXEventBuffer;
/**
* This class represents a process definitions with
* the root tag ProcessDefinition. It converts the input to the
* respective objects in the process definition database.
*/
public class DefaultProcessDefinition
implements ProcessDefinition, Serializable {
/**
* logger of this class.
*/
static final org.apache.commons.logging.Log logger
= org.apache.commons.logging.LogFactory
.getLog(DefaultProcessDefinition.class);
private static Properties overridesCache = null;
private final String packagePath = "/{" + XPDLUtil.XPDL_NS + "}Package";
private final String processPath = packagePath
+ "/{" + XPDLUtil.XPDL_NS + "}WorkflowProcesses"
+ "/{" + XPDLUtil.XPDL_NS + "}WorkflowProcess";
private final String applRelPath
= "/{" + XPDLUtil.XPDL_NS + "}Applications"
+ "/{" + XPDLUtil.XPDL_NS + "}Application";
private final String extAttrRelPath
= "/{" + XPDLUtil.XPDL_NS + "}ExtendedAttributes"
+ "/{" + XPDLUtil.XPDL_NS + "}ExtendedAttribute";
/*
* Implementation hint: this class is used as return value of a
* remote method call. Therefore care should be taken to keep the
* amount of data transfered (i.e. the number of non-transient
* attributes) small. Any information derived from the process
* description should be cached in transient attributes.
*/
// name of the workflow process
private String packageId = null;
// name of the workflow process
private String processId = null;
// name of the workflow process
private String packageName = null;
// name of the workflow process
private String processName = null;
// the process description as SAX events.
// this is the primary representation.
private SAXEventBufferImpl procDef = null;
// the representation of process header date and package header data
private transient ProcessHeaderData processHeader = null;
// the context information of the process
private transient ProcessDataInfo contextSignature = null;
/** The result signature cache. */
private transient ProcessDataInfo resultSignature = null;
/** The formal parameters of the process. */
private transient FormalParameter[] formalParams;
/** The defined applications. */
private transient Map applications = null;
/** The defined participants. */
private transient Map participants = null;
/** The remove closed process extended attribute. */
private transient Integer cleanupMode = null;
/** The audit state change events only extended attribute. */
private transient Integer auditEventSelection = null;
/** The audit state change events only extended attribute. */
private transient Boolean storeAuditEvents = null;
/**
* This class defines the elements of package header.
*/
public static class DefaultPackageHeader
implements ProcessDefinition.PackageHeaderData, Serializable {
private String xpdlVersion = null;
private String vendor = null;
private String created = null;
private String description = null;
private String documentation = null;
private String priorityUnit = null;
private String costUnit = null;
/**
* Version of the process definition specification.
* @return the version.
*/
public String xpdlVersion () {
return xpdlVersion;
}
/**
* Defines the origin of this model definition
* and contains vendor's name, vendor's product name
* and product's release number.
* @return the vendor.
*/
public String vendor () {
return vendor;
}
/**
* Creation date of package definition.
* @return the created time.
*/
public String created () {
return created;
}
/**
* Short description of the package definition.
* @return the description.
*/
public String description () {
return description;
}
/**
* Operating system specific path and -filename
* of help file/description file.
* @return the documentation.
*/
public String documentation () {
return documentation;
}
/**
* Units used in simulation data.
* @return the priority unit.
*/
public String priorityUnit () {
return priorityUnit;
}
/**
* Units used in simulation data.
* @return the cost unit.
*/
public String costUnit () {
return costUnit;
}
/**
* A helper class that can be used to initialize this class
* from SAX events.
*/
public class SAXInitializer extends StackedHandler {
/**
* Receive notification of the end of an element.
*
* @param uri the Namespace URI, or the empty string if the
* element has no Namespace URI or if Namespace processing is not
* being performed.
* @param loc the local name (without prefix), or the empty string
* if Namespace processing is not being performed.
* @param raw the raw XML 1.0 name (with prefix), or the empty
* string if raw names are not available.
* @throws SAXException not thrown.
*/
public void endElement(String uri, String loc, String raw)
throws SAXException {
if (loc.equals ("XPDLVersion")) {
xpdlVersion = text ();
} else if (loc.equals ("Vendor")) {
vendor = text ();
} else if (loc.equals ("Created")) {
created = text ();
} else if (loc.equals ("Description")) {
description = text ();
} else if (loc.equals ("Documentation")) {
documentation = text ();
} else if (loc.equals ("PriorityUnit")) {
priorityUnit = text ();
} else if (loc.equals ("CostUnit")) {
costUnit = text ();
}
}
}
/**
* Return a handler that can be used to initialize an object
* from SAX events.
* @return the handler.
*/
StackedHandler saxInitializer () {
return new SAXInitializer ();
}
}
/**
* This class defines the elements of the process header.
*/
public static class DefaultProcessHeader
implements ProcessHeaderData, Serializable {
private String created = null;
private String description = null;
private String priority = null;
private String limit = null;
private String validFrom = null;
private String validTo = null;
private String timeEstimationWaiting = null;
private String timeEstimationWorking = null;
private String timeEstimationDuration = null;
private String author = null;
private String codepage = null;
private String countrykey = null;
private String publicationStatus = null;
private List responsibles = null;
private String version = null;
private PackageHeaderData packageHeader;
/**
* Default constructor.
*/
public DefaultProcessHeader() {
packageHeader = new DefaultPackageHeader();
}
/**
* Dreation date of the process definition.
* @return the created information.
*/
public String created () {
return created;
}
/**
* Short description of the process definition.
* @return the description.
*/
public String description () {
return description;
}
/**
* Priority of the process type.
* @return the priority.
*/
public String priority () {
return priority;
}
/**
* Expected duration for time management purposes in units
* of duration unit (duration unit does not present in this class).
* @return the limit.
*/
public String limit () {
return limit;
}
/**
* Date that the workflow process definition is active from.
* @return the valid from date.
*/
public String validFrom () {
return validFrom;
}
/**
* Date at witch the process definition becomes valid.
* @return the valid to date.
*/
public String validTo () {
return validTo;
}
/**
* Describes the amount of time the performer of the activity
* needs to perform the task.
* @return the estimated time.
*/
public String timeEstimationWaiting () {
return timeEstimationWaiting;
}
/**
* Describes the amount of time the performer of the activity
* needs to perform the task.
* @return the estimated time.
*/
public String timeEstimationWorking () {
return timeEstimationWorking;
}
/**
* Describes the amount of time the performer of the activity
* needs to perform the task.
* @return the estimated time.
*/
public String timeEstimationDuration () {
return timeEstimationDuration;
}
/**
* Name of the author of this workflow process definition.
* @return the author.
*/
public String author () {
return author;
}
/**
* Describes the version of this workflow process definition.
* @return the version.
*/
public String version() {
return version;
}
/**
* Describes the codepage used for the text parts.
* @return the codepage.
*/
public String codepage() {
return codepage;
}
/**
* Describes the country code based on ISO 3166. It could
* be either the three digits country code number, or the
* two alpha characters country codes.
* @return the country code.
*/
public String countrykey() {
return countrykey;
}
/**
* Describes the status of the Workflow Process Definition.
* @return the status.
*/
public String publicationStatus() {
return publicationStatus;
}
/**
* Describes the responsible Workflow participant(s). It is assumed
* that the responsible is the supervisor during the execution
* of the process.
* @return the list of responsible Workflow participant in String.
*/
public List responsibles() {
return responsibles;
}
/**
* The elements of package header.
* @return the package header.
*/
public PackageHeaderData packageHeader () {
return packageHeader;
}
/**
* A helper class that can be used to initialize this class
* from SAX events.
*/
public class SAXInitializer extends StackedHandler {
/**
* Receive notification of the beginning of an element.
*
* @param uri the Namespace URI, or the empty string if the
* element has no Namespace URI or if Namespace processing is not
* being performed.
* @param loc the local name (without prefix), or the empty string
* if Namespace processing is not being performed.
* @param raw the raw XML 1.0 name (with prefix), or the empty
* string if raw names are not available.
* @param a the attributes attached to the element. If there are
* no attributes, it shall be an empty Attributes object.
* @throws SAXException not thrown.
*/
public void startElement
(String uri, String loc, String raw, Attributes a)
throws SAXException {
if (loc.equals ("RedefinableHeader")) {
publicationStatus = a.getValue("PublicationStatus");
} else if (loc.equals ("Responsibles")) {
responsibles = new ArrayList();
}
}
/**
* Receive notification of the end of an element.
*
* @param uri the Namespace URI, or the empty string if the
* element has no Namespace URI or if Namespace processing is not
* being performed.
* @param loc the local name (without prefix), or the empty string
* if Namespace processing is not being performed.
* @param raw the raw XML 1.0 name (with prefix), or the empty
* string if raw names are not available.
* @throws SAXException not thrown.
*/
public void endElement(String uri, String loc, String raw)
throws SAXException {
if (loc.equals ("Created")) {
created = text ();
} else if (loc.equals ("Description")) {
description = text ();
} else if (loc.equals ("Priority")) {
priority = text ();
} else if (loc.equals ("Limit")) {
limit = text ();
} else if (loc.equals ("ValidFrom")) {
validFrom = text ();
} else if (loc.equals ("ValidTo")) {
validTo = text ();
} else if (loc.equals ("WaitingTime")) {
timeEstimationWaiting = text ();
} else if (loc.equals ("WorkingTime")) {
timeEstimationWorking = text ();
} else if (loc.equals ("Duration")) {
timeEstimationDuration = text ();
} else if (loc.equals ("Author")) {
author = text ();
} else if (loc.equals ("Version")) {
version = text ();
} else if (loc.equals ("Codepage")) {
codepage = text ();
} else if (loc.equals ("Countrykey")) {
countrykey = text ();
} else if (loc.equals ("Responsible")) {
responsibles.add(text());
}
}
}
/**
* Return a handler that can be used to initialize an object
* from SAX events.
* @return the handler.
*/
StackedHandler saxInitializer () {
return new SAXInitializer ();
}
}
/**
* Construct a new <code>DefaultProcessDefinition</code> from a
* JDOM document.
*
* @param processDefinition parsed process definition
* as <code>Document</code>.
* @throws JDOMException If an error occurs.
* @throws ImportException if the XPDL is not correct.
*/
public DefaultProcessDefinition (Document processDefinition)
throws JDOMException, ImportException {
try {
SAXOutputter outputter = new SAXOutputter();
procDef = new SAXEventBufferImpl ();
outputter.setContentHandler(procDef);
outputter.setLexicalHandler(procDef);
outputter.output (processDefinition);
} catch (JDOMException e) {
String s = "Cannot get XPDL from DOM: " + e.getMessage();
logger.error (s, e);
throw new IllegalArgumentException (s);
}
init ();
}
/**
* Construct a new <code>DefaultProcessDefinition</code> from an
* XPDL Document.<P>
*
* This constructor assumes that XPDL has been verified before, i.e.
* it parses the process definition with verification turned off.
*
* @param xpdl String representation of the process definition
* @throws IOException If an error occurs.
* @throws ImportException if the XPDL is not correct.
*/
public DefaultProcessDefinition (String xpdl)
throws IOException, ImportException {
try {
SAXParserFactory pf = SAXParserFactory.newInstance();
pf.setValidating (false);
pf.setNamespaceAware (true);
pf.setFeature
("http://xml.org/sax/features/namespace-prefixes", true);
XMLReader reader = null;
try {
pf.setFeature
("http://xml.org/sax/features/xmlns-uris", true);
SAXParser parser = pf.newSAXParser();
reader = parser.getXMLReader();
} catch (SAXException e) {
SAXParser parser = pf.newSAXParser();
reader = new XmlnsUrisPatcher (parser.getXMLReader());
}
procDef = new SAXEventBufferImpl ();
reader.setContentHandler(procDef);
// Parse the file
InputSource inSrc = new InputSource(new StringReader(xpdl));
reader.parse (inSrc);
init ();
} catch (SAXException e) {
String s = "Cannot read XPDL: " + e.getMessage();
logger.error (s, e);
throw new IOException (s);
} catch (ParserConfigurationException e) {
String s = "Cannot read XPDL: " + e.getMessage();
logger.fatal (s, e);
throw new IllegalStateException (s);
}
}
private void writeObject(ObjectOutputStream oos)
throws IOException {
oos.defaultWriteObject();
// OverridesCache cannot be initialized on client side
oos.writeObject(overridesCache);
}
private void readObject (ObjectInputStream stream)
throws IOException, ClassNotFoundException {
stream.defaultReadObject();
overridesCache = (Properties)stream.readObject();
init ();
}
private Properties overrides() {
synchronized (DefaultProcessDefinition.class) {
if (overridesCache == null) {
overridesCache = new Properties();
InputStream is = Thread.currentThread()
.getContextClassLoader().getResourceAsStream
("de.danet.an.workflow.ToolAgentOverrides.properties");
if (is != null) {
try {
overridesCache.load(is);
} catch (IOException e) {
logger.error("Problem reading de.danet.an.workflow."
+ "ToolAgentOverrides.properties: "
+ e.getMessage(), e);
}
}
}
}
return overridesCache;
}
private class MyDelegatingHandler extends DelegatingHandler {
private ApplicationDefinition applDef = null;
// Add some "on the fly" handling
public void startElement
(String uri, String loc, String raw, Attributes a)
throws SAXException {
super.startElement (uri, loc, raw, a);
String curPath = currentPath ();
if (curPath.equals (packagePath)) {
packageId = a.getValue("Id");
packageName = a.getValue("Name");
} else if (curPath.equals (processPath)) {
processId = a.getValue("Id");
processName = a.getValue("Name");
} else if (curPath.equals (packagePath + applRelPath)
|| (curPath.equals (processPath + applRelPath))) {
applDef = new ApplicationDefinition ();
getStack().push (applDef.saxInitializer());
}
}
public void endElement (String uri, String loc, String raw)
throws SAXException {
if (applDef != null) {
applications.put (applDef.id(), applDef);
applDef = null;
} else if (uri.equals (XPDLUtil.XPDL_NS)
&& loc.equals ("Participant")) {
Participant p = (Participant)
getStack().removeContextData ("Participant");
participants.put (p.getId(), p);
}
}
}
/**
* Initialize this <code>DefaultProcessDefinition</code> from the
* SAX events.
*/
private void init () {
formalParams = new FormalParameter[0];
final String dfRelPath = "/{" + XPDLUtil.XPDL_NS + "}DataFields";
final String partiRelPath
= "/{" + XPDLUtil.XPDL_NS + "}Participants"
+ "/{" + XPDLUtil.XPDL_NS + "}Participant";
try {
DelegatingHandler dh = new MyDelegatingHandler ();
processHeader = new DefaultProcessHeader();
dh.addHandler
(packagePath + "/{" + XPDLUtil.XPDL_NS + "}PackageHeader",
((DefaultPackageHeader)processHeader.packageHeader())
.saxInitializer());
dh.addHandler
(processPath + "/{" + XPDLUtil.XPDL_NS + "}ProcessHeader",
((DefaultProcessHeader)processHeader).saxInitializer());
dh.addHandler
(packagePath + "/{" + XPDLUtil.XPDL_NS + "}RedefinableHeader",
((DefaultProcessHeader)processHeader).saxInitializer());
dh.addHandler
(processPath + "/{" + XPDLUtil.XPDL_NS + "}RedefinableHeader",
((DefaultProcessHeader)processHeader).saxInitializer());
StackedHandler dfh = new SAXDataFieldHandler ();
contextSignature = new DefaultProcessDataInfo();
dh.addHandler (packagePath + dfRelPath, dfh);
dh.addHandler (processPath + dfRelPath, dfh);
resultSignature = new DefaultProcessDataInfo();
dh.addHandler
(processPath + "/{" + XPDLUtil.XPDL_NS + "}FormalParameters",
new SAXFormalParameterHandler ());
participants = new HashMap();
dh.addHandler (packagePath + partiRelPath,
DefaultParticipant.SAXInitializer.class);
dh.addHandler (processPath + partiRelPath,
DefaultParticipant.SAXInitializer.class);
StackedHandler extAttrHandler = new SAXExtAttrHandler ();
dh.addHandler (packagePath + extAttrRelPath, extAttrHandler);
dh.addHandler (processPath + extAttrRelPath, extAttrHandler);
applications = new HashMap ();
HandlerStack hs = new HandlerStack (dh);
procDef.emit(hs.contentHandler(), null);
for (Iterator i = applications().iterator(); i.hasNext();) {
ApplicationDefinition applDef
= (ApplicationDefinition)i.next();
postfixApplicationDefinition(applDef);
}
} catch (SAXException e) {
String s = "Cannot initialize: " + e.getMessage ();
logger.error (s, e);
throw new IllegalStateException (s);
}
}
/**
* @param applDef
*/
private void postfixApplicationDefinition(ApplicationDefinition applDef) {
if (applDef.handler() == null) {
String fallbackHandler = null;
String classForHandler = null;
String fallbacks = overrides()
.getProperty("(Class)" + applDef.agentClassName());
if (fallbacks != null) {
String[] vals = fallbacks.split("#");
fallbackHandler = vals[0];
if (vals.length > 1) {
classForHandler = vals[1];
}
} else {
String appPkgPrefix = packageId + "." + applDef.id();
String appProcPrefix
= packageId + "." + processId + "." + applDef.id();
fallbackHandler = overrides().getProperty
(appProcPrefix + ".FallbackHandler");
classForHandler = overrides().getProperty
(appProcPrefix + ".ClassForFallbackHandler");
if (fallbackHandler == null) {
fallbackHandler = overrides().getProperty
(appPkgPrefix + ".FallbackHandler");
classForHandler = overrides().getProperty
(appPkgPrefix + ".ClassForFallbackHandler");
}
}
if (fallbackHandler != null) {
if (logger.isDebugEnabled()) {
logger.debug ("Adding handler "
+ fallbackHandler + " in " + applDef);
}
applDef.updateHandler(fallbackHandler);
if (classForHandler != null) {
if (logger.isDebugEnabled()) {
logger.debug ("Replacing agent class with "
+ classForHandler + " in " + applDef);
}
applDef.updateAgentClassName(classForHandler);
}
}
}
}
/**
* Helper class for retrieving the context info from the process
* definition.
*/
public class SAXDataFieldHandler extends StackedHandler {
private String dfId = null;
/**
* Receive notification of the beginning of an element.
*
* @param uri the Namespace URI, or the empty string if the
* element has no Namespace URI or if Namespace processing is not
* being performed.
* @param loc the local name (without prefix), or the empty string
* if Namespace processing is not being performed.
* @param raw the raw XML 1.0 name (with prefix), or the empty
* string if raw names are not available.
* @param a the attributes attached to the element. If there are
* no attributes, it shall be an empty Attributes object.
* @throws SAXException not thrown.
*/
public void startElement
(String uri, String loc, String raw, Attributes a)
throws SAXException {
if (loc.equals ("DataField")) {
dfId = a.getValue("Id");
} else if (loc.equals ("DataType")) {
getStack().push (new XPDLUtil.SAXDataTypeHandler());
}
}
/**
* Receive notification of the end of an element.
*
* @param uri the Namespace URI, or the empty string if the
* element has no Namespace URI or if Namespace processing is not
* being performed.
* @param loc the local name (without prefix), or the empty string
* if Namespace processing is not being performed.
* @param raw the raw XML 1.0 name (with prefix), or the empty
* string if raw names are not available.
* @throws SAXException not thrown.
*/
public void endElement(String uri, String loc, String raw)
throws SAXException {
if (loc.equals ("DataField")) {
Object dtc = removeContextData ("DataType");
if (dtc == null) {
logger.error ("Unknown data type in data field with id = \""
+ dfId + "\".");
return;
}
contextSignature.put (dfId, dtc);
}
}
}
/**
* Helper class for retrieving the formal parameter info from the process
* definition.
*/
public class SAXFormalParameterHandler extends StackedHandler {
private String fpId = null;
private int fpIdx = 0;
private FormalParameter.Mode fpMode = null;
private List fps = null;
/**
* Receive notification of the beginning of an element.
*
* @param uri the Namespace URI, or the empty string if the
* element has no Namespace URI or if Namespace processing is not
* being performed.
* @param loc the local name (without prefix), or the empty string
* if Namespace processing is not being performed.
* @param raw the raw XML 1.0 name (with prefix), or the empty
* string if raw names are not available.
* @param a the attributes attached to the element. If there are
* no attributes, it shall be an empty Attributes object.
* @throws SAXException not thrown.
*/
public void startElement
(String uri, String loc, String raw, Attributes a)
throws SAXException {
if (loc.equals ("FormalParameters")) {
fps = new ArrayList ();
} else if (loc.equals ("FormalParameter")) {
fpId = a.getValue("Id");
fpMode = FormalParameter.Mode.fromString(a.getValue("Mode"));
} else if (loc.equals ("DataType")) {
getStack().push (new XPDLUtil.SAXDataTypeHandler());
}
}
/**
* Receive notification of the end of an element.
*
* @param uri the Namespace URI, or the empty string if the
* element has no Namespace URI or if Namespace processing is not
* being performed.
* @param loc the local name (without prefix), or the empty string
* if Namespace processing is not being performed.
* @param raw the raw XML 1.0 name (with prefix), or the empty
* string if raw names are not available.
* @throws SAXException not thrown.
*/
public void endElement(String uri, String loc, String raw)
throws SAXException {
if (loc.equals ("FormalParameter")) {
Object dtc = removeContextData ("DataType");
if (dtc == null) {
logger.error ("Unknown data type in formal parameter "
+ "with id = \"" + fpId + "\".");
return;
}
if (fpMode != FormalParameter.Mode.IN) {
resultSignature.put (fpId, dtc);
}
contextSignature.put (fpId, dtc);
fps.add (new FormalParameter
(fpId, Integer.toString(fpIdx++), fpMode, dtc));
} else if (loc.equals ("FormalParameters")) {
formalParams = (FormalParameter[])
fps.toArray(new FormalParameter[fps.size()]);
}
}
}
/**
* Helper class for retrieving extended attributes from the process
* definition.
*/
public class SAXExtAttrHandler extends StackedHandler {
private String waitingFor = null;
private String valueAttr = null;
/**
* Receive notification of the beginning of an element.
*
* @param uri the Namespace URI, or the empty string if the
* element has no Namespace URI or if Namespace processing is not
* being performed.
* @param loc the local name (without prefix), or the empty string
* if Namespace processing is not being performed.
* @param raw the raw XML 1.0 name (with prefix), or the empty
* string if raw names are not available.
* @param a the attributes attached to the element. If there are
* no attributes, it shall be an empty Attributes object.
* @throws SAXException not thrown.
*/
public void startElement
(String uri, String loc, String raw, Attributes a)
throws SAXException {
waitingFor = a.getValue ("Name");
if (waitingFor == null) {
waitingFor = "(unknown)";
}
valueAttr = a.getValue ("Value");
}
/**
* Receive notification of the end of an element.
*
* @param uri the Namespace URI, or the empty string if the
* element has no Namespace URI or if Namespace processing is not
* being performed.
* @param loc the local name (without prefix), or the empty string
* if Namespace processing is not being performed.
* @param raw the raw XML 1.0 name (with prefix), or the empty
* string if raw names are not available.
* @throws SAXException not thrown.
*/
public void endElement(String uri, String loc, String raw)
throws SAXException {
if (waitingFor.equals ("RemoveClosedProcess")) {
// note that for extended attributes process level values
// preceed package level settings.
if (cleanupMode != null) {
return;
}
if (valueAttr == null) {
valueAttr = getStack().text().trim();
}
cleanupMode = new Integer(REMOVE_AUTOMATIC);
if (valueAttr.equals("MANUAL")) {
cleanupMode = new Integer(REMOVE_MANUAL);
} else if (valueAttr.equals("COMPLETED")) {
cleanupMode = new Integer(REMOVE_COMPLETED);
}
return;
}
if (waitingFor.equals ("AuditEventSelection")) {
// note that for extended attributes process level values
// preceed package level settings.
if (auditEventSelection != null) {
return;
}
if (valueAttr == null) {
valueAttr = getStack().text().trim();
}
if (valueAttr.equals ("AllEvents")) {
auditEventSelection = new Integer
(ProcessDefinition.AUDIT_SELECTION_ALL_EVENTS);
}
if (valueAttr.equals ("StateEventsOnly")) {
auditEventSelection = new Integer
(ProcessDefinition.AUDIT_SELECTION_STATE_EVENTS_ONLY);
}
if (valueAttr.equals ("ProcessClosedEventsOnly")) {
auditEventSelection = new Integer
(ProcessDefinition
.AUDIT_SELECTION_PROCESS_CLOSED_EVENTS_ONLY);
}
if (valueAttr.equals ("NoEvents")) {
auditEventSelection = new Integer
(ProcessDefinition.AUDIT_SELECTION_NO_EVENTS);
}
return;
}
if (waitingFor.equals ("StoreAuditEvents")) {
// note that for extended attributes process level values
// preceed package level settings.
if (storeAuditEvents != null) {
return;
}
if (valueAttr == null) {
valueAttr = getStack().text().trim();
}
storeAuditEvents
= new Boolean (valueAttr.equalsIgnoreCase("true"));
return;
}
}
}
/**
* Return the id of the package.
* @return id of the package in String.
*/
public String packageId() {
return packageId;
}
/**
* Return the id of the process.
* @return id of the process in String.
*/
public String processId() {
return processId;
}
/**
* Return the name of the package.
* @return name of the package in String.
*/
public String packageName() {
return packageName;
}
/**
* Return the name of the process.
* @return name of the process in String.
*/
public String processName() {
return processName;
}
/**
* Return the name of the process manager.
* @return name of the process manager as String.
*/
public String mgrName() {
return packageId + "/" + processId;
}
/**
* Return the version of the process manager.
* @return version of the process manager as String.
*/
public String version() {
return processHeader().version();
}
/**
* Returns the meta information that describes the context for
* this kind of process. Equivalent to calling {@link
* de.danet.an.workflow.localcoreapi.WfProcessMgrLocal#contextSignature
* <code>contextSignature</code>} on a process manager for this
* kind of process.
* @return the process meta information.
* @see de.danet.an.workflow.localcoreapi.WfProcessMgrLocal#contextSignature
*/
public ProcessDataInfo contextSignature() {
return contextSignature;
}
/**
* Returns the meta information that describes the result for this
* kind of process. Equivalent to calling {@link
* de.danet.an.workflow.localcoreapi.WfProcessMgrLocal#resultSignature
* <code>resultSignature</code>} on a process manager for this
* kind of process.<P>
*
* The implementation returns all formal IN or INOUT parameters.
*
* @return the process meta information.
* @see de.danet.an.workflow.localcoreapi.WfProcessMgrLocal#resultSignature
*/
public ProcessDataInfo resultSignature() {
return resultSignature;
}
/**
* Returns the meta information that describes the formal parameters
* for this kind of process.
* @return the process meta information.
*/
public FormalParameter[] formalParameters() {
return formalParams;
}
/**
* Return the process definition as SAX event buffer.
* @return the process definition
*/
public SAXEventBuffer toSAX () {
return procDef;
}
/**
* Use XMLOutputter to transfer process definition presented as JDOM Tree
* in String.
* @return the process definition in String.
*/
public String toXPDL() {
try {
TransformerFactory tf = TransformerFactory.newInstance();
if (!tf.getFeature(SAXTransformerFactory.FEATURE)) {
String s = "JAXP transformer factory does not support"
+ " a SAX transformer!";
logger.fatal (s);
throw new IllegalStateException (s);
}
TransformerHandler th
= ((SAXTransformerFactory)tf).newTransformerHandler ();
StringWriter out = new StringWriter ();
th.setResult (new StreamResult(out));
procDef.emit(th, th);
return out.toString();
} catch (SAXException e) {
String s = "Error generating XPDL as string: " + e.getMessage ();
logger.error (s, e);
throw new IllegalStateException (s);
} catch (TransformerConfigurationException e) {
String s = "Error generating XPDL as string: " + e.getMessage ();
logger.error (s, e);
throw new IllegalStateException (s);
}
}
/**
* Return the process definition presented as JDOM document.
*
* @return the process definition presented as JDOM document.
*/
public Document toJDOM () {
try {
SAXHandler sh = new SAXHandler ();
procDef.emit(sh, sh);
return sh.getDocument();
} catch (SAXException e) {
logger.error (e.getMessage(), e);
throw new IllegalStateException
("Error generating XPDL as JDOM: " + e.getMessage());
}
}
/** Return the process header data of this process definition.
*
* @return the process header data of this process definition.
*/
public ProcessHeaderData processHeader() {
return processHeader;
}
/**
* Extracts all the applications definition in the package and
* in this process and use it to generate a collection of
* <code>ApplicationDefinition</code>.
* @return a collection of <code>ApplicationDefinition</code>.
*/
public Collection applications() {
return applications.values();
}
/**
* Find out the dedicated application using the given id.
* @param id the given id of the application to be found.
* @throws InvalidIdException If no such application be found
* @return the dedicated application.
*/
public Application applicationById (String id) throws InvalidIdException {
Application a = (Application)applications.get (id);
if (a == null) {
throw new InvalidIdException ("No application with Id = " + id);
}
return a;
}
/**
* Gets the participants for this process.
* @return a collection of {@link Participant
* <code>Participant</code>s} for this process.
*/
public Collection participants() {
return participants.values();
}
/**
* Return the participant identified by the id.
* @param id identity of the participant in string
* @return a Participant object
* @throws InvalidIdException if no participant with the given id exists.
*/
public Participant participantById(String id) throws InvalidIdException {
Participant p = (Participant)participants.get (id);
if (p == null) {
throw new InvalidIdException ("No participant with Id = " + id);
}
return p;
}
/**
* This method checks if the closed process should be removed. Parse the
* process definition and find out if the extendAttribute with the name of
* RemoveClosedProcess has the value of MANUAL, then return false; if it
* has the value of AUTOMATIC, then return true. Default is true.
* @return true if the closed process should be removed, otherwise false.
*/
public boolean removeClosedProcess() {
return cleanupMode() == REMOVE_AUTOMATIC;
}
/**
* This method checks if a closed process should be removed. Parse the
* process definition and find out if the extendAttribute with the name of
* RemoveClosedProcess has the value of <code>MANUAL</code>,
* <code>AUTOMATIC</code> or <code>COMPLETED</code> and return the
* corresponding constant. Default is to remove automatically, i.e.
* {@link #REMOVE_AUTOMATIC <code>REMOVE_AUTOMATIC</code>}.
* @return the cleanup mode.
*/
public int cleanupMode() {
return cleanupMode == null ? REMOVE_AUTOMATIC
: cleanupMode.intValue();
}
/**
* This method the audit event filter for instances of this
* process definition.
* @return the audit event filter in effect.
*/
public int auditEventSelection() {
return (auditEventSelection == null)
? ProcessDefinition.AUDIT_SELECTION_ALL_EVENTS
: auditEventSelection.intValue();
}
/**
* This method reports if audit events are written to the
* database.
* @return <code>true</code> if only state change events of the
* process instance are audited.
*/
public boolean storeAuditEvents() {
return (storeAuditEvents == null)
|| storeAuditEvents.booleanValue ();
}
//
// Additional methods provided by the domain class.
//
//
// Some private helpers.
//
/**
* Retrieves the value for the attribute "Id" of this element.
* @return the value for the attribute or <code>null</code>, if
* the attribute does not exist.
* @param el element
*/
private String getAttributeValue(Element el, String at) {
return el != null ? el.getAttributeValue(at): null;
}
}