Enterprise Java

Wildfly, Apache CXF and @SchemaValidation

Over the last few days, I have been working on an application migration from JBoss 4 to Wildfly 8. The application is using different technologies, but we are going to focus here on XML Web Services, JAX-WS. Yeah, I know that they are not trendy anymore, but these were developed a long time ago and need to be maintained for compatibility issues.

Anyway, the path to migrate these services was not so easy. I’m sharing some of the problems and fixes with the hope that these could help other developers out there stuck with the same problems.
 
 

Sample Definition

Here is a sample of a Web Service definition in the old system, JBoss 4:

@javax.jws.WebService(endpointInterface = "some.pack.age.WebService")
@javax.jws.soap.SOAPBinding(style = SOAPBinding.Style.DOCUMENT)
@org.jboss.ws.annotation.EndpointConfig(configName = "Standard WSSecurity Endpoint")
@javax.jws.HandlerChain(file = "handlers.xml")
@org.jboss.ws.annotation.SchemaValidation(enabled = true, errorHandler = CustomErrorHandler.class)
public class WebServiceImpl implements WebService {

Luckily, most of the definition is using standard Java EE annotations. Only @org.jboss.ws.annotation.EndpointConfig and @org.jboss.ws.annotation.SchemaValidation are from the old JBossWS libraries.

We can easily get rid of @org.jboss.ws.annotation.EndpointConfig since we are not going to need it in the new application. For reference, it’s used to set up extra configuration data to be predefined with an endpoint. Check the documentation Predefined client and endpoint configurations.

We want to keep @org.jboss.ws.annotation.SchemaValidation. For reference, this annotation validates incoming and outgoing SOAP messages against the relevant schema in the endpoint wsdl contract. Since the annotation no longer exists in JBossWS we have to use Apache CXF, which is the underlying implementation for JAX-WS on Wildfly.

Problems

Here are a few of the problems I’ve faced:

SchemaValidation Annotation

The annotation @org.jboss.ws.annotation.SchemaValidation doesn’t exist anymore. You have to use the annotation org.apache.cxf.annotations.SchemaValidation from Apache CXF.

Add the following Maven dependency to use the Apache CXF annotation:

<dependency>
    <groupId>org.apache.cxf</groupId>
    <artifactId>cxf-api</artifactId>
    <version>2.7.11</version>
    <scope>provided</scope>
</dependency>

Also, notice that in the original annotation we could define an errorHandler property. The old application used a custom error handler to set a custom error message on schema validation errors. There is no equivalent in the new annotation, so we need to do it in another way. To replicate the old behaviour I’ve used Apache CXF Interceptors. Create an interceptor class and extend AbstractPhaseInterceptor. Here is a sample:

public class SchemaValidationErrorInterceptor 
        extends AbstractPhaseInterceptor<Message> {
    public SchemaValidationErrorInterceptor() {
        super(Phase.MARSHAL);
    }

    @Override
    public void handleMessage(Message message) throws Fault {
        Fault fault = (Fault) message.getContent(Exception.class);
        Throwable cause = fault.getCause();
        while (cause != null) {
            if (cause instanceof SAXParseException) {
                fault.setMessage("Invalid XML: " + fault.getLocalizedMessage());
                break;
            }

            cause = cause.getCause();
        }
    }
}

And you can use it like this:

@org.apache.cxf.interceptor.OutFaultInterceptors(
    classes = SchemaValidationErrorInterceptor.class
)

Interceptors are used by both CXF clients and CXF servers. There are incoming and outgoing interceptor chains being executed for regular processing and also when an error occurs. In this case, we want to override the Schema Validation message, so we need to bind our interceptor in the error outgoing interceptor chain. You can use the annotation @OutFaultInterceptors for that behaviour. Each chain is split into phases. You define the phase where you want the interceptor to run by passing the Phase.MARSHAL in the constructor. There are other phases, but since we want to change the error message we do it in the MARSHAL phase.

Different WSDL

The old Web Services had the WSDL file being auto generated on deploy time. Unfortunately, in some situations, the WSDL generated by JBoss 4 and Wildfly 8 are different. This can cause problems with your external callers. In this case the main problem was in the Schema Validation. Requests that were valid in JBoss 4 were not valid anymore when being executed in Wildfly 8.

The reason for this behaviour was in the target namespaces. If you are using annotated @XmlRootElement pojos in your Web Service parameters, without defining the namespace property in the annotation, JBoss 4 WS generated the target WSDL element with a black namespace. Apache CXF will use the Web Service default namespace to bind the WSDL elements if they are blank. For reference, this is done in CXF code: org.apache.cxf.jaxws.support.JaxWsServiceConfiguration#getParameterName.

This could be fixed by changing the CXF code, but we opted to place the old generated WSDL file in the migrated application sources and include it in the distribution. It’s not auto generated anymore, meaning that we need to manually generate the WSDL if we change the API. We need to be careful to make sure that we are not breaking anything in the WSDL. This approach seemed better than having to maintain our own CXF version. We could probably submit a fix for this as well, but we believe that JBoss 4 behaviour was not intended.

Start CXF

To use specific API’s from CXF, is not enough to have a project dependency for it. In fact, the first few times I’ve tried the changes, nothing related with CXF seemed to work. This happens because Wildfly it’s only looking for the standard Java EE JAX-WS annotations. To have all the CXF behaviour working, we need to tell Wildfly that our application depends on CXF, even if the libs are already on the server. Yeah, it’s a bit confusing.

The application is deployed in a EAR file. So you need to create a jboss-deployment-structure.xml and add the following content:

<jboss-deployment-structure>

