Package de.danet.an.workflow.domain

Source Code of de.danet.an.workflow.domain.AbstractProcess$DupEndFilter

/*
* 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: AbstractProcess.java 2677 2007-12-04 12:54:46Z drmlipp $
*
* $Log$
* Revision 1.24  2007/09/21 06:19:35  mlipp
* Fixed problem with NamingException during process deletion.
*
* Revision 1.23  2007/09/14 12:34:41  drmlipp
* Improved map initialization.
*
* Revision 1.22  2007/05/03 21:58:16  mlipp
* Internal refactoring for making better use of local EJBs.
*
* Revision 1.21  2007/03/27 21:59:43  mlipp
* Fixed lots of checkstyle warnings.
*
* Revision 1.20  2007/02/27 14:34:13  drmlipp
* Some refactoring to reduce cyclic dependencies.
*
* Revision 1.19  2007/01/25 23:00:31  mlipp
* Fixed result().
*
* Revision 1.18  2006/12/03 22:44:20  mlipp
* Fixed return object of requester() for subflows.
*
* Revision 1.17  2006/11/19 21:53:47  mlipp
* Finished support for native Java types.
*
* Revision 1.16  2006/10/19 11:16:30  drmlipp
* Block activity deadlines cannot be delayed.
*
* Revision 1.15  2006/10/17 22:58:39  mlipp
* Continuing implementation of suspended exception handling.
*
* Revision 1.14  2006/10/17 15:55:12  drmlipp
* Introducing new state ...suspended.abandoning.
*
* Revision 1.13  2006/10/07 20:41:34  mlipp
* Merged J2EE 1.4 adaptions from test branch.
*
* Revision 1.12  2006/09/29 12:32:08  drmlipp
* Consistently using WfMOpen as projct name now.
*
* Revision 1.11  2006/07/18 13:18:14  drmlipp
* Merged changes from wfmopen-1.3.x branch up to 1.3.4.
*
* Revision 1.6.2.20  2006/03/15 10:46:07  drmlipp
* Fixed problems with E4X and empty input/output documents.
*
* Revision 1.6.2.19  2006/03/14 16:44:27  drmlipp
* Fixed loop.
*
* Revision 1.6.2.18  2006/03/07 13:51:32  drmlipp
* Finished transition to E4X usage for actual parameter evaluation.
*
* Revision 1.10  2006/03/08 14:46:43  drmlipp
* Synchronized with 1.3.3p5.
*
* Revision 1.9  2005/10/10 20:02:12  mlipp
* Synchronized with 1.3.3.
*
* Revision 1.8  2005/04/22 15:10:53  drmlipp
* Merged changes from 1.3 branch up to 1.3p15.
*
* Revision 1.6.2.11  2005/04/22 14:09:27  drmlipp
* Support specification of all extended attributes in Value attribute.
*
* Revision 1.6.2.10  2005/04/18 11:11:20  drmlipp
* More event handling optimization.
*
* Revision 1.6.2.9  2005/04/16 21:18:30  drmlipp
* Made audit event filtering more flexible and added possibility to turn
* off audit log.
*
* Revision 1.6.2.8  2005/04/15 21:07:39  drmlipp
* Added "storeAuditEvents" flag.
*
* Revision 1.6.2.7  2005/04/15 08:26:34  drmlipp
* Fixed isHandled.
*
* Revision 1.6.2.6  2005/04/14 21:40:52  drmlipp
* Optimized event feedback.
*
* Revision 1.6.2.5  2005/04/14 15:08:51  drmlipp
* Added "filterable" event auditing.
*
* Revision 1.7  2005/02/04 14:25:26  drmlipp
* Synchronized with 1.3rc2.
*
* Revision 1.6.2.4  2005/02/01 21:15:51  drmlipp
* Fixed audit event generation for deferred choice.
*
* Revision 1.6.2.3  2005/02/01 16:08:41  drmlipp
* Implemented deferred choice.
*
* Revision 1.6.2.2  2005/01/31 21:12:58  drmlipp
* Continued implementation of deferred choice.
*
* Revision 1.6.2.1  2005/01/31 15:41:12  drmlipp
* Started implementation of deferred choice.
*
* Revision 1.6  2005/01/21 09:34:56  drmlipp
* Moved initialization of debug attribute to proper class.
*
* Revision 1.5  2005/01/12 22:10:35  mlipp
* Added trim to string comparison.
*
* Revision 1.4  2005/01/10 22:19:35  mlipp
* Added extension attribute for debug mode.
*
* Revision 1.3  2005/01/02 20:49:13  mlipp
* First version of debug mode.
*
* Revision 1.2  2004/08/19 13:24:49  drmlipp
* Fixed AVK errors and (many) warnings.
*
* Revision 1.1.1.6  2004/08/18 15:17:38  drmlipp
* Update to 1.2
*
* Revision 1.201  2004/07/14 12:08:39  lipp
* Fixed problem with default for condition type.
*
* Revision 1.200  2004/07/14 11:06:03  lipp
* Supplied default for condition type.
*
* Revision 1.199  2004/07/04 17:34:19  lipp
* Fixed problem with event source.
*
* Revision 1.198  2004/06/30 12:05:39  lipp
* Added jelly variable scoping.
*
* Revision 1.197  2004/06/29 14:52:46  lipp
* Unpacking values from JavaScript argument evaluation.
*
* Revision 1.196  2004/06/29 13:16:29  lipp
* Fixed jelly script argument access.
*
* Revision 1.195  2004/06/28 20:32:00  lipp
* Added support for accessing process data in jelly scripts.
*
* Revision 1.194  2004/06/28 14:59:49  lipp
* Started jelly interpretation of actual XML arguments.
*
* Revision 1.193  2004/05/09 18:42:59  lipp
* Finished process instantiation restructuring.
*
* Revision 1.192  2004/05/07 15:02:27  lipp
* Removed legacy initialization code.
*
* Revision 1.191  2004/05/06 19:39:17  lipp
* Restructured block activity handling.
*
* Revision 1.190  2004/05/05 09:44:07  lipp
* Finished SAX based process creation (no cleanup of old code, yet).
*
* Revision 1.189  2004/05/04 19:48:07  lipp
* Getting on with SAX based process creation.
*
* Revision 1.188  2004/05/03 15:38:11  lipp
* Getting on with SAX based process creation.
*
* Revision 1.187  2004/04/30 13:46:19  lipp
* Getting on with SAX based initialization.
*
* Revision 1.186  2004/04/30 12:44:53  lipp
* Fixed SAX initialization bug.
*
* Revision 1.185  2004/04/29 15:39:31  lipp
* Getting on with SAX based initialization.
*
* Revision 1.184  2004/04/28 14:19:20  lipp
* Getting started with SAX based process creation.
*
* Revision 1.183  2004/04/12 19:33:52  lipp
* Clarified application invocation interface.
*
* Revision 1.182  2004/04/06 21:18:39  lipp
* Reflect value conversions in audit event.
*
* Revision 1.181  2004/04/06 15:34:19  lipp
* Supporting values of type Document.
*
* Revision 1.180  2004/04/01 20:53:04  lipp
* Fixed problem with missing Type attribute.
*
* Revision 1.179  2004/03/25 14:41:46  lipp
* Added possibility to specify actual parameters as XML.
*
* Revision 1.178  2004/03/21 12:37:12  lipp
* Removed left over event logger.
*
* Revision 1.177  2004/03/20 21:08:43  lipp
* Added access to requesting processes' channels.
*
* Revision 1.176  2004/03/20 19:28:02  lipp
* Keeping relationship to super-flow both for sync and asynch call.
*
* Revision 1.175  2004/03/18 09:14:45  lipp
* Workaround for transformer bug.
*
* Revision 1.174  2004/02/21 14:42:43  lipp
* Moved TransitionManager to domain package. Having it in its own
* package caused too many circular dependencies (and there are good
* points for having it in the domain package anyway).
*
* Revision 1.173  2004/02/16 15:38:51  lipp
* Fixed handling of non-well formed result values.
*
* Revision 1.172  2004/02/06 13:37:35  lipp
* Added channel close notification.
*
* Revision 1.171  2004/01/21 09:54:45  lipp
* Adapted to contextSignature modification.
*
* Revision 1.170  2003/12/16 21:56:02  lipp
* Track all activity closures, transition manager might be reused.
*
* Revision 1.169  2003/12/16 16:46:25  lipp
* Let process close activities to avoid inconsistent transition manager
* states.
*
* Revision 1.168  2003/11/26 16:40:12  huaiyang
* workflow for jboss calling ejbs toString.
*
* Revision 1.167  2003/11/17 16:16:36  lipp
* Fixed refresh.
*
* Revision 1.166  2003/10/28 13:24:16  lipp
* Fixed handling of RemoveException.
*
* Revision 1.165  2003/09/25 12:00:05  lipp
* Avoid client dependency on rhino.
*
* Revision 1.164  2003/09/25 11:01:20  lipp
* Fixed usage of jsScope (may not be used remotely).
*
* Revision 1.163  2003/09/24 13:48:49  lipp
* Fixed JavaScript access to process relevant data of type Date. Fixed
* thread handling for block activities.
*
* Revision 1.162  2003/09/23 17:05:04  lipp
* Fixed various problems with block activities.
*
* Revision 1.161  2003/09/22 20:50:12  lipp
* Most of deadline handling for block activities.
*
* Revision 1.160  2003/09/22 15:53:44  lipp
* Receiving deadline events.
*
* Revision 1.159  2003/09/22 12:51:44  lipp
* Fixed key creation for block activity (must be unique).
*
* Revision 1.158  2003/09/22 12:32:57  lipp
* Implemented deadline creation for block activities.
*
* Revision 1.157  2003/09/21 21:28:14  lipp
* Introducing "virtual" block activity.
*
* Revision 1.156  2003/09/19 13:11:13  lipp
* New way of handling timeouts for subflows.
*
* Revision 1.155  2003/09/18 14:08:16  lipp
* Added abandon method to handle deadlines.
*
* Revision 1.154  2003/09/15 15:43:40  lipp
* Initial version of handling exceptions in transition manager.
*
* Revision 1.153  2003/09/04 08:46:44  lipp
* Fixed client dependency on rhino.jar.
*
* Revision 1.152  2003/07/09 13:25:14  lipp
* Accept strings as values for fields of type performer.
*
* Revision 1.151  2003/07/04 15:54:14  lipp
* Fixed handling of W3C DOM result.
*
* Revision 1.150  2003/06/27 08:51:45  lipp
* Fixed copyright/license information.
*
* Revision 1.149  2003/06/26 22:08:04  lipp
* Added handling of JDOM results.
*
* Revision 1.148  2003/06/05 21:21:20  lipp
* Real fix of state table problem.
*
* Revision 1.147  2003/06/05 17:24:44  lipp
* Fixed bug in transition table.
*
* Revision 1.146  2003/05/31 20:05:25  lipp
* Added support for different condition types.
*
* Revision 1.145  2003/05/25 20:47:00  lipp
* Fixed initialization.
*
* Revision 1.144  2003/05/16 08:08:49  lipp
* Handling OTHERWISE condition type.
*
* Revision 1.143  2003/05/07 14:45:49  lipp
* Implemented synchronous subflow.
*
* Revision 1.142  2003/05/05 14:39:50  lipp
* Moved code for removing process automatically to event handling.
*
* Revision 1.141  2003/05/05 07:04:51  lipp
* Handling parameters for sub-flow now.
*
* Revision 1.140  2003/05/02 14:55:59  lipp
* Resolved some more package dependencies.
*
* Revision 1.139  2003/04/26 18:56:24  lipp
* Moved extended interfaces to own package.
*
* Revision 1.138  2003/04/26 16:11:15  lipp
* Moved some classes to reduce package dependencies.
*
* Revision 1.137  2003/04/25 14:50:59  lipp
* Fixed javadoc errors and warnings.
*
* Revision 1.136  2003/04/24 20:50:13  lipp
* Fixed some warnings.
*
* Revision 1.135  2003/04/23 14:27:35  lipp
* Improved modelling of header data.
*
* Revision 1.134  2003/04/19 18:33:29  lipp
* Improved handling of info.
*
* Revision 1.133  2003/04/16 09:52:48  lipp
* Added initialization of formal parameters as data fields.
*
* Revision 1.132  2003/04/09 07:51:26  lipp
* Storing description now.
*
* Revision 1.131  2003/04/08 13:08:45  lipp
* Fixed null pointer exception.
*
* Revision 1.130  2003/04/03 11:44:06  lipp
* Support for W3C DOM arguments.
*
* Revision 1.129  2003/04/02 11:20:06  lipp
* Moved type adaption to framework.
*
* Revision 1.128  2003/04/02 09:30:05  lipp
* Supporting more data types.
*
* Revision 1.127  2003/03/31 16:50:28  huaiyang
* Logging using common-logging.
*
* Revision 1.126  2003/03/28 14:42:35  lipp
* Moved some code to XPDLUtil.
*
* Revision 1.125  2003/03/28 12:44:08  lipp
* Moved XPDL related constants to XPDLUtil.
*
* Revision 1.124  2003/03/28 11:41:59  lipp
* More changes for data type support.
*
* Revision 1.123  2003/03/27 16:32:20  lipp
* Started support for addditional data types.
*
* Revision 1.122  2003/03/13 14:07:13  lipp
* Improved implementation of condition evaluation.
*
*/
package de.danet.an.workflow.domain;

