Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8255919

Problematic behavior when combining validation with XML catalog file

    XMLWordPrintable

    Details

    • Type: Bug
    • Status: Closed
    • Priority: P4
    • Resolution: Not an Issue
    • Affects Version/s: 11, 15, 16
    • Fix Version/s: None
    • Component/s: xml

      Description

      ADDITIONAL SYSTEM INFORMATION :
      openjdk version "15" 2020-09-15
      OpenJDK Runtime Environment (build 15+36-1562)
      OpenJDK 64-Bit Server VM (build 15+36-1562, mixed mode, sharing)

      A DESCRIPTION OF THE PROBLEM :
      The method java.xml/javax.xml.catalog.CatalogResolverImpl.resolveEntity checks if systemId is null and if so reports the problem "JAXP09020006: The argument 'systemId' can not be null.". The thing is that when using a catalog file the systemId parameter is always set to null and this in turn triggers JAXP09020006. However if a wrapper class is introduced that implement the CatalogResolver interface and intercepts any systemId null values (replacing them with the empty string "") validation in combination with a catalog file magically start to work as expected.

      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      Use a catalog file in combination with validation.

      EXPECTED VERSUS ACTUAL BEHAVIOR :
      EXPECTED -
      Validation should be able to work together with a catalog file.
      ACTUAL -
      (Source code for foo/Foo.java provided in the test case)

      java.lang.NullPointerException: JAXP09020006: The argument 'systemId' can not be null.
              at java.xml/javax.xml.catalog.CatalogMessages.reportNPEOnNull(CatalogMessages.java:129)
              at java.xml/javax.xml.catalog.CatalogResolverImpl.resolveEntity(CatalogResolverImpl.java:70)
              at java.xml/com.sun.org.apache.xerces.internal.impl.XMLEntityManager.resolveEntity(XMLEntityManager.java:1154)
              at java.xml/com.sun.org.apache.xerces.internal.impl.xs.XMLSchemaLoader.resolveDocument(XMLSchemaLoader.java:662)
              at java.xml/com.sun.org.apache.xerces.internal.impl.xs.XMLSchemaValidator.findSchemaGrammar(XMLSchemaValidator.java:2694)
              at java.xml/com.sun.org.apache.xerces.internal.impl.xs.XMLSchemaValidator.handleStartElement(XMLSchemaValidator.java:2069)
              at java.xml/com.sun.org.apache.xerces.internal.impl.xs.XMLSchemaValidator.startElement(XMLSchemaValidator.java:829)
              at java.xml/com.sun.org.apache.xerces.internal.impl.XMLNSDocumentScannerImpl.scanStartElement(XMLNSDocumentScannerImpl.java:374)
              at java.xml/com.sun.org.apache.xerces.internal.impl.XMLNSDocumentScannerImpl$NSContentDriver.scanRootElementHook(XMLNSDocumentScannerImpl.java:613)
              at java.xml/com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl$FragmentContentDriver.next(XMLDocumentFragmentScannerImpl.java:3078)
              at java.xml/com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl$PrologDriver.next(XMLDocumentScannerImpl.java:836)
              at java.xml/com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl.next(XMLDocumentScannerImpl.java:605)
              at java.xml/com.sun.org.apache.xerces.internal.impl.XMLNSDocumentScannerImpl.next(XMLNSDocumentScannerImpl.java:112)
              at java.xml/com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl.scanDocument(XMLDocumentFragmentScannerImpl.java:541)
              at java.xml/com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:888)
              at java.xml/com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:824)
              at java.xml/com.sun.org.apache.xerces.internal.jaxp.validation.StreamValidatorHelper.validate(StreamValidatorHelper.java:176)
              at java.xml/com.sun.org.apache.xerces.internal.jaxp.validation.ValidatorImpl.validate(ValidatorImpl.java:115)
              at foo.Foo.main(Foo.java:45)

      ---------- BEGIN SOURCE ----------
      package foo;

      import java.io.File;
      import java.io.StringWriter;

      import java.net.URL;
      import java.net.URI;
      import java.net.URISyntaxException;

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

      public class Foo
      {
        public static void main(String[] args)
        {
          final String catalogFile = CatalogFeatures.Feature.FILES.getPropertyName();
          final String catalogPath = "foo/catalog.xml";

          final ClassLoader classLoader = Foo.class.getClassLoader();

          try
          {
            final URL catalogUrl = classLoader.getResource(catalogPath);
            final URI catalog = catalogUrl.toURI();

            if (catalog != null)
            {
              SchemaFactory schemaFactory =
                SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
              Schema schema = schemaFactory.newSchema();

              StreamSource source = new StreamSource(new File("xyzzy.xml"));
              Validator validator = schema.newValidator();

              validator.setProperty(catalogFile, catalog.toString());

              StringWriter writer = new StringWriter();
              StreamResult result = new StreamResult(writer);
              validator.validate(source, result);

              System.out.println(writer);
            }
          }
          catch (Exception e)
          {
            e.printStackTrace();
          }
        }
      }

      Catalog file ./foo/catalog.xml
      <?xml version="1.0"?>
      <!DOCTYPE catalog
      PUBLIC "-//OASIS/DTD Entity Resolution XML Catalog V1.0//EN"
      "http://www.oasis-open.org/comittees/entity/release/1.0/catalog.dtd">

      <catalog xmlns="urn:oasis:names:tc:entity:xmlns:xml:catalog">
        <uri name="urn:foo:bar:xyzzy.xsd:0.1"
             uri="schemas/xyzzy.xsd"/>
      </catalog>

      XSD schema file ./foo/schemas/xyzzy.xsd
      <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
                 targetNamespace="urn:foo:bar"
                 xmlns:gazonk="urn:foo:bar"
                 elementFormDefault="qualified">
        <xs:element name="xyzzy">
          <xs:complexType/>
        </xs:element>
      </xs:schema>

      Input XML file ./xyzzy.xml
      <?xml version="1.0"?>
      <gazonk:xyzzy xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                    xmlns:gazonk="urn:foo:bar"
                    xsi:schemaLocation="urn:foo:bar:xyzzy.xsd:0.1"/>
      ---------- END SOURCE ----------

      CUSTOMER SUBMITTED WORKAROUND :
      Introduce wrapper class for CatalogResolver (./foo/Resolver.java)
      package foo;

      import java.io.InputStream;

      import javax.xml.catalog.CatalogResolver;
      import javax.xml.transform.Source;

      import org.w3c.dom.ls.LSInput;

      import org.xml.sax.InputSource;


      public class Resolver implements CatalogResolver
      {
        private final CatalogResolver m_resolver;


        public Resolver(CatalogResolver resolver)
        {
          if (resolver != null)
          {
            m_resolver = resolver;
          }
          else
          {
            String message = "Wrapped resolver must not be null.";
            throw new IllegalArgumentException(message);
          }
        }

        public Source resolve(String href, String base)
        {
          return m_resolver.resolve(href, base);
        }

        public InputSource resolveEntity(String publicId, String systemId)
        {
          // Ensure systemId is not null.
          return m_resolver.resolveEntity(publicId,
                                          (systemId == null)? "" : systemId);
        }

        public InputStream resolveEntity(String publicId,
                                         String systemId,
                                         String baseUri,
                                         String namespace)
        {
          // Ensure systemId is not null.
          return m_resolver.resolveEntity(publicId,
                                          (systemId == null)? "" : systemId,
                                          baseUri,
                                          namespace);
        }

        public LSInput resolveResource(String type,
                                       String namespaceUri,
                                       String publicId,
                                       String systemId,
                                       String baseUri)
        {
          // Ensure both publicId and systemId are not null at the same time
          // before passing it on to the real resolver.
          if ((publicId == null) && (systemId == null))
          {
            String message = ("Missing namespace and schema location pair, " +
                               "only have namespace URI '" + namespaceUri +
                               "' which is not enough to go on when trying to " +
                               "locate the schema file...");
            throw new NullPointerException(message);
          }

          // Ensure systemId is not null.
          return m_resolver.resolveResource(type,
                                            namespaceUri,
                                            publicId,
                                            (systemId == null)? "" : systemId,
                                            baseUri);
        }
      }

      and modified variant of ./foo/Foo.java called ./foo/Bar.java that uses foo.Resolver
      package foo;

      import java.io.File;
      import java.io.StringWriter;

      import java.net.URL;
      import java.net.URI;
      import java.net.URISyntaxException;

      import javax.xml.XMLConstants;
      import javax.xml.catalog.CatalogFeatures;
      import javax.xml.catalog.CatalogManager;
      import javax.xml.catalog.CatalogResolver;
      import javax.xml.transform.stream.StreamSource;
      import javax.xml.transform.stream.StreamResult;
      import javax.xml.validation.SchemaFactory;
      import javax.xml.validation.Schema;
      import javax.xml.validation.Validator;

      import org.w3c.dom.ls.LSInput;

      public class Bar
      {
        public static void main(String[] args)
        {
          final String catalogFile = CatalogFeatures.Feature.FILES.getPropertyName();
          final String catalogPath = "foo/catalog.xml";

          final ClassLoader classLoader = Foo.class.getClassLoader();

          try
          {
            final URL catalogUrl = classLoader.getResource(catalogPath);
            final URI catalog = catalogUrl.toURI();

            if (catalog != null)
            {
              CatalogFeatures features = CatalogFeatures.builder()
                //.with(Feature.FILES, catalog.toString())
                .with(CatalogFeatures.Feature.PREFER, "public")
                .with(CatalogFeatures.Feature.DEFER, "true")
                .with(CatalogFeatures.Feature.RESOLVE, "strict")
                .build();
              CatalogResolver resolver = CatalogManager.catalogResolver(features,
                                                                         catalog);
              Resolver wrapper = new Resolver(resolver);

              SchemaFactory schemaFactory =
                SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
              schemaFactory.setResourceResolver(wrapper);
              Schema schema = schemaFactory.newSchema();

              StreamSource source = new StreamSource(new File("xyzzy.xml"));
              Validator validator = schema.newValidator();

              validator.setProperty(catalogFile, catalog.toString());
              validator.setResourceResolver(wrapper);

              StringWriter writer = new StringWriter();
              StreamResult result = new StreamResult(writer);
              validator.validate(source, result);

              System.out.println(writer);
              /*
              */
            }
          }
          catch (Exception e)
          {
            e.printStackTrace();
          }
        }
      }


      FREQUENCY : always


        Attachments

          Activity

            People

            Assignee:
            joehw Joe Wang
            Reporter:
            webbuggrp Webbug Group
            Votes:
            0 Vote for this issue
            Watchers:
            3 Start watching this issue

              Dates

              Created:
              Updated:
              Resolved: