JSR 181: A Java Simplification Request
By Michael Easter, OCI Senior Software Engineer
June 2007
Introduction
Historically, web services in Java have vexed developers with myriad specifications and complex solutions. Surrounded by legions of W3C specs, JSRs, and XML descriptors, these developers endured complexity while other Java communities (such as EJB3, Seam and Guice) celebrate the elegance of Java 5 and beyond. Moreover, with the success of the sleek REST school of web architecture, there is a fitting rallying cry: "hear our Java Simplification Request for web services!"
If only there were a world in which Java developers express web services in a compact manner, where tools assist with the mundane, "plumbing" details. This world is a far cry from the status quo, and certainly appeals. For example, what if web services were as easy as:
- @WebService
- public class Concatenator {
-
- @WebMethod
- public String concatenate(String a, String b) {
- return a + " " + b;
- }
- }
Welcome to the world of JSR 181: the above is a bona fide web service. JSR 181, "Web Services Metadata for Java", rescues downtrodden web services developers with a simple, annotation-based development model for common web service tasks. Combined with a lightweight HTTP server in Java 6, JSR 181 enables rapid prototyping of web services (WS) and brings WS development into the modern era of Java.
This article has two goals:
- A bewildering mix of JSRs comprise the Java WS space, both in terms of volume and the subtle relationships among them. We almost need a JSR Modeling Language (JML) to communicate! Defining JML is too ambitious, but the key JSRs are examined in a historical context, so as to set the stage for JSR 181.
- Examples explore the JSR 181 development model.
It is assumed that the reader understands web services as an approach to distributed computing, and has a basic knowledge of web service constructs (e.g. WSDL, SOAP). All examples require JDK 1.6.x.
Setting the Stage
There are two main eras of web services in Java: JAX-RPC and JAX-WS. Articles on both topics refer to several JSRs and their implicit relationships. This section provides a bird's eye view of the landscape.
JAX-RPC 1.x
JAX-RPC represents the first version of web services in Java. See the graphical overview below. Some key points:
- The primary spec, JSR 101 ("Java APIs for XML-based RPC"), defines the APIs and conventions for the Java platform, including the WSDL/Java mapping, the SOAP binding and SOAP handlers. Version 1.0 was released in mid-2002.
- Due to scheduling concerns, the JAX-RPC team elected to define its own facilities to bind data between Java and XML. This was in addition to other, more generalized data binding solutions.
- The state-of-the-JVM in this timeframe was JDK 1.4. JSR 88 defines the general deployment API for J2EE. JSR 109 ("Implementing Enterprise Web Services") implements this API for the WS space. Note that the best practice of the time was XML-based deployment descriptors: i.e. artifacts to describe web services at deployment time.
- Version 1.1 of JAX-RPC appeared in mid-2003 with support for the WS-I Basic Profile 1.0, to improve interoperability with other technologies.
JAX-WS 2.x
Though initially met with considerable excitement, there are problems with JAX-RPC 1.x: the implementation is complex and competing technologies/philosophies (e.g. Apache Axis, REST) offer simplicity that is seductive, despite being non-standard. Also, new specs and standards render some of the machinery of JAX-RPC 1.x (e.g. data binding) as obsolete. Most of all, since the release of JAX-RPC, the Java landscape itself underwent seismic shifts, particularly with respect to EJB.
Seismic Shifts
Initially, tools (e.g. XDoclet) assisted with the generation of deployment descriptors. Later, Java 5 brought the promise of annotations. In recent years, prominent authors railed against the complexity of J2EE and EJB. Alternative frameworks (e.g. Ruby on Rails) introduced simple, Zen-like philosophies, such as "prefer convention over configuration". All of these forces culimated in a shift against the explicit use of complex deployment time descriptors, and even XML configuration. EJB3, Seam and Guice clearly reflect this shift.
JSR 224
For version 2 of WS in Java, the technology was renamed as JAX-WS 2.0. Just as JSR 109 is the primary spec for JAX-RPC, JSR 224 "Java API for XML-Based Web Services", is the central document for JAX-WS. Some of the spec's stated goals pertain to evolving standards:
- JAX-WS adds support for SOAP 1.2 in addition to SOAP 1.1.
- JAX-WS will support WS-I Basic Profile 1.1 as it continues to evolve.
- JAX-WS may support WSDL 2.0 in JAX-WS version 2.1+
However, the following goals are more interesting:
- JAX-WS delegates data binding of Java/XML to JAXB 2.x (aka JSR 222).
- JAX-WS "aligns with and complements" the annotations defined by JSR 181. That is, just as JAX-RPC/JSR 109 defines a deployment time facility for the Java/WSDL mapping, JAX-WS works with JSR 181 to define a development time facility for this mapping, using Java 5 annotations (aka JSR 175).
See the graphical overview below. There are several themes here:
- JAX-WS uses delegation to leverage facilities provided by the modern JDK (JAXB and annotations). This is classic division of labor. Note: the diagram shows JSRs, yet we can loosely apply the OO ideas of composition and encapsulation.
- Within the JAX-WS space itself, JSR 224 delegates some work to JSR 181. More on this coming up.
- With development time annotations, clearly JAX-WS responds to the criticisms of JAX-RPC with respect to the complex, antiquated nature of deployment descriptors.
JSR 181 within JAX-WS
The full JAX-WS spec (JSR 224) exceeds the scope of this article but, as noted, it defines the architecture for "WS Version 2." Clearly, it relates to JSR 181. What is the relationship? Some answers come from the introduction of the JSR 181 spec:
- JSR 181 "defines a standard for building and deploying Web Services without requiring knowledge and implementation of generalized APIs and deployment descriptors." It "addresses the need to simplify... development of server applications that conform to SOAP and WSDL standards."
- "It is not a goal ... to support every feature or enable the creation of every Web Service possible...." "The goal is to make it easy to build the most common types of Web Services."
In short, JSR 181 is "Web Services Lite": all of the common WS functionality, without that complex aftertaste. In the parlance of OO design, JSR 181 is a facade design pattern: a simpler interface to the full-blown JSR 224 (see diagram below). It truly represents a "front-end" to JAX-WS as it requires the presence of JSRs 109, 224 and 175.
Mercifully, the JAX-WS 2.0 reference implementation contains all of the WS-oriented JSR solutions and is bundled in Java 6, along with a lightweight HTTP server. This allows us to experiment with JSR 181 out-of-the-box.
A Closer Look
The stage is set for JSR 181: let's dig in.
The Development Model in JSR 181
In the JAX-WS world, a web service requires these artifacts:
- a WSDL document stating the contract of the service
- a service endpoint interface (SEI) as a Java representation of the WS interface
- a service implementation bean (SIB) as the Java implementation of the SEI
- deployment descriptors which link everything together
JSR 181 requires only the SIB (i.e. the business logic). Annotations are used to generate the other artifacts. No doubt, annotation of business POJOs should be music to the ears of the long-suffering WS developers.
There are several programming models defined in JSR 181 (only the first is required by implementations):
- Start with Java. The developer writes an annotated Java class. The JSR 181 processor accepts the class as input and generates the WSDL, schema and deployment descriptors.
- Start with WSDL. Here, an implementation-specific tool accepts an existing WSDL file and produces: a Java SEI and Java classes representing the schema and message parts specified by the WSDL contract. The developer must write the SIB that satisfies the SEI.
- Start with WSDL and Java. In this model, the developer uses JSR 181 annotations to map the methods of Java SIB to an existing WSDL contract.
The examples in this article use the "Start with Java" model.
Example 1: Concatenator
Before listing all of the available annotations in JSR 181, we'll start small, to get a view of the landscape of the JAX-WS tools. The first example (available here) is a trivial "string concatenation" service.
Server Side Concatenation
Let's begin with the service implementation bean in Java:
- package com.ociweb.demo;
-
- import javax.jws.WebService;
- import javax.jws.WebMethod;
- import javax.xml.ws.Endpoint;
-
- @WebService
- public class Concatenator {
-
- @WebMethod
- public String concatenate(String a, String b) {
- return a + " " + b;
- }
- }
Note the annotations on this simple class, which clearly denote a web service and a "web method." Amazingly, we are done with development and can start testing the web service! The JSR 181 processor will take care of the rest. To prove it, we write a main
method to publish the service to the HTTP server contained in Java 6:
- public static void main(String[] args) {
- // e.g.
- // publish.url = http://localhost:9998/concatservice
-
- String publishUrl = System.getProperty("publish.url");
- if( publishUrl != null & publishUrl.length() > 0 ) {
- System.out.println("publishing service at: " + publishUrl);
- Concatenator concatenator = new Concatenator();
- Endpoint endpoint = Endpoint.publish(publishUrl,concatenator);
- } else {
- System.err.println("usage: Concatenator publishUrl");
- }
- }
The main
method creates an instance of the SIB, and calls Endpoint.publish()
. The first parameter, a URI, specifies the address and transport/protocol. The current javadoc states that for a 'http:' URI, it is assumed that the SOAP 1.1/HTTP binding is used. The publish
method uses a default configuration to create the necessary server infrastructure for publication.
Ant takes care of the next steps (see build_server.xml
for details):
- Run the Java6
apt
tool on the source code. The JSR 181 processor will generate the necessary artifacts;apt
compiles the Java code as well. - Run the
Concatenator.main()
method.
Once the server is running, we can point a browser to http://localhost:9998/concatservice?wsdl
:
Presto! With stunning ease, the web service is available, and follows these JSR 224 conventions:
- The targetNamespace for the WSDL is the Java package, with reversed tokens.
- The identifiers for service, port, binding and other WSDL elements follow the natural convention by decorating the original SIB name,
Concatenator
. - The schema types are published to the URI:
http://localhost:9998/concatservice?xsd=1
If we point the browser to the URI of the schema types:
We see that the types follow a similar, logical convention. However, there's more: the JSR 181 processor generates these classes for us. In the example directory, you can find the generated Java classes Concatenate
and ConcatenateResponse
. Note how they are annotated with JAXB meta-data. These classes are entirely inline with the sophisticated JSR 224 conventions.
Client Side Concatenation
Here is the simple client class:
- package com.ociweb.demo;
-
- class Client {
- public static void main(String args[]) {
- // create service
- ConcatenatorService service = new ConcatenatorService();
-
- // get the port
- Concatenator concatProxy = service.getConcatenatorPort();
-
- // Invoke the remote method
- String a = "Hello";
- String b = "Web Services!";
- String resultStr = concatProxy.concatenate(a,b);
- System.out.println("result = " + resultStr);
- }
- }
This class simply uses a ConcatenatorService
object to acquire a proxy for the web service operation, and then uses it with concatProxy.concatenate
. Note there are no annotations on this class: where do the other classes originate? The answer is another JAX-WS tool, wsimport
. The Ant build on the client side works like this:
- Use the
wsimport
tool to generate the client side support classes for the web service. The generation is predicated on the same WSDL URI shown earlier:wsimport
accepts the WSDL as input and generates/compiles the classes. This example uses a parameter to "keep" the generated classes for viewing. - Compile and run the
Client
class above
The output from the client is:
$ ant -f build_client.xml
Buildfile: build_client.xml
clean:
[delete] Deleting directory C:\measter\src\jsr181\ex1\client\classes
[delete] Deleting directory C:\measter\src\jsr181\ex1\client\generated
init:
[mkdir] Created dir: C:\measter\src\jsr181\ex1\client\classes
[mkdir] Created dir: C:\measter\src\jsr181\ex1\client\generated
build_client:
[javac] Compiling 1 source file to C:\measter\src\jsr181\ex1\client\classes
run_client:
[exec] result = Hello Web Services!
BUILD SUCCESSFUL
Total time: 11 seconds
Recap
Example 1 represents a complete round-trip from client to server for a trivial web service. Note the compact amount of code and how it concentrates on the business logic for the task at hand. With JSR 181, Java WS development joins EJB3 in the modern era of annotations.
The Annotations
The last example demonstrates the JAX-WS tools. However, the annotations are the heart of JSR 181. The table below gives a broad look at the available annotations:
Annotation | Description |
---|---|
@WebService | Marks a class/interface as a web service |
Allows overrides for outer WSDL attributes: targetNamespace, wsdlLocation, portName | |
@WebMethod | Customizes an exposed WS operation (or excludes a method from WS publication) |
Allows overrides for the name of the wsdl:operation | |
@Oneway | Denotes a web method as having one input message and no output |
@SOAPBinding | Allows a WS or web method to customize mapping from WS to SOAP protocol |
Defaults are DOCUMENT/LITERAL/WRAPPED but each dimension can be altered, e.g. setting style=RPC | |
@WebParam | Customizes the mapping from a Java param to a WS message part |
The legal usage and behavior depends on RPC vs DOCUMENT style, etc | |
@WebResult | Analogous to WebParam, but for the return value |
@HandlerChain | Advanced functionality to associate WS with external handler chain |
The most effective way to learn a given annotation is to modify the server Java class in Example 1, run the HTTP server from Ant and view the resulting WSDL in the browser. Also, one should examine the generated Java classes. Here is a starting example to try in this interactive manner:
- package com.ociweb.demo;
-
- import javax.jws.WebService;
- import javax.jws.WebMethod;
- import javax.jws.soap.SOAPBinding;
- import javax.xml.ws.Endpoint;
-
- @WebService(
- name="myConcatenatorType",
- serviceName="myConcatService",
- targetNamespace="http://reversed.when.normally.reads"
- )
-
- @SOAPBinding(
- style=SOAPBinding.Style.RPC
- )
-
- public class Concatenator {
- // as before
- }
Example 2: Acme Web Service
This example, though unabashedly contrived, illustrates more of the JSR 181 annotations.
The scenario: Superheroes, like many enterprises, have concentrated on their core competencies and have farmed out the manufacturing of their super-materials to specialty factories. Acme is one such manufacturer; it has a division to support the "arachnid class" of superhero, which has strong demand for custom-made super silk. In the past, the superheroes have interfaced with Acme's Arachnid Division through legacy technologies. Now, Acme wants to join the modern era with a new, well, "web service."
Acme has selected Java and JSR 181 as their technology. The specific goals include:
- Provide a web service that supports 3 operations:
- Request an estimate for a quantity of web material
- Place an order for a quantity of web material
- Optionally confirm the receipt of a shipment
- For legacy reasons, the project should use an existing Java "record" object and business interface. The interface essentially uses the Java record for 2 roles: the estimate and the purchase order.
- To mitigate a potentially poor design, a goal is to separate the business logic from the interface.
- A high-level architect insists on using the RPC-style SOAP.
Do Try This At Home (Not in Production)
Before examining the code, note:
- This example is contrived: the best practices in WS and Service Oriented Architecture mandate the publication of clean interfaces. These new, shiny interfaces hide poorly designed code via various design patterns (e.g. adapter).
- The HTTP server in Java 6 does not meet production requirements. It serves as a tool for experimentation and fast prototyping.
A Rapid Prototype
This example (available here) uses the same structure and Ant tasks as Example 1. The server side exists entirely in Acme.java
, which we analyze here. First, the legacy record:
- class OrderRecord {
- // very poor design! assume "legacy" record
- // estimate fields
- public int numUnits;
- public String customerId;
- public float cost;
- // order fields
- public String orderCode;
- public Date expectedArrival;
- }
Here is the legacy interface, decorated with JSR 181 meta-data:
- @WebService (
- name="AcmeWebOrderInterface"
- )
- @SOAPBinding(style=SOAPBinding.Style.RPC)
- interface LegacyInterface {
-
- @WebMethod
- @WebResult(name="Estimate")
- OrderRecord getEstimate( String customerId, int numUnits );
-
- @WebMethod
- boolean placeOrder( @WebParam(mode=WebParam.Mode.INOUT)
- Holder<OrderRecord> orderHolder );
-
- @WebMethod
- @Oneway
- void confirmReceipt(String orderCode);
- }
Recall in JAX-WS terminology, this interface is a Service Endpoint Interface (SEI). Note the following points:
- The @WebService annotation overrides the name of the web service, as we don't want a legacy name to be published. Also, @SOAPBinding is overridden to be RPC style.
- The
getEstimate
method returns anOrderRecord
, but we override the name to be "Estimate" in the WSDL. - The
placeOrder
uses an INOUT parameter: theOrderRecord
comes in with populated "estimate" fields and should leave with all fields populated. Note that JAX-WS enables INOUT params with theHolder<T>
class. - @Oneway adds the self-titled semantic to the
confirmReceipt
method, as it consists of one input and neither output nor inout elements.
A stated goal of JSR 181 is to separate public contracts from private implementations; this dovetails with an Acme requirement. The following Service Implementation Bean (SIB) demonstrates this:
- // Note that this SIB defers meta-data information to the SEI
- @WebService(
- endpointInterface="com.ociweb.demo.LegacyInterface",
- serviceName="AcmeWebService",
- portName="AcmeWebOrderPort"
- )
- public class Acme implements LegacyInterface {
-
- public OrderRecord getEstimate( String customerId, int numUnits ) {
- OrderRecord record = new OrderRecord();
- System.out.println("received estimate request. customerId = " + customerId);
- record.numUnits = numUnits;
- record.customerId = customerId;
- record.cost = numUnits * 100.0f;
- return record;
- }
-
- public boolean placeOrder( Holder<OrderRecord> orderHolder ) {
- OrderRecord order = orderHolder.value;
- final int arbitraryLimit = 10000;
- int randomInt = (new Random()).nextInt(arbitraryLimit);
- order.orderCode = Integer.toString(randomInt);
- order.expectedArrival = new Date();
- System.out.println("placing order with orderCode = " + order.orderCode);
- return true;
- }
-
- public void confirmReceipt(String orderCode) {
- // oneway methods should generally spawn a thread to do
- // the business logic
- System.out.println("writing confirmation to DB for: " + orderCode);
- }
-
- // arbitrary unit-test method -- not exposed to the web service
- boolean testingMethod() { return true; }
- }
-
Note the following points:
- The @WebService annotation overrides names but also ties the SIB to the SEI with the
endpointInterface
element. Note the separation: there are no other annotations in this class! - The
getEstimate
method is straight-forward. - The
placeOrder
method illustrates the use of the Holder class for INOUT parameters. - The spec recommendation for oneway methods such as
confirmReceipt
is to delegate the business logic to a separate thread. - To emphasize the separation between the SEI and SIB, the
testingMethod
might be used to unit-test the business logic. Note that if this method were in the interface, it could be excluded via an element on the @WebMethod annotation.
That's it for the server! The client steps through the use case of getting an estimate, making an order and confirming receipt. As with Example 1, JSR 181 and JAX-WS perform an astounding amount of heavy lifting.
In Production
As noted, the HTTP server in JDK 6 is not appropriate for production. In order to move Example 2 to a production enviroment, it is necessary to use a web/app server that is enabled for JAX-WS.Here is a list of suitable application servers which are Java EE 5 compatible (and thus JAX-WS enabled). Note that Tomcat does not yet work out-of-the-box: here are instructions to enable Tomcat for JAX-WS.
Even with the appropriate server, another aspect to consider is the generation of the JSR-109 deployment descriptors, especially as this is the major boon of JAX-WS. The wsgen
tool (here) performs this task: it accepts a SEI and generates the required artifacts for deployment and invocation.
JAX-WS RI
The reference implemenation (RI) for JAX-WS is located here. Here is a quick road-map regarding the RI:
- Version 2.0 is bundled with Java 6, but exists as a separate download for Java 5. (Strictly speaking, it requires only the annotation support in Java 5.)
- Version 2.0.1 existed only as an intermediate version to transition to version 2.1. However, due to a technical issue, version 2.1 required changes to the API and spec, and is no longer available. At this writing, version 2.1.1 is the most current release.
- Some highlights of new features in version 2.1.1:
- Ant and Maven support for
wsimport
andwsgen
tools. - Support for standards such as SOAP 1.2 and WS-I Basic Profile 1.1.
- Extension support for the WS-* specs within the WSIT umbrella.
- Ant and Maven support for
Summary
Java 5's original annotation facility was not a solution, but a promise. Eventually, the solutions poured in for EJB3, JAXB and other communities within Java.
Now, JAX-WS brings the same power of development time metadata to the WS space, leveraging annotations and JAXB. Moreover, JSR 181 acts as a facade pattern for JAX-WS and brings remarkable simplicity for common WS tasks. Combined with the innate HTTP server in Java 6, WS developers finally enjoy simplicity and agility. The woeful cry of a "Java Simplification Request" may well soar into a joyful (if grammatically incorrect) cheer: "we never met a data we didn't like!"
Example Notes
The examples in this article use Java 1.6.0, and Ant 1.6.x. It is assumed that Java and Ant are installed on the machine. The first example is inspired by a blog post by Vivek Pandey.
References and Resources
- [1] Java SE6
- [2] JSR 181 - Web Service Metadata for the Java Platform
- [3] JSR 224 - Java API for XML-Based Web Services (JAX-WS) 2.0
- [4] The JAX-WS Reference Implementation (RI)
- [5] JSR 101 - Java APIs for XML based RPC (JAX-RPC)
- [6] JSR 109 - Implementing Enterprise Web Services
- [7] JSR 222 - Java Architecture for XML Binding (JAXB) 2.0
- [8] JSR 175 - A Metadata Facility for the Java Programming Language
- [9] Doug Kohlert's JAX-WS blog (tech lead for spec)
- [10] Vivek Pandey's JAX-WS blog
- [11] Sun site: Developing Web Services at a Glance
- [12] SOA Using Java Web Services
- [13] SOAP (Wikipedia)
- [14] WSDL (Wikipedia)
Michael Easter thanks Eric Burke, Tom Wheeler, Dean Wette and Jeremy Ford for reviewing this article and providing useful suggestions.