Use JAX-WS 2.0 with EasyBeans

Web Services

Web Services are now widely used in the industry. But their usage were often too complicated :

  • numerous files used for only 1 web services (classes, XML descriptors, mapping file, ...), more if it was an EJB exposed as a web service !
  • difficulty to synchronize theses files when changes happen.

Java EE 5.0

The goal of the next Java Enterprise Edition is to ease the development of Java EE components.

WebServices are now first class Java EE components and their development has been greatly simplified, thanks to the annotations and to the new web services specifications (JAXB 2.0, JAX-WS 2.0, WS metadata, ...).

JAXB 2.0

JAXB 2.0, aka Java API for XML Binding 2.0 is the specification used when someone wants to map a Java type to an XML element.

JAXB2 implementations are responsible for marshalling/unmarshalling of XML documents and Java types.

It's used in JAX-WS 2.0 because Java Web Services needs a way to bind java objects to and from XML.

JAX-WS 2.0

JAX-WS 2.0, aka Java API for XML Web Services (former JAX-RPC 2.0), is the JSR used to expose Java Objects (POJOs or Stateless Session EJB3s) as web services. It defines the client view of a web service too (when the developer wants to access an external endpoint).

It relies on the Web Services MetaData specification that define a new way to declare web services endpoints : by using Java SE 5.0 annotations (@WebService, @WebMethod, ...).

Celtix

Celtix is an Objectweb project. This is an OpenSource Java ESB.

And it implements the JAX-WS 2.0 API.

Celtix has just achieved its 1.0 release.(Download)

Some WS-* specifications are also supported in this release :

  • WS-Addressing
  • WS-ReliableMessaging
And some more are being implemented for the next releases :
  • WS-Policy
  • WS-Security
  • MTOM/XOP
A technical document about the Celtix Integration is available.

Apache CXF

Apache CXF is the merger of Objectweb Celtix and Codehaus XFire. It is considered as the successor of Celtix, provides too a JAX-WS 2 implementation,a nd so can be used as a JAX-WS 2.0 runtime engine.

Build and run the sample

Download the webapp ...

Available here : EasyBeans/JAX-WS 2.0 (Tomcat Only)

… Or build it yourself

Get the sources

EasyBeans project

The sources are available in subversion at the following URL :

svn://svn.forge.objectweb.org/svnroot/easybeans/trunk/easybeans

Checkout them :

svn checkout svn://svn.forge.objectweb.org/svnroot/easybeans/trunk/easybeans

We will refer to the EasyBeans sources with the #noformat("EASYBEANS_HOME") variable.

EasyBeans-Celtix project

The sources are available in subversion at the following URL :

svn://svn.forge.objectweb.org/svnroot/easybeans/sandbox/easybeans-celtix

Checkout them :

svn checkout svn://svn.forge.objectweb.org/svnroot/easybeans/sandbox/easybeans-celtix

Set the environment

Before compiling the Celtix integration into EasyBeans, you must set the #noformat("EASYBEANS_HOME") environment variable.

The #noformat("EASYBEANS_HOME") should point to the location where the EasyBeans sources have been checkouted.

  • Linux :
>$ export EASYBEANS_HOME=/path/to/easybeans-project
  • Windows
> set EASYBEANS_HOME=c:\\path\\to\\easybeans-project

Build

First, you need to build Easybeans WebApp :

>$ cd $EASYBEANS_HOME
>$ ant clean war

Then build easybeans-celtix :

>$ cd easybeans-celtix
>$ ant clean war

This will produce a new webapp in #noformat("output/dist/ow_easybeans.war")

Run

Add the modified #noformat("ow_easybeans.war") into the #noformat("$CATALINA_BASE/webapps/") folder. And start Tomcat :

>$ catalina.sh start

Deploy EJBs

Build the Sample

>$ cd examples/celtix
>$ ant clean jar

This will produce an EjbJar file in #noformat("examples/celtix/output/dist/session-greeter.jar")

Deploy the Sample

Simply copy the #noformat("session-greeter.jar") in the #noformat("$CATALINA_BASE/ejb3s/") folder.

EasyBeans will auto-detect the new EjbJar and will deploy it automatically.

#note("Tomcat must be started before using EasyBeans.")

>$ cp output/dist/session-greeter.jar $CATALINA_BASE/ejb3s/

Try It !

There is no Java Client for your new WebService at the time of writing.

But the SOAPUI project will help you to send SOAP requests to your endpoint:

#info("Launch SOAP UI 1.5 (Java Web Start)")

