You are viewing a plain text version of this content. The canonical link for it is here.
Posted to jdo-dev@db.apache.org by Craig L Russell <Cr...@Sun.COM> on 2006/03/06 00:42:30 UTC

Re: JDO-319 patch

Hi Michael,

A few comments.

1. The main method should print a usage message, e.g.
System.err.println("No commandline arguments and system property  
metadata not defined; nothing to be tested.\nUsage: XMLTest  
<directories>\n\tAll .jdo, .orm, and .jdoquery files in the directory  
(recursively) will be tested.");

As a future enhancement we might consider the "standard" -r flag to  
mean recursively search the directory.

2. Perhaps the name of the metadata system property should be more  
specific, like javax.jdo.test.metadata

3. I added a convenience method to JDO_Test: void failOnError() that  
checks if messages is null and if not, fails with the error message  
text. You can see it in the checkin
+    /**
+     * Fail the test if there are any error messages.
+     */
+    protected void failOnError() {
+        String errors = retrieveMessages();
+        if (errors != null) {
+            fail (errors);
+        }
+    }

Craig

On Mar 5, 2006, at 2:33 PM, Michael Bouschen wrote:

> Hi,
>
> attached you find a patch for review. JIRA is down, because of a  
> system maintenance, so I send the patch to the jdo-dev alias. It  
> factors out the code from the existing api20 test class XMLTest  
> running an xml parser on .jdo, .orm and .jdoquery files into an  
> utility class. The XMLTest class now uese the utility class for  
> testing the metadata files under test/schema. There is a new goal  
> in maven.xml allowing to run the utility class as a standalone tool  
> to check JDO metadata files specified by a system property called  
> metadata.
>
> Regards Michael
>
> -- 
> Michael Bouschen		Tech@Spree Engineering GmbH
> mailto:mbo.tech@spree.de	http://www.tech.spree.de/
> Tel.:++49/30/235 520-33		Buelowstr. 66			
> Fax.:++49/30/2175 2012		D-10783 Berlin			
>
> Index: test/java/javax/jdo/schema/XMLTest.java
> ===================================================================
> --- test/java/javax/jdo/schema/XMLTest.java	(Revision 383084)
> +++ test/java/javax/jdo/schema/XMLTest.java	(Arbeitskopie)
> @@ -16,23 +16,17 @@
>
>  package javax.jdo.schema;
>
> -import java.io.BufferedReader;
>  import java.io.File;
> -import java.io.FileReader;
> -import java.io.InputStream;
> -import java.io.InputStreamReader;
>  import java.io.IOException;
>  import java.io.FilenameFilter;
>
> -import java.security.AccessController;
> -import java.security.PrivilegedAction;
> -import java.util.ArrayList;
> -import java.util.HashMap;
> -import java.util.Map;
> +import java.util.Arrays;
> +import java.util.List;
>
>  import javax.jdo.JDOFatalException;
>  import javax.jdo.util.AbstractTest;
>  import javax.jdo.util.BatchTestRunner;
> +import javax.jdo.util.XMLTestUtil;
>
>  import javax.xml.parsers.*;
>  import org.w3c.dom.Document;
> @@ -48,31 +42,10 @@
>      /** */
>      protected static String BASEDIR = System.getProperty 
> ("basedir", ".");
>
> -    /** "http://www.w3.org/2001/XMLSchema" */
> -    protected static final String XSD_TYPE =
> -        "http://www.w3.org/2001/XMLSchema";
> +    /** File prefix */
> +    protected static final String FILE_PREFIX = BASEDIR + "/test/ 
> schema/";
>
>      /** */
> -    protected static final String SCHEMA_LANGUAGE_PROP =
> -        "http://java.sun.com/xml/jaxp/properties/schemaLanguage";
> -
> -    /** */
> -    protected static final String SCHEMA_LOCATION_PROP =
> -        "http://apache.org/xml/properties/schema/external- 
> schemaLocation";
> -
> -    /** jdo namespace */
> -    protected static final String JDO_XSD_NS =
> -        "http://java.sun.com/xml/ns/jdo/jdo";
> -
> -    /** orm namespace */
> -    protected static final String ORM_XSD_NS =
> -        "http://java.sun.com/xml/ns/jdo/orm";
> -
> -    /** jdoquery namespace */
> -    protected static final String JDOQL_XSD_NS =
> -        "http://java.sun.com/xml/ns/jdo/jdoquery";
> -
> -    /** */
>      protected static final File JDO_XSD_FILE =
>          new File(BASEDIR + "/target/classes/javax/jdo/jdo.xsd");
>
> @@ -81,39 +54,29 @@
>          new File(BASEDIR + "/target/classes/javax/jdo/orm.xsd");
>
>      /** */
> -    protected static final File JDOQL_XSD_FILE =
> +    protected static final File JDOQUERY_XSD_FILE =
>          new File(BASEDIR + "/target/classes/javax/jdo/jdoquery.xsd");
>
> -    /** File prefix */
> -    protected static final String FILE_PREFIX = BASEDIR + "/test/ 
> schema/";
> -
> -    /** Entity resolver */
> -    protected static final EntityResolver resolver = new  
> JDOEntityResolver();
> -
> -    /** Error handler */
> -    protected static final Handler handler = new Handler();
> -
>      /** .xsd files */
> -    protected static final File[] XSD_FILES = {
> -        JDO_XSD_FILE, ORM_XSD_FILE, JDOQL_XSD_FILE
> -    };
> +    protected static final File[] XSD_FILES =
> +        new File[] {JDO_XSD_FILE, ORM_XSD_FILE, JDOQUERY_XSD_FILE};
>
>      /** XSD metadata files. */
>      protected static File[] positiveXSDJDO = getFiles("Positive",  
> "-xsd.jdo");
>      protected static File[] negativeXSDJDO = getFiles("Negative",  
> "-xsd.jdo");
>      protected static File[] positiveXSDORM = getFiles("Positive",  
> "-xsd.orm");
>      protected static File[] negativeXSDORM = getFiles("Negative",  
> "-xsd.orm");
> -    protected static File[] positiveXSDJDOQL = getFiles 
> ("Positive", "-xsd.jdoquery");
> -    protected static File[] negativeXSDJDOQL = getFiles 
> ("Negative", "-xsd.jdoquery");
> -
> +    protected static File[] positiveXSDJDOQUERY = getFiles 
> ("Positive", "-xsd.jdoquery");
> +    protected static File[] negativeXSDJDOQUERY = getFiles 
> ("Negative", "-xsd.jdoquery");
> +
>      /** DTD metadata files. */
>      protected static File[] positiveDTDJDO = getFiles("Positive",  
> "-dtd.jdo");
>      protected static File[] negativeDTDJDO = getFiles("Negative",  
> "-dtd.jdo");
>      protected static File[] positiveDTDORM = getFiles("Positive",  
> "-dtd.orm");
>      protected static File[] negativeDTDORM = getFiles("Negative",  
> "-dtd.orm");
> -    protected static File[] positiveDTDJDOQL = getFiles 
> ("Positive", "-dtd.jdoquery");
> -    protected static File[] negativeDTDJDOQL = getFiles 
> ("Negative", "-dtd.jdoquery");
> -
> +    protected static File[] positiveDTDJDOQUERY = getFiles 
> ("Positive", "-dtd.jdoquery");
> +    protected static File[] negativeDTDJDOQUERY = getFiles 
> ("Negative", "-dtd.jdoquery");
> +
>      /** Returns array of files of matching file names. */
>      protected static File[] getFiles(final String prefix, final  
> String suffix) {
>          FilenameFilter filter = new FilenameFilter () {
> @@ -121,22 +84,13 @@
>                  return (name.startsWith(prefix) && name.endsWith 
> (suffix));
>              }
>          };
> -        File dir = new File(FILE_PREFIX);
> -        return dir.listFiles(filter);
> +        return new File(FILE_PREFIX).listFiles(filter);
>      }
>
> -    /** */
> -    public static void main(String args[]) {
> -        BatchTestRunner.run(XMLTest.class);
> -    }
> -
>      /** Test XSD files jdo.xsd, orm.xsd, and jdoquery.xsd. */
> -    public void testXSD() throws SAXException, IOException {
> -        DocumentBuilder builder = null;
> -        DocumentBuilderFactory factory =  
> DocumentBuilderFactory.newInstance();
> -        factory.setNamespaceAware(true);
> -        builder = getParser(factory);
> -        checkXML(builder, XSD_FILES, true);
> +    public void testXSD() {
> +        XMLTestUtil util = new XMLTestUtil();
> +        util.checkXMLNonValidating(XSD_FILES);
>          String messages = retrieveMessages();
>          if (messages != null) {
>              fail(messages);
> @@ -145,17 +99,13 @@
>
>      /** Test XSD based .jdo, .orm and .jdoquery files. */
>      public void testXSDBased() {
> -        // create XSD parser
> -        DocumentBuilder builder = null;
> -        builder = createBuilder(JDO_XSD_NS + " " +  
> JDO_XSD_FILE.toURI().toString());
> -        checkXML(builder, positiveXSDJDO, true);
> -        checkXML(builder, negativeXSDJDO, false);
> -        builder = createBuilder(ORM_XSD_NS + " " +  
> ORM_XSD_FILE.toURI().toString());
> -        checkXML(builder, positiveXSDORM, true);
> -        checkXML(builder, negativeXSDORM, false);
> -        builder = createBuilder(JDOQL_XSD_NS + " " +  
> JDOQL_XSD_FILE.toURI().toString());
> -        checkXML(builder, positiveXSDJDOQL, true);
> -        checkXML(builder, negativeXSDJDOQL, false);
> +        XMLTestUtil util = new XMLTestUtil();
> +        util.checkXML(positiveXSDJDO, true);
> +        util.checkXML(negativeXSDJDO, false);
> +        util.checkXML(positiveXSDORM, true);
> +        util.checkXML(negativeXSDORM, false);
> +        util.checkXML(positiveXSDJDOQUERY, true);
> +        util.checkXML(negativeXSDJDOQUERY, false);
>          String messages = retrieveMessages();
>          if (messages != null) {
>              fail(messages);
> @@ -164,251 +114,18 @@
>
>      /** Test DTD based .jdo, .orm and .jdoquery files. */
>      public void testDTDBased() {
> -        // create DTD parser
> -        DocumentBuilder builder = createBuilder();
> -        checkXML(builder, positiveDTDJDO, true);
> -        checkXML(builder, negativeDTDJDO, false);
> -        checkXML(builder, positiveDTDORM, true);
> -        checkXML(builder, negativeDTDORM, false);
> -        checkXML(builder, positiveDTDJDOQL, true);
> -        checkXML(builder, negativeDTDJDOQL, false);
> +        XMLTestUtil util = new XMLTestUtil();
> +        util.checkXML(positiveDTDJDO, true);
> +        util.checkXML(negativeDTDJDO, false);
> +        util.checkXML(positiveDTDORM, true);
> +        util.checkXML(negativeDTDORM, false);
> +        util.checkXML(positiveDTDJDOQUERY, true);
> +        util.checkXML(negativeDTDJDOQUERY, false);
>          String messages = retrieveMessages();
>          if (messages != null) {
>              fail(messages);
>          }
>      }
>
> -    /** Create XSD builder.
> -     */
> -    private DocumentBuilder createBuilder(String location) {
> -        DocumentBuilderFactory factory =  
> DocumentBuilderFactory.newInstance();
> -        factory.setValidating(true);
> -        factory.setNamespaceAware(true);
> -        factory.setAttribute(SCHEMA_LANGUAGE_PROP, XSD_TYPE);
> -        factory.setAttribute(SCHEMA_LOCATION_PROP, location);
> -        return getParser(factory);
> -    }
> -
> -    /** Create DTD builder.
> -     */
> -    private DocumentBuilder createBuilder() {
> -        DocumentBuilderFactory factory =  
> DocumentBuilderFactory.newInstance();
> -        factory.setValidating(true);
> -        factory.setNamespaceAware(true);
> -        return getParser(factory);
> -    }
> -
> -    /** Returns a parser obtained from specified factroy. */
> -    private DocumentBuilder getParser(DocumentBuilderFactory  
> factory) {
> -        try {
> -            DocumentBuilder builder = factory.newDocumentBuilder();
> -            builder.setEntityResolver(resolver);
> -            builder.setErrorHandler(handler);
> -            return builder;
> -        } catch (ParserConfigurationException ex) {
> -            throw new JDOFatalException("Cannot create XML  
> parser", ex);
> -        }
> -    }
> -
> -    /** Parse the specified files using the specified builder. The  
> valid
> -     * parameter determines whether the specified files are valid  
> JDO metadata
> -     * files. The method does not throw an exception on an error,  
> instead it
> -     * appends any error message to the global message handler.
> -     */
> -    private void checkXML(DocumentBuilder builder, File[] files,  
> boolean valid) {
> -        for (int i = 0; i < files.length; i++) {
> -            File file = files[i];
> -            handler.init(file);
> -            try {
> -                builder.parse(file);
> -            } catch (SAXParseException ex) {
> -                handler.error(ex);
> -            } catch (Exception ex) {
> -                throw new JDOFatalException("Fatal error  
> processing " +
> -                        file.getName(), ex);
> -            }
> -            String messages = handler.getMessages();
> -            if (valid && (messages != null)) {
> -                appendMessage(messages);
> -            } else if (!valid && (messages == null)) {
> -                appendMessage(file.getName() + " is not valid, " +
> -                              "but the parser did not catch the  
> error.");
> -            }
> -        }
> -    }
> -
> -    /** ErrorHandler implementation. */
> -    private static class Handler implements ErrorHandler {
> -
> -        private File fileUnderTest;
> -        private String[] lines;
> -        private StringBuffer messages;
> -
> -        public void error(SAXParseException ex) {
> -            append("Handler.error: ", ex);
> -        }
> -
> -        public void fatalError(SAXParseException ex) {
> -            append("Handler.fatalError: ", ex);
> -        }
> -
> -        public void warning(SAXParseException ex) {
> -            append("Handler.warning: ", ex);
> -        }
> -
> -        public void init(File file) {
> -            this.fileUnderTest = file;
> -            this.messages = new StringBuffer();
> -            this.lines = null;
> -        }
> -
> -        public String getMessages() {
> -            return (messages.length() == 0) ? null :  
> messages.toString();
> -        }
> -
> -        private void append(String prefix, SAXParseException ex) {
> -            int lineNumber = ex.getLineNumber();
> -            int columnNumber = ex.getColumnNumber();
> -            messages.append("------------------------").append(NL);
> -            messages.append(prefix).append(fileUnderTest.getName());
> -            messages.append(" [line=").append(lineNumber);
> -            messages.append(", col=").append(columnNumber).append 
> ("]: ");
> -            messages.append(ex.getMessage()).append(NL);
> -            messages.append(getErrorLocation(lineNumber,  
> columnNumber));
> -        }
> -
> -        private String[] getLines() {
> -            if (lines == null) {
> -                try {
> -                    BufferedReader bufferedReader =
> -                        new BufferedReader(new FileReader 
> (fileUnderTest));
> -                    ArrayList tmp = new ArrayList();
> -                    while (bufferedReader.ready()) {
> -                        tmp.add(bufferedReader.readLine());
> -                    }
> -                    lines = (String[])tmp.toArray(new String 
> [tmp.size()]);
> -                } catch (IOException ex) {
> -                    throw new JDOFatalException("getLines: caught  
> IOException", ex);
> -                }
> -            }
> -            return lines;
> -        }
> -
> -        /** Return the error location for the file under test.
> -         */
> -        private String getErrorLocation(int lineNumber, int  
> columnNumber) {
> -            String[] lines = getLines();
> -            int length = lines.length;
> -            if (lineNumber > length) {
> -                return "Line number " + lineNumber +
> -                    " exceeds the number of lines in the file (" +
> -                    lines.length + ")";
> -            } else if (lineNumber < 1) {
> -                return "Line number " + lineNumber +
> -                    " does not allow retriving the error location.";
> -            }
> -            StringBuffer buf = new StringBuffer();
> -            if (lineNumber > 2) {
> -                buf.append(lines[lineNumber-3]);
> -                buf.append(NL);
> -                buf.append(lines[lineNumber-2]);
> -                buf.append(NL);
> -            }
> -            buf.append(lines[lineNumber-1]);
> -            buf.append(NL);
> -            for (int i = 1; i < columnNumber; ++i) {
> -                buf.append(' ');
> -            }
> -            buf.append("^\n");
> -            if (lineNumber + 1 < length) {
> -                buf.append(lines[lineNumber]);
> -                buf.append(NL);
> -                buf.append(lines[lineNumber+1]);
> -                buf.append(NL);
> -            }
> -            return buf.toString();
> -        }
> -    }
> -
> -    /** Implementation of EntityResolver interface to check the  
> jdo.dtd location
> -     **/
> -    private static class JDOEntityResolver
> -        implements EntityResolver {
> -
> -        private static final String RECOGNIZED_JDO_PUBLIC_ID =
> -            "-//Sun Microsystems, Inc.//DTD Java Data Objects  
> Metadata 2.0//EN";
> -        private static final String RECOGNIZED_JDO_SYSTEM_ID =
> -            "file:/javax/jdo/jdo.dtd";
> -        private static final String RECOGNIZED_JDO_SYSTEM_ID2 =
> -            "http://java.sun.com/dtd/jdo_2_0.dtd";
> -        private static final String RECOGNIZED_ORM_PUBLIC_ID =
> -            "-//Sun Microsystems, Inc.//DTD Java Data Objects  
> Mapping Metadata 2.0//EN";
> -        private static final String RECOGNIZED_ORM_SYSTEM_ID =
> -            "file:/javax/jdo/orm.dtd";
> -        private static final String RECOGNIZED_ORM_SYSTEM_ID2 =
> -            "http://java.sun.com/dtd/orm_2_0.dtd";
> -        private static final String RECOGNIZED_JDOQUERY_PUBLIC_ID =
> -            "-//Sun Microsystems, Inc.//DTD Java Data Objects  
> Query Metadata 2.0//EN";
> -        private static final String RECOGNIZED_JDOQUERY_SYSTEM_ID =
> -            "file:/javax/jdo/jdoquery.dtd";
> -        private static final String RECOGNIZED_JDOQUERY_SYSTEM_ID2 =
> -            "http://java.sun.com/dtd/jdoquery_2_0.dtd";
> -        private static final String JDO_DTD_FILENAME =
> -            "javax/jdo/jdo.dtd";
> -        private static final String ORM_DTD_FILENAME =
> -            "javax/jdo/orm.dtd";
> -        private static final String JDOQUERY_DTD_FILENAME =
> -            "javax/jdo/jdoquery.dtd";
> -
> -        static Map publicIds = new HashMap();
> -        static Map systemIds = new HashMap();
> -        static {
> -            publicIds.put(RECOGNIZED_JDO_PUBLIC_ID,  
> JDO_DTD_FILENAME);
> -            publicIds.put(RECOGNIZED_ORM_PUBLIC_ID,  
> ORM_DTD_FILENAME);
> -            publicIds.put(RECOGNIZED_JDOQUERY_PUBLIC_ID,  
> JDOQUERY_DTD_FILENAME);
> -            systemIds.put(RECOGNIZED_JDO_SYSTEM_ID,  
> JDO_DTD_FILENAME);
> -            systemIds.put(RECOGNIZED_ORM_SYSTEM_ID,  
> ORM_DTD_FILENAME);
> -            systemIds.put(RECOGNIZED_JDOQUERY_SYSTEM_ID,  
> JDOQUERY_DTD_FILENAME);
> -            systemIds.put(RECOGNIZED_JDO_SYSTEM_ID2,  
> JDO_DTD_FILENAME);
> -            systemIds.put(RECOGNIZED_ORM_SYSTEM_ID2,  
> ORM_DTD_FILENAME);
> -            systemIds.put(RECOGNIZED_JDOQUERY_SYSTEM_ID2,  
> JDOQUERY_DTD_FILENAME);
> -        }
> -        public InputSource resolveEntity(String publicId, final  
> String systemId)
> -            throws SAXException, IOException
> -        {
> -            // check for recognized ids
> -            String filename = (String)publicIds.get(publicId);
> -            if (filename == null) {
> -                filename = (String)systemIds.get(systemId);
> -            }
> -            final String finalName = filename;
> -            if (finalName == null) {
> -                return null;
> -            } else {
> -                // Substitute the dtd with the one from  
> javax.jdo.jdo.dtd,
> -                // but only if the publicId is equal to  
> RECOGNIZED_PUBLIC_ID
> -                // or there is no publicID and the systemID is  
> equal to
> -                // RECOGNIZED_SYSTEM_ID.
> -                    InputStream stream = (InputStream)  
> AccessController.doPrivileged (
> -                        new PrivilegedAction () {
> -                            public Object run () {
> -                            return getClass().getClassLoader().
> -                                getResourceAsStream(finalName);
> -                            }
> -                         }
> -                     );
> -                    if (stream == null) {
> -                        // TDB: error handling + I18N
> -                        throw new JDOFatalException("Cannot load " +
> -                            finalName +
> -                            ", because the file does not exist in  
> the jdo.jar file, " +
> -                            "or the JDOParser class is not granted  
> permission to read this file.  " +
> -                            "The metadata .xml file contained  
> PUBLIC=" + publicId +
> -                            " SYSTEM=" + systemId + ".");
> -                    }
> -                return new InputSource(new InputStreamReader 
> (stream));
> -            }
> -        }
> -    }
>  }
>
> Index: test/java/javax/jdo/util/XMLTestUtil.java
> ===================================================================
> --- test/java/javax/jdo/util/XMLTestUtil.java	(Revision 0)
> +++ test/java/javax/jdo/util/XMLTestUtil.java	(Revision 0)
> @@ -0,0 +1,503 @@
> +/*
> + * Copyright 2006 The Apache Software Foundation.
> + *
> + * Licensed under the Apache License, Version 2.0 (the "License");
> + * you may not use this file except in compliance with the License.
> + * You may obtain a copy of the License at
> + *
> + *     http://www.apache.org/licenses/LICENSE-2.0
> + *
> + * Unless required by applicable law or agreed to in writing,  
> software
> + * distributed under the License is distributed on an "AS IS" BASIS,
> + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or  
> implied.
> + * See the License for the specific language governing permissions  
> and
> + * limitations under the License.
> + */
> +
> +package javax.jdo.util;
> +
> +import java.io.BufferedReader;
> +import java.io.File;
> +import java.io.FileReader;
> +import java.io.InputStream;
> +import java.io.InputStreamReader;
> +import java.io.IOException;
> +import java.io.FilenameFilter;
> +
> +import java.security.AccessController;
> +import java.security.PrivilegedAction;
> +import java.util.Arrays;
> +import java.util.ArrayList;
> +import java.util.HashMap;
> +import java.util.Iterator;
> +import java.util.List;
> +import java.util.Map;
> +import java.util.StringTokenizer;
> +
> +import javax.jdo.JDOFatalException;
> +import javax.jdo.util.AbstractTest;
> +import javax.jdo.util.BatchTestRunner;
> +
> +import javax.xml.parsers.*;
> +import org.w3c.dom.Document;
> +import org.xml.sax.*;
> +import org.xml.sax.helpers.*;
> +
> +/**
> + * Tests schema files.
> + * <p>
> + */
> +public class XMLTestUtil {
> +
> +    /** */
> +    protected static String BASEDIR = System.getProperty 
> ("basedir", ".");
> +
> +    /** "http://www.w3.org/2001/XMLSchema" */
> +    protected static final String XSD_TYPE =
> +        "http://www.w3.org/2001/XMLSchema";
> +
> +    /** */
> +    protected static final String SCHEMA_LANGUAGE_PROP =
> +        "http://java.sun.com/xml/jaxp/properties/schemaLanguage";
> +
> +    /** */
> +    protected static final String SCHEMA_LOCATION_PROP =
> +        "http://apache.org/xml/properties/schema/external- 
> schemaLocation";
> +
> +    /** jdo namespace */
> +    protected static final String JDO_XSD_NS =
> +        "http://java.sun.com/xml/ns/jdo/jdo";
> +
> +    /** orm namespace */
> +    protected static final String ORM_XSD_NS =
> +        "http://java.sun.com/xml/ns/jdo/orm";
> +
> +    /** jdoquery namespace */
> +    protected static final String JDOQUERY_XSD_NS =
> +        "http://java.sun.com/xml/ns/jdo/jdoquery";
> +
> +    /** jdo xsd file */
> +    protected static final File JDO_XSD_FILE =
> +        new File(BASEDIR + "/target/classes/javax/jdo/jdo.xsd");
> +
> +    /** orm xsd file */
> +    protected static final File ORM_XSD_FILE =
> +        new File(BASEDIR + "/target/classes/javax/jdo/orm.xsd");
> +
> +    /** jdoquery xsd file */
> +    protected static final File JDOQUERY_XSD_FILE =
> +        new File(BASEDIR + "/target/classes/javax/jdo/jdoquery.xsd");
> +
> +    /** Entity resolver */
> +    protected static final EntityResolver resolver = new  
> JDOEntityResolver();
> +
> +    /** Error handler */
> +    protected static final Handler handler = new Handler();
> +
> +    /** Name of the metadata property, a comma separated list of  
> JDO metadata
> +     * file or directories containing such files. */
> +    protected static String METADATA_PROP = "metadata";
> +
> +    /** Separator character for the metadata property. */
> +    protected static String DELIM = ",;";
> +
> +    /** XSD builder for jdo namespace. */
> +    private final DocumentBuilder jdoXsdBuilder =
> +        createBuilder(JDO_XSD_NS + " " + JDO_XSD_FILE.toURI 
> ().toString());
> +
> +    /** XSD builder for orm namespace. */
> +    private final DocumentBuilder ormXsdBuilder =
> +        createBuilder(ORM_XSD_NS + " " + ORM_XSD_FILE.toURI 
> ().toString());
> +
> +    /** XSD builder for jdoquery namespace. */
> +    private final DocumentBuilder jdoqueryXsdBuilder =
> +        createBuilder(JDOQUERY_XSD_NS + " " +  
> JDOQUERY_XSD_FILE.toURI().toString());
> +
> +    /** DTD builder. */
> +    private final DocumentBuilder dtdBuilder = createBuilder(true);
> +
> +    /** Non validating builder. */
> +    private final DocumentBuilder nonValidatingBuilder =  
> createBuilder(false);
> +
> +    /** Create XSD builder. */
> +    private DocumentBuilder createBuilder(String location) {
> +        DocumentBuilderFactory factory =  
> DocumentBuilderFactory.newInstance();
> +        factory.setValidating(true);
> +        factory.setNamespaceAware(true);
> +        factory.setAttribute(SCHEMA_LANGUAGE_PROP, XSD_TYPE);
> +        factory.setAttribute(SCHEMA_LOCATION_PROP, location);
> +        return getParser(factory);
> +    }
> +
> +    /** Create builder. */
> +    private DocumentBuilder createBuilder(boolean validating) {
> +        DocumentBuilderFactory factory =  
> DocumentBuilderFactory.newInstance();
> +        factory.setValidating(validating);
> +        factory.setNamespaceAware(true);
> +        return getParser(factory);
> +    }
> +
> +    /** Returns a parser obtained from specified factroy. */
> +    private DocumentBuilder getParser(DocumentBuilderFactory  
> factory) {
> +        try {
> +            DocumentBuilder builder = factory.newDocumentBuilder();
> +            builder.setEntityResolver(resolver);
> +            builder.setErrorHandler(handler);
> +            return builder;
> +        } catch (ParserConfigurationException ex) {
> +            throw new JDOFatalException("Cannot create XML  
> parser", ex);
> +        }
> +    }
> +
> +    /** Parse the specified files. The valid parameter determines  
> whether the
> +     * specified files are valid JDO metadata files. The method  
> does not throw
> +     * an exception on an error, instead it appends any error  
> message to the
> +     * global message handler.
> +     */
> +    public void checkXML(File[] files, boolean valid) {
> +        for (int i = 0; i < files.length; i++) {
> +            String messages = checkXML(files[i], valid);
> +            if (messages != null) {
> +                AbstractTest.appendMessage(messages);
> +            }
> +        }
> +    }
> +
> +    /** Parse the specified files using a non validating parser.  
> The method
> +     * does not throw an exception on an error, instead it appends  
> any error
> +     * message to the global message handler.
> +     */
> +    public void checkXMLNonValidating(File[] files) {
> +        for (int i = 0; i < files.length; i++) {
> +            String messages = checkXML(nonValidatingBuilder, files 
> [i], true);
> +            if (messages != null) {
> +                AbstractTest.appendMessage(messages);
> +            }
> +        }
> +    }
> +
> +     /** Parse the specified file. The method checks whether it is  
> a XSD or
> +     * DTD base file and parses the file using a builder according  
> to the file
> +     * name suffix. The valid parameter determines whether the  
> specified files
> +     * are valid JDO metadata files. The method does not throw an  
> exception on
> +     * an error, instead it returns the error message as string.
> +     */
> +    private String checkXML(File file, boolean valid) {
> +        String messages = null;
> +        String fileName = file.getName();
> +        try {
> +            if (isDTDBased(file)) {
> +                messages = checkXML(dtdBuilder, file, valid);
> +            } else if (fileName.endsWith(".jdo")) {
> +                messages = checkXML(jdoXsdBuilder, file, valid);
> +            } else if (fileName.endsWith(".orm")) {
> +                messages = checkXML(ormXsdBuilder, file, valid);
> +            } else if (fileName.endsWith(".jdoquery")) {
> +                messages = checkXML(jdoqueryXsdBuilder, file, valid);
> +            }
> +        } catch (SAXException ex) {
> +            messages = ex.getMessage();
> +        }
> +        return messages;
> +    }
> +
> +    /** Parse the specified file using the specified builder. The  
> valid
> +     * parameter determines whether the specified files are valid  
> JDO metadata
> +     * files. The method does not throw an exception on an error,  
> instead it
> +     * returns the error message as string.
> +     */
> +    private String checkXML(DocumentBuilder builder, File file,  
> boolean valid) {
> +        handler.init(file);
> +        try {
> +            builder.parse(file);
> +        } catch (SAXParseException ex) {
> +            handler.error(ex);
> +        } catch (Exception ex) {
> +            throw new JDOFatalException("Fatal error processing " +
> +                                        file.getName(), ex);
> +        }
> +        String messages = handler.getMessages();
> +        if (!valid) {
> +            if (messages != null) {
> +                // expected error for negative test
> +                messages = null;
> +            } else {
> +                messages = file.getName() + " is not valid, " +
> +                    "but the parser did not catch the error.";
> +            }
> +        }
> +        return messages;
> +    }
> +
> +    /** Checks whether the specifeid file is DTD or XSD based. The  
> method
> +     * throws a SAXException if the file has syntax errors. */
> +    private boolean isDTDBased(File file) throws SAXException {
> +        handler.init(file);
> +        try {
> +            Document document = nonValidatingBuilder.parse(file);
> +            return document.getDoctype() != null;
> +        } catch (SAXParseException ex) {
> +            handler.error(ex);
> +            throw new SAXException(handler.getMessages());
> +        } catch (Exception ex) {
> +            throw new SAXException("Fatal error processing " +  
> file.getName() +
> +                                   ":  " + ex.getMessage());
> +        }
> +    }
> +
> +    /** ErrorHandler implementation. */
> +    private static class Handler implements ErrorHandler {
> +
> +        private File fileUnderTest;
> +        private String[] lines;
> +        private StringBuffer messages;
> +
> +        public void error(SAXParseException ex) {
> +            append("Handler.error: ", ex);
> +        }
> +
> +        public void fatalError(SAXParseException ex) {
> +            append("Handler.fatalError: ", ex);
> +        }
> +
> +        public void warning(SAXParseException ex) {
> +            append("Handler.warning: ", ex);
> +        }
> +
> +        public void init(File file) {
> +            this.fileUnderTest = file;
> +            this.messages = new StringBuffer();
> +            this.lines = null;
> +        }
> +
> +        public String getMessages() {
> +            return (messages.length() == 0) ? null :  
> messages.toString();
> +        }
> +
> +        private void append(String prefix, SAXParseException ex) {
> +            int lineNumber = ex.getLineNumber();
> +            int columnNumber = ex.getColumnNumber();
> +            messages.append("------------------------").append 
> (AbstractTest.NL);
> +            messages.append(prefix).append(fileUnderTest.getName());
> +            messages.append(" [line=").append(lineNumber);
> +            messages.append(", col=").append(columnNumber).append 
> ("]: ");
> +            messages.append(ex.getMessage()).append(AbstractTest.NL);
> +            messages.append(getErrorLocation(lineNumber,  
> columnNumber));
> +        }
> +
> +        private String[] getLines() {
> +            if (lines == null) {
> +                try {
> +                    BufferedReader bufferedReader =
> +                        new BufferedReader(new FileReader 
> (fileUnderTest));
> +                    ArrayList tmp = new ArrayList();
> +                    while (bufferedReader.ready()) {
> +                        tmp.add(bufferedReader.readLine());
> +                    }
> +                    lines = (String[])tmp.toArray(new String 
> [tmp.size()]);
> +                } catch (IOException ex) {
> +                    throw new JDOFatalException("getLines: caught  
> IOException", ex);
> +                }
> +            }
> +            return lines;
> +        }
> +
> +        /** Return the error location for the file under test.
> +         */
> +        private String getErrorLocation(int lineNumber, int  
> columnNumber) {
> +            String[] lines = getLines();
> +            int length = lines.length;
> +            if (lineNumber > length) {
> +                return "Line number " + lineNumber +
> +                    " exceeds the number of lines in the file (" +
> +                    lines.length + ")";
> +            } else if (lineNumber < 1) {
> +                return "Line number " + lineNumber +
> +                    " does not allow retriving the error location.";
> +            }
> +            StringBuffer buf = new StringBuffer();
> +            if (lineNumber > 2) {
> +                buf.append(lines[lineNumber-3]);
> +                buf.append(AbstractTest.NL);
> +                buf.append(lines[lineNumber-2]);
> +                buf.append(AbstractTest.NL);
> +            }
> +            buf.append(lines[lineNumber-1]);
> +            buf.append(AbstractTest.NL);
> +            for (int i = 1; i < columnNumber; ++i) {
> +                buf.append(' ');
> +            }
> +            buf.append("^\n");
> +            if (lineNumber + 1 < length) {
> +                buf.append(lines[lineNumber]);
> +                buf.append(AbstractTest.NL);
> +                buf.append(lines[lineNumber+1]);
> +                buf.append(AbstractTest.NL);
> +            }
> +            return buf.toString();
> +        }
> +    }
> +
> +    /** Implementation of EntityResolver interface to check the  
> jdo.dtd location
> +     **/
> +    private static class JDOEntityResolver
> +        implements EntityResolver {
> +
> +        private static final String RECOGNIZED_JDO_PUBLIC_ID =
> +            "-//Sun Microsystems, Inc.//DTD Java Data Objects  
> Metadata 2.0//EN";
> +        private static final String RECOGNIZED_JDO_SYSTEM_ID =
> +            "file:/javax/jdo/jdo.dtd";
> +        private static final String RECOGNIZED_JDO_SYSTEM_ID2 =
> +            "http://java.sun.com/dtd/jdo_2_0.dtd";
> +        private static final String RECOGNIZED_ORM_PUBLIC_ID =
> +            "-//Sun Microsystems, Inc.//DTD Java Data Objects  
> Mapping Metadata 2.0//EN";
> +        private static final String RECOGNIZED_ORM_SYSTEM_ID =
> +            "file:/javax/jdo/orm.dtd";
> +        private static final String RECOGNIZED_ORM_SYSTEM_ID2 =
> +            "http://java.sun.com/dtd/orm_2_0.dtd";
> +        private static final String RECOGNIZED_JDOQUERY_PUBLIC_ID =
> +            "-//Sun Microsystems, Inc.//DTD Java Data Objects  
> Query Metadata 2.0//EN";
> +        private static final String RECOGNIZED_JDOQUERY_SYSTEM_ID =
> +            "file:/javax/jdo/jdoquery.dtd";
> +        private static final String RECOGNIZED_JDOQUERY_SYSTEM_ID2 =
> +            "http://java.sun.com/dtd/jdoquery_2_0.dtd";
> +        private static final String JDO_DTD_FILENAME =
> +            "javax/jdo/jdo.dtd";
> +        private static final String ORM_DTD_FILENAME =
> +            "javax/jdo/orm.dtd";
> +        private static final String JDOQUERY_DTD_FILENAME =
> +            "javax/jdo/jdoquery.dtd";
> +
> +        static Map publicIds = new HashMap();
> +        static Map systemIds = new HashMap();
> +        static {
> +            publicIds.put(RECOGNIZED_JDO_PUBLIC_ID,  
> JDO_DTD_FILENAME);
> +            publicIds.put(RECOGNIZED_ORM_PUBLIC_ID,  
> ORM_DTD_FILENAME);
> +            publicIds.put(RECOGNIZED_JDOQUERY_PUBLIC_ID,  
> JDOQUERY_DTD_FILENAME);
> +            systemIds.put(RECOGNIZED_JDO_SYSTEM_ID,  
> JDO_DTD_FILENAME);
> +            systemIds.put(RECOGNIZED_ORM_SYSTEM_ID,  
> ORM_DTD_FILENAME);
> +            systemIds.put(RECOGNIZED_JDOQUERY_SYSTEM_ID,  
> JDOQUERY_DTD_FILENAME);
> +            systemIds.put(RECOGNIZED_JDO_SYSTEM_ID2,  
> JDO_DTD_FILENAME);
> +            systemIds.put(RECOGNIZED_ORM_SYSTEM_ID2,  
> ORM_DTD_FILENAME);
> +            systemIds.put(RECOGNIZED_JDOQUERY_SYSTEM_ID2,  
> JDOQUERY_DTD_FILENAME);
> +        }
> +        public InputSource resolveEntity(String publicId, final  
> String systemId)
> +            throws SAXException, IOException
> +        {
> +            // check for recognized ids
> +            String filename = (String)publicIds.get(publicId);
> +            if (filename == null) {
> +                filename = (String)systemIds.get(systemId);
> +            }
> +            final String finalName = filename;
> +            if (finalName == null) {
> +                return null;
> +            } else {
> +                // Substitute the dtd with the one from  
> javax.jdo.jdo.dtd,
> +                // but only if the publicId is equal to  
> RECOGNIZED_PUBLIC_ID
> +                // or there is no publicID and the systemID is  
> equal to
> +                // RECOGNIZED_SYSTEM_ID.
> +                    InputStream stream = (InputStream)  
> AccessController.doPrivileged (
> +                        new PrivilegedAction () {
> +                            public Object run () {
> +                            return getClass().getClassLoader().
> +                                getResourceAsStream(finalName);
> +                            }
> +                         }
> +                     );
> +                    if (stream == null) {
> +                        // TDB: error handling + I18N
> +                        throw new JDOFatalException("Cannot load " +
> +                            finalName +
> +                            ", because the file does not exist in  
> the jdo.jar file, " +
> +                            "or the JDOParser class is not granted  
> permission to read this file.  " +
> +                            "The metadata .xml file contained  
> PUBLIC=" + publicId +
> +                            " SYSTEM=" + systemId + ".");
> +                    }
> +                return new InputSource(new InputStreamReader 
> (stream));
> +            }
> +        }
> +    }
> +
> +    /** Helper class to find all test JDO metadata files. */
> +    public static class XMLFinder {
> +
> +        private List metadataFiles = new ArrayList();
> +
> +        /** Constructor. */
> +        public XMLFinder(String[] fileNames) {
> +            if (fileNames == null) return;
> +            for (int i = 0; i < fileNames.length; i++) {
> +                appendTestFiles(fileNames[i]);
> +            }
> +        }
> +
> +        /** Returns array of files of matching file names. */
> +        private File[] getFiles(File dir, final String suffix) {
> +            FilenameFilter filter = new FilenameFilter () {
> +                    public boolean accept(File file, String name) {
> +                        return name.endsWith(suffix);
> +                    }
> +                };
> +            return dir.listFiles(filter);
> +        }
> +
> +        /** */
> +        private void appendTestFiles(String fileName) {
> +            File file = new File(fileName);
> +            if (file.isDirectory()) {
> +                metadataFiles.addAll(Arrays.asList(getFiles(file,  
> ".jdo")));
> +                metadataFiles.addAll(Arrays.asList(getFiles(file,  
> ".orm")));
> +                metadataFiles.addAll(Arrays.asList(getFiles(file,  
> ".jdoquery")));
> +            } else if (fileName.endsWith(".jdo") ||
> +                       fileName.endsWith(".orm") ||
> +                       fileName.endsWith(".jdoquery")) {
> +                metadataFiles.add(new File(fileName));
> +            }
> +        }
> +
> +        /** Returns an array of test files with suffix .jdo, .orm  
> or .jdoquery. */
> +        public File[] getMetadataFiles() {
> +            return (File[])metadataFiles.toArray(new File 
> [metadataFiles.size()]);
> +        }
> +
> +    }
> +
> +    /** */
> +    private static String[] checkMetadataSystemProperty() {
> +        String[] ret = null;
> +        String metadata = System.getProperty(METADATA_PROP);
> +        if ((metadata != null) && (metadata.length() > 0)) {
> +            List entries = new ArrayList();
> +            StringTokenizer st = new StringTokenizer(metadata,  
> DELIM);
> +            while (st.hasMoreTokens()) {
> +                entries.add(st.nextToken());
> +            }
> +            ret = (String[])entries.toArray(new String[entries.size 
> ()]);
> +        }
> +        return ret;
> +    }
> +
> +    /** */
> +    public static void main(String args[]) {
> +        String[] fromProp = checkMetadataSystemProperty();
> +        if ((args.length == 0) && (fromProp == null)) {
> +            System.err.println("No commandline arguments and  
> system property metadata not defined; nothing to be tested.");
> +        } else if ((args.length == 0) && (fromProp != null)) {
> +            // use metadata system property
> +            args = fromProp;
> +        } else if ((args.length != 0) && (fromProp != null)) {
> +            System.err.println("Commandline arguments specified  
> and system property metadata defined; ignoring system property  
> metadata.");
> +        }
> +        XMLTestUtil xmlTest = new XMLTestUtil();
> +        File[] files = new XMLFinder(args).getMetadataFiles();
> +        for (int i = 0; i < files.length; i++) {
> +            File file = files[i];
> +            System.out.print("Checking " + file.getName() + ": ");
> +            String messages = xmlTest.checkXML(file, true);
> +            messages = (messages == null) ?  "OK" :  
> AbstractTest.NL + messages;
> +            System.out.println(messages);
> +        }
> +    }
> +}
> +
> Index: project.properties
> ===================================================================
> --- project.properties	(Revision 383084)
> +++ project.properties	(Arbeitskopie)
> @@ -16,5 +16,9 @@
>  maven.junit.sysproperties =  
> javax.xml.parsers.DocumentBuilderFactory basedir
>   
> javax.xml.parsers.DocumentBuilderFactory=org.apache.xerces.jaxp.Docume 
> ntBuilderFactoryImpl
>
> +xmlapis.jarfile = ${pom.getDependencyPath('xml-apis:xml-apis')}
> +xerces.jarfile = ${pom.getDependencyPath('xerces:xerces')}
> +junit.jarfile = ${pom.getDependencyPath('junit:junit')}
> +
>  # Manifest seed file
>  maven.jar.manifest = ${basedir}/../JDO20.MF
> Index: maven.xml
> ===================================================================
> --- maven.xml	(Revision 383084)
> +++ maven.xml	(Arbeitskopie)
> @@ -45,4 +45,26 @@
>          </delete>
>      </goal>
>
> +    <!-- ======== -->
> +    <!-- XML test -->
> +    <!-- ======== -->
> +
> +    <!-- Run XML parser on JDO metadata files (suffix .jdo, .orm  
> or .jdoquery).   -->
> +    <!-- The tool uses the following system  
> properties:                           -->
> +    <!--   metadata: a comma separated list of JDO metadata files  
> or directories. -->
> +    <goal name="xmltest" prereqs="test:compile">
> +        <java classname="javax.jdo.util.XMLTestUtil" fork="true">
> +            <classpath>
> +                <pathelement location="${xmlapis.jarfile}"/>
> +                <pathelement location="${xerces.jarfile}"/>
> +                <pathelement location="${junit.jarfile}"/>
> +                <pathelement location="${maven.build.dir}/classes"/>
> +                <pathelement location="${maven.build.dir}/test- 
> classes"/>
> +            </classpath>
> +            <sysproperty key="metadata" value="${metadata}"/>
> +            <sysproperty  
> key="javax.xml.parsers.DocumentBuilderFactory"
> +                         value="$ 
> {javax.xml.parsers.DocumentBuilderFactory}"/>
> +        </java>
> +    </goal>
> +
>  </project>
> Index: project.xml
> ===================================================================
> --- project.xml	(Revision 383084)
> +++ project.xml	(Arbeitskopie)
> @@ -42,14 +42,19 @@
>          </dependency>
>          <dependency>
>              <groupId>xerces</groupId>
> -            <artifactId>xercesImpl</artifactId>
> -            <version>2.7.1</version>
> +            <artifactId>xerces</artifactId>
> +            <version>2.4.0</version>
>          </dependency>
>          <dependency>
> -            <groupId>xerces</groupId>
> -            <artifactId>xmlParserAPIs</artifactId>
> -            <version>2.6.2</version>
> +            <groupId>xml-apis</groupId>
> +            <artifactId>xml-apis</artifactId>
> +            <version>1.0.b2</version>
>          </dependency>
> +        <dependency>
> +            <groupId>junit</groupId>
> +            <artifactId>junit</artifactId>
> +            <version>3.8.1</version>
> +        </dependency>
>      </dependencies>
>      <!-- =================== -->
>      <!-- Build Specification -->
> Index: test/schema/Positive99-xsd.jdoquery
> ===================================================================
> --- test/schema/Positive99-xsd.jdoquery	(Revision 383084)
> +++ test/schema/Positive99-xsd.jdoquery	(Arbeitskopie)
> @@ -1,5 +1,4 @@
>  <?xml version="1.0" encoding="UTF-8"?>
> -<!DOCTYPE jdoquery SYSTEM "file:/javax/jdo/jdoquery.dtd">
>  <jdoquery xmlns="http://java.sun.com/xml/ns/jdo/jdoquery"
>       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
>       xsi:schemaLocation="http://java.sun.com/xml/ns/jdo/jdoquery

Craig Russell
Architect, Sun Java Enterprise System http://java.sun.com/products/jdo
408 276-5638 mailto:Craig.Russell@sun.com
P.S. A good JDO? O, Gasp!