0

Is there a way of retrieving the current base URI in an XmlAdapter? Or how is this usually archieved?

public class Service{
...
    @GET
    public MyEntity getEntity() {
        return em.find(MyEntity.class, "dummy");
    }
...
}


@XmlRootElement(name = "myEntity")
public class MyEntity {

    @XmlJavaTypeAdapter(MyAdapter.class)
    private Entity2 entity2Ref;
...

}



public class MyAdapter extends XmlAdapter<Entity2Ref, Entity2> {

// Is NULL but shold be injected with host URI
@Context
UriInfo uri;

...

}
bdoughan
  • 147,609
  • 23
  • 300
  • 400
Johannes Staehlin
  • 3,680
  • 7
  • 36
  • 50

1 Answers1

2

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);
        }
    }

}
bdoughan
  • 147,609
  • 23
  • 300
  • 400