views:

733

answers:

5

I would like to validate XML documents using RELAX NG schemata, and I would like to use the JAXP validation API.

From Googling around, it appeared that I could use Jing and the ISO RELAX JARV to JAXP Bridge. Unfortunately, after adding both to my classpath, I can't get it to work. SchemaFactory is just throwing an IllegalArgumentException as soon as it tries to instantiate a factory — I looked inside SchemaFactory, apparently SchemaFactoryFinder is returning a null result.

So I'd appreciate answers to either question:

  • How can I make this work with Jing and this bridge?
  • Is there a better/different set of libraries I should try?

I need this to work with Java 5 and Java 6.

Thanks!

A: 

... IllegalArgumentException as soon as it tries to instantiate a factory

Means the schema language isn't recognized, there could be a few causes.

  • Since the Sun JDK doesn't include a RELAX NG validator by default, it could be that it's not being found.
  • It could be that you've made an error in the schema language identifier.
Martin Spamer
+1  A: 

I can't help you with the JAXP validation API, but Nux provides a class that can validate virtually every type of schema known to man. Regarding RELAX NG schemata, use this factory method to create the relevant validator object.

Don
Useful, thank you!
Avi Flax
+1  A: 

Another option is Trang, which is a RelaxNG-to-XMLSchema translator. I believe it's designed to be used as a build tool rather than a runtime library, but your best option might be to convert your schema to XMLSchema using Trang at build time, and then validate against that instead. That way, you can see exactly what the translation looks like, whilst getting full advantage of the XML Schema support of JAXP.

skaffman
Interesting, creative idea, thanks!
Avi Flax
+1  A: 

See Stefan Bodewig's Weblog written on March 7, 2008 titled RELAX NG Validation in XMLUnit:

Since last night XMLUnit's trunk contains a new Validator class that is based on javax.xml.validation which is part of JAXP 1.3 (i.e. Java5+).

...

To the best of my knowledge there is no JAXP implementation that supported RELAX NG out of the box. Sun's own JAXP 1.4 (Java6+) certainly doesn't. Some searching around brought me to Kohsuke Kawaguchi's Blog who should know, given his work on JAXP, Sun's Multi Schema Validator, isorelax and other stuff.

Using his isorelax-bridge and Jing didn't get me anywhere on Java6. I went back to Kohsuke Kawaguchi's article and read the comments: the bridge doesn't work with Java6 since they changed the SchemaFactory lookup algorithm. OK, tried Java5 instead - progress, I now get a NullPointerException somewhere inside of Jing, so at least it is loading the factory. Next I replaced Jing with MSV (which is here now, no matter how many links out there lead you to the WebServices stack page at Sun, so much for "good URLs never change") and really, my simplistic tests pass.

So you may have to jump through some hoops to get RELAX NG support into your JAXP setup - in my case Java5, MSV and Kawaguchi's bridge worked, but the comments indicate it should be doable with Java6 as well - but once you manage to configure everything correctly, XMLUnit will now be there to let you assert your document's validity in Unit tests. It seems that it doesn't work for compact syntax, though.

To read the comments on Kohsuke Kawaguchi's blog, you have to use archive.org because somehow they are all gone now:

Java 5 interprets the Service Provider file as a list of key/value pairs, which is a violation to the Java 5 & 6 JAR file specification but happens to match your example.

Java 6 parses the Service Provider file as specified, ie. as a list of fully qualified class names, but thus fails to instantiate your adapter's SchemaFactory as the Service Provider file's contents are invalid.

To be compatible with both Java 5 and Java 6 without having to change the JAXP-JARV-adapter JAR file, one can simply add another JAR file containing a correct javax.xml.validation.SchemaFactory Service Provider file.

eed3si9n
Very, very useful — thanks!
Avi Flax
+1  A: 

I resolved this very error on Java 1.6 with the following line:

// Specify you want a factory for RELAX NG "compact"
System.setProperty(SchemaFactory.class.getName() + ":" + XMLConstants.RELAXNG_NS_URI, "com.thaiopensource.relaxng.jaxp.CompactSyntaxSchemaFactory");

SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.RELAXNG_NS_URI);

This allows me to use Jing to validate an XML document against a Compact RELAX NG schema. Full example below. I didn't use the bridge or anything else. The runtime classpath only has jing.jar (20091111) and my own Validator class.

import java.io.File;
import java.io.IOException;

import javax.xml.XMLConstants;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import javax.xml.validation.Validator;

import org.xml.sax.SAXException;

public class Validate
{

    public static void main(String[] args) throws SAXException, IOException
    {
        // Specify you want a factory for RELAX NG
        System.setProperty(SchemaFactory.class.getName() + ":" + XMLConstants.RELAXNG_NS_URI, "com.thaiopensource.relaxng.jaxp.CompactSyntaxSchemaFactory");
        SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.RELAXNG_NS_URI);

        // Load the specific schema you want.
        // Here I load it from a java.io.File, but we could also use a
        // java.net.URL or a javax.xml.transform.Source
        File schemaLocation = new File(args[0]);

        // Compile the schema.
        Schema schema = factory.newSchema(schemaLocation);

        // Get a validator from the schema.
        Validator validator = schema.newValidator();

        for (int i = 1; i < args.length; i++)
        {
            String file = args[i];

            // Check the document
            try
            {
                validator.validate(new StreamSource(new File(file)));
                System.out.println(file + " is valid.");
            }
            catch (SAXException ex)
            {
                System.out.print(file + " is not valid because: " + ex.getMessage());
            }
        }
    }

}

Once again, I've only tested this ion Java 1.6.

$ java -version
java version "1.6.0_01"
Java(TM) SE Runtime Environment (build 1.6.0_01-b06)
Java HotSpot(TM) Client VM (build 1.6.0_01-b06, mixed mode, sharing)
ren waldura
Replace at "System.setProperty" line :"com.thaiopensource.relaxng.jaxp.CompactSyntaxSchemaFactory" with "com.thaiopensource.relaxng.jaxp.XMLSyntaxSchemaFactory" to validate against an XML-based RelaxNG schema.Thank you for the sample, works like a charm !!
Dr1Ku