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) |
- WS-Addressing
- WS-ReliableMessaging
- WS-Policy
- WS-Security
- MTOM/XOP
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
svn checkout svn://svn.forge.objectweb.org/svnroot/easybeans/trunk/easybeans
EasyBeans-Celtix project
The sources are available in subversion at the following URL :svn://svn.forge.objectweb.org/svnroot/easybeans/sandbox/easybeans-celtix
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
>$ cd easybeans-celtix >$ ant clean 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
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"; } }
- #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
/* 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 ); }
- #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; }}
- #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(); }
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 { … }
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(); } } }
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
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"); }
#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."); } }
PostConstruct called by EasyBeans <----------- Here . . . Method 'greetMe' invoked 1 times. Entering method : greetMe Message received: GuillaumeLeaving 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;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; }
PostConstruct called by EasyBeans Method 'greetMe' invoked 1 times. Entering method : greetMe Message received: GuillaumeEJBContext 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;PostConstruct called by EasyBeans Method 'greetMe' invoked 1 times. Entering method : greetMe Message received: GuillaumeSessionContext was injected by Easybeans. WebServiceContext was injected by Celtix. <------------- Here Leaving method : greetMe PreDestroy called by EasyBeans.
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 { . . . }
<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>
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: MiguelSessionContext 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
- EasyBeans : http://www.easybeans.org
- EasyBeans Developer's Guide : http://www.easybeans.org/doc/developerguide/en/html/developerguide.html
- Celtix 1.0 : http://celtix.objectweb.org
- Celtix getting Started : http://celtix.objectweb.org/docs/user_guides/getting_started/getting_started.html
- JAX-WS 2.0 : http://www.jcp.org/en/jsr/detail?id=224
- Celtix Technical Integration paper



Project