Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[4.x.x] Fix XML Catalogue Resolution #4320

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
119 changes: 119 additions & 0 deletions exist-core/src/main/java/org/exist/resolver/ResolverFactory.java
@@ -0,0 +1,119 @@
/*
* Copyright (C) 2014, Evolved Binary Ltd
*
* This file was originally ported from FusionDB to eXist-db by
* Evolved Binary, for the benefit of the eXist-db Open Source community.
* Only the ported code as it appears in this file, at the time that
* it was contributed to eXist-db, was re-licensed under The GNU
* Lesser General Public License v2.1 only for use in eXist-db.
*
* This license grant applies only to a snapshot of the code as it
* appeared when ported, it does not offer or infer any rights to either
* updates of this source code or access to the original source code.
*
* The GNU Lesser General Public License v2.1 only license follows.
*
* ---------------------------------------------------------------------
*
* Copyright (C) 2014, Evolved Binary Ltd
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; version 2.1.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package org.exist.resolver;

import com.evolvedbinary.j8fu.tuple.Tuple2;
import org.exist.xmldb.XmldbURI;
import org.xml.sax.InputSource;
import org.xmlresolver.Resolver;
import org.xmlresolver.ResolverFeature;
import org.xmlresolver.XMLResolverConfiguration;

import java.io.StringReader;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

import static com.evolvedbinary.j8fu.tuple.Tuple.Tuple;

/**
* Factory for creating Resolvers.
*
* @author <a href="adam@evolvedbinary.com>Adam Retter</a>
*/
public interface ResolverFactory {

/**
* Create a Resolver that is configured for specific catalogs.
*
* @param catalogs the list of catalogs, the first entry in the tuple is their URI (and/or location),
* and the optional second argument is an InputSource for obtaining them directly.
*
* @return the resolver
*
* @throws URISyntaxException
*/
static Resolver newResolver(final List<Tuple2<String, Optional<InputSource>>> catalogs) throws URISyntaxException {
final XMLResolverConfiguration resolverConfiguration = new XMLResolverConfiguration();
resolverConfiguration.setFeature(ResolverFeature.RESOLVER_LOGGER_CLASS, "org.xmlresolver.logging.SystemLogger");
resolverConfiguration.setFeature(ResolverFeature.CATALOG_LOADER_CLASS, "org.xmlresolver.loaders.ValidatingXmlLoader");
resolverConfiguration.setFeature(ResolverFeature.CLASSPATH_CATALOGS, true);
resolverConfiguration.setFeature(ResolverFeature.URI_FOR_SYSTEM, true);

for (final Tuple2<String, Optional<InputSource>> catalog : catalogs) {
if (catalog._2.isPresent()) {
resolverConfiguration.addCatalog(new URI(catalog._1), catalog._2.get());
} else {
resolverConfiguration.addCatalog(catalog._1);
}
}

return new Resolver(resolverConfiguration);
}

/**
* Catalog URI if stored in database must start with
* URI Scheme xmldb:// (and NOT xmldb:exist://) so that
* the {@link Resolver} can use {@link org.exist.protocolhandler.protocols.xmldb.Handler}
* to resolve any relative URI resources from the database.
*
* @param catalogs the catalog URIs
*
* @return the catalog URIs suitable for use with the {@link Resolver}.
*/
static List<Tuple2<String, Optional<InputSource>>> fixupExistCatalogUris(final List<Tuple2<String, Optional<InputSource>>> catalogs) {
return catalogs.stream().map(catalog -> Tuple(fixupExistCatalogUri(catalog._1), catalog._2)).collect(Collectors.toList());
}

/**
* Catalog URI if stored in database must start with
* URI Scheme xmldb:// (and NOT xmldb:exist://) so that
* the {@link Resolver} can use {@link org.exist.protocolhandler.protocols.xmldb.Handler}
* to resolve any relative URI resources from the database.
*
* @param catalogUri the catalog URI
*
* @return the catalog URI suitable for use with the {@link Resolver}.
*/
static String fixupExistCatalogUri(String catalogUri) {
if (catalogUri.startsWith("xmldb:exist://")) {
catalogUri = catalogUri.replace("xmldb:exist://", "xmldb://");
} else if (catalogUri.startsWith("/db")) {
catalogUri = "xmldb://" + catalogUri;
}
return catalogUri;
}

}
@@ -0,0 +1,136 @@
/*
* Copyright (C) 2014, Evolved Binary Ltd
*
* This file was originally ported from FusionDB to eXist-db by
* Evolved Binary, for the benefit of the eXist-db Open Source community.
* Only the ported code as it appears in this file, at the time that
* it was contributed to eXist-db, was re-licensed under The GNU
* Lesser General Public License v2.1 only for use in eXist-db.
*
* This license grant applies only to a snapshot of the code as it
* appeared when ported, it does not offer or infer any rights to either
* updates of this source code or access to the original source code.
*
* The GNU Lesser General Public License v2.1 only license follows.
*
* ---------------------------------------------------------------------
*
* Copyright (C) 2014, Evolved Binary Ltd
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; version 2.1.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package org.exist.resolver;

import org.apache.xerces.impl.dtd.XMLDTDDescription;
import org.apache.xerces.impl.xs.XSDDescription;
import org.apache.xerces.util.SAXInputSource;
import org.apache.xerces.util.XMLEntityDescriptionImpl;
import org.apache.xerces.xni.QName;
import org.apache.xerces.xni.XMLResourceIdentifier;
import org.apache.xerces.xni.XNIException;
import org.apache.xerces.xni.grammars.XMLSchemaDescription;
import org.apache.xerces.xni.parser.XMLEntityResolver;
import org.apache.xerces.xni.parser.XMLInputSource;
import org.exist.util.XMLReaderObjectFactory;
import org.xml.sax.*;
import org.xmlresolver.Resolver;

import java.io.IOException;

/**
* Adapts an {@link org.xmlresolver.Resolver} for use
* with Xerces SAX Parser by implementing {@link org.apache.xerces.xni.parser.XMLEntityResolver}.
*
* @author <a href="mailto:adam@evolvedbinary.com">Adam Retter</a>
*/
public class XercesXmlResolverAdapter implements XMLEntityResolver {
private final Resolver resolver;

public XercesXmlResolverAdapter(final Resolver resolver) {
this.resolver = resolver;
}

@Override
public XMLInputSource resolveEntity(final XMLResourceIdentifier xmlResourceIdentifier) throws XNIException, IOException {

try {
// get the name
final String name;
if (xmlResourceIdentifier instanceof XSDDescription) {
final QName triggeringComponent = ((XSDDescription) xmlResourceIdentifier).getTriggeringComponent();
name = triggeringComponent != null ? triggeringComponent.localpart : null;
} else if (xmlResourceIdentifier instanceof XMLSchemaDescription) {
final QName triggeringComponent = ((XMLSchemaDescription) xmlResourceIdentifier).getTriggeringComponent();
name = triggeringComponent != null ? triggeringComponent.localpart : null;
} else if (xmlResourceIdentifier instanceof XMLEntityDescriptionImpl) {
name = ((XMLEntityDescriptionImpl)xmlResourceIdentifier).getEntityName();
} else if (xmlResourceIdentifier instanceof XMLDTDDescription) {
name = ((XMLDTDDescription)xmlResourceIdentifier).getRootName();
} else {
name = null;
}

// get the systemId
final String systemId;
if (xmlResourceIdentifier.getExpandedSystemId() != null) {
systemId = xmlResourceIdentifier.getExpandedSystemId();
} else {
systemId = xmlResourceIdentifier.getNamespace();
}

// System.out.println(String.format("xri=(name=%s publicId=%s baseSystemId=%s systemId=%s)", name, xmlResourceIdentifier.getPublicId(), xmlResourceIdentifier.getBaseSystemId(), systemId));

// resolve the entity via an org.xmlresolver.Resolver
final InputSource src = resolver.resolveEntity(name, xmlResourceIdentifier.getPublicId(), xmlResourceIdentifier.getBaseSystemId(), systemId);
if (src == null) {
return null;
}

return new SAXInputSource(src);

} catch (final SAXException e) {
throw new XNIException(e);
}
}

/**
* Wraps the {@code resolver} in a XercesXMLResolverAdapter
* and then sets it as the property {@code http://apache.org/xml/properties/internal/entity-resolver}
* on the {@code xmlReader}.
*
* @param xmlReader the Xerces XML Reader
* @param resolver the resolver
*
* @throws SAXNotSupportedException if the property is not supported by the XMLReader
* @throws SAXNotRecognizedException if the property is not recognised by the XMLReader
*/
public static void setXmlReaderEntityResolver(final XMLReader xmlReader, final Resolver resolver) throws SAXNotSupportedException, SAXNotRecognizedException {
final XMLEntityResolver xmlEntityResolver = new XercesXmlResolverAdapter(resolver);
setXmlReaderEntityResolver(xmlReader, xmlEntityResolver);
}

/**
* Sets the {@code xmlEntityResolver} as the property {@code http://apache.org/xml/properties/internal/entity-resolver}
* on the {@code xmlReader}.
*
* @param xmlReader the Xerces XML Reader
* @param xmlEntityResolver the resolver
*
* @throws SAXNotSupportedException if the property is not supported by the XMLReader
* @throws SAXNotRecognizedException if the property is not recognised by the XMLReader
*/
public static void setXmlReaderEntityResolver(final XMLReader xmlReader, final XMLEntityResolver xmlEntityResolver) throws SAXNotSupportedException, SAXNotRecognizedException {
xmlReader.setProperty(XMLReaderObjectFactory.APACHE_PROPERTIES_ENTITYRESOLVER, xmlEntityResolver);
}
}