A good Web service has to have a well defined and enforceable contract. Typically, the
contract is expressed using XML Schema language. Unfortunately, up until recently,
enforcing the contact was very problematic because of the huge performance overhead
associated with running schema validation. XML parsers used to load and parse the schema
for every validation request, which was clearly expensive.
This has changed with the release JAXP 1.3 which comes with the schema
validation framework which allows to first load and compile a schema and then reuse
the compiled version for all subsequent validation requests. This may finally
make it feasible to use the schema validation for Web services in production
environment.
I decided to prototype the use of the validation framework for my Web services
implemented in XFire
(you may also want to explore a
schema validation component implemented in ServiceMix ESB).
All it took is a fairly simple handler:
public class XFireSchemaValidationHandler extends AbstractHandler {
private Schema schema = null;
public XFireSchemaValidationHandler() throws SAXException{
super();
setPhase(Phase.PARSE);
before(ReadHeadersHandler.class.getName());
// Load the schema - note that handler is only
// instantiated once, so we can keep the schema in
// an instance variable
SchemaFactory factory = SchemaFactory
.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
// I'm hardcoding the path for now, but we should be able
// to extract it from the WSDL
StreamSource ss = new StreamSource(
new File("/ws/xfire/etc/person.xsd"));
schema = factory.newSchema(ss);
}
public void invoke(MessageContext ctx) throws Exception {
InMessage message = ctx.getInMessage();
XMLStreamReader streamReader = message.getXMLStreamReader();
// create JDom from the stream - alternatively we can rely on
// DOM and XFire DOM handler
StaxBuilder builder = new StaxBuilder();
Document doc = builder.build(streamReader);
// get to the body first
Element body =
(Element)doc.getRootElement().getChildren().get(0);
// we assume that we use "unwrapped" mode and
// our payload is the direct child of the body element
Element payload = (Element) body.getChildren().get(0);
// dump the message for testing purposes
XMLOutputter outputter =
new XMLOutputter(Format.getPrettyFormat());
outputter.output(payload, System.out);
// create validation handler from the pre-complied schema
ValidatorHandler vh = schema.newValidatorHandler();
// the handler only works with SAX events, so we create
// SAX from JDom
SAXOutputter so = new SAXOutputter(vh);
// Validator will run as a SAX handler and throw an exception
// if the validation fails.
so.output(payload);
System.out.println("\nValidation passed");
// rewind the stream reader for subsequent processing
message.setXMLStreamReader( new JDOMStreamReader( doc) );
}
}
Unfortunately, the validation framework does not currently support StAX, so
an XML document has to be parsed using SAX or DOM (or JDom with subsequent SAX
conversion which is what I chose to do since it was quicker to develop
that way). I’m sure this conversion (and the validation process itself) adds
some performance overhead, but I’m hoping that it
won’t be too bad, especially for simple schemas (and from my experience 90% of
all Web services have very simple messages). JAX-RPC-compliant Web services engines
should see even less of performance penalty since everything is loaded into
DOM from the get-go. Perhaps I’ll try to run some performance tests and see what
happens.
Nice work! Any chance you’d like to contribute this back to XFire? It’d be great if you filed a JIRA for this.