Package de.danet.an.workflow.ejbs.core

Source Code of de.danet.an.workflow.ejbs.core.WfProcessEJB$DeadlineMap

/*
* 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: WfProcessEJB.java 3205 2009-09-27 17:13:47Z mlipp $
*/
package de.danet.an.workflow.ejbs.core;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Serializable;
import java.lang.ref.SoftReference;

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

import java.rmi.RemoteException;
import java.security.Principal;
import java.security.ProviderException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;

import javax.ejb.CreateException;
import javax.ejb.EJBException;
import javax.ejb.EJBLocalObject;
import javax.ejb.EntityBean;
import javax.ejb.EntityContext;
import javax.ejb.FinderException;
import javax.ejb.NoSuchEntityException;
import javax.ejb.NoSuchObjectLocalException;
import javax.ejb.ObjectNotFoundException;
import javax.ejb.RemoveException;
import javax.ejb.TimedObject;
import javax.ejb.Timer;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.ObjectMessage;
import javax.jms.Queue;
import javax.jms.QueueConnection;
import javax.jms.QueueConnectionFactory;
import javax.jms.QueueReceiver;
import javax.jms.QueueSender;
import javax.jms.QueueSession;
import javax.jms.Topic;
import javax.jms.TopicConnection;
import javax.jms.TopicConnectionFactory;
import javax.jms.TopicPublisher;
import javax.jms.TopicSession;
import javax.naming.NamingException;
import javax.sql.DataSource;

import org.hibernate.Criteria;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projections;

import de.danet.an.util.CollectionsUtil;
import de.danet.an.util.EJBUtil;
import de.danet.an.util.JDBCUtil;
import de.danet.an.util.PersistentMap;
import de.danet.an.util.ResourceNotAvailableException;
import de.danet.an.util.SimplePrincipal;
import de.danet.an.util.UniversalPrepStmt;
import de.danet.an.util.persistentmaps.PersistentMapSQLException;

import de.danet.an.workflow.internalapi.ExtActivityLocal;
import de.danet.an.workflow.internalapi.ExtProcessLocal;
import de.danet.an.workflow.internalapi.ExtTransitionLocal;
import de.danet.an.workflow.localapi.ActivityLocal;
import de.danet.an.workflow.localapi.ProcessDefinitionDirectoryLocal;
import de.danet.an.workflow.localapi.ProcessLocal;
import de.danet.an.workflow.localapi.TransitionLocal;
import de.danet.an.workflow.omgcore.AlreadyRunningException;
import de.danet.an.workflow.omgcore.CannotCompleteException;
import de.danet.an.workflow.omgcore.CannotStartException;
import de.danet.an.workflow.omgcore.HistoryNotAvailableException;
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.ProcessData;
import de.danet.an.workflow.omgcore.WfAuditEvent;
import de.danet.an.workflow.omgcore.WfProcessMgr;
import de.danet.an.workflow.omgcore.WfRequester;
import de.danet.an.workflow.omgcore.WfExecutionObject.NotRunningState;
import de.danet.an.workflow.omgcore.WfExecutionObject.State;

import de.danet.an.workflow.api.Activity;
import de.danet.an.workflow.api.Process;
import de.danet.an.workflow.api.CannotRemoveException;
import de.danet.an.workflow.api.DefaultProcessData;
import de.danet.an.workflow.api.ImportException;
import de.danet.an.workflow.api.InvalidKeyException;
import de.danet.an.workflow.api.ProcessDefinition;
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.apix.ExtActivity;

import de.danet.an.workflow.domain.AbstractProcess;
import de.danet.an.workflow.domain.Deadline;
import de.danet.an.workflow.domain.DefaultAuditEvent;
import de.danet.an.workflow.domain.DefaultProcessDefinition;
import de.danet.an.workflow.domain.SubProcRequester;
import de.danet.an.workflow.domain.TransitionDefinition;
import de.danet.an.workflow.domain.TransitionDefinitionLocal;

import de.danet.an.workflow.ejbs.admin.ProcessDefinitionDirectoryHome;
import de.danet.an.workflow.ejbs.admin.ProcessDefinitionDirectoryLocalHome;
import de.danet.an.workflow.ejbs.admin.ProcessMgrStub;
import de.danet.an.workflow.ejbs.admin.WorkflowEngineLocal;
import de.danet.an.workflow.ejbs.admin.WorkflowEngineLocalHome;
import de.danet.an.workflow.ejbs.util.QueuerUtils;

