I'm currently developing web services in an Oracle WebLogic environment. To enable schema validation on the web service, one only has to use the @SchemaValidation annotation in the web service class. Like this:
import com.sun.xml.ws.developer.SchemaValidation;
@WebService(...)
@SchemaValidation
public class WebService implements WebServiceInterface {
...
}
Unfortunately, that doesn't work for me. I get this error when I try to deploy my application:
Caused by: javax.xml.ws.WebServiceException:
Annotation@com.sun.xml.internal.ws.developer.SchemaValidation
(handler=class com.sun.xml.internal.ws.server.DraconianValidationErrorHandler)
is not recognizable,
atleast one constructor of class com.sun.xml.internal.ws.developer.SchemaValidationFeature
should be marked with @FeatureConstructor
I also tried creating my own validator to use in the annotation (@SchemaValidation(handler = ExampleValidationHandler.class)):
import org.xml.sax.SAXParseException;
import com.sun.xml.internal.ws.developer.ValidationErrorHandler;
public class ExampleValidationHandler extends ValidationErrorHandler {
public void warning(SAXParseException exception) {
;
}
public void error(SAXParseException exception) {
;
}
public void fatalError(SAXParseException exception) {
;
}
}
Then I enabled schema validation explicitly in the Oracle WebLogic web services deployment descriptor:
weblogic-webservices.xml.
<?xml version="1.0" encoding="UTF-8"?>
<weblogic-webservices
xmlns="http://xmlns.oracle.com/weblogic/weblogic-webservices"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.oracle.com/weblogic/weblogic-webservices http://xmlns.oracle.com/weblogic/weblogic-webservices/1.1/weblogic-webservices.xsd">
<webservice-description>
<webservice-description-name>ExampleWSService</webservice-description-name>
<webservice-type>JAXWS</webservice-type>
<port-component>
<port-component-name>ExampleWSService</port-component-name>
<validate-request>true</validate-request>
<reliability-config>
<inactivity-timeout>P0DT600S</inactivity-timeout>
<base-retransmission-interval>P0DT3S</base-retransmission-interval>
<retransmission-exponential-backoff>true</retransmission-exponential-backoff>
<acknowledgement-interval>P0DT3S</acknowledgement-interval>
<sequence-expiration>P1D</sequence-expiration>
<buffer-retry-count>3</buffer-retry-count>
<buffer-retry-delay>P0DT5S</buffer-retry-delay>
</reliability-config>
</port-component>
</webservice-description>
</weblogic-webservices>
Unfortunately, that didn't work either. Google shows me three other developers with the same problem, but there is no working solution for me, and for them. So, another approach is needed to enable schema validation on incoming messages.
The solution presented in this blog post makes use of web service handlers. A web service handler is like an intercepting filter, which enables you to intercept incoming and outgoing messages, before they are passed on to the (external) system. The steps needed for this solution:
1. Create web service handler class
2. Define handler chain in XML-descriptor
3. Use annotation in web service class
1. Create web service handler classThere are two types of web service handlers:
- LogicalHandler
- SOAPHandler
The LogicalHandler provides access to the logical message, without protocol details like: SOAP envelopes. The SOAPHandler provides access to SOAP protocol details. For the purpose of message validation, the LogicalHandler is a better choice.
package com.javaeenotes;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.StringWriter;
import java.net.MalformedURLException;
import java.net.URL;
import javax.xml.XMLConstants;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.TransformerFactoryConfigurationError;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import javax.xml.ws.LogicalMessage;
import javax.xml.ws.handler.LogicalHandler;
import javax.xml.ws.handler.LogicalMessageContext;
import javax.xml.ws.handler.MessageContext;
import org.w3c.dom.Document;
import org.xml.sax.SAXException;
public class ValidationHandler implements LogicalHandler<LogicalMessageContext> {
private String schemaUrl = "http://127.0.0.1:8080/webservice/schema.xsd";
@Override
public boolean handleMessage(LogicalMessageContext context) {
Boolean isOutBound = (Boolean) context
.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
if (isOutBound) {
return true;
}
LogicalMessage lm = context.getMessage();
Source payload = lm.getPayload();
StreamResult res = new StreamResult(new StringWriter());
String message = "";
try {
Transformer trans;
trans = TransformerFactory.newInstance().newTransformer();
trans.transform(payload, res);
message = res.getWriter().toString();
// Validate
validate(message, schemaUrl);
} catch (TransformerConfigurationException e) {
// When Source payload Transformer object could not be obtained.
throw new WebServiceException(e);
} catch (TransformerFactoryConfigurationError e) {
// When Source payload Transformer object could not be obtained.
throw new WebServiceException(e);
} catch (TransformerException e) {
// When Source payload could not be transformed to String.
throw new WebServiceException(e);
} catch (MalformedURLException e) {
// When URL to schema is invalid.
throw new WebServiceException(e);
} catch (ParserConfigurationException e) {
// When parser needed for validation could not be obtained.
throw new WebServiceException(e);
} catch (IOException e) {
// When something is wrong with IO.
throw new WebServiceException(e);
} catch (SAXException e) {
// When XSD-schema validation fails.
throw new WebServiceException(e);
}
return true;
}
private void validate(String xml, String schemaUrl)
throws ParserConfigurationException, IOException,
MalformedURLException, SAXException {
DocumentBuilder parser = null;
Document document = null;
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
parser = dbf.newDocumentBuilder();
byte bytes[] = xml.getBytes();
document = parser.parse(new ByteArrayInputStream(bytes));
SchemaFactory factory = SchemaFactory
.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
Schema schema = null;
schema = factory.newSchema(new URL(schemaUrl));
javax.xml.validation.Validator validator = schema.newValidator();
validator.validate(new DOMSource(document));
}
@Override
public boolean handleFault(LogicalMessageContext context) {
return true;
}
@Override
public void close(MessageContext context) {
;
}
}
It is important to note that the payload must be defined in an XSD-schema. A definition in a WSDL file cannot be used. Use the boolean property:
MessageContext.MESSAGE_OUTBOUND_PROPERTY to differentiate between out-bound or in-bound messages. Throw a
WebServiceException when something goes wrong.
2. Define handler chain in XML-descriptorNow, create a handler chain XML-descriptor file, which refers to the handler created in the previous step. In this example, I named the file
handlers.xml and placed it in the same directory as the handler. The location of the file is important in the last step.
<?xml version="1.0" encoding="UTF-8"?>
<handler-chains xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/javaee_web_services_metadata_handler_2_0.xsd">
<handler-chain>
<handler>
<handler-name>ValidationHandler</handler-name>
<handler-class>com.javaeenotes.ValidationHandler</handler-class>
</handler>
</handler-chain>
</handler-chains>
3. Use annotation in web service classThe final step is to use the @HandlerChain annotation in the web service class. The file parameter must be a relative path to the XML-descriptor file from the annotated class file or an absolute URL.
package com.javaeenotes;
import javax.jws.HandlerChain;
import javax.jws.WebService;
import com.javaeenotes.ws.ExampleWS;
//import com.sun.xml.ws.developer.SchemaValidation;
@WebService(name = "ERSService",
targetNamespace = "http://javaeenotes.com/",
serviceName = "ExampleWSService",
portName = "ExampleWSPort",
endpointInterface = "com.javaeenotes.ws.ExampleWS",
wsdlLocation = "WEB-INF/wsdl/ExampleWSService.wsdl")
//@SchemaValidation
@HandlerChain(file = "handlers.xml")
public class WsImplementation implements ExampleWS {
@Override
public int sum(int arg0, int arg1) {
return 0;
}
@Override
public String greet(String arg0) {
return null;
}
@Override
public int multiply(int arg0, int arg1) {
return 0;
}
}