Enabling SOAP request validation in JBOSS 5.0 using JAX-WS

By default, JBoss JAX-WS Web services doesn’t validate SOAP requests for a valid xml or schema. This is by default in order to prevent performance overhead.
This may turn into a problem when developing the web service, since a required attribute in the schema may not be present in the request and the request will arrive to our service with no further notice. In many cases, this behavior is not a problem, you just need to consider in your web service that a null value may come for a required attribute.

So, Why validate?

  • Enforce schema
  • Sometimes it is good to enforce the schema validation when the request must comply with the schema, for example, when an attribute is required, or when the request must contain a list with exactly 2 elements but no more. Basically, when the request needs to comply with the xsd of the element. Validation in these cases is an aid for development, since the developer wouldn’t need to verify if the request has the required attributes.

  • Prevent default behavior
  • There are cases when the client can mispell a tag of the request, for example if the request has an attribute to filter the modification of a DB using a <filter> tag. If the client mispells the request and sends a <filters> tag instead of <filter> when no validation is enabled, the server will think that the client doesn’t need any filter (JBoss will set the filter variable to null), thus altering the behavior of the processing, updating all the rows in the DB without filtering, probably leading to disaster.

Validation in JBoss

In JBoss there is a simple way to enforce validation for the Web Service SOAP requests using the @SchemaValidation annotation. At the beginning, the use of the annotation seemed to be trivial, but after some unsuccessful attempts I found out that an extra effort was needed to get it going. This tutorial explains the simple steps needed to enable schema validation for JBoss and JAX-WS.

We start with a simple web service IMyWebService and the implementing class MyWebService, which has only one WebMethod getResponse, which receives one parameter param1. You can download the full project from here. It’s an eclipse Java project.
The parameter param1 is of class Param1 which has 2 fields:

private String param1Str;
private Integer param1Int;

After deploying the test project, using Soap UI to call the web service, we first import the wsdl from: http://localhost:8080/WSTest/WebService?wsdl
Calling the web service with the following message:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:type="http://test/com/wsTest/ws/type">
	<soapenv:Header/>
	<soapenv:Body>
		<type:param1>
			<param1Int>10</param1Int>
			<param1Str>TestString</param1Str>
		</type:param1>
	</soapenv:Body>
</soapenv:Envelope>

We can see the following log in the server console (we are just printing out the input values):

19:17:24,824 INFO  [STDOUT] 10
19:17:24,824 INFO  [STDOUT] TestString

So far so good, but until now we didn’t add any requirements on the fields. Let’s make the param1Str attribute required by adding the following annotation in the Param1 class:

@XmlElement(required=true)
public String getParam1Str() {
	return param1Str;
}

After redeploying WSTest and running the test case again we’d expect the server to reject the message due to validation errors, but this isn’t the case. As discussed earlier, JBoss disables SOAP request validation by default.
In order to enable validation, we need to do the following steps:

  1. Create a schema file with the declared elements from the wsdl
  2. Enable @SchemaValidation pointing to the schema file

Let’s discuss these points

Create a schema file with the declared elements from the wsdl

First, we need to create a schema file containing the elements we are going to use in the web service request. This may seem a time consuming task, but following these steps it should become really easy.
Create an xsd file, for example: schema.xsd using the following skeleton

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<xs:schema version="1.0"
	targetNamespace="<same_as_target_namespace_in_wsdl>"
	xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:ns1="<same_as_target_namespace_in_wsdl>">
	<!-- elements -->
	<xs:element name="param1" nillable="true" type="ns1:param1"/>
	...
	<!-- types -->
	<xs:complexType name="param1">
		...
	</xs:complexType>
	...
</xs:schema>

