8323571: Regression in source resolution process

Reviewed-by: lancea, naoto
This commit is contained in:
Joe Wang 2024-01-11 22:38:39 +00:00
parent 49e6121347
commit e4389d8dc2
4 changed files with 357 additions and 18 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2009, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2009, 2024, Oracle and/or its affiliates. All rights reserved.
*/
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
@ -94,7 +94,7 @@ import org.xml.sax.InputSource;
* @author K.Venugopal SUN Microsystems
* @author Neeraj Bajaj SUN Microsystems
* @author Sunitha Reddy SUN Microsystems
* @LastModified: Nov 2023
* @LastModified: Jan 2024
*/
public class XMLEntityManager implements XMLComponent, XMLEntityResolver {
@ -1038,8 +1038,9 @@ public class XMLEntityManager implements XMLComponent, XMLEntityResolver {
}
// Step 2: custom catalog if specified
if ((publicId != null || literalSystemId != null) &&
staxInputSource == null && (fUseCatalog && fCatalogFile != null)) {
if (staxInputSource == null
&& (publicId != null || literalSystemId != null)
&& (fUseCatalog && fCatalogFile != null)) {
if (fCatalogResolver == null) {
fCatalogFeatures = JdkXmlUtils.getCatalogFeatures(fDefer, fCatalogFile, fPrefer, fResolve);
fCatalogResolver = CatalogManager.catalogResolver(fCatalogFeatures);
@ -1049,8 +1050,9 @@ public class XMLEntityManager implements XMLComponent, XMLEntityResolver {
}
// Step 3: use the default JDK Catalog Resolver if Step 2's resolve is continue
if ((publicId != null || literalSystemId != null) &&
staxInputSource == null && JdkXmlUtils.isResolveContinue(fCatalogFeatures)) {
if (staxInputSource == null
&& (publicId != null || literalSystemId != null)
&& JdkXmlUtils.isResolveContinue(fCatalogFeatures)) {
initJdkCatalogResolver();
staxInputSource = resolveWithCatalogStAX(fDefCR, JdkCatalog.JDKCATALOG, publicId, literalSystemId);
@ -1061,9 +1063,9 @@ public class XMLEntityManager implements XMLComponent, XMLEntityResolver {
// Note if both publicId and systemId are null, the resolution process continues as usual
if (staxInputSource != null) {
fISCreatedByResolver = true;
} else if ((publicId == null && literalSystemId == null) ||
(JdkXmlUtils.isResolveContinue(fCatalogFeatures) &&
fSecurityManager.is(Limit.JDKCATALOG_RESOLVE, JdkConstants.CONTINUE))) {
} else if ((publicId == null && literalSystemId == null)
|| (JdkXmlUtils.isResolveContinue(fCatalogFeatures)
&& fSecurityManager.is(Limit.JDKCATALOG_RESOLVE, JdkConstants.CONTINUE))) {
staxInputSource = new StaxXMLInputSource(
new XMLInputSource(publicId, literalSystemId, baseSystemId, true), false);
}
@ -1206,8 +1208,9 @@ public class XMLEntityManager implements XMLComponent, XMLEntityResolver {
}
// Step 2: custom catalog if specified
if ((publicId != null || literalSystemId != null || resourceIdentifier.getNamespace() !=null)
&& xmlInputSource == null && (fUseCatalog && fCatalogFile != null)) {
if (xmlInputSource == null
&& (publicId != null || literalSystemId != null || resourceIdentifier.getNamespace() !=null)
&& (fUseCatalog && fCatalogFile != null)) {
if (fCatalogResolver == null) {
fCatalogFeatures = JdkXmlUtils.getCatalogFeatures(fDefer, fCatalogFile, fPrefer, fResolve);
fCatalogResolver = CatalogManager.catalogResolver(fCatalogFeatures);
@ -1217,8 +1220,9 @@ public class XMLEntityManager implements XMLComponent, XMLEntityResolver {
}
// Step 3: use the default JDK Catalog Resolver if Step 2's resolve is continue
if ((publicId != null || literalSystemId != null)
&& xmlInputSource == null && JdkXmlUtils.isResolveContinue(fCatalogFeatures)) {
if (xmlInputSource == null
&& (publicId != null || literalSystemId != null)
&& JdkXmlUtils.isResolveContinue(fCatalogFeatures)) {
initJdkCatalogResolver();
// unlike a custom catalog, the JDK Catalog only contains entity references
xmlInputSource = resolveEntity(fDefCR, publicId, literalSystemId, baseSystemId);
@ -1226,11 +1230,13 @@ public class XMLEntityManager implements XMLComponent, XMLEntityResolver {
// Step 4: default resolution if not resolved by a resolver and the RESOLVE
// feature is set to 'continue'
// Note if both publicId and systemId are null, the resolution process continues as usual
if ((publicId == null && literalSystemId == null) ||
((xmlInputSource == null) && JdkXmlUtils.isResolveContinue(fCatalogFeatures) &&
fSecurityManager.is(Limit.JDKCATALOG_RESOLVE, JdkConstants.CONTINUE))) {
xmlInputSource = new XMLInputSource(publicId, literalSystemId, baseSystemId, false);
if (xmlInputSource == null) {
// Note if both publicId and systemId are null, the resolution process continues as usual
if ((publicId == null && literalSystemId == null) ||
(JdkXmlUtils.isResolveContinue(fCatalogFeatures) &&
fSecurityManager.is(Limit.JDKCATALOG_RESOLVE, JdkConstants.CONTINUE))) {
xmlInputSource = new XMLInputSource(publicId, literalSystemId, baseSystemId, false);
}
}
return xmlInputSource;

View File

@ -0,0 +1,201 @@
/*
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package common.catalog;
import org.w3c.dom.ls.LSInput;
import org.w3c.dom.ls.LSResourceResolver;
import org.xml.sax.SAXException;
import javax.xml.catalog.CatalogFeatures;
import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.SchemaFactory;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StringReader;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import static java.util.Objects.requireNonNull;
import static javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI;
import static javax.xml.catalog.CatalogManager.catalogResolver;
import javax.xml.catalog.CatalogResolver;
import javax.xml.validation.Validator;
import org.testng.annotations.Test;
/*
* @test
* @bug 8323571
* @library /javax/xml/jaxp/libs /javax/xml/jaxp/unittest
* @run testng common.catalog.NullIdTest
* @summary Verifies null values are handled properly in the source resolution
* process.
*/
public class NullIdTest {
private static final Map<String, String> SCHEMAS;
// Source Level JDK 8
static {
Map<String, String> map = new HashMap<>();
map.put("https://schemas.opentest4j.org/reporting/events/0.1.0", "events.xsd");
map.put("https://schemas.opentest4j.org/reporting/core/0.1.0", "core.xsd");
SCHEMAS = Collections.unmodifiableMap(map);
}
/*
* Verifies that the source resolution process recognizes the custom InputSource
* correctly even though the public and system IDs are null.
*/
@Test
public void test() throws Exception {
String xml = "<events xmlns=\"https://schemas.opentest4j.org/reporting/events/0.1.0\"/>";
validate(new StreamSource(new StringReader(xml)));
System.out.println("Successfully validated");
}
private static void validate(Source source) throws SAXException, IOException {
SchemaFactory schemaFactory = SchemaFactory.newInstance(W3C_XML_SCHEMA_NS_URI);
Validator validator = schemaFactory.newSchema().newValidator();
validator.setResourceResolver(createResourceResolver());
validator.validate(source);
}
private static LSResourceResolver createResourceResolver() {
return (type, namespaceURI, publicId, systemId, baseURI) -> {
if (namespaceURI != null) {
if (SCHEMAS.containsKey(namespaceURI)) {
CustomLSInputImpl input = new CustomLSInputImpl();
input.setPublicId(publicId);
String schema = SCHEMAS.get(namespaceURI);
input.setSystemId(requireNonNull(NullIdTest.class.getResource(schema)).toExternalForm());
input.setBaseURI(baseURI);
InputStream stream = NullIdTest.class.getResourceAsStream(schema);
input.setCharacterStream(new InputStreamReader(requireNonNull(stream)));
return input;
}
}
if (systemId != null) {
CatalogFeatures features = CatalogFeatures.builder()
.with(CatalogFeatures.Feature.RESOLVE, "continue")
.build();
CatalogResolver catalogResolver = catalogResolver(features);
return catalogResolver.resolveResource(type, namespaceURI, publicId, systemId, baseURI);
}
return null;
};
}
static class CustomLSInputImpl implements LSInput {
private Reader characterStream;
private InputStream byteStream;
private String stringData;
private String systemId;
private String publicId;
private String baseURI;
private String encoding;
private boolean certifiedText;
@Override
public Reader getCharacterStream() {
return characterStream;
}
@Override
public void setCharacterStream(Reader characterStream) {
this.characterStream = characterStream;
}
@Override
public InputStream getByteStream() {
return byteStream;
}
@Override
public void setByteStream(InputStream byteStream) {
this.byteStream = byteStream;
}
@Override
public String getStringData() {
return stringData;
}
@Override
public void setStringData(String stringData) {
this.stringData = stringData;
}
@Override
public String getSystemId() {
return systemId;
}
@Override
public void setSystemId(String systemId) {
this.systemId = systemId;
}
@Override
public String getPublicId() {
return publicId;
}
@Override
public void setPublicId(String publicId) {
this.publicId = publicId;
}
@Override
public String getBaseURI() {
return baseURI;
}
@Override
public void setBaseURI(String baseURI) {
this.baseURI = baseURI;
}
@Override
public String getEncoding() {
return encoding;
}
@Override
public void setEncoding(String encoding) {
this.encoding = encoding;
}
@Override
public boolean getCertifiedText() {
return certifiedText;
}
@Override
public void setCertifiedText(boolean certifiedText) {
this.certifiedText = certifiedText;
}
}
}

View File

@ -0,0 +1,97 @@
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:core="https://schemas.opentest4j.org/reporting/core/0.1.0"
targetNamespace="https://schemas.opentest4j.org/reporting/core/0.1.0"
elementFormDefault="qualified">
<xs:element name="infrastructure" type="core:Infrastructure"/>
<xs:complexType name="Infrastructure">
<xs:sequence>
<xs:element name="hostName" minOccurs="0" type="xs:string"/>
<xs:element name="userName" minOccurs="0" type="xs:string"/>
<xs:element name="operatingSystem" minOccurs="0" type="xs:string"/>
<xs:element name="cpuCores" minOccurs="0" type="xs:int"/>
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="TestInfo">
<xs:sequence>
<xs:element name="metadata" minOccurs="0">
<xs:complexType>
<xs:sequence>
<xs:element name="tags" minOccurs="0">
<xs:complexType>
<xs:sequence>
<xs:element name="tag" type="xs:string" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="sources" minOccurs="0">
<xs:complexType>
<xs:sequence>
<xs:element name="directorySource" minOccurs="0" maxOccurs="unbounded">
<xs:complexType>
<xs:attribute name="path" type="xs:string" use="required"/>
</xs:complexType>
</xs:element>
<xs:element name="fileSource" minOccurs="0" maxOccurs="unbounded">
<xs:complexType>
<xs:sequence>
<xs:element name="filePosition" type="core:FilePosition" minOccurs="0"/>
</xs:sequence>
<xs:attribute name="path" type="xs:string" use="required"/>
</xs:complexType>
</xs:element>
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="attachments" minOccurs="0">
<xs:complexType>
<xs:sequence>
<xs:element name="data" minOccurs="0" maxOccurs="unbounded">
<xs:complexType>
<xs:sequence>
<xs:element name="entry" minOccurs="0" maxOccurs="unbounded">
<xs:complexType>
<xs:simpleContent>
<xs:extension base="xs:string">
<xs:attribute name="key" type="xs:string" use="required"/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
</xs:element>
</xs:sequence>
<xs:attribute name="time" type="xs:dateTime" use="required"/>
</xs:complexType>
</xs:element>
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="result" minOccurs="0">
<xs:complexType>
<xs:sequence>
<xs:element name="reason" type="xs:string" minOccurs="0"/>
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
</xs:sequence>
<xs:attribute name="status" type="core:Status"/>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
<xs:simpleType name="Status">
<xs:restriction base="xs:string">
<xs:enumeration value="SUCCESSFUL"/>
<xs:enumeration value="SKIPPED"/>
<xs:enumeration value="ABORTED"/>
<xs:enumeration value="FAILED"/>
</xs:restriction>
</xs:simpleType>
<xs:complexType name="FilePosition">
<xs:attribute name="line" type="xs:int" use="required"/>
<xs:attribute name="column" type="xs:int"/>
</xs:complexType>
</xs:schema>

View File

@ -0,0 +1,35 @@
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:core="https://schemas.opentest4j.org/reporting/core/0.1.0"
xmlns:e="https://schemas.opentest4j.org/reporting/events/0.1.0"
targetNamespace="https://schemas.opentest4j.org/reporting/events/0.1.0"
elementFormDefault="qualified">
<xs:import schemaLocation="core.xsd" namespace="https://schemas.opentest4j.org/reporting/core/0.1.0"/>
<xs:element name="events">
<xs:complexType>
<xs:sequence>
<xs:element ref="core:infrastructure" minOccurs="0"/>
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element name="started" type="e:Started"/>
<xs:element name="reported" type="e:Event"/>
<xs:element name="finished" type="e:Event"/>
</xs:choice>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:complexType name="Event">
<xs:complexContent>
<xs:extension base="core:TestInfo">
<xs:attribute name="id" type="xs:string" use="required"/>
<xs:attribute name="time" type="xs:dateTime" use="required"/>
</xs:extension>
</xs:complexContent>
</xs:complexType>
<xs:complexType name="Started">
<xs:complexContent>
<xs:extension base="e:Event">
<xs:attribute name="parentId" type="xs:string"/>
<xs:attribute name="name" type="xs:string" use="required"/>
</xs:extension>
</xs:complexContent>
</xs:complexType>
</xs:schema>