Skip to main content

RESTful Web Services with JAX-WS

Posted by mhadley on January 10, 2006 at 1:58 PM PST

The JAX-WS implementation that is included in the, recently released, JWSDP 2.0 preview now supports the XML/HTTP binding. This new functionality allows JAX-WS applications to implement and use RESTful Web services. Here is a worked example that demonstrates how to use JAX-WS to query the Yahoo News Search service.

The first step is to create a JAX-WS port for the Yahoo News Search service

URI nsURI = new URI("urn:yahoo:yn");
QName serviceName = new QName("yahoo",nsURI.toString());
QName portName = new QName("yahoo_port",nsURI.toString());
Service s = Service.create(serviceName);
URI address = new URI("http", null, "", 80,
s.addPort(portName, HTTPBinding.HTTP_BINDING, address.toString());

Note that the URI constructed above contains a set of input parameters to the search service. The appid parameter identifies the application to Yahoo, if you are writing a new application you should register a new ID for it here. In this instance we are creating a URI that will search for news about Java and return at most 10 English language results sorted by date. Note that its not necessary to create a new port each time you change the arguments, more on that at the end.

Once the port is created we can use the Service instance to create a Dispatch instance. Dispatch is a new JAX-WS interface designed with dynamic, document-oriented services in mind. Note that it is a generic interface, in this instance we create a Dispatch<Source> instance since we are working with simple XML documents. You can also create Dispatch<SOAPMessage> instances when exchanging SOAP-based messages and Dispatch<DataHandler> for working with arbitrary types of data.

Dispatch<Source> d = s.createDispatch(portName, Source.class, Service.Mode.PAYLOAD);
Map<String, Object> requestContext = d.getRequestContext();
requestContext.put(MessageContext.HTTP_REQUEST_METHOD, new String("GET"));

Now that we have a Dispatch instance we can send the search request and retrieve the results. In this case we retrieve the results and transform them into a DOM tree.

Source result = d.invoke(null);
DOMResult domResult = new DOMResult();
Transformer trans = TransformerFactory.newInstance().newTransformer();
trans.transform(result, domResult);

Now we have the results as a DOM tree we can apply XPath queries to extract the data we are interested in. Here we first use an XPath query to obtain the number of search results returned and then use that knowledge to interate through the results using additional queries.

XPathFactory xpf = XPathFactory.newInstance();
XPath xp = xpf.newXPath();
xp.setNamespaceContext(new NSResolver("yn", nsURI.toString()));
NodeList resultList = (NodeList)xp.evaluate("/yn:ResultSet/yn:Result", domResult.getNode(),
int len = resultList.getLength();
for (int i=1;i<=len;i++) {
    String title = xp.evaluate("/yn:ResultSet/yn:Result["+i+"]/yn:Title", domResult.getNode());
    String click = xp.evaluate("/yn:ResultSet/yn:Result["+i+"]/yn:ClickUrl", domResult.getNode());
    System.out.printf("[%d] %s (%s)\n",i,title,click);

The NSResolver class in the above is a custom class based on an example I found here. It simply maps the yn namespace prefix used in the XPath queries to the Yahoo namespace URI (urn:yahoo:yn) used in the result XML document.

I mentioned above that there's no need to create a new JAX-WS port when the input data changes, the following lines submit a new query using the existing objects we've already created.

result = d.invoke(null);

That's it for now, in future blogs I'll demonstrate how to use JAXB instead of the XPath APIs to get strongly typed access to the results and how WADL can be used to generate JAX-WS based stubs for RESTful services.

Related Topics >>