WorkflowEngineEJB.java :  » Workflow-Engines » wfmopen-2.1.1 » de » danet » an » workflow » ejbs » admin » Java Open Source

Java Open Source » Workflow Engines » wfmopen 2.1.1 
wfmopen 2.1.1 » de » danet » an » workflow » ejbs » admin » WorkflowEngineEJB.java
/*
 * 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: WorkflowEngineEJB.java,v 1.30 2007/07/08 20:35:02 mlipp Exp $
 *
 * $Log: WorkflowEngineEJB.java,v $
 * Revision 1.30  2007/07/08 20:35:02  mlipp
 * Even more usage of local EJB interfaces.
 *
 * Revision 1.29  2007/06/10 05:55:30  drmlipp
 * Using local interface for optimization.
 *
 * Revision 1.28  2007/05/03 21:58:25  mlipp
 * Internal refactoring for making better use of local EJBs.
 *
 */
package de.danet.an.workflow.ejbs.admin;

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

import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.Map;

import java.lang.reflect.InvocationTargetException;
import java.rmi.NoSuchObjectException;
import java.rmi.RemoteException;
import java.security.Principal;
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.FinderException;
import javax.ejb.SessionBean;
import javax.ejb.SessionContext;
import javax.ejb.TransactionRolledbackLocalException;
import javax.jms.JMSException;
import javax.jms.ObjectMessage;
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 de.danet.an.util.EJBUtil;
import de.danet.an.util.JDBCUtil;
import de.danet.an.util.ResourceNotAvailableException;
import de.danet.an.util.UniversalPrepStmt;
import de.danet.an.util.persistentmaps.JDBCPersistentMap;
import de.danet.an.util.persistentmaps.PersistentMapSQLException;

import de.danet.an.workflow.localapi.ProcessLocal;
import de.danet.an.workflow.omgcore.CannotCompleteException;
import de.danet.an.workflow.omgcore.HistoryNotAvailableException;
import de.danet.an.workflow.omgcore.InvalidDataException;
import de.danet.an.workflow.omgcore.ProcessData;
import de.danet.an.workflow.omgcore.TransitionNotAllowedException;
import de.danet.an.workflow.omgcore.WfActivity;
import de.danet.an.workflow.omgcore.WfAssignment;
import de.danet.an.workflow.omgcore.WfAssignmentAuditEvent;
import de.danet.an.workflow.omgcore.WfAuditEvent;
import de.danet.an.workflow.omgcore.WfCreateProcessAuditEvent;
import de.danet.an.workflow.omgcore.WfDataAuditEvent;
import de.danet.an.workflow.omgcore.WfExecutionObject;
import de.danet.an.workflow.omgcore.WfProcess;
import de.danet.an.workflow.omgcore.WfResource;
import de.danet.an.workflow.omgcore.WfStateAuditEvent;

import de.danet.an.workflow.api.Activity;
import de.danet.an.workflow.api.Batch;
import de.danet.an.workflow.api.Configuration;
import de.danet.an.workflow.api.DefaultProcessData;
import de.danet.an.workflow.api.InvalidKeyException;
import de.danet.an.workflow.api.NoSuchResourceException;
import de.danet.an.workflow.api.ProcessDefinition;
import de.danet.an.workflow.api.ProcessDirectory;

import de.danet.an.workflow.domain.AbstractActivity;
import de.danet.an.workflow.domain.AbstractProcess;
import de.danet.an.workflow.domain.DefaultAssignmentAuditEvent;
import de.danet.an.workflow.domain.DefaultAuditEvent;
import de.danet.an.workflow.domain.DefaultCreateProcessAuditEvent;
import de.danet.an.workflow.domain.DefaultDataAuditEvent;
import de.danet.an.workflow.domain.DefaultStateAuditEvent;
import de.danet.an.workflow.domain.ImplCompleteAuditEvent;
import de.danet.an.workflow.domain.ToolInvocationFailedAuditEvent;

import de.danet.an.workflow.apix.ExtActivity;
import de.danet.an.workflow.ejbs.WorkflowEngine;
import de.danet.an.workflow.ejbs.util.RedeliveryRequiredException;
import de.danet.an.workflow.internalapi.ExtActivityLocal;
import de.danet.an.workflow.internalapi.ExtApplication;
import de.danet.an.workflow.internalapi.ExtProcessDirectoryLocal;
import de.danet.an.workflow.internalapi.ExtProcessLocal;
import de.danet.an.workflow.internalapi.ToolInvocationException;
import de.danet.an.workflow.internalapi.ExtApplication.InvocationResult;
import de.danet.an.workflow.internalapi.ExtExecutionObjectLocal.RunningState;
import de.danet.an.workflow.spis.aii.ApplicationNotStoppedException;
import de.danet.an.workflow.spis.aii.ToolAgentContext;
import de.danet.an.workflow.spis.aii.ResultProvider.ExceptionResult;
import de.danet.an.workflow.spis.ras.ResourceAssignmentService;
import de.danet.an.workflow.spis.ras.ResourceAssignmentServiceFactory;

