/*
* This file is part of the WfMOpen project.
* Copyright (C) 2001-2003 Danet GmbH (www.danet.de), GS-AN.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* $Id: ProcessDefinitionDirectoryEJB.java 2921 2009-02-14 20:41:30Z mlipp $
*/
package de.danet.an.workflow.ejbs.admin;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.OptionalDataException;
import java.io.Serializable;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import javax.ejb.CreateException;
import javax.ejb.EJBException;
import javax.ejb.SessionBean;
import javax.ejb.SessionContext;
import javax.naming.NamingException;
import javax.sql.DataSource;
import org.apache.commons.collections.map.ReferenceMap;
import org.xml.sax.InputSource;
import de.danet.an.util.EJBUtil;
import de.danet.an.util.JDBCUtil;
import de.danet.an.util.ResourceNotAvailableException;
import de.danet.an.util.UniversalPrepStmt;
import de.danet.an.util.logging.RequestLog;
import de.danet.an.util.logging.RequestScope;
import de.danet.an.workflow.internalapi.ExtProcessLocal;
import de.danet.an.workflow.localcoreapi.WfProcessLocal;
import de.danet.an.workflow.omgcore.InvalidRequesterException;
import de.danet.an.workflow.omgcore.NotEnabledException;
import de.danet.an.workflow.omgcore.RequesterRequiredException;
import de.danet.an.workflow.omgcore.WfRequester;
import de.danet.an.workflow.api.ImportException;
import de.danet.an.workflow.api.InvalidKeyException;
import de.danet.an.workflow.api.ProcessDefinition;
import de.danet.an.workflow.api.ProcessMgr;
import de.danet.an.workflow.domain.AbstractProcessDefinitionDirectory;
import de.danet.an.workflow.domain.DefaultProcessDefinition;
import de.danet.an.workflow.ejbs.core.WfProcessHome;
import de.danet.an.workflow.ejbs.core.WfProcessLocalHome;
/**
* The session bean <code>ProcessDefinitionDirectoryEJB</code> manage the
* process definitions.<P>
*
* Setting log level to <code>DEBUG</code> outputs messages about
* process definition caching.
*
* @ejb.bean name="ProcessDefinitionDirectory"
* display-name="ProcessDefinitionDirectory"
* jndi-name="ejb/@@@_JNDI_Name_Prefix_@@@ProcessDefinitionDirectory"
* local-jndi-name="ejb/@@@_JNDI_Name_Prefix_@@@ProcessDefinitionDirectoryLocal"
* type="Stateless" transaction-type="Container" view-type="both"
* @jonas.bean ejb-name="ProcessDefinitionDirectory"
* @ejb.home
* remote-class="de.danet.an.workflow.ejbs.admin.ProcessDefinitionDirectoryHome"
* @ejb.interface
* extends="javax.ejb.EJBObject, de.danet.an.workflow.api.ProcessDefinitionDirectory"
* remote-class="de.danet.an.workflow.ejbs.admin.ProcessDefinitionDirectory"
* local-extends="javax.ejb.EJBLocalObject, de.danet.an.workflow.localapi.ProcessDefinitionDirectoryLocal"
* local-class="de.danet.an.workflow.ejbs.admin.ProcessDefinitionDirectoryLocal"
* @ejb.ejb-ref ejb-name="ConfigurationBean" view-type="remote"
* ref-name="ejb/Configuration"
* @ejb.ejb-ref ejb-name="ProcessBean" view-type="remote"
* @ejb.ejb-ref ejb-name="ProcessBean" view-type="local"
* @ejb.ejb-external-ref ref-name="ejb/JdbcKeyGenLocal" link="KeyGen"
* type="Session" view-type="local" home="de.danet.an.util.KeyGenLocalHome"
* business="de.danet.an.util.KeyGenLocal"
* @ejb.resource-ref res-ref-name="jdbc/WfEngine"
* res-type="javax.sql.DataSource" res-auth="Container"
* @jonas.resource res-ref-name="jdbc/WfEngine" jndi-name="jdbc_1"
* @weblogic.enable-call-by-reference True
* @weblogic.resource-description
* res-ref-name="jdbc/WfEngine" jndi-name="DefaultDS"
* @ejb.transaction type="Required"
* @ejb.permission role-name="WfMOpenAdmin"
* @weblogic.transaction-isolation TRANSACTION_READ_COMMITTED
*/
public class ProcessDefinitionDirectoryEJB
extends AbstractProcessDefinitionDirectory
implements SessionBean {
/**
* logger of this class.
*/
private static final org.apache.commons.logging.Log logger
= org.apache.commons.logging.LogFactory.getLog
(ProcessDefinitionDirectoryEJB.class);
/**
* A cache for for process definitions as found in the database
*/
private static Map processDefinitionInfoCache
= Collections.synchronizedMap(new HashMap());
/**
* A cache for archived process definitions.
*/
private static Map archivedProcessDefinitionCache
= Collections.synchronizedMap(new ReferenceMap());
/**
* An index on the cache using pacjageId/processId as key.
*/
private static Map processDefinitionDBIds
= Collections.synchronizedMap(new HashMap());
/**
* The SessionContext interface of the instance.
*/
private SessionContext ctx;
/**
* The data source of the database.
* @see javax.sql.DataSource
*/
private DataSource ds = null;
/** Database name */
private static final String DB_NAME = "java:comp/env/jdbc/WfEngine";
/** The cached home interface of the process EJB. */
private WfProcessHome processHomeCache = null;
private WfProcessLocalHome processLocalHomeCache = null;
/** The cached home interface of the configuration EJB. */
private ConfigurationHome configurationHomeCache = null;
/**
* This operation method delivers a WfProcessHome interface.
* @return home interface of WfProcessBean
* {@link de.danet.an.workflow.omgcore.WfProcess <code>WfProcess</code>}
*/
private WfProcessHome processHome() throws ResourceNotAvailableException {
if (processHomeCache == null) {
processHomeCache = (WfProcessHome)EJBUtil.retrieveEJBHome
(WfProcessHome.class, "java:comp/env/ejb/ProcessBean");
}
return processHomeCache;
}
/**
* This operation method delivers a WfProcessLocalHome interface.
* @return home interface of WfProcessBean
* {@link de.danet.an.workflow.omgcore.WfProcess <code>WfProcess</code>}
*/
private WfProcessLocalHome processLocalHome() {
if (processLocalHomeCache == null) {
try {
processLocalHomeCache
= (WfProcessLocalHome)EJBUtil.retrieveEJBLocalHome
(WfProcessLocalHome.class,
"java:comp/env/ejb/ProcessBeanLocal");
} catch (ResourceNotAvailableException e) {
throw new EJBException(e);
}
}
return processLocalHomeCache;
}
/**
* set the session context and get new data source.
* @param context the given session context.
* @throws EJBException if problem encountered with getting the data source.
*/
public void setSessionContext(SessionContext context)
throws EJBException {
ctx = context;
processHomeCache = null;
configurationHomeCache = null;
try {
// getting new data source
ds = JDBCUtil.refreshDS(null, DB_NAME);
} catch (NamingException ne) {
throw new EJBException(ne);
}
}
/**
* Not called for stateless session bean.
*/
public void ejbActivate() {
}
/**
* Not called for stateless session bean.
*/
public void ejbPassivate() {
}
/**
* A container invokes this method before it ends the life of the session
* object. This happens as a result of a client's invoking a remove
* operation, or when a container decides to terminate the session object
* after a timeout.
* @see javax.ejb.SessionBean
*/
public void ejbRemove() {
ds = null;
processHomeCache = null;
configurationHomeCache = null;
ctx = null;
}
/**
* Create an new instance of ProcessDefinitonDirectoryBean.
* @throws CreateException Throws if the ProcessDefinitionDirectoryBean
* can not be created.
*/
public void ejbCreate() throws CreateException {
init ();
}
private void init() {
}
/**
* This operation method delivers a collection of all
* defined process definitions or an empty collection if no process
* definition are found.
*
* @return collection
* @ejb.interface-method view-type="remote"
*/
public Collection processDefinitions() {
RequestScope scope = RequestLog.enterScope
(this, "processDefinitions", new Object[] {});
Collection definitions = new ArrayList();
Connection con = null;
PreparedStatement prepStmt = null;
ResultSet rs = null;
try {
try {
con = ds.getConnection();
prepStmt = con.prepareStatement
("SELECT PACKAGEID, PROCESSID FROM PROCESSDEFINITION ");
rs = prepStmt.executeQuery();
while (rs.next()) {
String pkgId = JDBCUtil.getString(ds, rs, 1);
String prcId = JDBCUtil.getString(ds, rs, 2);
try {
definitions.add (lookupProcessDefinition(pkgId, prcId));
} catch (InvalidKeyException e) {
logger.debug ("Couldn't find definition, propably"
+ " deleted since key lookup.");
}
}
} finally {
JDBCUtil.closeAll (rs, prepStmt, con);
}
} catch (SQLException se) {
throw new EJBException(se);
} catch (OptionalDataException ode) {
throw new EJBException(ode);
} catch (IOException ioe) {
throw new EJBException(ioe);
} finally {
scope.leave(definitions);
}
return definitions;
}
/**
* This method delivers the process manager for the given process.
*
* @param packageId Id attribute of the process package.
* @param processId Id attribute of the process.
* @return the process manager for the process type.
* @throws InvalidKeyException if no process definition with
* the given ids exists.
* @ejb.interface-method view-type="remote"
*/
public ProcessMgr processMgr (String packageId, String processId)
throws InvalidKeyException {
RequestScope scope = RequestLog.enterScope
(this, "processMgr", new Object[] { packageId, processId });
ProcessMgr res = null;
try {
ProcessDefinition procDef
= lookupProcessDefinition (packageId, processId);
res = new ProcessMgrStub
(packageId, processId, procDef.mgrName(), null,
(ProcessDefinitionDirectoryHome)ctx.getEJBHome(),
processHome());
} catch (ResourceNotAvailableException re) {
throw new EJBException (re);
} catch (InvalidKeyException ikex) {
ctx.setRollbackOnly();
throw ikex;
} finally {
scope.leave (res);
}
return res;
}
/**
* This method checks if a process definiton with the given ids
* exists in the database.
*
* @param packageId Id attribute of the process package.
* @param processId Id attribute of the process.
* @return <code>true</code> if a process definition with the given
* <code>id</code> exists, otherwise <code>false</code>.
* @ejb.interface-method view-type="remote"
*/
public boolean processDefinitionExists
(String packageId, String processId) {
RequestScope scope = RequestLog.enterScope
(this, "processDefinitionExists",
new Object[] { packageId, processId });
boolean res = false;
Connection con = null;
PreparedStatement prepStmt = null;
ResultSet rs = null;
try {
try {
con = ds.getConnection();
prepStmt = con.prepareStatement
("SELECT DBID FROM PROCESSDEFINITION "
+ "WHERE PACKAGEID = ? AND PROCESSID = ?");
prepStmt.setString(1, packageId);
prepStmt.setString(2, processId);
rs = prepStmt.executeQuery();
if(rs.next()) {
res = true;
return res;
}
} finally {
JDBCUtil.closeAll (rs, prepStmt, con);
}
} catch (SQLException se) {
throw new EJBException(se);
} finally {
scope.leave (new Boolean (res));
}
return res;
}
/**
* This operation method import new process definitions from an
* XPDL description.
*
* @param processDefinitions document describing the process definitions.
* @return list of prioritized message
* {@link de.danet.an.workflow.api.PrioritizedMessage
* <code>PrioritizedMessage</code>}
* @throws ImportException if the input is not a correct.
* @ejb.interface-method view-type="remote"
*/
public List importProcessDefinitions(String processDefinitions)
throws ImportException {
RequestScope scope = RequestLog.enterScope
(this, "importProcessDefinitions",
new Object[] { processDefinitions });
List res = null;
try {
res = importProcessDefinitions
(new InputSource(new StringReader(processDefinitions)));
} finally {
scope.leave (res);
}
return res;
}
/**
* This operation method import new process definitions from an
* XPDL description.
*
* @param processDefinitions byte array resulting from an
* InputStream that describes the process definitions.
* @return list of prioritized message
* {@link de.danet.an.workflow.api.PrioritizedMessage
* <code>PrioritizedMessage</code>}
* @throws ImportException if the input is not a correct.
* @ejb.interface-method view-type="remote"
*/
public List importProcessDefinitions(byte[] processDefinitions)
throws ImportException {
RequestScope scope = RequestLog.enterScope
(this, "importProcessDefinitions",
new Object[] { processDefinitions });
List res = null;
try {
res = importProcessDefinitions
(new InputSource(new ByteArrayInputStream(processDefinitions)));
} finally {
scope.leave (res);
}
return res;
}
/**
* This operation method import new process definitions from an
* XPDL description.
*
* @param inSrc the input that describes the package as XPDL.
* @return list of prioritized message
* {@link de.danet.an.workflow.api.PrioritizedMessage
* <code>PrioritizedMessage</code>}
* @throws ImportException if the input is not a correct.
*/
private List importProcessDefinitions(InputSource inSrc)
throws ImportException {
try {
List l = AbstractProcessDefinitionDirectory
.parseProcessDefinitions(inSrc);
if (l.size() > 1) {
// Remove all defintions with the imported package id
String pkgId = ((ProcessDefinition)l.get(1)).packageId();
List tbr = new ArrayList ();
try {
Connection con = null;
PreparedStatement prepStmt = null;
ResultSet rs = null;
try {
con = ds.getConnection();
prepStmt = con.prepareStatement
("SELECT PROCESSID FROM PROCESSDEFINITION "
+ "WHERE PACKAGEID = ?");
prepStmt.setString(1, pkgId);
rs = prepStmt.executeQuery();
while (rs.next()) {
tbr.add (rs.getString(1));
}
rs.close ();
rs = null;
prepStmt.close();
prepStmt = null;
for (Iterator ti = tbr.iterator(); ti.hasNext();) {
try {
removeProcessDefinition
(pkgId, (String)ti.next());
} catch (InvalidKeyException e) {
}
}
prepStmt = con.prepareStatement
("SELECT DBID FROM XPDLARCHIVE "
+ "WHERE PACKAGEID = ?");
prepStmt.setString(1, pkgId);
rs = prepStmt.executeQuery();
while (rs.next()) {
removeXpdlIfOrphaned(rs.getLong(1));
}
Iterator it = l.subList(1, l.size()).iterator();
while (it.hasNext()) {
ProcessDefinition pd = (ProcessDefinition)it.next();
insertProcessDefinition(ds, con, pd);
}
} finally {
JDBCUtil.closeAll (rs, prepStmt, con);
}
} catch (SQLException e) {
throw new EJBException (e);
}
}
return (List)l.get(0);
} catch (ImportException pex) {
ctx.setRollbackOnly();
throw pex;
}
}
/**
* Insert the process definition into the database.
* @throws SQLException, if another process with the
* same id already exist in the database.
*/
private void insertProcessDefinition
(DataSource ds, Connection con, ProcessDefinition processDefiniton)
throws SQLException {
UniversalPrepStmt prepStmt = null;
try {
// Insert new XPDL in archive
long xpdlKey = EJBUtil.newPrimaryKey ("xpdlarchive");
prepStmt = new UniversalPrepStmt
(ds, con, "INSERT INTO XPDLARCHIVE ("
+ "DBID, PACKAGEID, XPDL) VALUES (?, ?, ?)");
int offset = 1;
prepStmt.setLong (offset++, xpdlKey);
prepStmt.setString(offset++, processDefiniton.packageId());
prepStmt.setLargeString(offset++, processDefiniton.toXPDL());
prepStmt.executeUpdate();
prepStmt.close();
prepStmt = null;
// Insert new process definition (with reference to XPDL in
// archive). The XPDL column in processdefinition is deprecated.
prepStmt = new UniversalPrepStmt
(ds, con, "INSERT INTO PROCESSDEFINITION ("
+ "DBID, PACKAGEID, PROCESSID, XPDLREF, ENABLED) "
+ "VALUES (?, ?, ?, ?, 'T')");
offset = 1;
long procDefKey = EJBUtil.newPrimaryKey ("processdefinition");
prepStmt.setLong (offset++, procDefKey);
prepStmt.setString(offset++, processDefiniton.packageId());
prepStmt.setString(offset++, processDefiniton.processId());
prepStmt.setLong(offset++, xpdlKey);
prepStmt.executeUpdate();
cacheDefinition
(new ProcessDefinitionInfo
(procDefKey, new Long(xpdlKey), processDefiniton, true));
} catch (ResourceNotAvailableException e) {
throw new SQLException ("Cannot get primary key" + e.getMessage());
} finally {
JDBCUtil.closeAll (null, prepStmt, null);
}
}
/**
* This operation method removes a process definition with the
* given ids from the database. If called for a definition that
* does not exist, it does nothing.
*
* @param packageId Id attribute of the process package.
* @param processId Id attribute of the process.
* @throws InvalidKeyException if packageId or processId are
* (formally) invalid ids.
* @ejb.interface-method view-type="remote"
*/
public void removeProcessDefinition(String packageId, String processId)
throws InvalidKeyException {
RequestScope scope = RequestLog.enterScope
(this, "removeProcessDefinition",
new Object[] { packageId, processId });
try {
prepareForRemoval(packageId, processId);
PreparedStatement prepStmt = null;
Connection con = null;
ResultSet rs = null;
try {
String processType = packageId + "/" + processId;
uncacheDefinition (processType);
con = ds.getConnection();
prepStmt = con.prepareStatement
("SELECT XPDLREF FROM PROCESSDEFINITION "
+ "WHERE PACKAGEID = ? AND PROCESSID = ?");
prepStmt.setString(1, packageId);
prepStmt.setString(2, processId);
rs = prepStmt.executeQuery();
if(!rs.next()) {
return;
}
long xpdlId = rs.getLong(1);
rs.close();
rs = null;
prepStmt.close();
prepStmt = null;
// remove process definition from the database
prepStmt = con.prepareStatement
("DELETE FROM PROCESSDEFINITION "
+ "WHERE PACKAGEID = ? AND PROCESSID = ?");
prepStmt.setString(1, packageId);
prepStmt.setString(2, processId);
prepStmt.executeUpdate();
removeXpdlIfOrphaned(xpdlId);
} finally {
JDBCUtil.closeAll (rs, prepStmt, con);
}
} catch (SQLException e) {
throw new EJBException(e);
} finally {
scope.leave ();
}
}
/**
* Prepares old (i.e. without xpdl in xpdlarchive) process definitons
* and processes for removal.
*
* @param packageId the package id
* @param processId the process id
*/
private void prepareForRemoval(String packageId, String processId) {
try {
UniversalPrepStmt prepStmt = null;
Connection con = null;
ResultSet rs = null;
try {
con = ds.getConnection();
prepStmt = new UniversalPrepStmt
(ds, con, "SELECT XPDL FROM PROCESSDEFINITION "
+ "WHERE PACKAGEID = ? AND PROCESSID = ?");
prepStmt.setString(1, packageId);
prepStmt.setString(2, processId);
rs = prepStmt.executeQuery();
if(!rs.next()) {
return;
}
String xpdl = JDBCUtil.getString(ds, rs, 1);
if (xpdl == null) {
return;
}
rs.close();
rs = null;
prepStmt.close();
prepStmt = null;
// Add to archive
long xpdlKey = EJBUtil.newPrimaryKey ("xpdlarchive");
prepStmt = new UniversalPrepStmt
(ds, con, "INSERT INTO XPDLARCHIVE ("
+ "DBID, PACKAGEID, XPDL) VALUES (?, ?, ?)");
int offset = 1;
prepStmt.setLong (offset++, xpdlKey);
prepStmt.setString(offset++, packageId);
prepStmt.setLargeString(offset++, xpdl);
prepStmt.executeUpdate();
prepStmt.close();
prepStmt = null;
// Update processes
prepStmt = new UniversalPrepStmt
(ds, con, "UPDATE PROCESS SET XPDLREF = ? "
+ "WHERE PACKAGEID = ? AND PROCESSID = ? "
+ "AND XPDLREF IS NULL AND XPDL IS NULL");
offset = 1;
prepStmt.setLong (offset++, xpdlKey);
prepStmt.setString(offset++, packageId);
prepStmt.setString(offset++, processId);
prepStmt.executeUpdate();
} finally {
JDBCUtil.closeAll (rs, prepStmt, con);
}
} catch (SQLException e) {
throw new EJBException(e);
} catch (IOException e) {
throw new EJBException(e);
}
}
/**
* This method delivers the process definition for the
* given ids. If no process definition with the ids exist, it
* throws an <code>IllegalArgumentException</code>.
*
* @param packageId the package id
* @param processId the process definition id
* @return the found ProcessDefinition object
* @throws InvalidKeyException if no process definition with
* the given ids exists.
* @ejb.interface-method view-type="remote"
*/
public ProcessDefinition lookupProcessDefinition
(String packageId, String processId) throws InvalidKeyException {
RequestScope scope = RequestLog.enterScope
(this, "lookupProcessDefinition",
new Object[] { packageId, processId });
ProcessDefinition res = null;
try {
res = lookupProcessDefinitionInfo
(packageId, processId).processDefinition;
} finally {
scope.leave (res);
}
return res;
}
/**
* Result type for {@link #lookupProcessDefinitionInfo
* <code>lookupProcessDefinitionInfo</code>.
*/
public class ProcessDefinitionInfo implements Serializable {
/** The db id used to identify the current entry */
public long dbid;
/** The process definition id in the archive */
public Long archivedXpdlId;
/** The process definition. */
public ProcessDefinition processDefinition;
/** The enabled state. */
public boolean enabled;
/**
* Create a new object with attributes initialized to the
* given values.
* @param dbid the id of the process definition in the database
* @param ax the id of the archived xpdl
* @param pd the process definition
* @param enabState the enabled state
*/
public ProcessDefinitionInfo
(long dbid, Long ax, ProcessDefinition pd, boolean enabState) {
this.dbid = dbid;
archivedXpdlId = ax;
processDefinition = pd;
enabled = enabState;
}
}
/**
* This method delivers the process definition for the
* given ids. If no process definition with the ids exist, it
* throws an <code>IllegalArgumentException</code>.
*
* @param packageId the package id
* @param processId the process definition id
* @return the found ProcessDefinition object
* @throws InvalidKeyException if no process definition with
* the given ids exists.
* @ejb.interface-method view-type="local"
* @ejb.transaction type="RequiresNew"
*/
public ProcessDefinitionInfo lookupProcessDefinitionInfo
(String packageId, String processId) throws InvalidKeyException {
RequestScope scope = RequestLog.enterScope
(this, "lookupProcessDefinitionInfo",
new Object[] { packageId, processId });
ProcessDefinitionInfo processDefinitionInfo = null;
try {
Connection con = null;
PreparedStatement prepStmt = null;
ResultSet rs = null;
try {
String processType = packageId + "/" + processId;
con = ds.getConnection();
prepStmt = con.prepareStatement
("SELECT DBID, ENABLED, XPDLREF FROM PROCESSDEFINITION "
+ "WHERE PACKAGEID = ? AND PROCESSID = ?");
prepStmt.setString(1, packageId);
prepStmt.setString(2, processId);
rs = prepStmt.executeQuery();
if(!rs.next()) {
// cleanup cache as a side effect
uncacheDefinition (processType);
throw new InvalidKeyException
("No process with packageId/processId = "+ processType);
}
long dbid = rs.getLong(1);
boolean enabled = (rs.getString(2).charAt(0) == 'T');
Long xpdlRef = JDBCUtil.getLong(rs, 3);
synchronized (processDefinitionInfoCache) {
processDefinitionInfo = (ProcessDefinitionInfo)
processDefinitionInfoCache.get(new Long(dbid));
if (processDefinitionInfo != null) {
if (processDefinitionInfo.dbid != dbid) {
uncacheDefinition(processType);
processDefinitionInfo = null;
}
}
}
if (processDefinitionInfo != null) {
processDefinitionInfo.enabled = enabled;
if (logger.isDebugEnabled ()) {
logger.debug
("found (" + processDefinitionInfo
.processDefinition.packageId()
+ "/" + processDefinitionInfo
.processDefinition.processId() + ") "
+ "in cache");
}
return processDefinitionInfo;
}
rs.close();
rs = null;
prepStmt.close();
prepStmt = null;
String xpdl = null;
// For backward compatibility allow xpdlref to be null
if (xpdlRef != null) {
prepStmt = new UniversalPrepStmt
(con, "SELECT XPDL FROM XPDLARCHIVE WHERE DBID = ?");
prepStmt.setLong(1, xpdlRef.longValue());
rs = prepStmt.executeQuery();
if (rs.next()) {
xpdl = JDBCUtil.getString(ds, rs, 1);
}
} else {
prepStmt = new UniversalPrepStmt
(con, "SELECT XPDL FROM PROCESSDEFINITION "
+ "WHERE DBID = ?");
prepStmt.setLong(1, dbid);
rs = prepStmt.executeQuery();
if (rs.next()) {
xpdl = JDBCUtil.getString(ds, rs, 1);
}
}
processDefinitionInfo = new ProcessDefinitionInfo
(dbid, xpdlRef,
new DefaultProcessDefinition (xpdl), enabled);
cacheDefinition(processDefinitionInfo);
return processDefinitionInfo;
} finally {
JDBCUtil.closeAll (rs, prepStmt, con);
}
} catch (SQLException se) {
throw new EJBException(se);
} catch (IOException ioe) {
throw new EJBException(ioe);
} catch (ImportException pe) {
throw new EJBException(pe);
} finally {
scope.leave (processDefinitionInfo);
}
}
/**
* This method delivers the XPDL from the archive with the
* given id.
*
* @param id the id
* @return the xpdl
* @ejb.interface-method view-type="local"
*/
public ProcessDefinition lookupArchivedProcessDefinition (long id) {
try {
ProcessDefinition processDefinition = null;
processDefinition = (ProcessDefinition)
archivedProcessDefinitionCache.get(new Long(id));
if (processDefinition != null) {
return processDefinition;
}
PreparedStatement prepStmt = null;
Connection con = null;
ResultSet rs = null;
try {
con = ds.getConnection();
prepStmt = new UniversalPrepStmt
(ds, con, "SELECT XPDL FROM XPDLARCHIVE WHERE DBID = ?");
prepStmt.setLong(1, id);
rs = prepStmt.executeQuery();
if (!rs.next()) {
throw new IllegalArgumentException();
}
processDefinition = new DefaultProcessDefinition
(JDBCUtil.getString(ds, rs, 1));
// Add new entry
archivedProcessDefinitionCache
.put (new Long(id), processDefinition);
return processDefinition;
} finally {
JDBCUtil.closeAll (rs, prepStmt, con);
}
} catch (ImportException e) {
throw new EJBException(e);
} catch (SQLException e) {
throw new EJBException(e);
} catch (IOException e) {
throw new EJBException(e);
}
}
/**
* This method removes a XPDL from the archive if it is no longer
* referenced.
*
* @param id the id
* @ejb.interface-method view-type="local"
*/
public void removeXpdlIfOrphaned (long id) {
try {
PreparedStatement prepStmt = null;
Connection con = null;
ResultSet rs = null;
try {
con = ds.getConnection();
prepStmt = new UniversalPrepStmt
(ds, con, "SELECT XPDLREF "
+ "FROM PROCESSDEFINITION WHERE XPDLREF = ?");
prepStmt.setLong(1, id);
prepStmt.setMaxRows(1);
rs = prepStmt.executeQuery();
if (rs.next()) {
return;
}
rs.close();
rs = null;
prepStmt.close();
prepStmt = null;
prepStmt = new UniversalPrepStmt
(ds, con, "SELECT XPDLREF "
+ "FROM PROCESS WHERE XPDLREF = ?");
prepStmt.setLong(1, id);
prepStmt.setMaxRows(1);
rs = prepStmt.executeQuery();
if (rs.next()) {
return;
}
rs.close();
rs = null;
prepStmt.close();
prepStmt = null;
prepStmt = new UniversalPrepStmt
(ds, con, "DELETE FROM XPDLARCHIVE WHERE DBID = ?");
prepStmt.setLong(1, id);
prepStmt.executeUpdate();
archivedProcessDefinitionCache.remove (new Long(id));
} finally {
JDBCUtil.closeAll (rs, prepStmt, con);
}
} catch (SQLException e) {
throw new EJBException(e);
}
}
/**
* This operation method returns true if the process definition
* with the given ids is enabled.
*
* @param packageId Id attribute of the process package.
* @param processId Id attribute of the process.
* @return <code>true</code> if the process definition is enabled.
* @throws InvalidKeyException if no process definition with
* the given ids exists.
* @ejb.interface-method view-type="remote"
*/
public boolean isEnabled(String packageId, String processId)
throws InvalidKeyException {
RequestScope scope = RequestLog.enterScope
(this, "isEnabled", new Object[] { packageId, processId });
boolean enabled = false;
Connection con = null;
ResultSet rs = null;
PreparedStatement prepStmt = null;
try {
con = ds.getConnection();
prepStmt = con.prepareStatement
("SELECT ENABLED FROM PROCESSDEFINITION "
+ "WHERE PACKAGEID = ? AND PROCESSID = ?");
prepStmt.setString(1, packageId);
prepStmt.setString(2, processId);
rs = prepStmt.executeQuery();
if(!rs.next()) {
ctx.setRollbackOnly();
throw new InvalidKeyException
("No process with packageId/processId = "
+ packageId + "/" + processId);
}
if (rs.getString(1).charAt(0) == 'T') {
enabled = true;
}
return enabled;
} catch (SQLException se) {
throw new EJBException(se);
} finally {
try {
JDBCUtil.closeAll (rs, prepStmt, con);
} catch (SQLException e) {
throw new EJBException(e);
}
scope.leave(new Boolean (enabled));
}
}
/**
* This operation method set the process definition with the given
* ids as enabled or disabled.
*
* @param packageId Id attribute of the process package.
* @param processId Id attribute of the process.
* @param enabled enable the process definition or not.
* @throws InvalidKeyException if no process definition with
* the given ids exists.
* @ejb.interface-method view-type="remote"
*/
public void setEnabled
(String packageId, String processId, boolean enabled)
throws InvalidKeyException {
RequestScope scope = RequestLog.enterScope
(this, "setEnabled",
new Object[] { packageId, processId, new Boolean (enabled) });
Connection con = null;
PreparedStatement prepStmt = null;
try {
con = ds.getConnection();
prepStmt = con.prepareStatement
("UPDATE PROCESSDEFINITION SET ENABLED = ? "
+ "WHERE PACKAGEID = ? AND PROCESSID = ?");
if (enabled) {
prepStmt.setString(1, "T");
} else {
prepStmt.setString(1, "F");
}
prepStmt.setString(2, packageId);
prepStmt.setString(3, processId);
int rowCount = prepStmt.executeUpdate();
if (rowCount == 0) {
ctx.setRollbackOnly();
throw new InvalidKeyException
("No process with packageId/processId = "
+ packageId + "/" + processId);
}
} catch (SQLException se) {
throw new EJBException(se);
} finally {
try {
JDBCUtil.closeAll (null, prepStmt, con);
} catch (SQLException e) {
throw new EJBException(e);
}
scope.leave();
}
}
/**
* Create a process of the given type with the given requester.
*
* @param packageId Id attribute of the process package.
* @param processId Id attribute of the process.
* @param requester the requester.
* @return the new process.
* @throws NotEnabledException if processes of this type may not be
* created.
* @throws InvalidRequesterException if the requester is not valid.
* @throws RequesterRequiredException if no requester is passed (i.e.
* parameter is <code>null</code>.
* @throws InvalidKeyException if no process definition with
* the given ids exists.
* @ejb.interface-method view-type="remote"
*/
public de.danet.an.workflow.omgcore.WfProcess createProcess
(String packageId, String processId, WfRequester requester)
throws NotEnabledException, InvalidRequesterException,
RequesterRequiredException, InvalidKeyException {
return ((ExtProcessLocal)createProcessLocal
(packageId, processId, requester)).toProcess();
}
/**
* Create a process of the given type with the given requester.
*
* @param packageId Id attribute of the process package.
* @param processId Id attribute of the process.
* @param requester the requester.
* @return the new process.
* @throws NotEnabledException if processes of this type may not be
* created.
* @throws InvalidRequesterException if the requester is not valid.
* @throws RequesterRequiredException if no requester is passed (i.e.
* parameter is <code>null</code>.
* @throws InvalidKeyException if no process definition with
* the given ids exists.
* @ejb.interface-method view-type="remote"
*/
public WfProcessLocal createProcessLocal
(String packageId, String processId, WfRequester requester)
throws NotEnabledException, InvalidRequesterException,
RequesterRequiredException, InvalidKeyException {
try {
if (requester == null) {
ctx.setRollbackOnly();
throw new RequesterRequiredException();
}
ProcessDefinitionInfo pdi
= ((ProcessDefinitionDirectoryLocal)ctx.getEJBLocalObject())
.lookupProcessDefinitionInfo (packageId, processId);
if (!pdi.enabled) {
ctx.setRollbackOnly();
throw new NotEnabledException("Process definition disabled.");
}
return processLocalHome().create
(pdi.archivedXpdlId, pdi.processDefinition, requester);
} catch (CreateException ce) {
throw new EJBException(ce);
}
}
/**
* @param processDefinitionInfo
*/
private void cacheDefinition(ProcessDefinitionInfo processDefinitionInfo) {
synchronized (processDefinitionInfoCache) {
// Remove any existing entry
String processType
= processDefinitionInfo.processDefinition.packageId()
+ "/" + processDefinitionInfo.processDefinition.processId();
Long curRef = (Long)processDefinitionDBIds.get(processType);
if (curRef != null) {
processDefinitionInfoCache.remove(curRef);
}
// Add new entry
processDefinitionInfoCache.put
(new Long(processDefinitionInfo.dbid), processDefinitionInfo);
processDefinitionDBIds.put
(processType, new Long(processDefinitionInfo.dbid));
if (logger.isDebugEnabled()) {
logger.debug
("put (" + processDefinitionInfo
.processDefinition.packageId()
+ "/" + processDefinitionInfo
.processDefinition.processId() + ") "
+ "in cache");
}
}
}
/**
* Remove given process definition from cache.
*/
private void uncacheDefinition (String key) {
synchronized (processDefinitionInfoCache) {
Long ck = (Long)processDefinitionDBIds.get (key);
if (ck != null) {
processDefinitionDBIds.remove (key);
processDefinitionInfoCache.remove(ck);
if (logger.isDebugEnabled ()) {
logger.debug("removed ("+key+") from cache");
}
}
}
}
}