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 java.util.logging.Logger;
import org.apache.xml.resolver.tools.CatalogResolver;
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.
*
* It also use local stored DTDs/XSDs as possible not to slow down validation.
* see https://www.w3.org/blog/systeam/2008/02/08/w3c_s_excessive_dtd_traffic/
*
* @author <a href="mailto:jesse.sightler@gmail.com">Jesse Sightler</a>
* @author <a href="mailto:hotmana76@gmail.com">Marek Novotny</a>
*/
public class EnhancedEntityResolver2 implements EntityResolver2
{
private static final Logger LOG = Logger.getLogger(EnhancedEntityResolver2.class.getSimpleName());
/*
* This is catalog resolver definition for resolving entities/schemas locally instead of remotely
* default catalogs settings are in CatalogManager.properties file available on classpath,
* There is a default settings in windup/rules-xml-api module as rules-xml/api/src/main/resources/CatalogManager.properties
* and there is also default catalog with HTML/XHTML/XML local definitions.
*
*/
private final static CatalogResolver catalogResolver = new CatalogResolver();
private boolean onlineMode;
public EnhancedEntityResolver2(boolean onlineMode)
{
this.onlineMode = onlineMode;
}
@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
{
LOG.fine("Entity for resolving " + publicId + " " + systemId);
// try to resolve first from catalog if it doesn't find entity continue with other ways
InputSource inputSource = catalogResolver.resolveEntity(publicId, systemId);
if (inputSource != null)
{
LOG.fine("Resolved entity through catalog.");
return inputSource;
}
URL url = baseURI != null ? new URL(new URL(baseURI), systemId) : new URL(systemId);
LOG.fine("Resolving entity -> " + url.toString());
if (!onlineMode && ValidateXmlHandler.isNetworkUrl(url.toExternalForm()))
throw new IOException("XSD Not accessible in offline mode!");
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();
for (int i = 0; i < 4 && isRedirect(status); i++)
{
String newUrl = httpConnection.getHeaderField("Location");
httpConnection = (HttpURLConnection) new URL(newUrl).openConnection();
httpConnection.setConnectTimeout(10000);
httpConnection.setReadTimeout(10000);
status = httpConnection.getResponseCode();
}
inputSource = new InputSource(httpConnection.getInputStream());
inputSource.setSystemId(url.toString());
inputSource.setPublicId(publicId);
return inputSource;
}
private boolean isRedirect(int status)
{
return (status != HttpURLConnection.HTTP_OK) &&
(status == HttpURLConnection.HTTP_MOVED_TEMP
|| status == HttpURLConnection.HTTP_MOVED_PERM
|| status == HttpURLConnection.HTTP_SEE_OTHER);
}
}