/**
 * The session bean <code>WorkflowEngineEJB</code> gives access to the
 * workflow engine. The remote and home interfaces of this EJB are
 * declared in package <code>de.danet.an.workflow.ejbs.util</code> as
 * {@link de.danet.an.workflow.ejbs.WorkflowEngine
 * <code>WorkflowEngine</code>} and {@link
 * de.danet.an.workflow.ejbs.WorkflowEngineHome
 * <code>WorkflowEngineHome</code>} because the interfaces are needed
 * in all sub-packages and putting the interfaces in a subpackage will
 * result in cyclic dependencies. Implementation is in {@link
 * de.danet.an.workflow.ejbs.admin this package}, however, because it
 * relies on other EJBs from this package. <P>
 *
 * Setting log level to debug for this class results in progress
 * messages during event handling, adds stack traces to warning
 * messages and logs the event queue data delivered to clients.
 *
 * @ejbHome <{de.danet.an.workflow.api.ejbhomes.WorkflowEngineHome}>
 * @ejbRemote <{de.danet.an.workflow.ejbs.admin.WorkflowEngine}> 
 *
 * @ejb.bean name="WorkflowEngine" display-name="WorkflowEngine"
 * jndi-name="ejb/@@@_JNDI_Name_Prefix_@@@WorkflowEngine"
 * type="Stateless" transaction-type="Container" view-type="both"
 * @jonas.bean ejb-name="WorkflowEngine" 
 * jndi-name="ejb/@@@_JNDI_Name_Prefix_@@@WorkflowEngine"
 * @ejb.home
 * remote-class="de.danet.an.workflow.ejbs.WorkflowEngineHome"
 * local-class="de.danet.an.workflow.ejbs.admin.WorkflowEngineLocalHome"
 * @ejb.interface remote-class="de.danet.an.workflow.ejbs.WorkflowEngine"
 * local-class="de.danet.an.workflow.ejbs.admin.WorkflowEngineLocal"
 * extends="javax.ejb.EJBObject, de.danet.an.workflow.spis.rms.ResourceAssignmentContext"
 * @ejb.transaction type="Required"
 * @ejb.permission role-name="WfMOpenAdmin"
 * @ejb.ejb-ref ejb-name="ConfigurationBean" view-type="remote"
 * ref-name="ejb/Configuration"
 * @ejb.ejb-ref ejb-name="ProcessDefinitionDirectory" view-type="remote"
 * @ejb.ejb-ref ejb-name="ProcessDirectory" view-type="remote"
 * @ejb.ejb-ref ejb-name="ProcessDirectory" view-type="local"
 * @ejb.ejb-ref ejb-name="WorkflowEngine" view-type="remote"
 * @ejb.ejb-ref ejb-name="Queuer" view-type="local"
 * @ejb.ejb-ref ejb-name="SimpleApplicationDirectory" view-type="local"
 * @ejb.ejb-ref ejb-name="SimpleApplicationDirectory" view-type="remote"
 * @ejb.ejb-ref ejb-name="TimerHandler" view-type="local"
 * ref-name="ejb/TimerHandlerLocal"
 * @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.resource-ref res-ref-name="toolagents/mailtool/Mail"
 * res-type="javax.mail.Session" res-auth="Container"
 * @jonas.resource res-ref-name="toolagents/mailtool/Mail" jndi-name="WfMOpenMail"
 * @weblogic.resource-description res-ref-name="toolagents/mailtool/Mail"
 * jndi-name="WfMOpenMail"
 * @ejb.resource-ref res-ref-name="jms/TCFRemote"
 * res-type="javax.jms.TopicConnectionFactory" res-auth="Application"
 * @jboss.resource-ref res-ref-name="jms/TCFRemote"
 * jndi-name="ConnectionFactory"
 * @jonas.resource res-ref-name="jms/TCFRemote" jndi-name="JCF"
 * @weblogic.resource-description res-ref-name="jms/TCFRemote"
 * jndi-name="weblogic.jms.ConnectionFactory"
 * @ejb.resource-ref res-ref-name="jms/TCF"
 * res-type="javax.jms.TopicConnectionFactory" res-auth="Container"
 * jndi-name="TCF"
 * @jboss.resource-ref res-ref-name="jms/TCF" jndi-name="java:/JmsXA"
 * @jonas.resource res-ref-name="jms/TCF" jndi-name="TCF"
 * @weblogic.resource-description res-ref-name="jms/TCF"
 * jndi-name="weblogic.jms.XAConnectionFactory"
 * @ejb.resource-ref res-ref-name="jms/QCF"
 * res-type="javax.jms.QueueConnectionFactory" res-auth="Container"
 * jndi-name="QCF"
 * @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/EventService"
 * res-type="javax.jms.Topic" res-auth="Container"
 * jndi-name="topic/@@@_JNDI_Name_Prefix_@@@EventService"
 * @jboss.resource-ref res-ref-name="jms/EventService"
 * jndi-name="topic/@@@_JNDI_Name_Prefix_@@@EventService"
 * @jonas.resource res-ref-name="jms/EventService" 
 * jndi-name="topic/@@@_JNDI_Name_Prefix_@@@EventService"
 * @weblogic.resource-description res-ref-name="jms/EventService"
 * jndi-name="topic/@@@_JNDI_Name_Prefix_@@@EventService"
 * @ejb.resource-ref res-ref-name="jms/ChannelIn"
 * res-type="javax.jms.Queue" res-auth="Container"
 * jndi-name="queue/@@@_JNDI_Name_Prefix_@@@ChannelInMessages"
 * @jboss.resource-ref res-ref-name="jms/ChannelIn"
 * jndi-name="queue/@@@_JNDI_Name_Prefix_@@@ChannelInMessages"
 * @jonas.resource 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"
 * jndi-name="topic/@@@_JNDI_Name_Prefix_@@@ChannelOutMessages"
 * @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"
 * @ejb.env-entry type="java.lang.String"
 * name="GlobalConnectionFactoryName" value="null"
 * @ejb.env-entry type="java.lang.String"
 * name="GlobalQueueConnectionFactoryName" value="null"
 * @ejb.env-entry type="java.lang.String"
 * name="GlobalTopicConnectionFactoryName" value="null"
 * @ejb.env-entry type="java.lang.String" name="GlobalEventTopicName" 
 * value="topic/@@@_JNDI_Name_Prefix_@@@EventService"
 * @ejb.env-entry type="java.lang.String" name="GlobalChannelOutTopicName"
 * value="topic/@@@_JNDI_Name_Prefix_@@@ChannelOutMessages"
 * @ejb.env-entry type="java.lang.String"
 * name="de.danet.an.workflow.assignment.assignmentService"
 * value="ejb/@@@_JNDI_Name_Prefix_@@@AssignmentService"
 * @weblogic.transaction-isolation TRANSACTION_READ_COMMITTED
 */
public class WorkflowEngineEJB implements SessionBean {

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

    /** The SessionContext interface of the instance. */
    private SessionContext ctx;

    /**
     * The data source of the database.
     * @see javax.sql.DataSource
     */
    private DataSource ds = null;

    private TopicConnectionFactory topConFac = null;
    private Topic eventService = null;
    private TopicConnection evtSvcCon = null;

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

    /** The cached home interface of the configuration. */
    private ConfigurationHome configurationHomeCache = null;

    /** The cached home interface of the process definition directory. */
    private ProcessDefinitionDirectoryHome processDefinitionDirectoryHomeCache 
  = null;

    /** The cached home interface of the process directory. */
    private ProcessDirectoryHome processDirectoryHomeCache = null;
    private ProcessDirectoryLocalHome processDirectoryLocalHomeCache = null;

    /** The resource assignment service. */
    private static boolean rasNotConfigured = false;
    private ResourceAssignmentService rasCache = null;