    <sub-deployment name="application.war">
        <dependencies>
            <module name="org.apache.cxf"/>
        </dependencies>
    </sub-deployment>

</jboss-deployment-structure>

Using a MANIFEST.MF in the WAR file apparently doesn’t work if it’s deployed inside an EAR file. For more information, please check Class Loading in WildFly.

If you want to use other CXF features, especially the ones linked with Spring, thing might be a bit trickier. Have a look into this post: Assorted facts about JBoss. Fact 6: JBoss and CXF: match made in heaven.

Final Definition

This should be our final definition for our Web Service:

@WebService(
        wsdlLocation = "WebService.wsdl",
        endpointInterface = "some.pack.age.WebService"
)
@SOAPBinding(style = SOAPBinding.Style.DOCUMENT)
@HandlerChain(file = "/handlers.xml")
@SchemaValidation(type = SchemaValidation.SchemaValidationType.IN)
@OutFaultInterceptors(classes = SchemaValidationErrorInterceptor.class)
public class WebServiceImpl implements BDNSWebService {

As you can see, the required changes to migrate a Web Service from JBoss 4 to Wildfly are just a few. However, there are a few minor details that can block you for a long time if you don’t know the details. Maybe you have a different setup and the problems that you face are different. This can also help if you are just trying to setup CXF with Wildfly Anyway, I hope that this post can be useful to you.

Roberto Cortez

My name is Roberto Cortez and I was born in Venezuela, but I have spent most of my life in Coimbra – Portugal, where I currently live. I am a professional Java Developer working in the software development industry, with more than 8 years of experience in business areas like Finance, Insurance and Government. I work with many Java based technologies like JavaEE, Spring, Hibernate, GWT, JBoss AS and Maven just to name a few, always relying on my favorite IDE: IntelliJ IDEA.Most recently, I became a Freelancer / Independent Contractor. My new position is making me travel around the world (an old dream) to customers, but also to attend Java conferences. The direct contact with the Java community made me want to become an active member in the community itself. For that reason, I have created the Coimbra Java User Group, started to contribute to Open Source on Github and launched my own blog (www.radcortez.com), so I can share some of the knowledge that I gained over the years.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments
Back to top button