- What Is REST?
- The Pieces of a RESTFul Web Service
- Introducing JAX-RS
- More JAX-RS Annotations
- JAXB and More Interesting XML-Based Web Services
- JSON Serialization
- More on Content Negotiation
- RESTful SOA
- Summary
JAXB and More Interesting XML-Based Web Services
We’ve now implemented a simple RESTful web service for the banking example, but it still leaves a lot to be desired. Hand-crafting XML might have been an appealing thought at the dawn of the REST services era, but it’s hardly a scalable solution. Instead, we need to discuss ways of generating the XML produced by our services from our POJOs. This can be accomplished in several ways. One is using the org.w3c.dom.Document interface to generate a document from its parts. In some cases, this is the best possible approach, especially if you have to handle the generation of several different XML schemas. However, you usually don’t need the flexibility of a dynamic approach. In such a case, a simple static approach that ties your POJOs to a single XML schema is best. The JAXB standard gives you the capability to easily generate XML documents from your POJO objects with just a few annotations.
The JAXB Annotations
Essentially, only two annotations are needed to get going with JAXB:
- @XmlRootElement: This annotation maps an entire Java class or enum type to an XML element. It’s called the “RootElement” because it’s the root of the tree of XML tags (with the attributes of the class being the leaves of the tree).
- @XmlElement: This annotation maps a JavaBean property or a nonstatic, nontransient field to an XML element.
One of the common changes made to the XML tags that are output by a JAXB mapping is to change the name of the tag (which, by default, is the same as the name of the field or property). This is done with the name parameter to the annotation. Consider the following example where you have a field named foo in your code, which you annotate with a standard @XMLElement tag:
@XmlElement public int foo;
Your XML output then is of the form <foo>123</foo>. That might not be helpful for someone trying to read and parse the XML without knowledge of your special variable naming conventions. Instead, using name as follows
@XmlElement(name="accountNumber") public int foo;
results in more readable output:
<accountNumber>123</accountNumber>.
Combining these annotations is easy. Consider a simple POJO class that represents an account in our banking example. As in previous examples, you create a new class in your project, named com.ibm.mwdbook.restexamples.Account, and then fill in the code from the example (see Listing 4.4).
Listing 4.4 Account Class
package com.ibm.mwdbook.restexamples; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; @XmlRootElement public class Account{ int id; String accountType; String description; String currency; double balance; // For JAXB Serialization to work every class must have a // default no-arg constructor // if there are any other constructors defined! public Account() { } public Account(int i, String name, String type, double balance) { setId(i); setDescription(name); setAccountType(type); setBalance(balance); setCurrency("USD"); } @XmlElement public int getId() { return id; } public void setId(int id) { this.id = id; } @XmlElement(name="name") public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } @XmlElement(name="type") public String getAccountType() { return accountType; } public void setAccountType(String accountType) { this.accountType = accountType; } @XmlElement public String getCurrency() { return currency; } public void setCurrency(String currency) { this.currency = currency; } @XmlElement public double getBalance() { return balance; } public void setBalance(double balance) { this.balance = balance; } }
A couple of points are worth calling out in this example. The first is the use of the no-arg constructor. Even if your code doesn’t use a no-arg constructor, one is necessary for any class serialized by JAXB because the JAXB framework itself expects to use it. The second point to notice is that we’ve annotated the getter methods. This means that we’ve annotated the properties for this example; later in Listing 4.7, we annotate the fields and discuss the differences. In one particular case, we’ve even illustrated a common aspect of properties annotation—note this annotation:
@XmlElement(name="type")
Here, we want the name of the tag in the XML to differ from the name of the property itself. You can do this by specifying the name= property within the annotation. This way, the segment of XML generated would be of this form
<type> somevalue </type>
instead of the default:
<accountType> somevalue </accountType>
You’ll find yourself substituting names like this fairly often when you have to work with existing XML schemas or JSON formats.
A Trivial DAO (and Its Use)
Now that we have an annotated POJO class representing our accounts, we need to turn our attention to how accounts are created and managed. One of the biggest contributions to the field of Java EE design over the last 15 years is the book Core J2EE Patterns, by Alur, et. al. Later developments in JEE have superseded many of the patterns called out in this book, but some are still as appropriate as ever. One in particular that is extremely helpful in many different situations, and one that we will follow in this book, is the Data Access Object (DAO) pattern. (You might remember that you built a simple JDBC-based DAO in Chapter 2.) The benefit of this pattern is that it provides an interface that encapsulates and abstracts away all the details of access to any data source. This enables you to replace one implementation of a DAO with another, without having to change any of the code that uses the DAO. So in this case, we’re building a very trivial DAO that’s useful for testing and hides the details of retrieving an account from an account list. Listing 4.5 shows the code for this DAO.
Listing 4.5 AccountDao Class
public class AccountDao { HashMap<Integer, Account> accounts = new HashMap<Integer, Account>(); public AccountDao() { Account anAccount = new Account(123,"savings",110.0); accounts.put(123, anAccount); } public List<Account> getAccounts() { List<Account> accountslist = new Vector<Account>(); accountslist.addAll(accounts.values()); return accountslist; } public Account get(int id) { return (Account) accounts.get(id); } }
That’s all you need for now—just a simple constructor that creates a HashMap of accounts and adds one to the list, and then a getter for both a list of all accounts and an account stored at a specific ID. However, that’s enough to help implement our next, more useful example of a JAX-RS resource (see Listing 4.6).
Listing 4.6 AccountResource Class
@Path("/accounts") public class AccountResource { AccountDao dao = new AccountDao(); public AccountResource() { } @GET @Produces(MediaType.APPLICATION_XML) public List<Account> getAccounts() { return dao.getAccounts(); } @GET @Path("{id}") @Produces(MediaType.APPLICATION_XML) public Account getAccount(@PathParam(value="id") int id){ return dao.get(id); } }
We now have an example that’s complete enough to be of some use. This is exactly the type of simple resource that you would implement when testing an AJAX UI that relies on a number of REST services to provide information that you can manipulate through JavaScript and HTML. You can see that we’ve used all the different annotations we’ve examined already, and we’ve also used the constants in the class MediaType instead of hand-coding application/xml for the @Produces annotation (which is a best practice to eliminate the possibility of mistyping).
To test the example, simply type the following into your browser:
http://localhost:9080/RestServicesSamples/banking/accounts/123