    /**
     * This operation method delivers a ConfigurationHome
     * interface.
     * @return home interface of the ConfigurationBean
     */
    private ConfigurationHome configurationHome() 
  throws ResourceNotAvailableException {
  if (configurationHomeCache == null) {
      configurationHomeCache = (ConfigurationHome)EJBUtil.retrieveEJBHome
    (ConfigurationHome.class, "java:comp/env/ejb/Configuration");
  }
  return configurationHomeCache;
    }

    /**
     * This operation method delivers a ProcessDefinitionDirectoryHome
     * interface.
     * @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;
    }

    /**
     * This operation method delivers a ProcessDirectoryHome interface.
     * @return home interface of the ProcessDirectoryBean
     */
    private ProcessDirectoryHome processDirectoryHome() 
  throws ResourceNotAvailableException {
  if (processDirectoryHomeCache == null) {
      processDirectoryHomeCache 
    = (ProcessDirectoryHome)EJBUtil.retrieveEJBHome 
    (ProcessDirectoryHome.class,
     "java:comp/env/ejb/ProcessDirectory");
  }
  return processDirectoryHomeCache;
    }

    /**
     * This operation method delivers a ProcessDirectoryHome interface.
     * @return home interface of the ProcessDirectoryBean
     */
    private ProcessDirectoryLocalHome processDirectoryLocalHome() 
  throws ResourceNotAvailableException {
  if (processDirectoryLocalHomeCache == null) {
      processDirectoryLocalHomeCache 
    = (ProcessDirectoryLocalHome)EJBUtil.retrieveEJBLocalHome 
    (ProcessDirectoryLocalHome.class,
     "java:comp/env/ejb/ProcessDirectoryLocal");
  }
  return processDirectoryLocalHomeCache;
    }

    /**
     * This method returns the cached resource assignment service.
     * @return the resource assignment service
     */
    private ResourceAssignmentService getRas ()
  throws RemoteException {
  if (rasCache == null && !rasNotConfigured) {
            ResourceAssignmentServiceFactory rasf = null;
      try {
    rasf = ResourceAssignmentServiceFactory.newInstance();
      } catch (de.danet.an.workflow.spis.ras.FactoryConfigurationError 
         rae) {
    logger.warn ("No resource assignment service configured. "
                         + "This may be ignored if done intentionally ("
                         + "message from factory: " + rae.getMessage() + ").");
                logger.debug (rae.getMessage(), rae);
    rasNotConfigured = true;
      }
            try {
                rasCache = rasf.newResourceAssignmentService();
            } catch (de.danet.an.workflow.spis.ras.FactoryConfigurationError 
                     rae) {
                logger.error ("Problem creating resource assignment service: "
                              + rae.getMessage(), rae);
                rasNotConfigured = true;
            }
  }
  return rasCache;
    }

    /**
     * Set the session context and get new data source.  
     * @param context the given session context.
     * @throws EJBException if problem encountered with getting the data source.
     */
    public void setSessionContext(SessionContext context) 
  throws EJBException {
        ctx = context;
  configurationHomeCache = null;
  processDefinitionDirectoryHomeCache = null;
  processDirectoryHomeCache = null;
  processDirectoryLocalHomeCache = null;
  rasCache = null;
  try {
      ds = JDBCUtil.refreshDS(null, DB_NAME);
  } catch (NamingException e) {
      throw new EJBException (e);
  }
    }
    
    /**
     * Create a new instance of ProcessDefinitonDirectoryBean.
     * @throws CreateException Throws if the ProcessDefinitionDirectoryBean 
     * can not be created.
     * @ejb.create-method
     * view-type="remote"
     */
    public void ejbCreate() throws CreateException {
  // getting new data source
  try {
      topConFac = (TopicConnectionFactory)
    EJBUtil.lookupJNDIEntry ("java:comp/env/jms/TCF");
      eventService = (Topic)EJBUtil.lookupJNDIEntry 
    ("java:comp/env/jms/EventService");
      evtSvcCon = topConFac.createTopicConnection();
  } catch (NamingException ne) {
      throw new EJBException(ne);
  } catch (JMSException ne) {
      throw new EJBException(ne);
  }
    }

    /**
     * Not called for stateless EJB.
     */
    public void ejbActivate() {
    }

    /**
     * Not called for stateless EJB.
     */
    public void ejbPassivate() {
    }

    /**
     * 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.
     * @see javax.ejb.SessionBean
     */
    public void ejbRemove() {
  try {
      evtSvcCon.close ();
  } catch (JMSException e) {
      logger.error (e.getMessage (), e);
  }
  configurationHomeCache = null;
  processDefinitionDirectoryHomeCache = null;
  processDirectoryHomeCache = null;
  processDirectoryLocalHomeCache = null;
  rasCache = null;
  ds = null;
    }

    //
    // domain methods
    //

    /**
     * Return the workflow engine configuration.
     *
     * @return the configuration.
     * @ejb.interface-method view-type="remote"
     * @ejb.transaction type="Supports"
     */
    public Configuration configuration () {
  try {
      return configurationHome().findByPrimaryKey(new Integer(0)); 
  } catch (FinderException fe) {
      throw new EJBException (fe);
  } catch (RemoteException re) {
      throw new EJBException (re);
  }
    }

    /**
     * Return the event service connection data. Used by
     * <code>StandardWorkflowService</code> only. This allows to
     * configure the data needed by the client in the server
     * environment and thus eases client configuration.
     *
     * @return the data.
     * @ejb.interface-method view-type="remote"
     * @ejb.transaction type="Supports"
     */
    public Object[] eventServiceData () {
  try {
      String conFacName = (String)EJBUtil.retrieveJNDIEntry 
    ("java:comp/env/GlobalConnectionFactoryName");
      String queueConFacName = (String)EJBUtil.retrieveJNDIEntry 
    ("java:comp/env/GlobalQueueConnectionFactoryName");
      String topicConFacName = (String)EJBUtil.retrieveJNDIEntry 
    ("java:comp/env/GlobalTopicConnectionFactoryName");
      String evtQueueName = (String)EJBUtil.retrieveJNDIEntry
    ("java:comp/env/GlobalEventTopicName");
      String chanOutName = (String)EJBUtil.retrieveJNDIEntry
    ("java:comp/env/GlobalChannelOutTopicName");
      if (logger.isDebugEnabled()) {
    logger.debug 
        ("Returning event service data to client: "
         + conFacName + ", " + evtQueueName + ", " + chanOutName);
      }
      return new Object[] { conFacName, queueConFacName, topicConFacName,
          evtQueueName, chanOutName };
  } catch (ResourceNotAvailableException e) {
      throw new EJBException (e);
  }
    }

    /**
     * Return the process definition directory of the workflow engine.
     *
     * @return the process definition directory.
     * @ejb.interface-method view-type="remote"
     * @ejb.transaction type="Supports"
     */
    public de.danet.an.workflow.api.ProcessDefinitionDirectory 
  processDefinitionDirectory () {
  try {
      return processDefinitionDirectoryHome().create();
  } catch (CreateException ce) {
      throw new EJBException (ce);
  } catch (RemoteException re) {
      throw new EJBException (re);
  }
    }

    /**
     * Return the process directory of the workflow engine.
     *
     * @return the process directory.
     * @ejb.interface-method view-type="remote"
     * @ejb.transaction type="Supports"
     */
    public ExtProcessDirectoryLocal processDirectoryLocal () {
  try {
      return processDirectoryLocalHome().create();
  } catch (CreateException ce) {
      throw new EJBException (ce);
  } catch (RemoteException re) {
      throw new EJBException (re);
  }
    }

    /**
     * Return the process directory of the workflow engine.
     *
     * @return the process directory.
     * @ejb.interface-method view-type="remote"
     * @ejb.transaction type="Supports"
     */
    public ProcessDirectory processDirectory () {
  try {
      return processDirectoryHome().create();
  } catch (CreateException ce) {
      throw new EJBException (ce);
  } catch (RemoteException re) {
      throw new EJBException (re);
  }
    }

    /**
     * Return the resource assignment service used by the
     * workflow engine.
     *
     * @return the resource assignment service
     * @ejb.interface-method view-type="local"
     * @ejb.transaction type="Supports"
     */
    public ResourceAssignmentService resourceAssignmentService () {
        try {
            return getRas ();
        } catch (RemoteException e) {
            throw new EJBException (e);
        }
    }

    /**
     * Return the assignments of a given resource.
     *
     * @param resource the resource.
     * @return the collection of assigned work items (instances of
     * {@link de.danet.an.workflow.omgcore.WfAssignment
     * <code>WfAssignment</code>}).
     * @throws RemoteException if a system-level error occurs.
     * @throws NoSuchResourceException if the resource is invalid.
     * As the environment is a concurrent multi user environment, 
     * <code>WfResource</code> objects may become invalid.
     */
    public Collection workItems (WfResource resource)
        throws NoSuchResourceException {
        try {
            return getRas().workItems(resource);
        } catch (RemoteException e) {
            throw new EJBException (e);
        }
    }

    /**
     * Find out if a given assignment belongs to the work items assigned to 
     * a particular resource.
     *
     * @param resource the resource.
     * @param assignment the assignment in question.
     * @return <code>true</code> if the <code>assignment</code> belongs to 
     * the work items of the <code>resource</code>.
     * @throws RemoteException if a system-level error occurs.
     * @throws NoSuchResourceException if the resource is invalid.
     * As the environment is a concurrent multi user environment, 
     * <code>WfResource</code> objects may become invalid.
     */
    public boolean isMemberOfWorkItems (WfResource resource, 
                                 WfAssignment assignment)
        throws NoSuchResourceException {
        try {
            return getRas().isMemberOfWorkItems(resource, assignment);
        } catch (RemoteException e) {
            throw new EJBException (e);
        }
    }
    
    
    /**
     * Return the known resources.
     *
     * @return the known resources
     * @throws RemoteException if a communication error occurs.
     * @ejb.interface-method view-type="remote"
     * @ejb.transaction type="Supports"
     */
    public Collection knownResources ()
  throws RemoteException {
        ResourceAssignmentService ras = getRas();
        if (ras == null) {
            throw new UnsupportedOperationException 
                ("No resource assignment service configured.");
        }
  return ras.knownResources ();
    }

    /**
     * Return the resource associated with the given key.
     *
     * @param key the key
     * @return the resource
     * @throws InvalidKeyException if there is no known resource
     * with this key
     * @throws RemoteException if a communication error occurs.
     * @ejb.interface-method view-type="remote"
     * @ejb.transaction type="Supports"
     */
    public WfResource resourceByKey (String key)
  throws InvalidKeyException, RemoteException {
        ResourceAssignmentService ras = getRas();
        if (ras == null) {
            throw new UnsupportedOperationException 
                ("No resource assignment service configured.");
        }
  return ras.resourceByKey(key);
    }

    /**
     * Return the authorizers for the given resource.
     *
     * @param resource the resource
     * @return the authorizers
     * @throws RemoteException if a communication error occurs.
     * @ejb.interface-method view-type="remote"
     * @ejb.transaction type="Supports"
     */
    public Collection authorizers (WfResource resource) 
  throws RemoteException {
        ResourceAssignmentService ras = getRas();
        if (ras == null) {
            throw new UnsupportedOperationException 
                ("No resource assignment service configured.");
        }
  return ras.authorizers(resource);
    }

    /**
     * Return the resource associated with the given principal.
     *
     * @param principal the principal
     * @return the resource
     * @throws RemoteException if a communication error occurs.
     * @throws InvalidKeyException if there is no known resource
     * associated with this principal
     * @ejb.interface-method view-type="remote"
     * @ejb.transaction type="Supports"
     */
    public WfResource asResource (Principal principal)
  throws RemoteException, InvalidKeyException {
        ResourceAssignmentService ras = getRas();
        if (ras == null) {
            throw new UnsupportedOperationException 
                ("No resource assignment service configured.");
        }
  return ras.asResource(principal);
    }

    /**
     * Returns the user currently authenticated as a <code>Principal</code>.
     * @return the caller principal.
     * @ejb.interface-method view-type="remote"
     * @ejb.transaction type="Supports"
     */
    public Principal caller() {
  return ctx.getCallerPrincipal();
    }

