/*
* 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: StandardWorkflowServiceFactory.java 2921 2009-02-14 20:41:30Z mlipp $
*
* $Log$
* Revision 1.8 2007/02/08 15:26:28 mlipp
* Properly propagate cause.
*
* Revision 1.7 2006/10/10 14:31:48 drmlipp
* Improved error message.
*
* Revision 1.6 2006/10/09 13:51:32 drmlipp
* New configuration options.
*
* Revision 1.5 2006/09/29 12:32:09 drmlipp
* Consistently using WfMOpen as projct name now.
*
* Revision 1.4 2006/09/27 15:14:17 drmlipp
* Added option to configure workflow engine's initial context
* using default initial context.
*
* Revision 1.3 2005/04/22 15:11:04 drmlipp
* Merged changes from 1.3 branch up to 1.3p15.
*
* Revision 1.2.2.1 2005/04/11 07:24:55 drmlipp
* Fixed javadoc.
*
* Revision 1.2 2004/12/20 22:30:54 drmlipp
* Updated JNDI names.
*
* Revision 1.1.1.1 2004/08/18 15:17:38 drmlipp
* Update to 1.2
*
* Revision 1.1 2004/02/21 21:31:00 lipp
* Some more refactoring to resolve cyclic dependencies.
*
* Revision 1.10 2004/01/23 12:49:26 lipp
* Fixes to WorkflowService[Factory] implementation/documentation.
*
* Revision 1.9 2004/01/22 15:06:09 lipp
* Clarified serializability of workflow service.
*
* Revision 1.8 2003/11/27 16:27:16 lipp
* Improved logging.
*
* Revision 1.7 2003/11/21 14:54:20 lipp
* Support initial context override.
*
* Revision 1.6 2003/06/27 08:51:45 lipp
* Fixed copyright/license information.
*
* Revision 1.5 2003/05/02 14:55:58 lipp
* Resolved some more package dependencies.
*
* Revision 1.4 2002/11/18 11:57:43 lipp
* Corrected comment.
*
* Revision 1.3 2002/10/09 11:17:13 lipp
* Removed extra semicolon.
*
* Revision 1.2 2002/09/18 20:48:21 lipp
* Cleanly separated workflow engine and service.
*
* Revision 1.1 2002/09/18 14:29:44 lipp
* Partial workflow service implementation added.
*
*/
package de.danet.an.workflow.ejbs.client;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
import java.rmi.RemoteException;
import javax.ejb.CreateException;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.rmi.PortableRemoteObject;
import de.danet.an.util.EJBUtil;
import de.danet.an.util.logging.RequestLog;
import de.danet.an.workflow.api.FactoryConfigurationError;
import de.danet.an.workflow.api.WorkflowService;
import de.danet.an.workflow.api.WorkflowServiceFactory;
import de.danet.an.workflow.ejbs.WorkflowEngine;
import de.danet.an.workflow.ejbs.WorkflowEngineHome;
/**
* Implements the workflow service factory.<P>
*
* Usage of this class as service factory requires an additional
* configuration parameter. The service factory implementation needs
* to connect to the workflow engine EJB. In order to do so, it needs
* a JNDI name to look up the engine's home interface. As JNDI names
* must be changeable by the application deployer, the name can't be
* hard coded.<P>
*
* This factory therefore uses the following ordered lookup procedure
* to determine the JNDI name of the workflow engine EJB home interface:
* <ul>
* <li>Look for a property "<code>de.danet.an.workflow.engine</code>" among
* the properties set for this factory.</li>
* <li>Look for a name in
* <code>java:comp/env/de.danet.an.workflow.engine</code>.
* The configuration for the
* <code>StandardWorkflowServiceFactory</code> using
* this mechanism thus looks like:
* <PRE><env-entry>
* <description>Configure the chosen factory</description>
* <env-entry-name>de.danet.an.workflow.engine</env-entry-name>
* <env-entry-type>java.lang.String</env-entry-type>
* <env-entry-value><i>JNDI name of workflow engine EJB home</i></env-entry-value>
* </env-entry></PRE>
* Note that this environment entry must be inserted in the
* <code>ejb-jar.xml</code> or <code>web.xml</code> for every EJB
* resp. servlet that calls the
* {@link de.danet.an.workflow.api.WorkflowServiceFactory#newInstance
* <code>newInstance</code>} method of
* <code>WorkflowServiceFactory</code>.</li>
*
* <li>Find the application resource file
* <code>de.danet.an.workflow-wfs.properties</code>
* and look for an entry "<code>engine
* = <i>JNDI name of workflow engine EJB home</i></code>".</li>
* </ul>
*
*/
public class StandardWorkflowServiceFactory extends WorkflowServiceFactory {
private static final org.apache.commons.logging.Log logger
= org.apache.commons.logging.LogFactory.getLog
(StandardWorkflowServiceFactory.class);
private static Map defaultHomeNames = new HashMap ();
/** The home interface name of the assignment service. */
private String homeName = null;
/** The initial context used for lookups. */
private Context serverContext = null;
/**
* Creates an instance of <code>StandardWorkflowServiceFactory</code>
* and looks up the associated EJB.
*/
public StandardWorkflowServiceFactory () {
}
/* Comment copied from interface. */
public WorkflowService newWorkflowService ()
throws FactoryConfigurationError {
Map recoveryProps = new HashMap ();
Hashtable env = null;
try {
// get JNDI
Object o = getProperties().get ("javax.naming.InitialContext");
if (o != null && o instanceof Context) {
serverContext = (Context)o;
} else {
o = getProperties()
.get("javax.naming.InitialContext.Environment");
if (o == null || !(o instanceof Hashtable)) {
try {
String factory = (String)EJBUtil.lookupJNDIEntry
("java:comp/env/de.danet.an.workflow.api."
+ "WorkflowService.NAMING_CONTEXT_FACTORY");
String url = (String)EJBUtil.lookupJNDIEntry
("java:comp/env/de.danet.an.workflow.api."
+ "WorkflowService.NAMING_CONTEXT_URL");
o = new Hashtable ();
((Hashtable)o).put
(Context.INITIAL_CONTEXT_FACTORY, factory);
((Hashtable)o).put (Context.PROVIDER_URL, url);
} catch (NamingException e) {
// fall through
}
}
if (o != null && o instanceof Hashtable) {
serverContext = new InitialContext((Hashtable)o);
} else {
serverContext = new InitialContext();
}
}
env = serverContext.getEnvironment();
recoveryProps.put
("javax.naming.InitialContext.Environment", env);
// get home name of associated engine EJB
o = getProperties().get("de.danet.an.workflow.engine");
if (o != null && o instanceof String) {
homeName = (String)o;
} else {
homeName = (String)defaultHomeNames.get (env);
if (homeName == null) {
homeName = findHomeName();
defaultHomeNames.put (env, homeName);
}
}
recoveryProps.put ("de.danet.an.workflow.engine", homeName);
} catch (NamingException e) {
logger.error (e.getMessage (), e);
throw new FactoryConfigurationError
("Problem accessing JNDI: " + e.getMessage());
}
if (logger.isDebugEnabled ()) {
logger.debug
("Creating new WorkflowService for "
+ mapToString("InitialContext", env) + "/" + homeName);
}
try {
// Do not use EJBUtil here because we do not want
// de.danet.an.workflow.api to depend on other packes
// than de.danet.an.workflow.omgcore
Object objref = serverContext.lookup(homeName);
WorkflowEngineHome wfeh
= (WorkflowEngineHome)PortableRemoteObject.narrow
(objref, WorkflowEngineHome.class);
WorkflowEngine wfe = wfeh.create();
WorkflowService service = new StandardWorkflowService
(recoveryProps, serverContext, wfe);
return service;
} catch (NamingException e) {
logger.error (e.getMessage (), e);
throw new FactoryConfigurationError
("Problem accessing JNDI: " + e.getMessage(), e);
} catch (CreateException e) {
logger.error (e.getMessage (), e);
throw new FactoryConfigurationError
("Cannot create WorkflowEngineEJB: " + e.getMessage(), e);
} catch (RemoteException e) {
logger.error (e.getMessage (), e);
throw new FactoryConfigurationError
("Cannot create WorkflowEngineEJB: " + e.getMessage(), e);
}
}
/**
* Tries to find the name of the workflow service home interface
* as described {@link StandardWorkflowServiceFactory
* for the class}.
*/
private String findHomeName () throws FactoryConfigurationError {
String home = null;
try {
home = (String)serverContext.lookup
("java:comp/env/de.danet.an.workflow.engine");
return home;
} catch (NamingException ne) {
// Name not defined
}
// try properties
try {
InputStream is = StandardWorkflowServiceFactory.class
.getResourceAsStream ("/de.danet.an.workflow-wfs.properties");
if (is != null) {
Properties props = new Properties();
props.load (is);
home = props.getProperty ("engine");
if (home != null) {
return home;
}
}
} catch (IOException ex) {
}
throw new FactoryConfigurationError
("JNDI name of workflow engine neither set as property, "
+ "specified as JNDI environment entry nor "
+ "as entry in de.danet.an.workflow-wfs.properties; "
+ "see section \"Workflow Module\" in user manual.");
}
private String mapToString (String name, Map map) {
StringBuffer res = new StringBuffer ();
res.append(name + "[");
if (map != null) {
boolean first = true;
for (Iterator i = map.keySet().iterator(); i.hasNext ();) {
Object key = i.next ();
Object value = map.get(key);
if (!first) {
res.append (",");
} else {
first = false;
}
res.append (key.toString() + "=" + value.toString());
}
}
res.append ("]");
return res.toString ();
}
}