1. Replace the values of with the namespace used for the definitions of the wsdl. To find this, you will need to point your browser to the published wsdl ( http://localhost:8080/WSTest/WebService?wsdl ), navigate through the imported wsdl until you find the type definitions. Use the targetNamespace of the elements defined. In this case: http://test/com/wsTest/ws/type

2. Copy the <element> tags into the schema file.
3. Copy the <complexType> tags into the schema file.
After this, the schema.xsd should look like this (containing the elements of param1 and result1, complexType for param1 and result1):

<xs:schema elementFormDefault="qualified" version="1.0"
	targetNamespace="http://test/com/wsTest/ws/type" xmlns:xs="http://www.w3.org/2001/XMLSchema"
	xmlns:ns1="http://test/com/wsTest/ws/type">
	<!-- elements -->
	<xs:element name="param1" nillable="true" type="ns1:param1" />
	<xs:element name="result1" nillable="true" type="ns1:result1" />
	<!-- types -->
	<xs:complexType name="param1">
		<xs:sequence>
			<xs:element minOccurs="0" name="param1Int" type="xs:int" />
			<xs:element name="param1Str" type="xs:string" />
		</xs:sequence>
	</xs:complexType>
	<xs:complexType name="result1">
		<xs:sequence />
	</xs:complexType>
</xs:schema>

After creating the file, we need to put it in an accessible place for the application, for example META-INF/xsd/schema.xsd

Enable @SchemaValidation

We need to tell JBoss that it needs to validate the incoming requests for the web service we are publishing. In order to do this, we’ll simply add the following annotation in the implementing class of the web service, pointing to the schema file in the schemaLocation attribute.

@WebService(endpointInterface = "com.wsTest.ws.IMyWebService", serviceName = "MyWebService", name = "MyWebService", portName = "MyWebServicePort")
@SchemaValidation(enabled = true ,schemaLocation="META-INF/xsd/schema.xsd")
@SOAPBinding(parameterStyle = SOAPBinding.ParameterStyle.BARE)
public class MyWebService implements IMyWebService {

After deploying and running the application, JBoss will start logging messages saying that it is validating the request/responses

16:41:33,608 INFO  [SOAPBodyElementDoc] Validating: XML_VALID
16:41:33,623 INFO  [STDOUT] 10
16:41:33,623 INFO  [STDOUT] TestString
16:41:33,623 INFO  [SOAPBodyElementDoc] Validating: XML_VALID

Removing the param1Str from the Soap UI request:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:type="http://test/com/wsTest/ws/type">
	<soapenv:Header/>
	<soapenv:Body>
		<type:param1>
			<param1Int>10</param1Int>
		</type:param1>
	</soapenv:Body>
</soapenv:Envelope>

Issuing this request will lead to a validation error in JBoss:

16:48:11,052 INFO  [SOAPBodyElementDoc] Validating: XML_VALID
16:48:11,052 ERROR [StrictlyValidErrorHandler] org.xml.sax.SAXParseException: cvc-complex-type.2.4.a: Invalid content was found starting with element 'param1Int'. One of '{"http://test/com/wsTest/ws/type":param1Int, "http://test/com/wsTest/ws/type":param1Str}' is expected.
16:48:11,068 ERROR [SOAPFaultHelperJAXWS] SOAP request exception
org.jboss.ws.WSException: org.xml.sax.SAXException: cvc-complex-type.2.4.a: Invalid content was found starting with element 'param1Int'. One of '{"http://test/com/wsTest/ws/type":param1Int, "http://test/com/wsTest/ws/type":param1Str}' is expected.
	at org.jboss.ws.WSException.rethrow(WSException.java:68)
	at org.jboss.ws.core.soap.SOAPBodyElementDoc.validatePayload(SOAPBodyElementDoc.java:130)
	at org.jboss.ws.core.soap.SOAPBodyElementDoc.transitionTo(SOAPBodyElementDoc.java:82)
	at org.jboss.ws.core.soap.SOAPContentElement.getObjectValue(SOAPContentElement.java:172)

And the corresponding error response in Soap UI:

e xmlns:env="http://schemas.xmlsoap.org/soap/envelope/">
   <env:Header/>
   <env:Body>
      <env:Fault>
         <faultcode>env:Server</faultcode>
         <faultstring>org.xml.sax.SAXException: cvc-complex-type.2.4.a: Invalid content was found starting with element 'param1Int'. One of '{"http://test/com/wsTest/ws/type":param1Int, "http://test/com/wsTest/ws/type":param1Str}' is expected.</faultstring>
      </env:Fault>
   </env:Body>
</env:Envelope>

From this point, validating more complex requests shouldn’t be a difficult task. You can download the resulting source code from here. Open it as an Eclipse project or use the source code from your favorite IDE.

I hope this post makes your life easier enabling soap request validation for JBoss. Please let me know if this is your case or if you have any comments or difficulties.

JBOSS EAP 5.0 – JAX-WS – Excessive logging when soap faults are thrown

Developing web services with JAX-WS is very quick and versatile, becoming very easy to create a simple web service and quickly adding complexity like more methods, complex datatypes, validation, custom application faults, etc. In this example I am using JBOSS EAP 5.0.
This said, sometimes you stumble onto an awkward moment when you need to start digging to fix some issue. In this case this was not really an issue, but a bugging log that started filling our log files.
In our web services we throw specific application faults which are defined automatically in the generated wsdls. The problem is that every time an application fault is thrown, the server logs an error, even if the fault is expected to be thrown to the client. Also, as the client (a JAX-WS client) receives the fault, the JAX-WS framework logs an error, even if the client catches the application fault and manages it. This logs may mask any other important issue or some other important information reported by the log file.
These are the logs that can be seen in the server / client:

	SERVER
14:10:15,758 ERROR [SOAPFaultHelperJAXWS] SOAP request exception
com.myapp.ws.MyAppFault
	at com.myapp.ws.MyWebService(MyWebService.java:59)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
	at java.lang.reflect.Method.invoke(Method.java:597)
	at org.jboss.wsf.container.jboss50.invocation.InvocationHandlerJSE.invoke(InvocationHandlerJSE.java:108)
	at org.jboss.ws.core.server.ServiceEndpointInvoker.invoke(ServiceEndpointInvoker.java:221)
	at org.jboss.wsf.stack.jbws.RequestHandlerImpl.processRequest(RequestHandlerImpl.java:468)
	at org.jboss.wsf.stack.jbws.RequestHandlerImpl.handleRequest(RequestHandlerImpl.java:293)
	at org.jboss.wsf.stack.jbws.RequestHandlerImpl.doPost(RequestHandlerImpl.java:203)
	at org.jboss.wsf.stack.jbws.RequestHandlerImpl.handleHttpRequest(RequestHandlerImpl.java:129)
	at org.jboss.wsf.common.servlet.AbstractEndpointServlet.service(AbstractEndpointServlet.java:85)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
	at org.jboss.web.tomcat.filters.ReplyHeaderFilter.doFilter(ReplyHeaderFilter.java:96)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:235)
	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191)
	at org.jboss.web.tomcat.security.SecurityAssociationValve.invoke(SecurityAssociationValve.java:190)
	at org.jboss.web.tomcat.security.JaccContextValve.invoke(JaccContextValve.java:92)
	at org.jboss.web.tomcat.security.SecurityContextEstablishmentValve.process(SecurityContextEstablishmentValve.java:126)
	at org.jboss.web.tomcat.security.SecurityContextEstablishmentValve.invoke(SecurityContextEstablishmentValve.java:70)
	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:127)
	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
	at org.jboss.web.tomcat.service.jca.CachedConnectionValve.invoke(CachedConnectionValve.java:158)
	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:330)
	at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:829)
	at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:598)
	at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:447)
	at java.lang.Thread.run(Thread.java:619)
	CLIENT
