Exposing EJBs as Webservices using JaxWs 2.0
Objectives
This how-to have the objectives of quickly bootstrap you on JAX-WS webservices creation. It focus on how to expose EJB3 as webservices, and do not explain client view of the web services.
After reading this how to you should be able to:
- Expose an existing EJB3 as a webservice
- Generate a WSDL file (+ JAXB required classes) from a Java interface
- Generate JAX-WS (server-side) standards artifacts (classes, ...) from the WSDL
- Deploy the web service using JOnAS
- Access it though its URL
Pre-requisites
JOnAS Services activation
Before everything, the 'jaxws' service should be activated on JOnAS startup.
See the 'jonas.services' property in your {JONAS_BASE}/conf/jonas.properties.
If using Ant to generate JAX-WS artifacts (classes + XML)
- Download latest CXF 2.0.x (At the time of writing it's the 2.0.8)
- Unpack it
- We will refer to this directory by using CXF_HOME in the rest of this document
The EJB
There are 2 ways to develop a web service, you can start from a Java SIB (Service Implementation Bean) or from an existing WSDL.
The WSDL is an XML format that describe both the service contract (the interface) and technical datas (XSD types, URL of the endpoints, ...) that will help to use the web service.
The Service Implementation Bean is basically your EJB class and a Service Endpoint interface (SEI), both annotated with some JAX-WS annotations.
Starting from Java
The preferred way when starting a JAX-WS endpoint from Java is to start by writing a SEI (Service Endpoint Interface). This interface describes the service that will be exposed to clients.
Once the SEI is ready, It's time to write the Service Implementation Bean (SIB) that is an implementation of the SEI interface.
Service Endpoint Interface
Here, a simple Stock Quote Reporter service interface is described:
targetNamespace="http://jonas.ow2.org/examples/jaxws/quote")
public interface QuoteReporter {
public Quote getQuote(@WebParam(name="ticker") String ticker);
}
the @WebWervice annotation, placed on a SEI, provides the information required to generate a valid WSDL:
- name: this value will be used as the wsdl:portType name in the generated WSDL
- targetNamespace: this value will be used as the XML namespace containg your WSDL declaration. For example, the serviceName provided above is "prefixed" with this value ({targetNamespace}serviceName -> {http://easybeans.ow2.org/examples/cxf/greeter}SOAPService). It's required that this value is a URI, it's NOT required to use an URL or that this URL is accessible.
Service Implementation Bean
@WebService(portName="QuoteReporterPort",
serviceName="QuoteReporterService",
targetNamespace="http://jonas.ow2.org/examples/jaxws/quote",
endpointInterface="org.ow2.jonas.examples.jaxws.quote.QuoteReporter",
wsdlLocation="META-INF/wsdl/QuoteReporterService.wsdl")
public class QuoteReporterBean implements QuoteReporter {
public Quote getQuote(String ticker) {
return new Quote(ticker, Math.random() * 100);
}
}
You have noticed that there is also a @WebWervice annotation on the SIB.
<p/>
This time, this annotation will provide a value for SIB specific attributes:
- portName: this value specifies the wsdl:port to be used
- serviceName: this value specifies the wsdl:service to be used
- endpointInterface: this value specifies the service endpoint interface classname (SEI)
- wsdlLocation: this value describe where the JAX-WS container, at runtime, will be able to find the associated WSDL. In this case it's in the standard directory (META-INF/wsdl).
<p/>
As this class is also annotated with @Stateless, this component will be deployed as a Bean.
Generating WSDL using Ant tasks
First, declares the CXF namespace in your project element
xmlns:cxf="antlib:org.apache.cxf.ant.extensions">
Then, enable the CXF ant tasks in your build:
location="!!! CXF_HOME HERE !!!" />
<path id="cxf.classpath">
<pathelement location="${cxf.home}/lib/cxf-manifest.jar" />
</path>
<path id="cxf.anttasks.classpath">
<pathelement location="${cxf.home}/modules/integration/cxf-anttasks-2.0.8.jar" />
<path refid="cxf.classpath" />
</path>
<taskdef uri="antlib:org.apache.cxf.ant.extensions"
resource="org/apache/cxf/ant/extensions/antlib.xml"
classpathref="cxf.anttasks.classpath" />
Now you can use the Java 2 WSDL ant task.
This task will generate for you, from the SEI:
- A WSDL file (describing the XML contract)
- Plus a XML Schema files describing the types that will be exchanged on the wire
- Plus some JAXB 2.0 classes
<mkdir dir="${build.generated.dir}"/>
<property name="sei.classname"
value="org.ow2.jonas.examples.jaxws.quote.QuoteReporter"/>
<echo level="info" message="Generating WSDL from Service Endpoint Interface (${sei.classname}) ..."/>
<cxf:java2wsdl sei="${sei.classname}"
resourceDestDir="${build.generated.dir}">
<classpath>
<pathelement location="${build.classes.dir}"/>
</classpath>
</cxf:java2wsdl>
</target>
Remember that this is the SEI that is used for WSDL generation, NOT the SIB !
Starting from WSDL
Starting a endpoint development from WSDL means that you have a WSDL that describes your service contract before having your EJB code.
The idea here is to generate as much JAX-WS artifacts as possible from a given WSDL.
CXF provides a WSDL2Java ant task that will generate the SEI interface, a skeleton of a SIB (although it's not yet an EJB) plus all the JAXB artifacts that the Java2WSDL ant tasks is also generating:
- {portType}.java that is the SEI
- {portType}Impl.java that is the SIB
- JAXB annotated classes + ObjectFactory
Some generated classes are only useful on the client side (the one who will use the service), so theses generated classes can be safely suppressed:
- {service}.java
- {portType}_{port}_Server.java is an helper class for Java SE environments

Implementing the SIB
First thing to do, you may rename your <portType>Impl.java to a more EJB name (like XYZBean.java), but that's up to you ! At least, doing so will prevent your EJB to be overwritten if an accidental 2nd attempt to wsdl2java is made.
* This class was generated by Apache CXF 2.0.8
* Fri Oct 10 15:04:06 GMT+01:00 2008
* Generated source version: 2.0.8
*
*/
@javax.jws.WebService(name="HelloWorldType",
serviceName="HelloWorldService",
portName="HelloWorldPort",
targetNamespace="http://jonas.ow2.org/examples/jaxws/greeter",
wsdlLocation="file:etc/wsdl/HelloWorld.wsdl",
endpointInterface="org.ow2.jonas.examples.jaxws.greeter.HelloWorldType")
public class HelloWorldTypeImpl implements HelloWorldType {
private static final Logger LOG = Logger.getLogger(HelloWorldTypeImpl.class.getName());
/* (non-Javadoc)
* @see org.ow2.jonas.examples.jaxws.greeter.HelloWorldType#hello(java.lang.String name )*
*/
public java.lang.String hello(java.lang.String name) {
LOG.info("Executing operation hello");
System.out.println(name);
try {
java.lang.String _return = "";
return _return;
} catch (Exception ex) {
ex.printStackTrace();
throw new RuntimeException(ex);
}
}
If you look at the SIB, you will discover that it is very similar to the SIB that was produced with the "Java first" approach. At least, the JAX-WS annotations are here.
Concerning the @WebService annotation, the only change to do is to change the wsdlLocation value in order to match the location of the WSDL in the soon to be created EjbJar file (META-INF/wsdl instead of etc/wsdl in my case).
All you have to do is to implement the real behavior of the method (hello() should return something better than an empty string), and declare your class as an EJB.
@WebService(name="HelloWorldType",
serviceName="HelloWorldService",
portName="HelloWorldPort",
targetNamespace="http://jonas.ow2.org/examples/jaxws/greeter",
wsdlLocation="META-INF/wsdl/HelloWorld.wsdl", // <----- Updated
endpointInterface="org.ow2.jonas.examples.jaxws.greeter.HelloWorldType")
public class HelloWorldTypeImpl implements HelloWorldType {
public String hello(String name) {
return "Hello " + name; // <------ Implemented
}
}
Runtime
Portability
One important thing to understand when writing JAX-WS web services is that all the generated artifacts (JAX-WS SEI, SIB and JAXB classes) are portable accros application servers.
That means, that the EjbJar you're about to deploy on JOnAS 5.1 is also ready to be deployed on any other Java EE 5 compatible application server.
Deployment
As usual in JOnAS, deployment is super simple: simply drop the ejbjar in the ${jonas.base}/deploy/ directory.
After a few seconds, similar traces should appear on the console:
Internal EasyBeans Bus started ...
Creating Service {http://jonas.ow2.org/examples/jaxws/quote}QuoteReporterService from WSDL: jar:file:/home/sauthieg/jonasbases/jb.50/deploy/quote.jar!/META-INF/wsdl/QuoteReporterService.wsdl
PortMetaData [context:quote, pattern:/QuoteReporterService/QuoteReporterPort]
Setting the server's publish address to be http://localhost:9090/hello
...
Endpoint org.ow2.jonas.examples.jaxws.quote.QuoteReporterBean inited
Container '/home/sauthieg/jonasbases/jb.50/deploy/quote.jar' [1 SLSB, 0 SFSB, 0 MDB] started in 10 592 ms
'EJB3DeployableImpl[archive=/home/sauthieg/jonasbases/jb.50/deploy/quote.jar]' EJB3 Deployable is now deployed
Look closely at the PortMetaData line, it will give you all the necessary information to access your endpoint:
Endpoint URL is: http://localhost:9000/{ejbjar-name}/{service-name}/{port-name}
If you hit the right spot, the following message should appear: "There is a service here, but it's only accessible with POST method."
Testing
Testing is an important phase of the application development, and testing your new webservice is a need !
SoapUI is a very helpful application to help web services testing. Furthermore, it has a Java Web Start launcher.
- Starts by creating a "new WSDL Project"
- It needs the component WSDL that you should have somewhere in your build directory (generated by Java2WSDL or you already had it)
- SoapUI will generate empty requests for all the declared operations
- When opening a Request, DO NOT FORGET to update the endpoint URL value (on the top), because the endpoint location in the WSDL has not been updated
- This is were you use the URL endpoint value we've just seen above
- Then you can change the XML content to perform whatever test you want
- Look at the '?' in the XML, they denotes places where you can write something
- Press the "play" button on the left to invoke your webservice and see the result.

Conclusion
In this quick how-to, it has been explained how to expose an existing EJB3 as a webservice (using the WSDL first approach and the Java first approach) with the help of CXF (mainly ant tasks, but maven plugins exists and command line is also supported). The deployment of the web service in JOnAS have been described, and the testing of the endpoint using the SoapUI application have been introduced.
Resources
- Apache CXF 2.0 is an ASL 2.0 implementation of JAX-WS 2.0
- Apache CXF Generation Tools
- WSDL2Java
- Starting from a given WSDL file and generate client and/or server side standards artifacts
- Client side artifacts can be used as-is
- Server side artifacts are skeletons that needs to be filled up
- Java2WSDL
- Starting from a Java interface definition (SEI) and generates the server side standard artifacts
- Server side artifacts are skeletons that needs to be filled up
- WSDL2Java