/**
* The entity bean <code>WfProcessEJB</code> represent a process
* in the workflow.
*
* @ejb.bean name="ProcessBean" display-name="ProcessBean" type="BMP"
* jndi-name="ejb/@@@_JNDI_Name_Prefix_@@@ProcessBean"
* reentrant="true" view-type="both"
* @jonas.bean ejb-name="ProcessBean"
* @ejb.pk class="java.lang.Long" generate="false"
* @ejb.home remote-class="de.danet.an.workflow.ejbs.core.WfProcessHome"
* local-class="de.danet.an.workflow.ejbs.core.WfProcessLocalHome"
* @ejb.interface
* extends="javax.ejb.EJBObject, de.danet.an.workflow.apix.ExtProcess"
* remote-class="de.danet.an.workflow.ejbs.core.WfProcess"
* local-extends="javax.ejb.EJBLocalObject,
* de.danet.an.workflow.internalapi.ExtProcessLocal"
* local-class="de.danet.an.workflow.ejbs.core.WfProcessLocal"
* @ejb.transaction type="Required"
* @ejb.security-identity run-as="WfMOpenAdmin"
* sunone-principal="WfMCorePrincipalForAdmin"
* @weblogic.run-as-identity-principal WfMCorePrincipalForAdmin
* @ejb.ejb-ref ejb-name="WorkflowEngine" view-type="local"
* @ejb.ejb-ref ejb-name="ActivityBean" view-type="remote"
* @ejb.ejb-ref ejb-name="ActivityBean" view-type="local"
* @ejb.ejb-ref ejb-name="ProcessDefinitionDirectory" view-type="remote"
* @ejb.ejb-ref ejb-name="ProcessDefinitionDirectory" view-type="local"
* @ejb.ejb-external-ref ref-name="ejb/JdbcKeyGenLocal" link="KeyGen"
* type="Session" view-type="local" home="de.danet.an.util.KeyGenLocalHome"
* business="de.danet.an.util.KeyGenLocal"
* @ejb.resource-ref res-ref-name="jdbc/WfEngine"
* res-type="javax.sql.DataSource" res-auth="Container"
* @jonas.resource res-ref-name="jdbc/WfEngine" jndi-name="jdbc_1"
* @weblogic.enable-call-by-reference True
* @weblogic.resource-description
* res-ref-name="jdbc/WfEngine" jndi-name="DefaultDS"
* @ejb.permission role-name="WfMOpenAdmin"
* @weblogic.transaction-isolation TRANSACTION_READ_COMMITTED
* @weblogic.cache concurrency-strategy="Exclusive"
* @ejb.resource-ref res-ref-name="jms/QCF"
* res-type="javax.jms.QueueConnectionFactory" res-auth="Container"
* @jboss.resource-ref res-ref-name="jms/QCF" jndi-name="java:/JmsXA"
* @jonas.resource res-ref-name="jms/QCF" jndi-name="QCF"
* @weblogic.resource-description res-ref-name="jms/QCF"
* jndi-name="weblogic.jms.XAConnectionFactory"
* @ejb.resource-ref res-ref-name="jms/TCF"
* res-type="javax.jms.TopicConnectionFactory" res-auth="Container"
* @jboss.resource-ref res-ref-name="jms/TCF" jndi-name="java:/JmsXA"
* @jonas.resource res-ref-name="jms/TCF" jndi-name="TCF"
* @weblogic.enable-call-by-reference True
* @weblogic.resource-description res-ref-name="jms/TCF"
* jndi-name="weblogic.jms.XAConnectionFactory"
* @ejb.resource-ref res-ref-name="jms/InternalEventQueue"
* res-type="javax.jms.Queue" res-auth="Container"
* @jboss.resource-ref res-ref-name="jms/InternalEventQueue"
* jndi-name="queue/@@@_JNDI_Name_Prefix_@@@InternalEventQueue"
* @jonas.resource res-ref-name="jms/InternalEventQueue"
* jndi-name="queue/@@@_JNDI_Name_Prefix_@@@InternalEventQueue"
* @weblogic.resource-description res-ref-name="jms/InternalEventQueue"
* jndi-name="queue/@@@_JNDI_Name_Prefix_@@@InternalEventQueue"
* @ejb.resource-ref res-ref-name="jms/ChannelIn"
* res-type="javax.jms.Queue" res-auth="Container"
* @jonas.resource res-ref-name="jms/ChannelIn"
* jndi-name="queue/@@@_JNDI_Name_Prefix_@@@ChannelInMessages"
* @jboss.resource-ref res-ref-name="jms/ChannelIn"
* jndi-name="queue/@@@_JNDI_Name_Prefix_@@@ChannelInMessages"
* @weblogic.resource-description res-ref-name="jms/ChannelIn"
* jndi-name="queue/@@@_JNDI_Name_Prefix_@@@ChannelInMessages"
* @ejb.resource-ref res-ref-name="jms/ChannelOut"
* res-type="javax.jms.Topic" res-auth="Container"
* @jboss.resource-ref res-ref-name="jms/ChannelOut"
* jndi-name="topic/@@@_JNDI_Name_Prefix_@@@ChannelOutMessages"
* @jonas.resource res-ref-name="jms/ChannelOut"
* jndi-name="topic/@@@_JNDI_Name_Prefix_@@@ChannelOutMessages"
* @weblogic.resource-description res-ref-name="jms/ChannelOut"
* jndi-name="topic/@@@_JNDI_Name_Prefix_@@@ChannelOutMessages"
*/
public class WfProcessEJB extends AbstractProcess
    implements EntityBean, TimedObject {

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

    /** Cache for QueueConnectionFactory. */
    private QueueConnectionFactory qcfCache = null;
   
    /** Cache for TopicConnectionFactory. */
    private TopicConnectionFactory tcfCache = null;
   
    /** Cache for event queue. */
    private Queue eventQueueCache = null;
   
    /** Cache for channel in queue. */
    private Queue channelInQueueCache = null;
   
    /** Cache for channel out topic. */
    private Topic channelOutTopicCache = null;
   
    private QueueConnectionFactory queueConnectionFactory ()
        throws ResourceNotAvailableException {
        if (qcfCache == null) {
            qcfCache = ((QueueConnectionFactory)EJBUtil
                .retrieveJNDIEntry ("java:comp/env/jms/QCF"));
        }
        return qcfCache;
    }
   
    private TopicConnectionFactory topicConnectionFactory ()
        throws ResourceNotAvailableException {
        if (tcfCache == null) {
            tcfCache = ((TopicConnectionFactory)EJBUtil
                    .retrieveJNDIEntry ("java:comp/env/jms/TCF"));
        }
        return tcfCache;
}

    private Queue eventQueue ()
        throws ResourceNotAvailableException {
        if (eventQueueCache == null) {
            eventQueueCache = (Queue)EJBUtil.retrieveJNDIEntry
                ("java:comp/env/jms/InternalEventQueue");
        }
        return eventQueueCache;
    }

    private Queue channelInQueue ()
        throws ResourceNotAvailableException {
        if (channelInQueueCache == null) {
            channelInQueueCache = (Queue)EJBUtil.retrieveJNDIEntry
                ("java:comp/env/jms/ChannelIn");
        }
        return channelInQueueCache;
    }
   
    private Topic channelOutTopic ()
        throws ResourceNotAvailableException {
        if (channelOutTopicCache == null) {
            channelOutTopicCache = (Topic)EJBUtil.retrieveJNDIEntry
                ("java:comp/env/jms/ChannelOut");
        }
        return channelOutTopicCache;
    }
   
    /**
     * A lazily loading implementation for deadline storage. Not all
     * methods are implemented.
     */
    private class DeadlineMap extends HashMap {
  /**
   * Returns the deadlines for the activity with the given key.
   * @param key activity key
   * @return the value to which this map maps the specified key,
   * or null if the map contains no mapping for this key
   * @throws ClassCastException if the key is of an
   * inappropriate type for this map.
   * @throws NullPointerException key is null
   */ 
  public Object get(Object key)
      throws ClassCastException, NullPointerException {
      Object res = super.get (key);
      if (res == null) {
    // try to load from db
    try {
        res = loadDeadlines (ds, (Long)key);
        super.put (key, res);
    } catch (SQLException e) {
        throw new EJBException (e);
    } catch (IOException e) {
        throw new EJBException (e);
    }
      }
      return res;
  }

  /**
   * Not supported.
   * @param key key whose presence in this map is to be tested.
   * @return true if this map contains a mapping for the specified key.
   */ 
  public boolean containsKey(Object key) {
      throw new UnsupportedOperationException ();
  }

  /**
   * Not supported.
   * @param value value whose presence in this map is to be tested.
   * @return true if this map maps one or more keys to the
   * specified value.
   */ 
  public boolean containsValue(Object value) {
      throw new UnsupportedOperationException ();
  }

  /**
   * Not supported.
   * @return  set view of the keys contained in this map.
   */ 
  public Set keySet() {
      throw new UnsupportedOperationException ();
  }

  /**
   * Not supported.
   * @return a collection view of the values contained in this map.
   */ 
  public Collection values() {
      throw new UnsupportedOperationException ();
  }

  /**
   * Not supported.
   * @return a set view of the mappings contained in this map.
   */ 
  public Set entrySet() {
      throw new UnsupportedOperationException ();
  }
    }

    /** Entity context. */
    private EntityContext ctx;

    /** The data source of the database. */
    private DataSource ds = null;

    private SessionFactory hibernateSessionFactory = null;
   
    /** Indicates if the database supports select for update. */
    private Boolean haveSelForUp = null;

    /** Database name. */
    private static final String DB_NAME = "java:comp/env/jdbc/WfEngine";

    /** Associated activities. */
    private Map activityMapCache;
    private Map activityLocalMapCache;

    /** Used for fast lookup of process definition. */
    private String packageId = null;

    /** Used for fast lookup of process definition. */
    private String processId = null;

    /** Used for fast lookup of process definition. */
    private Long xpdlRef = null;
   
    /** Creating principal. */
    private String processCreator = null;

    /** The cached workflow engine. */
    private WorkflowEngineLocal engineLocalCache = null;

    /** The cached home interface of the activity. */
    private WfActivityHome activityHomeCache = null;
    private WfActivityLocalHome activityLocalHomeCache = null;

    /** The cached home interface of the processDefinitionDirectory. */
    private ProcessDefinitionDirectoryHome
        processDefinitionDirectoryHomeCache = null;
    private ProcessDefinitionDirectoryLocalHome
        processDefinitionDirectoryLocalHomeCache = null;

    /** Set during call to underlying init() in ejbPostCreate */
    private boolean callbackFromInitDuringCreate = false;
   
    /** Delayed events. */
    private List delayedEvents = null;

    /** Cached transition information. */
    private List transitionsCache = null;

    /** Cached transition information. */
    private List transitionsLocalCache = null;
    private Map transLocalByFrom = null;

    /** Local process definition as string, to be stored. */
    private String myProcDef = null;
   
    /** List of updated transitions (need to be stored). */
    private Set updatedTransitions = new HashSet();

    //
    // Provide the persistent attributes
    //

    /** Indicates change of a persistent attribute. */
    private boolean persistentAttributeModified = false;

    /** Indicates change of a persistent attribute description. */
    private boolean paDescriptionModified = false;

    /** Persistent attribute <code>createTime</code>. */
    private Date paCreateTime;

    /** Persistent attribute <code>name</code>. */
    private String paName;

    /** Persistent attribute <code>id</code>. */
    private String paId;

    /** Persistent attribute <code>description</code>. */
    private String paDescription;

    /** Persistent attribute <code>priority</code>. */
    private Priority paPriority;

    /** Persistent attribute <code>typedState</code>. */
    private State paTypedState;

    /** Persistent attribute <code>lastStateTime</code>. */
    private Date paLastStateTime;

    /** Persistent attribute <code>requester</code>. */
    private WfRequester paRequester;

    /** Persistent attribute <code>processMgrName</code>. */
    private String paProcessMgrName;

    /** Persistent attribute <code>processMgrVersion</code>. */
    private String paProcessMgrVersion;

    /** Persistent attribute <code>processDef</code>. */
    private ProcessDefinition paProcessDef;
   
    /** Alternate store for <code>processDef</code> if individual
        process description is used. */
    private SoftReference paProcessDefSoft;

    /** Persistent attribute <code>processData</code>. */
    private ProcessData paProcessData;
   
    /** Persistent attribute <code>blockDeadlines</code>. */
    private Map paBlockDeadlines;

    /**
     * Persistent attribute <code>flags</code>. This attribute is not
     * exposed, it serves as space efficient storage for various
     * attributes that are exposed.
     */
    private int paFlags;

    private static final int FLAGS_DEBUG = 1;
    private static final int FLAGS_AUDIT_SELECTION_SHIFT = 1;
    private static final int FLAGS_AUDIT_SELECTION_MASK = 7 << 1;
    private static final int FLAGS_STORE_AUDIT_EVENTS = 16;

    /* Transition flags. */
    private static int FLAGS_HAS_PENDING_TOKEN = 1;
   
    /**
     * Contructor.
     */
    public WfProcessEJB () {
    }

    //
    // Accessor methods for cached attributes
    //

    /**
     * The workflow engine.
     * @return the workflow engine
     */
    private WorkflowEngineLocal engineLocal()
        throws ResourceNotAvailableException {
        if (engineLocalCache == null) {
            engineLocalCache = (WorkflowEngineLocal)EJBUtil.createSession
                (WorkflowEngineLocalHome.class,
                 "java:comp/env/ejb/WorkflowEngineLocal");
        }
        return engineLocalCache;
    }

    /**
     * The home interface of the WfActivityBean.
     * @return home interface of the WfActivityBean
     */
    private WfActivityHome activityHome()
  throws ResourceNotAvailableException {
  if (activityHomeCache == null) {
      activityHomeCache = (WfActivityHome)EJBUtil.retrieveEJBHome
    (WfActivityHome.class, "java:comp/env/ejb/ActivityBean");
  }
  return activityHomeCache;
    }

    /**
     * The home interface of the WfActivityBean.
     * @return home interface of the WfActivityBean
     */
    private WfActivityLocalHome activityLocalHome()
        throws ResourceNotAvailableException {
        if (activityLocalHomeCache == null) {
            activityLocalHomeCache = (WfActivityLocalHome)
                EJBUtil.retrieveEJBLocalHome
                (WfActivityLocalHome.class,
                 "java:comp/env/ejb/ActivityBeanLocal");
        }
        return activityLocalHomeCache;
    }

    /**
     * The home interface of the ProcessDefinitionDirectoryBean.
     * @return home interface of the ProcessDefinitionDirectoryBean
     */
    private ProcessDefinitionDirectoryHome
        processDefinitionDirectoryHome()
        throws ResourceNotAvailableException {
        if (processDefinitionDirectoryHomeCache == null) {
            processDefinitionDirectoryHomeCache
                = (ProcessDefinitionDirectoryHome)EJBUtil.retrieveEJBHome
                (ProcessDefinitionDirectoryHome.class,
                 "java:comp/env/ejb/ProcessDefinitionDirectory");
        }
        return processDefinitionDirectoryHomeCache;
    }

    /**
     * The home interface of the ProcessDefinitionDirectoryBean.
     * @return home interface of the ProcessDefinitionDirectoryBean
     */
    private ProcessDefinitionDirectoryLocalHome
        processDefinitionDirectoryLocalHome()
  throws ResourceNotAvailableException {
  if (processDefinitionDirectoryLocalHomeCache == null) {
      processDefinitionDirectoryLocalHomeCache
    = (ProcessDefinitionDirectoryLocalHome)
                EJBUtil.retrieveEJBLocalHome
    (ProcessDefinitionDirectoryLocalHome.class,
     "java:comp/env/ejb/ProcessDefinitionDirectoryLocal");
  }
  return processDefinitionDirectoryLocalHomeCache;
    }

    /**
     * Returns the associated activities as map by key.
     * @return activities for this process by key.
     */
    private Map activityMap() throws RemoteException {
  if (activityMapCache == null) {
      activityMapCache = new HashMap ();
      try {
    for (Iterator i = activityHome().findByProcess
       ((Long)ctx.getPrimaryKey()).iterator ();
         i.hasNext();) {
        ExtActivity a = (ExtActivity)i.next();
        cacheActivity (a);
    }
      } catch (FinderException e) {
    throw new EJBException (e);
      }
  }
  return activityMapCache;
    }

    /**
     * Returns the associated activities as map by key.
     * @return activities for this process by key.
     */
    private Map activityLocalMap() {
        if (activityLocalMapCache == null) {
            activityLocalMapCache = new HashMap ();
            try {
                for (Iterator i = activityLocalHome().findByProcess
                         ((Long)ctx.getPrimaryKey()).iterator ();
                     i.hasNext();) {
                    ExtActivityLocal a = (ExtActivityLocal)i.next();
                    cacheActivityLocal (a);
                }
            } catch (FinderException e) {
                throw new EJBException (e);
            } catch (ResourceNotAvailableException e) {
                throw new EJBException (e);
            }
        }
        return activityLocalMapCache;
    }

    /**
     * Returns the associated activities.
     * @return list of activities for this process
     */
    private Collection activitiesLocal() {
        return activityLocalMap().values();
    }

    /**
     * Put the given activity in the cache. An
     * <code>ActivityProxy</code> around the activity ensures that the
     * cache is cleared (and thus reloaded when retrying the call) if
     * a <code>RemoteException</code> occurs.
     */
    private ExtActivity cacheActivity (ExtActivity act)
  throws RemoteException {
  ExtActivity a = ActivityProxy.wrap (act, (Process)toProcess());
  activityMapCache.put (a.key(), a);
  return a;
    }

    /**
     * Put the given local activity in the cache. An
     * <code>ActivityProxy</code> around the activity ensures that the
     * cache is cleared (and thus reloaded when retrying the call) if
     * a <code>RemoteException</code> occurs.
     */
    private ExtActivityLocal cacheActivityLocal (ExtActivityLocal act) {
        activityLocalMap().put (act.key(), act);
        return act;
    }

    /**
     * The remote interface of the ProcessDefinitionDirectoryBean.
     * @return remote interface of the ProcessDefinitionDirectoryBean
     */
    private ProcessDefinitionDirectoryLocal
        getProcessDefinitionDirectoryLocal()
  throws CreateException, NamingException, RemoteException {
  return (ProcessDefinitionDirectoryLocal)
            processDefinitionDirectoryLocalHome().create();
    }

    //
    // EJB container contract
    //

    /**
     * Set the associated session context. The container calls this method
     * after the instance creation.
     * @see javax.ejb.EntityBean
     * @throws EJBException Throws on any error.
     * @param context - A SessionContext interface for the instance
     */
    public void setEntityContext(EntityContext context)
  throws EJBException {
  try {
      ctx = context;
      ds = JDBCUtil.refreshDS(null, DB_NAME);
      // we can always use the same object for process data.
      paProcessData = new ProcessDataStore (ds);
      // prepare Hibernate
      hibernateSessionFactory = engineLocal().hibernateSessionFactory ();
  } catch (Exception ex) {
      throw new EJBException(ex);
  }
    }
   
    /**
     * Unset the associated session context.
     * @see javax.ejb.EntityBean
     */
    public void unsetEntityContext() throws EJBException {
        qcfCache = null;
  engineLocalCache = null;
  activityHomeCache = null;
  processDefinitionDirectoryLocalHomeCache = null;
  ds = null;
        ctx = null;
    }

    /**
     * This method looks up a process EJB by its primary key (dbid).
     * @param primaryKey the process primary key.
     * @return the process EJB (remote interface).
     * @throws FinderException if no process found.
     */
    public Long ejbFindByPrimaryKey(Long primaryKey)
  throws FinderException {

  boolean result = false;
  try {
      result = selectByPrimaryKey(primaryKey);
  } catch (SQLException ex) {
      throw new EJBException(ex);
  }
  if (!result) {
      throw new ObjectNotFoundException
    ("Row for id " + primaryKey + " not found.");
  }
  return primaryKey;
    }

    /**
     * This method looks up a process by its
     * {@link de.danet.an.workflow.omgcore.WfProcess#key key}.
     * @param key the process key.
     * @return the process EJB (remote interface).
     * @throws FinderException if no process found.
     */
    public Long ejbFindByProcessKey(String key)
  throws FinderException {

  Long pk = null;
  try {
      pk = Long.valueOf (key);
  } catch (NumberFormatException nex) {
      throw new ObjectNotFoundException
    ("Invalid key format for key = " + key);
  }
  return ejbFindByPrimaryKey (pk);
    }

    /**
     * This method looks up a process by the key {@link
     * de.danet.an.workflow.omgcore.WfActivity#key key} of one of its
     * activities.
     * @param key the activity key.
     * @return the process EJB (remote interface).
     * @throws FinderException if no process found.
     */
    public Long ejbFindByActivityKey(String key)
  throws FinderException {

  Long pk = null;
  try {
      pk = selectByActivityKey(Long.valueOf(key));
  } catch (NumberFormatException nex) {
      throw new ObjectNotFoundException
    ("Invalid key format for key = " + key);
  } catch (SQLException ex) {
      throw new EJBException(ex);
  }
  if (pk == null) {
      throw new ObjectNotFoundException
    ("Row for id " + key + " not found.");
  }
  return pk;
    }

    /**
     * This method looks up all the process by its process type.
     * @param processMgrName the process type.
     * @return a Collection object with remote interface of WfProcess.
     * @throws FinderException because of the EJB syntax.
     */
    public Collection ejbFindByProcessMgrName(String processMgrName)
  throws FinderException {
  Collection col = null;
  try {
      col = selectByProcessMgrName(processMgrName);
  } catch (SQLException ex) {
      throw new EJBException(ex);
  }
  return col;
    }

    /**
     * This method looks up all processes without a local copy of the
     * process definition by their process type.
     * @param processMgrName the process type.
     * @return a Collection object with remote interface of WfProcess.
     * @throws FinderException because of the EJB syntax.
     */
    public Collection ejbFindWithoutDefinitionByProcessMgrName
  (String processMgrName)
  throws FinderException {
  Collection col = null;
  try {
      col = selectWithoutDefinitionByProcessMgrName(processMgrName);
  } catch (SQLException ex) {
      throw new EJBException(ex);
  }
  return col;
    }

    /**
     * This method looks up all processes that have a requester with
     * the given hash value.
     * @param hash the hash value.
     * @return a Collection object with remote interface of WfProcess.
     * @throws FinderException because of the EJB syntax.
     */
    public Collection ejbFindByRequesterHash(int hash)
  throws FinderException {
  Collection col = null;
  try {
      col = selectByRequesterHash(hash);
  } catch (SQLException ex) {
      throw new EJBException(ex);
  }
  return col;
    }

    /**
     * Look up all processes of a given type that have a given value
     * in a particular process data item. Note that this method may
     * only be used for data items of type string.
     *
     * @param processMgr the process type
     * @param itemName the name of the process data item
     * @param itemValue the value of the process data item
     * @return a Collection object with remote interface of WfProcess.
     * @throws FinderException because of the EJB syntax.
     */
    public Collection ejbFindByProcessTypeAndDataItem
  (String processMgr, String itemName, String itemValue)
  throws FinderException {
  Connection con = null;
  try {
      try {
    con = ds.getConnection();     
    return ((ProcessDataStore)paProcessData)
        .findProcessesByTypeAndDataItem
        (con, processMgr, itemName, itemValue);
      } finally {
    JDBCUtil.closeAll (null, null, con);
      }
  } catch (SQLException ex) {
      throw new EJBException(ex);
  } catch (IOException ex) {
      throw new EJBException(ex);
  }
    }

    /**
     * This method looks up all processes.
     * @return a Collection object with remote interface of WfProcess.
     * @throws FinderException because of the EJB syntax.
     */
    public Collection ejbFindAll()
  throws FinderException {
  Collection col = null;
  try {
      col = selectAll();
  } catch (SQLException ex) {
      throw new EJBException(ex);
  }
  return col;
    }

    /**
     * This method looks up a range of processes.
     * @param filterData the filter criteria
     * @param orderData the order criteria
     * @param start the index of the first item
     * @param end the index of the last item
     * @return a Collection object with processes primary keys
     * @throws FinderException because of the EJB syntax
     */
    public Collection ejbFindRange
        (byte[] filterData, byte[] orderData, long start, long end)
        throws FinderException {
        Session session = null;
        Criterion filterCriterion = null;
        List orderCriteria = null;
        try {
            ByteArrayInputStream bis
                = new ByteArrayInputStream(filterData);
            ObjectInputStream ois = new ObjectInputStream (bis);
            filterCriterion = (Criterion)ois.readObject();
            bis = new ByteArrayInputStream(orderData);
            ois = new ObjectInputStream (bis);
            orderCriteria = (List)ois.readObject();
        } catch (IOException e) {
            throw (IllegalArgumentException)
                (new IllegalArgumentException()).initCause(e);
        } catch (ClassNotFoundException e) {
            throw (IllegalArgumentException)
                (new IllegalArgumentException()).initCause(e);
        }
        try {
            session = hibernateSessionFactory.openSession();
            Criteria curCrit = session.createCriteria(DAO.class);
            curCrit.setProjection(Projections.property("dbId"));
            if (filterCriterion != null) {
                curCrit.add(filterCriterion);
            }
            if (orderCriteria != null) {
                for (Iterator i = orderCriteria.iterator(); i.hasNext();) {
                    curCrit.addOrder((Order)i.next());
                }
            }
            curCrit.setFirstResult((int)start);
            curCrit.setMaxResults((int)(end - start + 1));
            List res = curCrit.list();
            return res;
        } finally {
            if (session != null) {
                session.close();
            }
        }
    }

    /**
     * The activate method is called when the instance is activated from its
     * "passive" state. The instance should acquire any resource that it has
     * released earlier in the ejbPassivate() method.
     * This method gets the primary key from container.
     * @see javax.ejb.EntityBean
     */
    public void ejbActivate() throws EJBException {
  // "bind" entity container to primary key
  ((ProcessDataStore)paProcessData)
      .setMapId ((Long)ctx.getPrimaryKey());
  activityMapCache = null;
        activityLocalMapCache = null;
  transitionsCache = null;
        transitionsLocalCache = null;
        transLocalByFrom = null;
        updatedTransitions.clear();
    }

    /**
     * The passivate method is called before the instance enters the
     * "passive" state. The instance should release any resources that it
     * can re-acquire later in the ejbActivate() method.
     * @see javax.ejb.EntityBean
     */
    public void ejbPassivate() throws EJBException {
  paProcessDef = null;
  paProcessDefSoft = null;
  activityMapCache = null;
        activityLocalMapCache = null;
  transitionsCache = null;
        transitionsLocalCache = null;
        transLocalByFrom = null;
        updatedTransitions.clear();
  paBlockDeadlines = null;
  dispose();
    }

    /**
     * Create an process and save it into database.
     * It throws <code>EJBException</code> if any error occurs.
     * @param procDef process definition
     * @param req requester of the process
     * @return Long primary key of the process
     * @throws CreateException no
     * @ejb.create-method
     * view-type="local"
     */
    public Long ejbCreate
        (Long xpdlRef, ProcessDefinition procDef, WfRequester req)
  throws CreateException {
  try {
      Long processPk = null;
      // create new processPk
      processPk = new Long (EJBUtil.newPrimaryKey("process"));
      // "bind" entity container to primary key
      ((ProcessDataStore)paProcessData).setMapId (processPk);
      ((ProcessDataStore)paProcessData).setNewMap ();
      return processPk;
  } catch (RemoteException ex) {
      logger.error (ex.getMessage(), ex);
      throw new CreateException(ex.getMessage());
  }
    }

    /**
     * Create an process and save it into database. First are created
     * activities, transitions and transitions references.
     * @param procDef process definition
     * @param req requester of the process
     * @throws CreateException if the process can not be created.
     */
    public void ejbPostCreate
        (Long xpdlRef, ProcessDefinition procDef, WfRequester req)
  throws CreateException {
  try {
      // initialize private attributes
      packageId = procDef.packageId();
      processId = procDef.processId();
      this.xpdlRef = xpdlRef;
      if (req instanceof SubProcRequester) {
    processCreator = ((SubProcRequester)req).creator ();
      } else {
    processCreator = ctx.getCallerPrincipal().getName();
      }
            activityLocalMapCache = new HashMap ();
      // initialize persistent attributes
      paRequester = req;
      paCreateTime = new Date();
            transitionsLocalCache = new ArrayList ();
            transLocalByFrom = new HashMap ();
            transitionsCache = null;
            updatedTransitions.clear();
      paProcessDef = procDef;
      paProcessDefSoft = null;
      paBlockDeadlines = CollectionsUtil.trackedMap(new DeadlineMap ());
      // initialize underlying domain object. Delay events
      // because event processing tries to lookup the process in
      // the database, and it isn't there yet. This is probably
      // only necessary in the JBoss environment (and may even
      // not be sufficient) because the creation of the process
      // and the queuing of events should be in one transaction
      // and become visible to the event handler EJB at the same
      // time.
      delayedEvents = new ArrayList();
      try {
          callbackFromInitDuringCreate = true;
          init(procDef);
      } finally {
          callbackFromInitDuringCreate = false;
      }
      // Is set by init, we initially use xpdlref, however.
      Collections.sort (transitionsLocalCache, new Comparator () {
        public int compare (Object o1, Object o2) {
      return ((TransitionDefinitionLocal)o1).order()
          - ((TransitionDefinitionLocal)o2).order();
        }
    });
      refresh();
      // save the new process into database
      insertProcess();
      insertTransitions();
      persistentAttributeModified = false;
      while (delayedEvents.size() > 0) {
    QueuerUtils.queue
        (queueConnectionFactory(), eventQueue(),
         (DefaultAuditEvent)delayedEvents.remove(0));
      }
      delayedEvents = null;
  } catch (RemoteException re) {
      throw new EJBException(re);
  } catch (SQLException e) {
      throw new EJBException(e);
  } catch (IOException ioe) {
      logger.error (ioe.getMessage(), ioe);
      throw new CreateException (ioe.getMessage());
  }
    }

    /**
     * A container invokes this method before it ends the life of the session
     * object. This happens as a result of a client's invoking a remove
     * operation, or when a container decides to terminate the session object
     * after a timeout.
     * @throws RemoveException if the activity is not closed.
     * @see javax.ejb.EntityBean
     */
    public void ejbRemove() throws RemoveException {
  // Note that #removeThis must be adapted if RemoveException is
  // thrown due to any other cause. And prepareRemove must be
  // adapted if there are any more preconditions to meet.
  try {
      if (getPaTypedState().workflowState() != State.CLOSED
    && (!getPaTypedState().isSameOrSubState
        (NotRunningState.NOT_STARTED))) {
    throw new RemoveException
        ("Cannot remove " + toString() + ": still running.");
            }
            removeProcess();
      dispose();
      paProcessDef = null;
      paProcessDefSoft = null;
      activityMapCache = null;
            activityLocalMapCache = null;
      transitionsCache = null;
            transitionsLocalCache = null;
            transLocalByFrom = null;
            updatedTransitions.clear();
      paBlockDeadlines = null;
      engineLocal().removeAuditEvents
                (((Long)ctx.getPrimaryKey()).toString());
  } catch (RemoteException ex) {
      throw new EJBException(ex);
  } catch (IOException ex) {
      throw new EJBException(ex);
  } catch (SQLException ex) {
      throw new EJBException(ex);
  }
    }

    /**
     * A container invokes this method to synchronize the state of an
     * enterprise bean instance with the entity object's state in the database.
     * @see javax.ejb.EntityBean
     */
    public void ejbLoad() throws EJBException {
  try {
      // load process attributes from persistent store
       loadProcess();
      paBlockDeadlines = CollectionsUtil.trackedMap(new DeadlineMap ());
            updatedTransitions.clear();
      // notify underlying domain object
      refresh ();
  } catch (SQLException e) {
      throw new EJBException(e);
  } catch (IOException e) {
      throw new EJBException(e);
  } catch (FinderException e) {
      throw new EJBException(e);
  }
    }

    /**
     * A container invokes this method to synchronize the state of the
     * entity object in the database with the state of the enterprise bean
     * instancee.
     * @see javax.ejb.EntityBean
     */
    public void ejbStore() throws EJBException {
  try {
      // moved the check for persistentAttributeModified
      // to storeProcess because of paProcessData handling
      storeProcess();
      persistentAttributeModified = false;
  } catch (SQLException ex) {
      throw new EJBException(ex);
  } catch (IOException ex) {
      throw new EJBException(ex);
  }
    }

    /*********************** Database Routines *************************/
   
    /**
     * Helper class for Hibernate.
     */
    public static class DAO {
        private long dbId;
        private String name;
        private int priority;
        private String description;
        private String state;
        private Date createTime;
        private Date lastStateTime;
        private String managerName;
       
        /**
         * Create a new instance with all attributes initialized
         * to defaults or the given values.
         *
         */
        public DAO() {
        }

        /**
         * @return Returns the dbId.
         */
        public long getDbId() {
            return dbId;
        }

        /**
         * @param dbId The dbId to set.
         */
        public void setDbId(long dbId) {
            this.dbId = dbId;
        }

        /**
         * @return Returns the name.
         */
        public String getName() {
            return name;
        }

        /**
         * @param name The name to set.
         */
        public void setName(String name) {
            this.name = name;
        }

        /**
         * @return Returns the priority.
         */
        public int getPriority() {
            return priority;
        }

        /**
         * @param priority The priority to set.
         */
        public void setPriority(int priority) {
            this.priority = priority;
        }

        /**
         * @return Returns the description.
         */
        public String getDescription() {
            return description;
        }

        /**
         * @param description The description to set.
         */
        public void setDescription(String description) {
            this.description = description;
        }

        /**
         * @return Returns the state.
         */
        public String getState() {
            return state;
        }

        /**
         * @param state The state to set.
         */
        public void setState(String state) {
            this.state = state;
        }

        /**
         * @return Returns the createTime.
         */
        public Date getCreateTime() {
            return createTime;
        }

        /**
         * @param createTime The createTime to set.
         */
        public void setCreateTime(Date createTime) {
            this.createTime = createTime;
        }

        /**
         * @return Returns the lastStateTime.
         */
        public Date getLastStateTime() {
            return lastStateTime;
        }

        /**
         * @param lastStateTime The lastStateTime to set.
         */
        public void setLastStateTime(Date lastStateTime) {
            this.lastStateTime = lastStateTime;
        }

        /**
         * @return Returns the managerName.
         */
        public String getManagerName() {
            return managerName;
        }

        /**
         * @param managerName The managerName to set.
         */
        public void setManagerName(String managerName) {
            this.managerName = managerName;
        }
    }
   
    /**
     * This method checks if the process exists.
     * @param primaryKey key to check
     * @return If it is ok, it returns true.
     */
    private boolean selectByPrimaryKey(Long primaryKey) throws SQLException {
  Connection con = null;
  PreparedStatement prepStmt = null;
  ResultSet rs = null;
  try {
      con = ds.getConnection();
      String selectStatement
                = "SELECT DBId FROM process WHERE DBId = ? ";
      prepStmt = con.prepareStatement(selectStatement);
      prepStmt.setLong(1, primaryKey.longValue());
      rs = prepStmt.executeQuery();
      boolean result = rs.next();
      return result;
  } finally {
      JDBCUtil.closeAll (rs, prepStmt, con);
  }
    }
   
    /**
     * This method retrieves an activity's container
     * @param key activity key
     * @return primary key of process
     */
    private Long selectByActivityKey(Long key) throws SQLException {
  Connection con = null;
  PreparedStatement prepStmt = null;
  ResultSet rs = null;
  try {
      con = ds.getConnection();
      prepStmt = con.prepareStatement
    ("SELECT ProcessDBId FROM Activity WHERE DBId = ? ");
      prepStmt.setLong(1, key.longValue());
      rs = prepStmt.executeQuery();
      if (! rs.next()) {
    return null;
      }
      return new Long(rs.getLong(1));
  } finally {
      JDBCUtil.closeAll (rs, prepStmt, con);
  }
    }
   
    private Collection selectByProcessMgrName(String processMgrName)
  throws SQLException {
  Connection con = null;
  PreparedStatement prepStmt = null;
  ResultSet rs = null;
  try {
      Collection processes = new ArrayList();
      con = ds.getConnection();
      String selectStatement = "SELECT dbid FROM process "
    + " WHERE processmgr = ? "
    + " ORDER BY dbid ";
      prepStmt = con.prepareStatement(selectStatement);
      prepStmt.setString(1, processMgrName);
      rs = prepStmt.executeQuery();
      while (rs.next()) {
    processes.add(new Long(rs.getLong(1)));
      }
      return processes;
  } finally {
      JDBCUtil.closeAll (rs, prepStmt, con);
  }
    }

    private Collection selectWithoutDefinitionByProcessMgrName
  (String processMgrName)
  throws SQLException {
  Connection con = null;
  PreparedStatement prepStmt = null;
  ResultSet rs = null;
  try {
      Collection processes = new ArrayList();
      con = ds.getConnection();
      String selectStatement = "SELECT dbid FROM process "
    + " WHERE processmgr = ? AND xpdl IS NULL "
    + " ORDER BY dbid ";
      prepStmt = con.prepareStatement(selectStatement);
      prepStmt.setString(1, processMgrName);
      rs = prepStmt.executeQuery();
      while (rs.next()) {
    processes.add(new Long(rs.getLong(1)));
      }
      return processes;
  } finally {
      JDBCUtil.closeAll (rs, prepStmt, con);
  }
    }

    private Collection selectByRequesterHash(int hash) throws SQLException {
  Connection con = null;
  PreparedStatement prepStmt = null;
  ResultSet rs = null;
  try {
      Collection processes = new ArrayList();
      con = ds.getConnection();
      prepStmt = con.prepareStatement
    ("SELECT dbid FROM process WHERE ReqHash = ? ORDER BY dbid");
      prepStmt.setInt(1, hash);
      rs = prepStmt.executeQuery();
      while (rs.next()) {
    processes.add(new Long(rs.getLong(1)));
      }
      return processes;
  } finally {
      JDBCUtil.closeAll (rs, prepStmt, con);
  }
    }

    private Collection selectAll() throws SQLException {
  Connection con = null;
  PreparedStatement prepStmt = null;
  ResultSet rs = null;
  try {
      Collection processes = new ArrayList();
      con = ds.getConnection();
      String selectStatement = "SELECT dbid FROM process "
    + " ORDER BY dbid ";
      prepStmt = con.prepareStatement(selectStatement);
      rs = prepStmt.executeQuery();
      while (rs.next()) {
    processes.add(new Long(rs.getLong(1)));
      }
      return processes;
  } finally {
      JDBCUtil.closeAll (rs, prepStmt, con);
  }
    }

    /**
     * Insert a new process info into the database.
     */
    private void insertProcess()
  throws SQLException, IOException {
  Connection con = null;
  UniversalPrepStmt prepStmt = null;
  try {
      con = ds.getConnection();
      prepStmt = new UniversalPrepStmt
    (ds, con, "INSERT INTO process ("
     + "DBId, PackageId, ProcessId, XPDLREF, Name, State, "
     + "StateTime, Priority"
     + (paRequester != null
        ? ", Requester, ReqHash" : "")
     + ", Creator, ProcessMgr, "
     + "MgrVersion, CreateTime, Description, Flags) "
     + "VALUES (?, ?, ?, ?, ?, ?, ?, ?"
     + (paRequester != null ? ", ?, ?" : "")
     + ", ?, ?, ?, ?, ?, ?)");
      int offset = 1;
      Long procKey = (Long)ctx.getPrimaryKey();
      prepStmt.setLong(offset++, procKey.longValue());
      prepStmt.setString(offset++, packageId);
      prepStmt.setString(offset++, processId);
            prepStmt.setLong(offset++, xpdlRef);
      prepStmt.setString(offset++, paName);
      prepStmt.setString(offset++, paTypedState.toString());
      prepStmt.setTimestamp
    (offset++, new Timestamp (paLastStateTime.getTime()));
      prepStmt.setInt(offset++, paPriority.toInt());
      if (paRequester != null) {
    prepStmt.setBinary(offset++, paRequester);
    prepStmt.setInt (offset++, paRequester.hashCode ());
      }
      prepStmt.setString(offset++, processCreator);
      prepStmt.setString(offset++, paProcessMgrName);
      prepStmt.setString(offset++, paProcessMgrVersion);
      prepStmt.setTimestamp
    (offset++, new Timestamp (paCreateTime.getTime()));
      prepStmt.setString(offset++, paDescription);
      prepStmt.setInt(offset++, paFlags);
      prepStmt.executeUpdate();
      ((ProcessDataStore)paProcessData).setConnection (con);
      try {
    ((PersistentMap)paProcessData).store();
      } catch (PersistentMapSQLException e) {
    throw (SQLException)e.getCause();
      }
      for (Iterator i = CollectionsUtil.modifiedEntries(paBlockDeadlines)
         .iterator (); i.hasNext ();) {
    Long key = (Long)i.next ();
    updateDeadlines (ds, con, (Long)ctx.getPrimaryKey(), key,
         (List)paBlockDeadlines.get(key), false);
      }
  } catch (RemoteException e) {
      throw new EJBException (e);
  } catch (NumberFormatException e) {
      throw new IOException ("Activity key not a long:" + e.getMessage());
  } finally {
      ((ProcessDataStore)paProcessData).setConnection (null);
      JDBCUtil.closeAll (null, prepStmt, con);
  }
    }

    /**
     * Store process info in the database. The
     * transitions of a process does not need
     * to be stored.
     */
    private void storeProcess() throws SQLException, IOException {
  Connection con = null;
  UniversalPrepStmt prepStmt = null;
  try {
      if (persistentAttributeModified) {
    con = ds.getConnection();
    prepStmt = new UniversalPrepStmt
        (ds, con, "UPDATE process SET Name = ?, State = ?, "
         + "StateTime = ?, Priority = ?, Flags = ?"
         + (paDescriptionModified ? ", Description = ? " : "")
         + (myProcDef != null ? ", Xpdl = ? " : "")
         + " WHERE DBId = ? ");
    int offset = 1;
    prepStmt.setString(offset++, paName);
    prepStmt.setString(offset++, paTypedState.toString());
    prepStmt.setTimestamp
        (offset++, new Timestamp (paLastStateTime.getTime()));
    prepStmt.setInt(offset++, paPriority.toInt());
    prepStmt.setInt(offset++, paFlags);
    if (paDescriptionModified) {
        prepStmt.setString(offset++, paDescription);
        paDescriptionModified = false;
    }
    if (myProcDef != null) {
        prepStmt.setLargeString(offset++, myProcDef);
    }
    // for where clause
    prepStmt.setLong
        (offset++, ((Long)ctx.getPrimaryKey()).longValue());
    int rowCount = prepStmt.executeUpdate();
    myProcDef = null;
    if (rowCount == 0) {
        throw new NoSuchEntityException
      ("Storing row for DBId "
       + (Long)ctx.getPrimaryKey() + " failed.");
    }
      }
      if (((ProcessDataStore)paProcessData).isModified ()) {
    if (con == null) {
        con = ds.getConnection();
    }
    ((ProcessDataStore)paProcessData).setConnection (con);
    try {
        ((PersistentMap)paProcessData).store();
    } catch (PersistentMapSQLException e) {
        throw (SQLException)e.getCause();
    }
      }
      if (CollectionsUtil.hasBeenModified(paBlockDeadlines)) {
    if (con == null) {
        con = ds.getConnection();
    }
    for (Iterator i = CollectionsUtil
       .modifiedEntries(paBlockDeadlines).iterator ();
         i.hasNext ();) {
        Long key = (Long)i.next ();
        updateDeadlines (ds, con, (Long)ctx.getPrimaryKey(), key,
             (List)paBlockDeadlines.get(key), true);
    }
      }
            if (updatedTransitions.size() > 0) {
                if (con == null) {
                    con = ds.getConnection();
                }
                updateTransitions (con);
            }
  } catch (RemoteException re) {
      throw new EJBException (re);
  } finally {
      ((ProcessDataStore)paProcessData).setConnection (null);
      JDBCUtil.closeAll (null, prepStmt, con);
  }
  // transitions does not need to be stored
    }

    /**
     * Load process info from the database. Loads also
     * the transitions of the process.
     * After sucessful loading, the process attribute structure
     * is set with the values just loaded.
     */
    private void loadProcess()
  throws SQLException, FinderException, IOException {
  Connection con = null;
  PreparedStatement prepStmt = null;
  ResultSet rs = null;
  try {
      con = ds.getConnection();
      if (haveSelForUp == null) {
    haveSelForUp = new Boolean(JDBCUtil.dbProperties(ds, con)
             .supportsSelectForUpdate ());
      }
      prepStmt = con.prepareStatement
    ("SELECT PackageId, ProcessId, Name, State, StateTime, "
     + "Priority, Requester, Creator, ProcessMgr, MgrVersion, "
     + "CreateTime, Description, Flags, XpdlRef "
     + "FROM process WHERE DBId = ? "
     + (haveSelForUp.booleanValue() ? " FOR UPDATE" : ""));
      Long procKey = (Long)ctx.getPrimaryKey();
      prepStmt.setLong(1, procKey.longValue());
      rs = prepStmt.executeQuery();
      if (!rs.next()) {
    throw new NoSuchEntityException
        ("Row for id " + (Long)ctx.getPrimaryKey()
         + " not found in database.");
      }
      // read process from db
      int offset = 1;
      packageId = rs.getString(offset++);
      processId = rs.getString(offset++);
      paName = rs.getString(offset++);
      paTypedState = State.fromString(rs.getString(offset++));
      paLastStateTime = rs.getTimestamp(offset++);
      paPriority = Priority.fromInt(rs.getInt(offset++));
      paRequester = (WfRequester)JDBCUtil.getBinary(rs, offset++);
      processCreator = rs.getString(offset++);
      paProcessMgrName = rs.getString(offset++);
      paProcessMgrVersion = rs.getString(offset++);
      paCreateTime = rs.getTimestamp(offset++);
      paDescription = rs.getString(offset++);
      paDescriptionModified = false;
      paFlags = rs.getInt(offset++);
      xpdlRef = JDBCUtil.getLong(rs, offset++);
      ((ProcessDataStore)paProcessData).setConnection (con);
      try {
    ((PersistentMap)paProcessData).load ();
      } catch (PersistentMapSQLException e) {
    throw (SQLException)e.getCause();
      }
  } catch (InvalidPriorityException ipe){
      logger.error (ipe.getMessage (), ipe);
      throw new SQLException(ipe.getMessage ());
  } catch (InvalidStateException ise) {
      logger.error (ise.getMessage (), ise);
      throw new SQLException(ise.getMessage ());
  } catch (ClassNotFoundException e) {
      logger.error (e.getMessage (), e);
      throw new SQLException(e.getMessage ());
  } finally {
      ((ProcessDataStore)paProcessData).setConnection (null);
      JDBCUtil.closeAll (rs, prepStmt, con);
  }
    }

    /**
     * Returns the process definition of this process.
     * @return the definition for this process in Document.
     */
    private String loadProcDef() throws SQLException, NamingException {
  Connection con = null;
  PreparedStatement prepStmt = null;
  ResultSet rs = null;
  try {
      con = ds.getConnection();
      String selectStatement
                = "SELECT Xpdl FROM process WHERE DBId = ? ";
      prepStmt = con.prepareStatement(selectStatement);
      prepStmt.setLong(1, ((Long)ctx.getPrimaryKey()).longValue());
      rs = prepStmt.executeQuery();
      if (!rs.next()) {
    throw new NoSuchEntityException
        ("No process with DBId = " + ctx.getPrimaryKey());
      }
      return JDBCUtil.getString(ds, rs, 1);
  } catch (IOException e) {
      throw new EJBException (e);
  } finally {
      try {
    JDBCUtil.closeAll (rs, prepStmt, con);
      } catch (SQLException e) {
    logger.warn (e.getMessage(), e);
      }
  }
    }

    /**
     * Remove process info from the database.
     */
    private void removeProcess()
  throws SQLException, IOException, RemoveException {
  Connection con = null;
  PreparedStatement prepStmt = null;
  try {
            removeActivities();
      long procId = ((Long)ctx.getPrimaryKey()).longValue();
      con = ds.getConnection();
            removeTransitions(con, procId);
      ((ProcessDataStore)paProcessData).setConnection (con);
      paProcessData.clear();
      try {
    ((PersistentMap)paProcessData).store();
      } catch (PersistentMapSQLException e) {
    throw (SQLException)e.getCause();
      }
      String deleteStatement = "DELETE FROM process WHERE DBId = ? ";
      prepStmt = con.prepareStatement(deleteStatement);
      prepStmt.setLong(1, procId);
      prepStmt.executeUpdate();
      prepStmt.close ();
      prepStmt = null;
      prepStmt = new UniversalPrepStmt
    (ds, con, "DELETE FROM Deadline WHERE Process = ?");
      prepStmt.setLong(1, ((Long)ctx.getPrimaryKey()).longValue());
      prepStmt.executeUpdate();
      prepStmt.close ();
      prepStmt = null;
      prepStmt = new UniversalPrepStmt
    (ds, con, "DELETE FROM SimpleAppl WHERE ProcessKey = ?");
      prepStmt.setString(1, ((Long)ctx.getPrimaryKey()).toString());
      prepStmt.executeUpdate();
      if (xpdlRef != null) {
          processDefinitionDirectoryLocal()
              .removeXpdlIfOrphaned(xpdlRef.longValue());
      }
  } finally {
      ((ProcessDataStore)paProcessData).setConnection (null);
      JDBCUtil.closeAll (null, prepStmt, con);
  }
    }

    /**
     * Remove transitions info as associated to the process from the database.
     * @param procKey the process key for to remove the transitions
     * @throws SQLException in case of any SQL errors
     * @throws IOException in case of any IO errors
     */
    private void removeTransitions(Connection con, long procKey)
  throws SQLException, IOException {
  PreparedStatement prepStmt = null;
  try {
      String deleteStatement = "DELETE FROM transition "
            + "WHERE ProcessDBId = ? ";
      prepStmt = con.prepareStatement(deleteStatement);
      prepStmt.setLong(1, procKey);
      prepStmt.executeUpdate();
  } finally {
      JDBCUtil.closeAll (null, prepStmt, null);
  }
    }

    /**
     * Remove activities as associated to the process.
     * @param procKey the process key for to remove the transitions
     * @throws SQLException in case of any SQL errors
     * @throws IOException in case of any IO errors
     */
    private void removeActivities() throws RemoveException, RemoteException {
  // remove the activities from the system
  for (Iterator i = activitiesLocal().iterator(); i.hasNext();){
            EJBLocalObject activity = ((EJBLocalObject)i.next());
      activity.remove();
  }
  activityMapCache = null;
    }
   
    /**
     * Load the deadlines for the activity with the given primary key
     * from the given datasource.<P>
     *
     * This is provided as a static package visible method as it is
     * used by <code>WfActivityEJB</code> as well. The deadline table
     * is considered a shared resource. As a process and its
     * activities operate on a distinct set of deadlines, there is no
     * need for activities to access the deadlines via the process
     * (and we can save the overhead).
     *
     * @param ds the data source to use
     * @param pk the key of the activity
     * @return the list of deadlines
     * @throws SQLException if an error occurs
     * @throws IOException if an error occurs
     */
    static List loadDeadlines(DataSource ds, Long pk)
  throws SQLException, IOException {
  Connection con = null;
  PreparedStatement prepStmt = null;
  ResultSet rs = null;
  try {
      List res = new ArrayList ();
      con = ds.getConnection();
      String selectStatement ="SELECT "
    + "DeadlineCond, ExceptionName, Execution "
    + "FROM Deadline WHERE Activity = ? ORDER BY Entry";
      prepStmt = con.prepareStatement(selectStatement);
      prepStmt.setLong(1, pk.longValue());
      rs = prepStmt.executeQuery();
      while (rs.next()) {
    res.add (new Deadline
       (rs.getInt (3), JDBCUtil.getString(ds, rs, 1),
        rs.getString (2)));
      }
      return res;
  } finally {
      JDBCUtil.closeAll (rs, prepStmt, con);
  }
    }

    /**
     * Updates the deadlines for the activity with the given primary key
     * using the given connection.<P>
     *
     * This is provided as a static package visible method as it is
     * used by <code>WfActivityEJB</code> as well. The deadline table
     * is considered a shared resource. As a process and its
     * activities operate on a distinct set of deadlines, there is no
     * need for activities to access the deadlines via the process
     * (and we can save the overhead).
     *
     * @param ds the data source
     * @param con the connection to the database
     * @param pk the key of the process
     * @param ak the key of the activity
     * @param dls the list of deadlines
     * @param doDelete if old values should be deleted (should only be
     * set to <code>false</code> if updating the deadlines for a newly
     * created activity
     * @throws SQLException if an error occurs
     * @throws IOException if an error occurs
     */
    public static void updateDeadlines
  (DataSource ds, Connection con,
   Long pk, Long ak, List dls, boolean doDelete)
  throws SQLException, IOException {
  UniversalPrepStmt prepStmt = null;
  try {
      if (doDelete) {
    prepStmt = new UniversalPrepStmt
        (ds, con, "DELETE FROM Deadline WHERE Activity = ?");
    prepStmt.setLong(1, ak.longValue());
    prepStmt.executeUpdate();
    prepStmt.close ();
                prepStmt = null;
      }
      if (dls != null && dls.size () > 0) {
    prepStmt = new UniversalPrepStmt
        (ds, con, "INSERT INTO Deadline (Process, Activity, Entry, "
         + "DeadlineCond, ExceptionName, Execution, State) "
         + "VALUES (?, ?, ?, ?, ?, ?, ?)",
         new String[] {"Activity", "Entry"});
    int entry = 0;
    for (Iterator i = dls.iterator(); i.hasNext();) {
        Deadline dl = (Deadline)i.next ();
        int offset = 1;
        prepStmt.setLong(offset++, pk.longValue());
        prepStmt.setLong(offset++, ak.longValue());
        prepStmt.setInt(offset++, entry++);
        prepStmt.setLargeString(offset++, dl.getCondition());
        prepStmt.setString (offset++, dl.getExceptionName());
        prepStmt.setInt(offset++, dl.getExecution());
        prepStmt.setInt(offset++, dl.getState());
        prepStmt.executeUpdate();
    }
      }
  } finally {
      JDBCUtil.closeAll (null, prepStmt, null);
  }
    }

    //
    // Domain methods and helpers
    //

    /**
     * Indicates if some other object is equal to this one. <P>
     *
     * Note that <code>obj</code> must implement the process's
     * remote interface. Comparing a domain object usually
     * indicates some violation of the constraints of our
     * implementation environment.
     *
     * @param obj the object to compare with.
     * @return <code>true</code> if the other object is equal.
     */
    public boolean equals (Object obj) {
  try {
      return ctx.getPrimaryKey().toString()
    .equals (((WfProcess)obj).key());
  } catch (RemoteException e) {
      throw new EJBException (e);
  }
    }

    /**
     *  Return the hash code.<P>
     @return the result.
     */
    public int hashCode () {
        return ctx.getPrimaryKey().toString().hashCode();
    }
   
    /**
     * 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 {
  super.start ();
    }

    /**
     * Returns a collection of activities of the process.
     * @return collection of activities of the process
     */
    public Collection steps() {
  // HashMap.values() returns a Collection implementation that
  // is not serializable. Besides we really do not need to
  // transfer the whole map, so let's create a new
  // collection-container.
        try {
            return new ArrayList (activityMap().values());
        } catch (RemoteException e) {
            throw new EJBException(e);
        }
    }

    /**
     * Returns a collection of activities of the process.
     * @return collection of activities of the process
     */
    public Collection stepsLocal() {
        // HashMap.values() returns a Collection implementation that
        // is not serializable. Besides we really do not need to
        // transfer the whole map, so let's create a new
        // collection-container.
        return new ArrayList (activitiesLocal());
    }

    /**
     * Retrieve the base event information about a requesting activity.
     * @param req the requester.
     * @return the base info using an audit event as DTO.
     */
    protected WfAuditEvent activityRequesterInfo(WfRequester req) {
  if (!(req instanceof ExtActivity)) {
      return null;
  }
  try {
      ExtActivity act = (ExtActivity)req;
      return act.auditEventBase();
  } catch (RemoteException e) {
      throw new EJBException (e);
  }
    }

    /**
     * Set a new name of the process. The change must be forwarded
     * to the activity EJBs as they cache the name.
     * @param newValue new name.
     */
    public void setName (String newValue) {
  super.setName(newValue);
  for (Iterator i = activitiesLocal().iterator(); i.hasNext();) {
      ExtActivityLocal a = (ExtActivityLocal)i.next();
      a.updateProcessName (newValue);
  }
    }

    /**
     * Returns the process manager which created this process.
     * @return the associated process manager.
     */
    public WfProcessMgr manager () {
  ProcessDefinition def = getPaProcessDef();
        try {
            return new ProcessMgrStub
                (def.packageId(), def.processId(), def.mgrName(), key(),
                 processDefinitionDirectoryHome(),
                 (WfProcessHome)ctx.getEJBHome());
        } catch (ResourceNotAvailableException e) {
            throw new EJBException (e);
        }
    }

    /**
     * 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 deferChoiceOnSplit if true, choice is to be deferred
     * @param auditEventSelection the audit event selection
     * @param storeAuditEvents if true, audit events are stored in the
     * database
     * @return the created activity.
     */
    protected de.danet.an.workflow.localcoreapi.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 deferChoiceOnSplit, int auditEventSelection,
   boolean storeAuditEvents) {
  try {
      ExtActivityLocal act = activityLocalHome().create
    ((WfProcessLocal)ctx.getEJBLocalObject(),
                 (Long)ctx.getPrimaryKey(), auditEventBase(),
     blockActId == null ? null : new Long (blockActId),
     priority, name, description, startMode, finishMode,
     joinMode, splitMode, implementation, performer, deadlines,
     deferChoiceOnSplit, auditEventSelection,
     storeAuditEvents);
      return cacheActivityLocal (act);
  } catch (CreateException ce) {
      logger.error (ce.getMessage (), ce);
      throw new IllegalStateException (ce.getMessage());
  } catch (RemoteException re) {
      logger.error (re.getMessage (), re);
      throw new IllegalStateException (re.getMessage());
  }
    }

    /**
     * Provide a new unique activity key.
     *
     * @return the key.
     */
    protected Long createActivityKey () {
  try {
      return new Long(EJBUtil.newPrimaryKey("activity"));
  } catch (ResourceNotAvailableException e) {
      throw new EJBException (e);
  }
    }

    /**
     * Create a new transition with given id, from-activity,
     * to-activity.
     * @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 type of the condition
     * @param condition condition of this transition
     * @return the created transition.
     */
    protected TransitionLocal doCreateTransition
  (String id, String group, int order,
         ActivityLocal fromAct, ActivityLocal toAct,
   int condType, String condition) {
  TransitionLocal trans = new InternalTransitionDefinition
      (this, ((Long)ctx.getPrimaryKey()).toString(),
       id, group, order, fromAct, toAct, condType, condition, false);
  transitionsLocalCache.add(trans);
  String fromKey = fromAct.key();
  if (!transLocalByFrom.containsKey(fromKey)) {
      transLocalByFrom.put(fromKey, new ArrayList());
  }
  ((List)transLocalByFrom.get(fromKey)).add(trans);
  return trans;
    }

    /**
     * Gets a list of transitions for this process.
     * @return list of transitions for this process
     */
    public List transitions() {
  if (transitionsCache == null) {
            List ts = new ArrayList ();
            for (Iterator i = transitionsLocal().iterator(); i.hasNext();) {
                TransitionDefinitionLocal def
                    = (TransitionDefinitionLocal)i.next();
                ts.add (new TransitionDefinition(def));
            }
            transitionsCache = ts;
  }
  return transitionsCache;
    }

    /**
     * Gets a list of transitions for this process.
     * The method is public, because the transition manager needs
     * access to the transitions of a process.
     * @return list of transitions for this process
     */
    public List transitionsLocal() {
        if (transitionsLocalCache == null) {
            try {
                loadTransitions();
            } catch (RemoteException e) {
                throw new EJBException (e);
            } catch (SQLException e) {
                throw new EJBException(e);
            }
        }
        return transitionsLocalCache;
    }
   
    /**
     * Gets a list of transitions for this process that start
     * at the given activity.
     * @return list of transitions for this process
     */
    public List transitionsLocalFrom(String fromActivityKey) {
        if (transLocalByFrom == null) {
            try {
                loadTransitions();
            } catch (RemoteException e) {
                throw new EJBException (e);
            } catch (SQLException e) {
                throw new EJBException(e);
            }
        }
        List res = (List)transLocalByFrom.get(fromActivityKey);
        if (res == null) {
            res = new ArrayList();
        }
        return res;
    }

    /**
     * Return the process definition directory.
     * While there is no immediate relation between a process
     * instance and the known process definitions, the definitions
     * nevertheless make up part of the environment of a process.
     * Specifically, this method is needed to start a sub-process
     * in an activity.
     * @return the process definition directory
     */
    public ProcessDefinitionDirectoryLocal processDefinitionDirectoryLocal () {
  try {
      return getProcessDefinitionDirectoryLocal ();
  } catch (RemoteException e) {
      throw new EJBException (e);
  } catch (NamingException e) {
      throw new EJBException (e);
  } catch (CreateException e) {
      throw new EJBException (e);
  }
    }

    /**
     * Returns the {@link Activity <code>Activity</code>} with the given key.
     * The OMG interface only defines a {@link WfProcess#steps
     * method for listing all the activities} associated with the process.
     * While, of course, one could select the activity with a certain key
     * from that list, this would be rather insufficient.
     * @param key the
     * {@link de.danet.an.workflow.omgcore.WfActivity#key key} of the process.
     * @return the activity associated with the key.
     * @throws InvalidKeyException if no activity with the given key
     * exists.
     */
    public Activity activityByKey (String key)
  throws InvalidKeyException {
        try {
            Activity act = (Activity)activityMap().get(key);
            if (act != null) {
                return act;
            }
        } catch (RemoteException e) {
            throw new EJBException(e);
        }
  throw new InvalidKeyException ("Key " + key + " is invalid.");
    }   
   
    /**
     * Returns the {@link Activity <code>ActivityLocal</code>} with the given
     * key. The OMG interface only defines a {@link WfProcess#steps
     * method for listing all the activities} associated with the process.
     * While, of course, one could select the activity with a certain key
     * from that list, this would be rather insufficient.
     * @param key the
     * {@link de.danet.an.workflow.omgcore.WfActivity#key key} of the process.
     * @return the activity associated with the key.
     * @throws InvalidKeyException if no activity with the given key
     * exists.
     */
    public ActivityLocal activityByKeyLocal (String key)
        throws InvalidKeyException {
        ActivityLocal act = (ActivityLocal)activityLocalMap().get(key);
        if (act != null) {
            return act;
        }
        throw new InvalidKeyException ("Key " + key + " is invalid.");
    }   
   
    /**
     * Loads the transitions of the process from the db.
     * @param con db connection
     * @return list of transitions for this process.
     */
    private void loadTransitions()
  throws SQLException, RemoteException {
  Connection con = null;
  PreparedStatement prepStmt = null;
  ResultSet rs = null;
  try {
      con = ds.getConnection();
      String selectStatement ="SELECT "
    + "TransitionId, FromActivity, ToActivity, TransGroup, "
    + "TransOrder, CondType, TransCond, Flags "
    + "FROM transition WHERE ProcessDBId = ? "
    + "ORDER BY TransOrder ";
      prepStmt = con.prepareStatement(selectStatement);
      Long pk = (Long)ctx.getPrimaryKey();
      prepStmt.setLong(1, pk.longValue());
      rs = prepStmt.executeQuery();
      transitionsLocalCache = new ArrayList();
      transLocalByFrom = new HashMap ();
      String pks = pk.toString();
      while (rs.next()) {
    String id = rs.getString(1);
    Long from = new Long(rs.getLong(2));
    Long to = new Long(rs.getLong(3));
    String group = rs.getString(4);
    int order = rs.getInt(5);
    int condType = rs.getInt(6);
    String condition = JDBCUtil.getString(ds, rs, 7);
                int flags = rs.getInt (8);
    // add transition
                InternalTransitionDefinition transDef
                    = new InternalTransitionDefinition
                        (this, pks, id, group, order,
                         from.longValue() < 0
                         ? blockActivityRepresentation (from.toString())
                         : activityByKeyLocal(from.toString()),
                         activityByKeyLocal(to.toString()), condType, condition,
                         (flags & FLAGS_HAS_PENDING_TOKEN) != 0);
    transitionsLocalCache.add (transDef);
          if (!transLocalByFrom.containsKey(from.toString())) {
              transLocalByFrom.put(from.toString(), new ArrayList());
          }
          ((List)transLocalByFrom.get(from.toString())).add(transDef);
      }
  } catch (IOException e) {
      throw new EJBException (e);
  } catch (InvalidKeyException e) {
      throw new EJBException (e);
  } finally {
      JDBCUtil.closeAll (rs, prepStmt, con);
  }
    }   


    /**
     * insert the transitions into the db.
     */
    private void insertTransitions() throws SQLException {
  Connection con = null;
  UniversalPrepStmt prepStmt = null;
  try {
      con = ds.getConnection();
      Iterator it = transitionsLocalCache.iterator();
      prepStmt = new UniversalPrepStmt
    (ds, con, "INSERT INTO transition ("
     + "TransitionId, ProcessDBId, FromActivity, ToActivity, "
     + "TransGroup, TransOrder, CondType, TransCond) "
     + "VALUES (?, ?, ?, ?, ?, ?, ?, ?)");
      while (it.hasNext()) {
    TransitionLocal t = (TransitionLocal)it.next();
    TransitionDefinitionLocal td = (TransitionDefinitionLocal)t;
    try {
        int offset = 1;
        prepStmt.setString(offset++, t.id());
        prepStmt.setLong(offset++,
             ((Long)ctx.getPrimaryKey()).longValue());
        prepStmt.setLong
      (offset++, Long.parseLong(t.from().key()));
        prepStmt.setLong
      (offset++, Long.parseLong(t.to().key()));
        prepStmt.setString(offset++, td.group());
        prepStmt.setInt(offset++, td.order());
        prepStmt.setInt(offset++, td.conditionType());
        prepStmt.setString(offset++, td.condition());
    } catch (NumberFormatException re) {
        throw new SQLException(re.getMessage());
    }
    prepStmt.executeUpdate();
      }
  } finally {
      try {
    JDBCUtil.closeAll (null, prepStmt, con);
      } catch (SQLException e) {
    logger.error("Error while closing: " + e.getMessage (), e);
      }
  }
    }

    private void updateTransitions (Connection con)
        throws SQLException, RemoteException {
        UniversalPrepStmt prepStmt = null;
        try {
            Iterator it = updatedTransitions.iterator();
            prepStmt = new UniversalPrepStmt
                (ds, con, "UPDATE transition SET Flags = ?"
                     + " WHERE ProcessDBId = ? AND TransitionId = ?");
            while (it.hasNext()) {
                ExtTransitionLocal t = (ExtTransitionLocal)it.next();
                if (logger.isDebugEnabled ()) {
                    logger.debug ("Storing " + t);
                }
                int offset = 1;
                prepStmt.setInt(offset++,
                        ((t.hasPendingToken() ? FLAGS_HAS_PENDING_TOKEN : 0)));
                prepStmt.setLong(offset++,
                        ((Long)ctx.getPrimaryKey()).longValue());
                prepStmt.setString(offset++, t.id());
                prepStmt.executeUpdate();
            }
            updatedTransitions.clear();
        } finally {
            try {
                JDBCUtil.closeAll (null, prepStmt, null);
            } catch (SQLException e) {
                logger.error("Error while closing: " + e.getMessage (), e);
            }
        }
    }
   
    /**
     * Notify about the change of a transition.
     * @param transition the changed transition
     */
    public void transitionUpdated (ExtTransitionLocal transition) {
        updatedTransitions.add (transition);
    }
   
    /**
     * Return the remote version of this object. This is the client side
     * implementation of the remote interface (i.e. the EJB object).
     *
     * @return the remote stub.
     */
    public Process toProcess () {
  return (Process)ctx.getEJBObject();
    }
   
    /* (non-Javadoc)
     * Comment copied from interface or superclass.
     */
    public ExtProcessLocal toProcessLocal() {
        ExtProcessLocal procLocal = (ExtProcessLocal)ctx.getEJBLocalObject();
        return procLocal;
    }

    /**
     * Remove this process.
     * @throws CannotRemoveException if the process is still in progress
     */
    protected void removeThis () throws CannotRemoveException {
  try {
      ctx.getEJBObject().remove();
  } catch (RemoteException e) {
      throw new EJBException (e);
  } catch (RemoveException e) {
      // the process being still in process is the only
      // prossible cause
      throw new CannotRemoveException (e.getMessage ());
  }
    }

    /**
     * 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 ExtActivityLocal lookupActivityLocal (String key)
  throws InvalidKeyException {
  // Search via process to get process into transaction first
  try {
      ProcessLocal proc = ((WfProcessLocalHome)ctx.getEJBLocalHome())
    .findByActivityKey (key);
      return (ExtActivityLocal)proc.activityByKeyLocal(key);
  } catch (FinderException e) {
      logger.debug (e.getMessage (), e);
      throw new InvalidKeyException (e.getMessage());
  }
    }

    /**
     * Return the Principal that created this process.
     * @return the principal.
     * @ejb.interface-method
     * view-type="remote"
     */
    public Principal processCreator () {
  return new SimplePrincipal (processCreator);
    }

    /**
     * Returns an audit event object with process relevant information
     * initialized.
     * @return the process related information.
     * @ejb.interface-method
     * view-type="remote"
     */
    public WfAuditEvent auditEventBase() {
  return auditEventBase(null);
    }

    /**
     * Process newly generated event.
     * @param evt Event
     */
    protected void fireAuditEvent(WfAuditEvent evt) {
  if (delayedEvents != null) {
      delayedEvents.add (evt);
  } else {
      try {
    QueuerUtils.queue
        (queueConnectionFactory(), eventQueue(),
         (DefaultAuditEvent)evt);
      } catch (RemoteException e) {
    throw new EJBException (e);
      }
  }
    }

    /**
     * Returns a collection of <code>WfAuditEvent</code>s associated with
     * this process describing its history.
     * @return the collection of audit events
     * @throws HistoryNotAvailableException in case the history is not
     * available for any reason
     */
    public Collection history()
  throws HistoryNotAvailableException {
  try {
      return engineLocal().history (toProcess());
  } catch (RemoteException rex){
      throw new EJBException (rex);
  }
    }

    /**
     * Deliver a message on the given channel to a receiver tool
     * listening on that channel.
     *
     * @param channel the channel name
     * @param message the message
     * @return <code>true</code> if the mesage could be delivered,
     * <code>false</code> if no activity was listening
     * @throws InvalidDataException if the message contains invalid data
     * @ejb.interface-method view-type="remote"
     */
    public boolean deliverChannelMessage (String channel, Map message)
  throws InvalidDataException {
  ActivityLocal act = null;
  try {
      act = activityLocalHome().findByWaitingOn
    ((Long)ctx.getPrimaryKey(), channel);
      if (logger.isDebugEnabled ()) {
    logger.debug
        ("Delivering message " + CollectionsUtil.toString(message)
         + " from channel " + channel
         + " to waiting activity " + act);
      }
      try {
    act.setResult (new DefaultProcessData (message));
    act.complete ();
      } catch (CannotCompleteException e) {
    logger.error
        ("Activity " + act + " listening on channel "
         + "but cannot be completed?!: " + e.getMessage (), e);
    throw new IllegalStateException
        ("Receiving activity cannot be completed");
      }
      return true;
  } catch (ObjectNotFoundException e) {
      return false;
  } catch (RemoteException rex){
      throw new EJBException (rex);
  } catch (FinderException e) {
      throw new EJBException (e);
  }
    }

    /**
     * Deliver a message on the given channel to a receiver tool
     * listening on that channel. If no tool is listening, store the
     * message.
     *
     * @param channel the channel name
     * @param message the message
     * @throws InvalidDataException if the message contains invalid data
     * @ejb.interface-method view-type="remote"
     */
    public void submitChannelMessage (String channel, Map message)
  throws InvalidDataException {
        if (! deliverChannelMessage (channel, message)) {
            if (logger.isDebugEnabled ()) {
                logger.debug ("Putting message from channel " + channel
                              + " on queue");
            }
            queueChannelMessage (channel, message);
  }
    }
   
    /**
     * Queue the given channel message.
     *
     * @param channel the channel
     * @param message the message
     */
    private void queueChannelMessage (String channel, Map message) {
        QueueConnection qc = null;
        QueueSession qs = null;
        try {
            qc = queueConnectionFactory().createQueueConnection();
            qs = qc.createQueueSession (true, 0);
            QueueSender snd = qs.createSender (channelInQueue());
            snd.setDisableMessageID (true);
            snd.setDisableMessageTimestamp (true);
            ObjectMessage msg = qs.createObjectMessage((Serializable)message);
            String processKey = ((Long)ctx.getPrimaryKey()).toString();
            msg.setStringProperty ("processKey", processKey);
            msg.setStringProperty ("channelName", channel);
            snd.send (msg);
            snd.close ();
        } catch (JMSException e) {
            throw new EJBException (e);
        } catch (RemoteException e) {
            throw new EJBException (e);
        } finally {
            QueuerUtils.release(qc, qs);
        }
    }

    /**
     * Looks for a message on the given channel and if found returns it.
     *
     * @param channel the channel name
     * @return the message or <code>null</code>
     * @ejb.interface-method view-type="remote"
     */
    public Map lookForMessage (String channel) {
        QueueConnection qc = null;
        QueueSession qs = null;
        try {
            qc = queueConnectionFactory().createQueueConnection();
            qc.start();
            qs = qc.createQueueSession (true, 0);
            QueueReceiver rec = qs.createReceiver
                (channelInQueue(), "processKey = '"
                 + ((Long)ctx.getPrimaryKey()).toString() + "'"
                 + " AND channelName = '" + channel + "'");
            Message msg = rec.receiveNoWait();
            Map result = null;
            if (msg != null && (msg instanceof ObjectMessage)) {
                result = (Map)((ObjectMessage)msg).getObject();
            }
            rec.close ();
            return result;
        } catch (JMSException e) {
            throw new EJBException (e);
        } catch (ResourceNotAvailableException e) {
            throw new EJBException (e);
        } finally {
            QueuerUtils.release(qc, qs);
        }
    }

    /**
     * Broadcast the given channel message.
     *
     * @param channel the channel
     * @param message the message
     * @ejb.interface-method view-type="remote"
     */
    public void broadcastChannelMessage (String channel, Map data) {
        TopicConnection tc = null;
        TopicSession ts = null;
        QueueConnection qc = null;
        QueueSession qs = null;
        try {
            tc = topicConnectionFactory().createTopicConnection();
            ts = tc.createTopicSession (true, 0);
            TopicPublisher sndr = ts.createPublisher (channelOutTopic());
            sndr.setDisableMessageID (true);
            ObjectMessage msg
                = ts.createObjectMessage ((Serializable)data);
            String processKey = ((Long)ctx.getPrimaryKey()).toString();
            msg.setStringProperty ("processKey", processKey);
            msg.setStringProperty ("channelName", channel);
            msg.setStringProperty ("messageType", "DATA");
            sndr.publish(msg);
            sndr.close();

            // Now clean up in queue
            qc = queueConnectionFactory().createQueueConnection();
            qs = qc.createQueueSession (true, 0);
            QueueReceiver rec = qs.createReceiver
                (channelInQueue(), "processKey = '" + processKey + "'");
            while (rec.receiveNoWait() != null) {
            }
            rec.close ();
        } catch (JMSException e) {
            throw new EJBException (e);
        } catch (RemoteException e) {
            throw new EJBException (e);
        } finally {
            QueuerUtils.release(tc, ts);
            QueuerUtils.release(qc, qs);
        }
    }
   
    /**
     * Closes all channels.
     */
    protected void closeChannels () {
        TopicConnection tc = null;
        TopicSession ts = null;
        QueueConnection qc = null;
        QueueSession qs = null;
        try {
            tc = topicConnectionFactory().createTopicConnection();
            ts = tc.createTopicSession (true, 0);
            TopicPublisher sndr = ts.createPublisher (channelOutTopic());
            sndr.setDisableMessageID (true);
            Message msg = ts.createMessage();
            String processKey = ((Long)ctx.getPrimaryKey()).toString();
            msg.setStringProperty ("processKey", processKey);
            msg.setStringProperty ("messageType", "CLOSE_NOTIFICATION");
            sndr.publish(msg);
            sndr.close();

            // Now clean up in queue
            qc = queueConnectionFactory().createQueueConnection();
            qs = qc.createQueueSession (true, 0);
            QueueReceiver rec = qs.createReceiver
                (channelInQueue(), "processKey = '" + processKey + "'");
            while (rec.receiveNoWait() != null) {
            }
            rec.close ();
        } catch (JMSException e) {
            throw new EJBException (e);
        } catch (ResourceNotAvailableException e) {
            throw new EJBException (e);
        } finally {
            QueuerUtils.release(tc, ts);
            QueuerUtils.release(qc, qs);
        }
    }

    //
    // Timers
    //

    /**
     * Start a timer that will call <code>handleTimeout</code> at the
     * given date with the given info.
     * @param due target date
     * @param info the context
     */
    public void startTimer (Date due, Serializable info) {
        // Adjust duration
        long duration = due.getTime() - System.currentTimeMillis();
        if (duration <= 0) {
            logger.warn("Deadline with zero or negative duration ("
                        + ((double)duration / 1000) + "s) for "
                        + toString() + " will be triggered immediately.");
            duration = 1;
        }
        if (ctx.getClass().getName().startsWith("org.jboss.ejb.")) {
            // create a timer with interval to circumvent JBoss bug JBAS-2274
            ctx.getTimerService().createTimer (duration, 3600 * 1000, info);
        } else {
            ctx.getTimerService().createTimer (duration, info);
        }
    }

    /**
     * Stop all timers that this object has created.
     */
    public void stopTimers () {
  for (Iterator i = ctx.getTimerService().getTimers().iterator ();
       i.hasNext ();) {
      Timer timer = (Timer)i.next();
      timer.cancel ();
  }
    }

    /**
     * Handle the timeout of a timer.
     * @param timer the timer that has expired.
     */
    public void ejbTimeout (Timer timer) {
        try {
            if (logger.isDebugEnabled()) {
                logger.debug ("Received timeout from " + timer
                              + " for " + toString());
            }
            try {
                handleTimeout (timer.getInfo());
                if (ctx.getClass().getName().startsWith("org.jboss.ejb.")) {
                    // Because of JBoss bug JBAS-2274 we have an interval and
                    // must therefore cancel the timer
                    timer.cancel();
                }
            } catch (NoSuchObjectLocalException e) {
                // JBoss may call this method with a canceled timer
                if (timer.getClass().getName()
                    .equals("org.jboss.ejb.txtimer.TimerImpl")) {
                    logger.debug("Exception \"" + e.getMessage()
                                 + "\" ignored, caused by JBoss bug.");
                    return;
                }
                throw e;
            }
        } catch (EJBException e) {
            throw e;
        } catch (Throwable e) {
            logger.error ("Problem handling timer event " + timer
                          + "(discarded): " + e.getMessage(), e);          
        }
    }
   
    //
    // Implement accessor methods for the persistent attributes
    //

    /**
     * The getter method implementation for the persistent
     * attribute <code>key</code>.
     *
     * @return the value of key.
     */
    protected String getPaKey() {
  return ((Long)ctx.getPrimaryKey()).toString();
    }

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

    /**
     * The getter method implementation for the persistent
     * attribute <code>name</code>.
     *
     * @see #setPaName
     * @return the value of name.
     */
    protected String getPaName() {
  return paName;
    }

    /**
     * The setter method implementation for the persistent
     * attribute <code>name</code>.
     *
     * @param newName the new value of name.
     * @see #getPaName
     */
    protected void setPaName(String newName) {
  paName = newName;
  persistentAttributeModified = true;
    }

    /**
     * The getter method implementation for the persistent
     * attribute <code>description</code>.
     *
     * @see #setPaDescription
     * @return the value of description.
     */
    protected String getPaDescription() {
  return paDescription;
    }

    /**
     * The setter method implementation for the persistent
     * attribute <code>description</code>.
     *
     * @param newDescription the new value of description.
     * @see #getPaDescription
     */
    protected void setPaDescription(String newDescription) {
  paDescription = newDescription;
  paDescriptionModified = true;
  persistentAttributeModified = true;
    }

    /**
     * The getter method implementation for the persistent
     * attribute <code>priority</code>.
     *
     * @see #setPaPriority
     * @return the value of priority.
     */
    protected Priority getPaPriority() {
  return paPriority;
    }

    /**
     * The setter method implementation for the persistent
     * attribute <code>priority</code>.
     *
     * @param newPriority the new value of priority.
     * @see #getPaPriority
     */
    protected void setPaPriority(Priority newPriority) {
  paPriority = newPriority;
  persistentAttributeModified = true;
    }

    /**
     * The getter method implementation for the persistent
     * attribute <code>typedState</code>.
     *
     * @see #setPaTypedState
     * @return the value of typedState.
     */
    protected State getPaTypedState() {
  return paTypedState;
    }

    /**
     * The getter method implementation for the persistent
     * attribute <code>lastStateTime</code>.
     *
     * @see #setPaLastStateTime
     * @return the value of lastStateTime.
     */
    protected Date getPaLastStateTime() {
  return paLastStateTime;
    }

    /**
     * The setter method implementation for the persistent
     * attribute <code>lastStateTime</code>.
     *
     * @param newLastStateTime the new value of lastStateTime.
     * @see #getPaLastStateTime
     */
    protected void setPaLastStateTime(Date newLastStateTime) {
  paLastStateTime = newLastStateTime;
  persistentAttributeModified = true;
    }

    /**
     * The setter method implementation for the persistent
     * attribute <code>typedState</code>.
     *
     * @param newTypedState the new value of typedState.
     * @see #getPaTypedState
     */
    protected void setPaTypedState(State newTypedState) {
  paTypedState = newTypedState;
  persistentAttributeModified = true;
    }

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

    /**
     * The setter method implementation for the persistent
     * attribute <code>processMgrName</code>.
     *
     * @param newProcessMgrName the new value of processMgrName.
     * @see #getPaProcessMgrName
     */
    protected void setPaProcessMgrName(String newProcessMgrName) {
  paProcessMgrName = newProcessMgrName;
  persistentAttributeModified = true;
    }

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

    /**
     * The setter method implementation for the persistent
     * attribute <code>processMgrVersion</code>.
     *
     * @param newProcessMgrVersion the new value of processMgrVersion.
     * @see #getPaProcessMgrVersion
     */
    protected void setPaProcessMgrVersion(String newProcessMgrVersion) {
  paProcessMgrVersion = newProcessMgrVersion;
  persistentAttributeModified = true;
    }

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

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


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

    /**
     * The getter method implementation for the persistent attribute
     * <code>processDef</code>. Three fields in the database
     * contribute to the persistence implementation for the process
     * definition: PackageId, ProcessId and Xpdl. If the
     * process definition for the process has not been changed
     * specifically and if the process definition still exists in the
     * process definition directory, then packageId and processId are
     * used to lookup the process definition.<P>
     *
     * If the definition of the process has changed, e.g. by inserting
     * an additional activity or the process definition has been
     * removed from the process definition directory. the string in
     * field xpdl is used to reconstruct a process definition.
     *
     * @see #setPaProcessDef
     * @return the value of processDef.
     */
    protected ProcessDefinition getPaProcessDef() {
  if (paProcessDef != null) {
      return paProcessDef;
  }
  if (paProcessDefSoft != null) {
      ProcessDefinition pd = (ProcessDefinition)paProcessDefSoft.get();
      if (pd != null) {
          return pd;
      }
  }
  ProcessDefinitionDirectoryLocal pdd = null;
  try {
      String def = loadProcDef();
      if (def != null) {
          ProcessDefinition pd = new DefaultProcessDefinition (def);
          paProcessDefSoft = new SoftReference(pd);
          return pd;
      }
            pdd = getProcessDefinitionDirectoryLocal();
      if (xpdlRef != null) {
          paProcessDef = pdd.lookupArchivedProcessDefinition
              (xpdlRef.longValue());
          return paProcessDef;
      }
      paProcessDef = pdd.lookupProcessDefinition (packageId, processId);
      return paProcessDef;
  } catch (SQLException sex) {
      logger.error (sex.getMessage(), sex);
      throw new ProviderException (sex.getMessage());
  } catch (IOException ioe) {
      logger.error (ioe.getMessage(), ioe);
      throw new ProviderException (ioe.getMessage());
  } catch (ImportException pe) {
      logger.error (pe.getMessage(), pe);
      throw new ProviderException (pe.getMessage());
  } catch (NamingException nex) {
      logger.error (nex.getMessage(), nex);
      throw new ProviderException (nex.getMessage());
  } catch (CreateException cex) {
      logger.error (cex.getMessage(), cex);
      throw new ProviderException (cex.getMessage());
  } catch (InvalidKeyException kex) {
      logger.error (kex.getMessage(), kex);
      throw new IllegalStateException (kex.getMessage());
  } finally {
      EJBUtil.removeSession(pdd);
  }
    }

    /**
     * The setter method implementation for the persistent
     * attribute <code>processDef</code>.
     *
     * @param newProcessDef the new value of processDef.
     * @see #getPaProcessDef
     */
    protected void setPaProcessDef(ProcessDefinition newProcessDef) {
  if (callbackFromInitDuringCreate) {
            paProcessDef = newProcessDef;
  } else {
      paProcessDef = null;
      paProcessDefSoft = new SoftReference(newProcessDef);
            myProcDef = newProcessDef.toXPDL();
  }
  persistentAttributeModified = true;
    }
   
    /**
     * The getter method implementation for the persistent
     * attribute <code>processData</code>.
     *
     * @return the value of processData.
     */
    protected ProcessData getPaProcessData() {
  return paProcessData;
    }

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

    /**
     * The getter method implementation for the persistent
     * attribute <code>debug</code>.
     *
     * @see #setPaDebug
     * @return the value of debug.
     */
    protected boolean getPaDebug() {
  return (paFlags & FLAGS_DEBUG) != 0;
    }

    /**
     * The setter method implementation for the persistent
     * attribute <code>debug</code>.
     *
     * @param newDebug the new value of debug.
     * @see #getPaDebug
     */
    protected void setPaDebug(boolean newDebug) {
  paFlags = (paFlags & FLAGS_DEBUG)
      | (newDebug ? FLAGS_DEBUG : 0);
  persistentAttributeModified = true;
    }

    /**
     * The getter method implementation for the persistent
     * attribute <code>auditEventSelection</code>.
     *
     * @see #setPaAuditEventSelection
     * @return the value of auditEventSelection.
     */
    protected int getPaAuditEventSelection() {
  return (paFlags & FLAGS_AUDIT_SELECTION_MASK)
      >> FLAGS_AUDIT_SELECTION_SHIFT;
    }

    /**
     * The setter method implementation for the persistent
     * attribute <code>auditEventSelection</code>.
     *
     * @param newValue the new value of auditEventSelection.
     * @see #getPaAuditEventSelection
     */
    protected void setPaAuditEventSelection (int newValue) {
  paFlags = (paFlags & ~FLAGS_AUDIT_SELECTION_MASK)
      | (newValue << FLAGS_AUDIT_SELECTION_SHIFT);
  persistentAttributeModified = true;
    }

    /**
     * The getter method implementation for the persistent
     * attribute <code>storeAuditEvents</code>.
     *
     * @see #setPaStoreAuditEvents
     * @return the value of storeAuditEvents.
     */
    protected boolean getPaStoreAuditEvents() {
  return (paFlags & FLAGS_STORE_AUDIT_EVENTS) != 0;
    }

    /**
     * The setter method implementation for the persistent
     * attribute <code>storeAuditEvents</code>.
     *
     * @param newValue the new value of storeAuditEvents.
     * @see #getPaStoreAuditEvents
     */
    protected void setPaStoreAuditEvents(boolean newValue) {
  paFlags = (paFlags & ~FLAGS_STORE_AUDIT_EVENTS)
      | (newValue ? FLAGS_STORE_AUDIT_EVENTS : 0);
  persistentAttributeModified = true;
    }

}
TOP

Related Classes of de.danet.an.workflow.ejbs.core.WfProcessEJB$DeadlineMap

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.