1 /*
   2  * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 package catalog;
  24 
  25 import java.io.File;
  26 import java.io.FileInputStream;
  27 import java.io.IOException;
  28 import java.io.InputStream;
  29 import java.io.StringReader;
  30 import java.io.StringWriter;
  31 import java.net.URI;
  32 import java.nio.file.Paths;
  33 import javax.xml.XMLConstants;
  34 import javax.xml.catalog.Catalog;
  35 import javax.xml.catalog.CatalogException;
  36 import javax.xml.catalog.CatalogFeatures;
  37 import javax.xml.catalog.CatalogFeatures.Feature;
  38 import javax.xml.catalog.CatalogManager;
  39 import javax.xml.catalog.CatalogResolver;
  40 import javax.xml.parsers.ParserConfigurationException;
  41 import javax.xml.parsers.SAXParser;
  42 import javax.xml.parsers.SAXParserFactory;
  43 import javax.xml.stream.XMLInputFactory;
  44 import javax.xml.stream.XMLStreamConstants;
  45 import javax.xml.stream.XMLStreamReader;
  46 import javax.xml.transform.Source;
  47 import javax.xml.transform.Transformer;
  48 import javax.xml.transform.TransformerFactory;
  49 import javax.xml.transform.sax.SAXSource;
  50 import javax.xml.transform.stream.StreamResult;
  51 import javax.xml.transform.stream.StreamSource;
  52 import javax.xml.validation.Schema;
  53 import javax.xml.validation.SchemaFactory;
  54 import javax.xml.validation.Validator;
  55 import static jaxp.library.JAXPTestUtilities.clearSystemProperty;
  56 import static jaxp.library.JAXPTestUtilities.setSystemProperty;
  57 import org.testng.Assert;
  58 import org.testng.annotations.BeforeClass;
  59 import org.testng.annotations.DataProvider;
  60 import org.testng.annotations.Listeners;
  61 import org.testng.annotations.Test;
  62 import org.xml.sax.Attributes;
  63 import org.xml.sax.ErrorHandler;
  64 import org.xml.sax.InputSource;
  65 import org.xml.sax.SAXException;
  66 import org.xml.sax.XMLReader;
  67 import org.xml.sax.ext.DefaultHandler2;
  68 
  69 /*
  70  * @test
  71  * @bug 8081248 8144966 8146606 8146237 8150969 8151162 8152527 8154220 8163232
  72  * @library /javax/xml/jaxp/libs /javax/xml/jaxp/unittest
  73  * @run testng/othervm -DrunSecMngr=true catalog.CatalogTest
  74  * @run testng/othervm catalog.CatalogTest
  75  * @summary Tests basic Catalog functions.
  76  */
  77 @Listeners({jaxp.library.FilePolicy.class})
  78 public class CatalogTest extends CatalogSupportBase {
  79     static final String KEY_FILES = "javax.xml.catalog.files";
  80 
  81 
  82     /*
  83      * Initializing fields
  84      */
  85     @BeforeClass
  86     public void setUpClass() throws Exception {
  87         super.setUp();
  88     }
  89 
  90     /*
  91      * @bug 8163232
  92      * Verifies that the CatalogResolver supports the following XML Resolvers:
  93           javax.xml.stream.XMLResolver
  94           javax.xml.transform.URIResolver
  95           org.w3c.dom.ls.LSResourceResolver
  96           org.xml.sax.EntityResolver
  97      *
  98      * Plus, system and uri entries can equally be used.
  99      */
 100 
 101     /*
 102      * Verifies the support for org.xml.sax.EntityResolver.
 103      * Expected: the parser returns the expected string.
 104     */
 105     @Test(dataProvider = "supportXMLResolver")
 106     public void supportEntityResolver(URI catalogFile, String xml, String expected) throws Exception {
 107         String xmlSource = getClass().getResource(xml).getFile();
 108 
 109         CatalogResolver cr = CatalogManager.catalogResolver(CatalogFeatures.defaults(), catalogFile);
 110         MyCatalogHandler handler = new MyCatalogHandler(cr, elementInSystem);
 111         SAXParser parser = getSAXParser(false, true, null);
 112         parser.parse(xmlSource, handler);
 113 
 114         Assert.assertEquals(handler.getResult().trim(), expected);
 115     }
 116 
 117     /*
 118      * Verifies the support for javax.xml.stream.XMLResolver.
 119      * Expected: the parser returns the expected string.
 120     */
 121     @Test(dataProvider = "supportXMLResolver")
 122     public void supportXMLResolver(URI catalogFile, String xml, String expected) throws Exception {
 123         String xmlSource = getClass().getResource(xml).getFile();
 124 
 125         CatalogResolver cr = CatalogManager.catalogResolver(CatalogFeatures.defaults(), catalogFile);
 126 
 127         XMLInputFactory xifactory = XMLInputFactory.newInstance();
 128         xifactory.setProperty(XMLInputFactory.IS_COALESCING, true);
 129         xifactory.setProperty(XMLInputFactory.RESOLVER, cr);
 130         File file = new File(xmlSource);
 131         String systemId = file.toURI().toASCIIString();
 132         InputStream entityxml = new FileInputStream(file);
 133         XMLStreamReader streamReader = xifactory.createXMLStreamReader(systemId, entityxml);
 134         String result = null;
 135         while (streamReader.hasNext()) {
 136             int eventType = streamReader.next();
 137             if (eventType == XMLStreamConstants.START_ELEMENT) {
 138                 eventType = streamReader.next();
 139                 if (eventType == XMLStreamConstants.CHARACTERS) {
 140                     result = streamReader.getText();
 141                 }
 142             }
 143         }
 144         System.out.println(": expected [" + expected + "] <> actual [" + result.trim() + "]");
 145 
 146         Assert.assertEquals(result.trim(), expected);
 147     }
 148 
 149     /*
 150      * Verifies the support for org.w3c.dom.ls.LSResourceResolver by ShemaFactory.
 151      * Success: parsing goes through with no error
 152      * Fail: throws Exception if references are not resolved (by the CatalogResolver)
 153     */
 154     @Test(dataProvider = "supportLSResourceResolver")
 155     public void supportLSResourceResolver(URI catalogFile, Source schemaSource) throws SAXException {
 156 
 157         CatalogResolver cr = CatalogManager.catalogResolver(CatalogFeatures.defaults(), catalogFile);
 158 
 159         SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
 160         factory.setResourceResolver(cr);
 161         Schema schema = factory.newSchema(schemaSource);
 162 
 163     }
 164 
 165     /*
 166      * Verifies the support for org.w3c.dom.ls.LSResourceResolver by Validator.
 167      * Success: parsing goes through with no error
 168      * Fail: throws Exception if references are not resolved (by the CatalogResolver)
 169     */
 170     @Test(dataProvider = "supportLSResourceResolver1")
 171     public void supportLSResourceResolver1(URI catalogFile, Source source) throws Exception {
 172 
 173         CatalogResolver cr = CatalogManager.catalogResolver(CatalogFeatures.defaults(), catalogFile);
 174 
 175         SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
 176         Validator validator = factory.newSchema().newValidator();
 177         validator.setResourceResolver(cr);
 178         validator.validate(source);
 179     }
 180 
 181     /*
 182      * Verifies the support for javax.xml.transform.URIResolver.
 183      * Success: parsing goes through with no error
 184      * Fail: throws Exception if references are not resolved (by the CatalogResolver)
 185     */
 186     @Test(dataProvider = "supportURIResolver")
 187     public void supportURIResolver(URI catalogFile, Source xsl, Source xml, String expected) throws Exception {
 188 
 189         CatalogResolver cr = CatalogManager.catalogResolver(CatalogFeatures.defaults(), catalogFile);
 190 
 191             TransformerFactory factory = TransformerFactory.newInstance();
 192             factory.setURIResolver(cr);
 193             Transformer transformer = factory.newTransformer(xsl);
 194             StringWriter out = new StringWriter();
 195             transformer.transform(xml, new StreamResult(out));
 196             if (expected != null) {
 197                 Assert.assertTrue(out.toString().contains(expected), "supportURIResolver");
 198             }
 199     }
 200 
 201     /*
 202        DataProvider: used to verify the support of XML Resolvers.
 203         Data columns:
 204         catalog filepath, xml source file, expected result
 205      */
 206     @DataProvider(name = "supportXMLResolver")
 207     public Object[][] supportXMLResolver() throws Exception {
 208         URI catalogFile = getClass().getResource("catalog.xml").toURI();
 209         URI catalogFileUri = getClass().getResource("catalog_uri.xml").toURI();
 210 
 211         return new Object[][]{
 212             {catalogFile, "system.xml", "Test system entry"},
 213             {catalogFile, "rewritesystem.xml", "Test rewritesystem entry"},
 214             {catalogFile, "rewritesystem1.xml", "Test rewritesystem entry"},
 215             {catalogFile, "systemsuffix.xml", "Test systemsuffix entry"},
 216             {catalogFile, "delegatesystem.xml", "Test delegatesystem entry"},
 217             {catalogFile, "public.xml", "Test public entry"},
 218             {catalogFile, "delegatepublic.xml", "Test delegatepublic entry"},
 219             // using uri entries
 220             {catalogFileUri, "system.xml", "Test system entry"},
 221             {catalogFileUri, "rewritesystem.xml", "Test rewritesystem entry"},
 222             {catalogFileUri, "rewritesystem1.xml", "Test rewritesystem entry"},
 223             {catalogFileUri, "systemsuffix.xml", "Test systemsuffix entry"},
 224             {catalogFileUri, "delegateuri.xml", "Test delegateuri entry"},
 225             {catalogFileUri, "public.xml", "Test public entry"},
 226          };
 227     }
 228 
 229     /*
 230        DataProvider: used to verify the support of LSResourceResolver by SchemaFactory.
 231         Data columns:
 232         catalog filepath, schema source file
 233      */
 234     @DataProvider(name = "supportLSResourceResolver")
 235     public Object[][] supportLSResourceResolver() throws Exception {
 236         URI catalogFile = getClass().getResource("CatalogSupport.xml").toURI();
 237         URI catalogFileUri = getClass().getResource("CatalogSupport_uri.xml").toURI();
 238 
 239         /*
 240          * XMLSchema.xsd has a reference to XMLSchema.dtd which in turn refers to
 241          * datatypes.dtd
 242         */
 243         return new Object[][]{
 244             {catalogFile, new StreamSource(new StringReader(xsd_xmlSchema))},
 245             {catalogFile, new StreamSource(new StringReader(xsd_xmlSchema_import))},
 246             {catalogFile, new StreamSource(new StringReader(xsd_include_company))},
 247             {catalogFileUri, new StreamSource(new StringReader(xsd_xmlSchema))},
 248             {catalogFileUri, new StreamSource(new StringReader(xsd_xmlSchema_import))},
 249             {catalogFileUri, new StreamSource(new StringReader(xsd_include_company))},
 250          };
 251     }
 252 
 253     /*
 254        DataProvider: used to verify the support of LSResourceResolver by Validator.
 255         Data columns:
 256         catalog filepath, source file
 257      */
 258     @DataProvider(name = "supportLSResourceResolver1")
 259     public Object[][] supportLSResourceResolver1() throws Exception {
 260         URI catalogFile = getClass().getResource("CatalogSupport.xml").toURI();
 261         URI catalogFileUri = getClass().getResource("CatalogSupport_uri.xml").toURI();
 262 
 263         /*
 264          * val_test.xml has a reference to system.dtd and val_test.xsd
 265         */
 266         SAXSource ss = new SAXSource(new InputSource(xml_val_test));
 267         ss.setSystemId(xml_val_test_id);
 268 
 269         return new Object[][]{
 270             {catalogFile, ss},
 271             {catalogFileUri, ss},
 272          };
 273     }
 274 
 275 
 276     /*
 277        DataProvider: used to verify the support of LSResourceResolver by Validator.
 278         Data columns:
 279         catalog filepath, xsl source, xml source file
 280      */
 281     @DataProvider(name = "supportURIResolver")
 282     public Object[][] supportURIResolver() throws Exception {
 283         URI catalogFile = getClass().getResource("CatalogSupport.xml").toURI();
 284         URI catalogFileUri = getClass().getResource("CatalogSupport_uri.xml").toURI();
 285         SAXSource xslSource = new SAXSource(new InputSource(new File(xsl_doc).toURI().toASCIIString()));
 286 
 287         /*
 288          * val_test.xml has a reference to system.dtd and val_test.xsd
 289         */
 290         SAXSource ss = new SAXSource(new InputSource(xml_val_test));
 291         ss.setSystemId(xml_val_test_id);
 292 
 293         return new Object[][]{
 294             {catalogFile, new SAXSource(new InputSource(new File(xsl_doc).toURI().toASCIIString())),
 295                 new StreamSource(new File(xml_doc)), "Resolved by a catalog"},
 296             {catalogFileUri, new SAXSource(new InputSource(new StringReader(xsl_include))),
 297                 new StreamSource(new StringReader(xml_xsl)), null},
 298          };
 299     }
 300 
 301     /*
 302      * @bug 8150187
 303      * NPE is expected if the systemId is null. The specification for systemId
 304      * is as follows:
 305      * A system identifier is required on all external entities. XML
 306      * requires a system identifier on all external entities, so this value is
 307      * always specified.
 308      */
 309     @Test(expectedExceptions = NullPointerException.class)
 310     public void sysIdCantBeNull() {
 311         CatalogResolver catalogResolver = CatalogManager.catalogResolver(CatalogFeatures.defaults());
 312         InputSource is = catalogResolver.resolveEntity("-//FOO//DTD XML Dummy V0.0//EN", null);
 313     }
 314 
 315     /*
 316      * @bug 8156845
 317      * Verifies that an URI reference with a urn:publicid is correctly resolved
 318      * with an uri entry with a publicId.
 319      *
 320      * @param expectedFile is not used in this test, it's kept since we're
 321      * copying the JCK test and its dataProvider. This test may be reused for
 322      * other cases in that test.
 323      */
 324     @Test(dataProvider = "resolveUri")
 325     public void testMatch1(String cFile, String href, String expectedFile,
 326             String expectedUri, String msg) throws Exception {
 327         URI catalogFile = getClass().getResource(cFile).toURI();
 328         CatalogResolver cur = CatalogManager.catalogResolver(CatalogFeatures.defaults(), catalogFile);
 329         Source source = cur.resolve(href, null);
 330         Assert.assertNotNull(source, "Source returned is null");
 331         Assert.assertEquals(expectedUri, source.getSystemId(), msg);
 332     }
 333 
 334     /*
 335      * @bug 8154220
 336      * Verifies that the file input is validated properly. Valid input includes
 337      * multiple file paths separated by semicolon.
 338      */
 339     @Test(dataProvider = "hierarchyOfCatFilesData")
 340     public void hierarchyOfCatFiles2(String systemId, String expectedUri) {
 341         String file1 = getClass().getResource("first_cat.xml").toExternalForm();
 342         String file2 = getClass().getResource("second_cat.xml").toExternalForm();
 343         String files = file1 + ";" + file2;
 344 
 345         try {
 346             setSystemProperty(KEY_FILES, files);
 347             CatalogResolver catalogResolver = CatalogManager.catalogResolver(CatalogFeatures.defaults());
 348             String sysId = catalogResolver.resolveEntity(null, systemId).getSystemId();
 349             Assert.assertEquals(sysId, Paths.get(filepath + expectedUri).toUri().toString().replace("///", "/"),
 350                     "System ID match not right");
 351         } finally {
 352             clearSystemProperty(KEY_FILES);
 353         }
 354 
 355     }
 356 
 357     /*
 358      * @bug 8152527
 359      * This test is the same as the JDK test ResolveEntityTests:testMatch1.
 360      * Verifies that the CatalogResolver resolves a publicId and/or systemId as
 361      * expected.
 362      */
 363     @Test(dataProvider = "resolveEntity")
 364     public void testMatch1(String cfile, String prefer, String sysId, String pubId,
 365             String expectedUri, String expectedFile, String msg) throws Exception {
 366         URI catalogFile = getClass().getResource(cfile).toURI();
 367         CatalogFeatures features = CatalogFeatures.builder().with(CatalogFeatures.Feature.PREFER, prefer).build();
 368         CatalogResolver catalogResolver = CatalogManager.catalogResolver(features, catalogFile);
 369         InputSource is = catalogResolver.resolveEntity(pubId, sysId);
 370         Assert.assertNotNull(is, msg);
 371         String expected = (expectedUri == null) ? expectedFile : expectedUri;
 372         Assert.assertEquals(expected, is.getSystemId(), msg);
 373     }
 374 
 375     /*
 376      * @bug 8151162
 377      * Verifies that the Catalog matches specified publicId or systemId and returns
 378      * results as expected.
 379      */
 380     @Test(dataProvider = "matchWithPrefer")
 381     public void matchWithPrefer(String prefer, String cfile, String publicId,
 382             String systemId, String expected) throws Exception {
 383         URI catalogFile = getClass().getResource(cfile).toURI();
 384         Catalog c = CatalogManager.catalog(
 385                 CatalogFeatures.builder().with(CatalogFeatures.Feature.PREFER, prefer).build(),
 386                 catalogFile);
 387         String result;
 388         if (publicId != null && publicId.length() > 0) {
 389             result = c.matchPublic(publicId);
 390         } else {
 391             result = c.matchSystem(systemId);
 392         }
 393         Assert.assertEquals(expected, result);
 394     }
 395 
 396     /*
 397      * @bug 8151162
 398      * Verifies that the CatalogResolver resolves specified publicId or systemId
 399      * in accordance with the prefer setting.
 400      * prefer "system": resolves with a system entry.
 401      *                  Exception: use the public entry when the catalog contains
 402      *                  only public entry and only publicId is specified.
 403      * prefer "public": attempts to resolve with a system entry;
 404      *                  attempts to resolve with a public entry if no matching
 405      *                  system entry is found.
 406      */
 407     @Test(dataProvider = "resolveWithPrefer")
 408     public void resolveWithPrefer(String prefer, String cfile, String publicId,
 409             String systemId, String expected) throws Exception {
 410         URI catalogFile = getClass().getResource(cfile).toURI();
 411         CatalogFeatures f = CatalogFeatures.builder()
 412                 .with(CatalogFeatures.Feature.PREFER, prefer)
 413                 .with(CatalogFeatures.Feature.RESOLVE, "ignore")
 414                 .build();
 415         CatalogResolver catalogResolver = CatalogManager.catalogResolver(f, catalogFile);
 416         String result = catalogResolver.resolveEntity(publicId, systemId).getSystemId();
 417         Assert.assertEquals(expected, result);
 418     }
 419 
 420     /**
 421      * @bug 8150969
 422      * Verifies that the defer attribute set in the catalog file takes precedence
 423      * over other settings, in which case, whether next and delegate Catalogs will
 424      * be loaded is determined by the defer attribute.
 425      */
 426     @Test(dataProvider = "invalidAltCatalogs", expectedExceptions = CatalogException.class)
 427     public void testDeferAltCatalogs(String file) throws Exception {
 428         URI catalogFile = getClass().getResource(file).toURI();
 429         CatalogFeatures features = CatalogFeatures.builder().
 430                 with(CatalogFeatures.Feature.DEFER, "true")
 431                 .build();
 432         /*
 433           Since the defer attribute is set to false in the specified catalog file,
 434           the parent catalog will try to load the alt catalog, which will fail
 435           since it points to an invalid catalog.
 436         */
 437         Catalog catalog = CatalogManager.catalog(features, catalogFile);
 438     }
 439 
 440 
 441     /**
 442      * @bug 8146237
 443      * PREFER from Features API taking precedence over catalog file
 444      */
 445     @Test
 446     public void testJDK8146237() throws Exception {
 447         URI catalogFile = getClass().getResource("JDK8146237_catalog.xml").toURI();
 448 
 449         try {
 450             CatalogFeatures features = CatalogFeatures.builder()
 451                     .with(CatalogFeatures.Feature.PREFER, "system")
 452                     .build();
 453             Catalog catalog = CatalogManager.catalog(features, catalogFile);
 454             CatalogResolver catalogResolver = CatalogManager.catalogResolver(catalog);
 455             String actualSystemId = catalogResolver.resolveEntity(
 456                     "-//FOO//DTD XML Dummy V0.0//EN",
 457                     "http://www.oracle.com/alt1sys.dtd")
 458                     .getSystemId();
 459             Assert.assertTrue(actualSystemId.contains("dummy.dtd"),
 460                     "Resulting id should contain dummy.dtd, indicating a match by publicId");
 461 
 462         } catch (Exception e) {
 463             Assert.fail(e.getMessage());
 464         }
 465     }
 466 
 467     /*
 468        @bug 8146606
 469        Verifies that the resulting systemId does not contain duplicate slashes
 470     */
 471     @Test
 472     public void testRewriteSystem() throws Exception {
 473         URI catalog = getClass().getResource("rewriteCatalog.xml").toURI();
 474 
 475         try {
 476             CatalogResolver resolver = CatalogManager.catalogResolver(CatalogFeatures.defaults(), catalog);
 477             String actualSystemId = resolver.resolveEntity(null, "http://remote.com/dtd/book.dtd").getSystemId();
 478             Assert.assertTrue(!actualSystemId.contains("//"), "result contains duplicate slashes");
 479         } catch (Exception e) {
 480             Assert.fail(e.getMessage());
 481         }
 482 
 483     }
 484 
 485     /*
 486        @bug 8146606
 487        Verifies that the resulting systemId does not contain duplicate slashes
 488     */
 489     @Test
 490     public void testRewriteUri() throws Exception {
 491         URI catalog = getClass().getResource("rewriteCatalog.xml").toURI();
 492 
 493         try {
 494 
 495             CatalogResolver resolver = CatalogManager.catalogResolver(CatalogFeatures.defaults(), catalog);
 496             String actualSystemId = resolver.resolve("http://remote.com/import/import.xsl", null).getSystemId();
 497             Assert.assertTrue(!actualSystemId.contains("//"), "result contains duplicate slashes");
 498         } catch (Exception e) {
 499             Assert.fail(e.getMessage());
 500         }
 501     }
 502 
 503     /*
 504        @bug 8144966
 505        Verifies that passing null as CatalogFeatures will result in a NPE.
 506     */
 507     @Test(expectedExceptions = NullPointerException.class)
 508     public void testFeatureNull() {
 509         CatalogResolver resolver = CatalogManager.catalogResolver(null, null);
 510 
 511     }
 512 
 513     /*
 514        @bug 8144966
 515        Verifies that passing null as the URI will result in a NPE.
 516     */
 517     @Test(expectedExceptions = NullPointerException.class)
 518     public void testPathNull() {
 519         URI uri = null;
 520         CatalogResolver resolver = CatalogManager.catalogResolver(CatalogFeatures.defaults(), uri);
 521     }
 522 
 523     /*
 524        Tests basic catalog feature by using a CatalogResolver instance to
 525     resolve a DTD reference to a locally specified DTD file. If the resolution
 526     is successful, the Handler shall return the value of the entity reference
 527     that matches the expected value.
 528      */
 529     @Test(dataProvider = "catalog")
 530     public void testCatalogResolver(String test, String expected, String catalogFile,
 531             String xml, SAXParser saxParser) throws Exception {
 532         URI catalog = null;
 533         if (catalogFile != null) {
 534             catalog = getClass().getResource(catalogFile).toURI();
 535         }
 536         String url = getClass().getResource(xml).getFile();
 537         try {
 538             CatalogResolver cr = CatalogManager.catalogResolver(CatalogFeatures.defaults(), catalog);
 539             XMLReader reader = saxParser.getXMLReader();
 540             reader.setEntityResolver(cr);
 541             MyHandler handler = new MyHandler(saxParser);
 542             reader.setContentHandler(handler);
 543             reader.parse(url);
 544             System.out.println(test + ": expected [" + expected + "] <> actual [" + handler.getResult() + "]");
 545             Assert.assertEquals(handler.getResult(), expected);
 546         } catch (SAXException | IOException e) {
 547             Assert.fail(e.getMessage());
 548         }
 549     }
 550 
 551     /*
 552        Verifies that when there's no match, in this case only an invalid
 553     catalog is provided, the resolver will throw an exception by default.
 554     */
 555     @Test
 556     public void testInvalidCatalog() throws Exception {
 557         String expectedMsgId = "JAXP09040001";
 558         URI catalog = getClass().getResource("catalog_invalid.xml").toURI();
 559 
 560         try {
 561             CatalogResolver resolver = CatalogManager.catalogResolver(
 562                     CatalogFeatures.defaults(), catalog);
 563             String actualSystemId = resolver.resolveEntity(
 564                     null,
 565                     "http://remote/xml/dtd/sys/alice/docAlice.dtd")
 566                     .getSystemId();
 567         } catch (Exception e) {
 568             String msg = e.getMessage();
 569             if (msg != null) {
 570                 Assert.assertTrue(msg.contains(expectedMsgId),
 571                         "Message shall contain the corrent message ID " + expectedMsgId);
 572             }
 573         }
 574     }
 575 
 576     /*
 577        Verifies that if resolve is "ignore", an empty InputSource will be returned
 578     when there's no match. The systemId is then null.
 579     */
 580     @Test
 581     public void testIgnoreInvalidCatalog() {
 582         String catalog = getClass().getResource("catalog_invalid.xml").toExternalForm();
 583         CatalogFeatures f = CatalogFeatures.builder()
 584                 .with(Feature.FILES, catalog)
 585                 .with(Feature.PREFER, "public")
 586                 .with(Feature.DEFER, "true")
 587                 .with(Feature.RESOLVE, "ignore")
 588                 .build();
 589 
 590         String test = "testInvalidCatalog";
 591         try {
 592             CatalogResolver resolver = CatalogManager.catalogResolver(f);
 593             String actualSystemId = resolver.resolveEntity(
 594                     null,
 595                     "http://remote/xml/dtd/sys/alice/docAlice.dtd")
 596                     .getSystemId();
 597             System.out.println("testIgnoreInvalidCatalog: expected [null]");
 598             System.out.println("testIgnoreInvalidCatalog: expected [null]");
 599             System.out.println("actual [" + actualSystemId + "]");
 600             Assert.assertEquals(actualSystemId, null);
 601         } catch (Exception e) {
 602             Assert.fail(e.getMessage());
 603         }
 604     }
 605 
 606 
 607     /*
 608         DataProvider: used to verify CatalogResolver's resolve function.
 609         Data columns:
 610         catalog, uri or publicId, expectedFile, expectedUri, msg
 611 
 612         This DataProvider is copied from JCK ResolveTests' dataMatch1
 613      */
 614     @DataProvider(name = "resolveUri")
 615     public Object[][] getDataForUriResolver() {
 616         return new Object[][]{
 617             {"uri.xml",
 618                 "urn:publicid:-:Acme,+Inc.:DTD+Book+Version+1.0",
 619                 null,
 620                 "http://local/base/dtd/book.dtd",
 621                 "Uri in publicId namespace is incorrectly unwrapped"},
 622         };
 623     }
 624 
 625     /*
 626         DataProvider: used to verify hierarchical catalogs. Refer to JCK test
 627     hierarchyOfCatFiles2.
 628      */
 629     @DataProvider(name = "hierarchyOfCatFilesData")
 630     public Object[][] getHierarchyOfCatFilesData() {
 631         return new Object[][]{
 632             {"http://www.oracle.com/sequence.dtd", "first.dtd"},
 633             {"http://www.oracle.com/sequence_next.dtd", "next.dtd"},
 634             {"http://www.oracle.com/sequence_second.dtd", "second.dtd"}
 635         };
 636     }
 637 
 638     /*
 639         DataProvider: used to verify CatalogResolver's resolveEntity function.
 640         Data columns:
 641         catalog, prefer, systemId, publicId, expectedUri, expectedFile, msg
 642      */
 643     @DataProvider(name = "resolveEntity")
 644     public Object[][] getDataForMatchingBothIds() {
 645         String expected = "http://www.groupxmlbase.com/dtds/rewrite.dtd";
 646         return new Object[][]{
 647             {"rewriteSystem_id.xml",
 648                 "system",
 649                 "http://www.sys00test.com/rewrite.dtd",
 650                 "PUB-404",
 651                 expected,
 652                 expected,
 653                 "Relative rewriteSystem with xml:base at group level failed"},
 654         };
 655     }
 656 
 657     static String id = "http://openjdk.java.net/xml/catalog/dtd/system.dtd";
 658     /*
 659        DataProvider: used to verify how prefer settings affect the result of the
 660         Catalog's matching operation.
 661         Data columns:
 662         prefer, catalog, publicId, systemId, expected result
 663      */
 664     @DataProvider(name = "matchWithPrefer")
 665     public Object[][] getDataForMatch() {
 666         return new Object[][]{
 667             {"public", "pubOnly.xml", id, "", "http://local/base/dtd/public.dtd"},
 668             {"public", "sysOnly.xml", id, "", null},
 669             {"public", "sysAndPub.xml", id, "", "http://local/base/dtd/public.dtd"},
 670             {"system", "pubOnly.xml", id, "", "http://local/base/dtd/public.dtd"},
 671             {"system", "sysOnly.xml", id, "", null},
 672             {"system", "sysAndPub.xml", id, "", "http://local/base/dtd/public.dtd"},
 673             {"public", "pubOnly.xml", "", id, null},
 674             {"public", "sysOnly.xml", "", id, "http://local/base/dtd/system.dtd"},
 675             {"public", "sysAndPub.xml", "", id, "http://local/base/dtd/system.dtd"},
 676             {"system", "pubOnly.xml", "", id, null},
 677             {"system", "sysOnly.xml", "", id, "http://local/base/dtd/system.dtd"},
 678             {"system", "sysAndPub.xml", "", id, "http://local/base/dtd/system.dtd"},
 679         };
 680     }
 681 
 682     /*
 683        DataProvider: used to verify how prefer settings affect the result of the
 684         CatalogResolver's resolution operation.
 685         Data columns:
 686         prefer, catalog, publicId, systemId, expected result
 687      */
 688     @DataProvider(name = "resolveWithPrefer")
 689     public Object[][] getDataForResolve() {
 690         return new Object[][]{
 691             {"system", "pubOnly.xml", id, "", "http://local/base/dtd/public.dtd"},
 692             {"system", "pubOnly.xml", "", id, null},
 693             {"system", "pubOnly.xml", id, id, null},
 694             {"public", "pubOnly.xml", id, "", "http://local/base/dtd/public.dtd"},
 695             {"public", "pubOnly.xml", "", id, null},
 696             {"public", "pubOnly.xml", id, id, "http://local/base/dtd/public.dtd"},
 697             {"system", "sysOnly.xml", id, "", null},
 698             {"system", "sysOnly.xml", "", id, "http://local/base/dtd/system.dtd"},
 699             {"system", "sysOnly.xml", id, id, "http://local/base/dtd/system.dtd"},
 700             {"public", "sysOnly.xml", id, "", null},
 701             {"public", "sysOnly.xml", "", id, "http://local/base/dtd/system.dtd"},
 702             {"public", "sysOnly.xml", id, id, "http://local/base/dtd/system.dtd"},
 703             {"system", "sysAndPub.xml", id, "", "http://local/base/dtd/public.dtd"},
 704             {"system", "sysAndPub.xml", "", id, "http://local/base/dtd/system.dtd"},
 705             {"system", "sysAndPub.xml", id, id, "http://local/base/dtd/system.dtd"},
 706             {"public", "sysAndPub.xml", id, "", "http://local/base/dtd/public.dtd"},
 707             {"public", "sysAndPub.xml", "", id, "http://local/base/dtd/system.dtd"},
 708             {"public", "sysAndPub.xml", id, id, "http://local/base/dtd/system.dtd"},
 709         };
 710     }
 711     /*
 712        DataProvider: catalogs that contain invalid next or delegate catalogs.
 713                      The defer attribute is set to false.
 714      */
 715     @DataProvider(name = "invalidAltCatalogs")
 716     public Object[][] getCatalogs() {
 717         return new Object[][]{
 718             {"defer_false_2.xml"},
 719             {"defer_del_false.xml"}
 720         };
 721     }
 722 
 723 
 724     /*
 725        DataProvider: provides test name, expected string, the catalog, and XML
 726        document.
 727      */
 728     @DataProvider(name = "catalog")
 729     public Object[][] getCatalog() {
 730         return new Object[][]{
 731             {"testSystem", "Test system entry", "catalog.xml", "system.xml", getParser()},
 732             {"testRewriteSystem", "Test rewritesystem entry", "catalog.xml", "rewritesystem.xml", getParser()},
 733             {"testRewriteSystem1", "Test rewritesystem entry", "catalog.xml", "rewritesystem1.xml", getParser()},
 734             {"testSystemSuffix", "Test systemsuffix entry", "catalog.xml", "systemsuffix.xml", getParser()},
 735             {"testDelegateSystem", "Test delegatesystem entry", "catalog.xml", "delegatesystem.xml", getParser()},
 736             {"testPublic", "Test public entry", "catalog.xml", "public.xml", getParser()},
 737             {"testDelegatePublic", "Test delegatepublic entry", "catalog.xml", "delegatepublic.xml", getParser()},
 738         };
 739     }
 740 
 741     SAXParser getParser() {
 742         SAXParser saxParser = null;
 743         try {
 744             SAXParserFactory factory = SAXParserFactory.newInstance();
 745             factory.setNamespaceAware(true);
 746             saxParser = factory.newSAXParser();
 747         } catch (ParserConfigurationException | SAXException e) {
 748         }
 749 
 750         return saxParser;
 751     }
 752 
 753     /**
 754      * SAX handler
 755      */
 756     public class MyHandler extends DefaultHandler2 implements ErrorHandler {
 757 
 758         StringBuilder textContent = new StringBuilder();
 759         SAXParser saxParser;
 760 
 761         MyHandler(SAXParser saxParser) {
 762             textContent.setLength(0);
 763             this.saxParser = saxParser;
 764         }
 765 
 766         String getResult() {
 767             return textContent.toString();
 768         }
 769 
 770         @Override
 771         public void startElement(String uri, String localName, String qName, Attributes attributes)
 772                 throws SAXException {
 773             textContent.delete(0, textContent.length());
 774             try {
 775                 System.out.println("Element: " + uri + ":" + localName + " " + qName);
 776             } catch (Exception e) {
 777                 throw new SAXException(e);
 778             }
 779 
 780         }
 781 
 782         @Override
 783         public void characters(char ch[], int start, int length) throws SAXException {
 784             textContent.append(ch, start, length);
 785         }
 786     }
 787 }