Create a new WSDL project, Import the WSDL from URL (http://localhost:8080/ow2-easybeans-cxf-war-xxxxx/services/greeter?wsdl) SOAPUI, will creates "Stubs" for each WebMethods available.

Simply fill the blanks if needed and press the "Play" button. You'll see some traces on the server side and the response into SOAPUI.

The Basic Sample

To demonstrates the use of JAX-WS 2.0 annotations inside EJB 3, we will follow a sample took from Celtix distribution (small changes).

The sample is a Greeter object with a #noformat("@Stateless") annotation.

The code can be found under #noformat("examples/celtix/").

Annotated class

package org.objectweb.easybeans.examples.celtix;

import javax.ejb.Stateless; import javax.jws.WebService;

import org.objectweb.easybeans.celtix.PortComponent; import org.objectweb.easybeans.examples.celtix.gen.IGreeter;

@Stateless @WebService(name="Greeter", serviceName="SOAPService", targetNamespace="http://objectweb.org/greeter", wsdlLocation = "file:META-INF/wsdl/greeter.wsdl") @PortComponent(urlPattern="/greeter") public class Greeter implements IGreeter {

public String greetMe(String me) { System.out.println("Executing operation greetMe"); System.out.println("Message received: " + me + "n"); return "Hello " + me; }

public String sayHi() { System.out.println("Executing operation sayHin"); return "Bonjour"; } }

Annotations in use :

  • #noformat("javax.ejb.Stateless") : The Greeter will be an EJB 3 Stateless Session Bean.
  • #noformat("javax.jws.WebService") : The Greeter will be exposed as a WebService.
  • #noformat("org.objectweb.easybeans.celtix.PortComponent") : Commodity annotation used to specify the URL Pattern where the Web Service will be available.

Generated interfaces and types

That interface and all the types has been generated by Celtix WSDL2Java tool.

This is beyond the scope of this article to explain how to generate them. Check the Celtix documentation if you want more informations.

Service Endpoint Interface

It can seems complex, but some of theses annotations are not required :

  • #noformat("@WebMethod") : default to method's name
And some other are missing and default values are used (#noformat("@SOAPBinding") for example, that defaults to SOAP1.1, doc/lit).

/* Generated by WSDLToJava Compiler. */

package org.objectweb.easybeans.examples.celtix.gen;

import javax.jws.WebMethod; import javax.jws.WebParam; import javax.jws.WebResult; import javax.jws.WebService; import javax.xml.ws.RequestWrapper; import javax.xml.ws.ResponseWrapper;

/** * This class was generated by the Celtix 1.0 * Wed Apr 01 16:34:53 CET 2006 * Generated source version: 1.0 */ @WebService(wsdlLocation="META-INF/wsdl/greeter.wsdl", targetNamespace="http://objectweb.org/greeter", name="Greeter") public interface IGreeter {

@ResponseWrapper(targetNamespace="http://objectweb.org/greeter/types", className="org.objectweb.easybeans.examples.celtix.gen.types.SayHiResponse", localName="sayHiResponse") @RequestWrapper(targetNamespace="http://objectweb.org/greeter/types", className="org.objectweb.easybeans.examples.celtix.gen.types.SayHi", localName = "sayHi") @WebResult(targetNamespace="http://objectweb.org/greeter/types", name="responseType") @WebMethod(operationName="sayHi") public java.lang.String sayHi();

@ResponseWrapper(targetNamespace="http://objectweb.org/greeter/types", className="org.objectweb.easybeans.examples.celtix.gen.types.GreetMeResponse", localName="greetMeResponse") @RequestWrapper(targetNamespace="http://objectweb.org/greeter/types", className="org.objectweb.easybeans.examples.celtix.gen.types.GreetMe", localName="greetMe") @WebResult(targetNamespace="http://objectweb.org/greeter/types", name="responseType") @WebMethod(operationName="greetMe") public java.lang.String greetMe( @WebParam(targetNamespace="http://objectweb.org/greeter/types", name="requestType") java.lang.String requestType ); }

Annotations in use :

  • #noformat("javax.xml.ws.RequestWrapper") : Capture the JAXB generated wrapper bean (containing the parameters) and the XML element for marshalling/unmarshalling the bean.
  • #noformat("javax.xml.ws.ResponseWrapper") : Same as RequestWrapper but for the response flow.
  • #noformat("javax.jws.WebResult") : Map the result class of the method to an XML type (with QName).
  • #noformat("javax.jws.WebMethod") : Describe a Method exposed as a web service operation (wsdl:operation).

Generated Types

package org.objectweb.easybeans.examples.celtix.gen.types;

import javax.xml.bind.annotation.AccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlType;

/** * <p>Java class for greetMe element declaration. * * <p>The following schema fragment specifies the expected content contained within this class. * * <pre> * <element name="greetMe"> * <complexType> * <complexContent> * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> * <sequence> * <element name="requestType" type="{http://www.w3.org/2001/XMLSchema}string"/> * </sequence> * </restriction> * </complexContent> * </complexType> * </element> * </pre> * * */ @XmlAccessorType(AccessType.FIELD) @XmlType(name="", propOrder={"requestType"}) @XmlRootElement(name="greetMe") public class GreetMe {

@XmlElement(namespace = "http://objectweb.org/greeter/types") protected String requestType;

public String getRequestType() { return requestType; }

public void setRequestType(String value) { this.requestType = value; }

}

Annotations in use :

  • #noformat("javax.xml.bind.annotation.XmlAccessorType") : Fields not explicitely annotated must be mapped.
  • #noformat("javax.xml.bind.annotation.XmlType") : Map the current Java type to an XML type.
  • #noformat("javax.xml.bind.annotation.XmlRootElement") : Name of the XML element.
  • #noformat("javax.xml.bind.annotation.XmlElement") : Denotes a field parts of an XML element sequence.

Add Interceptors

With #noformat("@AroundInvoke")

The #noformat("@AroundInvoke") annotated method, will describe a Bean's method as a Business interface interceptor.

In our case, all business interface methods will be intercepted by the 'trace' method.

@AroundInvoke
private Object trace(InvocationContext ic) throws Exception {
    System.out.println("Intercepted method : " + ic.getMethod().getName());
    return ic.proceed();
}

The trace interceptor simply prints the method name before delegating the call continuation.

Output on the server side :

INFO: Before invoking on implementor
Intercepted method : sayHi
Executing operation sayHi

With #noformat("@Interceptors")

Add the #noformat("@Interceptors") on the Bean, specifying the Class that hold the interceptor Method (annotated with #noformat("@AroundInvoke")) :

@Stateless
@Local(ISayHi.class)
@WebService(name="Greeter",
            serviceName="SOAPService",
            targetNamespace="http://objectweb.org/greeter",
            wsdlLocation="META-INF/wsdl/greeter.wsdl")
@PortComponent(urlPattern="/greeter")
@Interceptors(GreeterInterceptor.class)
public class Greeter implements IGreeter, ISayHi {
   …
}

#note("The @Interceptors annotation is used to declare both external method interceptors (@AroundInvoke) and external lifecyle callbacks(@PostConstruct, ...).")

The GreeterInterceptor class :

package org.objectweb.easybeans.examples.celtix;

import javax.interceptor.AroundInvoke; import javax.interceptor.InvocationContext;

/** * This class intercepts Business interfaces methods. * @author Guillaume Sauthier */ public class GreeterInterceptor {

/** * Invocation counter */ private int counter = 0;

/** * Count the invocations of a bean. * @param ic InvocationContext * @return proceeded method * @throws Exception if something wrong occurs. */ @AroundInvoke public Object count(InvocationContext ic) throws Exception {

synchronized(this) { counter++; System.out.println("Method '" + ic.getMethod().getName() + "' invoked " + counter + " times."); return ic.proceed(); } } }

The GreeterInterceptor class is a simple POJO that count invocations of a method.

Output (the WebServices is executed 2 times) :

INFO: Before invoking on implementor
Method 'sayHi' invoked 1 times.
Intercepted method : sayHi
Executing operation sayHi

...

INFO: Before invoking on implementor Method 'sayHi' invoked 2 times. Intercepted method : sayHi Executing operation sayHi

As you can see EJB3 Interception mechanism is still working very well when mixed with JAX-WS 2.0 WebServices.

Add LifeCycle Callbacks

#noformat("@PostConstruct")

An annotated method with noformat("@PostConstruct") is required to be called after dependencies injection (resolution of noformat("@Resource"), ...) and before being used (business methods called).

The #noformat("@PostConstruct") annotation is defined inside the Bean class :

@PostConstruct
private void post() {
    System.out.println("PostConstruct called by EasyBeans");
}

The output is displayed in the next section.

#noformat("@PreDestroy")

An annotated method with noformat("@PreDestroy") is required to be called before instance removal. Once the annotated method has been called, the bean is unavailable for futurre uses.

The #noformat("@PreDestroy") annotation is defined in a separate class (re-using the #noformat("@Interceptors") annotation). So the annotated Method goes inside the GreeterInterceptor class :

public class GreeterInterceptor {

...

/** * Called before Bean destruction. * @param ic InvocationContext * @return proceeded method * @throws Exception if something wrong occurs. */ @PreDestroy public void destroy(InvocationContext ic) throws Exception { System.out.println("PreDestroy invoked."); } }

#info("Simply annotate a method with @PreDestroy to release resources that may have been locked/used by the Bean.")

The output for #noformat("@PreDestroy") and #noformat("@PostConstruct") :

PostConstruct called by EasyBeans             <----------- Here . . .
Method 'greetMe' invoked 1 times.
Entering method : greetMe
Message received: Guillaume

Leaving method : greetMe PreDestroy called by EasyBeans. <----------- . . . and here

Resources Injection

Access the SessionContext

The SessionContext may be used to perform lookups, manage Transactions, access the Timer Service, ...

@Resource
private SessionContext sessionContext;

To see if the SessionContext was correctly injected, I've added a simple trace in the greetMe Method :

public String greetMe(String me) {
    System.out.println("Message received: " + me + "n");

if (sessionContext != null) { System.out.println("SessionContext was injected by EasyBeans."); } return "Hello " + me; }

We will check with the output :

PostConstruct called by EasyBeans
Method 'greetMe' invoked 1 times.
Entering method : greetMe
Message received: Guillaume

EJBContext was injected by Easybeans. <---------- Here Leaving method : greetMe PreDestroy called by EasyBeans.

Access the WebServiceContext

The WebServiceContext is a Resource that can be accessed/injected like all other Resources : through an annotated field or setter :

@Resource
private WebServiceContext wsContext;

A simple check (simmilar to the one for SessionContext) has been added in the greetMe method.

Output :

PostConstruct called by EasyBeans
Method 'greetMe' invoked 1 times.
Entering method : greetMe
Message received: Guillaume

SessionContext was injected by Easybeans. WebServiceContext was injected by Celtix. <------------- Here Leaving method : greetMe PreDestroy called by EasyBeans.

#note("Celtix is responsible for WebServiceContext injection.
EasyBeans ignores that @Resource when encountered !")

Add Handlers

The Handlers are the WebServices equivalent to EJB3 Interceptors.

They can be defined with the #noformat("@HandlerChain") annotation applied on the WebService class. That annotation specify a resource name where a given handler chain can be retrieved

@HandlerChain(file="config/greeter-chain.xml", name="GreeterHandlerChain")
public class Greeter implements IGreeter, ISayHi {
   . . .
}

And here is a simple greeter-chain.xml file :

<handler-config>
  <handler-chain>
    <handler-chain-name>GreeterHandlerChain</handler-chain-name>
    <handler>
      <handler-name>LogHandler</handler-name>
      <handler-class>org.objectweb.easybeans.examples.celtix.LogHandler</handler-class>
    </handler>
  </handler-chain>
</handler-config>

Notice that the #noformat("@HandlerChain.name()") and #noformat("handler-config/handler-chain/handler-chain-name") match !

#note("In a Java EE Container (like JOnAS), the @HandlerChain MUST NOT define the 'name' attribute.")

#note("Celtix 1.0 doesn't conform to the latest JAX-WS 2.0 specification : the 'handler-config' element was replaced by a 'handler-chains' element and every elements are in the javaee namespace (http://java.sun.com/xml/ns/javaee).")

The output :

Inbound message:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
                  xmlns:typ="http://objectweb.org/greeter/types">
   <soapenv:Body>
      <typ:greetMe>
         <typ:requestType>Miguel</typ:requestType>
      </typ:greetMe>
   </soapenv:Body>
</soapenv:Envelope>
PostConstruct called by EasyBeans
Method 'greetMe' invoked 1 times.
Entering method : greetMe
Message received: Miguel

SessionContext was injected by Easybeans. WebServiceContext was injected by Celtix. Leaving method : greetMe PreDestroy called by EasyBeans.

Outbound message: <?xml version="1.0" encoding="utf-8" ?> <SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <SOAP-ENV:Body> <ns4:greetMeResponse xmlns:ns4="http://objectweb.org/greeter/types" xmlns:ns5="http://www.w3.org/2005/08/addressing/wsdl"> <ns4:responseType>Hello Miguel</ns4:responseType> </ns4:greetMeResponse> </SOAP-ENV:Body> </SOAP-ENV:Envelope>

Conclusion

Resources

Creator: sauthieg  Date: 2006/05/05 20:47
Last Author: benoitf  Date: 2008/05/01 17:14