1 /* 2 * Copyright (c) 2015, 2016, 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.IOException; 26 import javax.xml.catalog.Catalog; 27 import javax.xml.catalog.CatalogException; 28 import javax.xml.catalog.CatalogFeatures; 29 import javax.xml.catalog.CatalogFeatures.Feature; 30 import javax.xml.catalog.CatalogManager; 31 import javax.xml.catalog.CatalogResolver; 32 import javax.xml.catalog.CatalogUriResolver; 33 import javax.xml.parsers.ParserConfigurationException; 34 import javax.xml.parsers.SAXParser; 35 import javax.xml.parsers.SAXParserFactory; 36 import org.testng.Assert; 37 import org.testng.annotations.DataProvider; 38 import org.testng.annotations.Test; 39 import org.xml.sax.Attributes; 40 import org.xml.sax.ErrorHandler; 41 import org.xml.sax.InputSource; 42 import org.xml.sax.SAXException; 43 import org.xml.sax.XMLReader; 44 import org.xml.sax.ext.DefaultHandler2; 45 46 /* 47 * @bug 8081248, 8144966, 8146606, 8146237, 8151154, 8150969, 8151162, 8152527 48 * @summary Tests basic Catalog functions. 49 */ 50 public class CatalogTest { 51 /* 52 * @bug 8152527 53 * This test is the same as the JDK test ResolveEntityTests:testMatch1. 54 * Verifies that the CatalogResolver resolves a publicId and/or systemId as 55 * expected. 56 */ 57 @Test(dataProvider = "resolveEntity") 58 public void testMatch1(String cfile, String prefer, String sysId, String pubId, String expectedUri, String expectedFile, String msg) { 59 String catalogFile = getClass().getResource(cfile).getFile(); 60 CatalogFeatures features = CatalogFeatures.builder().with(CatalogFeatures.Feature.PREFER, prefer).build(); 61 CatalogResolver catalogResolver = CatalogManager.catalogResolver(features, catalogFile); 62 InputSource is = catalogResolver.resolveEntity(pubId, sysId); 63 Assert.assertNotNull(is, msg); 64 String expected = (expectedUri == null) ? expectedFile : expectedUri; 65 Assert.assertEquals(expected, is.getSystemId(), msg); 66 } 67 68 /* 69 * @bug 8151162 70 * Verifies that the Catalog matches specified publicId or systemId and returns 71 * results as expected. 72 */ 73 @Test(dataProvider = "matchWithPrefer") 74 public void matchWithPrefer(String prefer, String cfile, String publicId, String systemId, String expected) { 75 String catalogFile = getClass().getResource(cfile).getFile(); 76 Catalog c = CatalogManager.catalog(CatalogFeatures.builder().with(CatalogFeatures.Feature.PREFER, prefer).build(), catalogFile); 77 String result; 78 if (publicId != null && publicId.length() > 0) { 79 result = c.matchPublic(publicId); 80 } else { 81 result = c.matchSystem(systemId); 82 } 83 Assert.assertEquals(expected, result); 84 } 85 86 /* 87 * @bug 8151162 88 * Verifies that the CatalogResolver resolves specified publicId or systemId 89 * in accordance with the prefer setting. 90 * prefer "system": resolves with a system entry. 91 * Exception: use the public entry when the catalog contains 92 * only public entry and only publicId is specified. 93 * prefer "public": attempts to resolve with a system entry; 94 * attempts to resolve with a public entry if no matching 95 * system entry is found. 96 */ 97 @Test(dataProvider = "resolveWithPrefer") 98 public void resolveWithPrefer(String prefer, String cfile, String publicId, String systemId, String expected) { 99 String catalogFile = getClass().getResource(cfile).getFile(); 100 CatalogFeatures f = CatalogFeatures.builder().with(CatalogFeatures.Feature.PREFER, prefer).with(CatalogFeatures.Feature.RESOLVE, "ignore").build(); 101 CatalogResolver catalogResolver = CatalogManager.catalogResolver(f, catalogFile); 102 String result = catalogResolver.resolveEntity(publicId, systemId).getSystemId(); 103 Assert.assertEquals(expected, result); 104 } 105 106 /** 107 * @bug 8150969 108 * Verifies that the defer attribute set in the catalog file takes precedence 109 * over other settings, in which case, whether next and delegate Catalogs will 110 * be loaded is determined by the defer attribute. 111 */ 112 @Test(dataProvider = "invalidAltCatalogs", expectedExceptions = CatalogException.class) 113 public void testDeferAltCatalogs(String file) { 114 String catalogFile = getClass().getResource(file).getFile(); 115 CatalogFeatures features = CatalogFeatures.builder().with(CatalogFeatures.Feature.DEFER, "true").build(); 116 /* 117 Since the defer attribute is set to false in the specified catalog file, 118 the parent catalog will try to load the alt catalog, which will fail 119 since it points to an invalid catalog. 120 */ 121 Catalog catalog = CatalogManager.catalog(features, catalogFile); 122 } 123 124 /** 125 * @bug 8151154 126 * Verifies that the CatalogFeatures' builder throws IllegalArgumentException 127 * on invalid file inputs. 128 * @param file the file path 129 */ 130 @Test(dataProvider = "invalidPaths", expectedExceptions = IllegalArgumentException.class) 131 public void testFileInput(String file) { 132 CatalogFeatures features = CatalogFeatures.builder() 133 .with(CatalogFeatures.Feature.FILES, file) 134 .build(); 135 } 136 137 /** 138 * @bug 8146237 139 * PREFER from Features API taking precedence over catalog file 140 */ 141 @Test 142 public void testJDK8146237() { 143 String catalogFile = getClass().getResource("JDK8146237_catalog.xml").getFile(); 144 145 try { 146 CatalogFeatures features = CatalogFeatures.builder().with(CatalogFeatures.Feature.PREFER, "system").build(); 147 Catalog catalog = CatalogManager.catalog(features, catalogFile); 148 CatalogResolver catalogResolver = CatalogManager.catalogResolver(catalog); 149 String actualSystemId = catalogResolver.resolveEntity("-//FOO//DTD XML Dummy V0.0//EN", "http://www.oracle.com/alt1sys.dtd").getSystemId(); 150 Assert.assertTrue(actualSystemId.contains("dummy.dtd"), "Resulting id should contain dummy.dtd, indicating a match by publicId"); 151 152 } catch (Exception e) { 153 Assert.fail(e.getMessage()); 154 } 155 } 156 157 /* 158 @bug 8146606 159 Verifies that the resulting systemId does not contain duplicate slashes 160 */ 161 @Test 162 public void testRewriteSystem() { 163 String catalog = getClass().getResource("rewriteCatalog.xml").getFile(); 164 165 try { 166 CatalogResolver resolver = CatalogManager.catalogResolver(CatalogFeatures.defaults(), catalog); 167 String actualSystemId = resolver.resolveEntity(null, "http://remote.com/dtd/book.dtd").getSystemId(); 168 Assert.assertTrue(!actualSystemId.contains("//"), "result contains duplicate slashes"); 169 } catch (Exception e) { 170 Assert.fail(e.getMessage()); 171 } 172 173 } 174 175 /* 176 @bug 8146606 177 Verifies that the resulting systemId does not contain duplicate slashes 178 */ 179 @Test 180 public void testRewriteUri() { 181 String catalog = getClass().getResource("rewriteCatalog.xml").getFile(); 182 183 try { 184 185 CatalogUriResolver resolver = CatalogManager.catalogUriResolver(CatalogFeatures.defaults(), catalog); 186 String actualSystemId = resolver.resolve("http://remote.com/import/import.xsl", null).getSystemId(); 187 Assert.assertTrue(!actualSystemId.contains("//"), "result contains duplicate slashes"); 188 } catch (Exception e) { 189 Assert.fail(e.getMessage()); 190 } 191 } 192 193 /* 194 @bug 8144966 195 Verifies that passing null as CatalogFeatures will result in a NPE. 196 */ 197 @Test(expectedExceptions = NullPointerException.class) 198 public void testFeatureNull() { 199 CatalogResolver resolver = CatalogManager.catalogResolver(null, ""); 200 201 } 202 203 /* 204 @bug 8144966 205 Verifies that passing null as the path will result in a NPE. 206 */ 207 @Test(expectedExceptions = NullPointerException.class) 208 public void testPathNull() { 209 String path = null; 210 CatalogResolver resolver = CatalogManager.catalogResolver(CatalogFeatures.defaults(), path); 211 } 212 213 /* 214 Tests basic catalog feature by using a CatalogResolver instance to 215 resolve a DTD reference to a locally specified DTD file. If the resolution 216 is successful, the Handler shall return the value of the entity reference 217 that matches the expected value. 218 */ 219 @Test(dataProvider = "catalog") 220 public void testCatalogResolver(String test, String expected, String catalogFile, String xml, SAXParser saxParser) { 221 String catalog = null; 222 if (catalogFile != null) { 223 catalog = getClass().getResource(catalogFile).getFile(); 224 } 225 String url = getClass().getResource(xml).getFile(); 226 try { 227 CatalogResolver cr = CatalogManager.catalogResolver(CatalogFeatures.defaults(), catalog); 228 XMLReader reader = saxParser.getXMLReader(); 229 reader.setEntityResolver(cr); 230 MyHandler handler = new MyHandler(saxParser); 231 reader.setContentHandler(handler); 232 reader.parse(url); 233 System.out.println(test + ": expected [" + expected + "] <> actual [" + handler.getResult() + "]"); 234 Assert.assertEquals(handler.getResult(), expected); 235 } catch (SAXException | IOException e) { 236 Assert.fail(e.getMessage()); 237 } 238 } 239 240 /* 241 Verifies that when there's no match, in this case only an invalid 242 catalog is provided, the resolver will throw an exception by default. 243 */ 244 @Test 245 public void testInvalidCatalog() { 246 String catalog = getClass().getResource("catalog_invalid.xml").getFile(); 247 248 String test = "testInvalidCatalog"; 249 try { 250 CatalogResolver resolver = CatalogManager.catalogResolver(CatalogFeatures.defaults(), catalog); 251 String actualSystemId = resolver.resolveEntity(null, "http://remote/xml/dtd/sys/alice/docAlice.dtd").getSystemId(); 252 } catch (Exception e) { 253 String msg = e.getMessage(); 254 if (msg != null) { 255 if (msg.contains("No match found for publicId")) { 256 Assert.assertEquals(msg, "No match found for publicId 'null' and systemId 'http://remote/xml/dtd/sys/alice/docAlice.dtd'."); 257 System.out.println(test + ": expected [No match found for publicId 'null' and systemId 'http://remote/xml/dtd/sys/alice/docAlice.dtd'.]"); 258 System.out.println("actual [" + msg + "]"); 259 } 260 } 261 } 262 } 263 264 /* 265 Verifies that if resolve is "ignore", an empty InputSource will be returned 266 when there's no match. The systemId is then null. 267 */ 268 @Test 269 public void testIgnoreInvalidCatalog() { 270 String catalog = getClass().getResource("catalog_invalid.xml").getFile(); 271 CatalogFeatures f = CatalogFeatures.builder() 272 .with(Feature.FILES, catalog) 273 .with(Feature.PREFER, "public") 274 .with(Feature.DEFER, "true") 275 .with(Feature.RESOLVE, "ignore") 276 .build(); 277 278 String test = "testInvalidCatalog"; 279 try { 280 CatalogResolver resolver = CatalogManager.catalogResolver(f, ""); 281 String actualSystemId = resolver.resolveEntity(null, "http://remote/xml/dtd/sys/alice/docAlice.dtd").getSystemId(); 282 System.out.println("testIgnoreInvalidCatalog: expected [null]"); 283 System.out.println("testIgnoreInvalidCatalog: expected [null]"); 284 System.out.println("actual [" + actualSystemId + "]"); 285 Assert.assertEquals(actualSystemId, null); 286 } catch (Exception e) { 287 Assert.fail(e.getMessage()); 288 } 289 } 290 291 /* 292 DataProvider: used to verify CatalogResolver's resolveEntity function. 293 Data columns: 294 catalog, prefer, systemId, publicId, expectedUri, expectedFile, msg 295 */ 296 @DataProvider(name = "resolveEntity") 297 Object[][] getDataForMatchingBothIds() { 298 String expected = "http://www.groupxmlbase.com/dtds/rewrite.dtd"; 299 return new Object[][]{ 300 {"rewriteSystem_id.xml", "system", "http://www.sys00test.com/rewrite.dtd", "PUB-404", expected, expected, "Relative rewriteSystem with xml:base at group level failed"}, 301 }; 302 } 303 static String id = "http://openjdk.java.net/xml/catalog/dtd/system.dtd"; 304 /* 305 DataProvider: used to verify how prefer settings affect the result of the 306 Catalog's matching operation. 307 Data columns: 308 prefer, catalog, publicId, systemId, expected result 309 */ 310 @DataProvider(name = "matchWithPrefer") 311 Object[][] getDataForMatch() { 312 return new Object[][]{ 313 {"public", "pubOnly.xml", id, "", "http://local/base/dtd/public.dtd"}, 314 {"public", "sysOnly.xml", id, "", null}, 315 {"public", "sysAndPub.xml", id, "", "http://local/base/dtd/public.dtd"}, 316 {"system", "pubOnly.xml", id, "", "http://local/base/dtd/public.dtd"}, 317 {"system", "sysOnly.xml", id, "", null}, 318 {"system", "sysAndPub.xml", id, "", "http://local/base/dtd/public.dtd"}, 319 {"public", "pubOnly.xml", "", id, null}, 320 {"public", "sysOnly.xml", "", id, "http://local/base/dtd/system.dtd"}, 321 {"public", "sysAndPub.xml", "", id, "http://local/base/dtd/system.dtd"}, 322 {"system", "pubOnly.xml", "", id, null}, 323 {"system", "sysOnly.xml", "", id, "http://local/base/dtd/system.dtd"}, 324 {"system", "sysAndPub.xml", "", id, "http://local/base/dtd/system.dtd"}, 325 }; 326 } 327 328 /* 329 DataProvider: used to verify how prefer settings affect the result of the 330 CatalogResolver's resolution operation. 331 Data columns: 332 prefer, catalog, publicId, systemId, expected result 333 */ 334 @DataProvider(name = "resolveWithPrefer") 335 Object[][] getDataForResolve() { 336 return new Object[][]{ 337 {"system", "pubOnly.xml", id, "", "http://local/base/dtd/public.dtd"}, 338 {"system", "pubOnly.xml", "", id, null}, 339 {"system", "pubOnly.xml", id, id, null}, 340 {"public", "pubOnly.xml", id, "", "http://local/base/dtd/public.dtd"}, 341 {"public", "pubOnly.xml", "", id, null}, 342 {"public", "pubOnly.xml", id, id, "http://local/base/dtd/public.dtd"}, 343 {"system", "sysOnly.xml", id, "", null}, 344 {"system", "sysOnly.xml", "", id, "http://local/base/dtd/system.dtd"}, 345 {"system", "sysOnly.xml", id, id, "http://local/base/dtd/system.dtd"}, 346 {"public", "sysOnly.xml", id, "", null}, 347 {"public", "sysOnly.xml", "", id, "http://local/base/dtd/system.dtd"}, 348 {"public", "sysOnly.xml", id, id, "http://local/base/dtd/system.dtd"}, 349 {"system", "sysAndPub.xml", id, "", "http://local/base/dtd/public.dtd"}, 350 {"system", "sysAndPub.xml", "", id, "http://local/base/dtd/system.dtd"}, 351 {"system", "sysAndPub.xml", id, id, "http://local/base/dtd/system.dtd"}, 352 {"public", "sysAndPub.xml", id, "", "http://local/base/dtd/public.dtd"}, 353 {"public", "sysAndPub.xml", "", id, "http://local/base/dtd/system.dtd"}, 354 {"public", "sysAndPub.xml", id, id, "http://local/base/dtd/system.dtd"}, 355 }; 356 } 357 /* 358 DataProvider: catalogs that contain invalid next or delegate catalogs. 359 The defer attribute is set to false. 360 */ 361 @DataProvider(name = "invalidAltCatalogs") 362 Object[][] getCatalogs() { 363 return new Object[][]{ 364 {"defer_false_2.xml"}, 365 {"defer_del_false.xml"} 366 }; 367 } 368 369 /* 370 DataProvider: for testing the verification of file paths by 371 the CatalogFeatures builder 372 */ 373 @DataProvider(name = "invalidPaths") 374 Object[][] getFiles() { 375 return new Object[][]{ 376 {null}, 377 {""}, 378 {"file:a/b\\c"}, 379 {"file:/../../.."}, 380 {"c:/te:t"}, 381 {"c:/te?t"}, 382 {"c/te*t"}, 383 {"in|valid.txt"}, 384 {"shema:invalid.txt"}, 385 }; 386 } 387 388 /* 389 DataProvider: provides test name, expected string, the catalog, and XML 390 document. 391 */ 392 @DataProvider(name = "catalog") 393 Object[][] getCatalog() { 394 return new Object[][]{ 395 {"testSystem", "Test system entry", "catalog.xml", "system.xml", getParser()}, 396 {"testRewriteSystem", "Test rewritesystem entry", "catalog.xml", "rewritesystem.xml", getParser()}, 397 {"testRewriteSystem1", "Test rewritesystem entry", "catalog.xml", "rewritesystem1.xml", getParser()}, 398 {"testSystemSuffix", "Test systemsuffix entry", "catalog.xml", "systemsuffix.xml", getParser()}, 399 {"testDelegateSystem", "Test delegatesystem entry", "catalog.xml", "delegatesystem.xml", getParser()}, 400 {"testPublic", "Test public entry", "catalog.xml", "public.xml", getParser()}, 401 {"testDelegatePublic", "Test delegatepublic entry", "catalog.xml", "delegatepublic.xml", getParser()}, 402 }; 403 } 404 405 SAXParser getParser() { 406 SAXParser saxParser = null; 407 try { 408 SAXParserFactory factory = SAXParserFactory.newInstance(); 409 factory.setNamespaceAware(true); 410 saxParser = factory.newSAXParser(); 411 } catch (ParserConfigurationException | SAXException e) { 412 } 413 414 return saxParser; 415 } 416 417 418 /** 419 * SAX handler 420 */ 421 public class MyHandler extends DefaultHandler2 implements ErrorHandler { 422 423 StringBuilder textContent = new StringBuilder(); 424 SAXParser saxParser; 425 426 MyHandler(SAXParser saxParser) { 427 textContent.setLength(0); 428 this.saxParser = saxParser; 429 } 430 431 String getResult() { 432 return textContent.toString(); 433 } 434 435 @Override 436 public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { 437 textContent.delete(0, textContent.length()); 438 try { 439 System.out.println("Element: " + uri + ":" + localName + " " + qName); 440 } catch (Exception e) { 441 throw new SAXException(e); 442 } 443 444 } 445 446 @Override 447 public void characters(char ch[], int start, int length) throws SAXException { 448 textContent.append(ch, start, length); 449 } 450 } 451 }