    /**
     * Invoke a tool in a new transaction, i.e. the transaction
     * attribute of this method is set to <code>RequiresNew</code>.<P>
     *
     * The implementation simply calls <code>appl.invoke(act,
     * params)</code>.
     *
     * @param appl the application description of the tool
     * @param act the <code>WfActivity</code>
     * @param params the invocation parameters
     * @return the invocation result
     * @throws ToolInvocationException if execution is not possible
     * @ejb.interface-method view-type="local"
     * @ejb.transaction type="RequiresNew"
     */
    public InvocationResult doInvokeLocal 
  (ExtApplication appl, Activity act, Map params) 
  throws ToolInvocationException {
  try {
      ToolAgentContext tac = new DefaultToolAgentContext 
    ((WorkflowEngine)ctx.getEJBObject(), act, appl.id());
      return appl.invoke(tac, act, params);
  } catch (NoSuchObjectException e) {
      logger.warn 
    ("NoSuchObjectException invoking " + appl + " on " + act
     + " (mapped to ToolInvocationException): " + e.getMessage(),e);
      throw new ToolInvocationException (appl + ": " + e.getMessage());
        } catch (RemoteException e) {
            throw new EJBException (e);
  } catch (RuntimeException e) {
      logger.warn 
    ("RuntimeException invoking " + appl + " on " + act
     + "(mapped to ToolInvocationException): " + e.getMessage(), e);
            // WLS seems to have problems with invoking setRollbackOnly twice
            if (!ctx.getRollbackOnly()) {
                ctx.setRollbackOnly();
            }
      throw new ToolInvocationException (appl + ": " + e.getMessage());
  }
    }

    /**
     * Terminate a tool in a new transaction, i.e. the transaction
     * attribute of this method is set to <code>RequiresNew</code>.<P>
     *
     * The implementation simply calls
     * <code>appl.terminate(act)</code>.
     *
     * @param appl the application description of the tool
     * @param act the <code>WfActivity</code>
     * @throws ApplicationNotStoppedException if termination is not
     * possible
     * @ejb.interface-method view-type="local"
     * @ejb.transaction type="RequiresNew"
     */
    public void doTerminateLocal (ExtApplication appl, Activity act) 
  throws ApplicationNotStoppedException {
  try {
      appl.terminate(act);
  } catch (NoSuchObjectException e) {
      logger.warn 
    ("NoSuchObjectException terminating " + appl + " on " + act
     + " (mapped to ApplicationNotStoppedException): " 
     + e.getMessage(), e);
      throw new ApplicationNotStoppedException
    (appl + ": " + e.getMessage());
        } catch (RemoteException e) {
            throw new EJBException (e);
  } catch (TransactionRolledbackLocalException e) {
      throw e;
  } catch (RuntimeException e) {
      logger.warn 
    ("RuntimeException terminating " + appl + " on " + act
     + "(mapped to ApplicationNotStoppedException): " 
     + e.getMessage(), e);
      throw new ApplicationNotStoppedException
    (appl + ": " + e.getMessage());
  }
    }

    /**
     * Returns a collection of <code>WfAuditEvent</code>s associated with
     * this process describing its history.
     * @param execObj the execution object for which to select the audit 
     * events
     * @return the collection of audit events
     * @throws HistoryNotAvailableException in case the history is not 
     * available for any reason
     * @ejb.interface-method view-type="local"
     * @ejb.transaction type="Supports"
     */
    public Collection history(WfExecutionObject execObj) 
  throws HistoryNotAvailableException {
  try {
            return selectAuditEvents(execObj);
  } catch (SQLException sex) {
      throw new EJBException(sex);
  } catch (NamingException nex) {
      logger.error(nex.getMessage(), nex);
      throw new HistoryNotAvailableException();
  } catch (IOException iex) {
      logger.error(iex.getMessage(), iex);
      throw new HistoryNotAvailableException();
  }
    }

    /**
     * Set a result and complete an activity in a new transaction,
     * i.e. the transaction attribute of this method is set to
     * <code>RequiresNew</code>.<P>
     *
     * @param act the <code>WfActivity</code>
     * @param result the tool's result data. If <code>null</code> do
     * not call <code>setResult</code>.
     * @throws InvalidDataException see {@link
     * de.danet.an.workflow.omgcore.WfActivity#setResult
     * <code>WfActivity.setResult(...)</code>}
     * @throws CannotCompleteException see {@link
     * de.danet.an.workflow.omgcore.WfActivity#complete
     * <code>WfActivity.complete()</code>}
     * @ejb.interface-method view-type="remote"
     * @ejb.transaction type="RequiresNew"
     */
    public void doFinish (WfActivity act, Map result)
  throws InvalidDataException, CannotCompleteException {
        doFinishLocal(act, result);
    }

    /**
     * Set a result and complete an activity in a new transaction,
     * i.e. the transaction attribute of this method is set to
     * <code>RequiresNew</code>.<P>
     *
     * @param act the <code>WfActivity</code>
     * @param result the tool's result data. If <code>null</code> do
     * not call <code>setResult</code>.
     * @throws InvalidDataException see {@link
     * de.danet.an.workflow.omgcore.WfActivity#setResult
     * <code>WfActivity.setResult(...)</code>}
     * @throws CannotCompleteException see {@link
     * de.danet.an.workflow.omgcore.WfActivity#complete
     * <code>WfActivity.complete()</code>}
     * @ejb.interface-method view-type="local"
     * @ejb.transaction type="RequiresNew"
     */
    public void doFinishLocal (WfActivity act, Map result)
  throws InvalidDataException, CannotCompleteException {
  try {
      if (result != null) {
    // Involve process in transaction before activity;
    // else we may get deadlocks. The process is needed by
    // the activity in setResult (container() has
    // transaction attribute NotSupported).
    String pn = act.container().name();
    act.setResult (new DefaultProcessData(result));
      }
      act.complete();
  } catch (NoSuchObjectException e) {
      logger.warn 
    (act + " does not exist any more "
     + "(mapped to CannotCompleteException): " + e.getMessage());
      logger.debug ("Stacktrace:", e);
      throw new CannotCompleteException (act + ": " + e.getMessage());
  } catch (RemoteException e) {
      throw new EJBException (e);
  } catch (RuntimeException e) {
      logger.warn 
    ("RuntimeException while finishing " + act
     + "(mapped to CannotCompleteException): " + e.getMessage());
      logger.debug ("Stacktrace:", e);
      throw new CannotCompleteException (act + ": " + e.getMessage());
  }
    }

    /**
     * Abandon an activity in a new transaction,
     * i.e. the transaction attribute of this method is set to
     * <code>RequiresNew</code>.<P>
     *
     * @param act the <code>WfActivity</code>
     * @param result the exception result to signal
     * @throws TransitionNotAllowedException see {@link
     * de.danet.an.workflow.api.Activity#abandon(String)
     * <code>Activity.abandon()</code>}
     * @ejb.interface-method view-type="remote"
     * @ejb.transaction type="RequiresNew"
     */
    public void doAbandon (Activity act, ExceptionResult result)
        throws TransitionNotAllowedException {
        doAbandonLocal (act, result);
    }

