Tuesday, August 7, 2018

Bottom-up SOAP Services with Apache CXF and Custom XSD

SOAP is not very popular these days, primarily because of almost nonhuman readable WSDL and protocol that puts a lot of overhead into a simple message. It's tough to send a message using a browser or curl. Not mentioning the fact that we have to obtain WSDL somehow.

But on the other hand side, SOAP has some advantages over REST. It allows us to define a contract and specify the data it will process. Once you have such WSLD, you can throw it over the hedge, and you do not have to answer questions like how do I delete a created resource, what 403 means in this case, or is this call idempotent?
WSLD contains everything needed to define an RPC interface and document it. I would choose REST for most cases, but still, there are some good use cases for SOAP.

This one thing bothers me about SOAP: how do I get proper WSLD? Should I use a tool to generate it or write it? Honestly... I really do not want to dig into SOAP to just write WSLD, and I do not want to spend hours playing around with some tools generating WSLD, importing it into my project, and binding generated stuff to actual implementation only to find out about the end that it does not operate as expected.

The perfect solution would be to write Java code, annotate it, and generate WSDL out of it. Those annotations should provide enough flexibility to influence WSLD generation so that we archive something that can be delivered to the customers.

I've tried a few Java frameworks, and all provide a way to generate WSLD out of Java code, but they all need to include one fundamental feature: you cannot offer XSD for data types. So it's impossible to specify the content of an email field or provide a format for a date. We get only half of a possible functionality of a WSDL: method calls, exception handling, documentation, and security, but there is no way to specify the data format.
I would like to have WSLD that is as precise as possible so that the client can call the particular method and know precisely what is possible and is not allowed.

Let's start with the standard SOAP Service generated out of Java code. We will use Apache CXF and Spring Boot for it. The source code is here: https://github.com/maciejmiklas/apache-cxf-soap.

It's a simple application where users can submit registration as a SOAP Request on http://localhost:8080/soap/Registration:


This code outpost following WSDL:
As you can see, this WSDL has little info about data types (lines 9-22). The email is mandatory, but there is no way to provide any further assertions.
The problem lies in JAXB. It ignores annotations to influence generated XML schema. There is a pull request (jaxb-facets) that would solve this issue, but it's already a few years old and looks like it will only be integrated for a while.

The limitation is not caused by SOAP frameworks but by the fact that they are based on JAXB.

This does not change the fact that I will not write WSLD myself. There has to be a better way!

We will implement a simple extension for Apache CXF to combine generated WSLD with custom XSD. We have to write XSD in this case, but CXF will generate WSLD for us and include the given schema.
XSD is mighty, so I prefer writing it by hand because it's possible to specify the data format precisely. Using annotations to generate XSD would be convenient, but it would still cover only some common areas.
The implementation below has its limitations. You might run into some issues. It might stop working after the next CXF update. But! I use it, and it does what I need, so it might be something for you as well ;)

Now we are going to modify the first example. The idea is to write XSD that defines simple types, references those types in transfer classes, and generates WSLD, combining them all.
In the beginning, we have to write a schema that defines types for transfer objects: The original source code will be modified only in a few places:
  • We will replace the default Apache CXF data binding with a custom implementation. It reads Schama from the file and integrates it into generated WSLD,
  • Some fields in transfer objects are annotated with @XmlSchemaType - this annotation provides a connection between Java types and types defined in XSD. For example, the ExRegistration#email is annotated with @XmlSchemaType(name = "email"), and XSD contains the email type. The email generated in WSDL references the type from a provided schema,
  • the classes following the pattern *Registration* have been renamed to *ExRegistration*
Here is our final WSDL: