Annotations are a relatively new Java language feature that debuted in the JDK 1.5 [see Resources]. A standard way for a developer to associate arbitrary attributes with classes, interfaces, methods, and fields, annotations are also referred to as a "metadata facility." Annotation-related language additions were introduced in A Metadata Facility for the Java Programming Language (JSR 175). Annotation attributes specific to Web service development were subsequently defined in Web Services Metadata for the Java Platform (JSR 181) [see Resources].
A key benefit of annotations is that they provide a kind of shorthand when programming. Writing Web service code, for instance, often involves defining numerous boiler-plate methods and classes, in addition to configuration elements and deployment-specific data files. Using shorthand for that boiler-plate code, and passing that shorthand to a processor that understands the Web-service-related annotations, reduces the amount of code and configuration data a programmer has to write.
JAX-RPC 2.0 prescribes a set of annotations that, when passed to the JDK's annotation processing utility, apt
[see Resources], generate much of a Web service's code automatically. This brief tutorial illustrates how JAX-RPC 2.0's use of annotations allow you to create a simple, working Web service in just 15 lines of code, comments included. Only two lines in the simple example have any reference to Web service-related code in the form of a single annotation. The rest of the code focuses entirely on business logic—the arduous task of saying hello—and JAX-RPC 2.0 tools generate all other required artifacts.
The example presented here is based on code samples included in the JAX-RPC early access reference implementation distribution [see Resources]. Since this is early access code, some details are subject to change. Note that the reference implementation runs only on the Java 5 platform, as it relies on JDK 1.5 tools.
In the JAX-RPC 2.0 programming model, defining a Web service may start from either Java code or a WSDL document. It is also possible to start with both a WSDL and a Java class, and define a Web service via customizations to either. When starting with a Java class, the JAX-RPC 2.0 programming model consists of these steps:
apt
tool, which is part of JDK 1.5.wsdeploy
utility, which is part of the JAX-RPC 2.0 reference implementation.JAX-RPC 2.0 no longer requires that a Web service bean implement an interface. Thus, you do not need to declare a service interface, but can start coding a Web service directly from the service implementation. Defining the Web service may be as simple writing this Java class:
package server; import javax.jws.WebService; @WebService public class HelloImpl { /** * @param name * @return Say hello to the person. */ public String sayHello(String name) { return "Hello, " + name + "!"; } }
What turns this class into a Web service is the @WebService
annotation. That annotation is one of a handful of Web-service annotations defined in the Web Services Metadata for the Java Platform specification (JSR 181). It marks this Java class as one implementing a Web service.
Note that this class does not implement an interface, and that the sayHello()
method does not declare a RemoteException
. A Web service implementation class's method parameters and return types must, however, be compatible with JAXB 2.0 mapping schema elements [see Resources]. As well, return types and parameters must not be Java "remote" objects—implement java.rmi.Remote
—an important contrast to Java RMI and its most recent implementation, JERI (Jini Extensible Remote Invocation)[see Resources].
The JAX-RPC reference implementation relies on the annotation processing tool apt
that debuted in JDK 1.5. apt
operates on the Java source files to create additional code elements specified by annotations. The JAX-RPC 2.0 early access reference implementation includes an Ant task for running apt
. Detailed documentation for apt
is part of the JDK 1.5 documentation bundle, and instructions for using the apt
Ant task are included in the JAX-RPC early access download package.
The next step is to run apt
on the above Java code, resulting in several artifacts:
HelloServiceImpl.wsdl schema1.xsd classes/server/HelloImpl.class classes/server/jaxrpc/SayHello.class classes/server/jaxrpc/SayHelloResponse.class classes/server/jaxrpc/SayHello.java classes/server/jaxrpc/SayHelloResponse.java
The meaning of these items becomes clear from the WSDL document that resulted from running apt
on the Web service definition. The generated WSDL is as follows:
<?xml version="1.0" encoding="UTF-8"?> <definitions name="HelloImplService" targetNamespace="http://server/jaxrpc" xmlns:tns="http://server/jaxrpc" xmlns="http://schemas.xmlsoap.org/wsdl/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"> <types> <xsd:schema> <xsd:import namespace="http://server/jaxrpc" schemaLocation="schema1.xsd"/> </xsd:schema> </types> <message name="sayHello"> <part name="parameters" element="tns:sayHello"/> </message> <message name="sayHelloResponse"> <part name="result" element="tns:sayHelloResponse"/> </message> <portType name="HelloImpl"> <operation name="sayHello"> <input message="tns:sayHello"/> <output message="tns:sayHelloResponse"/> </operation> </portType> <binding name="HelloImplBinding" type="tns:HelloImpl"> <soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document"/> <operation name="sayHello"> <soap:operation soapAction=""/> <input> <soap:body use="literal"/> </input> <output> <soap:body use="literal"/> </output> </operation> </binding> <service name="HelloImplService"> <port name="HelloImplPort" binding="tns:HelloImplBinding"> <soap:address location="REPLACE_WITH_ACTUAL_URL"/> </port> </service> </definitions>
The WSDL's type definition imports an external schema file, schema1.xsd
. That external schema file was also generated automatically, and it defines two complex types for the WSDL's message elements, sayHelloResponse
and sayHello
. Here is the generated schema:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <xs:schema version="1.0" targetNamespace="http://server/jaxrpc" xmlns:xs="http://www.w3.org/2001/XMLSchema"> <xs:element name="sayHelloResponse" type="ns1:sayHelloResponse" xmlns:ns1="http://server/jaxrpc"/> <xs:complexType name="sayHelloResponse"> <xs:sequence> <xs:element name="return" type="xs:string" minOccurs="0"/> </xs:sequence> </xs:complexType> <xs:element name="sayHello" type="ns2:sayHello" xmlns:ns2="http://server/jaxrpc"/> <xs:complexType name="sayHello"> <xs:sequence> <xs:element name="name" type="xs:string" minOccurs="0"/> </xs:sequence> </xs:complexType> </xs:schema>
The two Java classes, automatically defined by apt
, define the beans JAXB uses to marshal and unmarshal the Web service messages. The apt
tool compiles these sources, along with the service definition, into class files.
package server.jaxrpc; import javax.xml.bind.annotation.*; import javax.xml.rpc.ParameterIndex; @XmlRootElement(name="sayHelloResponse", namespace="http://server/jaxrpc") @XmlAccessorType(AccessType.FIELD) @XmlType(name="sayHelloResponse", namespace="http://server/jaxrpc", propOrder={"_return"}) public class SayHelloResponse { @XmlElement(namespace="", name="return") @ParameterIndex(value=-1) public java.lang.String _return; public SayHelloResponse(){} }
package server.jaxrpc; import javax.xml.bind.annotation.*; import javax.xml.rpc.ParameterIndex; @XmlRootElement(name="sayHello", namespace="http://server/jaxrpc") @XmlAccessorType(AccessType.FIELD) @XmlType(name="sayHello", namespace="http://server/jaxrpc", propOrder={"name"}) public class SayHello { @XmlElement(namespace="", name="name") @ParameterIndex(value=0) public java.lang.String name; public SayHello(){} }
Looking at these two bean source files reveals several further annotations defined in the JAXB 2.0 specification. Those annotations are used, in turn, to produce several additional classes to support the runtime marshaling and unmarshaling operations. We will not discuss those annotations in this article.
Next, we need to package these artifacts into a WAR file, along with one or more deployment descriptors. J2EE deployment descriptors may convey information not only to an application server, but possibly to other tools and even to people interacting with the service.
For a JAX-RPC 2.0 Web app, a deployment descriptor bundled in the WAR should be called jaxrpc-ri.xml
. This file conveys both runtime information about a Web service, and also communicates information to a deployment compiler tool discussed in the final deployment step. The content of the jaxrpc-ri.xml
file may be as simple as the following, fashioned after a sample in the JAX-RPC 2.0 reference implementation:
<?xml version="1.0" encoding="UTF-8"?> <webServices xmlns="http://java.sun.com/xml/ns/jax-rpc/ri/dd" version="1.0" targetNamespaceBase="http://artima.com" typeNamespaceBase="http://artima.com" urlPatternBase="/ws"> <endpoint name="sayhello" displayName="A Java Web service" interface="server.HelloImpl" implementation="server.HelloImpl" wsdl="/WEB-INF/HelloImplService.wsdl"/> <endpointMapping endpointName="sayhello" urlPattern="/sayhello"/> </webServices>
This descriptor defines a single endpoint, sayhello
, and maps that endpoint to the URL pattern /sayhello
. Note that the service interface and implementation refer to the same class.
To make this WAR file work inside a servlet container, you would also want to package a web.xml
file in the WAR, too:
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/j2ee/dtds/web-app_2_3.dtd"> <web-app> <display-name>Hello, JAX-RPC 2.0!</display-name> <description>A hello, world, Web service.</description> <session-config> <session-timeout>60</session-timeout> </session-config> </web-app>
Creating a WAR file is easily accomplished with the Ant war
task. Note that in the resulting Web archive, the schema file is located in the Web app's root directory so it can be downloaded by clients that require access to the WSDL. The WSDL itself will be served up by the JAX-RPC servlet, which will attach the service endpoint address to the WSDL at runtime. The following is the structure of the resulting WAR:
schema1.xsd WEB-INF/web.xml WEB-INF/jaxrpc-ri.xml WEB-INF/HelloImplService.wsdl WEB-INF/classes/server/HelloImpl.class WEB-INF/classes/server/jaxrpc/SayHello.class WEB-INF/classes/server/jaxrpc/SayHello.java WEB-INF/classes/server/jaxrpc/SayHelloResponse.class WEB-INF/classes/server/jaxrpc/SayHelloResponse.java
The final step is to deploy the Web service WAR archive. That requires "cooking" the initial WAR: modifying the deployment descriptor, and adding several runtime artifacts to facilitate the XML-Java data binding.
The JAX-RPC 2.0 reference implementation includes the wsdeploy
tool that operates an a single "raw" WAR file, performs the deployment tasks on elements in that WAR, and produces a new, deployment-ready, or "cooked," WAR file.
wsdeploy
reads the jaxrpc-ri.xml
inside the WAR, and then invokes another JAX-RPC tool, wscompile
with an option to generate deployment-time Web service implementation artifacts. Running wsdeploy
is as simple as invoking the wsdeploy
Ant task, which is also part of the JAX-RPC 2.0 reference implementation. I will not discuss the individual files generated for the deployment-ready WAR, partly because they are dependent on the current, early-access release of the reference implementation.
The WAR "cooked" by wsdeploy
can be copied to the webapps
directory of a servlet container. The Web service is now ready to be used.
In order for a servlet container to serve JAX-RPC 2.0-based Web services, all JAR files from the reference implementation must be copied to a library directory the servlet engine looks in for classes. With Jakarta Tomcat, for instance, you can copy the JAX-RPC reference implementation JARs to the /shared/lib
directory.
The JAX-RPC servlet provides a summary page for the newly installed Web service. For this example app, that summary page can be accessed at the following URL:
http://localhost:8080/jaxrpc-hello/hello
(substitute the host name, the port number, and the context root with appropriate values for your server). The following Web page is displayed at that URL:
The Web service's WSDL, in turn, is accessible via this URL, which is where clients would download that WSDL. Note that clients would also need the XML schema file imported in the WSDL, which is why that schema file had to reside in the Web archive's root:
http://localhost:8080/jaxrpc-hello/hello?WSDL
If you inspect the resulting WSDL with a browser, you will note that the WSDL correctly indicates the service's endpoint address:
<service name="HelloImplService"> <port name="HelloImplPort" binding="tns:HelloImplBinding"> <soap:address location="http://localhost:8080/jaxrpc-hello/hello"/> </port> </service>
This brief tutorial demonstrated that JAX-RPC 2.0's use of annotations greatly simplifies writing a Web service. Typing in the example code, and running the Ant tasks defined in JAX-RPC 2.0, should yield a fully working Web service in less than three minutes. All the while, you can concern yourself with the Java code that implements the business logic, and let JAX-RPC 2.0 tools handle the rest of the Web-service-related classes and files. Download the JAX-RPC 2.0 reference implementation for an example of how to write Web service client for a similar Web service.
The JAX-RPC 2.0 reference implementation and community Web site
https://jaxrpc.dev.java.net/
JSR 175: A Metadata Facility for the Java Programming Language
http://www.jcp.org/en/jsr/detail?id=175
JSR 181: Web Services Metadata for the Java Platform
http://www.jcp.org/en/jsr/detail?id=181
JSR 222: Java Architecture for XML Binding (JAXB) 2.0
http://www.jcp.org/en/jsr/detail?id=222
JSR 224: Java API for XML-Based RPC (JAX-RPC) 2.0
http://www.jcp.org/en/jsr/detail?id=224
Annotation Processing Tool (apt)
http://java.sun.com/j2se/1.5.0/docs/guide/apt/index.html
Have an opinion? Readers have already posted 6 comments about this article. Why not add yours?
Frank Sommers is a Senior Editor with Artima Developer. He also serves as chief editor of the Web zine ClusterComputing.org, the IEEE Technical Committee on Scalable Computing's newsletter, and is an elected member of the Jini Community's Technical Advisory Committee. Prior to joining Artima, Frank wrote the Jiniology and Web services columns for JavaWorld.
Artima provides consulting and training services to help you make the most of Scala, reactive
and functional programming, enterprise systems, big data, and testing.