Apache SOAP and JBossAs shown in Basic Apache SOAP coding, one of the most elegant aspects of Apache SOAP is that it allows a Java class to be deployed as a service without changing the source code. For those developers worried about maximizing scalability, or those with a significant investment in EJB development, it would be nice if deploying an EJB as a service was similarly transparent. This document provides the details of installing an EJB container (JBoss) to work with Tomcat and Apache SOAP, creating an EJB, then deploying the EJB as a service in Apache SOAP. It will be demonstrated that it is indeed quite easy to deploy an EJB as a service. The examples and descriptions below assume that Apache SOAP has been installed according to these instructions. To the extent the environment in which you work differs from the one described by those instructions, you will need to mentally adjust the text below. Installing JBossWriting the EJB Deploying the EJB Deploying the SOAP Service Resources Installing JBoss
Note that I did not enable JBoss to run in the same Java VM as Tomcat, which is done by removing some comments in i:\JBoss-2.4.4\conf\jboss.jcml. In my configuration, JBoss and Tomcat run in separate processes, and thus use RMI to communicate. Return to top.Coding the EJBI chose to write a simple EJB in the spirit of the "Hello, world" examples used in Basic Apache SOAP Coding. The EJB is a stateless session bean with the following remote and home interfaces.
/** This is an Enterprise Java Bean Remote Interface */
public interface EJBHelloService1 extends javax.ejb.EJBObject {
/** Says hello to a person */
String helloString(String name) throws java.rmi.RemoteException;
}
/** This is a Home interface for the Session Bean */
public interface EJBHelloService1Home extends javax.ejb.EJBHome {
EJBHelloService1 create() throws javax.ejb.CreateException,
java.rmi.RemoteException;
}
The bean itself is quite pedestrian, as shown in the following code.
import java.rmi.RemoteException;
import java.security.Identity;
import java.util.Properties;
import javax.ejb.*;
/** This session bean can be used with a stateless or stateful SOAP EJB provider. */
public class EJBHelloService1Bean implements SessionBean {
private javax.ejb.SessionContext mySessionCtx = null;
private int nbcall = 0; // keeps track of the calls (by all clients)
public void ejbActivate() throws java.rmi.RemoteException {}
public void ejbCreate() throws javax.ejb.CreateException,
java.rmi.RemoteException {}
public void ejbPassivate() throws java.rmi.RemoteException {}
public void ejbRemove() throws java.rmi.RemoteException {}
/** Gets the session context */
public javax.ejb.SessionContext getSessionContext() {
return mySessionCtx;
}
/** Says hello to a person */
public String helloString(String name) {
try {
Thread.sleep(0); // keep this from Apache SOAP sample
} catch(InterruptedException ex){
} finally {
++nbcall;
return "Hello, " + name + ". This is my " + nbcall + " greeting.";
}
}
/** Sets the session context */
public void setSessionContext(javax.ejb.SessionContext ctx)
throws java.rmi.RemoteException {
mySessionCtx = ctx;
}
}
Likewise, the deployment descriptor is quite ordinary.
<?xml version="1.0"?>
<!DOCTYPE ejb-jar PUBLIC '-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 1.1//EN'
'http://java.sun.com/j2ee/dtds/ejb-jar_1_1.dtd'>
<ejb-jar>
<enterprise-beans>
<session>
<ejb-name>EJBHelloService1Name</ejb-name>
<home>EJBHelloService1Home</home>
<remote>EJBHelloService1</remote>
<ejb-class>EJBHelloService1Bean</ejb-class>
<session-type>Stateless</session-type>
<transaction-type>Container</transaction-type>
</session>
</enterprise-beans>
</ejb-jar>
Note that JBoss will use the value of ejb-name as the name for the bean in its naming service. Return to top.Deploying the EJBThe first step in deploying the bean is compiling and packaging it, which I did with the following set of commands. (Note: the deployment descriptor, ejb-jar.xml, is in the META-INF subdirectory.)
Deploying the bean to JBoss is simplicity itself: simply copy the jar file created in the last step above to the JBoss deploy directory.
xcopy /y ejbhello1.jar i:\jboss-2.4.4\deploy
Before moving on to the SOAP deployment, I wanted to verify that the EJB was working. The following code is a client that accesses the EJB directly through RMI. The properties that are set are the standard values when working with JBoss.
import javax.rmi.*;
import javax.ejb.*;
import javax.naming.*;
import javax.naming.Context.*;
public class EJBHelloDirect1 {
public static void main(String[] args) throws Exception {
java.util.Properties properties = new java.util.Properties();
properties.put("java.naming.factory.initial",
"org.jnp.interfaces.NamingContextFactory");
properties.put("java.naming.provider.url", "localhost:1099");
javax.naming.Context contxt = new javax.naming.InitialContext(properties);
EJBHelloService1Home home = (EJBHelloService1Home)
PortableRemoteObject.narrow(contxt.lookup("EJBHelloService1Name"),
Class.forName("EJBHelloService1Home"));
EJBHelloService1 hello = (EJBHelloService1) home.create();
String ret = hello.helloString("Brent");
System.out.println(ret);
}
}
The following commands compile and run this in my environment.
Deploying the SOAP ServiceArmed with a working EJB deployed in JBoss, we can now make it available to SOAP clients. The Apache SOAP deployment descriptor provides the information required for it to access the EJB.
<?xml version="1.0"?>
<isd:service xmlns:isd="http://xml.apache.org/xml-soap/deployment"
id="urn:ejbhello1">
<isd:provider type="org.apache.soap.providers.StatelessEJBProvider"
scope="Application"
methods="create">
<isd:option key="JNDIName" value="EJBHelloService1Name" />
<isd:option key="FullHomeInterfaceName" value="EJBHelloService1Home" />
<isd:option key="ContextProviderURL" value="localhost:1099" />
<isd:option key="FullContextFactoryName"
value="org.jnp.interfaces.NamingContextFactory" />
</isd:provider>
<isd:faultListener>org.apache.soap.server.DOMFaultListener
</isd:faultListener>
</isd:service>
The type attribute specified for the isd:provider element indicates to Apache SOAP that it should use the provider for a stateless EJB, rather than a "regular" Java class. The isd:option elements provide the information the provider needs to access the EJB. When using JBoss, the JNDIName must match the value of ejb-name provided in the ejb-jar.xml deployment descriptor for the EJB. FullHomeInterfaceName is the (fully qualified) class of the EJB home interface. ContextProviderURL and FullContextFactoryName represent default values specific to JBoss. There are several classes that Apache SOAP must be able to load in order to execute the code for the provider for stateless EJBs. These are in jars that are part of JBoss: jboss-j2ee.jar, jboss.jar and jnpserver.jar. I simply copied them from the JBoss distribution to the web application in Tomcat.
Apache SOAP also needs to access the EJB interfaces, EJBHelloService1 and EJBHelloService1Home. To support this, I copied the jar file for the EJB to the web application.
xcopy /y ejbhello1.jar j:\jakarta-tomcat-4.0.1\webapps\soap\WEB-INF\lib
To get the files copied to the web application to load, bounce Tomcat. Then deploy the new service.
The following client tests SOAP access to the EJB.
import java.io.*;
import java.net.*;
import java.util.*;
import org.apache.soap.*;
import org.apache.soap.rpc.*;
/**
*/
public class EJBHelloClient1 {
/**
*/
public static void main (String[] args) throws Exception {
if (args.length != 2 ) {
System.err.println ("Usage: java EJBHelloClient1 " +
" endpointURL name");
System.exit (1);
}
URL url = new URL(args[0]);
String name = args[1];
String targetObjectURI = "urn:ejbhello1";
String methodName = "helloString";
String encodingStyleURI = Constants.NS_URI_SOAP_ENC;
for (int i = 0; i < 10; i++) {
Call call = new Call();
call.setTargetObjectURI(targetObjectURI);
call.setMethodName(methodName);
call.setEncodingStyleURI(encodingStyleURI);
Vector params = new Vector();
params.addElement (new Parameter("name", String.class, name, null));
call.setParams(params);
Response resp = call.invoke(url, targetObjectURI);
if (resp.generatedFault()) {
Fault fault = resp.getFault();
System.out.println("Fault: ");
System.out.println(" Code = " + fault.getFaultCode());
System.out.println(" String = " + fault.getFaultString());
} else {
Parameter result = resp.getReturnValue();
System.out.println("Result: " + result.getValue());
}
}
}
}
You can download the source for this page. Return to top.ResourcesAccessing EJBs from Visual Basic.NET using JBoss, which does not use SOAP but is an interesting application of JBoss and Tomcat (using the combined distribution in which both run in a single VM). Note that this is the site of a vendor that supplies the software that bridges .NET and J2EE.Return to top. | |
|
Copyright © 2002 Scott Nichol. |