Calling EJB 3 from BPEL 10.1.3

Despite a number of useful blog entries out there it seems that calling EJB 3 from BPEL is still stumping people so thought I would go through the steps.  Note that these are much easier than in earlier releases of EJB and BPEL.

Create an EJB 3 Session Bean

First thing I did was create a simple EJB 3 session bean that had two methods

  • hello - which uses just simple String input and output.
  • swap - which uses a custom class as input and output.

I used JDeveloper to create a simple EJB 3 session bean and accepted all the defaults.

Here is the code for the bean class

package testejb;

import javax.ejb.Stateless;
import javax.jws.WebService;
import javax.jws.soap.SOAPBinding;
import oracle.webservices.annotations.WSIFEJBBinding;
import javax.jws.soap.SOAPBinding.Style;

@Stateless(name="MySessionEJB")
public class MySessionEJBBean implements MySessionEJB, MySessionEJBLocal {
    public MySessionEJBBean() {
    }
    public String hello(String name) {
        return "Hello "+name;
    }
    public MyComplexType swap(MyComplexType input) {
        MyComplexType retval = new MyComplexType();
        retval.data1 = input.data2;
        retval.data2 = input.data1;
        return retval;
    }
}

The MyComplexType class is shown below

package testejb;
import java.io.Serializable;

public class MyComplexType implements Serializable {
    public String data1;
    public String data2;
};

The swap and hello methods were marked as available in the local and remote interfaces.

I then created a deployment descriptor and deployed the EJB to an OC4J container.

Testing EJB 3 Session Bean

I then used JDeveloper to generate a simple test client to ensure that the bean remote interface was working.  The code for that is shown below:

package testejb;

import java.util.Hashtable;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;

public class MySessionEJBClient {
    public static void main(String [] args) {
        try {
            final Context context = getInitialContext();
            MySessionEJB mySessionEJB = (MySessionEJB)context.lookup("MySessionEJB");
            // Call any of the Remote methods below to access the EJB
            // mySessionEJB.hello(  name );
             System.out.println(mySessionEJB.hello("Antony"));
            // mySessionEJB.swap(  input );
            MyComplexType req = new MySessionEJBBean.MyComplexType();
            MyComplexType resp;
            req.data1 = "d1";
            req.data2 = "d2";
            resp = mySessionEJB.swap(req);
            System.out.println("data1="+resp.data1);
            System.out.println("data2="+resp.data2);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    private static Context getInitialContext() throws NamingException {
        Hashtable env = new Hashtable();
        // Oracle Application Server 10g connection details
        env.put( Context.INITIAL_CONTEXT_FACTORY, "oracle.j2ee.rmi.RMIInitialContextFactory" );
        env.put( Context.SECURITY_PRINCIPAL, "oc4jadmin" );
        env.put( Context.SECURITY_CREDENTIALS, "welcome1" );
        env.put(Context.PROVIDER_URL, "opmn:ormi://w2k3:6003:home/MySessionEJBDeploymentProfile");
        return new InitialContext( env );
    }
}

Once I had verified that I had a working EJB the next step was to create a WSDL for the EJB

Creating WSDL for EJB 3

The easiest way to create a WSDL is to get JDev and OC4J to do most of the work.  To do this modify the Bean class to add Web Service annotations as shown below

import javax.jws.WebService;
import javax.jws.soap.SOAPBinding;
import oracle.webservices.annotations.WSIFEJBBinding;
import javax.jws.soap.SOAPBinding.Style;

...

@Stateless(name="MySessionEJB")
@WebService
@WSIFEJBBinding
@SOAPBinding(style=Style.RPC)
public class MySessionEJBBean implements MySessionEJB, MySessionEJBLocal {

The @WebService identifies that this should be a web service, the @WSIFEJBBinding says that we want to support an EJB binding and the @SOAPBinding(style=Style.RPC) says that the style of web service should be an RPC form.  The RPC style is needed for the EJB binding to work properly.

Having updated the annotations we can then re-deploy the EJB and access the newly created WSDL through the Web Service Test facility of Enterprise Manager.  This WSDL can be downloaded and used as the basis of the WSDL for access to the EJB.

Modifying the WSDL for use in BPEL

Before we use the WSDL we need to modify it as follows.

First we remove the SOAP port by commenting it out as shown below:

<!--
    <port name="MySessionEJB" binding="tns:MySessionEJBBeanSoapHttp">
        <soap:address location="http://w2k3/MySessionEJBDeploymentProfile/MySessionEJB"/>
    </port>
-->

We then enrich the EJB port by adding additional information about the EJB.  The initial EJB port is shown below:

<port name="WsifEjb" binding="tns:WsifEjbBinding">
    <ejb:address initialContextFactory="com.evermind.server.rmi.RMIInitialContextFactory"/>
</port>

Unfortunately this does not say where the service is located so we need to enrich this information by adding a jndiName field which is the name of the EJB and a jndiProviderURL which is the endpoint location of the naming context.  Both these values can be taken from the test client (the lookup() parameter and the jndi.PROVIDER_URL property value).  This gives us the following service element:

<port name="WsifEjb" binding="tns:WsifEjbBinding">
    <ejb:address initialContextFactory="com.evermind.server.rmi.RMIInitialContextFactory"
                 jndiName="MySessionEJB"
                 jndiProviderURL="opmn:ormi://10.148.55.149:6003:home/MySessionEJBDeploymentProfile"/>
</port>

We can now import this WSDL into our BPEL process and use it as any other partner link.

Security Concern

When using the EJB WSDL we need to provide a username and password for the JNDI lookup.  This is done by adding the following properties to the partner link:

  • java.naming.security.principal
  • java.naming.security.credentials

and setting them to appropriate values such as oc4jadmin and the corresponding password.

Classes

In order for BPEL to find the Java classes they must be on the classpath.  Move the required class files generated by the EJB into the $ORACLE_HOME/bpel/system/classes directory and restart the OC4J container.

Gotchas

There are a number of things that can go wrong.

If you get a name not found exception then it indicates that the port properties are not properly set up or the username and password are wrong.

If you get an unable to find method error it is probably because you failed to use RPC style, and instead used DOC style web services.

If you get a class not found then you have probably forgotten to deploy the classes.

Sample Code

I have uploaded the following sample application which consists of an EJB project with client code and an BPEL project.

Sample Application EJBTest.zip

Summary

In this blog entry we have looked at how to create a WSIF binding to invoke an EJB 3 Stateless Session Bean.  If you don't have the source to the bean then you can always create an equivalent interface and follow the steps in here to generate the WSDL.  Hopefully this has made using EJB 3 from BPEL a little easier.

Comments:

Post a Comment:
Comments are closed for this entry.
About

Musings on Fusion Middleware and SOA Picture of Antony Antony works with customers across the US and Canada in implementing SOA and other Fusion Middleware solutions. Antony is the co-author of the SOA Suite 11g Developers Cookbook, the SOA Suite 11g Developers Guide and the SOA Suite Developers Guide.

Search

Archives
« April 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today