Interfaces and JAX-B

I’ve been working with JAX-B and JAX-RS to create a RESTful service. This service interacts with another RESTful service. In terms of design, the two services share an interface, however the implementation of this interface is very different between the two services.

As an experienced Java developer, naturally, I create a common API. In this case, it is essentially a set of interfaces that ship with both services. My plan was to use these interfaces as acceptable input to the two REST services. I would then annotate these interfaces using JAX-B annotatations e.g. XmlRootElement, XmlElement, etc. The goal is to enable the services to communicate with each other via a shared interfaces, each having its own unique implementation. Suffice to say, this doesn’t work with JAX-B.

The paradigm I describe above is one which many folks have attempted, unfortunately it doesn’t work. I will show why this does not work, and how to do it properly.

Let’s assume the following:
We have a Common API, RestServiceA, and RestServiceB

In the Common API, we have the following interface:

[code language=”Java”]
@XmlRootElement // note there are games we can play with XmlJavaTypeAdapter, but lets not go there.
public interface Common {
//…
}
[/code]

In RestServiceA, we have an implementation of Common

[code language=”Java”]
@XmlRootElement // JAX-B annotation
public class ACommon implements Common {
//…
}
[/code]

In RestServiceB, we have a different implementation of Common

[code language=”Java”]
@XmlRootElement // JAX-B annotation
public class BCommon implements Common {
//…
}
[/code]

Both RestServiceA and RestServiceB provide a RESTful service. They are stateless beans as defined by the JAX-RS @Stateless annotation.

[code language=”Java”]
@Stateless
@LocalBean
@Path("/serviceA")
public class RestServiceA {
// create method, ServiceB will have the same method,
// however, as we know, its implementation of Common is different than service A
@POST
@Consumes({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
public Response create(Common common) {
// …
}
}
[/code]
[code language=”Java”]
@Stateless
@LocalBean
@Path("/serviceB")
public class RestServiceB {
@POST
@Consumes({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
public Response create(Common common) {
// …
}
}
[/code]

Here is a sample client that invokes the service. This client invokes serviceA.

[code language=”Java”]
//…
Common common = new CommonA();
// common.setSomeState(…
Entity<Common> entity = Entity.entity(common, MediaType.APPLICATION_XML);
WebTarget target = client.target("http://localhost:8080/test/rest/serviceA");
Response response = target.request().post(entity);
//…
[/code]

Unforunately, the request made by the client above will never make it to RestServiceA.create handler, instead it will fail during the unmarshal of ‘common’.

After a bit more research into JAX-B, I found that what I am trying to do is currently not possible with JAX-B.
By annotating the interface, my hope was that JAX-B would unmarshal and bind any class that implements the Common interface to an object. JAXB, however cannot do this. The unmarshaller, ultimately, must create an instance of some object that implements Common. It cannot do this since it was only given an interface and XML to work with. This is one of the major reason that this is currently won’t work.
Let’s suppose for a moment, JAX-B could unmarshal the interface to an object. We quickly, find yet another issue. As we know, in Java, objects can implement many interfaces. What if my object implements multiple interfaces? In this case the unmarshaller is given the interfaces and the XML. How will it known which object to marshal the XML as? As you can see, this is not a trivial problem and likely a reason why interface support is not at the same level as class support in JAX-B.

So, how do we resolve the issue? We don’t. We must give in.
In my case, I now use classes in the common API. For example, Common now looks like this:

[code language=”Java”]
@XmlRootElement // JAX-B annotation
public class Common {
//…
}
[/code]

One last note…
JAX-B does have some support for interfaces, however their usage is far more restrictive than classes.

Thank you!

You may also like...