The Source for Java Technology Collaboration
User: Password:



Marc Hadley

Marc Hadley's Blog

JAX-WS and Binary Data

Posted by mhadley on April 27, 2006 at 07:24 AM | Comments (2)

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

Bookmark blog post: del.icio.us del.icio.us Digg Digg DZone DZone Furl Furl Reddit Reddit
Comments
Comments are listed in date ascending order (oldest first) | Post Comment

  • This article is a very good read.

    I have a question. If I have a REST Web Service that expects username and password (as Base64 encoded strings) , how can username and password be passed to the REST Web Service ?
    Is there a provision in the Dispatch API that allows us to pass username and password to the REST Service ?

    Posted by: srivats_sivan on November 15, 2006 at 07:17 AM

  • There are some standard request context properties that you can use. If you are using standard HTTP authentication then use USERNAME_PROPERTY and PASSWORD_PROPERTY. If you are using some custom authentication scheme then you can set arbitrary HTTP headers using the HTTP_REQUEST_HEADERS property. E.g.:

    Map context = myDispatch.getRequestContext();
    context.put(BindingProvider.USERNAME_PROPERTY, "MyUSerName");
    context.put(BindingProvider.PASSWORD_PROPERTY, "MyPassword");

    Posted by: mhadley on November 15, 2006 at 08:23 AM



Only logged in users may post comments. Login Here.


Powered by
Movable Type 3.01D
 Feed java.net RSS Feeds