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 Michael Bouschen <mb...@spree.de> on 2006/03/05 23:33:04 UTC

JDO-319 patch

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			


Re: JDO-319 patch

Posted by Craig L Russell <Cr...@Sun.COM>.
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!