Invoke EJB Action
domkun Nov 15, 2007 6:22 AMHi there,
I had the problem, that I should invoke several SLSB operations located on another JBoss AS instance. For this purpose I wrote several actions doing all the jndi lookup stuff and so on...
Now I coded an action that is configured via jboss-esb.xml that uses reflection to do all the stuff for me and I only have one custom action that invokes any SLSB.
I thought maybe someone finds this helpful, so here is the code:
public class EJBProcessor extends AbstractActionLifecycle { private static final Logger log = Logger.getLogger(EJBProcessor.class); public static final String EJB_NAME = "ejb-name"; public static final String JNDI_NAME = "jndi-name"; public static final String EJB_METHOD = "method"; public static final String JAVA_TYPE = "type"; public static final String INICTXFACTORY = "initial-context-factory"; public static final String PROVIDERURL = "provider-url"; public static final String OUT_VAR = "esb-out-var"; public static final String DEFAULT_OUT = "DEFAULT_EJB_OUT"; public static final int ARG_PREFIX_LENGTH = 3; protected ConfigTree configTree; private Map<String, String> ejbRef; private Map<Integer, Argument> ejbParams; private List<String> ejbParamTypeNames; public EJBProcessor(ConfigTree config) { configTree = config; } public Message process(Message msg) throws ActionProcessingException, ConfigurationException { ejbRef = new HashMap<String, String>(); ejbParams = new HashMap<Integer, Argument>(); ejbParamTypeNames = new ArrayList<String>(); // Get the configuration from jboss-esb.xml ejbRef.put(EJB_NAME, configTree.getAttribute(EJB_NAME)); ejbRef.put(JNDI_NAME, configTree.getAttribute(JNDI_NAME)); ejbRef.put(EJB_METHOD, configTree.getAttribute(EJB_METHOD)); ejbRef.put(INICTXFACTORY, configTree.getAttribute(INICTXFACTORY)); ejbRef.put(PROVIDERURL, configTree.getAttribute(PROVIDERURL)); if (configTree.getAttribute(OUT_VAR) != null) { ejbRef.put(OUT_VAR, configTree.getAttribute(OUT_VAR)); } else { ejbRef.put(OUT_VAR, DEFAULT_OUT); } // Get all parameters for the EJB method, defined in jboss-esb.xml ConfigTree[] subElements = configTree.getAllChildren(); for (ConfigTree child : subElements) { Integer argNum; String jType; String esbLocation; argNum = Integer.parseInt(child.getName().substring(ARG_PREFIX_LENGTH)); jType = child.getAttribute(JAVA_TYPE); esbLocation = child.getWholeText(); ejbParams.put(argNum, new Argument(jType, esbLocation)); ejbParamTypeNames.add(jType); } // Check for missing configuration values for (String conf : ejbRef.values()) { if (conf == null) { throw new ConfigurationException("Error configuring EJBProcessor"); } } // Build Properties for InitialContext lookup Properties props = new Properties(); props.put(Context.INITIAL_CONTEXT_FACTORY, ejbRef.get(INICTXFACTORY)); props.put(Context.PROVIDER_URL, ejbRef.get(PROVIDERURL)); try { // Get the InitialContext InitialContext iniCtx = new InitialContext(props); // Lookup and narrow EJBHome home = (EJBHome) PortableRemoteObject.narrow( (EJBHome) iniCtx.lookup( ejbRef.get(JNDI_NAME)), EJBHome.class); // Get the EJB metadata EJBMetaData metaData = home.getEJBMetaData(); Class homeClass = metaData.getHomeInterfaceClass(); Class remoteClass = metaData.getRemoteInterfaceClass(); // convert handle to real home type home = (EJBHome) javax.rmi.PortableRemoteObject.narrow(home, homeClass); if (!(metaData.isSession() && metaData.isStatelessSession())) { throw new ActionProcessingException("Only SLSBs are supported!"); } EJBObject theRemote = (EJBObject) this.create(homeClass, home); // Assemble parameter array Object[] param = new Object[ejbParams.size()]; for (int i = 0; i < ejbParams.size(); i++) { // get the parameter from the esb message and // cast it to the in the jboss-esb.xml specified type param[ i ] = Class.forName(ejbParams.get(i).getType()).cast(msg.getBody().get(ejbParams.get(i).getLoc())); } msg.getBody().add(ejbRef.get(OUT_VAR), this.invoke(remoteClass, theRemote, ejbRef.get(EJB_METHOD), param)); log.debug("###########################################"); log.debug(msg); log.debug("###########################################"); } catch (Exception e) { throw new ActionProcessingException("Got an error while processing EJB " + ejbRef.get(EJB_NAME), new Throwable(e.getCause())); } return msg; } private static Object create(Class c, Object obj) throws Exception { Object ret = null; Method create = c.getMethod("create"); ret = create.invoke(obj); return ret; } private Object invoke(Class c, Object obj, String mname, Object[] params) throws Exception { // The return Object Object r = null; // Assemble method signature array Class[] sigArray = new Class[ejbParams.size()]; for (int i = 0; i < ejbParams.size(); i++) { sigArray[ i ] = Class.forName(ejbParams.get(i).getType()); } // Get the specified method Method method = c.getMethod(mname, sigArray); // finally invoke it... r = method.invoke(obj, params); return r; } // Helper inner class for method arguments and where to find it in the esb message class Argument { private String type; private String loc; public Argument(String javaType, String esbLoc) { type = javaType; loc = esbLoc; } public String getType() { return type; } public void setType(String type) { this.type = type; } public String getLoc() { return loc; } public void setLoc(String loc) { this.loc = loc; } } }
It is not at all well tested, but it works for me.
This is how you can configure it:
<action name="EJBTest" class="de.isogmbh.soa.crm.util.ESBActions.EJBProcessor"> <property name="ejb-name" value="MyStatelessSessionBean" /> <property name="jndi-name" value="ejb/MyStatelessSessionBean" /> <property name="initial-context-factory" value="org.jnp.interfaces.NamingContextFactory" /> <property name="provider-url" value="localhost:1099" /> <property name="method" value="myMethod" /> <property name="esb-out-var" value="MY_OUT_LOCATION"/> <property name="ejb-params"> <arg0 type="java.lang.String">named-message-body-content1</arg0> <arg1 type="java.lang.String">named-message-body-content2</arg1> <arg2 type="java.lang.String">named-message-body-content3</arg2> <arg3 type="java.lang.String">named-message-body-content4</arg3> </property> </action>
Further info:
<property name="esb-out-var" value="MY_OUT_LOCATION"/>
This line is optional and specifies the location where to store the result in the message (e.g. message.getBody.add("MY_OUT_LOCATION", Object). If it is not present the result is stored as "DEFAULT_EJB_OUT".
<property name="ejb-params"> <arg0 type="java.lang.String">named-message-body-content1</arg0> <arg1 type="java.lang.String">named-message-body-content2</arg1> <arg2 type="java.lang.String">named-message-body-content3</arg2> <arg3 type="java.lang.String">named-message-body-content4</arg3> </property>
This tells the action where to find the parameters in the message and the type of the parameters.
(e.g. <arg0 type="java.lang.String">named-message-body-content1</arg0> means the first argument of the SLSB method is a String and can be found in message.getBody().get("named-message-body-content1"))
Greetings,
Dominik