Below is a complete example of how this could be done:
XML Response
Below I am going to demonstrate how to get the following response where the URI in the address
element is put in via an XmlAdapter
that is aware of the UriInfo
.
<?xml version="1.0" encoding="UTF-8"?>
<customer id="1">
<name>Jane Doe</name>
<address>http://localhost:9999/address/123</address>
</customer>
Java Model
Below is the Java model that I will use for this example.
Customer
By default the contents of the Address
class will be marshalled beneath the customer
element. We will use an XmlAdapter
to perform special handling for this.
import javax.xml.bind.annotation.*;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
@XmlRootElement
@XmlType(propOrder={"name", "address"})
public class Customer {
private int id;
private String name;
private Address address;
@XmlAttribute
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@XmlJavaTypeAdapter(AddressAdapter.class)
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
}
Address
import javax.xml.bind.annotation.XmlAttribute;
public class Address {
private int id;
@XmlAttribute
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
}
XmlAdapter
Below is the XmlAdapter
that we will use. Notice how it gets information from the AddressResource
for building the URI
. It requires a UriInfo
, this makes it stateful. We will need to set an instance of this XmlAdapter
on the Marshaller
to get everything to work correctly.
import javax.ws.rs.core.*;
import javax.xml.bind.annotation.adapters.XmlAdapter;
public class AddressAdapter extends XmlAdapter<String, Address> {
private UriInfo uriInfo;
public AddressAdapter() {
}
public AddressAdapter(UriInfo uriInfo) {
this.uriInfo = uriInfo;
}
@Override
public Address unmarshal(String v) throws Exception {
// TODO Auto-generated method stub
return null;
}
@Override
public String marshal(Address v) throws Exception {
if(null == uriInfo) {
return "";
}
UriBuilder builder = UriBuilder.fromResource(AddressResource.class);
System.out.println(uriInfo.getAbsolutePath().getHost());
builder.scheme(uriInfo.getAbsolutePath().getScheme());
builder.host(uriInfo.getAbsolutePath().getHost());
builder.port(uriInfo.getAbsolutePath().getPort());
builder.path(AddressResource.class, "get");
return builder.build(v.getId()).toString();
}
}
JAX-RS Services
In this example there are two services one for Address
and another for Customer
.
AddressResource
import javax.ws.rs.*;
import javax.ws.rs.ext.Provider;
@Provider
@Path("/address")
public class AddressResource {
@GET
@Path("{id}")
public Address get(@PathParam("id") int id) {
Address address = new Address();
address.setId(id);
return address;
}
}
CustomerResource
Since we have a stateful XmlAdapter
we can't just leverage JAXB through the default binding. Instead we can access JAXB through a StreamingOutput
.
import javax.ws.rs.*;
import javax.ws.rs.core.*;
import javax.ws.rs.ext.Provider;
import javax.xml.bind.*;
@Provider
@Path("/customer")
public class CustomerResource {
private JAXBContext jaxbContext;
public CustomerResource() {
try {
jaxbContext = JAXBContext.newInstance(Customer.class);
} catch (JAXBException e) {
// TODO - Handle Exception
}
}
@GET
@Path("{id}")
@Produces(MediaType.APPLICATION_XML)
public StreamingOutput get(@Context UriInfo uriInfo, @PathParam("id") int id) {
Customer customer = new Customer();
customer.setId(id);
customer.setName("Jane Doe");
Address address = new Address();
address.setId(123);
customer.setAddress(address);
return new MyStreamingOutput(jaxbContext, customer, uriInfo);
}
}
StreamingOutput
import java.io.*;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.*;
import javax.xml.bind.*;
public class MyStreamingOutput implements StreamingOutput {
private JAXBContext jaxbContext;
private Object object;
private UriInfo uriInfo;
public MyStreamingOutput(JAXBContext jc, Object object, UriInfo uriInfo) {
this.jaxbContext = jc;
this.object = object;
this.uriInfo = uriInfo;
}
@Override
public void write(OutputStream os) throws IOException,
WebApplicationException {
try {
Marshaller marshaller = jaxbContext.createMarshaller();
marshaller.setAdapter(new AddressAdapter(uriInfo));
marshaller.marshal(object, os);
} catch(JAXBException jaxbException) {
throw new WebApplicationException(jaxbException);
}
}
}