import java.io.IOException;
import java.io.Serializable;
import java.io.StringReader;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import java.lang.reflect.Method;
import java.text.ParseException;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParserFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.sax.SAXResult;

import org.apache.commons.jelly.JellyContext;
import org.apache.commons.jelly.JellyException;
import org.apache.commons.jelly.Script;
import org.apache.commons.jelly.XMLOutput;
import org.apache.commons.jelly.parser.XMLParser;
import org.apache.xmlbeans.XmlCursor;
import org.apache.xmlbeans.XmlException;
import org.apache.xmlbeans.XmlObject;
import org.apache.xmlbeans.XmlOptions;
import org.apache.xmlbeans.XmlSaxHandler;

import org.dom4j.io.SAXContentHandler;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.DOMBuilder;
import org.jdom.output.SAXOutputter;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.Function;
import org.mozilla.javascript.JavaScriptException;
import org.mozilla.javascript.Scriptable;
import org.mozilla.javascript.ScriptableObject;
import org.mozilla.javascript.Wrapper;
import org.mozilla.javascript.xml.XMLObject;
import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;
import org.xml.sax.helpers.XMLFilterImpl;

import de.danet.an.util.XMLUtil;
import de.danet.an.util.sax.BodyFilter;
import de.danet.an.util.sax.HandlerStack;
import de.danet.an.util.sax.NamespaceAttributesFilter;
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.internalapi.ExtActivityLocal;
import de.danet.an.workflow.internalapi.ExtProcessLocal;
import de.danet.an.workflow.internalapi.ScriptException;
import de.danet.an.workflow.localapi.ActivityLocal;
import de.danet.an.workflow.localapi.ProcessLocal;
import de.danet.an.workflow.localapi.TransitionLocal;
import de.danet.an.workflow.localcoreapi.WfActivityLocal;
import de.danet.an.workflow.localcoreapi.WfProcessLocal;
import de.danet.an.workflow.omgcore.AlreadyRunningException;
import de.danet.an.workflow.omgcore.CannotChangeRequesterException;
import de.danet.an.workflow.omgcore.CannotStartException;
import de.danet.an.workflow.omgcore.CannotStopException;
import de.danet.an.workflow.omgcore.InvalidControlOperationException;
import de.danet.an.workflow.omgcore.InvalidDataException;
import de.danet.an.workflow.omgcore.InvalidPriorityException;
import de.danet.an.workflow.omgcore.InvalidStateException;
import de.danet.an.workflow.omgcore.NotRunningException;
import de.danet.an.workflow.omgcore.ProcessData;
import de.danet.an.workflow.omgcore.ProcessDataInfo;
import de.danet.an.workflow.omgcore.ResultNotAvailableException;
import de.danet.an.workflow.omgcore.TransitionNotAllowedException;
import de.danet.an.workflow.omgcore.UpdateNotAllowedException;
import de.danet.an.workflow.omgcore.WfAuditEvent;
import de.danet.an.workflow.omgcore.WfRequester;
import de.danet.an.workflow.omgcore.WfStateAuditEvent;
import de.danet.an.workflow.omgcore.WfExecutionObject.ClosedState;
import de.danet.an.workflow.omgcore.WfExecutionObject.NotRunningState;
import de.danet.an.workflow.omgcore.WfExecutionObject.OpenState;
import de.danet.an.workflow.omgcore.WfExecutionObject.State;

import de.danet.an.workflow.api.CannotRemoveException;
import de.danet.an.workflow.api.DefaultProcessData;
import de.danet.an.workflow.api.ExternalReference;
import de.danet.an.workflow.api.FormalParameter;
import de.danet.an.workflow.api.InvalidKeyException;
import de.danet.an.workflow.api.Participant;
import de.danet.an.workflow.api.ProcessDefinition;
import de.danet.an.workflow.api.SAXEventBuffer;
import de.danet.an.workflow.api.Transition;
import de.danet.an.workflow.api.Activity.ClosedCompletedState;
import de.danet.an.workflow.api.Activity.Implementation;
import de.danet.an.workflow.api.Activity.JoinAndSplitMode;
import de.danet.an.workflow.api.Activity.StartFinishMode;
import de.danet.an.workflow.api.Activity.SubFlowImplementation;
import de.danet.an.workflow.api.Process;


