Skip to main content

JAX-WS and Binary Data

Posted by mhadley on April 27, 2006 at 7:24 AM PDT

In previous entries I've shown how to use the JAX-WS APIs to publish and use RESTful Web services. Up till now I've focussed mainly on XML/HTTP since this is what most services use but JAX-WS can also handle other kinds of data. This entry shows how to use the JAX-WS APIs to work with binary image data and uses the popular Flickr service to illustrate the capabilities.

The Flickr APIs come in three varieties: REST, XML-RPC and SOAP. We'll concentrate on the REST API for this entry and develop some code that will search a particular users photo stream using keywords. Before you start developing your own application you'll need to obtain an API key from Flickr; API keys are free for non-commercial use, I'll include the one I obtained in the code below but please get a new key for any new applications you might develop.

The initial boilerplate code is the same as shown in previous Dispatch examples but I'll repeat it here for completeness:

URI baseURI = new URI("http://www.flickr.com/");
URI staticBaseURI = new URI("http://static.flickr.com/");
QName serviceName = new QName("FlickrService", baseURI.toString());
Service flickrService = Service.create(serviceName);
QName flickrPortName = new QName("flickr_port",baseURI.toString());
URI address = new URI("http", null, baseURI.getHost(), baseURI.getPort(),
    baseURI.getPath()+"services/rest/", null, null);
flickrService.addPort(flickrPortName, HTTPBinding.HTTP_BINDING, address.toString());
Dispatch<Source> flickrDispatch = flickrService.createDispatch(flickrPortName,
    Source.class, Service.Mode.PAYLOAD);

The Flickr photo search method takes a user ID rather than a user name so we first have to call the find by username method to convert the human-friendly username into the corresponding userId:

// set up the method call and parameters
Map<String, Object> requestContext = flickrDispatch.getRequestContext();
requestContext.put(MessageContext.HTTP_REQUEST_METHOD, "GET");
String queryString = "method=flickr.people.findByUsername" +
    "&api_key=f2b38aa733c997301cd7153f05d15410" +
    "&username=" + java.net.URLEncoder.encode(username, "utf-8");
requestContext.put(MessageContext.QUERY_STRING, queryString);
           
// execute query
Source result = flickrDispatch.invoke(null);
DOMResult domResult = new DOMResult();
Transformer trans = TransformerFactory.newInstance().newTransformer();
trans.transform(result, domResult);
           
// parse results
XPathFactory xpf = XPathFactory.newInstance();
XPath xp = xpf.newXPath();
String userId = xp.evaluate("/rsp/user/@nsid", domResult.getNode());

With userID and a set of keywords (aka tags) we can now execute the search:

queryString = "method=flickr.photos.search" +
    "&api_key=f2b38aa733c997301cd7153f05d15410" +
    "&user_id=" + java.net.URLEncoder.encode(user, "utf-8") +
    "&per_page=10" +
    "&tags=" + java.net.URLEncoder.encode(tags, "utf-8");
requestContext.put(MessageContext.QUERY_STRING, queryString);
           
// execute query
result = flickrDispatch.invoke(null);
domResult = new DOMResult();
trans.transform(result, domResult);
           
// parse results
XPath xp = xpf.newXPath();
NodeList resultList = (NodeList)xp.evaluate("/rsp/photos/photo",
    domResult.getNode(), XPathConstants.NODESET);
ArrayList<FlickrItem> list = new ArrayList<FlickrItem>();
for (int i=0;i<resultList.getLength();i++) {
    String id = xp.evaluate("@id", resultList.item(i));
    String title = xp.evaluate("@title", resultList.item(i));
    String secret = xp.evaluate("@secret", resultList.item(i));
    String server = xp.evaluate("@server", resultList.item(i));
    FlickrItem item = new FlickrItem(staticBaseURI.toString(), id, server, secret, title);
    list.add(item);
}

In the above I'm using a home-grown utility class FlickrItem to hold the data for a particular photo. I won't reproduce it in its entirety here since its a simple JavaBean with only one real trick: it can produce a URL to a particular photo using the algorithm described here. The relevant method is:

public URL getMediumURL() {
    return new URL(baseURI+server+"/"+id+"_"+secret+".jpg");
}

All of the above was really just a necessary precursor to get to what I really wanted to show: working with binary data using Dispatch. Given a particular FlickrItem member of list resulting from the photo search we can retrieve the photo as an Image as follows:

public Image getImage(FlickrItem item) {
    URL url = item.getMediumURL();
    Dispatch<DataSource> flickrStaticDispatch = flickrService.createDispatch(
        flickrPortName, DataSource.class, Service.Mode.MESSAGE);
    Map<String, Object> requestContext =
       flickrStaticDispatch.getRequestContext();
    requestContext.put(MessageContext.HTTP_REQUEST_METHOD, new String("GET"));
    requestContext.put(Dispatch.ENDPOINT_ADDRESS_PROPERTY, url.toString());

    DataSource result = flickrStaticDispatch.invoke(null);
    BufferedImage img = ImageIO.read(result.getInputStream());
    return img;
}

Note the use of DataSource and Service.Mode.MESSAGE in the creation of the Dispatch instance. This creates a Dispatch that works at the message (vs payload) level using a DataSource to represent each message - this type of Dispatch can be used to work with any kind of binary data desired. It can also be used to perform HTTP PUT and POST with arbitrary types of request payload.

Its a simple matter of programming to take the above code and turn it into an application like that shown below. If you'd like to see this working alongside other code samples from previous entries then join me at JavaOne on Tuesday 16th May at 3.15pm for my session on JAX-WS and RESTful Web Services.

flickr.png

Related Topics >>