0001: /*
0002: * This file is part of the WfMOpen project.
0003: * Copyright (C) 2001-2003 Danet GmbH (www.danet.de), GS-AN.
0004: * All rights reserved.
0005: *
0006: * This program is free software; you can redistribute it and/or modify
0007: * it under the terms of the GNU General Public License as published by
0008: * the Free Software Foundation; either version 2 of the License, or
0009: * (at your option) any later version.
0010: *
0011: * This program is distributed in the hope that it will be useful,
0012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
0013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
0014: * GNU General Public License for more details.
0015: *
0016: * You should have received a copy of the GNU General Public License
0017: * along with this program; if not, write to the Free Software
0018: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
0019: *
0020: * $Id: WorkflowEngineEJB.java,v 1.30 2007/07/08 20:35:02 mlipp Exp $
0021: *
0022: * $Log: WorkflowEngineEJB.java,v $
0023: * Revision 1.30 2007/07/08 20:35:02 mlipp
0024: * Even more usage of local EJB interfaces.
0025: *
0026: * Revision 1.29 2007/06/10 05:55:30 drmlipp
0027: * Using local interface for optimization.
0028: *
0029: * Revision 1.28 2007/05/03 21:58:25 mlipp
0030: * Internal refactoring for making better use of local EJBs.
0031: *
0032: */
0033: package de.danet.an.workflow.ejbs.admin;
0034:
0035: import java.io.IOException;
0036: import java.io.Serializable;
0037:
0038: import java.util.ArrayList;
0039: import java.util.Collection;
0040: import java.util.Date;
0041: import java.util.Map;
0042:
0043: import java.lang.reflect.InvocationTargetException;
0044: import java.rmi.NoSuchObjectException;
0045: import java.rmi.RemoteException;
0046: import java.security.Principal;
0047: import java.sql.Connection;
0048: import java.sql.PreparedStatement;
0049: import java.sql.ResultSet;
0050: import java.sql.SQLException;
0051: import java.sql.Timestamp;
0052:
0053: import javax.ejb.CreateException;
0054: import javax.ejb.EJBException;
0055: import javax.ejb.FinderException;
0056: import javax.ejb.SessionBean;
0057: import javax.ejb.SessionContext;
0058: import javax.ejb.TransactionRolledbackLocalException;
0059: import javax.jms.JMSException;
0060: import javax.jms.ObjectMessage;
0061: import javax.jms.Topic;
0062: import javax.jms.TopicConnection;
0063: import javax.jms.TopicConnectionFactory;
0064: import javax.jms.TopicPublisher;
0065: import javax.jms.TopicSession;
0066: import javax.naming.NamingException;
0067: import javax.sql.DataSource;
0068:
0069: import de.danet.an.util.EJBUtil;
0070: import de.danet.an.util.JDBCUtil;
0071: import de.danet.an.util.ResourceNotAvailableException;
0072: import de.danet.an.util.UniversalPrepStmt;
0073: import de.danet.an.util.persistentmaps.JDBCPersistentMap;
0074: import de.danet.an.util.persistentmaps.PersistentMapSQLException;
0075:
0076: import de.danet.an.workflow.localapi.ProcessLocal;
0077: import de.danet.an.workflow.omgcore.CannotCompleteException;
0078: import de.danet.an.workflow.omgcore.HistoryNotAvailableException;
0079: import de.danet.an.workflow.omgcore.InvalidDataException;
0080: import de.danet.an.workflow.omgcore.ProcessData;
0081: import de.danet.an.workflow.omgcore.TransitionNotAllowedException;
0082: import de.danet.an.workflow.omgcore.WfActivity;
0083: import de.danet.an.workflow.omgcore.WfAssignment;
0084: import de.danet.an.workflow.omgcore.WfAssignmentAuditEvent;
0085: import de.danet.an.workflow.omgcore.WfAuditEvent;
0086: import de.danet.an.workflow.omgcore.WfCreateProcessAuditEvent;
0087: import de.danet.an.workflow.omgcore.WfDataAuditEvent;
0088: import de.danet.an.workflow.omgcore.WfExecutionObject;
0089: import de.danet.an.workflow.omgcore.WfProcess;
0090: import de.danet.an.workflow.omgcore.WfResource;
0091: import de.danet.an.workflow.omgcore.WfStateAuditEvent;
0092:
0093: import de.danet.an.workflow.api.Activity;
0094: import de.danet.an.workflow.api.Batch;
0095: import de.danet.an.workflow.api.Configuration;
0096: import de.danet.an.workflow.api.DefaultProcessData;
0097: import de.danet.an.workflow.api.InvalidKeyException;
0098: import de.danet.an.workflow.api.NoSuchResourceException;
0099: import de.danet.an.workflow.api.ProcessDefinition;
0100: import de.danet.an.workflow.api.ProcessDirectory;
0101:
0102: import de.danet.an.workflow.domain.AbstractActivity;
0103: import de.danet.an.workflow.domain.AbstractProcess;
0104: import de.danet.an.workflow.domain.DefaultAssignmentAuditEvent;
0105: import de.danet.an.workflow.domain.DefaultAuditEvent;
0106: import de.danet.an.workflow.domain.DefaultCreateProcessAuditEvent;
0107: import de.danet.an.workflow.domain.DefaultDataAuditEvent;
0108: import de.danet.an.workflow.domain.DefaultStateAuditEvent;
0109: import de.danet.an.workflow.domain.ImplCompleteAuditEvent;
0110: import de.danet.an.workflow.domain.ToolInvocationFailedAuditEvent;
0111:
0112: import de.danet.an.workflow.apix.ExtActivity;
0113: import de.danet.an.workflow.ejbs.WorkflowEngine;
0114: import de.danet.an.workflow.ejbs.util.RedeliveryRequiredException;
0115: import de.danet.an.workflow.internalapi.ExtActivityLocal;
0116: import de.danet.an.workflow.internalapi.ExtApplication;
0117: import de.danet.an.workflow.internalapi.ExtProcessDirectoryLocal;
0118: import de.danet.an.workflow.internalapi.ExtProcessLocal;
0119: import de.danet.an.workflow.internalapi.ToolInvocationException;
0120: import de.danet.an.workflow.internalapi.ExtApplication.InvocationResult;
0121: import de.danet.an.workflow.internalapi.ExtExecutionObjectLocal.RunningState;
0122: import de.danet.an.workflow.spis.aii.ApplicationNotStoppedException;
0123: import de.danet.an.workflow.spis.aii.ToolAgentContext;
0124: import de.danet.an.workflow.spis.aii.ResultProvider.ExceptionResult;
0125: import de.danet.an.workflow.spis.ras.ResourceAssignmentService;
0126: import de.danet.an.workflow.spis.ras.ResourceAssignmentServiceFactory;
0127:
0128: /**
0129: * The session bean <code>WorkflowEngineEJB</code> gives access to the
0130: * workflow engine. The remote and home interfaces of this EJB are
0131: * declared in package <code>de.danet.an.workflow.ejbs.util</code> as
0132: * {@link de.danet.an.workflow.ejbs.WorkflowEngine
0133: * <code>WorkflowEngine</code>} and {@link
0134: * de.danet.an.workflow.ejbs.WorkflowEngineHome
0135: * <code>WorkflowEngineHome</code>} because the interfaces are needed
0136: * in all sub-packages and putting the interfaces in a subpackage will
0137: * result in cyclic dependencies. Implementation is in {@link
0138: * de.danet.an.workflow.ejbs.admin this package}, however, because it
0139: * relies on other EJBs from this package. <P>
0140: *
0141: * Setting log level to debug for this class results in progress
0142: * messages during event handling, adds stack traces to warning
0143: * messages and logs the event queue data delivered to clients.
0144: *
0145: * @ejbHome <{de.danet.an.workflow.api.ejbhomes.WorkflowEngineHome}>
0146: * @ejbRemote <{de.danet.an.workflow.ejbs.admin.WorkflowEngine}>
0147: *
0148: * @ejb.bean name="WorkflowEngine" display-name="WorkflowEngine"
0149: * jndi-name="ejb/@@@_JNDI_Name_Prefix_@@@WorkflowEngine"
0150: * type="Stateless" transaction-type="Container" view-type="both"
0151: * @jonas.bean ejb-name="WorkflowEngine"
0152: * jndi-name="ejb/@@@_JNDI_Name_Prefix_@@@WorkflowEngine"
0153: * @ejb.home
0154: * remote-class="de.danet.an.workflow.ejbs.WorkflowEngineHome"
0155: * local-class="de.danet.an.workflow.ejbs.admin.WorkflowEngineLocalHome"
0156: * @ejb.interface remote-class="de.danet.an.workflow.ejbs.WorkflowEngine"
0157: * local-class="de.danet.an.workflow.ejbs.admin.WorkflowEngineLocal"
0158: * extends="javax.ejb.EJBObject, de.danet.an.workflow.spis.rms.ResourceAssignmentContext"
0159: * @ejb.transaction type="Required"
0160: * @ejb.permission role-name="WfMOpenAdmin"
0161: * @ejb.ejb-ref ejb-name="ConfigurationBean" view-type="remote"
0162: * ref-name="ejb/Configuration"
0163: * @ejb.ejb-ref ejb-name="ProcessDefinitionDirectory" view-type="remote"
0164: * @ejb.ejb-ref ejb-name="ProcessDirectory" view-type="remote"
0165: * @ejb.ejb-ref ejb-name="ProcessDirectory" view-type="local"
0166: * @ejb.ejb-ref ejb-name="WorkflowEngine" view-type="remote"
0167: * @ejb.ejb-ref ejb-name="Queuer" view-type="local"
0168: * @ejb.ejb-ref ejb-name="SimpleApplicationDirectory" view-type="local"
0169: * @ejb.ejb-ref ejb-name="SimpleApplicationDirectory" view-type="remote"
0170: * @ejb.ejb-ref ejb-name="TimerHandler" view-type="local"
0171: * ref-name="ejb/TimerHandlerLocal"
0172: * @ejb.ejb-external-ref ref-name="ejb/JdbcKeyGenLocal" link="KeyGen"
0173: * type="Session" view-type="local" home="de.danet.an.util.KeyGenLocalHome"
0174: * business="de.danet.an.util.KeyGenLocal"
0175: * @ejb.resource-ref res-ref-name="jdbc/WfEngine"
0176: * res-type="javax.sql.DataSource" res-auth="Container"
0177: * @jonas.resource res-ref-name="jdbc/WfEngine" jndi-name="jdbc_1"
0178: * @weblogic.enable-call-by-reference True
0179: * @weblogic.resource-description res-ref-name="jdbc/WfEngine"
0180: * jndi-name="DefaultDS"
0181: * @ejb.resource-ref res-ref-name="toolagents/mailtool/Mail"
0182: * res-type="javax.mail.Session" res-auth="Container"
0183: * @jonas.resource res-ref-name="toolagents/mailtool/Mail" jndi-name="WfMOpenMail"
0184: * @weblogic.resource-description res-ref-name="toolagents/mailtool/Mail"
0185: * jndi-name="WfMOpenMail"
0186: * @ejb.resource-ref res-ref-name="jms/TCFRemote"
0187: * res-type="javax.jms.TopicConnectionFactory" res-auth="Application"
0188: * @jboss.resource-ref res-ref-name="jms/TCFRemote"
0189: * jndi-name="ConnectionFactory"
0190: * @jonas.resource res-ref-name="jms/TCFRemote" jndi-name="JCF"
0191: * @weblogic.resource-description res-ref-name="jms/TCFRemote"
0192: * jndi-name="weblogic.jms.ConnectionFactory"
0193: * @ejb.resource-ref res-ref-name="jms/TCF"
0194: * res-type="javax.jms.TopicConnectionFactory" res-auth="Container"
0195: * jndi-name="TCF"
0196: * @jboss.resource-ref res-ref-name="jms/TCF" jndi-name="java:/JmsXA"
0197: * @jonas.resource res-ref-name="jms/TCF" jndi-name="TCF"
0198: * @weblogic.resource-description res-ref-name="jms/TCF"
0199: * jndi-name="weblogic.jms.XAConnectionFactory"
0200: * @ejb.resource-ref res-ref-name="jms/QCF"
0201: * res-type="javax.jms.QueueConnectionFactory" res-auth="Container"
0202: * jndi-name="QCF"
0203: * @jboss.resource-ref res-ref-name="jms/QCF" jndi-name="java:/JmsXA"
0204: * @jonas.resource res-ref-name="jms/QCF" jndi-name="QCF"
0205: * @weblogic.resource-description res-ref-name="jms/QCF"
0206: * jndi-name="weblogic.jms.XAConnectionFactory"
0207: * @ejb.resource-ref res-ref-name="jms/EventService"
0208: * res-type="javax.jms.Topic" res-auth="Container"
0209: * jndi-name="topic/@@@_JNDI_Name_Prefix_@@@EventService"
0210: * @jboss.resource-ref res-ref-name="jms/EventService"
0211: * jndi-name="topic/@@@_JNDI_Name_Prefix_@@@EventService"
0212: * @jonas.resource res-ref-name="jms/EventService"
0213: * jndi-name="topic/@@@_JNDI_Name_Prefix_@@@EventService"
0214: * @weblogic.resource-description res-ref-name="jms/EventService"
0215: * jndi-name="topic/@@@_JNDI_Name_Prefix_@@@EventService"
0216: * @ejb.resource-ref res-ref-name="jms/ChannelIn"
0217: * res-type="javax.jms.Queue" res-auth="Container"
0218: * jndi-name="queue/@@@_JNDI_Name_Prefix_@@@ChannelInMessages"
0219: * @jboss.resource-ref res-ref-name="jms/ChannelIn"
0220: * jndi-name="queue/@@@_JNDI_Name_Prefix_@@@ChannelInMessages"
0221: * @jonas.resource res-ref-name="jms/ChannelIn"
0222: * jndi-name="queue/@@@_JNDI_Name_Prefix_@@@ChannelInMessages"
0223: * @weblogic.resource-description res-ref-name="jms/ChannelIn"
0224: * jndi-name="queue/@@@_JNDI_Name_Prefix_@@@ChannelInMessages"
0225: * @ejb.resource-ref res-ref-name="jms/ChannelOut"
0226: * res-type="javax.jms.Topic" res-auth="Container"
0227: * jndi-name="topic/@@@_JNDI_Name_Prefix_@@@ChannelOutMessages"
0228: * @jboss.resource-ref res-ref-name="jms/ChannelOut"
0229: * jndi-name="topic/@@@_JNDI_Name_Prefix_@@@ChannelOutMessages"
0230: * @jonas.resource res-ref-name="jms/ChannelOut"
0231: * jndi-name="topic/@@@_JNDI_Name_Prefix_@@@ChannelOutMessages"
0232: * @weblogic.resource-description res-ref-name="jms/ChannelOut"
0233: * jndi-name="topic/@@@_JNDI_Name_Prefix_@@@ChannelOutMessages"
0234: * @ejb.env-entry type="java.lang.String"
0235: * name="GlobalConnectionFactoryName" value="null"
0236: * @ejb.env-entry type="java.lang.String"
0237: * name="GlobalQueueConnectionFactoryName" value="null"
0238: * @ejb.env-entry type="java.lang.String"
0239: * name="GlobalTopicConnectionFactoryName" value="null"
0240: * @ejb.env-entry type="java.lang.String" name="GlobalEventTopicName"
0241: * value="topic/@@@_JNDI_Name_Prefix_@@@EventService"
0242: * @ejb.env-entry type="java.lang.String" name="GlobalChannelOutTopicName"
0243: * value="topic/@@@_JNDI_Name_Prefix_@@@ChannelOutMessages"
0244: * @ejb.env-entry type="java.lang.String"
0245: * name="de.danet.an.workflow.assignment.assignmentService"
0246: * value="ejb/@@@_JNDI_Name_Prefix_@@@AssignmentService"
0247: * @weblogic.transaction-isolation TRANSACTION_READ_COMMITTED
0248: */
0249: public class WorkflowEngineEJB implements SessionBean {
0250:
0251: private static final org.apache.commons.logging.Log logger = org.apache.commons.logging.LogFactory
0252: .getLog(WorkflowEngineEJB.class);
0253:
0254: /** The SessionContext interface of the instance. */
0255: private SessionContext ctx;
0256:
0257: /**
0258: * The data source of the database.
0259: * @see javax.sql.DataSource
0260: */
0261: private DataSource ds = null;
0262:
0263: private TopicConnectionFactory topConFac = null;
0264: private Topic eventService = null;
0265: private TopicConnection evtSvcCon = null;
0266:
0267: /** Database name */
0268: private static final String DB_NAME = "java:comp/env/jdbc/WfEngine";
0269:
0270: /** The cached home interface of the configuration. */
0271: private ConfigurationHome configurationHomeCache = null;
0272:
0273: /** The cached home interface of the process definition directory. */
0274: private ProcessDefinitionDirectoryHome processDefinitionDirectoryHomeCache = null;
0275:
0276: /** The cached home interface of the process directory. */
0277: private ProcessDirectoryHome processDirectoryHomeCache = null;
0278: private ProcessDirectoryLocalHome processDirectoryLocalHomeCache = null;
0279:
0280: /** The resource assignment service. */
0281: private static boolean rasNotConfigured = false;
0282: private ResourceAssignmentService rasCache = null;
0283:
0284: /**
0285: * This operation method delivers a ConfigurationHome
0286: * interface.
0287: * @return home interface of the ConfigurationBean
0288: */
0289: private ConfigurationHome configurationHome()
0290: throws ResourceNotAvailableException {
0291: if (configurationHomeCache == null) {
0292: configurationHomeCache = (ConfigurationHome) EJBUtil
0293: .retrieveEJBHome(ConfigurationHome.class,
0294: "java:comp/env/ejb/Configuration");
0295: }
0296: return configurationHomeCache;
0297: }
0298:
0299: /**
0300: * This operation method delivers a ProcessDefinitionDirectoryHome
0301: * interface.
0302: * @return home interface of the ProcessDefinitionDirectoryBean
0303: */
0304: private ProcessDefinitionDirectoryHome processDefinitionDirectoryHome()
0305: throws ResourceNotAvailableException {
0306: if (processDefinitionDirectoryHomeCache == null) {
0307: processDefinitionDirectoryHomeCache = (ProcessDefinitionDirectoryHome) EJBUtil
0308: .retrieveEJBHome(
0309: ProcessDefinitionDirectoryHome.class,
0310: "java:comp/env/ejb/ProcessDefinitionDirectory");
0311: }
0312: return processDefinitionDirectoryHomeCache;
0313: }
0314:
0315: /**
0316: * This operation method delivers a ProcessDirectoryHome interface.
0317: * @return home interface of the ProcessDirectoryBean
0318: */
0319: private ProcessDirectoryHome processDirectoryHome()
0320: throws ResourceNotAvailableException {
0321: if (processDirectoryHomeCache == null) {
0322: processDirectoryHomeCache = (ProcessDirectoryHome) EJBUtil
0323: .retrieveEJBHome(ProcessDirectoryHome.class,
0324: "java:comp/env/ejb/ProcessDirectory");
0325: }
0326: return processDirectoryHomeCache;
0327: }
0328:
0329: /**
0330: * This operation method delivers a ProcessDirectoryHome interface.
0331: * @return home interface of the ProcessDirectoryBean
0332: */
0333: private ProcessDirectoryLocalHome processDirectoryLocalHome()
0334: throws ResourceNotAvailableException {
0335: if (processDirectoryLocalHomeCache == null) {
0336: processDirectoryLocalHomeCache = (ProcessDirectoryLocalHome) EJBUtil
0337: .retrieveEJBLocalHome(
0338: ProcessDirectoryLocalHome.class,
0339: "java:comp/env/ejb/ProcessDirectoryLocal");
0340: }
0341: return processDirectoryLocalHomeCache;
0342: }
0343:
0344: /**
0345: * This method returns the cached resource assignment service.
0346: * @return the resource assignment service
0347: */
0348: private ResourceAssignmentService getRas() throws RemoteException {
0349: if (rasCache == null && !rasNotConfigured) {
0350: ResourceAssignmentServiceFactory rasf = null;
0351: try {
0352: rasf = ResourceAssignmentServiceFactory.newInstance();
0353: } catch (de.danet.an.workflow.spis.ras.FactoryConfigurationError rae) {
0354: logger
0355: .warn("No resource assignment service configured. "
0356: + "This may be ignored if done intentionally ("
0357: + "message from factory: "
0358: + rae.getMessage() + ").");
0359: logger.debug(rae.getMessage(), rae);
0360: rasNotConfigured = true;
0361: }
0362: try {
0363: rasCache = rasf.newResourceAssignmentService();
0364: } catch (de.danet.an.workflow.spis.ras.FactoryConfigurationError rae) {
0365: logger.error(
0366: "Problem creating resource assignment service: "
0367: + rae.getMessage(), rae);
0368: rasNotConfigured = true;
0369: }
0370: }
0371: return rasCache;
0372: }
0373:
0374: /**
0375: * Set the session context and get new data source.
0376: * @param context the given session context.
0377: * @throws EJBException if problem encountered with getting the data source.
0378: */
0379: public void setSessionContext(SessionContext context)
0380: throws EJBException {
0381: ctx = context;
0382: configurationHomeCache = null;
0383: processDefinitionDirectoryHomeCache = null;
0384: processDirectoryHomeCache = null;
0385: processDirectoryLocalHomeCache = null;
0386: rasCache = null;
0387: try {
0388: ds = JDBCUtil.refreshDS(null, DB_NAME);
0389: } catch (NamingException e) {
0390: throw new EJBException(e);
0391: }
0392: }
0393:
0394: /**
0395: * Create a new instance of ProcessDefinitonDirectoryBean.
0396: * @throws CreateException Throws if the ProcessDefinitionDirectoryBean
0397: * can not be created.
0398: * @ejb.create-method
0399: * view-type="remote"
0400: */
0401: public void ejbCreate() throws CreateException {
0402: // getting new data source
0403: try {
0404: topConFac = (TopicConnectionFactory) EJBUtil
0405: .lookupJNDIEntry("java:comp/env/jms/TCF");
0406: eventService = (Topic) EJBUtil
0407: .lookupJNDIEntry("java:comp/env/jms/EventService");
0408: evtSvcCon = topConFac.createTopicConnection();
0409: } catch (NamingException ne) {
0410: throw new EJBException(ne);
0411: } catch (JMSException ne) {
0412: throw new EJBException(ne);
0413: }
0414: }
0415:
0416: /**
0417: * Not called for stateless EJB.
0418: */
0419: public void ejbActivate() {
0420: }
0421:
0422: /**
0423: * Not called for stateless EJB.
0424: */
0425: public void ejbPassivate() {
0426: }
0427:
0428: /**
0429: * A container invokes this method before it ends the life of the session
0430: * object. This happens as a result of a client's invoking a remove
0431: * operation, or when a container decides to terminate the session object
0432: * after a timeout.
0433: * @see javax.ejb.SessionBean
0434: */
0435: public void ejbRemove() {
0436: try {
0437: evtSvcCon.close();
0438: } catch (JMSException e) {
0439: logger.error(e.getMessage(), e);
0440: }
0441: configurationHomeCache = null;
0442: processDefinitionDirectoryHomeCache = null;
0443: processDirectoryHomeCache = null;
0444: processDirectoryLocalHomeCache = null;
0445: rasCache = null;
0446: ds = null;
0447: }
0448:
0449: //
0450: // domain methods
0451: //
0452:
0453: /**
0454: * Return the workflow engine configuration.
0455: *
0456: * @return the configuration.
0457: * @ejb.interface-method view-type="remote"
0458: * @ejb.transaction type="Supports"
0459: */
0460: public Configuration configuration() {
0461: try {
0462: return configurationHome().findByPrimaryKey(new Integer(0));
0463: } catch (FinderException fe) {
0464: throw new EJBException(fe);
0465: } catch (RemoteException re) {
0466: throw new EJBException(re);
0467: }
0468: }
0469:
0470: /**
0471: * Return the event service connection data. Used by
0472: * <code>StandardWorkflowService</code> only. This allows to
0473: * configure the data needed by the client in the server
0474: * environment and thus eases client configuration.
0475: *
0476: * @return the data.
0477: * @ejb.interface-method view-type="remote"
0478: * @ejb.transaction type="Supports"
0479: */
0480: public Object[] eventServiceData() {
0481: try {
0482: String conFacName = (String) EJBUtil
0483: .retrieveJNDIEntry("java:comp/env/GlobalConnectionFactoryName");
0484: String queueConFacName = (String) EJBUtil
0485: .retrieveJNDIEntry("java:comp/env/GlobalQueueConnectionFactoryName");
0486: String topicConFacName = (String) EJBUtil
0487: .retrieveJNDIEntry("java:comp/env/GlobalTopicConnectionFactoryName");
0488: String evtQueueName = (String) EJBUtil
0489: .retrieveJNDIEntry("java:comp/env/GlobalEventTopicName");
0490: String chanOutName = (String) EJBUtil
0491: .retrieveJNDIEntry("java:comp/env/GlobalChannelOutTopicName");
0492: if (logger.isDebugEnabled()) {
0493: logger.debug("Returning event service data to client: "
0494: + conFacName + ", " + evtQueueName + ", "
0495: + chanOutName);
0496: }
0497: return new Object[] { conFacName, queueConFacName,
0498: topicConFacName, evtQueueName, chanOutName };
0499: } catch (ResourceNotAvailableException e) {
0500: throw new EJBException(e);
0501: }
0502: }
0503:
0504: /**
0505: * Return the process definition directory of the workflow engine.
0506: *
0507: * @return the process definition directory.
0508: * @ejb.interface-method view-type="remote"
0509: * @ejb.transaction type="Supports"
0510: */
0511: public de.danet.an.workflow.api.ProcessDefinitionDirectory processDefinitionDirectory() {
0512: try {
0513: return processDefinitionDirectoryHome().create();
0514: } catch (CreateException ce) {
0515: throw new EJBException(ce);
0516: } catch (RemoteException re) {
0517: throw new EJBException(re);
0518: }
0519: }
0520:
0521: /**
0522: * Return the process directory of the workflow engine.
0523: *
0524: * @return the process directory.
0525: * @ejb.interface-method view-type="remote"
0526: * @ejb.transaction type="Supports"
0527: */
0528: public ExtProcessDirectoryLocal processDirectoryLocal() {
0529: try {
0530: return processDirectoryLocalHome().create();
0531: } catch (CreateException ce) {
0532: throw new EJBException(ce);
0533: } catch (RemoteException re) {
0534: throw new EJBException(re);
0535: }
0536: }
0537:
0538: /**
0539: * Return the process directory of the workflow engine.
0540: *
0541: * @return the process directory.
0542: * @ejb.interface-method view-type="remote"
0543: * @ejb.transaction type="Supports"
0544: */
0545: public ProcessDirectory processDirectory() {
0546: try {
0547: return processDirectoryHome().create();
0548: } catch (CreateException ce) {
0549: throw new EJBException(ce);
0550: } catch (RemoteException re) {
0551: throw new EJBException(re);
0552: }
0553: }
0554:
0555: /**
0556: * Return the resource assignment service used by the
0557: * workflow engine.
0558: *
0559: * @return the resource assignment service
0560: * @ejb.interface-method view-type="local"
0561: * @ejb.transaction type="Supports"
0562: */
0563: public ResourceAssignmentService resourceAssignmentService() {
0564: try {
0565: return getRas();
0566: } catch (RemoteException e) {
0567: throw new EJBException(e);
0568: }
0569: }
0570:
0571: /**
0572: * Return the assignments of a given resource.
0573: *
0574: * @param resource the resource.
0575: * @return the collection of assigned work items (instances of
0576: * {@link de.danet.an.workflow.omgcore.WfAssignment
0577: * <code>WfAssignment</code>}).
0578: * @throws RemoteException if a system-level error occurs.
0579: * @throws NoSuchResourceException if the resource is invalid.
0580: * As the environment is a concurrent multi user environment,
0581: * <code>WfResource</code> objects may become invalid.
0582: */
0583: public Collection workItems(WfResource resource)
0584: throws NoSuchResourceException {
0585: try {
0586: return getRas().workItems(resource);
0587: } catch (RemoteException e) {
0588: throw new EJBException(e);
0589: }
0590: }
0591:
0592: /**
0593: * Find out if a given assignment belongs to the work items assigned to
0594: * a particular resource.
0595: *
0596: * @param resource the resource.
0597: * @param assignment the assignment in question.
0598: * @return <code>true</code> if the <code>assignment</code> belongs to
0599: * the work items of the <code>resource</code>.
0600: * @throws RemoteException if a system-level error occurs.
0601: * @throws NoSuchResourceException if the resource is invalid.
0602: * As the environment is a concurrent multi user environment,
0603: * <code>WfResource</code> objects may become invalid.
0604: */
0605: public boolean isMemberOfWorkItems(WfResource resource,
0606: WfAssignment assignment) throws NoSuchResourceException {
0607: try {
0608: return getRas().isMemberOfWorkItems(resource, assignment);
0609: } catch (RemoteException e) {
0610: throw new EJBException(e);
0611: }
0612: }
0613:
0614: /**
0615: * Return the known resources.
0616: *
0617: * @return the known resources
0618: * @throws RemoteException if a communication error occurs.
0619: * @ejb.interface-method view-type="remote"
0620: * @ejb.transaction type="Supports"
0621: */
0622: public Collection knownResources() throws RemoteException {
0623: ResourceAssignmentService ras = getRas();
0624: if (ras == null) {
0625: throw new UnsupportedOperationException(
0626: "No resource assignment service configured.");
0627: }
0628: return ras.knownResources();
0629: }
0630:
0631: /**
0632: * Return the resource associated with the given key.
0633: *
0634: * @param key the key
0635: * @return the resource
0636: * @throws InvalidKeyException if there is no known resource
0637: * with this key
0638: * @throws RemoteException if a communication error occurs.
0639: * @ejb.interface-method view-type="remote"
0640: * @ejb.transaction type="Supports"
0641: */
0642: public WfResource resourceByKey(String key)
0643: throws InvalidKeyException, RemoteException {
0644: ResourceAssignmentService ras = getRas();
0645: if (ras == null) {
0646: throw new UnsupportedOperationException(
0647: "No resource assignment service configured.");
0648: }
0649: return ras.resourceByKey(key);
0650: }
0651:
0652: /**
0653: * Return the authorizers for the given resource.
0654: *
0655: * @param resource the resource
0656: * @return the authorizers
0657: * @throws RemoteException if a communication error occurs.
0658: * @ejb.interface-method view-type="remote"
0659: * @ejb.transaction type="Supports"
0660: */
0661: public Collection authorizers(WfResource resource)
0662: throws RemoteException {
0663: ResourceAssignmentService ras = getRas();
0664: if (ras == null) {
0665: throw new UnsupportedOperationException(
0666: "No resource assignment service configured.");
0667: }
0668: return ras.authorizers(resource);
0669: }
0670:
0671: /**
0672: * Return the resource associated with the given principal.
0673: *
0674: * @param principal the principal
0675: * @return the resource
0676: * @throws RemoteException if a communication error occurs.
0677: * @throws InvalidKeyException if there is no known resource
0678: * associated with this principal
0679: * @ejb.interface-method view-type="remote"
0680: * @ejb.transaction type="Supports"
0681: */
0682: public WfResource asResource(Principal principal)
0683: throws RemoteException, InvalidKeyException {
0684: ResourceAssignmentService ras = getRas();
0685: if (ras == null) {
0686: throw new UnsupportedOperationException(
0687: "No resource assignment service configured.");
0688: }
0689: return ras.asResource(principal);
0690: }
0691:
0692: /**
0693: * Returns the user currently authenticated as a <code>Principal</code>.
0694: * @return the caller principal.
0695: * @ejb.interface-method view-type="remote"
0696: * @ejb.transaction type="Supports"
0697: */
0698: public Principal caller() {
0699: return ctx.getCallerPrincipal();
0700: }
0701:
0702: /**
0703: * Invoke a tool in a new transaction, i.e. the transaction
0704: * attribute of this method is set to <code>RequiresNew</code>.<P>
0705: *
0706: * The implementation simply calls <code>appl.invoke(act,
0707: * params)</code>.
0708: *
0709: * @param appl the application description of the tool
0710: * @param act the <code>WfActivity</code>
0711: * @param params the invocation parameters
0712: * @return the invocation result
0713: * @throws ToolInvocationException if execution is not possible
0714: * @ejb.interface-method view-type="local"
0715: * @ejb.transaction type="RequiresNew"
0716: */
0717: public InvocationResult doInvokeLocal(ExtApplication appl,
0718: Activity act, Map params) throws ToolInvocationException {
0719: try {
0720: ToolAgentContext tac = new DefaultToolAgentContext(
0721: (WorkflowEngine) ctx.getEJBObject(), act, appl.id());
0722: return appl.invoke(tac, act, params);
0723: } catch (NoSuchObjectException e) {
0724: logger.warn("NoSuchObjectException invoking " + appl
0725: + " on " + act
0726: + " (mapped to ToolInvocationException): "
0727: + e.getMessage(), e);
0728: throw new ToolInvocationException(appl + ": "
0729: + e.getMessage());
0730: } catch (RemoteException e) {
0731: throw new EJBException(e);
0732: } catch (RuntimeException e) {
0733: logger.warn("RuntimeException invoking " + appl + " on "
0734: + act + "(mapped to ToolInvocationException): "
0735: + e.getMessage(), e);
0736: // WLS seems to have problems with invoking setRollbackOnly twice
0737: if (!ctx.getRollbackOnly()) {
0738: ctx.setRollbackOnly();
0739: }
0740: throw new ToolInvocationException(appl + ": "
0741: + e.getMessage());
0742: }
0743: }
0744:
0745: /**
0746: * Terminate a tool in a new transaction, i.e. the transaction
0747: * attribute of this method is set to <code>RequiresNew</code>.<P>
0748: *
0749: * The implementation simply calls
0750: * <code>appl.terminate(act)</code>.
0751: *
0752: * @param appl the application description of the tool
0753: * @param act the <code>WfActivity</code>
0754: * @throws ApplicationNotStoppedException if termination is not
0755: * possible
0756: * @ejb.interface-method view-type="local"
0757: * @ejb.transaction type="RequiresNew"
0758: */
0759: public void doTerminateLocal(ExtApplication appl, Activity act)
0760: throws ApplicationNotStoppedException {
0761: try {
0762: appl.terminate(act);
0763: } catch (NoSuchObjectException e) {
0764: logger.warn("NoSuchObjectException terminating " + appl
0765: + " on " + act
0766: + " (mapped to ApplicationNotStoppedException): "
0767: + e.getMessage(), e);
0768: throw new ApplicationNotStoppedException(appl + ": "
0769: + e.getMessage());
0770: } catch (RemoteException e) {
0771: throw new EJBException(e);
0772: } catch (TransactionRolledbackLocalException e) {
0773: throw e;
0774: } catch (RuntimeException e) {
0775: logger.warn("RuntimeException terminating " + appl + " on "
0776: + act
0777: + "(mapped to ApplicationNotStoppedException): "
0778: + e.getMessage(), e);
0779: throw new ApplicationNotStoppedException(appl + ": "
0780: + e.getMessage());
0781: }
0782: }
0783:
0784: /**
0785: * Returns a collection of <code>WfAuditEvent</code>s associated with
0786: * this process describing its history.
0787: * @param execObj the execution object for which to select the audit
0788: * events
0789: * @return the collection of audit events
0790: * @throws HistoryNotAvailableException in case the history is not
0791: * available for any reason
0792: * @ejb.interface-method view-type="local"
0793: * @ejb.transaction type="Supports"
0794: */
0795: public Collection history(WfExecutionObject execObj)
0796: throws HistoryNotAvailableException {
0797: try {
0798: return selectAuditEvents(execObj);
0799: } catch (SQLException sex) {
0800: throw new EJBException(sex);
0801: } catch (NamingException nex) {
0802: logger.error(nex.getMessage(), nex);
0803: throw new HistoryNotAvailableException();
0804: } catch (IOException iex) {
0805: logger.error(iex.getMessage(), iex);
0806: throw new HistoryNotAvailableException();
0807: }
0808: }
0809:
0810: /**
0811: * Set a result and complete an activity in a new transaction,
0812: * i.e. the transaction attribute of this method is set to
0813: * <code>RequiresNew</code>.<P>
0814: *
0815: * @param act the <code>WfActivity</code>
0816: * @param result the tool's result data. If <code>null</code> do
0817: * not call <code>setResult</code>.
0818: * @throws InvalidDataException see {@link
0819: * de.danet.an.workflow.omgcore.WfActivity#setResult
0820: * <code>WfActivity.setResult(...)</code>}
0821: * @throws CannotCompleteException see {@link
0822: * de.danet.an.workflow.omgcore.WfActivity#complete
0823: * <code>WfActivity.complete()</code>}
0824: * @ejb.interface-method view-type="remote"
0825: * @ejb.transaction type="RequiresNew"
0826: */
0827: public void doFinish(WfActivity act, Map result)
0828: throws InvalidDataException, CannotCompleteException {
0829: doFinishLocal(act, result);
0830: }
0831:
0832: /**
0833: * Set a result and complete an activity in a new transaction,
0834: * i.e. the transaction attribute of this method is set to
0835: * <code>RequiresNew</code>.<P>
0836: *
0837: * @param act the <code>WfActivity</code>
0838: * @param result the tool's result data. If <code>null</code> do
0839: * not call <code>setResult</code>.
0840: * @throws InvalidDataException see {@link
0841: * de.danet.an.workflow.omgcore.WfActivity#setResult
0842: * <code>WfActivity.setResult(...)</code>}
0843: * @throws CannotCompleteException see {@link
0844: * de.danet.an.workflow.omgcore.WfActivity#complete
0845: * <code>WfActivity.complete()</code>}
0846: * @ejb.interface-method view-type="local"
0847: * @ejb.transaction type="RequiresNew"
0848: */
0849: public void doFinishLocal(WfActivity act, Map result)
0850: throws InvalidDataException, CannotCompleteException {
0851: try {
0852: if (result != null) {
0853: // Involve process in transaction before activity;
0854: // else we may get deadlocks. The process is needed by
0855: // the activity in setResult (container() has
0856: // transaction attribute NotSupported).
0857: String pn = act.container().name();
0858: act.setResult(new DefaultProcessData(result));
0859: }
0860: act.complete();
0861: } catch (NoSuchObjectException e) {
0862: logger.warn(act + " does not exist any more "
0863: + "(mapped to CannotCompleteException): "
0864: + e.getMessage());
0865: logger.debug("Stacktrace:", e);
0866: throw new CannotCompleteException(act + ": "
0867: + e.getMessage());
0868: } catch (RemoteException e) {
0869: throw new EJBException(e);
0870: } catch (RuntimeException e) {
0871: logger.warn("RuntimeException while finishing " + act
0872: + "(mapped to CannotCompleteException): "
0873: + e.getMessage());
0874: logger.debug("Stacktrace:", e);
0875: throw new CannotCompleteException(act + ": "
0876: + e.getMessage());
0877: }
0878: }
0879:
0880: /**
0881: * Abandon an activity in a new transaction,
0882: * i.e. the transaction attribute of this method is set to
0883: * <code>RequiresNew</code>.<P>
0884: *
0885: * @param act the <code>WfActivity</code>
0886: * @param result the exception result to signal
0887: * @throws TransitionNotAllowedException see {@link
0888: * de.danet.an.workflow.api.Activity#abandon(String)
0889: * <code>Activity.abandon()</code>}
0890: * @ejb.interface-method view-type="remote"
0891: * @ejb.transaction type="RequiresNew"
0892: */
0893: public void doAbandon(Activity act, ExceptionResult result)
0894: throws TransitionNotAllowedException {
0895: doAbandonLocal(act, result);
0896: }
0897:
0898: /**
0899: * Abandon an activity in a new transaction,
0900: * i.e. the transaction attribute of this method is set to
0901: * <code>RequiresNew</code>.<P>
0902: *
0903: * @param act the <code>WfActivity</code>
0904: * @param result the exception result to signal
0905: * @throws TransitionNotAllowedException see {@link
0906: * de.danet.an.workflow.api.Activity#abandon(String)
0907: * <code>Activity.abandon()</code>}
0908: * @ejb.interface-method view-type="local"
0909: * @ejb.transaction type="RequiresNew"
0910: */
0911: public void doAbandonLocal(Activity act, ExceptionResult result)
0912: throws TransitionNotAllowedException {
0913: try {
0914: ((ExtActivity) act).abandon(result);
0915: } catch (RemoteException e) {
0916: throw new EJBException(e);
0917: }
0918: }
0919:
0920: /**
0921: * Process the given event. The transaction attribute of this
0922: * method is set to <code>RequiresNew</code>, so processing will
0923: * be done in its own transaction.
0924: * @param event the event.
0925: * @throws RedeliveryRequiredException if the event should be redelivered
0926: * @ejb.interface-method view-type="local"
0927: * @ejb.transaction type="RequiresNew"
0928: */
0929: public void processEvent(WfAuditEvent event)
0930: throws RedeliveryRequiredException {
0931: if (logger.isDebugEnabled()) {
0932: logger.debug("Got audit event: " + event);
0933: }
0934: try {
0935: if (!((DefaultAuditEvent) event).skip()) {
0936: feedBack(event);
0937: // feedback may have caused the rollback already
0938: // without actually throwing an exception
0939: if (ctx.getRollbackOnly()) {
0940: return;
0941: }
0942: }
0943: // now log and publish
0944: if (!(event instanceof ImplCompleteAuditEvent)
0945: && !(event instanceof ToolInvocationFailedAuditEvent)) {
0946: int aes = ((DefaultAuditEvent) event)
0947: .auditEventSelection();
0948: // Note that if we have
0949: // AUDIT_SELECTION_STATE_EVENTS_ONLY, only state
0950: // events will be processed here, so we do not have
0951: // to look at the events again.
0952: if (aes == ProcessDefinition.AUDIT_SELECTION_ALL_EVENTS
0953: || aes == ProcessDefinition.AUDIT_SELECTION_STATE_EVENTS_ONLY
0954: || (aes == (ProcessDefinition.AUDIT_SELECTION_PROCESS_CLOSED_EVENTS_ONLY)
0955: && (event.eventType()
0956: .equals(WfAuditEvent.PROCESS_STATE_CHANGED)) && (((WfStateAuditEvent) event)
0957: .newState().startsWith("closed")))) {
0958: if (((DefaultAuditEvent) event).store()) {
0959: storeAuditEvent(event);
0960: }
0961: publishEvent(event);
0962: }
0963: }
0964: } catch (NoSuchObjectException e) {
0965: // if any of the objects involved doesn't exist any more,
0966: // something has gone wrong. But we cannot do anything
0967: // about it (i.e. shouldn't happen).
0968: logger.error(event + " discarded: " + e.getMessage(), e);
0969: } catch (RemoteException e) {
0970: // we assume that this indicates a temporary condition.
0971: throw new EJBException(e);
0972: } catch (SQLException e) {
0973: // we assume that this indicates a temporary condition.
0974: throw new EJBException(e);
0975: } catch (RedeliveryRequiredException e) {
0976: // we pass this one
0977: throw e;
0978: } catch (Throwable t) {
0979: // as we are called from a MDB, it makes no sense
0980: // to pass the exception to caller.
0981: logger.error(event + " discarded: " + t.getMessage(), t);
0982: ctx.setRollbackOnly();
0983: } finally {
0984: if (logger.isDebugEnabled()) {
0985: logger.debug("Event has been processed: " + event);
0986: }
0987: }
0988: }
0989:
0990: /**
0991: * Feed the event back to the engine, i.e. to the processes or activities
0992: * that may be interested in it.
0993: * @param event the event.
0994: * @throws RemoteException if a system-level error occurs.
0995: * @throws RedeliveryRequiredException if the event should be redelivered
0996: */
0997: private void feedBack(WfAuditEvent event) throws RemoteException,
0998: RedeliveryRequiredException {
0999: // feed back only those events that we know the receivers to
1000: // be interested in
1001: boolean processHandles = AbstractProcess.isHandled(event);
1002: boolean activityHandles = (event.activityKey() != null && AbstractActivity
1003: .isHandled(event));
1004: if (!(processHandles || activityHandles)) {
1005: if (logger.isDebugEnabled()) {
1006: logger.debug("Not feeding back " + event);
1007: }
1008: return;
1009: }
1010: ExtProcessDirectoryLocal procDir = null;
1011: try {
1012: // Source is not usable internally (remote object). Lookup
1013: // local object in order to get process and (maybe) activity
1014: // in transaction.
1015: procDir = processDirectoryLocal();
1016: ProcessLocal proc = procDir.lookupProcessLocal(event
1017: .processMgrName(), event.processKey());
1018: ExtActivityLocal actLocal = null;
1019: if (event.activityKey() != null) {
1020: actLocal = (ExtActivityLocal) proc
1021: .activityByKeyLocal(event.activityKey());
1022: }
1023: if (activityHandles) {
1024: actLocal.handleAuditEvent(event);
1025: }
1026: if (processHandles) {
1027: ((ExtProcessLocal) proc).handleAuditEvent(event);
1028: }
1029: if (logger.isDebugEnabled()) {
1030: logger.debug("Fed back event: " + event);
1031: }
1032: } catch (InvalidKeyException e) {
1033: // There is a race condition between creation of the
1034: // process and starting it. If both create and start are
1035: // performed within the same transaction, it may happen
1036: // that the event generated by the call to start becomes
1037: // visible before the process, i.e. the data in the
1038: // database. In this case, the thread processing the event
1039: // may not find the process.
1040: if (event instanceof WfStateAuditEvent
1041: && event.eventType().equals(
1042: WfAuditEvent.PROCESS_STATE_CHANGED)
1043: && (((WfStateAuditEvent) event).newState()
1044: .equals(RunningState.RUNNING.toString()))) {
1045: throw new RedeliveryRequiredException(
1046: "Process not found (yet) for " + event);
1047: }
1048: // Else if we cannot lookup the process, well then this event
1049: // is no good.
1050: logger.warn(event
1051: + " discarded, process no longer available.");
1052: } finally {
1053: EJBUtil.removeSession(procDir);
1054: }
1055: }
1056:
1057: /**
1058: * Feed the event back to the engine, i.e. to the processes or activities
1059: * that may be interested in it.
1060: * @param event the event.
1061: * @throws RemoteException if a system-level error occurs.
1062: */
1063: private void publishEvent(WfAuditEvent event)
1064: throws RemoteException {
1065: ProcessDirectory procDir = null;
1066: try {
1067: TopicSession evtSvcSession = evtSvcCon.createTopicSession(
1068: true, 0);
1069: TopicPublisher evtSvcPublisher = evtSvcSession
1070: .createPublisher(eventService);
1071: evtSvcPublisher.setDisableMessageID(true);
1072: ObjectMessage msg = evtSvcSession.createObjectMessage();
1073: msg.setStringProperty("processKey", event.processKey());
1074: msg.setStringProperty("eventType", event.eventType());
1075: msg.setObject((Serializable) event);
1076: evtSvcPublisher.publish(msg);
1077: if (logger.isDebugEnabled()) {
1078: logger.debug("Published event " + event);
1079: }
1080: evtSvcPublisher.close();
1081: evtSvcSession.close();
1082: } catch (JMSException e) {
1083: logger.error("Cannot publish " + event + ": "
1084: + e.getMessage(), e);
1085: } finally {
1086: EJBUtil.removeSession(procDir);
1087: }
1088: }
1089:
1090: //
1091: // Helper methods
1092: //
1093:
1094: /**
1095: * Stores the given audit event data to the database.
1096: * @param event the event.
1097: * @throws SQLException in case of database access problems
1098: * @throws IOException in case of i/o problems
1099: */
1100: private void storeAuditEvent(WfAuditEvent event)
1101: throws SQLException, IOException {
1102: Connection con = null;
1103: UniversalPrepStmt prepStmt = null;
1104: try {
1105: con = ds.getConnection();
1106: // prepare statement
1107: prepStmt = new UniversalPrepStmt(
1108: ds,
1109: con,
1110: "INSERT INTO AuditEvents "
1111: + " (DBId, EventTime, EventType, ActivityKey,"
1112: + " ActivityName, ProcessKey, ProcessName, ProcessMgrName,"
1113: + " ProcessMgrVersion, EventData1, EventData2,"
1114: + " EventData3, EventData4, EventData5)"
1115: + " VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
1116: // primary key
1117: long primKey = EJBUtil.newPrimaryKey("AuditEvents");
1118: int offset = 1;
1119: prepStmt.setLong(offset++, primKey);
1120:
1121: // misc. data
1122: setupAuditEventInsertData(event, prepStmt, offset, primKey);
1123:
1124: // execute insert statement
1125: int rowCount = prepStmt.executeUpdate();
1126: if (rowCount == 0) {
1127: throw new EJBException("Inserting audit event >>> "
1128: + event + " <<< failed.");
1129: }
1130:
1131: // data audit events are stored in different tables
1132: if (event instanceof WfDataAuditEvent) {
1133: WfDataAuditEvent ev = (WfDataAuditEvent) event;
1134: storeDataAuditEventProcessData(con, primKey, ev
1135: .oldData(), ev.newData());
1136: }
1137: if (logger.isDebugEnabled()) {
1138: logger.debug("Saved event " + event);
1139: }
1140: } catch (ResourceNotAvailableException e) {
1141: throw new SQLException("Cannot get primary key"
1142: + e.getMessage());
1143: } finally {
1144: JDBCUtil.closeAll(null, prepStmt, con);
1145: }
1146: }
1147:
1148: /**
1149: * Sets up the entire data fields of the given prepared statement with
1150: * values of the given event.
1151: * @param event the data source event
1152: * @param prepStmt the insert statement
1153: * @param offset an offset of the statement's parameter list
1154: * @param mapId an identifier for a persistant map (in use for data audit
1155: * events, only.)
1156: * @throws SQLException in case of database access problems
1157: * @throws IOException in case of other i/o problems
1158: */
1159: private void setupAuditEventInsertData(WfAuditEvent event,
1160: UniversalPrepStmt prepStmt, int offset, long recKey)
1161: throws SQLException, IOException {
1162:
1163: // data common to all events
1164: prepStmt.setTimestamp(offset++, new Timestamp(event.timeStamp()
1165: .getTime()));
1166: prepStmt.setString(offset++, event.eventType());
1167: prepStmt.setString(offset++, event.activityKey());
1168: prepStmt.setString(offset++, event.activityName());
1169: prepStmt.setString(offset++, event.processKey());
1170: prepStmt.setString(offset++, event.processName());
1171: prepStmt.setString(offset++, event.processMgrName());
1172: prepStmt.setString(offset++, event.processMgrVersion());
1173:
1174: // event specific data
1175: String eventData1 = null;
1176: String eventData2 = null;
1177: String eventData3 = null;
1178: String eventData4 = null;
1179: String eventData5 = null;
1180: if (event instanceof WfAssignmentAuditEvent) {
1181: WfAssignmentAuditEvent ev = (WfAssignmentAuditEvent) event;
1182: eventData1 = ev.oldResourceKey();
1183: eventData2 = ev.oldResourceName();
1184: eventData3 = ev.newResourceKey();
1185: eventData4 = ev.newResourceName();
1186: } else if (event instanceof WfCreateProcessAuditEvent) {
1187: WfCreateProcessAuditEvent ev = (WfCreateProcessAuditEvent) event;
1188: eventData1 = ev.pActivityKey();
1189: eventData2 = ev.pProcessKey();
1190: eventData3 = ev.pProcessName();
1191: eventData4 = ev.pProcessMgrName();
1192: eventData5 = ev.pProcessMgrVersion();
1193: } else if (event instanceof WfDataAuditEvent) {
1194: eventData1 = Long.toString(recKey);
1195: } else if (event instanceof WfStateAuditEvent) {
1196: WfStateAuditEvent ev = (WfStateAuditEvent) event;
1197: eventData1 = ev.oldState();
1198: eventData2 = ev.newState();
1199: }
1200: prepStmt.setString(offset++, eventData1);
1201: prepStmt.setString(offset++, eventData2);
1202: prepStmt.setString(offset++, eventData3);
1203: prepStmt.setString(offset++, eventData4);
1204: prepStmt.setString(offset++, eventData5);
1205: }
1206:
1207: /**
1208: * Stores process data associated to a data audit event as identifed
1209: * by the given map id.
1210: * @param con the database connection to use
1211: * @param mapId the primary key that identifies the assocaited data
1212: * audit event
1213: * @param oldData the old data
1214: * @param newData the new data
1215: * @throws IOException in case of io problems
1216: */
1217: private void storeDataAuditEventProcessData(Connection con,
1218: long recKey, Map oldData, Map newData) throws SQLException,
1219: IOException {
1220: JDBCPersistentMap m = null;
1221: try {
1222: Long key = new Long(recKey);
1223: // old data
1224: if (oldData != null) {
1225: m = new JDBCPersistentMap(ds, key,
1226: "DataAuditEventOldData");
1227: m.setConnection(con);
1228: m.setMapId(key);
1229: m.setNewMap();
1230: m.putAll(oldData);
1231: try {
1232: m.store();
1233: } catch (PersistentMapSQLException e) {
1234: throw (SQLException) e.getCause();
1235: }
1236: m.setConnection(null);
1237: }
1238:
1239: // new data
1240: if (newData != null) {
1241: m = new JDBCPersistentMap(ds, key,
1242: "DataAuditEventNewData");
1243: m.setConnection(con);
1244: m.setMapId(key);
1245: m.setNewMap();
1246: m.putAll(newData);
1247: try {
1248: m.store();
1249: } catch (PersistentMapSQLException e) {
1250: throw (SQLException) e.getCause();
1251: }
1252: m.setConnection(null);
1253: }
1254: } finally {
1255: m.setConnection(null);
1256: }
1257: }
1258:
1259: /**
1260: * Returns a collection of <code>WfAuditEvent</code>s associated with
1261: * this process and stored in the database.
1262: * @param execObj the execution object for which to select the audit
1263: * events
1264: * @return the collection of audit events
1265: */
1266: private Collection selectAuditEvents(WfExecutionObject execObj)
1267: throws SQLException, IOException, NamingException {
1268: Connection con = null;
1269: PreparedStatement prepStmt = null;
1270: ResultSet rs = null;
1271: try {
1272: Collection events = new ArrayList();
1273: con = ds.getConnection();
1274: String selectStatement = "SELECT * FROM auditevents ";
1275: if (execObj instanceof WfProcess) {
1276: selectStatement += " WHERE ProcessKey = ? AND ActivityKey IS NULL";
1277: } else {
1278: selectStatement += " WHERE ActivityKey = ?";
1279: }
1280: prepStmt = con.prepareStatement(selectStatement);
1281: prepStmt.setString(1, execObj.key());
1282: rs = prepStmt.executeQuery();
1283: while (rs.next()) {
1284: events.add(restoreAuditEvent(execObj, con, rs));
1285: }
1286: return events;
1287: } finally {
1288: JDBCUtil.closeAll(rs, prepStmt, con);
1289: }
1290: }
1291:
1292: /**
1293: * Restores an audit event from the given result set (element).
1294: * @param execObj the execution object for which to select the audit
1295: * events
1296: * @param con a database connection
1297: * @param rs the result set containing the event information
1298: * @return the audit event
1299: * @throws SQLException in case of SQL access problems
1300: */
1301: private WfAuditEvent restoreAuditEvent(WfExecutionObject execObj,
1302: Connection con, ResultSet rs) throws SQLException,
1303: IOException {
1304: String eventType = rs.getString(3);
1305:
1306: WfAuditEvent eventBase = null;
1307: Timestamp ts = rs.getTimestamp(2);
1308: Date evtTime = new Date(ts.getTime() + ts.getNanos()
1309: / 1000000000);
1310: if (execObj instanceof WfProcess) {
1311: eventBase = new DefaultAuditEvent((WfProcess) execObj,
1312: eventType, evtTime, rs.getString(6), rs
1313: .getString(7), rs.getString(8), rs
1314: .getString(9),
1315: ProcessDefinition.AUDIT_SELECTION_ALL_EVENTS, false);
1316: } else {
1317: eventBase = new DefaultAuditEvent((WfActivity) execObj,
1318: eventType, evtTime, rs.getString(4), rs
1319: .getString(5), rs.getString(6), rs
1320: .getString(7), rs.getString(8), rs
1321: .getString(9),
1322: ProcessDefinition.AUDIT_SELECTION_ALL_EVENTS, false);
1323: }
1324:
1325: if (eventType.equals(WfAuditEvent.PROCESS_CREATED)) {
1326: return new DefaultCreateProcessAuditEvent(eventBase, rs
1327: .getString(10), rs.getString(11), rs.getString(12),
1328: rs.getString(13), rs.getString(14));
1329: } else if ((eventType
1330: .equals(WfAuditEvent.PROCESS_STATE_CHANGED))
1331: || (eventType
1332: .equals(WfAuditEvent.ACTIVITY_STATE_CHANGED))) {
1333: return new DefaultStateAuditEvent(eventBase, rs
1334: .getString(10), rs.getString(11));
1335: } else if ((eventType
1336: .equals(WfAuditEvent.PROCESS_CONTEXT_CHANGED))
1337: || (eventType
1338: .equals(WfAuditEvent.ACTIVITY_CONTEXT_CHANGED))
1339: || (eventType
1340: .equals(WfAuditEvent.ACTIVITY_RESULT_CHANGED))) {
1341: return new DefaultDataAuditEvent(eventBase,
1342: new DefaultProcessData(selectDataAuditEventData(
1343: con, rs.getString(10),
1344: "DataAuditEventOldData")),
1345: new DefaultProcessData(selectDataAuditEventData(
1346: con, rs.getString(10),
1347: "DataAuditEventNewData")));
1348: } else if (eventType
1349: .equals(WfAuditEvent.ACTIVITY_ASSIGNMENT_CHANGED)) {
1350: return new DefaultAssignmentAuditEvent(eventBase, rs
1351: .getString(10), rs.getString(12), rs.getString(11),
1352: rs.getString(13));
1353: } else {
1354: throw new IllegalStateException(
1355: "Invalid audit event selected for history");
1356: }
1357: }
1358:
1359: /**
1360: * Returns the process data as identified by the given parameters.
1361: * @param con a database connection
1362: * @param recKey the process data identifier
1363: * @param tableName the table name to look for the process data
1364: * @return the process data
1365: * @throws IOException in case of data access problems
1366: */
1367: private ProcessData selectDataAuditEventData(Connection con,
1368: String recKey, String tableName) throws SQLException,
1369: IOException {
1370: JDBCPersistentMap m = new JDBCPersistentMap(ds, Long
1371: .valueOf(recKey), tableName);
1372: try {
1373: m.setConnection(con);
1374: m.load();
1375: ProcessData data = new DefaultProcessData();
1376: data.putAll(m);
1377: return data;
1378: } catch (PersistentMapSQLException e) {
1379: throw (SQLException) e.getCause();
1380: } finally {
1381: m.setConnection(null);
1382: }
1383: }
1384:
1385: /**
1386: * Clears the history of the process with the given key.
1387: * @param procKey the process key.
1388: * @ejb.interface-method view-type="local"
1389: */
1390: public void removeAuditEvents(String procKey) {
1391: try {
1392: Connection con = null;
1393: UniversalPrepStmt prepStmt = null;
1394: try {
1395: con = ds.getConnection();
1396: prepStmt = new UniversalPrepStmt(
1397: ds,
1398: con,
1399: "DELETE FROM DataAuditEventOldData WHERE "
1400: + "MapId IN (SELECT DBId FROM AuditEvents "
1401: + "WHERE ProcessKey = ?)");
1402: prepStmt.setString(1, procKey);
1403: prepStmt.executeUpdate();
1404: prepStmt.close();
1405: prepStmt = null;
1406:
1407: prepStmt = new UniversalPrepStmt(
1408: ds,
1409: con,
1410: "DELETE FROM DataAuditEventNewData WHERE "
1411: + "MapId IN (SELECT DBId FROM AuditEvents "
1412: + "WHERE ProcessKey = ?)");
1413: prepStmt.setString(1, procKey);
1414: prepStmt.executeUpdate();
1415: prepStmt.close();
1416: prepStmt = null;
1417:
1418: prepStmt = new UniversalPrepStmt(ds, con,
1419: "DELETE FROM AuditEvents WHERE ProcessKey = ?");
1420: prepStmt.setString(1, procKey);
1421: prepStmt.executeUpdate();
1422: } finally {
1423: JDBCUtil.closeAll(null, prepStmt, con);
1424: }
1425: } catch (SQLException e) {
1426: throw new EJBException(e);
1427: }
1428: }
1429:
1430: /**
1431: * Execute a batch in the context of the workflow service
1432: * i.e. on the server.<P>
1433: * @param batch the batch to be executed.
1434: * @return the result.
1435: * @throws InvocationTargetException wraps exceptions as defined
1436: * by the implementing class
1437: * @ejb.interface-method view-type="remote"
1438: */
1439: public Object executeBatch(Batch batch)
1440: throws InvocationTargetException {
1441: return batch.execute(new Batch.Context() {
1442: public boolean isRollbackOnly() {
1443: return ctx.getRollbackOnly();
1444: }
1445: });
1446: }
1447: }
|