JAX-WS & JAX-RS with EclipseLink MOXy and external

2019-08-03 16:17发布

问题:

This is about using JAX-WS with EclipseLink MOXy and my problem of not being able to use MOXy's external mapping documents in this combination. (The same actually seems to apply to JAX-RS as well, but I'm limiting the details to JAX-WS to keep this from becoming even longer than it already is).

I first describe (in some detail) the context of my requirements and the test code I've written to try and solve them. The actual question follows at the end of the post.

The context:

In some legacy parts of our project, we use JAX-WS directly on top of a simple server-side POJO model. The model is part of an API that we use for direct Java calls, but we also provide a SOAP layer implementing the same Java interface as our direct implementation, i.e. we have an IManager interface using our model, and the caller doesn't care whether his IManager instance is the local implementation or a SOAP client that calls the JAX-WS server wrapper for the local implementation.

Back when we started implementing the model, we didn't know much about JAXB, so the model has no JAXB annotations and everything is auto-deduced by JAX-WS. From the JAX-WS annotated server classes, we create a client using wsgen and wsimport. But since the model is part of our API, and the model generated by wsimport is separate set of classes (albeit with identical signatures), we have to wrap all client calls with methods that copy between these two models. Copying from one model to the other is implemented manually and has to be updated manually for every tiny change in the API model.

Since both server and client are under our control, we'd like to use the same set of (hand-written) model classes in the server and the client implementation. I've spent the past few weeks playing around with a test project that works, but doesn't completely satisfy me yet.

I built my test project in three steps:

Step 1: Implement a simple test API consisting of a model and a manger interface. Then create a simple implementation.

In my case, the model consists mainly of and IPerson and an IBook interface, and the IModelManager has methods to get, store/update and delete persons and books. The interface also provides a factory for creating new persons and books, so client projects only need the API at compile time (and the implementation at runtime).

Step 2: Provide modules for serialization to and from XML and JSON.

Because of its support of external mapping documents, I played around with EclipseLink MOXy. This allowed me to take my model from step 1 and add XML binding declarations in my separate serialization project. I could also write my serialization utility classes in such a way that the client code calling them only knows about the API, i.e. all method signatures rely only on the model interfaces and not the implementation classes. All this without touching the classes from step 1 - all the serialization stuff was cleanly layered on top of the Java implementation project.

Step 3: Provide SOAP and REST layers via JAX-WS and JAX-RS.

Now I wrote server classes for SOAP and REST, using JAX-WS and JAX-RS annotations. From the JAX-WS classes I generated a client with wsgen and wsimport and wrapped that in a client-side IModelManager implementation which simply redirects the calls to the client class generated by wsimport. For the JAX-RS classes I used Jersey with annotations in my sever class and wrote a IModelManager implementation which uses the Jersey client classes to call the REST service.

This is where I lost some of the nice functionality from step 2.

Using the JAX-WS EclipseLink plugin, all the JAX-WS code uses MOXy for serializing and deserializing the model (or at least I think it does). But I couldn't find a way to specify my external binding files. I had to add annotations to my actual model implementation from step 1 (and even to the API, because the model also includes an enum referenced from the interfaces and the enum needs JAXB annotations as well). I also had to change my JAX-WS server class to use the model implementation classes instead of the interfaces (for now I simply use casts where necessary, assuming there will never be another model implementation).

Using the WSDL and schema generated from the updated model by wsgen, I can call wsimport with a custom JAXB bindings XML to generate a client that maps all the types from the generated XSD to my existing model classes instead of generating new ones. The result is a client interface that accepts and returns the model interfaces from my API and always uses the standard implementation. I just have to write simple wrappers and cast my model implementation classes to API interfaces in several places, which is a massive improvement over our legacy code with its duplicate (and triplicate) models and loads of copying logic.

The question:

I'm not happy with having to annotate all my model classes directly (and even the enums in the API), especially considering that I had a working serialization/deserialization pipeline without annotations in step 2. Presumably, if I could provide wsimport and the code that starts up the sever-side JAX-WS implementation with my MOXy mapping documents, I could do without any annotations just like in step 2. It even seems to me that if I could provide wsgen with the mapping files, I could use my API interfaces (instead of implementation classes) in the server-side JAX-WS methods and do without all the casts, and perhaps even without the JAXB bindings file that manually maps the wsgen created XSD types to my existing classes.

But I haven't been able to find a way to provide JAX-WS with the MOXy mappings, neither the wsgen and wsimport tools nor the client or server side code. Is there something I'm missing, or if not, is this indeed a gap in JAX-WS's MOXy bridge, and is there a chance to fill it to get the full MOXy functionality in a future version? Or did I make a fundamental error in my line of thinking and there either cannot be a solution or there already is one? (Hence my detailed description of my test project)

The target platform is plain Java 8, either as a standalone application (with Jetty, using javax.xml.ws.Endpoint to publish the SOAP service and Jersey for the REST service), or Tomcat with Jersey. My tests run directly in JUnit using Endpoints and JerseyTest, plus manual tests in Tomcat.