    /**
     * Abandon an activity in a new transaction,
     * i.e. the transaction attribute of this method is set to
     * <code>RequiresNew</code>.<P>
     *
     * @param act the <code>WfActivity</code>
     * @param result the exception result to signal
     * @throws TransitionNotAllowedException see {@link
     * de.danet.an.workflow.api.Activity#abandon(String)
     * <code>Activity.abandon()</code>}
     * @ejb.interface-method view-type="local"
     * @ejb.transaction type="RequiresNew"
     */
    public void doAbandonLocal (Activity act, ExceptionResult result)
  throws TransitionNotAllowedException {
  try {
      ((ExtActivity)act).abandon(result);
  } catch (RemoteException e) {
      throw new EJBException (e);
  }
    }

    /**
     * Process the given event. The transaction attribute of this
     * method is set to <code>RequiresNew</code>, so processing will
     * be done in its own transaction.
     * @param event the event.
     * @throws RedeliveryRequiredException if the event should be redelivered
     * @ejb.interface-method view-type="local"
     * @ejb.transaction type="RequiresNew"
     */
    public void processEvent (WfAuditEvent event) 
  throws RedeliveryRequiredException {
  if (logger.isDebugEnabled()) {
      logger.debug ("Got audit event: " + event);
  }
  try {
      if (!((DefaultAuditEvent)event).skip ()) {
    feedBack (event);
    // feedback may have caused the rollback already
    // without actually throwing an exception
    if (ctx.getRollbackOnly()) {
        return;
    }
      }
      // now log and publish
      if (!(event instanceof ImplCompleteAuditEvent)
    && !(event instanceof ToolInvocationFailedAuditEvent)) {
    int aes = ((DefaultAuditEvent)event).auditEventSelection ();
    // Note that if we have
    // AUDIT_SELECTION_STATE_EVENTS_ONLY, only state
    // events will be processed here, so we do not have
    // to look at the events again.
    if (aes == ProcessDefinition.AUDIT_SELECTION_ALL_EVENTS
        || aes==ProcessDefinition.AUDIT_SELECTION_STATE_EVENTS_ONLY
        || (aes == (ProcessDefinition
        .AUDIT_SELECTION_PROCESS_CLOSED_EVENTS_ONLY)
      && (event.eventType()
          .equals (WfAuditEvent.PROCESS_STATE_CHANGED))
      && (((WfStateAuditEvent)event)
          .newState().startsWith ("closed")))) {
        if (((DefaultAuditEvent)event).store ()) {
      storeAuditEvent(event);
        }
        publishEvent (event);
    }
      }
  } catch (NoSuchObjectException e) {
      // if any of the objects involved doesn't exist any more,
      // something has gone wrong. But we cannot do anything
      // about it (i.e. shouldn't happen).
      logger.error (event + " discarded: " + e.getMessage(), e);
  } catch (RemoteException e) {
      // we assume that this indicates a temporary condition.
      throw new EJBException (e);
  } catch (SQLException e) {
      // we assume that this indicates a temporary condition.
      throw new EJBException (e);
  } catch (RedeliveryRequiredException e) {
      // we pass this one
      throw e;
  } catch (Throwable t) {
      // as we are called from a MDB, it makes no sense
      // to pass the exception to caller.
      logger.error (event + " discarded: " + t.getMessage(), t);
      ctx.setRollbackOnly();
  } finally {
      if (logger.isDebugEnabled()) {
    logger.debug ("Event has been processed: " + event);
      }
  }
    }

    /**
     * Feed the event back to the engine, i.e. to the processes or activities
     * that may be interested in it.
     * @param event the event.
     * @throws RemoteException if a system-level error occurs.
     * @throws RedeliveryRequiredException if the event should be redelivered
     */
    private void feedBack (WfAuditEvent event) 
  throws RemoteException, RedeliveryRequiredException {
  // feed back only those events that we know the receivers to
  // be interested in
  boolean processHandles = AbstractProcess.isHandled (event);
  boolean activityHandles = (event.activityKey() != null 
           && AbstractActivity.isHandled (event));
  if (!(processHandles || activityHandles)) {
      if (logger.isDebugEnabled()) {
    logger.debug ("Not feeding back " + event);
      }
      return;
  }
  ExtProcessDirectoryLocal procDir = null;
  try {
      // Source is not usable internally (remote object). Lookup
            // local object in order to get process and (maybe) activity
            // in transaction.
      procDir = processDirectoryLocal();
      ProcessLocal proc = procDir.lookupProcessLocal 
    (event.processMgrName(), event.processKey());
            ExtActivityLocal actLocal = null;
      if (event.activityKey() != null) {
                actLocal = (ExtActivityLocal)
                    proc.activityByKeyLocal (event.activityKey()); 
      }
      if (activityHandles) {
    actLocal.handleAuditEvent (event);
      }
      if (processHandles) {
    ((ExtProcessLocal)proc).handleAuditEvent (event);
      }
      if (logger.isDebugEnabled()) {
    logger.debug ("Fed back event: " + event);
      }
  } catch (InvalidKeyException e) {
      // There is a race condition between creation of the
      // process and starting it. If both create and start are
      // performed within the same transaction, it may happen
      // that the event generated by the call to start becomes
      // visible before the process, i.e. the data in the
      // database. In this case, the thread processing the event
      // may not find the process.
      if (event instanceof WfStateAuditEvent
    && event.eventType().equals (WfAuditEvent.PROCESS_STATE_CHANGED)
    && (((WfStateAuditEvent)event).newState()
        .equals (RunningState.RUNNING.toString ()))) {
    throw new RedeliveryRequiredException 
        ("Process not found (yet) for " + event);
      }
      // Else if we cannot lookup the process, well then this event
      // is no good.
      logger.warn (event + " discarded, process no longer available.");
  } finally {
      EJBUtil.removeSession(procDir);
  }
    }

    /**
     * Feed the event back to the engine, i.e. to the processes or activities
     * that may be interested in it.
     * @param event the event.
     * @throws RemoteException if a system-level error occurs.
     */
    private void publishEvent (WfAuditEvent event) 
  throws RemoteException {
  ProcessDirectory procDir = null;
  try {
      TopicSession evtSvcSession = evtSvcCon.createTopicSession (true, 0);
      TopicPublisher evtSvcPublisher 
                = evtSvcSession.createPublisher (eventService);
      evtSvcPublisher.setDisableMessageID (true);
      ObjectMessage msg = evtSvcSession.createObjectMessage();
      msg.setStringProperty ("processKey", event.processKey());
      msg.setStringProperty ("eventType", event.eventType());
      msg.setObject ((Serializable)event);
      evtSvcPublisher.publish (msg);
      if (logger.isDebugEnabled ()) {
    logger.debug ("Published event " + event);
      }
      evtSvcPublisher.close ();
      evtSvcSession.close ();
  } catch (JMSException e) {
      logger.error ("Cannot publish "+event+": "+e.getMessage (), e);
  } finally {
      EJBUtil.removeSession(procDir);
  }
    }

