 |
RESTful Web Services with JAX-WS
Posted by mhadley on January 10, 2006 at 01:58 PM | Comments (7)
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, "api.search.yahoo.com", 80,
"/NewsSearchService/V1/newsSearch",
"appid=jaxws_restful_sample&type=all&results=10&sort=date&language=en&query=java",
null);
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(),
XPathConstants.NODESET);
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.
requestContext.put(MessageContext.QUERY_STRING,
"appid=jaxws_restful_sample&type=all&results=10&sort=date&language=en&query=solaris");
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.
Bookmark blog post: del.icio.us Digg DZone Furl Reddit
Comments
Comments are listed in date ascending order (oldest first) | Post Comment
-
I am trying something similar to what you have posted. Instead of using Dispatch, I am using Dispatch.
I get the error shown below.
javax.xml.ws.WebServiceException: Mode not allowed with HTTP Binding. Must use other Service.mode
Any ideas...
C:\mool\src>java -classpath .;c:\Sun\jwsdp-2.0\jaxb\lib\jaxb-api.jar;c:\Sun\jws
dp-2.0\jaxws\lib\jaxws-api.jar;C:\Sun\jwsdp-2.0\jaxws\lib\jaxws-rt.jar;c:\Sun\jw
sdp-2.0\jwsdp-shared\lib\resolver.jar;c:\Sun\jwsdp-2.0\saaj\lib\saaj-api.jar;c:\
Sun\jwsdp-2.0\jwsdp-shared\lib\activation.jar;c:\Sun\jwsdp-2.0\sjsxp\lib\jsr173_
api.jar;c:\sun\jwsdp-2.0\jaxb\lib\jaxb-impl.jar Test
javax.xml.ws.WebServiceException: Mode not allowed with HTTP Binding. Must use o
ther Service.mode
at com.sun.xml.ws.protocol.xml.client.XMLMessageDispatcher.doSend(XMLMes
sageDispatcher.java:142)
at com.sun.xml.ws.protocol.xml.client.XMLMessageDispatcher.send(XMLMessa
geDispatcher.java:124)
at com.sun.xml.ws.client.dispatch.impl.DispatchDelegate.send(DispatchDel
egate.java:71)
at com.sun.xml.ws.client.dispatch.DispatchBase.sendAndReceive(DispatchBa
se.java:308)
at com.sun.xml.ws.client.dispatch.DispatchBase.invoke(DispatchBase.java:
133)
at Test.foo(Test.java:30)
at Test.main(Test.java:16)
Posted by: poleman on April 27, 2006 at 09:24 PM
-
Dispatch<String> isn't one of the supported types, you need to use one of Dispatch<Source>, Dispatch<DataSource> or Dispatch<SOAPMessage>. Dispatch<DataSource> lets you get at a raw stream of the message data and that should be straightforward to turn into a String.
Posted by: mhadley on April 28, 2006 at 05:55 AM
-
Did you mean java.sql.DataSource ?
Posted by: poleman on April 30, 2006 at 01:14 AM
-
I tried it with javax.sql.DataSOurce and get the same error
Here's the code snippet
Dispatch d = s.createDispatch(portName, DataSource.class, Service.Mode.PAYLOAD);
Map requestContext = d.getRequestContext();
requestContext.put(MessageContext.HTTP_REQUEST_METHOD, new String("GET"));
DataSource result = d.invoke(null);
and the error
javax.xml.ws.WebServiceException: Mode not allowed with HTTP Binding. Must use o
ther Service.mode
at com.sun.xml.ws.protocol.xml.client.XMLMessageDispatcher.doSend(XMLMes
sageDispatcher.java:142)
at com.sun.xml.ws.protocol.xml.client.XMLMessageDispatcher.send(XMLMessa
geDispatcher.java:124)
at com.sun.xml.ws.client.dispatch.impl.DispatchDelegate.send(DispatchDel
egate.java:71)
at com.sun.xml.ws.client.dispatch.DispatchBase.sendAndReceive(DispatchBa
se.java:308)
at com.sun.xml.ws.client.dispatch.DispatchBase.invoke(DispatchBase.java:
133)
Posted by: poleman on April 30, 2006 at 01:32 AM
-
Alright, I tried with javax.activation.DataSource, I get this error
javax.xml.ws.WebServiceException: Can not have a Datahandler class with mode PAY
LOAD
at com.sun.xml.ws.client.dispatch.DispatchBase.setDispatchContext(Dispat
chBase.java:562)
at com.sun.xml.ws.client.dispatch.DispatchBase.setMetadata(DispatchBase.
java:459)
at com.sun.xml.ws.client.dispatch.DispatchBase.setMessageStruct(Dispatch
Base.java:432)
at com.sun.xml.ws.client.dispatch.DispatchBase.setupMessageStruct(Dispat
chBase.java:426)
at com.sun.xml.ws.client.dispatch.DispatchBase.invoke(DispatchBase.java:
Posted by: poleman on April 30, 2006 at 01:49 AM
-
I tried it with Service.MESSAGE and I get this error
java.lang.NullPointerException
at com.sun.xml.ws.encoding.xml.XMLMessage.(XMLMessage.java:148)
at com.sun.xml.ws.protocol.xml.client.XMLMessageDispatcher.makeXMLMessag
e(XMLMessageDispatcher.java:842)
at com.sun.xml.ws.protocol.xml.client.XMLMessageDispatcher.doSend(XMLMes
sageDispatcher.java:145)
at com.sun.xml.ws.protocol.xml.client.XMLMessageDispatcher.send(XMLMessa
geDispatcher.java:124)
at com.sun.xml.ws.client.dispatch.impl.DispatchDelegate.send(DispatchDel
egate.java:71)
at com.sun.xml.ws.client.dispatch.DispatchBase.sendAndReceive(DispatchBa
se.java:308)
at com.sun.xml.ws.client.dispatch.DispatchBase.invoke(DispatchBase.java:
133)
at Test.foo(Test.java:31)
at Test.main(Test.java:17)
Posted by: poleman on April 30, 2006 at 07:07 AM
-
MESSAGE mode with javax.activation.DataSource should give a working combination. I think I ran into a similar problem using an earlier build of JAX-WS with this combination but it works now. Try downloading a recent nightly build (available from: https://jax-ws.dev.java.net/ri-download.html) and use that rather than the older code in JWSDP.
Posted by: mhadley on May 01, 2006 at 06:07 AM
|