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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * 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.io.IOException;
import java.net.MalformedURLException; import java.net.MalformedURLException;
import java.net.URI; import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL; import java.net.URL;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.NoSuchElementException; 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * 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_DEFFER = 1;
static final int ATTRIBUTE_RESOLUTION = 2; static final int ATTRIBUTE_RESOLUTION = 2;
//Indicates a continuous session, should not reset state
boolean shouldKeepState = false;
//Unmodifiable features when the Catalog is created //Unmodifiable features when the Catalog is created
CatalogFeatures features; CatalogFeatures features;
@ -164,6 +167,16 @@ class GroupEntry extends BaseEntry {
longestSuffixMatch = 0; longestSuffixMatch = 0;
systemEntrySearched = false; 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. * Constructs a group entry.
* @param catalog the catalog this GroupEntry belongs to * @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. * @return a URI string if a mapping is found, or null otherwise.
*/ */
public String matchSystem(String systemId) { public String matchSystem(String systemId) {
resetOnStart();
systemEntrySearched = true; systemEntrySearched = true;
String match = null; String match = null;
for (BaseEntry entry : entries) { for (BaseEntry entry : entries) {
@ -296,6 +310,7 @@ class GroupEntry extends BaseEntry {
* @return a URI string if a mapping is found, or null otherwise. * @return a URI string if a mapping is found, or null otherwise.
*/ */
public String matchPublic(String publicId) { public String matchPublic(String publicId) {
resetOnStart();
/* /*
When both public and system identifiers are specified, and prefer is When both public and system identifiers are specified, and prefer is
not public (that is, system), only system entry will be used. not public (that is, system), only system entry will be used.
@ -326,6 +341,51 @@ class GroupEntry extends BaseEntry {
return matchDelegate(CatalogEntryType.DELEGATEPUBLIC, publicId); 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. * 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. * @return a URI string if a mapping is found, or null otherwise.
*/ */
public String matchURI(String uri) { public String matchURI(String uri) {
resetOnStart();
String match = null; String match = null;
for (BaseEntry entry : entries) { for (BaseEntry entry : entries) {
switch (entry.type) { switch (entry.type) {
@ -399,6 +460,7 @@ class GroupEntry extends BaseEntry {
* @return the URI string if a mapping is found, or null otherwise. * @return the URI string if a mapping is found, or null otherwise.
*/ */
private String matchDelegate(CatalogEntryType type, String id) { private String matchDelegate(CatalogEntryType type, String id) {
resetOnStart();
String match = null; String match = null;
int longestMatch = 0; int longestMatch = 0;
URI catalogId = null; 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * 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 * @return the resolved systemId if a match is found, null otherwise
*/ */
static String resolve(CatalogImpl catalog, String publicId, String systemId) { static String resolve(CatalogImpl catalog, String publicId, String systemId) {
String resolvedSystemId = null;
//search the current catalog //search the current catalog
catalog.reset(); catalog.reset();
if (systemId != null) { String resolvedSystemId = catalog.resolve(publicId, systemId);
/*
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);
}
//mark the catalog as having been searched before trying alternatives //mark the catalog as having been searched before trying alternatives
catalog.markAsSearched(); 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>