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.
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 represents the first version of web services in Java. See the graphical overview below. Some key points:
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.
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.
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:
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.
The stage is set for JSR 181: let's dig in.
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.
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.
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):
apt tool on the
source code. The JSR 181 processor will generate the necessary
artifacts; apt compiles the Java code as well.
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:
Concatenator.
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.
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:
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.
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
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 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
}
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:
Before examining the code, note:
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:
getEstimate method returns an
OrderRecord, but we
override the name to be "Estimate" in the WSDL.
placeOrder uses an INOUT
parameter: the OrderRecord comes in
with populated "estimate" fields and should leave with all fields
populated. Note
that JAX-WS enables INOUT params with the Holder<T>
class.
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:
endpointInterface element. Note the
separation: there are no other
annotations in this class!
getEstimate method is
straight-forward.
placeOrder method illustrates
the use of the Holder<T> class for
INOUT parameters.
confirmReceipt
is to delegate the business logic to a separate thread.
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.
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.
The reference implemenation (RI) for JAX-WS is located here. Here is a quick road-map regarding the RI:
wsimport
and wsgen tools.
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!"
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.
Michael Easter thanks Eric Burke, Tom Wheeler, Dean Wette and Jeremy Ford for reviewing this article and providing useful suggestions.