/**
* <code>AbstractProcess</code> represents the base implementation
* of the interface {@link ProcessLocal <code>ProcessLocal</code>}.<P>
*
* With logger level <code>DEBUG</code>, event handling information
* and information about process context changes will be logged.
*/
public abstract class AbstractProcess extends AbstractExecutionObject
    implements TimedObject, Serializable {

    private static final org.apache.commons.logging.Log logger
  = org.apache.commons.logging.LogFactory.getLog(AbstractProcess.class);

    /** The immutable root for JavaScript evaluation. */
    private static final ScriptableObject GLOBAL_JS_SCOPE
  = (new Context()).initStandardObjects (null, false);
    static {
        Context cx = Context.enter();
        try {
            cx.evaluateString (GLOBAL_JS_SCOPE, "java", "<init>", 1, null);
        } catch (JavaScriptException e) {
            logger.warn("Cannot initialize \"java\" in rhino context: "
                        + e.getMessage());
        }
        try {
            cx.evaluateString (GLOBAL_JS_SCOPE, "new XML()", "<init>", 1, null);
        } catch (JavaScriptException e) {
            logger.warn("Cannot initialize \"XML\" in rhino context: "
                        + e.getMessage());
        }
        GLOBAL_JS_SCOPE.sealObject ();
        Context.exit();
    }

    private static class TimeoutInfo implements Serializable {
  public Long activity;
  public int dlIndex;
  public TimeoutInfo (Long a, int d) {
      activity = a;
      dlIndex = d;
  }
  public String toString () {
      return "TimeoutInfo[activity=" + activity
    + ",deadlineIndex=" + dlIndex + "]";
  }
    }
  
    private Collection participants = null;

    /** The TransitionManager for this process. */
    private TransitionManager transitionMgrCache = null;

    /** The JavaScript scope for this process. */
    private ScriptableObject jsScopeCache = null;

    /** The jelly context for this process. */
    private JellyContext jellyContextCache = null;

    /** All block activity representations by key. */
    private Map baRepresentations = new HashMap ();

    /** Set in init, cleared in subsequent refresh */
    private boolean initedBeforeRefresh = false;

    /**
     * Creates a new <code>AbstractProcess</code>.
     */
    public AbstractProcess () {
    }

    //
    // Persistent attribute accessors and associated methods
    //

    /**
     * The getter method for the persistent attribute <code>requester</code>.
     *
     * @return the value of requester.
     */
    protected abstract WfRequester getPaRequester();

    /**
     * The getter method for the persistent attribute <code>id</code>.
     *
     * @return the value of process id.
     * @see #setPaId
     */
    protected abstract String getPaId();

    /**
     * The setter method for the persistent attribute <code>Id</code>.
     *
     * @param newId the new value of process id.
     * @see #getPaId
     */
    protected abstract void setPaId(String newId);

    /**
     * The getter method for the persistent attribute <code>createTime</code>.
     *
     * @return the value of createTime.
     */
    protected abstract Date getPaCreateTime();

    /**
     * The getter method for the persistent attribute
     * <code>processMgrName</code>.
     *
     * @return the value of processMgrName.
     * @see #setPaProcessMgrName
     */
    protected abstract String getPaProcessMgrName();

    /**
     * The setter method for the persistent attribute
     * <code>processMgrName</code>.
     *
     * @param newProcessMgrName the new value of processMgrName.
     * @see #getPaProcessMgrName
     */
    protected abstract void setPaProcessMgrName(String newProcessMgrName);

    /**
     * The getter method for the persistent attribute
     * <code>processMgrVersion</code>.
     *
     * @return the value of processMgrVersion.
     * @see #setPaProcessMgrVersion
     */
    protected abstract String getPaProcessMgrVersion();

    /**
     * The setter method for the persistent attribute
     * <code>processMgrVersion</code>.
     *
     * @param newProcessMgrVersion the new value of processMgrVersion.
     * @see #getPaProcessMgrVersion
     */
    protected abstract void setPaProcessMgrVersion(String newProcessMgrVersion);

    /**
     * The getter method for the persistent attribute <code>processDef</code>.
     *
     * @return the value of processDef.
     * @see #setPaProcessDef
     */
    protected abstract ProcessDefinition getPaProcessDef();

    /**
     * The setter method for the persistent attribute <code>processDef</code>.
     *
     * @param newProcessDef the new value of processDef.
     * @see #getPaProcessDef
     */
    protected abstract void setPaProcessDef(ProcessDefinition newProcessDef);

    /**
     * The getter method for the persistent attribute <code>processData</code>.
     *
     * @return the value of processData.
     */
    protected abstract ProcessData getPaProcessData();

    /**
     * The getter method for the persistent attribute <code>priority</code>.
     *
     * @return the value of priority.
     * @see #setPriority
     */
    protected abstract Priority getPaPriority();

    /**
     * The setter method for the persistent attribute <code>priority</code>.
     *
     * @param newPriority the new value of priority.
     * @see #getPaPriority
     */
    protected abstract void setPaPriority(Priority newPriority);

    /**
     * The getter method for the persistent attribute
     * <code>blockDeadlines</code>.
     *
     * @return the value of blockDeadlines.
     */
    protected abstract Map getPaBlockDeadlines();

    /**
     * Returns the transition manager.
     * @return the transition manager
     */
    protected TransitionManager transitionManager() {
  if (transitionMgrCache == null) {
      // create new transition manager
      transitionMgrCache = new TransitionManager(this);
  }
  return transitionMgrCache;
    }

    /**
     * Initializes the class, i.e. resets all attributes to default
     * values. Note that
     * {@link #refresh <code>refresh</code>} will be called subsequently.
     *
     * @param procDef the process definition.
     * @see #dispose
     */
    protected void init (ProcessDefinition procDef) {
  super.init();
  jsScopeCache = null;
  jellyContextCache = null;
  setPaProcessDef (procDef);
  setPaTypedState (NotRunningState.NOT_STARTED);
  setPaProcessMgrName (procDef.mgrName());
  setPaProcessMgrVersion (procDef.version());
  setPaDescription (procDef.processHeader().description());
  setPaAuditEventSelection(procDef.auditEventSelection());
  setPaStoreAuditEvents(procDef.storeAuditEvents());
  baRepresentations.clear ();

  setPaPriority(Priority.NORMAL);
  HandlerStack hs = new HandlerStack (new SAXInitializer ());
  hs.setContextData ("packageId", procDef.packageId());
  try {
      procDef.toSAX().emit(hs.contentHandler());
  } catch (SAXException e) {
      logger.error (e.getMessage (), e);
      throw new IllegalArgumentException (e.getMessage ());
  }

  // fire an appropriate audit event
  if (getPaAuditEventSelection()
      == ProcessDefinition.AUDIT_SELECTION_ALL_EVENTS) {
      fireAuditEvent
    (new DefaultCreateProcessAuditEvent
     (auditEventBase(WfAuditEvent.PROCESS_CREATED),
      activityRequesterInfo(getPaRequester())));
      fireAuditEvent
    (new DefaultDataAuditEvent
     (auditEventBase(WfAuditEvent.PROCESS_CONTEXT_CHANGED),
      null, getPaProcessData()));
  }
    }

    /**
     * Called after change of persistent attributes. May be used to
     * synchronise state derived from persistent attributes with
     * the new values.
     *
     * @see #init
     */
    protected void refresh () {
  super.refresh ();
  transitionMgrCache = null;
  jsScopeCache = null;
  jellyContextCache = null;
  participants = null;
  if (initedBeforeRefresh) {
      initedBeforeRefresh = false;
  } else {
      baRepresentations.clear ();
  }
    }

    /**
     * Releases all allocated resources. The object will be in an
     * unusable state until resources are reallocated by calling
     * {@link #init <code>init</code>} and
     * {@link #refresh <code>refresh</code>}.
     */
    protected void dispose () {
  super.dispose ();
  participants = null;
  // deallocate TransitionManager
  transitionMgrCache = null;
  baRepresentations.clear ();
    }

    //
    // Domain methods
    //

    /**
     * Indicates if some other object is equal to this one. <P>
     *
     * Note that <code>obj</code> may be a stub representing the
     * object. Stubs do not in general implement
     * <code>equals</code> correctly, so while
     * <code>this.equals(obj)</code> does indicate the equality as
     * defined for this class, <code>obj.equals(this)</code> generally
     * does not.
     *
     * @param obj the object to compare with.
     * @return <code>true</code> if the other object is equal.
     */
    public boolean equals (Object obj) {
        return (obj instanceof AbstractProcess)
            && getPaKey().equals (((AbstractProcess)obj).key())
            || (obj instanceof WfProcessLocal)
            && getPaKey().equals (((WfProcessLocal)obj).key());
    }

    /**
     * Provide a new unique activity key.
     *
     * @return the key.
     */
    protected abstract Long createActivityKey ();

    /**
     * Returns the requester for this process. Method of the omg interface.
     * @return requester for this process.
     */
    public WfRequester requester() {
  WfRequester req = getPaRequester();
        if (req instanceof SubProcRequester) {
            try {
                return lookupActivityLocal
                    (((SubProcRequester)req).requestingActivity()).toActivity();
            } catch (InvalidKeyException e) {
                return null;
            }
        }
        return req;
    }

    /**
     * Returns the requester for this process without resolving the
     * internally used SubProcRequester to the associated WfActivity.
     * @return requester for this process.
     */
    public WfRequester unresolvedRequester() {
        return getPaRequester();
    }

    /**
     * Return the process this process is a subflow of.
     * @return the process or <code>null</code> if this process is not
     * a subflow
     */
    public Process requestingProcess () {
  if (!(getPaRequester() instanceof SubProcRequester)) {
      return null;
  }
  String key = ((SubProcRequester)getPaRequester()).requestingActivity();
  try {
      ActivityLocal act = lookupActivityLocal (key);
      return ((ExtProcessLocal)act.containerLocal()).toProcess();
  } catch (InvalidKeyException e) {
      logger.warn (toString() + " cannot find requesting activity "
       + key + " : " + e.getMessage ());
      return null;
  }
    }

    /**
     * Returns the creation time of the process.
     * @return the creation time.
     */
    public Date createTime () {
  return getPaCreateTime();
    }

    /**
     * For the first iteration throws an
     * CannotChangeRequesterException. Method of the omg interface.
     * @param requester the requester.
     * @throws CannotChangeRequesterException if modification is not allowed.
     * @ejb.interface-method view-type="remote"
     */
    public void setRequester(WfRequester requester)
  throws CannotChangeRequesterException {
  throw new CannotChangeRequesterException();
    }

    /**
     * Return the context of this <code>WfExecutionObject</code>.
     * The process data object returned is a copy of the name value
     * association, the values are not copied, however.
     *
     * @return the process relevant data that define the context of the
     * process.
     */
    public ProcessData processContext () {
  return new DefaultProcessData (getPaProcessData());
    }

    /**
     * Filter out duplicate endDocument events.
     */
    private class DupEndFilter extends XMLFilterImpl {
  private boolean gotEnd = false;
  public DupEndFilter (ContentHandler parent) {
      setContentHandler (parent);
  }

  public void endDocument() throws SAXException {
      if (!gotEnd) {
    super.endDocument ();
    gotEnd = true;
      }
  }
    }

    /**
     * Updates process context of the execution object.
     * @param newValues the name-value pairs to be set.
     * @throws InvalidDataException If a name or value type does not match
     * the signature of this process.
     * @throws UpdateNotAllowedException If the update is not allowed.
     */
    public void setProcessContext (ProcessData newValues)
        throws InvalidDataException, UpdateNotAllowedException {
  if (!workflowState().equals (State.OPEN)) {
      throw new UpdateNotAllowedException
    ("Process is not in state open.");
  }
  // verify first to avoid partial change.
  ProcessDataInfo sig = getPaProcessDef().contextSignature ();
  for (Iterator i = newValues.keySet().iterator(); i.hasNext();) {
      String name = (String)i.next();
      Object type = sig.get (name);
      if (type == null) {
    throw new InvalidDataException ("No such data field: " + name);
      }
      Object v = newValues.get(name);
      if (v == null) {
    continue;
      }
            if ((type instanceof ExternalReference)
                && XPDLUtil.isJavaType((ExternalReference)type)) {
                Class vc = null;
                try {
                    vc = XPDLUtil.getJavaType((ExternalReference)type);
                } catch (ClassNotFoundException e) {
                    throw (InvalidDataException)
                        (new InvalidDataException
                         ("Required Java class no longer available: "
                          + e.getMessage())).initCause(e);
                }
                if (vc.isAssignableFrom(v.getClass())) {
                    continue;
                } else {
                    throw new InvalidDataException
                        ("Context entry " + name + " is "
                         + v.getClass().getName() + " must be instance of "
                         + vc.getName());
                }
            }
      if ((type instanceof SAXEventBuffer)
    || (type instanceof ExternalReference)
    || type.equals(org.w3c.dom.Element.class)) {
    boolean elemList = false;
    if (v instanceof List) {
        if (((List)v).size() == 0) {
      elemList = true;
        } else {
      Iterator ti = ((List)v).iterator ();
      if (ti.next() instanceof Element) {
          elemList = true;
      }
        }
    }
    if (! ((v instanceof Element)
           || (v instanceof Document)
           || elemList
           || (v instanceof org.w3c.dom.Element)
           || (v instanceof org.w3c.dom.DocumentFragment)
           || (v instanceof org.w3c.dom.Document)
           || (v instanceof SAXEventBuffer))) {
        throw new InvalidDataException
      ("Not a usable XML representation: " + name);
    }
    continue;
      }
      if (type instanceof Class) {
    Class vc = v.getClass();
    if (v instanceof Float) {
        vc = Double.class;
    } else if (v instanceof Integer) {
        vc = Long.class;
    }
    if (type == Participant.class) {
        type = String.class;
    }
    if (! ((Class)type).isAssignableFrom (vc)) {
        throw new InvalidDataException
      ("Values for data field \"" + name
       + "\" must be of type " + ((Class)type).getName ()
       + " (is " + v.getClass().getName () + ")");
    }
    continue;
      }
      throw new InvalidDataException
    ("Invalid type for data field \"" + name
     + "\": " + ((Class)type).getName ());
  }
  // now do changes
  DOMBuilder builder = null;
  ProcessData oldValues = new DefaultProcessData();
  for (Iterator i = (new ArrayList (newValues.keySet())).iterator();
       i.hasNext();) {
      String name = (String)i.next();
      oldValues.put(name, getPaProcessData().get(name));
      Object v = newValues.get(name);
            if (logger.isDebugEnabled ()) {
                logger.debug ("Setting context data item " + name + " = " + v);
            }
            Object type = sig.get (name);
            if ((type instanceof ExternalReference)
                && XPDLUtil.isJavaType((ExternalReference)type)) {
                // accept literally
            } else if (v instanceof Float) {
    v = new Double (((Float)v).doubleValue ());
    newValues.put (name, v);
      } else if (v instanceof Integer) {
    v = new Long (((Integer)v).longValue ());
    newValues.put (name, v);
      } else if ((v instanceof Element)
           || (v instanceof Document)
           || (v instanceof List)) {
    try {
        if (logger.isDebugEnabled ()) {
      logger.debug
          ("Convering item " + name + " from JDOM to SAX");
        }
        SAXOutputter outputter = new SAXOutputter();
        SAXEventBufferImpl b = new SAXEventBufferImpl ();
        outputter.setContentHandler(b);
        outputter.setLexicalHandler(b);
        if (v instanceof Document) {
      outputter.output ((Document)v);
        } else {
      List l;
      if (v instanceof List) {
          l = (List)v;
      } else {
          l = new ArrayList ();
          l.add (v);
      }
      outputter.output (l);
        }
        b.pack();
        v = b;
        newValues.put (name, v);
    } catch (JDOMException e) {
        logger.error (e.getMessage (), e);
        throw new InvalidDataException (e.getMessage ());
    }
      } else if ((v instanceof org.w3c.dom.Element)
           || (v instanceof org.w3c.dom.DocumentFragment)
           || (v instanceof org.w3c.dom.Document)) {
    try {
        if (logger.isDebugEnabled ()) {
      logger.debug
          ("Convering item " + name + " from W3C DOM to SAX");
        }
        TransformerFactory tf = TransformerFactory.newInstance();
        Transformer t = tf.newTransformer ();
        SAXEventBufferImpl b = new SAXEventBufferImpl ();
        // There seems to be a bug in Xalan that causes it to
        // fire two endDocument events when transforming a
        // DocumentFragment, filter it out.
        t.transform (new DOMSource ((org.w3c.dom.Node)v),
         new SAXResult(new DupEndFilter(b)));
        b.pack();
        v = b;
        newValues.put (name, v);
    } catch (TransformerConfigurationException e) {
        String s = "Error converting DOM to SAX: "+e.getMessage ();
        logger.error (s, e);
        throw new IllegalStateException (s);
    } catch (TransformerException e) {
        String s = "Error converting DOM to SAX: "+e.getMessage ();
        logger.error (s, e);
        throw new InvalidDataException (s);
    }
      }
      getPaProcessData().put(name, v);
  }

  // fire appropriate audit event
  if (getPaAuditEventSelection()
      == ProcessDefinition.AUDIT_SELECTION_ALL_EVENTS) {
      fireAuditEvent
    (new DefaultDataAuditEvent
     (auditEventBase(WfAuditEvent.PROCESS_CONTEXT_CHANGED),
      oldValues, newValues));
  }
    }

    /**
     * The <code>result</code> method returns the process context.
     *
     * @return the result.
     * @exception ResultNotAvailableException not thrown, intermediate results
     * are available.
     * @ejb.interface-method view-type="remote"
     */
    public ProcessData result ()
        throws ResultNotAvailableException {
        ProcessDataInfo resSig = processDefinition().resultSignature();
        ProcessData procCtx = getPaProcessData();
        ProcessData resData = new DefaultProcessData();
        for (Iterator i = resSig.keySet().iterator(); i.hasNext();) {
            String key = (String)i.next();
            resData.put(key, procCtx.get(key));
        }
  return resData;
    }

    /**
     * Returns a collection of activities of the process. This can usually
     * be handled more efficiently by the derived class that provides the
     * persistence. The default implementation simply wraps each local
     * objects.
     *
     * @return collection of activities of the process
     */
    public Collection steps() {
        Collection res = new ArrayList ();
        for (Iterator i = stepsLocal().iterator(); i.hasNext();) {
            ExtActivityLocal act = (ExtActivityLocal)i.next();
            res.add (act.toActivity());
        }
        return res;
    }

    /**
     * Returns all WfActivityLocals associated with this <code>WfProcess</code>.
     * @return the collection of all the WfActivities.
     */
    public abstract Collection stepsLocal();

    /**
     * Returns the {@link ActivityLocal <code>Activity</code>} with the
     * given key.
     * @param key the
     * {@link de.danet.an.workflow.localcoreapi.WfActivityLocal#key key}
     * of the activity
     * @return the activity associated with the key
     * @throws InvalidKeyException if no activity with the given key
     * exists
     */
    public abstract ActivityLocal activityByKeyLocal (String key)
        throws InvalidKeyException;

    /**
     * Returns all transitions associated with this <code>WfProcess</code>.
     * @return the collection of all the WfActivities.
     */
    public abstract List transitionsLocal();

    /**
     * Return all <code>WfActivity</code> objects that are in a certain state.
     * @param state the given state.
     * @return the collection of all WfActivities.
     * @throws InvalidStateException if an invalid state has been specified.
     */
    public Collection activitiesInState (String state)
  throws InvalidStateException {
  State s = State.fromString (state);
  Collection returnList = new ArrayList();

  Iterator it = stepsLocal().iterator();
  while (it.hasNext()) {
      ExtActivityLocal act = (ExtActivityLocal)it.next();
      if (act.typedState().isSameOrSubState (s)) {
    returnList.add(act.toActivity());
      }
  }
  return returnList;
    }

    /**
     * Return all <code>WfActivity</code> objects that belong to the
     * given block activity.
     * @param blockActivity the given block activity
     * @return the collection of all WfActivities
     */
    public Collection activitiesOfBlockActivity (String blockActivity) {
  Collection returnList = new ArrayList();

  Iterator it = stepsLocal().iterator();
  while (it.hasNext()) {
      ActivityLocal act = (ActivityLocal)it.next();
      String ba = act.blockActivity();
      if (ba != null && ba.equals (blockActivity)) {
    returnList.add(act);
      }
  }
  return returnList;
    }

    /**
     * Returns the associated process defintion.
     * @return the process definition
     */
    public ProcessDefinition processDefinition () {
  return getPaProcessDef ();
    }

    /**
     * Returns the cleanup mode for the process as defined in its process
     * definition.
     * @return the cleanup mode.
     */
    protected int cleanupMode () {
        return processDefinition().cleanupMode();
    }

    /**
     * Return the remote version of this object. This may be, but need
     * not be <code>this</code>, depending on the way remote objects
     * are implemented.
     *
     * @return the client side object.
     */
    public abstract Process toProcess ();

    /**
     * Return the version of this object to be passed to as local reference.
     *
     * @return the client side object.
     */
    public abstract ExtProcessLocal toProcessLocal ();

    /**
     * Helper for implementing access to process data in JavaScript.
     */
    public class DataAccessor implements Serializable {
  private String itemName;
        private Scriptable scope;

  /**
   * Create a new <code>DataAccessor</code> for the given item.
   * @param item the process data item to access.
   */
  public DataAccessor (Scriptable scope, String item) {
            this.scope = scope;
      itemName = item;
  }

  /**
   * Return the the value of the item this object was constructed for.
   * @param so needed to match rhino's interface requirements, ignored.
   * @return the value.
   */
  public Object getValue(ScriptableObject so) {     
      Object res = getPaProcessData().get (itemName);
      if (res instanceof Date) {
    try {
        res = Context.getCurrentContext().newObject
      (so, "Date", new Object[]
          { new Long(((Date)res).getTime()) });
    } catch (JavaScriptException e) {
        logger.error (e.getMessage (), e);
    }
                return res;
      }
      if (res instanceof SAXEventBuffer) {
          try {
              XmlSaxHandler sh = XmlObject.Factory.newXmlSaxHandler();
              ((SAXEventBuffer)res).emit(sh.getContentHandler());
              XmlObject xo = sh.getObject();
              XmlCursor cur = xo.newCursor();
              while (!cur.isStart()) {
                  if (cur.isEnddoc()) {
                            try {
                                Context cx = Context.enter();
                                return cx.evaluateString
                                    (scope, "<></>", "<script>", 1, null);
                            } finally {
                                Context.exit ();
                            }
                  }
                  cur.toNextToken();
              }
              xo = cur.getObject();
              Object wxo = Context.javaToJS (xo, scope);
              res =  Context.getCurrentContext().newObject
                        (scope, "XML", new Object[] { wxo });
          } catch (SAXException e) {
                    logger.error (e.getMessage (), e);
          } catch (XmlException e) {
                    logger.error (e.getMessage (), e);
          }
                return res;
      }
      return res;
  }
    }

    /**
     * Return a scope for evaluating JavaScript. The scope includes
     * the process relevant data as top-level variables. This scope
     * may be used as prototype for a thread specific evaluation
     * context only. This implies that it can not be used to modify
     * process data. This behaviour is intended.
     * @return the process specific sope.
     */
    public Scriptable jsScope () {
  if (jsScopeCache == null) {
      try {
    jsScopeCache = (ScriptableObject)
        (new Context()).newObject (GLOBAL_JS_SCOPE);
    jsScopeCache.setPrototype (GLOBAL_JS_SCOPE);
    jsScopeCache.setParentScope (null);
    Method getter = DataAccessor.class.getMethod
        ("getValue", new Class[] { ScriptableObject.class });
    Map procData = getPaProcessData ();
    for (Iterator i = procData.keySet().iterator(); i.hasNext ();) {
        String key = (String)i.next();
        DataAccessor da = new DataAccessor (jsScopeCache, key);
        try {
      jsScopeCache.defineProperty (key, da, getter, null, 0);
        } catch (JavaScriptException e) {
      logger.error (e.getMessage (), e);
        }
    }
    jsScopeCache.sealObject ();
      } catch (NoSuchMethodException e) {
    logger.error (e.getMessage (), e);
      } catch (JavaScriptException e) {
    logger.error (e.getMessage (), e);
      }
  }
  return jsScopeCache;
    }

    /**
     * Evaluate the given script using the process specific scope as
     * returned by {@link #jsScope <code>jsScope</code>}. <P>
     *
     * results of type JavaScript Date are converted to Java Date.
     *
     * @param script the script
     * @return the result
     * @throws ScriptException if evaluation fails
     */
    public Serializable evalScript (String script) throws ScriptException {
  Context cx = Context.enter();
  try {
      Scriptable proto = jsScope();
      Scriptable scope = cx.newObject (proto);
      scope.setPrototype (proto);
      scope.setParentScope (null);
   
      Object res = cx.evaluateString (scope, script, "<script>", 1, null);
      if (res instanceof Wrapper) {
    res = ((Wrapper)res).unwrap ();
            } else if (res instanceof XMLObject) {
                SAXEventBufferImpl seb = new SAXEventBufferImpl();
                if (((XMLObject)res).getClassName().equals("XMLList")) {
                    seb.startDocument();
                    for (int i = 0; true; i++) {
                        Object item = ((XMLObject)res).get(i, (XMLObject)res);
                        if (item.equals(Scriptable.NOT_FOUND)) {
                            break;
                        }
                        xmlObjectToSax(seb, (XMLObject)item, true);
                    }
                    seb.endDocument();
                } else {
                    xmlObjectToSax(seb, (XMLObject)res, false);
                }
                seb.pack();
                return seb;
      } else if ((res instanceof Scriptable)
    && ((Scriptable)res).getClassName().equals ("Date")) {
    Scriptable s = (Scriptable)res;
    Object gt = Scriptable.NOT_FOUND;
    for (Scriptable c = s;
         c != null && gt.equals(Scriptable.NOT_FOUND);
         c = c.getPrototype()) {
        gt = c.get("getTime", s);
    }
    Number millis = (Number)((Function)gt).call
        (cx, scope, s, new Object[] {});
    return new Date (millis.longValue());
      }
      return (Serializable)res;
  } catch (JavaScriptException e) {
      logger.error (e.getMessage (), e);
      throw new ScriptException
    (e.getMessage (),
     (e.getValue() instanceof Throwable)
     ? (Throwable)e.getValue() : e);
        } catch (SAXException e) {
            logger.error (e.getMessage (), e);
            throw new ScriptException (e.getMessage (), e);
  } finally {
      Context.exit();
  }
    }

    /**
     * Serialize a JavaScript XML object into the SAX event buffer.
     *
     * @param seb the SAX event buffer
     * @param xmlObj the XML object
     * @param fragment if <code>startDocument</code> and
     * <code>endDocument</code> events are to be suppressed
     * @throws SAXException
     */
    private void xmlObjectToSax
        (SAXEventBufferImpl seb, XMLObject xmlObj, boolean fragment)
        throws SAXException {
        Wrapper wrap = (Wrapper) ScriptableObject.callMethod
            ((XMLObject)xmlObj, "getXmlObject", new Object[0]);
        XmlObject result = (XmlObject) wrap.unwrap();
        XmlCursor cursor = result.newCursor();
        result = cursor.getObject();
        ContentHandler ch = seb;
        if (fragment) {
            ch = new BodyFilter (ch);
        }
        result.save(ch, seb, (new XmlOptions()).setSaveOuter());
    }

    /**
     * Evaluate the given expressions using the process specific data
     * (the scope as returned by {@link #jsScope
     * <code>jsScope</code>} for JavaScript).
     * @param expressionData an array of object arrays containing the result
     * type and the expression
     * @return an array that contains the result of each evaluation.
     * Note that the result may be an exception.
     */
    public Serializable[] evalExpressions (Object[][] expressionData) {
  Serializable[] res = new Serializable[expressionData.length];
  for (int i = 0; i < expressionData.length; i++) {
      try {
    Object[] item = expressionData[i];
    Object resType = item[0];
    String expr = (String)item[1];
    if (XPDLUtil.isXMLType (resType)
        && expr.length() > 0 && expr.startsWith("<j:jelly")) {
        res[i] = evalXML (expr);
    } else {
        res[i] = evalScript (expr);
    }
    if (resType.equals(Long.class) && (res[i] instanceof Double)) {
        res[i] = new Long(((Double)res[i]).longValue());
    }
      } catch (Exception e) {
    res[i] = e;
      }
  }
  return res;
    }

    private Serializable evalXML (String xml)
  throws SAXException, IOException {
  try {
      SAXParserFactory spf = SAXParserFactory.newInstance ();
      spf.setValidating (false);
      spf.setNamespaceAware (true);
      spf.setFeature
    ("http://xml.org/sax/features/namespace-prefixes", true);
      XMLReader xr = null;
      try {
    spf.setFeature
        ("http://xml.org/sax/features/xmlns-uris", true);
    xr = spf.newSAXParser().getXMLReader();
      } catch (SAXException e) {
    xr = new XmlnsUrisPatcher
        (spf.newSAXParser().getXMLReader());
      }
      SAXEventBufferImpl seb = new SAXEventBufferImpl ();
      XMLFilterImpl filter = new XMLFilterImpl () {
        private int level = 0;
        public void startElement
      (String uri, String localName, String qName,
       Attributes atts) throws SAXException {
      if (level > 0) {
          super.startElement (uri,localName,qName,atts);
      }
      level += 1;
        }
        public void endElement
      (String uri, String localName, String qName)
      throws SAXException{
      level -= 1;
      if (level > 0) {
          super.endElement (uri, localName, qName);
      }
        }
    };
      filter.setContentHandler (seb);
      xr.setContentHandler (filter);
      xr.parse (new InputSource
          (new StringReader
           ("<temporary-root>" + xml + "</temporary-root>")));
      seb.pack();
      XMLStreamReader rdr = seb.createXMLStreamReader ();
      while (rdr.next () != XMLStreamReader.START_ELEMENT) {
      }
      if (rdr.getNamespaceURI().equals ("jelly:core")
    && rdr.getLocalName().equals ("jelly")) {
    return evalJelly (seb);
      }
      return seb;
  } catch (ParserConfigurationException e) {
      throw new IllegalArgumentException
    ("Error initiliazing schema type: " + e.getMessage ());
  } catch (SAXException e) {
      throw new IllegalArgumentException
    ("Error initiliazing schema type: " + e.getMessage ());
  } catch (XMLStreamException e) {
      throw new IllegalArgumentException
    ("Error initiliazing schema type: " + e.getMessage ());
  } catch (IOException e) {
      throw new IllegalArgumentException
    ("Error initiliazing schema type: " + e.getMessage ());
  }
    }

    private class ExtXMLParser extends XMLParser {
  public void configure () {
      super.configure ();
  }
    }

    private JellyContext jellyContext() {
  if (jellyContextCache == null) {
      jellyContextCache = new JellyContext() {
        private Map xmlVals = new HashMap ();
        public Object getVariable(String name) {
      if (getPaProcessData().containsKey (name)) {
          Object v = getPaProcessData().get (name);
          if (v instanceof SAXEventBuffer) {
        if (!xmlVals.containsKey (name)) {
            SAXContentHandler hdlr
          = new SAXContentHandler ();
            try {
          ((SAXEventBuffer)v).emit (hdlr);
            } catch (SAXException e) {
          throw (IllegalArgumentException)
              (new IllegalArgumentException
               ("Error converting SAX events: "
                + e.getMessage ())).initCause(e);
            }
            xmlVals.put (name, hdlr.getDocument());
        }
        return xmlVals.get (name);
          }
          return v;
      } else {
          return super.getVariable (name);
      }
        }
    };
  }
  return jellyContextCache;
    }

    private SAXEventBufferImpl evalJelly (SAXEventBufferImpl seb)
  throws SAXException {
  if (logger.isDebugEnabled ()) {
      logger.debug ("Evaluating jelly script");
  }
  try {
      JellyContext context = new JellyContext (jellyContext ());
      ExtXMLParser jellyParser = new ExtXMLParser ();
      jellyParser.setContext(context);
      jellyParser.configure ();
      seb.emit (new NamespaceAttributesFilter(jellyParser));
      Script script = jellyParser.getScript ();
      script.compile ();
      SAXEventBufferImpl jres = new SAXEventBufferImpl ();
      jres.startDocument ();
      script.run (context, new XMLOutput(jres));
      jres.endDocument ();
      jres.pack ();
      return jres;
  } catch (JellyException e) {
      throw (IllegalArgumentException)(new IllegalArgumentException
       ("Error evaluating jelly script: " + e.getMessage ()))
    .initCause (e);
  }
    }

    /* Comment copied from Interface. */
    public void closeActivity (ExtActivityLocal activity, State closedState) {
  // initialize transition manager before changing activity state!
  TransitionManager tm = transitionManager();
  activity.doCloseActivity (closedState);
  tm.update (activity);
  // everything else is done when the activity's close event is
  // received, because we want to do it in another transaction
    }

    /* Comment copied from Interface. */
    public boolean choose (ExtActivityLocal activity)
  throws TransitionNotAllowedException {
  State state = activity.typedState ();
  if (state.isSameOrSubState (NotRunningState.NOT_STARTED)) {
      // this activity has been reset by some other activity
      return false;
  }
  if (!state.isSameOrSubState (OpenState.RUNNING)
      && !state.isSameOrSubState (NotRunningState.SUSPENDED)) {
      throw new TransitionNotAllowedException
    ("Cannot choose activity in state " + state + " must be "
     + OpenState.RUNNING + " or " + NotRunningState.SUSPENDED);
  }
  if (!activity.preliminarilyChosen ()) {
      return true;
  }
        transitionManager().resetCompeting (activity);
  return true;
    }

    //
    // State change API implementation
    //

    private static Map vstates = null;

    /**
     * Returns the {@link java.util.Map <code>Map</code>} that maps
     * the process states to a {@link java.util.Map <code>List</code>}
     * of reachable process states. <P>
     *
     * The map returns the state transitions allowed as parameters of
     * {@link
     * de.danet.an.workflow.localcoreapi.WfExecutionObjectLocal#changeState
     * <code>changeState</code>} only. I.e. the map does not reflect
     * all possible transitions, there may be more, but those are only
     * accessible to the workflow engine itself.
     * @return the resulting map.
     */
    protected Map getStateTransitionMap () {
  // Map must be initialized lazily. Static initialization holds
  // danger of "race conditions" with constant initialization
  // (resulting in null-entries). And new map must be fully initialized
        // before assigning to member variable to avoid concurrent
        // initialization.
  if (vstates == null) {
      Map transMap = new HashMap();
      // Transitions from open.not_running.not_started
      List l = new ArrayList ();
      transMap.put(NotRunningState.NOT_STARTED, l);
      l.add (ClosedState.TERMINATED);
      l.add (OpenState.RUNNING);
      // Transitions from open.running
      l = new ArrayList ();
      transMap.put(RunningState.RUNNING, l);
      l.add (NotRunningState.SUSPENDED);
      l.add (ClosedState.TERMINATED);
      // Transitions from open.not_running.suspended
      l = new ArrayList ();
      transMap.put(SuspendedState.SUSPENDED, l);
      l.add (OpenState.RUNNING);
      l.add (ClosedState.ABORTED);
      vstates = Collections.unmodifiableMap(transMap);
  }
  return vstates;
    }

    /**
     * Adds the possibility to change the state to
     * <code>OpenState.RUNNING</code> (i.e. "<code>start()</code>") to
     * the <code>changeState</code> method of the superclass.
     *
     * @param newState the new state.
     * @throws InvalidStateException If <code>newState</code> is an invalid
     * state for the execution object.
     * @throws TransitionNotAllowedException If the transition from the current
     * state to <code>newState</code> is not allowed.
      */
    public void changeState (State newState)
  throws InvalidStateException,
         TransitionNotAllowedException {
  try {
      if (typedState() == NotRunningState.NOT_STARTED
    && newState == OpenState.RUNNING) {
    start ();
    return;
      }
  } catch (CannotStartException e) {
      throw new TransitionNotAllowedException
    (e.getClass().getName() + ": " + e.getMessage());
  } catch (AlreadyRunningException e) {
      throw new TransitionNotAllowedException
    (e.getClass().getName() + ": " + e.getMessage());
  }
  super.changeState (newState);
    }

    /**
     * Starts a process.
     * @throws CannotStartException when the process cannot be started (e.g.,
     * because it is not properly initialized).
     * @throws AlreadyRunningException when the process has already been
     * started.
     * @ejb.interface-method view-type="remote"
     */
    public void start () throws CannotStartException,
        AlreadyRunningException {
  if (typedState() != NotRunningState.NOT_STARTED) {
      throw new AlreadyRunningException
    (toString() + " state is: " + state());
  }
  updateState (RunningState.RUNNING);
    }

    /**
     * Enable or disable debugging of the activity.
     * @param debug if the activity is to be debugged
     * @throws InvalidStateException if changing debug mode is not
     * allowed
     */
    public void setDebugEnabled (boolean debug) throws InvalidStateException {
  setPaDebug (debug);
  Iterator it = stepsLocal().iterator();
  while (it.hasNext()) {
      ExtActivityLocal act = (ExtActivityLocal)it.next();
      act.setDebugEnabled (true);
  }
  return;
    }
   
    /* Comment copied from Interface. */
    public void terminate () throws CannotStopException,
        NotRunningException {
  mayCloseCheck (ClosedState.TERMINATED);
  if (stepsLocal() != null) {
      for (Iterator it = stepsLocal().iterator(); it.hasNext();) {
    ActivityLocal act = (ActivityLocal)it.next();
    State s = act.typedState();
    if (s.isSameOrSubState(NotRunningState.SUSPENDED)
        || s == ClosedState.ABORTED) {
        throw new CannotStopException
      (act.toString() + " is suspended.");
    }
      }
      String unstoppable = null;
      try {
    setPaTypedState(RunningState.TERMINATING);
    for (Iterator it = stepsLocal().iterator(); it.hasNext();) {
        ActivityLocal act = (ActivityLocal)it.next();
        if (act.workflowState() == State.OPEN
      && (!act.typedState().isSameOrSubState
          (NotRunningState.NOT_STARTED))) {
      try {
          act.terminate();
      } catch (CannotStopException e) {
          unstoppable = act.toString()
        + " cannot be terminated: " + e.getMessage();
      } catch (NotRunningException e) {
          // cannot happen
          logger.error (e.getMessage(), e);
      }
        }
    }
      } finally {
    setPaTypedState(RunningState.RUNNING);
      }
      if (unstoppable != null) {
    throw new CannotStopException (unstoppable);
      }
  }
  updateState(ClosedState.TERMINATED);
    }

    /* Comment copied from Interface. */
    public void abort () throws CannotStopException,
        NotRunningException {
  mayCloseCheck (ClosedState.ABORTED);
  doAbort ();
    }

    private void doAbort ()
        throws CannotStopException {
  if (stepsLocal() != null) {
      String unstoppable = null;
      try {
    setPaTypedState(SuspendedState.ABORTING);
    for (Iterator it = stepsLocal().iterator(); it.hasNext();) {
        ActivityLocal act = (ActivityLocal)it.next();
        try {
      if (act.typedState().isSameOrSubState
          (NotRunningState.SUSPENDED)) {
          act.abort ();
      } else if (act.typedState().isSameOrSubState
           (OpenState.RUNNING)) {
          try {
        act.terminate();
          } catch (CannotStopException e) {
        act.suspend ();
        act.abort ();
          }
      }
        } catch (CannotStopException e) {
      unstoppable = act.toString()
          + " cannot be closed: " + e.getMessage();
        } catch (InvalidControlOperationException e) {
      // cannot happen, we check states before changing
      logger.error (e.getMessage(), e);
        }
    }
      } finally {
    setPaTypedState(SuspendedState.SUSPENDED);
      }
      if (unstoppable != null) {
    throw new CannotStopException (unstoppable);
      }
  }
  updateState(ClosedState.ABORTED);
    }

    private void mayCloseCheck (ClosedState s)
  throws CannotStopException, NotRunningException {
  if (!validTypedStates().contains (s)) {
      if (typedState().isSameOrSubState(OpenState.RUNNING)) {
    throw new CannotStopException(toString() + " is " + state());
      } else {
    throw new NotRunningException (toString() + " is " + state());
      }
  }
    }

    //
    // Timers
    //

    /**
     * Handle the timeout of a timer.
     * @param info the context.
     */
    public void handleTimeout (Serializable info) {
  TimeoutInfo toi = (TimeoutInfo)info;
  List dls = (List)getPaBlockDeadlines().get(toi.activity);
  Deadline dl = (Deadline)dls.get(toi.dlIndex);
  if (logger.isDebugEnabled ()) {
      logger.debug (toString () + " received timeout for " + dl);
  }
  // now accept deadline
  dl.setState(Deadline.STATE_REACHED);
  // notify persistence layer about change
  getPaBlockDeadlines().put
      (toi.activity, getPaBlockDeadlines().get(toi.activity));

  // now find not completed/not started activities and/or abandon
  Collection openActs = new ArrayList ();
  for (Iterator i = activitiesOfBlockActivity
     (toi.activity.toString ()).iterator (); i.hasNext ();) {
      ExtActivityLocal a = (ExtActivityLocal)i.next ();
      if (a.typedState().isSameOrSubState(OpenState.RUNNING)
    || a.typedState().isSameOrSubState(NotRunningState.SUSPENDED)) {
    openActs.add (a);
    // complete this if deadline is synchronous
    if (dl.getExecution() == Deadline.SYNCHR) {
        a.initiateAbandoning(true, dl.getExceptionName());
        transitionManager().update (a);
    }
      }
  }
  if (openActs.size() > 0) {
      BAForExceptionHandling ba = (BAForExceptionHandling)
    blockActivityRepresentation (toi.activity.toString());
      ba.update (dls, dl.getExecution() == Deadline.SYNCHR
           ? (State)ClosedCompletedState.ABANDONED
           : (State)RunningState.RUNNING,
           openActs);
      handleException (ba, dl.getExceptionName());
  }
    }

    //
    // State change handling
    //

    /**
     * Check if this class handles a specific event.
     * @param event the event to check
     * @return <code>true</code> if event is handled
     */
    public static boolean isHandled (WfAuditEvent event) {
  try {
      if (!(event instanceof WfStateAuditEvent)) {
    return false;
      }
      if (event.eventType().equals(WfAuditEvent.ACTIVITY_STATE_CHANGED)) {
    State newState = State.fromString
        (((WfStateAuditEvent)event).newState());
    if (newState.isSameOrSubState(State.CLOSED)) {
        return true;
    }
      } else if (event.eventType()
           .equals(WfAuditEvent.PROCESS_STATE_CHANGED)) {
    State oldState = State.fromString
        (((WfStateAuditEvent)event).oldState());
    State newState = State.fromString
        (((WfStateAuditEvent)event).newState());
    if (oldState.isSameOrSubState(NotRunningState.NOT_STARTED)) {
        if (newState == RunningState.RUNNING) {
      return true;
        }
    } else if (oldState.isSameOrSubState
         (NotRunningState.NOT_STARTED)) {
        if (newState == ClosedState.TERMINATED) {
      return true;
        }
    } else if (oldState.isSameOrSubState
         (NotRunningState.SUSPENDED)) {
        if (newState == RunningState.RUNNING) {
      return true;
        }
        if (newState == ClosedState.ABORTED) {
      return true;
        }
    } else if (oldState.isSameOrSubState(OpenState.RUNNING)) {
        if (newState == ClosedCompletedState.NORMAL) {
      return true;
        }
        if (newState == ClosedState.TERMINATED) {
      return true;
        }
    }
      }
  } catch (InvalidStateException e) {
      throw (IllegalArgumentException)
    (new IllegalArgumentException (e.getMessage ()))
    .initCause (e);
  }
  return false;
    }

    /**
     * Handles the given audit event.
     * @param event the event.
     * @ejb.interface-method view-type="remote"
     */
    public void handleAuditEvent (WfAuditEvent event) {
  if (event.activityKey() == null) {
      super.handleAuditEvent (event);
      return;
  }
  // One of our activities has closed (events are pre-filtered)
  State ps = typedState();
  if (ps.isSameOrSubState(RunningState.TERMINATING)
      || ps.isSameOrSubState(SuspendedState.ABORTING)) {
      return;
  }
  State closedState = null;
  try {
      closedState = State.fromString
    (((WfStateAuditEvent)event).newState());
  } catch (InvalidStateException e) {
      // actually, this is impossible
      logger.error (e.getMessage ());
      return;
  }
  if (closedState.isSameOrSubState (ClosedState.ABORTED)) {
      if (ps.isSameOrSubState(OpenState.RUNNING)) {
    updateInterim (SuspendedState.SUSPENDED);
      } else if ((! ps.isSameOrSubState (NotRunningState.SUSPENDED))) {
    return;
      }
      try {
    doAbort ();
      } catch (CannotStopException e) {
    logger.warn
        ("Cannot abort process: " + e.getMessage ());
      }
      return;
  }
  if (ps == RunningState.RUNNING) {
      continueProcessing ();
  }
    }

    /**
     * Handles a terminated audit event.
     * @param event the event.
     */
    protected void handleTerminatedEvent (WfStateAuditEvent event) {
  WfRequester req = unresolvedRequester();
  if ((req instanceof SubProcRequester)
      && (((SubProcRequester)req).execution()
    == SubFlowImplementation.SYNCHR)) {
      String key = ((SubProcRequester)req).requestingActivity();
      try {
    ActivityLocal act = lookupActivityLocal (key);
    if (act.typedState () != RunningState.ABANDONING
        && act.typedState() != ClosedCompletedState.ABANDONED) {
        act.terminate ();
    }
      } catch (InvalidKeyException e) {
    logger.warn (toString() + " cannot notify requesting activity "
           + key + " : " + e.getMessage ());
      } catch (InvalidControlOperationException e) {
    logger.warn(toString()+" cannot terminate requesting activity "
          + key + " : " + e.getMessage ());
      }
  }
  handleClosedEvent (event);
    }

    /**
     * Handles a aborting audit event.
     * @param event the event.
     */
    protected void handleAbortedEvent (WfStateAuditEvent event) {
  WfRequester req = unresolvedRequester();
  if ((req instanceof SubProcRequester)
      && (((SubProcRequester)req).execution()
    == SubFlowImplementation.SYNCHR)) {
      String key = ((SubProcRequester)req).requestingActivity();
      try {
    ExtActivityLocal act = lookupActivityLocal (key);
                act.abortRequester();
      } catch (InvalidKeyException e) {
    logger.warn (toString() + " cannot notify requesting activity "
           + key + " : " + e.getMessage ());
            }
  }
  handleClosedEvent (event);
    }

    /**
     * Handles a completed audit event.
     * @param event the event.
     */
    protected void handleCompletedEvent (WfStateAuditEvent event) {
  WfRequester req = unresolvedRequester();
  if ((req instanceof SubProcRequester)
      && (((SubProcRequester)req).execution()
    == SubFlowImplementation.SYNCHR)) {
      String key = ((SubProcRequester)req).requestingActivity();
      try {
    ActivityLocal act = lookupActivityLocal (key);
    if (act.typedState() != RunningState.ABANDONING
        && act.typedState() != ClosedCompletedState.ABANDONED) {
        ProcessData res = processContext();
        FormalParameter[] fps
      = processDefinition().formalParameters();
        ProcessData pd = new ProcessDataWithParams (fps);
        for (int i = 0; i < fps.length; i++) {
      FormalParameter fp = fps[i];
      if (fp.mode() == FormalParameter.Mode.IN) {
          continue;
      }
      String fpn = fps[i].id();
      pd.put (fpn, res.get (fpn));
        }
        act.setResult (pd);
        act.complete ();
    }
      } catch (InvalidKeyException e) {
    logger.warn (toString() + " cannot notify requesting activity "
           + key + " : " + e.getMessage ());
      } catch (InvalidDataException e) {
    logger.warn
        (toString() + " cannot set data on requesting activity "
         + key + " : " + e.getMessage ());
      } catch (InvalidControlOperationException e) {
    logger.warn (toString() + " cannot notify requesting activity "
           + key + " : " + e.getMessage ());
      }
  }
  handleClosedEvent (event);
    }

    /**
     * Called when the process is closed. Derived classes should
     * should override this method and notify clients waiting on
     * channels that the channel is closed and clean up any data
     * structures associated with channels.
     */
    protected void closeChannels () {
    }

    private void handleClosedEvent (WfStateAuditEvent event) {
  stopTimers ();
  closeChannels ();
        int cleanupMode = cleanupMode();
  if (cleanupMode == ProcessDefinition.REMOVE_AUTOMATIC
            || (cleanupMode == ProcessDefinition.REMOVE_COMPLETED
                && event.newState().startsWith("closed.completed"))) {
      try {
    removeThis ();
      } catch (CannotRemoveException e) {
    // cannot happen, we got this event because process
    // was closed, so it must be removable
    logger.error ("Closed process not removable (?): "
            + e.getMessage (), e);
      }
  }
    }

    /* Comment copied from interface. */
    public void handleException (ExtActivityLocal activity, String exceptionName) {
  transitionManager().update (activity, exceptionName);
  if (typedState() == RunningState.RUNNING) {
      continueProcessing ();
  }
    }

    /**
     * Remove this process. Since this involves the persistence layer,
     * this method must be provided by the derived class.
     * @throws CannotRemoveException if the process is still in progress
     */
    protected abstract void removeThis () throws CannotRemoveException;

    /**
     * Handles a started audit event.
     * @param event the event.
     */
    protected void handleStartedEvent (WfStateAuditEvent event) {
  // start all activities
  continueProcessing ();
    }

    /**
     * Handles a resumed audit event.
     * @param event the event.
     */
    protected void handleResumedEvent (WfStateAuditEvent event) {
  continueProcessing ();
    }

    private void continueProcessing () {
  // checkAllActivities
  TransitionManager tm = transitionManager();
  if (tm.isAtEnd()) {
      State resState = ClosedCompletedState.NORMAL;
      for (Iterator i = stepsLocal().iterator(); i.hasNext();) {
    State s = ((ActivityLocal)i.next()).typedState();
    if (s.isSameOrSubState (ClosedState.TERMINATED)) {
        resState = ClosedState.TERMINATED;
        break; // can't get worse, aborted activity
        // is handled separately.
    }
      }
      updateState(resState);
      return;
  }
  // start all runnableActivities
  Collection activitiesToStart = tm.startableActivities();
  for (Iterator it = activitiesToStart.iterator(); it.hasNext();) {
      ExtActivityLocal a = (ExtActivityLocal)it.next();
      try {
    String ba = a.blockActivity();
    if (ba != null && tm.isTransitionSource (ba)) {
        // We are about to start an activity that is part
        // of a block activity with exception
        // condition. Find out if it is the first activity
        // to be started in the set.
        boolean isFirst = true;
        for (Iterator i = activitiesOfBlockActivity(ba).iterator();
       i.hasNext ();) {
      ActivityLocal bm = (ActivityLocal)i.next ();
      if (!bm.typedState()
          .isSameOrSubState (NotRunningState.NOT_STARTED)) {
          isFirst = false;
          break;
      }
        }
        if (isFirst) {
      armDeadlines (ba);
        }
    }
    a.start();
      } catch (AlreadyRunningException e) {
    // can't do much about this, shouldn't happen
    logger.error (e.getMessage(), e);
      }
  }
    }

    private void armDeadlines (String ba) {
  if (logger.isDebugEnabled ()) {
      logger.debug ("Starting first activity of "
        + "block activity " + ba);
  }
  try {
      int dlIndex = 0;
      Date now = new Date();
      Long bak = new Long(ba);
      for (Iterator dli = ((List)getPaBlockDeadlines()
         .get(bak)).iterator(); dli.hasNext ();
     dlIndex += 1) {
    Deadline dl = (Deadline)dli.next ();
    if (dl.getState() != Deadline.STATE_INITIAL) {
        continue;
    }
    dl.arm (this, now, toProcessLocal(),
            new TimeoutInfo (bak, dlIndex));
      }
  } catch (NumberFormatException e) {
      // can't do much about this, shouldn't happen
      logger.error (e.getMessage(), e);
  }
    }

    /**
     * Returns an audit event object with process relevant information
     * initialized.
     * @param eventType event type
     * @return the process related information.
     */
    protected WfAuditEvent auditEventBase(String eventType) {
  return new DefaultAuditEvent
      (toProcess(), eventType, getPaKey(), getPaName(),
       getPaProcessMgrName(), getPaProcessMgrVersion(),
       getPaAuditEventSelection(), getPaStoreAuditEvents());
    }

    //
    // Helpers
    //

    /**
     * Return an arbitrary activity with the given key. The activity
     * need not belong to this process.
     * @param key activity key
     * @return the activity
     * @throws InvalidKeyException if the activity cannot be found
     */
    protected abstract ExtActivityLocal lookupActivityLocal (String key)
  throws InvalidKeyException;

    /**
     * Retrieve the base event information about a requesting activity.
     * @param req the requester.
     * @return the base info using an audit event as DTO.
     */
    protected abstract WfAuditEvent activityRequesterInfo(WfRequester req);

    /**
     * Return a runtime representation of a block activity. Block
     * activities are not persisted. However, the persistence layer
     * needs a representation to assign a "from"-activity to exception
     * transitions starting at block activities. This representation
     * is subsequently used by the domain layer only and therefore
     * provided by the domain layer.
     * @param key the block activity key
     * @return the block activity representation
     */
    protected ActivityLocal blockActivityRepresentation (String key) {
  ActivityLocal act = (ActivityLocal)baRepresentations.get (key);
  if (act == null) {
      act = new BAForExceptionHandling (key);
      baRepresentations.put (key, act);
  }
  return act;
    }

    /**
     * Return string representation for debugging purposes.
     * @return a string representation.
     */
    public String toString() {
  return "Process[key=" + getPaKey () + "]";
    }

    //
    // Process instantiation
    //

    /**
     * Helper class for retrieving the initialization information from
     * the process definition.
     */
    public class SAXInitializer extends StackedHandler {

  private Map actIdMap = null;
  private Map trefsMap = null;
  private Map actSetDefs = null;

  private String actSetId = null;
  private SAXEventBufferImpl actSetDef = null;

  private ProcessData procDataMap = getPaProcessData();

  private String extAttrName = null;
  private String extAttrValue = null;

  /**
   * Create a new SAX initializer with the given parameters.
   */
  public SAXInitializer () {
      actIdMap = new HashMap();
      trefsMap = new HashMap();
      actSetDefs = new HashMap ();
  }

  /**
   * 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 ("WorkflowProcess")) {
    setPaName(a.getValue("Name"));   
    setPaId(a.getValue("Id"));
      } else if (loc.equals ("Applications")) {
    getStack().push (new DefaultHandler ());
      } else if (loc.equals ("DataField")
           || loc.equals ("FormalParameter")) {
    setContextData ("ProcDataId", a.getValue ("Id"));
    setContextData ("InitialValue", null);
      } else if (loc.equals ("DataType")) {
    getStack().push (new XPDLUtil.SAXDataTypeHandler ());
      } else if (loc.equals ("Activity")) {
    getStack().push
        (new ActivityInitializer
         (actIdMap, trefsMap, actSetDefs, null));
      } else if (loc.equals ("Transition")) {
    getStack().push
        (new TransitionInitializer (actIdMap, trefsMap, null));
      } else if (loc.equals ("ActivitySet")) {
    actSetId = a.getValue ("Id");
    actSetDef = new SAXEventBufferImpl ();
    getStack().push (actSetDef);
      } else if (loc.equals ("ExtendedAttribute")
           && getStack().getRelativeDepth() == 4) {
    extAttrName = a.getValue("Name");
    extAttrValue = 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 (loc.equals ("Priority")) {
    try {
        setPaPriority(Priority.fromInt
          (Integer.parseInt(text().trim())));
    } catch (InvalidPriorityException e) {
        logger.error (e.getMessage (), e);
        throw new SAXException (e);
    }
      } else if (loc.equals ("InitialValue")) {
    Object dt = getContextData ("DataType");
    try {
        if (dt.equals (String.class)) {
      setContextData ("InitialValue", text ());
        } else if (dt.equals (Double.class)) {
      setContextData ("InitialValue", new Double(text ()));
        } else if (dt.equals (Long.class)) {
      setContextData ("InitialValue", new Long(text ()));
        } else if (dt.equals (Date.class)) {
      setContextData ("InitialValue",
          XMLUtil.parseXsdDateTime(text ()));
        } else if (dt.equals (Boolean.class)) {
      setContextData ("InitialValue", new Boolean(text ()));
        } else if (dt.equals (Participant.class)) {
      setContextData ("InitialValue", text ());
        } else if ((dt instanceof ExternalReference)
             || (dt instanceof SAXEventBuffer)
             || dt.equals (org.w3c.dom.Element.class)) {
      setContextData ("InitialValue",
          parseInitialXMLValue (text()));
        }
    } catch (NumberFormatException e) {
        throw new SAXException
      ("Cannot convert to type: " + e.getMessage ());
    } catch (ParseException e) {
        throw new SAXException
      ("Cannot convert to type: " + e.getMessage ());
    }
      } else if (loc.equals ("DataField")
           || loc.equals ("FormalParameter")) {
    procDataMap.put (getContextData ("ProcDataId"),
         getContextData ("InitialValue"));
      } else if (loc.equals ("ActivitySet")) {
    actSetDefs.put (actSetId, actSetDef);
      } else if (loc.equals ("ExtendedAttribute")
           && extAttrName != null) {
    if (extAttrValue == null) {
        extAttrValue = text().trim();
    }
    if (extAttrName.equals ("Debug")
        && extAttrValue.equalsIgnoreCase ("true")) {
        try {
      setDebugEnabled (true);
        } catch (InvalidStateException e) {
      logger.error ("Cannot set debug enabled: "
              + e.getMessage (), e);
        }
    }
    extAttrName = null;
    extAttrValue = null;
      }
  }
    }

    private SAXEventBuffer parseInitialXMLValue (String text) {
  try {
      SAXParserFactory spf = SAXParserFactory.newInstance ();
      spf.setValidating (false);
      spf.setNamespaceAware (true);
      spf.setFeature
    ("http://xml.org/sax/features/namespace-prefixes", true);
      XMLReader xr = null;
      try {
    spf.setFeature
        ("http://xml.org/sax/features/xmlns-uris", true);
    xr = spf.newSAXParser().getXMLReader();
      } catch (SAXException e) {
    xr = new XmlnsUrisPatcher
        (spf.newSAXParser().getXMLReader());
      }
      SAXEventBufferImpl seb = new SAXEventBufferImpl ();
      XMLFilterImpl filter = new XMLFilterImpl () {
        private int level = 0;
        public void startElement
      (String uri, String localName, String qName,
       Attributes atts) throws SAXException {
      if (level > 0) {
          super.startElement (uri,localName,qName,atts);
      }
      level += 1;
        }
        public void endElement
      (String uri, String localName, String qName)
      throws SAXException{
      level -= 1;
      if (level > 0) {
          super.endElement (uri, localName, qName);
      }
        }
    };
      filter.setContentHandler (seb);
      xr.setContentHandler (filter);
      xr.parse (new InputSource
          (new StringReader
           ("<temporary-root>" + text + "</temporary-root>")));
      seb.pack();
      return seb;
  } catch (ParserConfigurationException e) {
      throw new IllegalArgumentException
    ("Error initiliazing schema type: " + e.getMessage ());
  } catch (SAXException e) {
      throw new IllegalArgumentException
    ("Error initiliazing schema type: " + e.getMessage ());
  } catch (IOException e) {
      throw new IllegalArgumentException
    ("Error initiliazing schema type: " + e.getMessage ());
  }
    }

    /**
     * Helper class for retrieving the Activity information from
     * the process definition.
     */
    public class ActivityInitializer extends StackedHandler {

  private Map actIdMap = null;
  private Map trefsMap = null;
  private Map actSetDefs = null;
  private String blkActKey = null;

  private String actId = null;
  private Priority priority = Priority.NORMAL;
  private String name = null;
  private String description = null;
  private StartFinishMode startMode = StartFinishMode.AUTOMATIC;
  private StartFinishMode finishMode = StartFinishMode.AUTOMATIC;
  private JoinAndSplitMode joinMode = JoinAndSplitMode.AND;
  private JoinAndSplitMode splitMode = JoinAndSplitMode.AND;
  private String performer = null;
  private List deadlines = new ArrayList ();

  private boolean waitingForStartMode = false;
  private boolean waitingForFinishMode = false;
  private List trefs = null;
  private List impls = null;
  private String actSetRef = null;
  private int dlMode;
  private String dlCond = null;
  private String dlException = null;
  private boolean deferredChoice = false;

  private String extAttrName = null;
  private String extAttrValue = null;

  /**
   * Create a new initializer with the given parameters.
   * @param actIds activity id map
   * @param trefs transition reference map
   * @param actSets activity set definition map
   * @param blkActKey the key of the block activity this
   * activity belongs to or <code>null</code>
   */
  public ActivityInitializer
      (Map actIds, Map trefs, Map actSets, String blkActKey) {
      actIdMap = actIds;
      trefsMap = trefs;
      actSetDefs = actSets;
      this.blkActKey = blkActKey;
  }

  /**
   * 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 ("Activity")) {
    actId = a.getValue ("Id");
    name = a.getValue ("Name");
    trefs = new ArrayList ();
    impls = new ArrayList ();
      } else if (loc.equals ("StartMode")) {
    waitingForStartMode = true;
      } else if (loc.equals ("FinishMode")) {
    waitingForFinishMode = true;
      } else if (loc.equals ("Automatic")) {
    if (waitingForStartMode) {
        startMode = StartFinishMode.AUTOMATIC;
    } else if (waitingForFinishMode) {
        finishMode = StartFinishMode.AUTOMATIC;
    }
      } else if (loc.equals ("Manual")) {
    if (waitingForStartMode) {
        startMode = StartFinishMode.MANUAL;
    } else if (waitingForFinishMode) {
        finishMode = StartFinishMode.MANUAL;
    }
      } else if (loc.equals ("Join")) {
    joinMode = JoinAndSplitMode.fromString (a.getValue("Type"));
      } else if (loc.equals ("Split")) {
    splitMode = JoinAndSplitMode.fromString (a.getValue("Type"));
      } else if (loc.equals ("TransitionRef")) {
    trefs.add (a.getValue ("Id"));
      } else if (loc.equals ("Tool")) {
    getStack().push (ToolBasedImpl.saxConstructor ());
      } else if (loc.equals ("SubFlow")) {
    getStack().push (ProcBasedImpl.saxConstructor ());
      } else if (loc.equals ("Deadline")) {
    dlMode = Deadline.SYNCHR;
    if (a.getValue("Execution") != null
        && a.getValue("Execution").equals ("ASYNCHR")) {
        dlMode = Deadline.ASYNCHR;
    }
      } else if (loc.equals ("BlockActivity")) {
     actSetRef = a.getValue("BlockId");
      } else if (loc.equals ("ExtendedAttribute")
           && getStack().getRelativeDepth() == 2) {
    extAttrName = a.getValue("Name");
    extAttrValue = 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 (loc.equals ("Activity")) {
    if (actSetRef != null) {
        // create block activity, i.e. the activities defined
        // in the activity set
        Long baKey
      = new Long (-createActivityKey().longValue());
        BlockActivity ba = new BAForProcessInstantiation
      (AbstractProcess.this, baKey.toString (),
       (SAXEventBuffer)actSetDefs.get (actSetRef),
       (String)getContextData ("packageId"),
       actSetDefs, actId, joinMode, splitMode);
        actIdMap.put(actId, ba);
        getPaBlockDeadlines().put (baKey, deadlines);
    } else {
        // create a simple activity
        WfActivityLocal act = createActivity
      (blkActKey, priority, name, description,
       startMode, finishMode, joinMode, splitMode,
       impls.size() == 0 ? null
       : ((Implementation[])impls.toArray
          (new Implementation[impls.size()])),
       performer, deadlines, deferredChoice,
       getPaAuditEventSelection(), getPaStoreAuditEvents());
        actIdMap.put (actId, act);
    }
    trefsMap.put (actId, trefs);
      } else if (loc.equals ("Priority")) {
    try {
        priority = Priority
      .fromInt (Integer.parseInt(text().trim()));
    } catch (InvalidPriorityException e) {
        throw new SAXException (e);
    }
      } else if (loc.equals ("Description")) {
    description = text().trim();
      } else if (loc.equals ("StartMode")) {
    waitingForStartMode = false;
      } else if (loc.equals ("FinishMode")) {
    waitingForFinishMode = false;
      } else if (loc.equals ("Performer")) {
    performer = text().trim();
      } else if (loc.equals ("Tool") || loc.equals ("SubFlow")) {
    impls.add (getContextData ("implementation"));
      } else if (loc.equals ("DeadlineCondition")) {
    dlCond = text();
      } else if (loc.equals ("ExceptionName")) {
    dlException = text();
      } else if (loc.equals ("Deadline")) {
    deadlines.add (new Deadline (dlMode, dlCond, dlException));
      } else if (loc.equals ("ExtendedAttribute")
           && extAttrName != null) {
    if (extAttrValue == null) {
        extAttrValue = text().trim();
    }
    if (extAttrName.equals ("DeferredChoice")
        && extAttrValue.equalsIgnoreCase ("true")) {
        deferredChoice = true;
    }
    extAttrName = null;
    extAttrValue = null;
      }
  }
    }

    /**
     * Return a new initializer with the given parameters.
     * @param actIds activity id map
     * @param trefs transition reference map
     * @param actSets activity set definition map
     * @param blkActKey the id of the containing block activity or
     * <code>null</code>
     * @return the initializer
     */
    ActivityInitializer activityInitializer
  (Map actIds, Map trefs, Map actSets, String blkActKey) {
  return new ActivityInitializer (actIds, trefs, actSets, blkActKey);
    }

    /**
     * Factory method that create new persistent objects of type
     * <code>WfActivity</code>. Must be implement by the persistence
     * layer.
     *
     * @param blockActId if the activity is part of a block activity,
     * else <code>null</code>
     * @param priority a <code>Priority</code> value
     * @param name the activity's name
     * @param description activity description
     * @param startMode the start mode
     * @param finishMode the finish mode
     * @param joinMode the join mode
     * @param splitMode the split mode
     * @param implementation the implementation description
     * @param performer the performer
     * @param deadlines the deadlines
     * @param deferredChoice if true, the split is to be made as
     * deferred choice
     * @param auditEventSelection the audit event selection
     * @param storeAuditEvents if true, audit events are stored in the
     * database
     * @return the created activity.
     */
    protected abstract WfActivityLocal createActivity
  (String blockActId, Priority priority, String name, String description,
   StartFinishMode startMode, StartFinishMode finishMode,
   JoinAndSplitMode joinMode, JoinAndSplitMode splitMode,
   Implementation[] implementation, String performer, List deadlines,
   boolean deferredChoice, int auditEventSelection,
   boolean storeAuditEvents);

    /**
     * Helper class for retrieving the transition information from
     * the process definition.
     */
    public class TransitionInitializer extends StackedHandler {

  private Map actIdMap = null;
  private Map trefsMap = null;
  private String blkActId = null;

  private String transId = null;
  private String fromId = null;
  private String toId = null;
  private String condType = null;
  private String condExpr = null;

  /**
   * Create a new initializer with the given parameters.
   * @param actIds activity id map
   * @param trefs transition reference map
   * @param blkActId the Id of the block activity being
   * created, i.e. to which the transitions belong.
   */
  public TransitionInitializer (Map actIds, Map trefs, String blkActId) {
      actIdMap = actIds;
      trefsMap = trefs;
      this.blkActId = blkActId;
  }

  /**
   * 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 ("Transition")) {
    transId = a.getValue ("Id");
    fromId = a.getValue ("From");
    toId = a.getValue ("To");
      } else if (loc.equals ("Condition")) {
    condType = a.getValue ("Type");
    if (condType == null) {
        condType = "CONDITION";
    }
      }
  }

  /**
   * 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 ("Condition")) {
    condExpr = text();
      } else if (loc.equals ("Transition")) {
          createTransition
              (actIdMap, trefsMap, transId, fromId, toId,
               condType, condExpr, blkActId);
      }
  }
    }

    /**
     * Return a new initializer with the given parameters.
     * @param actIds activity id map
     * @param trefs transition reference map
     * @param blkActId the Id of the block activity being
     * created, i.e. to which the transitions belong.
     * @return the initializer
     */
    TransitionInitializer transitionInitializer
  (Map actIds, Map trefs, String blkActId) {
  return new TransitionInitializer (actIds, trefs, blkActId);
    }

    private void createTransition
  (Map actIdMap, Map trefsMap,
   String transId, String fromId, String toId,
   String condType, String condExpr, String blkActId) {
  // find associated "from"-activities
  int order = evalTransitionOrder(transId, fromId, trefsMap);

  ActivityLocal fromAct = (ActivityLocal)actIdMap.get(fromId);
  BAForProcessInstantiation fromBA = null;
  List fromActivities = new ArrayList();
  if (fromAct instanceof BlockActivity) {
      // we do not generate exception transitions from block
      // internals to block externals
      if (condExpr != null
    && (condType.equals ("EXCEPTION")
        || condType.equals ("DEFAULTEXCEPTION"))) {
    fromActivities.add
        (blockActivityRepresentation(fromAct.key()));
      } else {
    fromBA = (BAForProcessInstantiation)fromAct;
    fromActivities.addAll (fromBA.exitActivities());
      }
  } else {
      fromActivities.add (fromAct);
  }
     
  ActivityLocal toAct = (ActivityLocal)actIdMap.get(toId);
  BAForProcessInstantiation toBA = null;
  List toActivities = new ArrayList();
  if (toAct instanceof BlockActivity) {
      toBA = (BAForProcessInstantiation)toAct;
      toActivities.addAll (toBA.entryActivities ());
  } else {
      toActivities.add (toAct);
  }

  // create all the possible transitions
  for (Iterator fromIt = fromActivities.iterator(); fromIt.hasNext();){
      ActivityLocal fa = (ActivityLocal)fromIt.next();
      for (Iterator toIt = toActivities.iterator(); toIt.hasNext();){
    ActivityLocal ta = (ActivityLocal)toIt.next();

    // enhance transition id if block activities are
    // involved
    String id = transId;
    if (blkActId != null) {
        id += "/" + blkActId;
    }
    if (fromBA != null){
        id += "/" + fromBA.setId() + "/"
      + fromBA.getMemberId (fa.key());
    }
    if (toBA != null){
        id += "/" + toBA.setId() + "/"
      + toBA.getMemberId (ta.key());
    }

    // create and register transition(s)
    int ct = Transition.COND_TYPE_CONDITION;
    if (condExpr != null) {
        if (condType.equals ("OTHERWISE")) {
      ct = Transition.COND_TYPE_OTHERWISE;
        } else if (condType.equals ("EXCEPTION")) {
      ct = Transition.COND_TYPE_EXCEPTION;
        } else if (condType.equals ("DEFAULTEXCEPTION")) {
      ct = Transition.COND_TYPE_DEFAULTEXCEPTION;
        }
    }
    doCreateTransition (id, transId, order, fa, ta, ct, condExpr);
      }
  }
    }

    /**
     * Returns the transition order of the given transition id as defined with
     * the set of transitions assigned to a given activity id - using the given
     * map of transition refrences.
     * @param transId the transition id to define the order for
     * @param actId the activity id to determine the order for
     * @param trefsMap the map of transition references
     * @return the order value
     */
    private int evalTransitionOrder
  (String transId, String actId, Map trefsMap) {
  int order = 0;
  List trefs = (List)trefsMap.get (actId);
  if (trefs == null) {
      return Integer.MAX_VALUE;
  }
  Iterator refIt = trefs.iterator();
  while (refIt.hasNext()) {
      String refId = (String)refIt.next();
      if (transId.equals(refId)){
    break;
      }
      order += 1;
  }
  return order;
    }

    /**
     * Persist a new transition with given id, from-activity,
     * to-activity. The created transitions must be made persistent by
     * the persistence layer.
     * @param id the transition id
     * @param group the transition group id
     * @param order the transition priority
     * @param fromAct the from activity
     * @param toAct the to activity
     * @param condType the type of the condition
     * @param condition condition of this transition
     * @return the created transition.
     */
    protected abstract TransitionLocal doCreateTransition
  (String id, String group, int order,
         ActivityLocal fromAct, ActivityLocal toAct,
   int condType, String condition);

}
TOP

Related Classes of de.danet.an.workflow.domain.AbstractProcess$DupEndFilter

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.