    //
    // Helper methods
    //

    /**
     * Stores the given audit event data to the database.
     * @param event the event.
     * @throws SQLException in case of database access problems
     * @throws IOException in case of i/o problems
     */
    private void storeAuditEvent(WfAuditEvent event) 
  throws SQLException, IOException {
  Connection con = null;
  UniversalPrepStmt prepStmt = null;
  try {
      con = ds.getConnection();
      // prepare statement
      prepStmt = new UniversalPrepStmt
    (ds, con, "INSERT INTO AuditEvents " 
    + " (DBId, EventTime, EventType, ActivityKey,"
    + " ActivityName, ProcessKey, ProcessName, ProcessMgrName,"
    + " ProcessMgrVersion, EventData1, EventData2,"
    + " EventData3, EventData4, EventData5)"
    + " VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
      // primary key
      long primKey = EJBUtil.newPrimaryKey("AuditEvents");
      int offset = 1;
      prepStmt.setLong(offset++, primKey);
      
      // misc. data
      setupAuditEventInsertData (event, prepStmt, offset, primKey);
      
      // execute insert statement
      int rowCount = prepStmt.executeUpdate();
      if (rowCount == 0) {
    throw new EJBException
        ("Inserting audit event >>> " + event + " <<< failed.");
      }
      
      // data audit events are stored in different tables
      if (event instanceof WfDataAuditEvent){
    WfDataAuditEvent ev = (WfDataAuditEvent)event;
    storeDataAuditEventProcessData
        (con, primKey, ev.oldData(), ev.newData());
      }
      if (logger.isDebugEnabled ()) {
    logger.debug ("Saved event " + event);
      }
  } catch (ResourceNotAvailableException e) {
      throw new SQLException ("Cannot get primary key" + e.getMessage());
  } finally {
      JDBCUtil.closeAll (null, prepStmt, con);
  }
    }
    
    /**
     * Sets up the entire data fields of the given prepared statement with 
     * values of the given event.
     * @param event the data source event
     * @param prepStmt the insert statement
     * @param offset an offset of the statement's parameter list
     * @param mapId an identifier for a persistant map (in use for data audit 
     * events, only.)
     * @throws SQLException in case of database access problems
     * @throws IOException in case of other i/o problems
     */
    private void setupAuditEventInsertData
  (WfAuditEvent event, UniversalPrepStmt prepStmt, int offset, 
   long recKey)
  throws SQLException, IOException {
  
  // data common to all events
  prepStmt.setTimestamp (offset++, 
             new Timestamp(event.timeStamp().getTime()));
  prepStmt.setString (offset++, event.eventType());
  prepStmt.setString (offset++, event.activityKey());
  prepStmt.setString (offset++, event.activityName());
  prepStmt.setString (offset++, event.processKey());
  prepStmt.setString (offset++, event.processName());
  prepStmt.setString (offset++, event.processMgrName());
  prepStmt.setString (offset++, event.processMgrVersion());
      
  // event specific data
  String eventData1 = null;
  String eventData2 = null;
  String eventData3 = null;
  String eventData4 = null;
  String eventData5 = null;
  if (event instanceof WfAssignmentAuditEvent){
      WfAssignmentAuditEvent ev = (WfAssignmentAuditEvent)event;
      eventData1 = ev.oldResourceKey();
      eventData2 = ev.oldResourceName();
      eventData3 = ev.newResourceKey();
      eventData4 = ev.newResourceName();
  } else if (event instanceof WfCreateProcessAuditEvent){
      WfCreateProcessAuditEvent ev = (WfCreateProcessAuditEvent)event;
      eventData1 = ev.pActivityKey();
      eventData2 = ev.pProcessKey();
      eventData3 = ev.pProcessName();
      eventData4 = ev.pProcessMgrName();
      eventData5 = ev.pProcessMgrVersion();
  } else if (event instanceof WfDataAuditEvent){
      eventData1 = Long.toString (recKey);
  } else if (event instanceof WfStateAuditEvent){
      WfStateAuditEvent ev = (WfStateAuditEvent)event;
      eventData1 = ev.oldState();
      eventData2 = ev.newState();
  }
  prepStmt.setString (offset++, eventData1);
  prepStmt.setString (offset++, eventData2);
  prepStmt.setString (offset++, eventData3);
        prepStmt.setString (offset++, eventData4);
  prepStmt.setString (offset++, eventData5);
    }

    /**
     * Stores process data associated to a data audit event as identifed
     * by the given map id.
     * @param con the database connection to use
     * @param mapId the primary key that identifies the assocaited data 
     * audit event
     * @param oldData the old data 
     * @param newData the new data
     * @throws IOException in case of io problems
     */
    private void storeDataAuditEventProcessData
  (Connection con, long recKey, Map oldData, Map newData)
  throws SQLException, IOException {
  JDBCPersistentMap m = null;
  try {
      Long key = new Long(recKey);
      // old data 
      if (oldData != null){
    m = new JDBCPersistentMap (ds, key, "DataAuditEventOldData");
    m.setConnection(con); 
    m.setMapId (key);
    m.setNewMap ();
    m.putAll(oldData);
    try {
        m.store();
    } catch (PersistentMapSQLException e) {
        throw (SQLException)e.getCause();
    }
    m.setConnection(null);
      }
      
      // new data
      if (newData != null){
    m = new JDBCPersistentMap (ds, key, "DataAuditEventNewData");
    m.setConnection(con); 
    m.setMapId (key);
    m.setNewMap ();
    m.putAll(newData);
    try {
        m.store();
    } catch (PersistentMapSQLException e) {
        throw (SQLException)e.getCause();
    }
    m.setConnection(null);
      }
  } finally {
      m.setConnection(null);
  }
    }

    /**
     * Returns a collection of <code>WfAuditEvent</code>s associated with
     * this process and stored in the database.
     * @param execObj the execution object for which to select the audit 
     * events
     * @return the collection of audit events
     */
    private Collection selectAuditEvents(WfExecutionObject execObj) 
  throws SQLException, IOException, NamingException {
  Connection con = null;
  PreparedStatement prepStmt = null;
  ResultSet rs = null;
  try {
      Collection events = new ArrayList();
      con = ds.getConnection();
            String selectStatement = "SELECT * FROM auditevents ";
            if (execObj instanceof WfProcess){
    selectStatement 
        += " WHERE ProcessKey = ? AND ActivityKey IS NULL";
            } else { 
    selectStatement 
        += " WHERE ActivityKey = ?";
            }
      prepStmt = con.prepareStatement(selectStatement);
      prepStmt.setString(1, execObj.key());
      rs = prepStmt.executeQuery();
      while (rs.next()) {
    events.add(restoreAuditEvent(execObj, con, rs));
      }
      return events;
  } finally {
      JDBCUtil.closeAll (rs, prepStmt, con);
  }
    }
    
    /**
     * Restores an audit event from the given result set (element).
     * @param execObj the execution object for which to select the audit 
     * events
     * @param con a database connection
     * @param rs the result set containing the event information
     * @return the audit event
     * @throws SQLException in case of SQL access problems
     */    
    private WfAuditEvent restoreAuditEvent(WfExecutionObject execObj, 
             Connection con, ResultSet rs) 
  throws SQLException, IOException {
  String eventType = rs.getString(3);
  
        WfAuditEvent eventBase = null; 
  Timestamp ts = rs.getTimestamp(2);
  Date evtTime = new Date (ts.getTime() + ts.getNanos() / 1000000000);
  if (execObj instanceof WfProcess){
      eventBase 
    = new DefaultAuditEvent
    ((WfProcess)execObj, eventType, evtTime, 
     rs.getString(6), rs.getString(7), rs.getString(8), 
     rs.getString(9),
     ProcessDefinition.AUDIT_SELECTION_ALL_EVENTS, false);
  } else { 
      eventBase 
    = new DefaultAuditEvent
    ((WfActivity)execObj, eventType, evtTime, 
     rs.getString(4), rs.getString(5), rs.getString(6), 
     rs.getString(7), rs.getString(8), rs.getString(9),
     ProcessDefinition.AUDIT_SELECTION_ALL_EVENTS, false);
  }
        
        if (eventType.equals(WfAuditEvent.PROCESS_CREATED)){
      return new DefaultCreateProcessAuditEvent
    (eventBase, rs.getString(10), rs.getString(11), 
     rs.getString(12), rs.getString(13), rs.getString(14));
  } else if ((eventType.equals(WfAuditEvent.PROCESS_STATE_CHANGED))
       || (eventType.equals(WfAuditEvent.ACTIVITY_STATE_CHANGED))){
      return new DefaultStateAuditEvent
    (eventBase, rs.getString(10), rs.getString(11));
  } else if ((eventType.equals(WfAuditEvent.PROCESS_CONTEXT_CHANGED))
       ||(eventType.equals(WfAuditEvent.ACTIVITY_CONTEXT_CHANGED))
       ||(eventType.equals(WfAuditEvent.ACTIVITY_RESULT_CHANGED))){
      return new DefaultDataAuditEvent
    (eventBase, 
     new DefaultProcessData
     (selectDataAuditEventData
      (con, rs.getString(10), "DataAuditEventOldData")),
     new DefaultProcessData 
     (selectDataAuditEventData
      (con, rs.getString(10), "DataAuditEventNewData")));
        } else if (eventType.equals(WfAuditEvent.ACTIVITY_ASSIGNMENT_CHANGED)){
      return new DefaultAssignmentAuditEvent
    (eventBase, rs.getString(10), rs.getString(12), 
     rs.getString(11), rs.getString(13));
  } else {
      throw new IllegalStateException
    ("Invalid audit event selected for history");
  }
    }
    
    /**
     * Returns the process data as identified by the given parameters.
     * @param con a database connection
     * @param recKey the process data identifier
     * @param tableName the table name to look for the process data
     * @return the process data 
     * @throws IOException in case of data access problems
     */
    private ProcessData selectDataAuditEventData
  (Connection con, String recKey, String tableName)
  throws SQLException, IOException {
  JDBCPersistentMap m = new JDBCPersistentMap
      (ds, Long.valueOf (recKey), tableName);
  try {
      m.setConnection(con);
      m.load();
      ProcessData data = new DefaultProcessData();
      data.putAll(m); 
      return data;
  } catch (PersistentMapSQLException e) {
      throw (SQLException)e.getCause();
  } finally {
      m.setConnection(null);
  }
    }

    /**
     * Clears the history of the process with the given key.
     * @param procKey the process key.
     * @ejb.interface-method view-type="local"
     */
    public void removeAuditEvents (String procKey) {
  try {
      Connection con = null;
      UniversalPrepStmt prepStmt = null;
      try {
    con = ds.getConnection();
    prepStmt = new UniversalPrepStmt 
        (ds, con, "DELETE FROM DataAuditEventOldData WHERE "
         + "MapId IN (SELECT DBId FROM AuditEvents "
         + "WHERE ProcessKey = ?)");
    prepStmt.setString(1, procKey);
    prepStmt.executeUpdate();
    prepStmt.close ();
    prepStmt = null;

    prepStmt = new UniversalPrepStmt 
        (ds, con, "DELETE FROM DataAuditEventNewData WHERE "
         + "MapId IN (SELECT DBId FROM AuditEvents "
         + "WHERE ProcessKey = ?)");
    prepStmt.setString(1, procKey);
    prepStmt.executeUpdate();
    prepStmt.close ();
    prepStmt = null;

    prepStmt = new UniversalPrepStmt 
        (ds, con, "DELETE FROM AuditEvents WHERE ProcessKey = ?");
    prepStmt.setString(1, procKey);
    prepStmt.executeUpdate();
      } finally {
    JDBCUtil.closeAll(null, prepStmt, con);
      }
  } catch (SQLException e) {
      throw new EJBException (e);
  }
    }
    
    /**
     * Execute a batch in the context of the workflow service
     * i.e. on the server.<P>
     * @param batch the batch to be executed.
     * @return the result.
     * @throws InvocationTargetException wraps exceptions as defined
     * by the implementing class
     * @ejb.interface-method view-type="remote"
     */
    public Object executeBatch (Batch batch) throws InvocationTargetException {
  return batch.execute (new Batch.Context () {
    public boolean isRollbackOnly () {
        return ctx.getRollbackOnly();
    }
      });
    }
}
java2s.com  | Contact Us | Privacy Policy
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.