8253569: javax.xml.catalog.Catalog.matchURI() implementation should reset state variables

Reviewed-by: lancea, naoto
This commit is contained in:
Joe Wang 2022-03-31 01:50:41 +00:00
parent ec0897ab80
commit eeca3a3155
5 changed files with 197 additions and 22 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 2022, 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
@ -28,10 +28,8 @@ import com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 2022, 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
@ -40,6 +40,9 @@ class GroupEntry extends BaseEntry {
static final int ATTRIBUTE_DEFFER = 1;
static final int ATTRIBUTE_RESOLUTION = 2;
//Indicates a continuous session, should not reset state
boolean shouldKeepState = false;
//Unmodifiable features when the Catalog is created
CatalogFeatures features;
@ -164,6 +167,16 @@ class GroupEntry extends BaseEntry {
longestSuffixMatch = 0;
systemEntrySearched = false;
}
/**
* Resets the state of the Catalog instance, allowing it to be reused.
*/
private void resetOnStart() {
if (this instanceof Catalog && !shouldKeepState) {
reset();
}
}
/**
* Constructs a group entry.
* @param catalog the catalog this GroupEntry belongs to
@ -228,6 +241,7 @@ class GroupEntry extends BaseEntry {
* @return a URI string if a mapping is found, or null otherwise.
*/
public String matchSystem(String systemId) {
resetOnStart();
systemEntrySearched = true;
String match = null;
for (BaseEntry entry : entries) {
@ -296,6 +310,7 @@ class GroupEntry extends BaseEntry {
* @return a URI string if a mapping is found, or null otherwise.
*/
public String matchPublic(String publicId) {
resetOnStart();
/*
When both public and system identifiers are specified, and prefer is
not public (that is, system), only system entry will be used.
@ -326,6 +341,51 @@ class GroupEntry extends BaseEntry {
return matchDelegate(CatalogEntryType.DELEGATEPUBLIC, publicId);
}
/**
* Attempt to find a matching entry in the catalog by publicId or systemId.
*
* <p>
* The resolution follows the following rules determined by the prefer
* setting:
*
* prefer "system": attempts to resolve with a system entry; attempts to
* resolve with a public entry when only publicId is specified.
*
* prefer "public": attempts to resolve with a system entry; attempts to
* resolve with a public entry if no matching system entry is found.
*
* If no match is found, continue searching uri entries.
*
* @param publicId The public identifier of the external entity being
* referenced.
*
* @param systemId The system identifier of the external entity being
* referenced.
*
* @return the resolved systemId if a match is found, null otherwise
*/
String resolve(String publicId, String systemId) {
String resolvedSystemId = null;
shouldKeepState = true;
if (systemId != null) {
/*
If a system identifier is specified, it is used no matter how
prefer is set.
*/
resolvedSystemId = matchSystem(systemId);
}
if (resolvedSystemId == null && publicId != null) {
resolvedSystemId = matchPublic(publicId);
}
if (resolvedSystemId == null && systemId != null) {
resolvedSystemId = matchURI(systemId);
}
shouldKeepState = false;
return resolvedSystemId;
}
/**
* Attempt to find a matching entry in the catalog by the uri element.
*
@ -340,6 +400,7 @@ class GroupEntry extends BaseEntry {
* @return a URI string if a mapping is found, or null otherwise.
*/
public String matchURI(String uri) {
resetOnStart();
String match = null;
for (BaseEntry entry : entries) {
switch (entry.type) {
@ -399,6 +460,7 @@ class GroupEntry extends BaseEntry {
* @return the URI string if a mapping is found, or null otherwise.
*/
private String matchDelegate(CatalogEntryType type, String id) {
resetOnStart();
String match = null;
int longestMatch = 0;
URI catalogId = null;

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 2022, 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
@ -74,25 +74,9 @@ class Util {
* @return the resolved systemId if a match is found, null otherwise
*/
static String resolve(CatalogImpl catalog, String publicId, String systemId) {
String resolvedSystemId = null;
//search the current catalog
catalog.reset();
if (systemId != null) {
/*
If a system identifier is specified, it is used no matter how
prefer is set.
*/
resolvedSystemId = catalog.matchSystem(systemId);
}
if (resolvedSystemId == null && publicId != null) {
resolvedSystemId = catalog.matchPublic(publicId);
}
if (resolvedSystemId == null && systemId != null) {
resolvedSystemId = catalog.matchURI(systemId);
}
String resolvedSystemId = catalog.resolve(publicId, systemId);
//mark the catalog as having been searched before trying alternatives
catalog.markAsSearched();

View File

@ -0,0 +1,126 @@
/*
* Copyright (c) 2022, 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 catalog;
import java.net.URI;
import javax.xml.catalog.Catalog;
import javax.xml.catalog.CatalogFeatures;
import javax.xml.catalog.CatalogManager;
import org.testng.Assert;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
/*
* @test
* @bug 8253569
* @library /javax/xml/jaxp/libs /javax/xml/jaxp/unittest
* @run testng catalog.CatalogReuseTest
* @summary Verifies that a catalog can be reused.
*/
public class CatalogReuseTest extends CatalogSupportBase {
static final CatalogFeatures FEATURES_STRICT = CatalogFeatures.builder().
with(CatalogFeatures.Feature.RESOLVE, "strict").build();
/*
DataProvider: reuses a catalog. The length of the URIs is in descending order.
Data columns: catalog, uri, expected
*/
@DataProvider(name = "dataWithCatalogD")
public Object[][] dataWithCatalogD() {
Catalog c = getCatalog();
return new Object[][]{
{c, "http://entailments/example.org/A/B/derived.ttl", "derived/A/B/derived.ttl"},
{c, "http://example.org/A/B.owl", "sources/A/B.owl"},
};
}
/*
DataProvider: reuses a catalog. The length of the URIs is in ascending order.
Data columns: catalog, uri, expected
*/
@DataProvider(name = "dataWithCatalogA")
public Object[][] dataWithCatalogA() {
Catalog c = getCatalog();
return new Object[][]{
{c, "http://example.org/A/B.owl", "sources/A/B.owl"},
{c, "http://entailments/example.org/A/B/derived.ttl", "derived/A/B/derived.ttl"},
};
}
/*
DataProvider: provides no catalog. A new catalog will be created for each test.
Data columns: uri, expected
*/
@DataProvider(name = "dataWithoutCatalog")
public Object[][] dataWithoutCatalog() {
return new Object[][]{
{"http://entailments/example.org/A/B/derived.ttl", "derived/A/B/derived.ttl"},
{"http://example.org/A/B.owl", "sources/A/B.owl"},
};
}
/*
* Initializing fields
*/
@BeforeClass
public void setUpClass() throws Exception {
super.setUp();
}
/*
* Verifies that a Catalog object can be reused, that no state data are
* in the way of a subsequent matching attempt.
*/
@Test(dataProvider = "dataWithCatalogD")
public void testD(Catalog c, String uri, String expected) throws Exception {
String m = c.matchURI(uri);
Assert.assertTrue(m.endsWith(expected), "Expected: " + expected);
}
/*
* Verifies that a Catalog object can be reused.
*/
@Test(dataProvider = "dataWithCatalogA")
public void testA(Catalog c, String uri, String expected) throws Exception {
String m = c.matchURI(uri);
Assert.assertTrue(m.endsWith(expected), "Expected: " + expected);
}
/*
* Verifies that a match is found in a newly created Catalog.
*/
@Test(dataProvider = "dataWithoutCatalog")
public void testNew(String uri, String expected) throws Exception {
Catalog c = getCatalog();
String m = c.matchURI(uri);
Assert.assertTrue(m.endsWith(expected), "Expected: " + expected);
}
private Catalog getCatalog() {
String uri = "file://" + slash + filepath + "/catalogReuse.xml";
Catalog c = CatalogManager.catalog(FEATURES_STRICT, uri != null? URI.create(uri) : null);
return c;
}
}

View File

@ -0,0 +1,5 @@
<?xml version='1.0'?>
<catalog xmlns="urn:oasis:names:tc:entity:xmlns:xml:catalog" prefer="public">
<rewriteURI uriStartString="http://entailments/example.org/" rewritePrefix="derived/"/>
<rewriteURI uriStartString="http://example.org/" rewritePrefix="sources/"/>
</catalog>