Skip to content

Commit f6f8f0f

Browse files
committed
8345353: Test for JDK-8344800 W3C DTDs and XSDs in the built-in Catalog
1 parent 68b1b94 commit f6f8f0f

File tree

11 files changed

+419
-0
lines changed

11 files changed

+419
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,275 @@
1+
/*
2+
* Copyright (c) 2024, 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 common.jdkcatalog;
24+
25+
import java.io.StringReader;
26+
import java.nio.file.Files;
27+
import java.nio.file.Path;
28+
import java.nio.file.Paths;
29+
import java.util.stream.Collectors;
30+
import javax.xml.XMLConstants;
31+
import javax.xml.catalog.CatalogFeatures;
32+
import javax.xml.parsers.SAXParserFactory;
33+
import javax.xml.transform.sax.SAXSource;
34+
import javax.xml.validation.SchemaFactory;
35+
import javax.xml.validation.Validator;
36+
import org.testng.Assert;
37+
import org.testng.Assert.ThrowingRunnable;
38+
import org.testng.annotations.DataProvider;
39+
import org.testng.annotations.Test;
40+
import org.xml.sax.InputSource;
41+
import org.xml.sax.SAXException;
42+
import org.xml.sax.XMLReader;
43+
import org.xml.sax.helpers.DefaultHandler;
44+
45+
/*
46+
* @test
47+
* @bug 8344800 8345353
48+
* @run testng/othervm common.jdkcatalog.JDKCatalogTest
49+
* @summary Verifies the W3C DTDs and XSDs in the JDK built-in catalog.
50+
*/
51+
public class JDKCatalogTest {
52+
static String CLS_DIR = System.getProperty("test.classes");
53+
static String SRC_DIR = System.getProperty("test.src");
54+
public static boolean isWindows = false;
55+
static {
56+
if (System.getProperty("os.name").contains("Windows")) {
57+
isWindows = true;
58+
}
59+
};
60+
public static final String JDKCATALOG_RESOLVE = "jdk.xml.jdkcatalog.resolve";
61+
static final String PUBLIC_ID = "{{publicId}}";
62+
static final String SYSTEM_ID = "{{systemId}}";
63+
static final String XSD_LOCATION = "{{SCHEMA_LOCATION}}";
64+
static final String TARGET_NAMESPACE = "{{targetNamespace}}";
65+
static final String ROOT_ELEMENT = "{{rootElement}}";
66+
67+
/*
68+
* DataProvider: for verifying DTDs in the JDKCatalog
69+
* Data provided: see test testExternalDTD
70+
*/
71+
@DataProvider(name = "externalDTD")
72+
public Object[][] getDTD() throws Exception {
73+
return new Object[][]{
74+
// verifies the test method correctly throws an exception if the specified
75+
// DTD can not be resolved
76+
{"-//ORG//DTD FOO 200102//EN", "http://foo.org/2001/bar.dtd", SAXException.class},
77+
// this test also verifies datatypes.dtd as it's referenced in XMLSchema.dtd
78+
{"-//W3C//DTD XMLSCHEMA 200102//EN", "http://www.w3.org/2001/XMLSchema.dtd", null},
79+
{"-//W3C//DTD XHTML 1.0 Frameset//EN", "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd", null},
80+
{"-//W3C//DTD XHTML 1.0 Strict//EN", "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd", null},
81+
{"-//W3C//DTD XHTML 1.0 Transitional//EN", "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd", null},
82+
{"-//W3C//DTD XHTML 1.1//EN", "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd", null},
83+
{"-//W3C//DTD Specification V2.10//EN", "http://www.w3.org/2002/xmlspec/dtd/2.10/xmlspec.dtd", null},
84+
};
85+
}
86+
87+
/*
88+
* DataProvider: for verifying XSDs in the JDKCatalog
89+
* Data provided: see test testXSD
90+
*/
91+
@DataProvider(name = "getXSD")
92+
public Object[][] getXSD() throws Exception {
93+
return new Object[][]{
94+
// verifies the test method correctly throws an exception if the specified
95+
// XSD can not be resolved
96+
{"xsdtest.xml", "http://foo.org/2001/bar.xsd", "http://foo.org/2001/bar", "root", null, SAXException.class},
97+
// application XSD is resolved by a custom catalog, the W3C XSD then by the JDKCatalog
98+
{"testXML.xml", "http://www.w3.org/2001/xml.xsd", "http://www.w3.org/XML/1998/namespace", "testXMLXSD", "TestCatalog.xml", null},
99+
// this test also verifies XMLSchema.dtd and xml.xsd as they are referenced
100+
{"testXMLSchema.xml", "http://www.w3.org/2001/XMLSchema.xsd", "http://www.w3.org/2001/XMLSchema", "xs:schema", null, null},
101+
{"testDatatypes.xml", "http://www.w3.org/2009/XMLSchema/XMLSchema-datatypes.xsd", "http://www.w3.org/2001/XMLSchema-datatypes", "testDatatypes", "TestCatalog.xml", null},
102+
{"xhtml-frameset.xml", "https://www.w3.org/2002/08/xhtml/xhtml1-frameset.xsd", "http://www.w3.org/1999/xhtml", "html", null, null},
103+
{"xhtml.xml", "https://www.w3.org/2002/08/xhtml/xhtml1-strict.xsd", "http://www.w3.org/1999/xhtml", "html", null, null},
104+
{"xhtml.xml", "https://www.w3.org/2002/08/xhtml/xhtml1-transitional.xsd", "http://www.w3.org/1999/xhtml", "html", null, null},
105+
{"xhtml.xml", "http://www.w3.org/MarkUp/SCHEMA/xhtml11.xsd", "http://www.w3.org/1999/xhtml", "html", null, null},
106+
};
107+
}
108+
109+
/**
110+
* Verifies that references to the W3C DTDs are resolved by the JDK built-in
111+
* catalog.
112+
* @param publicId the PUBLIC identifier
113+
* @param systemId the SYSTEM identifier
114+
* @param expectedThrow the expected throw if the specified DTD can not be
115+
* resolved.
116+
* @throws Exception if the test fails
117+
*/
118+
@Test(dataProvider = "externalDTD")
119+
public void testExternalDTD(String publicId, String systemId, Class<Throwable> expectedThrow)
120+
throws Exception {
121+
final String xmlString = generateXMLWithDTDRef(publicId, systemId);
122+
123+
if (expectedThrow == null) {
124+
assertDoesNotThrow(() -> parseWithResolveStrict(xmlString),
125+
"JDKCatalog shall resolve " + systemId + " but exception is thrown.");
126+
} else {
127+
Assert.assertThrows(expectedThrow,
128+
() -> parseWithResolveStrict(xmlString));
129+
}
130+
}
131+
132+
/**
133+
* Verifies that references to the W3C DTDs are resolved by the JDK built-in
134+
* catalog.
135+
* @param xmlTemplate a template used to generate an XML instance
136+
* @param xsdLocation the XSD to be resolved
137+
* @param targetNS the target namespace
138+
* @param rootElement the root element
139+
* @param catalog the custom catalog to be used to resolve XSDs used by the
140+
* test.
141+
* @param expectedThrow the expected throw if the specified DTD can not be
142+
* resolved.
143+
* @throws Exception if the test fails
144+
*/
145+
@Test(dataProvider = "getXSD")
146+
public void testXSD(String xmlTemplate, String xsdLocation, String targetNS, String rootElement, String catalog,
147+
Class<Throwable> expectedThrow)
148+
throws Exception {
149+
String xmlSrcPath = SRC_DIR + "/" + xmlTemplate;
150+
final String xmlSrcId = getSysId(xmlSrcPath);
151+
152+
final String customCatalog = getSysId((catalog != null) ? SRC_DIR + "/" + catalog : null);
153+
154+
final String xmlString = generateXMLWithXSDRef(xmlSrcPath, xsdLocation,
155+
targetNS, rootElement);
156+
if (expectedThrow == null) {
157+
assertDoesNotThrow(() -> validateWithResolveStrict(xmlString, xmlSrcId, customCatalog),
158+
"JDKCatalog shall resolve " + xsdLocation + " but exception is thrown.");
159+
} else {
160+
Assert.assertThrows(expectedThrow,
161+
() -> validateWithResolveStrict(xmlString, xmlSrcId, customCatalog));
162+
}
163+
}
164+
165+
/**
166+
* Validate the specified XML document with jdk.xml.jdkCatalog.resolve set to strict.
167+
* @param xml the XML document to be validated
168+
* @param xmlSrcPathId the URI to the XML source (template in this case)
169+
* @param customCatalog the custom catalog used to resolve local XSDs
170+
* @throws Exception if validation fails
171+
*/
172+
public void validateWithResolveStrict(String xml, String xmlSrcPathId, String customCatalog)
173+
throws Exception {
174+
SAXSource ss = new SAXSource(new InputSource(new StringReader(xml)));
175+
ss.setSystemId(xmlSrcPathId);
176+
SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
177+
schemaFactory.setProperty(JDKCATALOG_RESOLVE, "strict");
178+
if (customCatalog != null) {
179+
schemaFactory.setProperty(CatalogFeatures.Feature.FILES.getPropertyName(), customCatalog);
180+
schemaFactory.setProperty(CatalogFeatures.Feature.RESOLVE.getPropertyName(), "continue");
181+
}
182+
Validator validator = schemaFactory.newSchema().newValidator();
183+
validator.validate(ss);
184+
}
185+
186+
/**
187+
* Parses the XML with jdk.xml.jdkCatalog.resolve set to strict.
188+
* @param xml the XML document to be parsed
189+
* @throws Exception if external access is denied
190+
*/
191+
public void parseWithResolveStrict(String xml)
192+
throws Exception {
193+
SAXParserFactory saxParserFactory = SAXParserFactory.newInstance();
194+
saxParserFactory.setNamespaceAware(true);
195+
XMLReader xmlReader = saxParserFactory.newSAXParser().getXMLReader();
196+
xmlReader.setProperty(JDKCATALOG_RESOLVE, "strict");
197+
xmlReader.setContentHandler(new DefaultHandler());
198+
xmlReader.parse(new InputSource(new StringReader(xml)));
199+
}
200+
201+
/**
202+
* Generates an XML with the specified PUBLIC and SYSTEM identifiers.
203+
* @param publicId the public identifier
204+
* @param systemId the system identifier
205+
* @return an XML
206+
* @throws Exception if error happens
207+
*/
208+
private String generateXMLWithDTDRef(String publicId, String systemId)
209+
throws Exception {
210+
Path path = Paths.get(SRC_DIR + "/dtdtest.xml");
211+
String xmlString = Files.lines(path).map(line -> {
212+
if (line.contains(PUBLIC_ID)) {
213+
line = line.replace(PUBLIC_ID, publicId);
214+
}
215+
if (line.contains(SYSTEM_ID)) {
216+
line = line.replace(SYSTEM_ID, systemId);
217+
}
218+
return line;
219+
}).collect(Collectors.joining(System.lineSeparator()));
220+
return xmlString;
221+
}
222+
223+
/**
224+
* Generates an XML with the specified XSD location.
225+
* @param xmlSrcPath the path to the XML source
226+
* @param xsd the XSD location
227+
* @return an XML
228+
* @throws Exception if error happens
229+
*/
230+
private String generateXMLWithXSDRef(String xmlSrcPath, String xsd,
231+
String targetNS, String rootElement)
232+
throws Exception {
233+
String xmlString = Files.lines(Paths.get(xmlSrcPath)).map(line -> {
234+
if (line.contains(XSD_LOCATION)) {
235+
line = line.replace(XSD_LOCATION, xsd);
236+
}
237+
if (line.contains(TARGET_NAMESPACE)) {
238+
line = line.replace(TARGET_NAMESPACE, targetNS);
239+
}
240+
if (line.contains(ROOT_ELEMENT)) {
241+
line = line.replace(ROOT_ELEMENT, rootElement);
242+
}
243+
return line;
244+
}).collect(Collectors.joining(System.lineSeparator()));
245+
return xmlString;
246+
}
247+
248+
/**
249+
* Returns the System identifier (URI) of the source.
250+
* @param path the path to the source
251+
* @return the System identifier
252+
*/
253+
private String getSysId(String path) {
254+
if (path == null) return null;
255+
String xmlSysId = "file://" + path;
256+
if (isWindows) {
257+
path = path.replace('\\', '/');
258+
xmlSysId = "file:///" + path;
259+
}
260+
return xmlSysId;
261+
}
262+
263+
/**
264+
* Asserts the run does not cause a Throwable.
265+
* @param runnable the runnable
266+
* @param message the message if the test fails
267+
*/
268+
private void assertDoesNotThrow(ThrowingRunnable runnable, String message) {
269+
try {
270+
runnable.run();
271+
} catch (Throwable t) {
272+
Assert.fail(message + "\n Exception thrown: " + t.getMessage());
273+
}
274+
}
275+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<catalog xmlns="urn:oasis:names:tc:entity:xmlns:xml:catalog">
3+
<!-- Catalog for testing the JDKCatalog
4+
-->
5+
<systemSuffix systemIdSuffix="testDatatypes.xsd" uri="testDatatypes.xsd"/>
6+
<systemSuffix systemIdSuffix="testXML.xsd" uri="testXML.xsd"/>
7+
8+
</catalog>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?xml version="1.0"?>
2+
<!DOCTYPE html PUBLIC "{{publicId}}"
3+
"{{systemId}}">
4+
<html>
5+
<head>
6+
<title>
7+
XHTML 1.0
8+
</title>
9+
</head>
10+
<body>
11+
<h1>
12+
XHTML 1.0
13+
</h1>
14+
<ul>
15+
<li>
16+
xhtml1-transition.dtd
17+
</li>
18+
</ul>
19+
</body>
20+
</html>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<testDatatypes xmlns="http://xmlschema.datatypes/testSchema"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://xmlschema.datatypes/testSchema testDatatypes.xsd">
5+
6+
<stringType>Hello, World!</stringType>
7+
<integerType>12345</integerType>
8+
<decimalType>67.78</decimalType>
9+
<booleanType>false</booleanType>
10+
<dateType>2024-11-24</dateType>
11+
</testDatatypes>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
3+
targetNamespace="http://xmlschema.datatypes/testSchema"
4+
xmlns="http://xmlschema.datatypes/testSchema"
5+
elementFormDefault="qualified">
6+
7+
<!-- Import the XMLSchema-datatypes.xsd -->
8+
<xs:import namespace="http://www.w3.org/2001/XMLSchema-datatypes" schemaLocation="http://www.w3.org/2009/XMLSchema/XMLSchema-datatypes.xsd">
9+
<xs:annotation>
10+
<xs:documentation>
11+
Get access to the type defined in datatypes
12+
</xs:documentation>
13+
</xs:annotation>
14+
</xs:import>
15+
16+
<!-- Define a root element that uses built-in datatypes -->
17+
<xs:element name="testDatatypes">
18+
<xs:complexType>
19+
<xs:sequence>
20+
<xs:element name="stringType" type="xs:string"/>
21+
<xs:element name="integerType" type="xs:integer"/>
22+
<xs:element name="decimalType" type="xs:decimal"/>
23+
<xs:element name="booleanType" type="xs:boolean"/>
24+
<xs:element name="dateType" type="xs:date"/>
25+
</xs:sequence>
26+
</xs:complexType>
27+
</xs:element>
28+
29+
</xs:schema>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<testXMLXSD xmlns="http://xml.xsd.test/testXMLXSD"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xmlns:xml="http://www.w3.org/XML/1998/namespace"
5+
xsi:schemaLocation="http://xml.xsd.test/testXMLXSD testXML.xsd"
6+
xml:lang="en"
7+
xml:base="http://xml.xsd.test/base">
8+
9+
<message>Hello, World!</message>
10+
</testXMLXSD>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
3+
xmlns:xml="http://www.w3.org/XML/1998/namespace"
4+
targetNamespace="http://xml.xsd.test/testXMLXSD"
5+
xmlns="http://xml.xsd.test/testXMLXSD"
6+
elementFormDefault="qualified">
7+
8+
<xs:import namespace="http://www.w3.org/XML/1998/namespace" schemaLocation="http://www.w3.org/2001/xml.xsd">
9+
<xs:annotation>
10+
<xs:documentation>
11+
Get access to the xml: attribute groups for xml:lang
12+
as declared on 'schema' and 'documentation' below
13+
</xs:documentation>
14+
</xs:annotation>
15+
</xs:import>
16+
<!-- Define a root element that uses xml:lang and xml:base attributes -->
17+
<xs:element name="testXMLXSD">
18+
<xs:complexType mixed="true">
19+
<xs:sequence>
20+
<xs:element name="message" type="xs:string"/>
21+
</xs:sequence>
22+
<xs:attribute ref="xml:lang"/>
23+
<xs:attribute ref="xml:base"/>
24+
</xs:complexType>
25+
</xs:element>
26+
27+
</xs:schema>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<?xml version="1.0"?>
2+
<xs:schema xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xsi:schemaLocation="{{targetNamespace}} {{SCHEMA_LOCATION}}"
4+
xmlns:xs="{{targetNamespace}}">
5+
<xs:element name="example" type="xs:string" />
6+
</xs:schema>

0 commit comments

Comments
 (0)