JSR 181: a Java Simplification Request

by
Michael Easter,
Senior Software Engineer
with Advantage Consulting, Inc.,
an OCI affiliated company.

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:

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:

an overview of JAX-RPC

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:

However, the following goals are more interesting:

See the graphical overview below. There are several themes here:

an overview of JAX-WS

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:

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.

JSR 181 as a Facade pattern

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:

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):

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):

Once the server is running, we can point a browser to http://localhost:9998/concatservice?wsdl:

the WSDL output for the Concatenation service

Presto! With stunning ease, the web service is available, and follows these JSR 224 conventions:

If we point the browser to the URI of the schema types:

the schema definitions for the Concatenation service

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:

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:

Do Try This At Home (Not in Production)

Before examining the code, note:

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:

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:

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:

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

Michael Easter thanks Eric Burke, Tom Wheeler, Dean Wette and Jeremy Ford for reviewing this article and providing useful suggestions.


Valid XHTML 1.0 Strict [Valid RSS]
RSS
Top