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.
Related articles
Related CZ Podcast episodes
- #3 · Java 6 SE, Vývoj webových aplikací
Dagi and Filemon discuss Java 6 and web application frameworks. · Czech audio
- #1 · Vývojová prostředí v Javě
The first CZ Podcast episode looks at Java development environments. · Czech audio