14:10:15,789 ERROR [CommonClient] Exception caught while (preparing for) performing the invocation: 
javax.xml.ws.soap.SOAPFaultException: com.myapp.ws.MyAppFault
	at org.jboss.ws.core.jaxws.SOAPFaultHelperJAXWS.getSOAPFaultException(SOAPFaultHelperJAXWS.java:84)
	at org.jboss.ws.core.jaxws.binding.SOAP11BindingJAXWS.throwFaultException(SOAP11BindingJAXWS.java:107)
	at org.jboss.ws.core.CommonSOAPBinding.unbindResponseMessage(CommonSOAPBinding.java:558)
	at org.jboss.ws.core.CommonClient.invoke(CommonClient.java:373)
	at org.jboss.ws.core.jaxws.client.ClientImpl.invoke(ClientImpl.java:231)
	at org.jboss.ws.core.jaxws.client.ClientProxy.invoke(ClientProxy.java:162)
	at org.jboss.ws.core.jaxws.client.ClientProxy.invoke(ClientProxy.java:148)
	at $Proxy209.getContrato(Unknown Source)
	...

According to https://issues.jboss.org/browse/JBWS-3299, the log created by the server was reported as a bug, and was fixed in the next release of JBOSS.
For the second, I couldn’t find any reported issue, but I assumed it is a similar issue as the reported for SOAPFaultHelperJAXWS.

After all this analysis, the workaround that did the job for me was a quick patch to the log4j configuration of the JBOSS server:

  1. Open the file /jboss-as/server//conf/jboss-log4j.xml
  2. Add the following lines inside the tag. For example: before opening the tag.
  3. 	<category name="org.jboss.ws.core.jaxws.SOAPFaultHelperJAXWS" additivity="false">
    	</category>   
    	<category name="org.jboss.ws.core.CommonClient" additivity="false">
    	</category>   
    

    This code will remove the appenders for the categories generating the unwanted logs.

  4. Restart the server

That’s it! Now the logs will look much cleaner, and these logs won’t mask any other log that may be important.