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: WfProcessEJB.java,v 1.22.2.2 2007/11/02 22:12:11 mlipp Exp $
0021: *
0022: * $Log: WfProcessEJB.java,v $
0023: * Revision 1.22.2.2 2007/11/02 22:12:11 mlipp
0024: * Added missing import.
0025: *
0026: * Revision 1.22.2.1 2007/11/02 16:00:33 drmlipp
0027: * Merged bug fixes from HEAD.
0028: *
0029: * Revision 1.23 2007/09/21 06:19:35 mlipp
0030: * Fixed problem with NamingException during process deletion.
0031: *
0032: * Revision 1.22 2007/07/04 21:32:22 mlipp
0033: * Using local interface whereever possible.
0034: *
0035: * Revision 1.21 2007/07/04 20:51:05 mlipp
0036: * Using local interface whereever possible.
0037: *
0038: * Revision 1.20 2007/06/10 05:55:30 drmlipp
0039: * Using local interface for optimization.
0040: *
0041: * Revision 1.19 2007/05/03 21:58:16 mlipp
0042: * Internal refactoring for making better use of local EJBs.
0043: *
0044: */
0045: package de.danet.an.workflow.ejbs.core;
0046:
0047: import java.io.IOException;
0048: import java.io.Serializable;
0049:
0050: import java.util.ArrayList;
0051: import java.util.Collection;
0052: import java.util.Collections;
0053: import java.util.Comparator;
0054: import java.util.Date;
0055: import java.util.HashMap;
0056: import java.util.HashSet;
0057: import java.util.Iterator;
0058: import java.util.List;
0059: import java.util.Map;
0060: import java.util.Set;
0061:
0062: import java.rmi.RemoteException;
0063: import java.security.Principal;
0064: import java.security.ProviderException;
0065: import java.sql.Connection;
0066: import java.sql.PreparedStatement;
0067: import java.sql.ResultSet;
0068: import java.sql.SQLException;
0069: import java.sql.Timestamp;
0070:
0071: import javax.ejb.CreateException;
0072: import javax.ejb.EJBException;
0073: import javax.ejb.EJBLocalObject;
0074: import javax.ejb.EntityBean;
0075: import javax.ejb.EntityContext;
0076: import javax.ejb.FinderException;
0077: import javax.ejb.NoSuchEntityException;
0078: import javax.ejb.NoSuchObjectLocalException;
0079: import javax.ejb.ObjectNotFoundException;
0080: import javax.ejb.RemoveException;
0081: import javax.ejb.TimedObject;
0082: import javax.ejb.Timer;
0083: import javax.naming.NamingException;
0084: import javax.sql.DataSource;
0085:
0086: import de.danet.an.util.CollectionsUtil;
0087: import de.danet.an.util.EJBUtil;
0088: import de.danet.an.util.JDBCUtil;
0089: import de.danet.an.util.PersistentMap;
0090: import de.danet.an.util.ResourceNotAvailableException;
0091: import de.danet.an.util.SimplePrincipal;
0092: import de.danet.an.util.UniversalPrepStmt;
0093: import de.danet.an.util.persistentmaps.PersistentMapSQLException;
0094:
0095: import de.danet.an.workflow.internalapi.ExtActivityLocal;
0096: import de.danet.an.workflow.internalapi.ExtProcessLocal;
0097: import de.danet.an.workflow.internalapi.ExtTransitionLocal;
0098: import de.danet.an.workflow.localapi.ActivityLocal;
0099: import de.danet.an.workflow.localapi.ProcessDefinitionDirectoryLocal;
0100: import de.danet.an.workflow.localapi.ProcessLocal;
0101: import de.danet.an.workflow.localapi.TransitionLocal;
0102: import de.danet.an.workflow.omgcore.AlreadyRunningException;
0103: import de.danet.an.workflow.omgcore.CannotCompleteException;
0104: import de.danet.an.workflow.omgcore.CannotStartException;
0105: import de.danet.an.workflow.omgcore.CannotStopException;
0106: import de.danet.an.workflow.omgcore.HistoryNotAvailableException;
0107: import de.danet.an.workflow.omgcore.InvalidDataException;
0108: import de.danet.an.workflow.omgcore.InvalidPriorityException;
0109: import de.danet.an.workflow.omgcore.InvalidStateException;
0110: import de.danet.an.workflow.omgcore.NotRunningException;
0111: import de.danet.an.workflow.omgcore.ProcessData;
0112: import de.danet.an.workflow.omgcore.TransitionNotAllowedException;
0113: import de.danet.an.workflow.omgcore.WfAuditEvent;
0114: import de.danet.an.workflow.omgcore.WfProcessMgr;
0115: import de.danet.an.workflow.omgcore.WfRequester;
0116: import de.danet.an.workflow.omgcore.WfExecutionObject.NotRunningState;
0117: import de.danet.an.workflow.omgcore.WfExecutionObject.State;
0118: import de.danet.an.workflow.spis.aii.ResultProvider.ExceptionResult;
0119:
0120: import de.danet.an.workflow.api.Activity;
0121: import de.danet.an.workflow.api.Process;
0122: import de.danet.an.workflow.api.CannotRemoveException;
0123: import de.danet.an.workflow.api.DefaultProcessData;
0124: import de.danet.an.workflow.api.ImportException;
0125: import de.danet.an.workflow.api.InvalidKeyException;
0126: import de.danet.an.workflow.api.ProcessDefinition;
0127: import de.danet.an.workflow.api.Activity.Implementation;
0128: import de.danet.an.workflow.api.Activity.JoinAndSplitMode;
0129: import de.danet.an.workflow.api.Activity.StartFinishMode;
0130: import de.danet.an.workflow.apix.ExtActivity;
0131:
0132: import de.danet.an.workflow.domain.AbstractProcess;
0133: import de.danet.an.workflow.domain.Deadline;
0134: import de.danet.an.workflow.domain.DefaultAuditEvent;
0135: import de.danet.an.workflow.domain.DefaultProcessDefinition;
0136: import de.danet.an.workflow.domain.SubProcRequester;
0137: import de.danet.an.workflow.domain.TransitionDefinition;
0138: import de.danet.an.workflow.domain.TransitionDefinitionLocal;
0139:
0140: import de.danet.an.workflow.ejbs.admin.ProcessDefinitionDirectoryHome;
0141: import de.danet.an.workflow.ejbs.admin.ProcessDefinitionDirectoryLocalHome;
0142: import de.danet.an.workflow.ejbs.admin.ProcessMgrStub;
0143: import de.danet.an.workflow.ejbs.admin.WorkflowEngineLocal;
0144: import de.danet.an.workflow.ejbs.admin.WorkflowEngineLocalHome;
0145: import de.danet.an.workflow.ejbs.util.QueuerLocal;
0146: import de.danet.an.workflow.ejbs.util.QueuerLocalHome;
0147:
0148: /**
0149: * The entity bean <code>WfProcessEJB</code> represent a process
0150: * in the workflow.
0151: *
0152: * @ejb.bean name="ProcessBean" display-name="ProcessBean" type="BMP"
0153: * jndi-name="ejb/@@@_JNDI_Name_Prefix_@@@ProcessBean"
0154: * reentrant="true" view-type="both"
0155: * @jonas.bean ejb-name="ProcessBean"
0156: * @ejb.pk class="java.lang.Long" generate="false"
0157: * @ejb.home remote-class="de.danet.an.workflow.ejbs.core.WfProcessHome"
0158: * local-class="de.danet.an.workflow.ejbs.core.WfProcessLocalHome"
0159: * @ejb.interface
0160: * extends="javax.ejb.EJBObject, de.danet.an.workflow.apix.ExtProcess"
0161: * remote-class="de.danet.an.workflow.ejbs.core.WfProcess"
0162: * local-extends="javax.ejb.EJBLocalObject,
0163: * de.danet.an.workflow.internalapi.ExtProcessLocal"
0164: * local-class="de.danet.an.workflow.ejbs.core.WfProcessLocal"
0165: * @ejb.transaction type="Required"
0166: * @ejb.security-identity run-as="WfMOpenAdmin"
0167: * sunone-principal="WfMCorePrincipalForAdmin"
0168: * @weblogic.run-as-identity-principal WfMCorePrincipalForAdmin
0169: * @ejb.ejb-ref ejb-name="WorkflowEngine" view-type="local"
0170: * @ejb.ejb-ref ejb-name="Queuer" view-type="local"
0171: * @ejb.ejb-ref ejb-name="ActivityBean" view-type="remote"
0172: * @ejb.ejb-ref ejb-name="ActivityBean" view-type="local"
0173: * @ejb.ejb-ref ejb-name="ProcessDefinitionDirectory" view-type="remote"
0174: * @ejb.ejb-ref ejb-name="ProcessDefinitionDirectory" view-type="local"
0175: * @ejb.ejb-external-ref ref-name="ejb/JdbcKeyGenLocal" link="KeyGen"
0176: * type="Session" view-type="local" home="de.danet.an.util.KeyGenLocalHome"
0177: * business="de.danet.an.util.KeyGenLocal"
0178: * @ejb.resource-ref res-ref-name="jdbc/WfEngine"
0179: * res-type="javax.sql.DataSource" res-auth="Container"
0180: * @jonas.resource res-ref-name="jdbc/WfEngine" jndi-name="jdbc_1"
0181: * @weblogic.enable-call-by-reference True
0182: * @weblogic.resource-description
0183: * res-ref-name="jdbc/WfEngine" jndi-name="DefaultDS"
0184: * @ejb.permission role-name="WfMOpenAdmin"
0185: * @weblogic.transaction-isolation TRANSACTION_READ_COMMITTED
0186: * @weblogic.cache concurrency-strategy="Exclusive"
0187: */
0188: public class WfProcessEJB extends AbstractProcess implements
0189: EntityBean, TimedObject {
0190:
0191: private static final org.apache.commons.logging.Log logger = org.apache.commons.logging.LogFactory
0192: .getLog(WfProcessEJB.class);
0193:
0194: /**
0195: * A lazily loading implementation for deadline storage. Not all
0196: * methods are implemented.
0197: */
0198: private class DeadlineMap extends HashMap {
0199: /**
0200: * Returns the deadlines for the activity with the given key.
0201: * @param key activity key
0202: * @return the value to which this map maps the specified key,
0203: * or null if the map contains no mapping for this key
0204: * @throws ClassCastException if the key is of an
0205: * inappropriate type for this map.
0206: * @throws NullPointerException key is null
0207: */
0208: public Object get(Object key) throws ClassCastException,
0209: NullPointerException {
0210: Object res = super .get(key);
0211: if (res == null) {
0212: // try to load from db
0213: try {
0214: res = loadDeadlines(ds, (Long) key);
0215: super .put(key, res);
0216: } catch (SQLException e) {
0217: throw new EJBException(e);
0218: } catch (IOException e) {
0219: throw new EJBException(e);
0220: }
0221: }
0222: return res;
0223: }
0224:
0225: /**
0226: * Not supported.
0227: * @param key key whose presence in this map is to be tested.
0228: * @return true if this map contains a mapping for the specified key.
0229: */
0230: public boolean containsKey(Object key) {
0231: throw new UnsupportedOperationException();
0232: }
0233:
0234: /**
0235: * Not supported.
0236: * @param value value whose presence in this map is to be tested.
0237: * @return true if this map maps one or more keys to the
0238: * specified value.
0239: */
0240: public boolean containsValue(Object value) {
0241: throw new UnsupportedOperationException();
0242: }
0243:
0244: /**
0245: * Not supported.
0246: * @return set view of the keys contained in this map.
0247: */
0248: public Set keySet() {
0249: throw new UnsupportedOperationException();
0250: }
0251:
0252: /**
0253: * Not supported.
0254: * @return a collection view of the values contained in this map.
0255: */
0256: public Collection values() {
0257: throw new UnsupportedOperationException();
0258: }
0259:
0260: /**
0261: * Not supported.
0262: * @return a set view of the mappings contained in this map.
0263: */
0264: public Set entrySet() {
0265: throw new UnsupportedOperationException();
0266: }
0267: }
0268:
0269: /** Entity context. */
0270: private EntityContext ctx;
0271:
0272: /** The data source of the database. */
0273: private DataSource ds = null;
0274:
0275: /** Indicates if the database supports select for update. */
0276: private Boolean haveSelForUp = null;
0277:
0278: /** Database name. */
0279: private static final String DB_NAME = "java:comp/env/jdbc/WfEngine";
0280:
0281: private QueuerLocal queuerCache = null;
0282:
0283: /** Associated activities. */
0284: private Map activityMapCache;
0285: private Map activityLocalMapCache;
0286:
0287: /** Used for fast lookup of process definition. */
0288: private String packageId = null;
0289:
0290: /** Used for fast lookup of process definition. */
0291: private String processId = null;
0292:
0293: /** Creating principal. */
0294: private String processCreator = null;
0295:
0296: /** The cached workflow engine. */
0297: private WorkflowEngineLocal engineLocalCache = null;
0298:
0299: /** The cached home interface of the activity. */
0300: private WfActivityHome activityHomeCache = null;
0301: private WfActivityLocalHome activityLocalHomeCache = null;
0302:
0303: /** The cached home interface of the processDefinitionDirectory. */
0304: private ProcessDefinitionDirectoryHome processDefinitionDirectoryHomeCache = null;
0305: private ProcessDefinitionDirectoryLocalHome processDefinitionDirectoryLocalHomeCache = null;
0306:
0307: /** Delayed events. */
0308: private List delayedEvents = null;
0309:
0310: /** Cached transition information. */
0311: private List transitionsCache = null;
0312:
0313: /** Cached transition information. */
0314: private List transitionsLocalCache = null;
0315:
0316: /** Local process definition as string, to be stored. */
0317: private String myProcDef = null;
0318:
0319: /** List of updated transitions (need to be stored). */
0320: private Set updatedTransitions = new HashSet();
0321:
0322: //
0323: // Provide the persistent attributes
0324: //
0325:
0326: /** Indicates change of a persistent attribute. */
0327: private boolean persistentAttributeModified = false;
0328:
0329: /** Indicates change of a persistent attribute description. */
0330: private boolean paDescriptionModified = false;
0331:
0332: /** Persistent attribute <code>createTime</code>. */
0333: private Date paCreateTime;
0334:
0335: /** Persistent attribute <code>name</code>. */
0336: private String paName;
0337:
0338: /** Persistent attribute <code>id</code>. */
0339: private String paId;
0340:
0341: /** Persistent attribute <code>description</code>. */
0342: private String paDescription;
0343:
0344: /** Persistent attribute <code>priority</code>. */
0345: private Priority paPriority;
0346:
0347: /** Persistent attribute <code>typedState</code>. */
0348: private State paTypedState;
0349:
0350: /** Persistent attribute <code>lastStateTime</code>. */
0351: private Date paLastStateTime;
0352:
0353: /** Persistent attribute <code>requester</code>. */
0354: private WfRequester paRequester;
0355:
0356: /** Persistent attribute <code>processMgrName</code>. */
0357: private String paProcessMgrName;
0358:
0359: /** Persistent attribute <code>processMgrVersion</code>. */
0360: private String paProcessMgrVersion;
0361:
0362: /** Persistent attribute <code>processDef</code>. */
0363: private ProcessDefinition paProcessDef;
0364:
0365: /** Persistent attribute <code>processData</code>. */
0366: private ProcessData paProcessData;
0367:
0368: /** Persistent attribute <code>blockDeadlines</code>. */
0369: private Map paBlockDeadlines;
0370:
0371: /**
0372: * Persistent attribute <code>flags</code>. This attribute is not
0373: * exposed, it serves as space efficient storage for various
0374: * attributes that are exposed.
0375: */
0376: private int paFlags;
0377:
0378: private static final int FLAGS_DEBUG = 1;
0379: private static final int FLAGS_AUDIT_SELECTION_SHIFT = 1;
0380: private static final int FLAGS_AUDIT_SELECTION_MASK = 7 << 1;
0381: private static final int FLAGS_STORE_AUDIT_EVENTS = 16;
0382:
0383: /* Transition flags. */
0384: private static int FLAGS_HAS_PENDING_TOKEN = 1;
0385:
0386: /**
0387: * Contructor.
0388: */
0389: public WfProcessEJB() {
0390: }
0391:
0392: //
0393: // Accessor methods for cached attributes
0394: //
0395:
0396: /**
0397: * The workflow engine.
0398: * @return the workflow engine
0399: */
0400: private WorkflowEngineLocal engineLocal()
0401: throws ResourceNotAvailableException {
0402: if (engineLocalCache == null) {
0403: engineLocalCache = (WorkflowEngineLocal) EJBUtil
0404: .createSession(WorkflowEngineLocalHome.class,
0405: "java:comp/env/ejb/WorkflowEngineLocal");
0406: }
0407: return engineLocalCache;
0408: }
0409:
0410: /**
0411: * The home interface of the WfActivityBean.
0412: * @return home interface of the WfActivityBean
0413: */
0414: private WfActivityHome activityHome()
0415: throws ResourceNotAvailableException {
0416: if (activityHomeCache == null) {
0417: activityHomeCache = (WfActivityHome) EJBUtil
0418: .retrieveEJBHome(WfActivityHome.class,
0419: "java:comp/env/ejb/ActivityBean");
0420: }
0421: return activityHomeCache;
0422: }
0423:
0424: /**
0425: * The home interface of the WfActivityBean.
0426: * @return home interface of the WfActivityBean
0427: */
0428: private WfActivityLocalHome activityLocalHome()
0429: throws ResourceNotAvailableException {
0430: if (activityLocalHomeCache == null) {
0431: activityLocalHomeCache = (WfActivityLocalHome) EJBUtil
0432: .retrieveEJBLocalHome(WfActivityLocalHome.class,
0433: "java:comp/env/ejb/ActivityBeanLocal");
0434: }
0435: return activityLocalHomeCache;
0436: }
0437:
0438: /**
0439: * The home interface of the ProcessDefinitionDirectoryBean.
0440: * @return home interface of the ProcessDefinitionDirectoryBean
0441: */
0442: private ProcessDefinitionDirectoryHome processDefinitionDirectoryHome()
0443: throws ResourceNotAvailableException {
0444: if (processDefinitionDirectoryHomeCache == null) {
0445: processDefinitionDirectoryHomeCache = (ProcessDefinitionDirectoryHome) EJBUtil
0446: .retrieveEJBHome(
0447: ProcessDefinitionDirectoryHome.class,
0448: "java:comp/env/ejb/ProcessDefinitionDirectory");
0449: }
0450: return processDefinitionDirectoryHomeCache;
0451: }
0452:
0453: /**
0454: * The home interface of the ProcessDefinitionDirectoryBean.
0455: * @return home interface of the ProcessDefinitionDirectoryBean
0456: */
0457: private ProcessDefinitionDirectoryLocalHome processDefinitionDirectoryLocalHome()
0458: throws ResourceNotAvailableException {
0459: if (processDefinitionDirectoryLocalHomeCache == null) {
0460: processDefinitionDirectoryLocalHomeCache = (ProcessDefinitionDirectoryLocalHome) EJBUtil
0461: .retrieveEJBLocalHome(
0462: ProcessDefinitionDirectoryLocalHome.class,
0463: "java:comp/env/ejb/ProcessDefinitionDirectoryLocal");
0464: }
0465: return processDefinitionDirectoryLocalHomeCache;
0466: }
0467:
0468: /**
0469: * Returns the associated activities as map by key.
0470: * @return activities for this process by key.
0471: */
0472: private Map activityMap() throws RemoteException {
0473: if (activityMapCache == null) {
0474: activityMapCache = new HashMap();
0475: try {
0476: for (Iterator i = activityHome().findByProcess(
0477: (Long) ctx.getPrimaryKey()).iterator(); i
0478: .hasNext();) {
0479: ExtActivity a = (ExtActivity) i.next();
0480: cacheActivity(a);
0481: }
0482: } catch (FinderException e) {
0483: throw new EJBException(e);
0484: }
0485: }
0486: return activityMapCache;
0487: }
0488:
0489: /**
0490: * Returns the associated activities as map by key.
0491: * @return activities for this process by key.
0492: */
0493: private Map activityLocalMap() {
0494: if (activityLocalMapCache == null) {
0495: activityLocalMapCache = new HashMap();
0496: try {
0497: for (Iterator i = activityLocalHome().findByProcess(
0498: (Long) ctx.getPrimaryKey()).iterator(); i
0499: .hasNext();) {
0500: ExtActivityLocal a = (ExtActivityLocal) i.next();
0501: cacheActivityLocal(a);
0502: }
0503: } catch (FinderException e) {
0504: throw new EJBException(e);
0505: } catch (ResourceNotAvailableException e) {
0506: throw new EJBException(e);
0507: }
0508: }
0509: return activityLocalMapCache;
0510: }
0511:
0512: /**
0513: * Returns the associated activities.
0514: * @return list of activities for this process
0515: */
0516: private Collection activitiesLocal() {
0517: return activityLocalMap().values();
0518: }
0519:
0520: /**
0521: * Put the given activity in the cache. An
0522: * <code>ActivityProxy</code> around the activity ensures that the
0523: * cache is cleared (and thus reloaded when retrying the call) if
0524: * a <code>RemoteException</code> occurs.
0525: */
0526: private ExtActivity cacheActivity(ExtActivity act)
0527: throws RemoteException {
0528: ExtActivity a = ActivityProxy.wrap(act, (Process) toProcess(),
0529: false);
0530: activityMapCache.put(a.key(), a);
0531: return a;
0532: }
0533:
0534: /**
0535: * Put the given local activity in the cache. An
0536: * <code>ActivityProxy</code> around the activity ensures that the
0537: * cache is cleared (and thus reloaded when retrying the call) if
0538: * a <code>RemoteException</code> occurs.
0539: */
0540: private ExtActivityLocal cacheActivityLocal(ExtActivityLocal act) {
0541: activityLocalMap().put(act.key(), act);
0542: return act;
0543: }
0544:
0545: /**
0546: * The remote interface of the ProcessDefinitionDirectoryBean.
0547: * @return remote interface of the ProcessDefinitionDirectoryBean
0548: */
0549: private ProcessDefinitionDirectoryLocal getProcessDefinitionDirectoryLocal()
0550: throws CreateException, NamingException, RemoteException {
0551: return (ProcessDefinitionDirectoryLocal) processDefinitionDirectoryLocalHome()
0552: .create();
0553: }
0554:
0555: /**
0556: * Access cached event queuer.
0557: * @return the event queuer
0558: */
0559: private QueuerLocal queuer() throws ResourceNotAvailableException {
0560: if (queuerCache == null) {
0561: queuerCache = (QueuerLocal) EJBUtil.createSession(
0562: QueuerLocalHome.class, "java:comp/env/ejb/Queuer");
0563: }
0564: return queuerCache;
0565: }
0566:
0567: //
0568: // EJB container contract
0569: //
0570:
0571: /**
0572: * Set the associated session context. The container calls this method
0573: * after the instance creation.
0574: * @see javax.ejb.EntityBean
0575: * @throws EJBException Throws on any error.
0576: * @param context - A SessionContext interface for the instance
0577: */
0578: public void setEntityContext(EntityContext context)
0579: throws EJBException {
0580: try {
0581: ctx = context;
0582: ds = JDBCUtil.refreshDS(null, DB_NAME);
0583: // we can always use the same object for process data.
0584: paProcessData = new ProcessDataStore(ds);
0585: } catch (Exception ex) {
0586: throw new EJBException(ex);
0587: }
0588: }
0589:
0590: /**
0591: * Unset the associated session context.
0592: * @see javax.ejb.EntityBean
0593: */
0594: public void unsetEntityContext() throws EJBException {
0595: engineLocalCache = null;
0596: activityHomeCache = null;
0597: processDefinitionDirectoryLocalHomeCache = null;
0598: ds = null;
0599: ctx = null;
0600: }
0601:
0602: /**
0603: * This method looks up a process EJB by its primary key (dbid).
0604: * @param primaryKey the process primary key.
0605: * @return the process EJB (remote interface).
0606: * @throws FinderException if no process found.
0607: */
0608: public Long ejbFindByPrimaryKey(Long primaryKey)
0609: throws FinderException {
0610:
0611: boolean result = false;
0612: try {
0613: result = selectByPrimaryKey(primaryKey);
0614: } catch (SQLException ex) {
0615: throw new EJBException(ex);
0616: }
0617: if (!result) {
0618: throw new ObjectNotFoundException("Row for id "
0619: + primaryKey + " not found.");
0620: }
0621: return primaryKey;
0622: }
0623:
0624: /**
0625: * This method looks up a process by its
0626: * {@link de.danet.an.workflow.omgcore.WfProcess#key key}.
0627: * @param key the process key.
0628: * @return the process EJB (remote interface).
0629: * @throws FinderException if no process found.
0630: */
0631: public Long ejbFindByProcessKey(String key) throws FinderException {
0632:
0633: Long pk = null;
0634: try {
0635: pk = Long.valueOf(key);
0636: } catch (NumberFormatException nex) {
0637: throw new ObjectNotFoundException(
0638: "Invalid key format for key = " + key);
0639: }
0640: return ejbFindByPrimaryKey(pk);
0641: }
0642:
0643: /**
0644: * This method looks up a process by the key {@link
0645: * de.danet.an.workflow.omgcore.WfActivity#key key} of one of its
0646: * activities.
0647: * @param key the activity key.
0648: * @return the process EJB (remote interface).
0649: * @throws FinderException if no process found.
0650: */
0651: public Long ejbFindByActivityKey(String key) throws FinderException {
0652:
0653: Long pk = null;
0654: try {
0655: pk = selectByActivityKey(Long.valueOf(key));
0656: } catch (NumberFormatException nex) {
0657: throw new ObjectNotFoundException(
0658: "Invalid key format for key = " + key);
0659: } catch (SQLException ex) {
0660: throw new EJBException(ex);
0661: }
0662: if (pk == null) {
0663: throw new ObjectNotFoundException("Row for id " + key
0664: + " not found.");
0665: }
0666: return pk;
0667: }
0668:
0669: /**
0670: * This method looks up all the process by its process type.
0671: * @param processMgrName the process type.
0672: * @return a Collection object with remote interface of WfProcess.
0673: * @throws FinderException because of the EJB syntax.
0674: */
0675: public Collection ejbFindByProcessMgrName(String processMgrName)
0676: throws FinderException {
0677: Collection col = null;
0678: try {
0679: col = selectByProcessMgrName(processMgrName);
0680: } catch (SQLException ex) {
0681: throw new EJBException(ex);
0682: }
0683: return col;
0684: }
0685:
0686: /**
0687: * This method looks up all processes without a local copy of the
0688: * process definition by their process type.
0689: * @param processMgrName the process type.
0690: * @return a Collection object with remote interface of WfProcess.
0691: * @throws FinderException because of the EJB syntax.
0692: */
0693: public Collection ejbFindWithoutDefinitionByProcessMgrName(
0694: String processMgrName) throws FinderException {
0695: Collection col = null;
0696: try {
0697: col = selectWithoutDefinitionByProcessMgrName(processMgrName);
0698: } catch (SQLException ex) {
0699: throw new EJBException(ex);
0700: }
0701: return col;
0702: }
0703:
0704: /**
0705: * This method looks up all processes that have a requester with
0706: * the given hash value.
0707: * @param hash the hash value.
0708: * @return a Collection object with remote interface of WfProcess.
0709: * @throws FinderException because of the EJB syntax.
0710: */
0711: public Collection ejbFindByRequesterHash(int hash)
0712: throws FinderException {
0713: Collection col = null;
0714: try {
0715: col = selectByRequesterHash(hash);
0716: } catch (SQLException ex) {
0717: throw new EJBException(ex);
0718: }
0719: return col;
0720: }
0721:
0722: /**
0723: * Look up all processes of a given type that have a given value
0724: * in a particular process data item. Note that this method may
0725: * only be used for data items of type string.
0726: *
0727: * @param processMgr the process type
0728: * @param itemName the name of the process data item
0729: * @param itemValue the value of the process data item
0730: * @return a Collection object with remote interface of WfProcess.
0731: * @throws FinderException because of the EJB syntax.
0732: */
0733: public Collection ejbFindByProcessTypeAndDataItem(
0734: String processMgr, String itemName, String itemValue)
0735: throws FinderException {
0736: Connection con = null;
0737: try {
0738: try {
0739: con = ds.getConnection();
0740: return ((ProcessDataStore) paProcessData)
0741: .findProcessesByTypeAndDataItem(con,
0742: processMgr, itemName, itemValue);
0743: } finally {
0744: JDBCUtil.closeAll(null, null, con);
0745: }
0746: } catch (SQLException ex) {
0747: throw new EJBException(ex);
0748: } catch (IOException ex) {
0749: throw new EJBException(ex);
0750: }
0751: }
0752:
0753: /**
0754: * This method looks up all processes.
0755: * @return a Collection object with remote interface of WfProcess.
0756: * @throws FinderException because of the EJB syntax.
0757: */
0758: public Collection ejbFindAll() throws FinderException {
0759: Collection col = null;
0760: try {
0761: col = selectAll();
0762: } catch (SQLException ex) {
0763: throw new EJBException(ex);
0764: }
0765: return col;
0766: }
0767:
0768: /**
0769: * The activate method is called when the instance is activated from its
0770: * "passive" state. The instance should acquire any resource that it has
0771: * released earlier in the ejbPassivate() method.
0772: * This method gets the primary key from container.
0773: * @see javax.ejb.EntityBean
0774: */
0775: public void ejbActivate() throws EJBException {
0776: // "bind" entity container to primary key
0777: ((ProcessDataStore) paProcessData).setMapId((Long) ctx
0778: .getPrimaryKey());
0779: activityMapCache = null;
0780: activityLocalMapCache = null;
0781: transitionsCache = null;
0782: transitionsLocalCache = null;
0783: updatedTransitions.clear();
0784: }
0785:
0786: /**
0787: * The passivate method is called before the instance enters the
0788: * "passive" state. The instance should release any resources that it
0789: * can re-acquire later in the ejbActivate() method.
0790: * @see javax.ejb.EntityBean
0791: */
0792: public void ejbPassivate() throws EJBException {
0793: paProcessDef = null;
0794: activityMapCache = null;
0795: activityLocalMapCache = null;
0796: transitionsCache = null;
0797: transitionsLocalCache = null;
0798: updatedTransitions.clear();
0799: paBlockDeadlines = null;
0800: dispose();
0801: }
0802:
0803: /**
0804: * Create an process and save it into database.
0805: * It throws <code>EJBException</code> if any error occurs.
0806: * @param procDef process definition
0807: * @param req requester of the process
0808: * @return Long primary key of the process
0809: * @throws CreateException no
0810: * @ejb.create-method
0811: * view-type="local"
0812: */
0813: public Long ejbCreate(
0814: de.danet.an.workflow.api.ProcessDefinition procDef,
0815: WfRequester req) throws CreateException {
0816: try {
0817: Long processPk = null;
0818: // create new processPk
0819: processPk = new Long(EJBUtil.newPrimaryKey("process"));
0820: // "bind" entity container to primary key
0821: ((ProcessDataStore) paProcessData).setMapId(processPk);
0822: ((ProcessDataStore) paProcessData).setNewMap();
0823: return processPk;
0824: } catch (RemoteException ex) {
0825: logger.error(ex.getMessage(), ex);
0826: throw new CreateException(ex.getMessage());
0827: }
0828: }
0829:
0830: /**
0831: * Create an process and save it into database. First are created
0832: * activities, transitions and transitions references.
0833: * @param procDef process definition
0834: * @param req requester of the process
0835: * @throws CreateException if the process can not be created.
0836: */
0837: public void ejbPostCreate(ProcessDefinition procDef, WfRequester req)
0838: throws CreateException {
0839: try {
0840: // initialize private attributes
0841: packageId = procDef.packageId();
0842: processId = procDef.processId();
0843: if (req instanceof SubProcRequester) {
0844: processCreator = ((SubProcRequester) req).creator();
0845: } else {
0846: processCreator = ctx.getCallerPrincipal().getName();
0847: }
0848: activityLocalMapCache = new HashMap();
0849: // initialize persistent attributes
0850: paRequester = req;
0851: paCreateTime = new Date();
0852: transitionsLocalCache = new ArrayList();
0853: transitionsCache = null;
0854: updatedTransitions.clear();
0855: paProcessDef = procDef;
0856: paBlockDeadlines = CollectionsUtil
0857: .trackedMap(new DeadlineMap());
0858: // initialize underlying domain object. Delay events
0859: // because event processing tries to lookup the process in
0860: // the database, and it isn't there yet. This is probably
0861: // only necessary in the JBoss environment (and may even
0862: // not be sufficient) because the creation of the process
0863: // and the queuing of events should be in one transaction
0864: // and become visible to the event handler EJB at the same
0865: // time.
0866: delayedEvents = new ArrayList();
0867: init(procDef);
0868: Collections.sort(transitionsLocalCache, new Comparator() {
0869: public int compare(Object o1, Object o2) {
0870: return ((TransitionDefinitionLocal) o1).order()
0871: - ((TransitionDefinitionLocal) o2).order();
0872: }
0873: });
0874: refresh();
0875: // save the new process into database
0876: insertProcess(paId);
0877: insertTransitions();
0878: persistentAttributeModified = false;
0879: while (delayedEvents.size() > 0) {
0880: queuer().queue(
0881: (DefaultAuditEvent) delayedEvents.remove(0));
0882: }
0883: delayedEvents = null;
0884: } catch (RemoteException re) {
0885: throw new EJBException(re);
0886: } catch (SQLException e) {
0887: throw new EJBException(e);
0888: } catch (IOException ioe) {
0889: logger.error(ioe.getMessage(), ioe);
0890: throw new CreateException(ioe.getMessage());
0891: }
0892: }
0893:
0894: /**
0895: * A container invokes this method before it ends the life of the session
0896: * object. This happens as a result of a client's invoking a remove
0897: * operation, or when a container decides to terminate the session object
0898: * after a timeout.
0899: * @throws RemoveException if the activity is not closed.
0900: * @see javax.ejb.EntityBean
0901: */
0902: public void ejbRemove() throws RemoveException {
0903: // Note that #removeThis must be adapted if RemoveException is
0904: // thrown due to any other cause. And prepareRemove must be
0905: // adapted if there are any more preconditions to meet.
0906: try {
0907: if (getPaTypedState().workflowState() != State.CLOSED
0908: && (!getPaTypedState().isSameOrSubState(
0909: NotRunningState.NOT_STARTED))) {
0910: throw new RemoveException("Cannot remove " + toString()
0911: + ": still running.");
0912: }
0913: removeProcess();
0914: dispose();
0915: paProcessDef = null;
0916: activityMapCache = null;
0917: activityLocalMapCache = null;
0918: transitionsCache = null;
0919: transitionsLocalCache = null;
0920: updatedTransitions.clear();
0921: paBlockDeadlines = null;
0922: engineLocal().removeAuditEvents(
0923: ((Long) ctx.getPrimaryKey()).toString());
0924: } catch (RemoteException ex) {
0925: throw new EJBException(ex);
0926: } catch (IOException ex) {
0927: throw new EJBException(ex);
0928: } catch (SQLException ex) {
0929: throw new EJBException(ex);
0930: }
0931: }
0932:
0933: /**
0934: * A container invokes this method to synchronize the state of an
0935: * enterprise bean instance with the entity object's state in the database.
0936: * @see javax.ejb.EntityBean
0937: */
0938: public void ejbLoad() throws EJBException {
0939: try {
0940: // load process attributes from persistent store
0941: loadProcess();
0942: paBlockDeadlines = CollectionsUtil
0943: .trackedMap(new DeadlineMap());
0944: updatedTransitions.clear();
0945: // notify underlying domain object
0946: refresh();
0947: } catch (SQLException e) {
0948: throw new EJBException(e);
0949: } catch (IOException e) {
0950: throw new EJBException(e);
0951: } catch (FinderException e) {
0952: throw new EJBException(e);
0953: }
0954: }
0955:
0956: /**
0957: * A container invokes this method to synchronize the state of the
0958: * entity object in the database with the state of the enterprise bean
0959: * instancee.
0960: * @see javax.ejb.EntityBean
0961: */
0962: public void ejbStore() throws EJBException {
0963: try {
0964: // moved the check for persistentAttributeModified
0965: // to storeProcess because of paProcessData handling
0966: storeProcess();
0967: persistentAttributeModified = false;
0968: } catch (SQLException ex) {
0969: throw new EJBException(ex);
0970: } catch (IOException ex) {
0971: throw new EJBException(ex);
0972: }
0973: }
0974:
0975: /*********************** Database Routines *************************/
0976: /**
0977: * This method checks if the process exists.
0978: * @param primaryKey key to check
0979: * @return If it is ok, it returns true.
0980: */
0981: private boolean selectByPrimaryKey(Long primaryKey)
0982: throws SQLException {
0983: Connection con = null;
0984: PreparedStatement prepStmt = null;
0985: ResultSet rs = null;
0986: try {
0987: con = ds.getConnection();
0988: String selectStatement = "SELECT DBId FROM process WHERE DBId = ? ";
0989: prepStmt = con.prepareStatement(selectStatement);
0990: prepStmt.setLong(1, primaryKey.longValue());
0991: rs = prepStmt.executeQuery();
0992: boolean result = rs.next();
0993: return result;
0994: } finally {
0995: JDBCUtil.closeAll(rs, prepStmt, con);
0996: }
0997: }
0998:
0999: /**
1000: * This method retrieves an activity's container
1001: * @param key activity key
1002: * @return primary key of process
1003: */
1004: private Long selectByActivityKey(Long key) throws SQLException {
1005: Connection con = null;
1006: PreparedStatement prepStmt = null;
1007: ResultSet rs = null;
1008: try {
1009: con = ds.getConnection();
1010: prepStmt = con
1011: .prepareStatement("SELECT ProcessDBId FROM Activity WHERE DBId = ? ");
1012: prepStmt.setLong(1, key.longValue());
1013: rs = prepStmt.executeQuery();
1014: if (!rs.next()) {
1015: return null;
1016: }
1017: return new Long(rs.getLong(1));
1018: } finally {
1019: JDBCUtil.closeAll(rs, prepStmt, con);
1020: }
1021: }
1022:
1023: private Collection selectByProcessMgrName(String processMgrName)
1024: throws SQLException {
1025: Connection con = null;
1026: PreparedStatement prepStmt = null;
1027: ResultSet rs = null;
1028: try {
1029: Collection processes = new ArrayList();
1030: con = ds.getConnection();
1031: String selectStatement = "SELECT dbid FROM process "
1032: + " WHERE processmgr = ? " + " ORDER BY dbid ";
1033: prepStmt = con.prepareStatement(selectStatement);
1034: prepStmt.setString(1, processMgrName);
1035: rs = prepStmt.executeQuery();
1036: while (rs.next()) {
1037: processes.add(new Long(rs.getLong(1)));
1038: }
1039: return processes;
1040: } finally {
1041: JDBCUtil.closeAll(rs, prepStmt, con);
1042: }
1043: }
1044:
1045: private Collection selectWithoutDefinitionByProcessMgrName(
1046: String processMgrName) throws SQLException {
1047: Connection con = null;
1048: PreparedStatement prepStmt = null;
1049: ResultSet rs = null;
1050: try {
1051: Collection processes = new ArrayList();
1052: con = ds.getConnection();
1053: String selectStatement = "SELECT dbid FROM process "
1054: + " WHERE processmgr = ? AND xpdl IS NULL "
1055: + " ORDER BY dbid ";
1056: prepStmt = con.prepareStatement(selectStatement);
1057: prepStmt.setString(1, processMgrName);
1058: rs = prepStmt.executeQuery();
1059: while (rs.next()) {
1060: processes.add(new Long(rs.getLong(1)));
1061: }
1062: return processes;
1063: } finally {
1064: JDBCUtil.closeAll(rs, prepStmt, con);
1065: }
1066: }
1067:
1068: private Collection selectByRequesterHash(int hash)
1069: throws SQLException {
1070: Connection con = null;
1071: PreparedStatement prepStmt = null;
1072: ResultSet rs = null;
1073: try {
1074: Collection processes = new ArrayList();
1075: con = ds.getConnection();
1076: prepStmt = con
1077: .prepareStatement("SELECT dbid FROM process WHERE ReqHash = ? ORDER BY dbid");
1078: prepStmt.setInt(1, hash);
1079: rs = prepStmt.executeQuery();
1080: while (rs.next()) {
1081: processes.add(new Long(rs.getLong(1)));
1082: }
1083: return processes;
1084: } finally {
1085: JDBCUtil.closeAll(rs, prepStmt, con);
1086: }
1087: }
1088:
1089: private Collection selectAll() throws SQLException {
1090: Connection con = null;
1091: PreparedStatement prepStmt = null;
1092: ResultSet rs = null;
1093: try {
1094: Collection processes = new ArrayList();
1095: con = ds.getConnection();
1096: String selectStatement = "SELECT dbid FROM process "
1097: + " ORDER BY dbid ";
1098: prepStmt = con.prepareStatement(selectStatement);
1099: rs = prepStmt.executeQuery();
1100: while (rs.next()) {
1101: processes.add(new Long(rs.getLong(1)));
1102: }
1103: return processes;
1104: } finally {
1105: JDBCUtil.closeAll(rs, prepStmt, con);
1106: }
1107: }
1108:
1109: /**
1110: * Insert a new process info into the database.
1111: * @param processDefinitionID the ID of the process definition
1112: */
1113: private void insertProcess(String processDefinitionID)
1114: throws SQLException, IOException {
1115: Connection con = null;
1116: UniversalPrepStmt prepStmt = null;
1117: try {
1118: con = ds.getConnection();
1119: prepStmt = new UniversalPrepStmt(
1120: ds,
1121: con,
1122: "INSERT INTO process ("
1123: + "DBId, PackageId, ProcessId, Name, State, StateTime, "
1124: + "Priority"
1125: + (paRequester != null ? ", Requester, ReqHash"
1126: : "")
1127: + ", Creator, ProcessMgr, "
1128: + "MgrVersion, CreateTime, Description, Flags) "
1129: + "VALUES (?, ?, ?, ?, ?, ?, ?"
1130: + (paRequester != null ? ", ?, ?" : "")
1131: + ", ?, ?, ?, ?, ?, ?)");
1132: int offset = 1;
1133: Long procKey = (Long) ctx.getPrimaryKey();
1134: prepStmt.setLong(offset++, procKey.longValue());
1135: prepStmt.setString(offset++, packageId);
1136: prepStmt.setString(offset++, processId);
1137: prepStmt.setString(offset++, paName);
1138: prepStmt.setString(offset++, paTypedState.toString());
1139: prepStmt.setTimestamp(offset++, new Timestamp(
1140: paLastStateTime.getTime()));
1141: prepStmt.setInt(offset++, paPriority.toInt());
1142: if (paRequester != null) {
1143: prepStmt.setBinary(offset++, paRequester);
1144: prepStmt.setInt(offset++, paRequester.hashCode());
1145: }
1146: prepStmt.setString(offset++, processCreator);
1147: prepStmt.setString(offset++, paProcessMgrName);
1148: prepStmt.setString(offset++, paProcessMgrVersion);
1149: prepStmt.setTimestamp(offset++, new Timestamp(paCreateTime
1150: .getTime()));
1151: prepStmt.setString(offset++, paDescription);
1152: prepStmt.setInt(offset++, paFlags);
1153: prepStmt.executeUpdate();
1154: ((ProcessDataStore) paProcessData).setConnection(con);
1155: try {
1156: ((PersistentMap) paProcessData).store();
1157: } catch (PersistentMapSQLException e) {
1158: throw (SQLException) e.getCause();
1159: }
1160: for (Iterator i = CollectionsUtil.modifiedEntries(
1161: paBlockDeadlines).iterator(); i.hasNext();) {
1162: Long key = (Long) i.next();
1163: updateDeadlines(ds, con, (Long) ctx.getPrimaryKey(),
1164: key, (List) paBlockDeadlines.get(key), false);
1165: }
1166: } catch (RemoteException e) {
1167: throw new EJBException(e);
1168: } catch (NumberFormatException e) {
1169: throw new IOException("Activity key not a long:"
1170: + e.getMessage());
1171: } finally {
1172: ((ProcessDataStore) paProcessData).setConnection(null);
1173: JDBCUtil.closeAll(null, prepStmt, con);
1174: }
1175: }
1176:
1177: /**
1178: * Store process info in the database. The
1179: * transitions of a process does not need
1180: * to be stored.
1181: */
1182: private void storeProcess() throws SQLException, IOException {
1183: Connection con = null;
1184: UniversalPrepStmt prepStmt = null;
1185: try {
1186: if (persistentAttributeModified || myProcDef != null) {
1187: con = ds.getConnection();
1188: prepStmt = new UniversalPrepStmt(
1189: ds,
1190: con,
1191: "UPDATE process SET Name = ?, State = ?, "
1192: + "StateTime = ?, Priority = ?, Flags = ?"
1193: + (paDescriptionModified ? ", Description = ? "
1194: : "")
1195: + (myProcDef != null ? ", Xpdl = ? "
1196: : "") + " WHERE DBId = ? ");
1197: int offset = 1;
1198: prepStmt.setString(offset++, paName);
1199: prepStmt.setString(offset++, paTypedState.toString());
1200: prepStmt.setTimestamp(offset++, new Timestamp(
1201: paLastStateTime.getTime()));
1202: prepStmt.setInt(offset++, paPriority.toInt());
1203: prepStmt.setInt(offset++, paFlags);
1204: if (paDescriptionModified) {
1205: prepStmt.setString(offset++, paDescription);
1206: paDescriptionModified = false;
1207: }
1208: if (myProcDef != null) {
1209: prepStmt.setLargeString(offset++, myProcDef);
1210: }
1211: // for where clause
1212: prepStmt.setLong(offset++, ((Long) ctx.getPrimaryKey())
1213: .longValue());
1214: int rowCount = prepStmt.executeUpdate();
1215: myProcDef = null;
1216: if (rowCount == 0) {
1217: throw new NoSuchEntityException(
1218: "Storing row for DBId "
1219: + (Long) ctx.getPrimaryKey()
1220: + " failed.");
1221: }
1222: }
1223: if (((ProcessDataStore) paProcessData).isModified()) {
1224: if (con == null) {
1225: con = ds.getConnection();
1226: }
1227: ((ProcessDataStore) paProcessData).setConnection(con);
1228: try {
1229: ((PersistentMap) paProcessData).store();
1230: } catch (PersistentMapSQLException e) {
1231: throw (SQLException) e.getCause();
1232: }
1233: }
1234: if (CollectionsUtil.hasBeenModified(paBlockDeadlines)) {
1235: if (con == null) {
1236: con = ds.getConnection();
1237: }
1238: for (Iterator i = CollectionsUtil.modifiedEntries(
1239: paBlockDeadlines).iterator(); i.hasNext();) {
1240: Long key = (Long) i.next();
1241: updateDeadlines(ds, con,
1242: (Long) ctx.getPrimaryKey(), key,
1243: (List) paBlockDeadlines.get(key), true);
1244: }
1245: }
1246: if (updatedTransitions.size() > 0) {
1247: if (con == null) {
1248: con = ds.getConnection();
1249: }
1250: updateTransitions(con);
1251: }
1252: } catch (RemoteException re) {
1253: throw new EJBException(re);
1254: } finally {
1255: ((ProcessDataStore) paProcessData).setConnection(null);
1256: JDBCUtil.closeAll(null, prepStmt, con);
1257: }
1258: // transitions does not need to be stored
1259: }
1260:
1261: /**
1262: * Load process info from the database. Loads also
1263: * the transitions of the process.
1264: * After sucessful loading, the process attribute structure
1265: * is set with the values just loaded.
1266: */
1267: private void loadProcess() throws SQLException, FinderException,
1268: IOException {
1269: Connection con = null;
1270: PreparedStatement prepStmt = null;
1271: ResultSet rs = null;
1272: try {
1273: con = ds.getConnection();
1274: if (haveSelForUp == null) {
1275: haveSelForUp = new Boolean(JDBCUtil.dbProperties(ds,
1276: con).supportsSelectForUpdate());
1277: }
1278: prepStmt = con
1279: .prepareStatement("SELECT PackageId, ProcessId, Name, State, StateTime, "
1280: + "Priority, Requester, Creator, ProcessMgr, MgrVersion, "
1281: + "CreateTime, Description, Flags "
1282: + "FROM process WHERE DBId = ? "
1283: + (haveSelForUp.booleanValue() ? " FOR UPDATE"
1284: : ""));
1285: Long procKey = (Long) ctx.getPrimaryKey();
1286: prepStmt.setLong(1, procKey.longValue());
1287: rs = prepStmt.executeQuery();
1288: if (!rs.next()) {
1289: throw new NoSuchEntityException("Row for id "
1290: + (Long) ctx.getPrimaryKey()
1291: + " not found in database.");
1292: }
1293: // read process from db
1294: int offset = 1;
1295: packageId = rs.getString(offset++);
1296: processId = rs.getString(offset++);
1297: paName = rs.getString(offset++);
1298: paTypedState = State.fromString(rs.getString(offset++));
1299: paLastStateTime = rs.getTimestamp(offset++);
1300: paPriority = Priority.fromInt(rs.getInt(offset++));
1301: paRequester = (WfRequester) JDBCUtil
1302: .getBinary(rs, offset++);
1303: processCreator = rs.getString(offset++);
1304: paProcessMgrName = rs.getString(offset++);
1305: paProcessMgrVersion = rs.getString(offset++);
1306: paCreateTime = rs.getTimestamp(offset++);
1307: paDescription = rs.getString(offset++);
1308: paDescriptionModified = false;
1309: paFlags = rs.getInt(offset++);
1310: ((ProcessDataStore) paProcessData).setConnection(con);
1311: try {
1312: ((PersistentMap) paProcessData).load();
1313: } catch (PersistentMapSQLException e) {
1314: throw (SQLException) e.getCause();
1315: }
1316: } catch (InvalidPriorityException ipe) {
1317: logger.error(ipe.getMessage(), ipe);
1318: throw new SQLException(ipe.getMessage());
1319: } catch (InvalidStateException ise) {
1320: logger.error(ise.getMessage(), ise);
1321: throw new SQLException(ise.getMessage());
1322: } catch (ClassNotFoundException e) {
1323: logger.error(e.getMessage(), e);
1324: throw new SQLException(e.getMessage());
1325: } finally {
1326: ((ProcessDataStore) paProcessData).setConnection(null);
1327: JDBCUtil.closeAll(rs, prepStmt, con);
1328: }
1329: }
1330:
1331: /**
1332: * Returns the process definition of this process.
1333: * @return the definition for this process in Document.
1334: */
1335: private String loadProcDef() throws SQLException, NamingException {
1336: Connection con = null;
1337: PreparedStatement prepStmt = null;
1338: ResultSet rs = null;
1339: try {
1340: con = ds.getConnection();
1341: String selectStatement = "SELECT Xpdl FROM process WHERE DBId = ? ";
1342: prepStmt = con.prepareStatement(selectStatement);
1343: prepStmt.setLong(1, ((Long) ctx.getPrimaryKey())
1344: .longValue());
1345: rs = prepStmt.executeQuery();
1346: if (!rs.next()) {
1347: throw new NoSuchEntityException(
1348: "No process with DBId = " + ctx.getPrimaryKey());
1349: }
1350: return JDBCUtil.getString(ds, rs, 1);
1351: } catch (IOException e) {
1352: throw new EJBException(e);
1353: } finally {
1354: try {
1355: JDBCUtil.closeAll(rs, prepStmt, con);
1356: } catch (SQLException e) {
1357: logger.warn(e.getMessage(), e);
1358: }
1359: }
1360: }
1361:
1362: /**
1363: * Remove process info from the database.
1364: */
1365: private void removeProcess() throws SQLException, IOException,
1366: RemoveException {
1367: Connection con = null;
1368: PreparedStatement prepStmt = null;
1369: try {
1370: removeActivities();
1371: long procId = ((Long) ctx.getPrimaryKey()).longValue();
1372: con = ds.getConnection();
1373: removeTransitions(con, procId);
1374: ((ProcessDataStore) paProcessData).setConnection(con);
1375: paProcessData.clear();
1376: try {
1377: ((PersistentMap) paProcessData).store();
1378: } catch (PersistentMapSQLException e) {
1379: throw (SQLException) e.getCause();
1380: }
1381: String deleteStatement = "DELETE FROM process WHERE DBId = ? ";
1382: prepStmt = con.prepareStatement(deleteStatement);
1383: prepStmt.setLong(1, procId);
1384: prepStmt.executeUpdate();
1385: prepStmt.close();
1386: prepStmt = null;
1387: prepStmt = new UniversalPrepStmt(ds, con,
1388: "DELETE FROM Deadline WHERE Process = ?");
1389: prepStmt.setLong(1, ((Long) ctx.getPrimaryKey())
1390: .longValue());
1391: prepStmt.executeUpdate();
1392: prepStmt.close();
1393: prepStmt = null;
1394: prepStmt = new UniversalPrepStmt(ds, con,
1395: "DELETE FROM SimpleAppl WHERE ProcessKey = ?");
1396: prepStmt.setString(1, ((Long) ctx.getPrimaryKey())
1397: .toString());
1398: prepStmt.executeUpdate();
1399: } finally {
1400: ((ProcessDataStore) paProcessData).setConnection(null);
1401: JDBCUtil.closeAll(null, prepStmt, con);
1402: }
1403: }
1404:
1405: /**
1406: * Remove transitions info as associated to the process from the database.
1407: * @param procKey the process key for to remove the transitions
1408: * @throws SQLException in case of any SQL errors
1409: * @throws IOException in case of any IO errors
1410: */
1411: private void removeTransitions(Connection con, long procKey)
1412: throws SQLException, IOException {
1413: PreparedStatement prepStmt = null;
1414: try {
1415: String deleteStatement = "DELETE FROM transition "
1416: + "WHERE ProcessDBId = ? ";
1417: prepStmt = con.prepareStatement(deleteStatement);
1418: prepStmt.setLong(1, procKey);
1419: prepStmt.executeUpdate();
1420: } finally {
1421: JDBCUtil.closeAll(null, prepStmt, null);
1422: }
1423: }
1424:
1425: /**
1426: * Remove activities as associated to the process.
1427: * @param procKey the process key for to remove the transitions
1428: * @throws SQLException in case of any SQL errors
1429: * @throws IOException in case of any IO errors
1430: */
1431: private void removeActivities() throws RemoveException,
1432: RemoteException {
1433: // remove the activities from the system
1434: for (Iterator i = activitiesLocal().iterator(); i.hasNext();) {
1435: EJBLocalObject activity = ((EJBLocalObject) i.next());
1436: activity.remove();
1437: }
1438: activityMapCache = null;
1439: }
1440:
1441: /**
1442: * Load the deadlines for the activity with the given primary key
1443: * from the given datasource.<P>
1444: *
1445: * This is provided as a static package visible method as it is
1446: * used by <code>WfActivityEJB</code> as well. The deadline table
1447: * is considered a shared resource. As a process and its
1448: * activities operate on a distinct set of deadlines, there is no
1449: * need for activities to access the deadlines via the process
1450: * (and we can save the overhead).
1451: *
1452: * @param ds the data source to use
1453: * @param pk the key of the activity
1454: * @return the list of deadlines
1455: * @throws SQLException if an error occurs
1456: * @throws IOException if an error occurs
1457: */
1458: static List loadDeadlines(DataSource ds, Long pk)
1459: throws SQLException, IOException {
1460: Connection con = null;
1461: PreparedStatement prepStmt = null;
1462: ResultSet rs = null;
1463: try {
1464: List res = new ArrayList();
1465: con = ds.getConnection();
1466: String selectStatement = "SELECT "
1467: + "DeadlineCond, ExceptionName, Execution "
1468: + "FROM Deadline WHERE Activity = ? ORDER BY Entry";
1469: prepStmt = con.prepareStatement(selectStatement);
1470: prepStmt.setLong(1, pk.longValue());
1471: rs = prepStmt.executeQuery();
1472: while (rs.next()) {
1473: res.add(new Deadline(rs.getInt(3), JDBCUtil.getString(
1474: ds, rs, 1), rs.getString(2)));
1475: }
1476: return res;
1477: } finally {
1478: JDBCUtil.closeAll(rs, prepStmt, con);
1479: }
1480: }
1481:
1482: /**
1483: * Updates the deadlines for the activity with the given primary key
1484: * using the given connection.<P>
1485: *
1486: * This is provided as a static package visible method as it is
1487: * used by <code>WfActivityEJB</code> as well. The deadline table
1488: * is considered a shared resource. As a process and its
1489: * activities operate on a distinct set of deadlines, there is no
1490: * need for activities to access the deadlines via the process
1491: * (and we can save the overhead).
1492: *
1493: * @param ds the data source
1494: * @param con the connection to the database
1495: * @param pk the key of the process
1496: * @param ak the key of the activity
1497: * @param dls the list of deadlines
1498: * @param doDelete if old values should be deleted (should only be
1499: * set to <code>false</code> if updating the deadlines for a newly
1500: * created activity
1501: * @throws SQLException if an error occurs
1502: * @throws IOException if an error occurs
1503: */
1504: public static void updateDeadlines(DataSource ds, Connection con,
1505: Long pk, Long ak, List dls, boolean doDelete)
1506: throws SQLException, IOException {
1507: UniversalPrepStmt prepStmt = null;
1508: try {
1509: if (doDelete) {
1510: prepStmt = new UniversalPrepStmt(ds, con,
1511: "DELETE FROM Deadline WHERE Activity = ?");
1512: prepStmt.setLong(1, ak.longValue());
1513: prepStmt.executeUpdate();
1514: prepStmt.close();
1515: prepStmt = null;
1516: }
1517: if (dls != null && dls.size() > 0) {
1518: prepStmt = new UniversalPrepStmt(
1519: ds,
1520: con,
1521: "INSERT INTO Deadline (Process, Activity, Entry, "
1522: + "DeadlineCond, ExceptionName, Execution, State) "
1523: + "VALUES (?, ?, ?, ?, ?, ?, ?)",
1524: new String[] { "Activity", "Entry" });
1525: int entry = 0;
1526: for (Iterator i = dls.iterator(); i.hasNext();) {
1527: Deadline dl = (Deadline) i.next();
1528: int offset = 1;
1529: prepStmt.setLong(offset++, pk.longValue());
1530: prepStmt.setLong(offset++, ak.longValue());
1531: prepStmt.setInt(offset++, entry++);
1532: prepStmt
1533: .setLargeString(offset++, dl.getCondition());
1534: prepStmt.setString(offset++, dl.getExceptionName());
1535: prepStmt.setInt(offset++, dl.getExecution());
1536: prepStmt.setInt(offset++, dl.getState());
1537: prepStmt.executeUpdate();
1538: }
1539: }
1540: } finally {
1541: JDBCUtil.closeAll(null, prepStmt, null);
1542: }
1543: }
1544:
1545: //
1546: // Domain methods and helpers
1547: //
1548:
1549: /**
1550: * Indicates if some other object is equal to this one. <P>
1551: *
1552: * Note that <code>obj</code> must implement the process's
1553: * remote interface. Comparing a domain object usually
1554: * indicates some violation of the constraints of our
1555: * implementation environment.
1556: *
1557: * @param obj the object to compare with.
1558: * @return <code>true</code> if the other object is equal.
1559: */
1560: public boolean equals(Object obj) {
1561: try {
1562: return ctx.getPrimaryKey().toString().equals(
1563: ((WfProcess) obj).key());
1564: } catch (RemoteException e) {
1565: throw new EJBException(e);
1566: }
1567: }
1568:
1569: /**
1570: * Return the hash code.<P>
1571: * @return the result.
1572: */
1573: public int hashCode() {
1574: return ctx.getPrimaryKey().toString().hashCode();
1575: }
1576:
1577: /**
1578: * Starts a process.
1579: * @throws CannotStartException when the process cannot be started (e.g.,
1580: * because it is not properly initialized).
1581: * @throws AlreadyRunningException when the process has already been
1582: * started.
1583: * @ejb.interface-method view-type="remote"
1584: */
1585: public void start() throws CannotStartException,
1586: AlreadyRunningException {
1587: super .start();
1588: }
1589:
1590: /**
1591: * Returns a collection of activities of the process.
1592: * @return collection of activities of the process
1593: */
1594: public Collection steps() {
1595: // HashMap.values() returns a Collection implementation that
1596: // is not serializable. Besides we really do not need to
1597: // transfer the whole map, so let's create a new
1598: // collection-container.
1599: try {
1600: return new ArrayList(activityMap().values());
1601: } catch (RemoteException e) {
1602: throw new EJBException(e);
1603: }
1604: }
1605:
1606: /**
1607: * Returns a collection of activities of the process.
1608: * @return collection of activities of the process
1609: */
1610: public Collection stepsLocal() {
1611: // HashMap.values() returns a Collection implementation that
1612: // is not serializable. Besides we really do not need to
1613: // transfer the whole map, so let's create a new
1614: // collection-container.
1615: return new ArrayList(activitiesLocal());
1616: }
1617:
1618: /**
1619: * Retrieve the base event information about a requesting activity.
1620: * @param req the requester.
1621: * @return the base info using an audit event as DTO.
1622: */
1623: protected WfAuditEvent activityRequesterInfo(WfRequester req) {
1624: if (!(req instanceof ExtActivity)) {
1625: return null;
1626: }
1627: try {
1628: ExtActivity act = (ExtActivity) req;
1629: return act.auditEventBase();
1630: } catch (RemoteException e) {
1631: throw new EJBException(e);
1632: }
1633: }
1634:
1635: /**
1636: * Set a new name of the process. The change must be forwarded
1637: * to the activity EJBs as they cache the name.
1638: * @param newValue new name.
1639: */
1640: public void setName(String newValue) {
1641: super .setName(newValue);
1642: for (Iterator i = activitiesLocal().iterator(); i.hasNext();) {
1643: ExtActivityLocal a = (ExtActivityLocal) i.next();
1644: a.updateProcessName(newValue);
1645: }
1646: }
1647:
1648: /**
1649: * Returns the process manager which created this process.
1650: * @return the associated process manager.
1651: */
1652: public WfProcessMgr manager() {
1653: ProcessDefinition def = getPaProcessDef();
1654: try {
1655: return new ProcessMgrStub(def.packageId(), def.processId(),
1656: def.mgrName(), key(),
1657: processDefinitionDirectoryHome(),
1658: (WfProcessHome) ctx.getEJBHome());
1659: } catch (ResourceNotAvailableException e) {
1660: throw new EJBException(e);
1661: }
1662: }
1663:
1664: /**
1665: * Factory method that create new persistent objects of type
1666: * <code>WfActivity</code>. Must be implement by the persistence
1667: * layer.
1668: *
1669: * @param blockActId if the activity is part of a block activity,
1670: * else <code>null</code>
1671: * @param priority a <code>Priority</code> value
1672: * @param name the activity's name
1673: * @param description activity description
1674: * @param startMode the start mode
1675: * @param finishMode the finish mode
1676: * @param joinMode the join mode
1677: * @param splitMode the split mode
1678: * @param implementation the implementation description
1679: * @param performer the performer
1680: * @param deadlines the deadlines
1681: * @param deferChoiceOnSplit if true, choice is to be deferred
1682: * @param auditEventSelection the audit event selection
1683: * @param storeAuditEvents if true, audit events are stored in the
1684: * database
1685: * @return the created activity.
1686: */
1687: protected de.danet.an.workflow.localcoreapi.WfActivityLocal createActivity(
1688: String blockActId, Priority priority, String name,
1689: String description, StartFinishMode startMode,
1690: StartFinishMode finishMode, JoinAndSplitMode joinMode,
1691: JoinAndSplitMode splitMode,
1692: Implementation[] implementation, String performer,
1693: List deadlines, boolean deferChoiceOnSplit,
1694: int auditEventSelection, boolean storeAuditEvents) {
1695: try {
1696: ExtActivityLocal act = activityLocalHome().create(
1697: (WfProcessLocal) ctx.getEJBLocalObject(),
1698: (Long) ctx.getPrimaryKey(), auditEventBase(),
1699: blockActId == null ? null : new Long(blockActId),
1700: priority, name, description, startMode, finishMode,
1701: joinMode, splitMode, implementation, performer,
1702: deadlines, deferChoiceOnSplit, auditEventSelection,
1703: storeAuditEvents);
1704: return cacheActivityLocal(act);
1705: } catch (CreateException ce) {
1706: logger.error(ce.getMessage(), ce);
1707: throw new IllegalStateException(ce.getMessage());
1708: } catch (RemoteException re) {
1709: logger.error(re.getMessage(), re);
1710: throw new IllegalStateException(re.getMessage());
1711: }
1712: }
1713:
1714: /**
1715: * Provide a new unique activity key.
1716: *
1717: * @return the key.
1718: */
1719: protected Long createActivityKey() {
1720: try {
1721: return new Long(EJBUtil.newPrimaryKey("activity"));
1722: } catch (ResourceNotAvailableException e) {
1723: throw new EJBException(e);
1724: }
1725: }
1726:
1727: /**
1728: * Create a new transition with given id, from-activity,
1729: * to-activity.
1730: * @param id the transition id
1731: * @param group the transition group id
1732: * @param order the transition priority
1733: * @param fromAct the from Activity
1734: * @param toAct the to Activity
1735: * @param condType type of the condition
1736: * @param condition condition of this transition
1737: * @return the created transition.
1738: */
1739: protected TransitionLocal doCreateTransition(String id,
1740: String group, int order, ActivityLocal fromAct,
1741: ActivityLocal toAct, int condType, String condition) {
1742: TransitionLocal trans = new InternalTransitionDefinition(this ,
1743: ((Long) ctx.getPrimaryKey()).toString(), id, group,
1744: order, fromAct, toAct, condType, condition, false);
1745: transitionsLocalCache.add(trans);
1746: return trans;
1747: }
1748:
1749: /**
1750: * Gets a list of transitions for this process.
1751: * @return list of transitions for this process
1752: */
1753: public List transitions() {
1754: if (transitionsCache == null) {
1755: List ts = new ArrayList();
1756: for (Iterator i = transitionsLocal().iterator(); i
1757: .hasNext();) {
1758: TransitionDefinitionLocal def = (TransitionDefinitionLocal) i
1759: .next();
1760: ts.add(new TransitionDefinition(def));
1761: }
1762: transitionsCache = ts;
1763: }
1764: return transitionsCache;
1765: }
1766:
1767: /**
1768: * Gets a list of transitions for this process.
1769: * The method is public, because the transition manager needs
1770: * access to the transitions of a process.
1771: * @return list of transitions for this process
1772: */
1773: public List transitionsLocal() {
1774: if (transitionsLocalCache == null) {
1775: try {
1776: transitionsLocalCache = loadTransitions();
1777: } catch (RemoteException e) {
1778: throw new EJBException(e);
1779: } catch (SQLException e) {
1780: throw new EJBException(e);
1781: }
1782: }
1783: return transitionsLocalCache;
1784: }
1785:
1786: /**
1787: * Return the process definition directory.
1788: * While there is no immediate relation between a process
1789: * instance and the known process definitions, the definitions
1790: * nevertheless make up part of the environment of a process.
1791: * Specifically, this method is needed to start a sub-process
1792: * in an activity.
1793: * @return the process definition directory
1794: */
1795: public ProcessDefinitionDirectoryLocal processDefinitionDirectoryLocal() {
1796: try {
1797: return getProcessDefinitionDirectoryLocal();
1798: } catch (RemoteException e) {
1799: throw new EJBException(e);
1800: } catch (NamingException e) {
1801: throw new EJBException(e);
1802: } catch (CreateException e) {
1803: throw new EJBException(e);
1804: }
1805: }
1806:
1807: /**
1808: * Returns the {@link Activity <code>Activity</code>} with the given key.
1809: * The OMG interface only defines a {@link WfProcess#steps
1810: * method for listing all the activities} associated with the process.
1811: * While, of course, one could select the activity with a certain key
1812: * from that list, this would be rather insufficient.
1813: * @param key the
1814: * {@link de.danet.an.workflow.omgcore.WfActivity#key key} of the process.
1815: * @return the activity associated with the key.
1816: * @throws InvalidKeyException if no activity with the given key
1817: * exists.
1818: */
1819: public Activity activityByKey(String key)
1820: throws InvalidKeyException {
1821: try {
1822: Activity act = (Activity) activityMap().get(key);
1823: if (act != null) {
1824: return act;
1825: }
1826: } catch (RemoteException e) {
1827: throw new EJBException(e);
1828: }
1829: throw new InvalidKeyException("Key " + key + " is invalid.");
1830: }
1831:
1832: /**
1833: * Returns the {@link Activity <code>ActivityLocal</code>} with the given
1834: * key. The OMG interface only defines a {@link WfProcess#steps
1835: * method for listing all the activities} associated with the process.
1836: * While, of course, one could select the activity with a certain key
1837: * from that list, this would be rather insufficient.
1838: * @param key the
1839: * {@link de.danet.an.workflow.omgcore.WfActivity#key key} of the process.
1840: * @return the activity associated with the key.
1841: * @throws InvalidKeyException if no activity with the given key
1842: * exists.
1843: */
1844: public ActivityLocal activityByKeyLocal(String key)
1845: throws InvalidKeyException {
1846: ActivityLocal act = (ActivityLocal) activityLocalMap().get(key);
1847: if (act != null) {
1848: return act;
1849: }
1850: throw new InvalidKeyException("Key " + key + " is invalid.");
1851: }
1852:
1853: /**
1854: * Loads the transitions of the process from the db.
1855: * @param con db connection
1856: * @return list of transitions for this process.
1857: */
1858: private List loadTransitions() throws SQLException, RemoteException {
1859: Connection con = null;
1860: PreparedStatement prepStmt = null;
1861: ResultSet rs = null;
1862: try {
1863: con = ds.getConnection();
1864: String selectStatement = "SELECT "
1865: + "TransitionId, FromActivity, ToActivity, TransGroup, "
1866: + "TransOrder, CondType, TransCond, Flags "
1867: + "FROM transition WHERE ProcessDBId = ? "
1868: + "ORDER BY TransOrder ";
1869: prepStmt = con.prepareStatement(selectStatement);
1870: Long pk = (Long) ctx.getPrimaryKey();
1871: prepStmt.setLong(1, pk.longValue());
1872: rs = prepStmt.executeQuery();
1873: List trans = new ArrayList();
1874: String pks = pk.toString();
1875: while (rs.next()) {
1876: String id = rs.getString(1);
1877: Long from = new Long(rs.getLong(2));
1878: Long to = new Long(rs.getLong(3));
1879: String group = rs.getString(4);
1880: int order = rs.getInt(5);
1881: int condType = rs.getInt(6);
1882: String condition = JDBCUtil.getString(ds, rs, 7);
1883: int flags = rs.getInt(8);
1884: // add transition
1885: trans
1886: .add(new InternalTransitionDefinition(
1887: this ,
1888: pks,
1889: id,
1890: group,
1891: order,
1892: from.longValue() < 0 ? blockActivityRepresentation(from
1893: .toString())
1894: : activityByKeyLocal(from
1895: .toString()),
1896: activityByKeyLocal(to.toString()),
1897: condType, condition,
1898: (flags & FLAGS_HAS_PENDING_TOKEN) != 0));
1899: }
1900: return trans;
1901: } catch (IOException e) {
1902: throw new EJBException(e);
1903: } catch (InvalidKeyException e) {
1904: throw new EJBException(e);
1905: } finally {
1906: JDBCUtil.closeAll(rs, prepStmt, con);
1907: }
1908: }
1909:
1910: /**
1911: * insert the transitions into the db.
1912: */
1913: private void insertTransitions() throws SQLException {
1914: Connection con = null;
1915: UniversalPrepStmt prepStmt = null;
1916: try {
1917: con = ds.getConnection();
1918: Iterator it = transitionsLocalCache.iterator();
1919: prepStmt = new UniversalPrepStmt(
1920: ds,
1921: con,
1922: "INSERT INTO transition ("
1923: + "TransitionId, ProcessDBId, FromActivity, ToActivity, "
1924: + "TransGroup, TransOrder, CondType, TransCond) "
1925: + "VALUES (?, ?, ?, ?, ?, ?, ?, ?)");
1926: while (it.hasNext()) {
1927: TransitionLocal t = (TransitionLocal) it.next();
1928: TransitionDefinitionLocal td = (TransitionDefinitionLocal) t;
1929: try {
1930: int offset = 1;
1931: prepStmt.setString(offset++, t.id());
1932: prepStmt.setLong(offset++, ((Long) ctx
1933: .getPrimaryKey()).longValue());
1934: prepStmt.setLong(offset++, Long.parseLong(t.from()
1935: .key()));
1936: prepStmt.setLong(offset++, Long.parseLong(t.to()
1937: .key()));
1938: prepStmt.setString(offset++, td.group());
1939: prepStmt.setInt(offset++, td.order());
1940: prepStmt.setInt(offset++, td.conditionType());
1941: prepStmt.setString(offset++, td.condition());
1942: } catch (NumberFormatException re) {
1943: throw new SQLException(re.getMessage());
1944: }
1945: prepStmt.executeUpdate();
1946: }
1947: } finally {
1948: try {
1949: JDBCUtil.closeAll(null, prepStmt, con);
1950: } catch (SQLException e) {
1951: logger.error("Error while closing: " + e.getMessage(),
1952: e);
1953: }
1954: }
1955: }
1956:
1957: private void updateTransitions(Connection con) throws SQLException,
1958: RemoteException {
1959: UniversalPrepStmt prepStmt = null;
1960: try {
1961: Iterator it = updatedTransitions.iterator();
1962: prepStmt = new UniversalPrepStmt(
1963: ds,
1964: con,
1965: "UPDATE transition SET Flags = ?"
1966: + " WHERE ProcessDBId = ? AND TransitionId = ?");
1967: while (it.hasNext()) {
1968: ExtTransitionLocal t = (ExtTransitionLocal) it.next();
1969: if (logger.isDebugEnabled()) {
1970: logger.debug("Storing " + t);
1971: }
1972: int offset = 1;
1973: prepStmt.setInt(offset++,
1974: ((t.hasPendingToken() ? FLAGS_HAS_PENDING_TOKEN
1975: : 0)));
1976: prepStmt.setLong(offset++, ((Long) ctx.getPrimaryKey())
1977: .longValue());
1978: prepStmt.setString(offset++, t.id());
1979: prepStmt.executeUpdate();
1980: }
1981: updatedTransitions.clear();
1982: } finally {
1983: try {
1984: JDBCUtil.closeAll(null, prepStmt, null);
1985: } catch (SQLException e) {
1986: logger.error("Error while closing: " + e.getMessage(),
1987: e);
1988: }
1989: }
1990: }
1991:
1992: /**
1993: * Notify about the change of a transition.
1994: * @param transition the changed transition
1995: */
1996: public void transitionUpdated(ExtTransitionLocal transition) {
1997: updatedTransitions.add(transition);
1998: }
1999:
2000: /**
2001: * Return the remote version of this object. This is the client side
2002: * implementation of the remote interface (i.e. the EJB object).
2003: *
2004: * @return the remote stub.
2005: */
2006: public Process toProcess() {
2007: return (Process) ctx.getEJBObject();
2008: }
2009:
2010: /* (non-Javadoc)
2011: * Comment copied from interface or superclass.
2012: */
2013: public ExtProcessLocal toProcessLocal() {
2014: return (ExtProcessLocal) ctx.getEJBLocalObject();
2015: }
2016:
2017: /**
2018: * Remove this process.
2019: * @throws CannotRemoveException if the process is still in progress
2020: */
2021: protected void removeThis() throws CannotRemoveException {
2022: try {
2023: ctx.getEJBObject().remove();
2024: } catch (RemoteException e) {
2025: throw new EJBException(e);
2026: } catch (RemoveException e) {
2027: // the process being still in process is the only
2028: // prossible cause
2029: throw new CannotRemoveException(e.getMessage());
2030: }
2031: }
2032:
2033: /**
2034: * Forward the state change to the given activity calling
2035: * <code>changeState</code>. This method is used by
2036: * <code>ActivityProxy</code> to first involve the process in the
2037: * transaction. This avoids a lot of deadlocks.
2038: *
2039: * @param activity the activity
2040: * @param state the state
2041: * @throws InvalidStateException if the state change is not
2042: * possible
2043: * @throws TransitionNotAllowedException if the state change is
2044: * not possible
2045: * @ejb.interface-method view-type="remote"
2046: */
2047: public void forwardStateChange(Activity activity, State state)
2048: throws InvalidStateException, TransitionNotAllowedException {
2049: try {
2050: activity.changeState(state);
2051: } catch (RemoteException e) {
2052: throw new EJBException(e);
2053: }
2054: }
2055:
2056: /**
2057: * Forward the state change to the given activity. This method is
2058: * used by <code>ActivityProxy</code> to first involve the process
2059: * in the transaction. This avoids a lot of deadlocks.
2060: *
2061: * @param activity the activity
2062: * @param result the exception data
2063: * @throws TransitionNotAllowedException if the state change is
2064: * not possible
2065: * @ejb.interface-method view-type="remote"
2066: */
2067: public void forwardAbandon(Activity activity, ExceptionResult result)
2068: throws TransitionNotAllowedException {
2069: try {
2070: ((ExtActivity) activity).abandon(result);
2071: } catch (RemoteException e) {
2072: throw new EJBException(e);
2073: }
2074: }
2075:
2076: /**
2077: * Forward the state change to the given activity. This method is
2078: * used by <code>ActivityProxy</code> to first involve the process
2079: * in the transaction. This avoids a lot of deadlocks.
2080: *
2081: * @param activity the activity
2082: * @throws CannotCompleteException if the state change is
2083: * not possible
2084: * @ejb.interface-method view-type="remote"
2085: */
2086: public void forwardComplete(Activity activity)
2087: throws CannotCompleteException {
2088: try {
2089: activity.complete();
2090: } catch (RemoteException e) {
2091: throw new EJBException(e);
2092: }
2093: }
2094:
2095: /**
2096: * Forward the state change to the given activity. This method is
2097: * used by <code>ActivityProxy</code> to first involve the process
2098: * in the transaction. This avoids a lot of deadlocks.
2099: *
2100: * @param activity the activity
2101: * @throws CannotStopException if the state change is
2102: * not possible
2103: * @throws NotRunningException if the state change is
2104: * not possible
2105: * @ejb.interface-method view-type="remote"
2106: */
2107: public void forwardTerminate(Activity activity)
2108: throws CannotStopException, NotRunningException {
2109: try {
2110: activity.terminate();
2111: } catch (RemoteException e) {
2112: throw new EJBException(e);
2113: }
2114: }
2115:
2116: /**
2117: * Forward the state change to the given activity. This method is
2118: * used by <code>ActivityProxy</code> to first involve the process
2119: * in the transaction. This avoids a lot of deadlocks.
2120: *
2121: * @param activity the activity
2122: * @throws CannotStopException if the state change is
2123: * not possible
2124: * @throws NotRunningException if the state change is
2125: * not possible
2126: * @ejb.interface-method view-type="remote"
2127: */
2128: public void forwardAbort(Activity activity)
2129: throws CannotStopException, NotRunningException {
2130: try {
2131: activity.abort();
2132: } catch (RemoteException e) {
2133: throw new EJBException(e);
2134: }
2135: }
2136:
2137: /**
2138: * Forward the result to the given activity. This method is used
2139: * by <code>ActivityProxy</code> to first involve the process in
2140: * the transaction. This avoids a lot of deadlocks.
2141: *
2142: * @param activity the activity
2143: * @param result the result
2144: * @throws InvalidDataException if the result cannot be set
2145: * @ejb.interface-method view-type="remote"
2146: */
2147: public void forwardResult(Activity activity, ProcessData result)
2148: throws InvalidDataException {
2149: try {
2150: activity.setResult(result);
2151: } catch (RemoteException e) {
2152: throw new EJBException(e);
2153: }
2154: }
2155:
2156: /**
2157: * Forward the request for the activity info to the given
2158: * activity. This method is used by <code>ActivityProxy</code> to
2159: * first involve the process in the transaction (activity info
2160: * includes the process' description). This avoids a lot of
2161: * deadlocks.
2162: *
2163: * @param activity the activity
2164: * @return the activity info
2165: * @ejb.interface-method view-type="remote"
2166: */
2167: public Activity.Info forwardActivityInfo(Activity activity) {
2168: try {
2169: return activity.activityInfo();
2170: } catch (RemoteException e) {
2171: throw new EJBException(e);
2172: }
2173: }
2174:
2175: /**
2176: * Choose the given activity for further processing in deferred choice.
2177: *
2178: * @param activity the activity
2179: * @return <code>true</code> if the activity could be made the
2180: * effectively chosen one
2181: * @throws TransitionNotAllowedException if the activity is
2182: * neither running nor suspended
2183: * @ejb.interface-method view-type="remote"
2184: */
2185: public boolean forwardChoose(Activity activity)
2186: throws TransitionNotAllowedException {
2187: try {
2188: return activity.choose();
2189: } catch (RemoteException e) {
2190: throw new EJBException(e);
2191: }
2192: }
2193:
2194: /**
2195: * Return an arbitrary activity with the given key. The activity
2196: * need not belong to this process.
2197: * @param key activity key
2198: * @return the activity
2199: * @throws InvalidKeyException if the activity cannot be found
2200: */
2201: protected ExtActivityLocal lookupActivityLocal(String key)
2202: throws InvalidKeyException {
2203: // Search via process to get process into transaction first
2204: try {
2205: ProcessLocal proc = ((WfProcessLocalHome) ctx
2206: .getEJBLocalHome()).findByActivityKey(key);
2207: return (ExtActivityLocal) proc.activityByKeyLocal(key);
2208: } catch (FinderException e) {
2209: logger.debug(e.getMessage(), e);
2210: throw new InvalidKeyException(e.getMessage());
2211: }
2212: }
2213:
2214: /**
2215: * Return the Principal that created this process.
2216: * @return the principal.
2217: * @ejb.interface-method
2218: * view-type="remote"
2219: */
2220: public Principal processCreator() {
2221: return new SimplePrincipal(processCreator);
2222: }
2223:
2224: /**
2225: * Returns an audit event object with process relevant information
2226: * initialized.
2227: * @return the process related information.
2228: * @ejb.interface-method
2229: * view-type="remote"
2230: */
2231: public WfAuditEvent auditEventBase() {
2232: return auditEventBase(null);
2233: }
2234:
2235: /**
2236: * Process newly generated event.
2237: * @param evt Event
2238: */
2239: protected void fireAuditEvent(WfAuditEvent evt) {
2240: if (delayedEvents != null) {
2241: delayedEvents.add(evt);
2242: } else {
2243: try {
2244: queuer().queue((DefaultAuditEvent) evt);
2245: } catch (RemoteException e) {
2246: throw new EJBException(e);
2247: }
2248: }
2249: }
2250:
2251: /**
2252: * Returns a collection of <code>WfAuditEvent</code>s associated with
2253: * this process describing its history.
2254: * @return the collection of audit events
2255: * @throws HistoryNotAvailableException in case the history is not
2256: * available for any reason
2257: */
2258: public Collection history() throws HistoryNotAvailableException {
2259: try {
2260: return engineLocal().history(toProcess());
2261: } catch (RemoteException rex) {
2262: throw new EJBException(rex);
2263: }
2264: }
2265:
2266: /**
2267: * Copy the process definition in xpdl string in this process. If the
2268: * process definition need to be removed, use this method to copy process
2269: * definition in the database field of xpdl so that the process definition
2270: * can still be reconstructed.
2271: *
2272: * @param procDefXpdl the given process definition in xpdl string.
2273: */
2274: public void copyProcessDefinition(String procDefXpdl) {
2275: myProcDef = procDefXpdl;
2276: }
2277:
2278: /**
2279: * Deliver a message on the given channel to a receiver tool
2280: * listening on that channel.
2281: *
2282: * @param channel the channel name
2283: * @param message the message
2284: * @return <code>true</code> if the mesage could be delivered,
2285: * <code>false</code> if no activity was listening
2286: * @throws InvalidDataException if the message contains invalid data
2287: * @ejb.interface-method view-type="remote"
2288: */
2289: public boolean deliverChannelMessage(String channel, Map message)
2290: throws InvalidDataException {
2291: ActivityLocal act = null;
2292: try {
2293: act = activityLocalHome().findByWaitingOn(
2294: (Long) ctx.getPrimaryKey(), channel);
2295: if (logger.isDebugEnabled()) {
2296: logger.debug("Delivering message "
2297: + CollectionsUtil.toString(message)
2298: + " from channel " + channel
2299: + " to waiting activity " + act);
2300: }
2301: try {
2302: act.setResult(new DefaultProcessData(message));
2303: act.complete();
2304: } catch (CannotCompleteException e) {
2305: logger.error("Activity " + act
2306: + " listening on channel "
2307: + "but cannot be completed?!: "
2308: + e.getMessage(), e);
2309: throw new IllegalStateException(
2310: "Receiving activity cannot be completed");
2311: }
2312: return true;
2313: } catch (ObjectNotFoundException e) {
2314: return false;
2315: } catch (RemoteException rex) {
2316: throw new EJBException(rex);
2317: } catch (FinderException e) {
2318: throw new EJBException(e);
2319: }
2320: }
2321:
2322: /**
2323: * Deliver a message on the given channel to a receiver tool
2324: * listening on that channel. If no tool is listening, store the
2325: * message.
2326: *
2327: * @param channel the channel name
2328: * @param message the message
2329: * @throws InvalidDataException if the message contains invalid data
2330: * @ejb.interface-method view-type="remote"
2331: */
2332: public void submitChannelMessage(String channel, Map message)
2333: throws InvalidDataException {
2334: try {
2335: if (!deliverChannelMessage(channel, message)) {
2336: if (logger.isDebugEnabled()) {
2337: logger.debug("Putting message from channel "
2338: + channel + " on queue");
2339: }
2340: queuer().queueChannelMessage(
2341: ((Long) ctx.getPrimaryKey()).toString(),
2342: channel, message);
2343: }
2344: } catch (RemoteException rex) {
2345: throw new EJBException(rex);
2346: }
2347: }
2348:
2349: /**
2350: * Looks for a message on the given channel and if found returns it.
2351: *
2352: * @param channel the channel name
2353: * @return the message or <code>null</code>
2354: * @ejb.interface-method view-type="remote"
2355: */
2356: public Map lookForMessage(String channel) {
2357: try {
2358: return queuer().lookForChannelMessage(
2359: ((Long) ctx.getPrimaryKey()).toString(), channel);
2360: } catch (RemoteException e) {
2361: throw new EJBException(e);
2362: }
2363: }
2364:
2365: /**
2366: * Closes all channels.
2367: */
2368: protected void closeChannels() {
2369: try {
2370: queuer().closeChannels(
2371: ((Long) ctx.getPrimaryKey()).toString());
2372: } catch (ResourceNotAvailableException e) {
2373: throw new EJBException(e);
2374: }
2375: }
2376:
2377: //
2378: // Timers
2379: //
2380:
2381: /**
2382: * Start a timer that will call <code>handleTimeout</code> at the
2383: * given date with the given info.
2384: * @param due target date
2385: * @param info the context
2386: */
2387: public void startTimer(Date due, Serializable info) {
2388: // Adjust duration
2389: long duration = due.getTime() - System.currentTimeMillis();
2390: if (duration <= 0) {
2391: logger.warn("Deadline with zero or negative duration ("
2392: + ((double) duration / 1000) + "s) for "
2393: + toString() + " will be triggered immediately.");
2394: duration = 1;
2395: }
2396: if (ctx.getClass().getName().startsWith("org.jboss.ejb.")) {
2397: // create a timer with interval to circumvent JBoss bug JBAS-2274
2398: ctx.getTimerService().createTimer(duration, 3600 * 1000,
2399: info);
2400: } else {
2401: ctx.getTimerService().createTimer(duration, info);
2402: }
2403: }
2404:
2405: /**
2406: * Stop all timers that this object has created.
2407: */
2408: public void stopTimers() {
2409: for (Iterator i = ctx.getTimerService().getTimers().iterator(); i
2410: .hasNext();) {
2411: Timer timer = (Timer) i.next();
2412: timer.cancel();
2413: }
2414: }
2415:
2416: /**
2417: * Handle the timeout of a timer.
2418: * @param timer the timer that has expired.
2419: */
2420: public void ejbTimeout(Timer timer) {
2421: try {
2422: if (logger.isDebugEnabled()) {
2423: logger.debug("Received timeout from " + timer + " for "
2424: + toString());
2425: }
2426: try {
2427: handleTimeout(timer.getInfo());
2428: if (ctx.getClass().getName().startsWith(
2429: "org.jboss.ejb.")) {
2430: // Because of JBoss bug JBAS-2274 we have an interval and
2431: // must therefore cancel the timer
2432: timer.cancel();
2433: }
2434: } catch (NoSuchObjectLocalException e) {
2435: // JBoss may call this method with a canceled timer
2436: if (timer.getClass().getName().equals(
2437: "org.jboss.ejb.txtimer.TimerImpl")) {
2438: logger.debug("Exception \"" + e.getMessage()
2439: + "\" ignored, caused by JBoss bug.");
2440: return;
2441: }
2442: throw e;
2443: }
2444: } catch (EJBException e) {
2445: throw e;
2446: } catch (Throwable e) {
2447: logger.error("Problem handling timer event " + timer
2448: + "(discarded): " + e.getMessage(), e);
2449: }
2450: }
2451:
2452: //
2453: // Implement accessor methods for the persistent attributes
2454: //
2455:
2456: /**
2457: * The getter method implementation for the persistent
2458: * attribute <code>key</code>.
2459: *
2460: * @return the value of key.
2461: */
2462: protected String getPaKey() {
2463: return ((Long) ctx.getPrimaryKey()).toString();
2464: }
2465:
2466: /**
2467: * The getter method implementation for the persistent
2468: * attribute <code>createTime</code>.
2469: *
2470: * @return the value of createTime.
2471: */
2472: protected Date getPaCreateTime() {
2473: return paCreateTime;
2474: }
2475:
2476: /**
2477: * The getter method implementation for the persistent
2478: * attribute <code>name</code>.
2479: *
2480: * @see #setPaName
2481: * @return the value of name.
2482: */
2483: protected String getPaName() {
2484: return paName;
2485: }
2486:
2487: /**
2488: * The setter method implementation for the persistent
2489: * attribute <code>name</code>.
2490: *
2491: * @param newName the new value of name.
2492: * @see #getPaName
2493: */
2494: protected void setPaName(String newName) {
2495: paName = newName;
2496: persistentAttributeModified = true;
2497: }
2498:
2499: /**
2500: * The getter method implementation for the persistent
2501: * attribute <code>description</code>.
2502: *
2503: * @see #setPaDescription
2504: * @return the value of description.
2505: */
2506: protected String getPaDescription() {
2507: return paDescription;
2508: }
2509:
2510: /**
2511: * The setter method implementation for the persistent
2512: * attribute <code>description</code>.
2513: *
2514: * @param newDescription the new value of description.
2515: * @see #getPaDescription
2516: */
2517: protected void setPaDescription(String newDescription) {
2518: paDescription = newDescription;
2519: paDescriptionModified = true;
2520: persistentAttributeModified = true;
2521: }
2522:
2523: /**
2524: * The getter method implementation for the persistent
2525: * attribute <code>priority</code>.
2526: *
2527: * @see #setPaPriority
2528: * @return the value of priority.
2529: */
2530: protected Priority getPaPriority() {
2531: return paPriority;
2532: }
2533:
2534: /**
2535: * The setter method implementation for the persistent
2536: * attribute <code>priority</code>.
2537: *
2538: * @param newPriority the new value of priority.
2539: * @see #getPaPriority
2540: */
2541: protected void setPaPriority(Priority newPriority) {
2542: paPriority = newPriority;
2543: persistentAttributeModified = true;
2544: }
2545:
2546: /**
2547: * The getter method implementation for the persistent
2548: * attribute <code>typedState</code>.
2549: *
2550: * @see #setPaTypedState
2551: * @return the value of typedState.
2552: */
2553: protected State getPaTypedState() {
2554: return paTypedState;
2555: }
2556:
2557: /**
2558: * The getter method implementation for the persistent
2559: * attribute <code>lastStateTime</code>.
2560: *
2561: * @see #setPaLastStateTime
2562: * @return the value of lastStateTime.
2563: */
2564: protected Date getPaLastStateTime() {
2565: return paLastStateTime;
2566: }
2567:
2568: /**
2569: * The setter method implementation for the persistent
2570: * attribute <code>lastStateTime</code>.
2571: *
2572: * @param newLastStateTime the new value of lastStateTime.
2573: * @see #getPaLastStateTime
2574: */
2575: protected void setPaLastStateTime(Date newLastStateTime) {
2576: paLastStateTime = newLastStateTime;
2577: persistentAttributeModified = true;
2578: }
2579:
2580: /**
2581: * The setter method implementation for the persistent
2582: * attribute <code>typedState</code>.
2583: *
2584: * @param newTypedState the new value of typedState.
2585: * @see #getPaTypedState
2586: */
2587: protected void setPaTypedState(State newTypedState) {
2588: paTypedState = newTypedState;
2589: persistentAttributeModified = true;
2590: }
2591:
2592: /**
2593: * The getter method implementation for the persistent
2594: * attribute <code>processMgrName</code>.
2595: *
2596: * @see #setPaProcessMgrName
2597: * @return the value of processMgrName.
2598: */
2599: protected String getPaProcessMgrName() {
2600: return paProcessMgrName;
2601: }
2602:
2603: /**
2604: * The setter method implementation for the persistent
2605: * attribute <code>processMgrName</code>.
2606: *
2607: * @param newProcessMgrName the new value of processMgrName.
2608: * @see #getPaProcessMgrName
2609: */
2610: protected void setPaProcessMgrName(String newProcessMgrName) {
2611: paProcessMgrName = newProcessMgrName;
2612: persistentAttributeModified = true;
2613: }
2614:
2615: /**
2616: * The getter method implementation for the persistent
2617: * attribute <code>processMgrVersion</code>.
2618: *
2619: * @see #setPaProcessMgrVersion
2620: * @return the value of processMgrVersion.
2621: */
2622: protected String getPaProcessMgrVersion() {
2623: return paProcessMgrVersion;
2624: }
2625:
2626: /**
2627: * The setter method implementation for the persistent
2628: * attribute <code>processMgrVersion</code>.
2629: *
2630: * @param newProcessMgrVersion the new value of processMgrVersion.
2631: * @see #getPaProcessMgrVersion
2632: */
2633: protected void setPaProcessMgrVersion(String newProcessMgrVersion) {
2634: paProcessMgrVersion = newProcessMgrVersion;
2635: persistentAttributeModified = true;
2636: }
2637:
2638: /**
2639: * The getter method implementation for the persistent
2640: * attribute <code>requester</code>.
2641: *
2642: * @return the value of requester.
2643: */
2644: protected WfRequester getPaRequester() {
2645: return paRequester;
2646: }
2647:
2648: /**
2649: * The setter method implementation for the persistent
2650: * attribute <code>id</code>.
2651: *
2652: * @param newId the new value of process Id.
2653: * @see #getPaId
2654: */
2655: protected void setPaId(String newId) {
2656: paId = newId;
2657: persistentAttributeModified = true;
2658: }
2659:
2660: /**
2661: * The getter method implementation for the persistent
2662: * attribute <code>id</code>.
2663: *
2664: * @see #setPaId
2665: * @return the value of process Id.
2666: */
2667: protected String getPaId() {
2668: return paId;
2669: }
2670:
2671: /**
2672: * The getter method implementation for the persistent attribute
2673: * <code>processDef</code>. Three fields in the database
2674: * contribute to the persistence implementation for the process
2675: * definition: PackageId, ProcessId and Xpdl. If the
2676: * process definition for the process has not been changed
2677: * specifically and if the process definition still exists in the
2678: * process definition directory, then packageId and processId are
2679: * used to lookup the process definition.<P>
2680: *
2681: * If the definition of the process has changed, e.g. by inserting
2682: * an additional activity or the process definition has been
2683: * removed from the process definition directory. the string in
2684: * field xpdl is used to reconstruct a process definition.
2685: *
2686: * @see #setPaProcessDef
2687: * @return the value of processDef.
2688: */
2689: protected ProcessDefinition getPaProcessDef() {
2690: if (paProcessDef == null) {
2691: ProcessDefinitionDirectoryLocal pdd = null;
2692: try {
2693: String def = loadProcDef();
2694: if (def != null) {
2695: paProcessDef = new DefaultProcessDefinition(def);
2696: } else {
2697: pdd = getProcessDefinitionDirectoryLocal();
2698: paProcessDef = pdd.lookupProcessDefinition(
2699: packageId, processId);
2700: }
2701: } catch (SQLException sex) {
2702: logger.error(sex.getMessage(), sex);
2703: throw new ProviderException(sex.getMessage());
2704: } catch (IOException ioe) {
2705: logger.error(ioe.getMessage(), ioe);
2706: throw new ProviderException(ioe.getMessage());
2707: } catch (ImportException pe) {
2708: logger.error(pe.getMessage(), pe);
2709: throw new ProviderException(pe.getMessage());
2710: } catch (NamingException nex) {
2711: logger.error(nex.getMessage(), nex);
2712: throw new ProviderException(nex.getMessage());
2713: } catch (CreateException cex) {
2714: logger.error(cex.getMessage(), cex);
2715: throw new ProviderException(cex.getMessage());
2716: } catch (InvalidKeyException kex) {
2717: logger.error(kex.getMessage(), kex);
2718: throw new IllegalStateException(kex.getMessage());
2719: } finally {
2720: EJBUtil.removeSession(pdd);
2721: }
2722: }
2723: return paProcessDef;
2724: }
2725:
2726: /**
2727: * The setter method implementation for the persistent
2728: * attribute <code>processDef</code>.
2729: *
2730: * @param newProcessDef the new value of processDef.
2731: * @see #getPaProcessDef
2732: */
2733: protected void setPaProcessDef(ProcessDefinition newProcessDef) {
2734: paProcessDef = newProcessDef;
2735: persistentAttributeModified = true;
2736: }
2737:
2738: /**
2739: * The getter method implementation for the persistent
2740: * attribute <code>processData</code>.
2741: *
2742: * @return the value of processData.
2743: */
2744: protected ProcessData getPaProcessData() {
2745: return paProcessData;
2746: }
2747:
2748: /**
2749: * The getter method implementation for the persistent
2750: * attribute <code>blockDeadlines</code>.
2751: *
2752: * @return the value of blockDeadlines.
2753: */
2754: protected Map getPaBlockDeadlines() {
2755: return paBlockDeadlines;
2756: }
2757:
2758: /**
2759: * The getter method implementation for the persistent
2760: * attribute <code>debug</code>.
2761: *
2762: * @see #setPaDebug
2763: * @return the value of debug.
2764: */
2765: protected boolean getPaDebug() {
2766: return (paFlags & FLAGS_DEBUG) != 0;
2767: }
2768:
2769: /**
2770: * The setter method implementation for the persistent
2771: * attribute <code>debug</code>.
2772: *
2773: * @param newDebug the new value of debug.
2774: * @see #getPaDebug
2775: */
2776: protected void setPaDebug(boolean newDebug) {
2777: paFlags = (paFlags & FLAGS_DEBUG)
2778: | (newDebug ? FLAGS_DEBUG : 0);
2779: persistentAttributeModified = true;
2780: }
2781:
2782: /**
2783: * The getter method implementation for the persistent
2784: * attribute <code>auditEventSelection</code>.
2785: *
2786: * @see #setPaAuditEventSelection
2787: * @return the value of auditEventSelection.
2788: */
2789: protected int getPaAuditEventSelection() {
2790: return (paFlags & FLAGS_AUDIT_SELECTION_MASK) >> FLAGS_AUDIT_SELECTION_SHIFT;
2791: }
2792:
2793: /**
2794: * The setter method implementation for the persistent
2795: * attribute <code>auditEventSelection</code>.
2796: *
2797: * @param newValue the new value of auditEventSelection.
2798: * @see #getPaAuditEventSelection
2799: */
2800: protected void setPaAuditEventSelection(int newValue) {
2801: paFlags = (paFlags & ~FLAGS_AUDIT_SELECTION_MASK)
2802: | (newValue << FLAGS_AUDIT_SELECTION_SHIFT);
2803: persistentAttributeModified = true;
2804: }
2805:
2806: /**
2807: * The getter method implementation for the persistent
2808: * attribute <code>storeAuditEvents</code>.
2809: *
2810: * @see #setPaStoreAuditEvents
2811: * @return the value of storeAuditEvents.
2812: */
2813: protected boolean getPaStoreAuditEvents() {
2814: return (paFlags & FLAGS_STORE_AUDIT_EVENTS) != 0;
2815: }
2816:
2817: /**
2818: * The setter method implementation for the persistent
2819: * attribute <code>storeAuditEvents</code>.
2820: *
2821: * @param newValue the new value of storeAuditEvents.
2822: * @see #getPaStoreAuditEvents
2823: */
2824: protected void setPaStoreAuditEvents(boolean newValue) {
2825: paFlags = (paFlags & ~FLAGS_STORE_AUDIT_EVENTS)
2826: | (newValue ? FLAGS_STORE_AUDIT_EVENTS : 0);
2827: persistentAttributeModified = true;
2828: }
2829:
2830: }
|