2006-01-10
| Table of Contents: |
| Rate This Article: | Add This Article To: |
( Page 2 of 3 )
Apache's AXIS has become a — some might say "the" — standard for developing SOAP services in Java and other languages. The normal deployment path is to build either a servlet or POJO (Plain Old Java Object) to receive the SOAP requests, and use the axis servlet to handle all of the talking. Then it is possible to use a POJO or servlet in front of your EJBs and connect SOAP directly to your enterprise application.
But this extra layer is no longer needed. It is now possible to define a stateless session bean as a service endpoint. You can directly connect this session bean to a SOAP interface, allowing client connects to talk directly to the EJB layer and remove the possibly unnecessary web layer completely. This results in less code being written, less layers of abstraction, and a cleaner overall design.
Sound good? I'll show you how.
To get everything working correctly, I'll use the following libraries and tools:
JBoss-4.0SP1: I use JBoss as our application server in this example. While you can use other application servers, JBoss is freely available for developers to test against.
Ant-1.6.0: To keep our build as simple and standard as possible, I use Ant to produce all of the files in this project.
Doclet-1.2.3: To reduce the amount of code to write for our EJBs, we use XDoclet to generate the interfaces.
Note that AXIS is not required for this project. JBoss comes with all of the libraries needed to generate the SOAP code, so we can skip this library.
We will use the most basic of bean examples:
package com.zarrastudios.session;
import javax.ejb.SessionBean;
import javax.ejb.CreateException;
/**
* @ejb.bean local-jndi-name="zds/local/Hello"
* view-type="service-endpoint"
* name="Hello"
* @wsee.port-component name="Hello" display-name="Hello World Example"
* @ejb.interface
* service-endpoint-class="com.zarrastudios.session.HelloInterface"
*/
public abstract class HelloWorldBean implements SessionBean {
/**
* @ejb.create-method
*/
public void ejbCreate() throws CreateException {
}
/**
* @ejb.interface-method
*/
public String hello(String s) {
return "Hello: " + s;
}
}
In this example, we have a very simple stateless session bean with a single method that responses to the string being passed into it. Note that in the XDoclet class tags we define this bean as having a view-type of "service-endpoint" — not local or remote. It is possible to produce a bean that can be all three, but we are keeping our focus limited in this example.
If you are not familiar with XDoclet, I strongly recommend downloading it and working through some of the examples posted on the web. It makes EJB development child's play compared to doing everything by hand.
The Build Script
That class is all of the Java code we need to write for this example. There are no interfaces for the EJBs (thanks to XDoclet) and no interfaces for the SOAP handler either (also thanks to XDoclet).
However, with this lack of Java code, some additional XML needs to be defined in the ant build.xml file. The normal flow for a Java project is clean >> compile >> jar. In this project things are slightly more complicated, as can be seen in Figure 1.
The actual build file is fairly complicated:
<project name="EJB SOAP Example" default="jar" basedir=".">
<property name="build.dir" value="build"/>
<property name="source.dir" value="src"/>
<property name="generate.dir" value="gen"/>
<property name="dist.dir" value="dist"/>
<property name="product.name" value="example"/>
<property name="meta-inf.dir" value="conf"/>
<property name="jsp.dir" value="jsp"/>
<property name="wsdl.name" value="example.wsdl"/>
<property name="ws.name" value="webservices.xml"/>
<property name="jax.name" value="mapping.xml"/>
<property name="jboss.server" value="zds"/>
<property file="${user.name}.properties"/>
<path id="xdoclet.classpath">
<fileset dir="${xdoclet.home}/lib">
<include name="*.jar"/>
</fileset>
<fileset dir="${jboss.home}/client">
<include name="jbossall-client.jar"/>
</fileset>
<fileset dir="${jboss.home}/server/${jboss.server}/lib">
<include name="jboss-servlet.jar"/>
<include name="jboss-j2ee.jar"/>
</fileset>
</path>
<path id="class.path">
<pathelement location="${build.dir}"/>
<fileset dir="${jboss.home}/client">
<include name="activation.jar"/>
<include name="jbossall-client.jar"/>
<include name="commons-logging.jar"/>
</fileset>
<fileset dir="${jboss.home}/server/${jboss.server}/deploy/jboss-ws4ee.sar/">
<include name="*.jar"/>
</fileset>
<fileset dir="${jboss.home}/server/${jboss.server}/lib">
<include name="jboss-servlet.jar"/>
<include name="jboss-j2ee.jar"/>
</fileset>
</path>
<taskdef name="ejbdoclet" classname="xdoclet.modules.ejb.EjbDocletTask"
classpathref="xdoclet.classpath"/>
<taskdef name="wseedoclet"
classname="xdoclet.modules.wsee.WseeDocletTask"
classpathref="xdoclet.classpath"/>
<target name="clean" description="Removes all generated files">
<delete>
<fileset dir="${meta-inf.dir}">
<include name="${ws.name}"/>
<include name="**/${wsdl.name}"/>
<include name="${jax.name}"/>
<include name="jboss.xml"/>
<include name="ejb-jar.xml"/>
</fileset>
</delete>
<delete dir="${meta-inf.dir}/META-INF"/>
<delete dir="${generate.dir}"/>
<delete dir="${build.dir}"/>
<delete dir="${dist.dir}"/>
</target>
<target name="generate" depends="clean">
<mkdir dir="${generate.dir}"/>
<ejbdoclet destDir="${generate.dir}" ejbspec="2.1">
<fileset dir="${source.dir}">
<include name="com/zarrastudios/session/**/*.java"/>
</fileset>
<entitycmp/>
<session/>
<localinterface/>
<localhomeinterface/>
<remoteinterface/>
<homeinterface/>
<valueobject>
<packageSubstitution packages="session"
substituteWith="data"/>
</valueobject>
<utilobject includeGUID="true" cacheHomes="true" kind="physical">
<packageSubstitution packages="session"
substituteWith="util"/>
</utilobject>
<deploymentdescriptor destdir="${meta-inf.dir}"/>
<jboss version="4.0" datasource="java:/mysql-zds"
datasourcemapping="mySQL"
destdir="${meta-inf.dir}"
xmlencoding="UTF-8"/>
<service-endpoint pattern="{0}Service"/>
</ejbdoclet>
</target>
<target name="wsdl" depends="compile">
<delete dir="${meta-inf.dir}/wsdl"/>
<mkdir dir="${meta-inf.dir}/wsdl"/>
<java classname="org.apache.axis.wsdl.Java2WSDL"
classpathref="class.path" fork="yes">
<arg value="-lhttp://this.value.is.replaced.by.jboss"/>
<arg value="-SHelloService"/>
<arg value="-sHelloPort"/>
<arg value="-o${meta-inf.dir}/wsdl/example.wsdl"/>
<arg value="-uLITERAL"/>
<arg value="com.zarrastudios.session.HelloInterface"/>
</java>
</target>
<target name="wsee" depends="wsdl">
<wseedoclet wseeSpec="1.1" jaxrpcMappingFile="${jax.name}"
wsdlFile="wsdl/${wsdl.name}"
destDir="${meta-inf.dir}" force="true">
<deploymentdescriptor name="HelloWorldServiceDD"
destinationFile="webservices.xml"/>
<fileset dir="src" includes="**/*Bean.java"/>
<jaxrpc-mapping/>
</wseedoclet>
<replace file="${meta-inf.dir}/${ws.name}" token="WEB-INF/" value="META-INF/"/>
</target>
<target name="compile" depends="generate">
<mkdir dir="${build.dir}"/>
<javac debug="true" destdir="${build.dir}" target="1.4" source="1.4">
<src path="src"/>
<src path="gen"/>
<include name="**/*.java"/>
</javac>
</target>
<target name="jar" depends="wsee">
<mkdir dir="${dist.dir}"/>
<delete file="${dist.dir}/${product.name}.jar"/>
<jar jarfile="${dist.dir}/${product.name}.jar">
<fileset dir="${build.dir}"/>
<metainf dir="${meta-inf.dir}">
<include name="*.xml"/>
<include name="**/*.wsdl"/>
</metainf>
</jar>
</target>
<target name="ear" depends="jar">
<ear destfile="${dist.dir}/${product.name}.ear"
appxml="${meta-inf.dir}/application.xml">
<fileset dir="dist">
<include name="${product.name}.jar"/>
</fileset>
</ear>
</target>
</project>
In this flow, we have a few additional steps which are detailed on the next page.
![]() |
|


