dagblog.
← Archive

Sun Multi-Schema XML Validator and schema compilation


The project I’m working on needs an XML schema validator
 to validate XML data coming into the application server. No problem, I thought, 
 and got to work. I had no idea I’d spend the next 
 day and a half looking for a silly mistake.

My journey began with the Sun Multi-Schema XML Validator,
 which implements the generic JARV API 
 for validators. That API lets you work with a validator programmatically. See
 the JARV User’s Guide for details.

JARV gave me a good way to tuck the validator into a component 
 for the application layer to use. The few schemas we’ll be using are stored
 inside the application server, so I chose to compile them in the validation component’s 
 init method.

Compiling a schema converts it into an object representation. The guide
 has this example. Schema schema = factory.compileSchema("http://www.example.org/test.xsd");

In the application server, the schema is a web application resource, an .xsd file. 
 I take a quick look at the VerifierFactory API
 and the compileSchema method, then choose compileSchema(java.io.InputStream stream).

That gives me the following code, showing just the essentials.

factory = VerifierFactory.newInstance("http://www.w3.org/2001/XMLSchema");              
.
.
InputStream is = context.getResource("foo.xsd").getInputStream();
Schema schema = factory.compileSchema(is);

I put together a test object to exercise the validation component. 
 I run the test and, lo and behold, the initialization method with that code
 throws an exception. A glance at the stack trace reveals a line I’ll have 
 engraved in my memory for a day and a half.

com.sun.msv.verifier.jarv.FactoryImpl$WrapperException: java.lang.NullPointerException (full stack trace)

After a day of debugging, searching Google for even a hint
 and trying everything possible and impossible, I conclude that it just won’t work
 and wonder where the comrades in East Germany went wrong. I go through 
 the examples once more and think, damn it, there must be a way. I follow several 
 examples and pass a File directly instead of an InputStream, compileSchema(java.io.File f) and
 WOW, it works!

Impossible. There can’t be a bug in compileSchema(java.io.InputStream stream)
 , I tell myself. I look once more at the VerifierFactory API and see something I’d missed.
 There are two methods that take an InputStream, compileSchema(java.io.InputStream stream) and 
 compileSchema(java.io.InputStream stream, java.lang.String systemId). 
 The second takes a systemId, the schema’s system identifier, for example foo.xsd. I search my memory for where it failed… ic.systemId.equals(url) in com.sun.msv.reader.GrammarReader, in the method 
 switchSource( InputSource source, State newState ).

Now I get it. ic is an InclusionContext, a private class in com.sun.msv.reader.GrammarReader. I already found that out 
 while reading GrammarReader. It clicks. The systemId on ic was null, which I knew, and the reason was
 simple.

I’d found that compileSchema creates the InclusionContext. Because some schemas have
 external entities, such as imported types, corresponding InclusionContexts get created for those entities too. The poor validator then tries
 to identify the InclusionContexts by systemId using ic.systemId.equals(url). Because I used
 compileSchema(java.io.InputStream stream) without a systemId on a schema that referenced an external entity, it ended in that
 null pointer exception. Had I got lucky and supplied a file without external entities, it would have worked fine because
 only one InclusionContext would have been created.

A small code change made everything work as expected.

factory = VerifierFactory.newInstance("http://www.w3.org/2001/XMLSchema");              
.
.
InputStream is = context.getResource("foo.xsd").getInputStream();
Schema schema = factory.compileSchema(is,"foo.xsd");

There are several lessons and mistakes here. First, the Javadoc for the JARV API
 is inadequate. It doesn’t mention any of this. All the compileSchema methods
 have the same documentation comment, so you can’t tell which one to use
 in a particular case.

This confirmed for me again that people who say source code availability is useless are wrong. Without
 the source, I’d have been lost here, or at best stuck for several more 
 hours.

Medium version

Related articles

Related CZ Podcast episodes