This is an automated email from the ASF dual-hosted git repository. desruisseaux pushed a commit to branch geoapi-4.0 in repository https://gitbox.apache.org/repos/asf/sis.git
The following commit(s) were added to refs/heads/geoapi-4.0 by this push: new 6b6622b8bd Follow redirections when unmarshalling a document from an URL. We need to update the URL in order to resolve relative xlink:href. 6b6622b8bd is described below commit 6b6622b8bd4e832abd969c2d8287006d46fa0aec Author: Martin Desruisseaux <martin.desruisse...@geomatys.com> AuthorDate: Thu Dec 21 14:59:43 2023 +0100 Follow redirections when unmarshalling a document from an URL. We need to update the URL in order to resolve relative xlink:href. --- .../org/apache/sis/xml/PooledUnmarshaller.java | 94 ++++++++++++++++------ 1 file changed, 69 insertions(+), 25 deletions(-) diff --git a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/PooledUnmarshaller.java b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/PooledUnmarshaller.java index 0f5177e642..cab80f94d7 100644 --- a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/PooledUnmarshaller.java +++ b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/PooledUnmarshaller.java @@ -16,7 +16,12 @@ */ package org.apache.sis.xml; +import java.util.HashSet; +import java.net.URI; import java.net.URL; +import java.net.URLConnection; +import java.net.HttpURLConnection; +import java.net.URISyntaxException; import java.io.File; import java.io.Reader; import java.io.InputStream; @@ -40,6 +45,7 @@ import org.w3c.dom.Node; import org.xml.sax.InputSource; import org.apache.sis.xml.bind.Context; import org.apache.sis.xml.util.ExternalLinkHandler; +import org.apache.sis.util.resources.Errors; /** @@ -159,7 +165,7 @@ final class PooledUnmarshaller extends Pooled implements Unmarshaller { if (version != null) try { return unmarshal(InputFactory.createXMLEventReader(input), version, linkHandler); } catch (XMLStreamException e) { - throw new JAXBException(e); + throw cannotParse(null, e); } else { final Context context = begin(linkHandler); try { @@ -172,24 +178,55 @@ final class PooledUnmarshaller extends Pooled implements Unmarshaller { /** * Delegates the unmarshalling to the wrapped unmarshaller. + * The URL is opened by this method instead of by the wrapped unmarshaller for allowing us to update + * the URL in case of redirection. This is necessary for resolution of relative {@code xlink:href}. */ @Override - public Object unmarshal(final URL input) throws JAXBException { - final var linkHandler = new ExternalLinkHandler(input); + public Object unmarshal(URL input) throws JAXBException { final TransformVersion version = getTransformVersion(); - if (version != null) try { - try (InputStream s = input.openStream()) { - return unmarshal(InputFactory.createXMLEventReader(s), version, linkHandler); + final var done = new HashSet<URL>(); + for (;;) try { // Will retry if there is redirect. + final URLConnection connection = input.openConnection(); + if (connection instanceof HttpURLConnection) { + final var hc = (HttpURLConnection) connection; + if (hc.getInstanceFollowRedirects()) { + switch (hc.getResponseCode()) { + /* + * The HTTP_SEE_OTHER case is questionable because the new URI is not considered + * equivalent to the original URI. However either we accept this URI, or either + * we cannot parse the content. + */ + case HttpURLConnection.HTTP_SEE_OTHER: + case HttpURLConnection.HTTP_MOVED_TEMP: case 307: // Temporary Redirect. + case HttpURLConnection.HTTP_MOVED_PERM: case 308: { // Moved Permanently. + if (!done.add(input)) { + // Safety against never-ending loop. + throw new IOException(Errors.format(Errors.Keys.CanNotConnectTo_1, input)); + } + final String location = hc.getHeaderField("Location"); + if (location != null) { + input = input.toURI().resolve(new URI(location)).toURL(); + continue; + } + } + } + } } - } catch (IOException | XMLStreamException e) { - throw new JAXBException(e); - } else { - final Context context = begin(linkHandler); - try { - return unmarshaller.unmarshal(input); - } finally { - context.finish(); + final var linkHandler = new ExternalLinkHandler(input); + try (InputStream s = connection.getInputStream()) { + if (version != null) { + return unmarshal(InputFactory.createXMLEventReader(s), version, linkHandler); + } else { + final Context context = begin(linkHandler); + try { + return unmarshaller.unmarshal(s); + } finally { + context.finish(); + } + } } + } catch (URISyntaxException | IOException | XMLStreamException e) { + throw cannotParse(input, e); } } @@ -205,7 +242,7 @@ final class PooledUnmarshaller extends Pooled implements Unmarshaller { return unmarshal(InputFactory.createXMLEventReader(s), version, linkHandler); } } catch (IOException | XMLStreamException e) { - throw new JAXBException(e); + throw cannotParse(input, e); } else { final Context context = begin(linkHandler); try { @@ -226,7 +263,7 @@ final class PooledUnmarshaller extends Pooled implements Unmarshaller { if (version != null) try { return unmarshal(InputFactory.createXMLEventReader(input), version, linkHandler); } catch (XMLStreamException e) { - throw new JAXBException(e); + throw cannotParse(null, e); } else { final Context context = begin(linkHandler); try { @@ -247,7 +284,7 @@ final class PooledUnmarshaller extends Pooled implements Unmarshaller { if (version != null) try { return unmarshal(InputFactory.createXMLEventReader(input), version, linkHandler); } catch (XMLStreamException e) { - throw new JAXBException(e); + throw cannotParse(input.getPublicId(), e); } else { final Context context = begin(linkHandler); try { @@ -268,7 +305,7 @@ final class PooledUnmarshaller extends Pooled implements Unmarshaller { if (version != null) try { return unmarshal(InputFactory.createXMLEventReader(input), version, linkHandler); } catch (XMLStreamException e) { - throw new JAXBException(e); + throw cannotParse(input.getNodeName(), e); } else { final Context context = begin(linkHandler); try { @@ -289,7 +326,7 @@ final class PooledUnmarshaller extends Pooled implements Unmarshaller { if (version != null) try { return unmarshal(InputFactory.createXMLEventReader(input), version, linkHandler, declaredType); } catch (XMLStreamException e) { - throw new JAXBException(e); + throw cannotParse(input.getNodeName(), e); } else { final Context context = begin(linkHandler); try { @@ -310,7 +347,7 @@ final class PooledUnmarshaller extends Pooled implements Unmarshaller { if (version != null) try { return unmarshal(InputFactory.createXMLEventReader(input), version, linkHandler); } catch (XMLStreamException e) { - throw new JAXBException(e); + throw cannotParse(input.getSystemId(), e); } else { final Context context = begin(linkHandler); try { @@ -331,7 +368,7 @@ final class PooledUnmarshaller extends Pooled implements Unmarshaller { if (version != null) try { return unmarshal(InputFactory.createXMLEventReader(input), version, linkHandler, declaredType); } catch (XMLStreamException e) { - throw new JAXBException(e); + throw cannotParse(input.getSystemId(), e); } else { final Context context = begin(linkHandler); try { @@ -352,7 +389,7 @@ final class PooledUnmarshaller extends Pooled implements Unmarshaller { if (version != null) try { return unmarshal(InputFactory.createXMLEventReader(input), version, linkHandler); } catch (XMLStreamException e) { - throw new JAXBException(e); + throw cannotParse(input.getLocalName(), e); } else { final Context context = begin(linkHandler); try { @@ -373,7 +410,7 @@ final class PooledUnmarshaller extends Pooled implements Unmarshaller { if (version != null) try { return unmarshal(InputFactory.createXMLEventReader(input), version, linkHandler, declaredType); } catch (XMLStreamException e) { - throw new JAXBException(e); + throw cannotParse(input.getLocalName(), e); } else { final Context context = begin(linkHandler); try { @@ -393,7 +430,7 @@ final class PooledUnmarshaller extends Pooled implements Unmarshaller { try { linkHandler = ExternalLinkHandler.create(input); } catch (XMLStreamException e) { - throw new JAXBException(e); + throw cannotParse(null, e); } final TransformVersion version = getTransformVersion(); if (version != null) { @@ -416,7 +453,7 @@ final class PooledUnmarshaller extends Pooled implements Unmarshaller { try { linkHandler = ExternalLinkHandler.create(input); } catch (XMLStreamException e) { - throw new JAXBException(e); + throw cannotParse(null, e); } final TransformVersion version = getTransformVersion(); if (version != null) { @@ -430,6 +467,13 @@ final class PooledUnmarshaller extends Pooled implements Unmarshaller { } } + /** + * Returns the exception to throw for an input file or URL that cannot be parsed. + */ + private static JAXBException cannotParse(final Object input, final Exception cause) { + return new JAXBException((input != null) ? Errors.format(Errors.Keys.CanNotParse_1, input) : cause.getMessage(), cause); + } + /** * Delegates to the wrapped unmarshaller. */