/*
* This file is part of the WfMOpen project.
* Copyright (C) 2001-2007 Danet GmbH (www.danet.de), BU BTS.
* 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: EJBInvoker.java 2550 2007-10-22 14:00:54Z $
*
* $Log$
* Revision 1.1 2007/03/22 13:50:44 schnelle
* Initial release.
*
*/
package de.danet.an.workflow.tools;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.rmi.RemoteException;
import java.util.HashMap;
import java.util.Map;
import javax.ejb.EJBHome;
import javax.ejb.EJBMetaData;
import javax.ejb.EJBObject;
import javax.naming.NamingException;
import de.danet.an.util.EJBUtil;
import de.danet.an.workflow.api.Activity;
import de.danet.an.workflow.api.FormalParameter;
import de.danet.an.workflow.api.SAXEventBuffer;
import de.danet.an.workflow.spis.aii.ApplicationNotStoppedException;
import de.danet.an.workflow.spis.aii.CannotExecuteException;
import de.danet.an.workflow.spis.aii.ResultProvider;
import de.danet.an.workflow.spis.aii.ToolAgent;
import de.danet.an.workflow.util.XPDLUtil;
/**
* This class provides a facility to call EJBs.
*
* @author Dirk Schnelle
*
*/
public class EJBInvoker implements ToolAgent, ResultProvider {
private static final org.apache.commons.logging.Log logger
= org.apache.commons.logging.LogFactory.getLog(EJBInvoker.class);
/** The result container. */
private ThreadLocal result = new ThreadLocal ();
/* (non-Javadoc)
* Comment copied from interface or super class.
*/
public void invoke(Activity activity, FormalParameter[] formalParameters,
Map actualParameters) throws RemoteException,
CannotExecuteException {
int len = formalParameters.length;
if (len < 3) {
throw new CannotExecuteException("Too few arguments. "
+ "At least JndiName, HomeClass, and Method are required!");
}
String jndiName
= (String) actualParameters.get(formalParameters[0].id());
String homeClassName
= (String) actualParameters.get(formalParameters[1].id());
String methodName
= (String) actualParameters.get(formalParameters[2].id());
int numArgs = len - 3;
String returnParam = null;
if (len > 3) {
FormalParameter.Mode lastParamMode
= formalParameters[len - 1].mode();
if ((lastParamMode == FormalParameter.Mode.OUT)
|| (lastParamMode == FormalParameter.Mode.INOUT)) {
returnParam = formalParameters[len - 1].id();
numArgs --;
}
}
Object[] args = new Object[numArgs];
Class[] sig = new Class[numArgs];
parseArguments(formalParameters, actualParameters, numArgs, args, sig);
Class homeClass;
try {
ClassLoader cl = Thread.currentThread().getContextClassLoader();
homeClass = cl.loadClass(homeClassName);
} catch (ClassNotFoundException e) {
throw new CannotExecuteException("cannot load HomeClass '"
+ homeClassName + "'");
}
if (logger.isDebugEnabled()) {
String str = getDebugMethodCall(formalParameters, jndiName,
homeClassName, methodName, numArgs, args, sig);
logger.debug(str);
}
Object callResult;
try {
callResult = callEJB(jndiName, homeClass, methodName, sig, args);
} catch (NamingException e) {
throw new CannotExecuteException(e.getMessage(), e);
} catch (IllegalArgumentException e) {
throw new CannotExecuteException(e.getMessage(), e);
} catch (IllegalAccessException e) {
throw new CannotExecuteException(e.getMessage(), e);
} catch (InvocationTargetException e) {
throw new CannotExecuteException(e.getMessage(), e);
} catch (NoSuchMethodException e) {
throw new CannotExecuteException(e.getMessage(), e);
}
if (returnParam != null) {
Map resData = new HashMap ();
resData.put(returnParam, callResult);
result.set(resData);
}
}
/**
* Parses the given arguments to create the signature and the arguments
* for the method call.
* @param formalParameters formal parameters of the tool
* @param actualParameters values for the parameters
* @param numArgs number of arguments
* @param args values for the method call (must be of size
* <code>numArgs</code>
* @param sig signature of the method (must be of size
* <code>numArgs</code>
* @throws CannotExecuteException
* Unknown parameter type.
*/
private void parseArguments(FormalParameter[] formalParameters,
Map actualParameters, int numArgs, Object[] args, Class[] sig)
throws CannotExecuteException {
for (int i=0; i<numArgs; i++) {
String id = formalParameters[i+3].id();
args[i] = actualParameters.get(id);
Object type = formalParameters[i+3].type();
if (type.equals(Long.class)) {
sig[i] = long.class;
} else if (type.equals(Boolean.class)) {
sig[i] = boolean.class;
} else if (type.equals(Double.class)) {
sig[i] = double.class;
} else if (XPDLUtil.isXMLType(type)) {
if (args[i] instanceof SAXEventBuffer) {
sig[i] = SAXEventBuffer.class;
} else if (args[i] instanceof org.w3c.dom.DocumentFragment) {
sig[i] = org.w3c.dom.DocumentFragment.class;
} else if (args[i] instanceof org.jdom.Element) {
sig[i] = org.jdom.Element.class;
} else {
String msg = "Got argument of XML type, but cannot"
+ " recognize value's class: " + args[i].getClass();
throw new CannotExecuteException (msg);
}
} else {
sig[i] = (Class) type;
}
}
}
/**
* Create a human readable output of the method call.
* @param formalParameters
* @param jndiName
* @param homeClassName
* @param methodName
* @param numArgs
* @param args
* @param sig
* @return
*/
private String getDebugMethodCall(FormalParameter[] formalParameters,
String jndiName, String homeClassName, String methodName,
int numArgs, Object[] args, Class[] sig) {
StringBuffer str = new StringBuffer();
str.append("calling ");
str.append(homeClassName);
str.append(" (");
str.append(jndiName);
str.append(") ");
str.append(methodName);
str.append("(");
for (int i=0; i<numArgs; i++) {
if (XPDLUtil.isXMLType(formalParameters[i+3].type())) {
str.append("XML");
} else {
if (sig[i].equals(String.class)) {
str.append("\"");
}
str.append(args[i]);
if (sig[i].equals(String.class)) {
str.append("\"");
}
}
if (i != numArgs - 1) {
str.append(", ");
}
}
str.append(")");
return str.toString();
}
/**
* Call the EJB.
* @param jndiName the JNDI name of the EJB
* @param homeClass the home class of the EJB
* @param method the name of the method to call.
* @param sig the signature of the method to call
* @param args the arguments to provide in the call
* @return Result of invocation.
* @throws NamingException
* JNDI name could not be resolved.
* @throws RemoteException
* Error accessing the EJB
* @throws IllegalArgumentException
* Error in the signature.
* @throws IllegalAccessException
* Method is not accessible.
* @throws InvocationTargetException
* Error in method call.
* @throws NoSuchMethodException
* Method is unknown.
*/
private Object callEJB(String jndiName, Class homeClass, String method,
Class[] sig, Object[] args) throws NamingException, RemoteException,
IllegalArgumentException, IllegalAccessException,
InvocationTargetException, NoSuchMethodException {
EJBHome home = EJBUtil.lookupEJBHome(homeClass, jndiName);
EJBMetaData md = home.getEJBMetaData();
EJBObject remote = (EJBObject)
invoke(homeClass, home, "create", null, null);
Class remoteClass = md.getRemoteInterfaceClass();
return invoke(remoteClass, remote, method, sig, args);
}
/**
* Convenience method to call a method on the given object.
* @param clazz The class of the object.
* @param obj the object.
* @param methodName the name of the method to call.
* @param sig the signature of the method.
* @param args the arguments to provide.
* @return result of the method call.
* @throws IllegalArgumentException
* @throws IllegalAccessException
* @throws InvocationTargetException
* @throws NoSuchMethodException
*/
private Object invoke(Class clazz, Object obj, String methodName,
Class[] sig, Object[] args) throws IllegalArgumentException,
IllegalAccessException, InvocationTargetException,
NoSuchMethodException {
Method method = clazz.getMethod(methodName, sig);
return method.invoke(obj, args);
}
/* (non-Javadoc)
* Comment copied from interface or super class.
*/
public void terminate(Activity activity)
throws ApplicationNotStoppedException, RemoteException {
throw new ApplicationNotStoppedException
("Terminate not implemented for EJBTool.");
}
/* (non-Javadoc)
* Comment copied from interface or super class.
*/
public Object result() {
Object res = result.get();
result.set (null);
return res;
}
}