3 Replies Latest reply on Nov 19, 2007 10:10 AM by marklittle

    Invoke EJB Action

    domkun

      Hi 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