Using JAXB

Creating JAXB beans

For this example, we'll work with a set of JAXB bean that are generated from an existing XML Schema document. The example below provides a sample schema document for a stock quote service. There is only one xml element defined, which includes the stock price and the company name and ticker symbol.

To generate a set of JAXB beans from this schema, you will need to run the "xjc" tool that is provided with the JDK (available in Java 6). Passing the schema document as a parameter, this should generate a set of java classes including one for the Stock and one of the StockQuote types.

Once you have generated the beans, you're ready to move on to the next step; building the stock quote JAX-RS resource.

<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
	xmlns:ns="http://ibm.com/sample/jaxrs"
	targetNamespace="http://ibm.com/sample/jaxrs">
  
    <xsd:element name="stockQuote">
       <xsd:complexType>
         <xsd:sequence>
           <xsd:element name="stock" type="ns:Stock" />
           <xsd:element name="quote" type="xsd:int" />
         </xsd:sequence>
       </xsd:complexType>
    </xsd:element>
    
    <xsd:complexType name="Stock">
       <xsd:sequence>
         <xsd:element name="symbol" type="xsd:string" />
         <xsd:element name="price" type="xsd:float" />
         <xsd:element name="companyName" type="xsd:string" />
       </xsd:sequence>
    </xsd:complexType>
</xsd:schema>

Adding JAXB beans to resources

JAXB beans can be included either as parameters to the request or the return type of a methods. For the purposes of simplicity, let's start by returning a JAXB from a resource method. The code below is an example of how to implement the StockQuoteService resource method. The method will return the StockQuote bean that corresponds to the stock symbol in the path parameter.

This resource can be packaged and deployed as a JAX-RS resource using the instructions found here.

package com.ibm.sample.jaxrs;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;

import java.util.HashMap;
import java.util.Map;

@Path(value="/stocks")
public class StockQuoteService {
    
    private static Map<String , StockQuote> stocks = new HashMap<String
	, StockQuote>();
    
    static {
        Stock s = new Stock();
        s.setCompanyName("IBM");
        s.setSymbol("IBM");
        StockQuote quote = new StockQuote();
        quote.setQuote(98.99);
        quote.setStock(s);
        stocks.put(s.getSymbol(), quote);
        
        s = new Stock();
        s.setCompanyName("Yahoo!");
        s.setSymbol("YHOO");
        quote = new StockQuote();
        quote.setQuote(23.18);
        quote.setStock(s);
        stocks.put(s.getSymbol(), quote);
    }
    
    @GET
    @Path(value="/{symbol}")
    @Produces(value="application/xml")
    public StockQuote getQuote(@PathParam(value="symbol") String symbol) {
        System.out.println("[stockquote] - looking up quote for symbol " + symbol);
        StockQuote quote = stocks.get(symbol);
        System.out.println("[stockquote] - returning value: " + quote);
        return quote;
    }

}

Creating a client

Once deployed, the newly created resource can be accessed from a browser using this URL:

http://<hostname>:<port>/<context root>/stocks/{stock symbol}

For this example, we'll provide some concrete values to those variables. We'll use:

http://localhost:9080/stockquote/stocks/IBM

However, it is more useful to access this resource from a more robust client. One way to do this is using the Apache Commons HTTP Client API. More information about how to write clients using this API can be found here.

The code sample below shows how to access this resource and retreive the information using JAXB. Notice that an InputStream is returned from the resource and the client passes that directly to the JAXB Umarshaller API. Based on the configuration provided, JAXB will return the appropriate object type.

package com.ibm.sample.jaxrs;

import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.methods.GetMethod;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.Unmarshaller;

import java.io.InputStream;

public class StockQuoteClient {
    
    private String url = "http://localhost:9080/stockquote/stocks/";
    
    private JAXBContext ctx;
    
    public static void main(String[] args) throws Exception {
        StockQuoteClient client = new StockQuoteClient();
        client.getQuote("IBM");        
    }
    
    public void getQuote(String symbol) throws Exception {
        HttpClient client = new HttpClient();
        GetMethod get = new GetMethod(url + symbol);
        
        int responseCode = client.executeMethod(get);
        System.out.println("[getquote] - response code: " + responseCode);
        
        JAXBContext ctx = JAXBContext.newInstance("com.ibm.sample.jaxrs");
        Unmarshaller u = ctx.createUnmarshaller();
        
        InputStream is = get.getResponseBodyAsStream();
        StockQuote quote = (StockQuote) u.unmarshal(is);
        
        if (quote != null) {
            System.out.println("[getquote] - " + quote.getStock().getSymbol() 
                + " = " + quote.getQuote());
        }
    }
    
}

Sending JAXB data from the client

Now that you know how to receive the JAXB data, try sending JAXB data to the server using the HTTP POST method. See if you can follow the code samples below to create a JAXB object instance and send the data using the Apache client so that a new stock symbol is added to the quotes database.

To do this, the first thing you'll need is the actual resource method. Here's an example of what this would look like. Notice that this is a method with a void response and the JAXB parameter for the input. Try adding this to your existing StockQuoteService and redeploy the service in your environment.

@POST
@Consumes(value="application/xml")
public void addQuote(StockQuote q) {
    stocks.put(q.getStock().getSymbol(), q);
}

Now add a method to the client that will target the new resource method. Notice that instead of creating an Umarshaller to get the data out of the stream for JAXB, this time we're creating a Marshaller and putting the data into a stream. The contents of the stream are then put into a RequestEntity object which also requires that the HTTP Content-Type header be set. Invoking this method will send your data to the server and add a new stock type to the database.

public int addQuote(String company, String symbol, double price) 
  throws Exception {
    ObjectFactory of = new ObjectFactory();
    Stock s = of.createStock();
        
    s.setCompanyName(company);
    s.setSymbol(symbol);
        
    StockQuote q = of.createStockQuote();
    q.setQuote(price);
    q.setStock(s);
        
    JAXBContext ctx = JAXBContext.newInstance("com.ibm.sample.jaxrs");
    Marshaller m = ctx.createMarshaller();

    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    m.marshal(q, baos);
        
    HttpClient client = new HttpClient();
    PostMethod post = new PostMethod(url);
        
    RequestEntity request = new ByteArrayRequestEntity(baos.toByteArray(), 
    	"application/xml; charset=utf-8");
    post.setRequestEntity(request);
                
    int responseCode = client.executeMethod(post);
    System.out.println("[addquote] - response code: " + responseCode);
        
    return responseCode;
}

The next step is to update the client code to call the new method. Here's a sample of how to to this. Once the addQuote method is called, you should be able to access the new resource via the getQuote method.

public static void main(String[] args) throws Exception {
    StockQuoteClient client = new StockQuoteClient();
    client.getQuote("IBM");
        
    client.addQuote("Microsoft", "MSFT", 22.45);
    client.getQuote("MSFT");
}

After making all of the updates, the output of the client should look like the following:

[getquote] - response code: 200
[getquote] - IBM = 98.99
[addquote] - response code: 200
[getquote] - response code: 200
[getquote] - MSFT = 22.45

Take the next step

Now see if you can take it one step further by adding a resource method that updates the stock price using the HTTP PUT method. Remember, you'll need to add a resource method annotated with @PUT along with new client code for invoking this method. The resource method signature should be similar to the addQuote signature, but this method will just updated the price of the stock. Good luck!