Java 7 XML entity resolver doesn't follow redirects, makes XSD validation fail
Posted by ozizka Jun 1, 2016When you validate a XML file, and this has a XSD URL for some namespace, and this XSD URL redirects to another URL, then the default EntityResolver2 takes that as a failure.
Jess noticed (and fixed) this when debugging WINDUP-997.
So for instance, this XML was not valid, because some of the URLs (perhaps in the linked XSD's) is behind a redirect:
<?xml version="1.0" encoding="UTF-8" ?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <mvc:resources mapping="/resources/**" location="/" />
So here's a fixed implementation. It follows only a single redirect, but change the if to a for and it will follow more (don't forget to prevent infinite loop).
package org.jboss.windup.rules.apps.xml.xml; import java.io.IOException; import java.net.HttpURLConnection; import java.net.URL; import java.net.URLConnection; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import org.xml.sax.ext.EntityResolver2; /** * This is an {@link EntityResolver2} that has been enhanced to support URL redirection. * * @author <a href="mailto:jesse.sightler@gmail.com">Jesse Sightler</a> */ public class EnhancedEntityResolver2 implements EntityResolver2 { @Override public InputSource getExternalSubset(String name, String baseURI) throws SAXException, IOException { return null; } @Override public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException { return resolveEntity(null, publicId, null, systemId); } @Override public InputSource resolveEntity(String name, String publicId, String baseURI, String systemId) throws SAXException, IOException { URL url = baseURI != null ? new URL(new URL(baseURI), systemId) : new URL(systemId); URLConnection connection = url.openConnection(); // Not a HTTP connection... skip redirection logic if (!(connection instanceof HttpURLConnection)) return new InputSource(connection.getInputStream()); HttpURLConnection httpConnection = (HttpURLConnection)connection; int status = httpConnection.getResponseCode(); if ((status != HttpURLConnection.HTTP_OK) && (status == HttpURLConnection.HTTP_MOVED_TEMP || status == HttpURLConnection.HTTP_MOVED_PERM || status == HttpURLConnection.HTTP_SEE_OTHER)) { String newUrl = httpConnection.getHeaderField("Location"); httpConnection = (HttpURLConnection) new URL(newUrl).openConnection(); } InputSource inputSource = new InputSource(httpConnection.getInputStream()); inputSource.setSystemId(url.toString()); inputSource.setPublicId(publicId); return inputSource; } }