/*
* Copyright (c) MuleSoft, Inc. All rights reserved. http://www.mulesoft.com
* The software in this package is published under the terms of the CPAL v1.0
* license, a copy of which has been included with this distribution in the
* LICENSE.txt file.
*/
package org.mule.runtime.module.artifact.classloader.net;
import org.mule.runtime.core.util.ClassUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.net.URL;
import java.net.URLStreamHandler;
import java.net.URLStreamHandlerFactory;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.StringTokenizer;
/**
* A factory for loading URL protocol handlers. This factory is necessary to make Mule work in cases where the standard approach
* using system properties does not work, e.g. in application servers or with maven's surefire tests.
* <p>
* Client classes can register a subclass of {@link URLStreamHandler} for a given protocol. This implementation first checks its
* registered handlers before resorting to the default mechanism.
* <p>
*
* @see java.net.URL#URL(String, String, int, String)
*/
public class MuleUrlStreamHandlerFactory extends Object implements URLStreamHandlerFactory {
private static final String HANDLER_PKGS_SYSTEM_PROPERTY = "java.protocol.handler.pkgs";
private static final Logger log = LoggerFactory.getLogger(MuleUrlStreamHandlerFactory.class);
private static Map registry = Collections.synchronizedMap(new HashMap());
/**
* Install an instance of this class as UrlStreamHandlerFactory. This may be done exactly once as {@link URL} will throw an
* {@link Error} on subsequent invocations.
* <p>
* This method takes care that multiple invocations are possible, but the UrlStreamHandlerFactory is installed only once.
*/
public static synchronized void installUrlStreamHandlerFactory() {
/*
* When running under surefire, this class will be loaded by different class loaders and will be running in multiple "main"
* thread objects. Thus, there is no way for this class to register a globally available variable to store the info whether
* our custom UrlStreamHandlerFactory was already registered.
*
* The only way to accomplish this is to catch the Error that is thrown by URL when trying to re-register the custom
* UrlStreamHandlerFactory.
*/
try {
URL.setURLStreamHandlerFactory(new MuleUrlStreamHandlerFactory());
} catch (Error err) {
if (log.isDebugEnabled()) {
log.debug("Custom MuleUrlStreamHandlerFactory already registered", err);
}
}
}
public static void registerHandler(String protocol, URLStreamHandler handler) {
registry.put(protocol, handler);
}
public URLStreamHandler createURLStreamHandler(String protocol) {
URLStreamHandler handler = (URLStreamHandler) registry.get(protocol);
if (handler == null) {
handler = this.defaultHandlerCreateStrategy(protocol);
}
return handler;
}
private URLStreamHandler defaultHandlerCreateStrategy(String protocol) {
String packagePrefixList = System.getProperty(HANDLER_PKGS_SYSTEM_PROPERTY, "");
if (packagePrefixList.endsWith("|") == false) {
packagePrefixList += "|sun.net.www.protocol";
}
StringTokenizer tokenizer = new StringTokenizer(packagePrefixList, "|");
URLStreamHandler handler = null;
while (handler == null && tokenizer.hasMoreTokens()) {
String packagePrefix = tokenizer.nextToken().trim();
String className = packagePrefix + "." + protocol + ".Handler";
try {
handler = (URLStreamHandler) ClassUtils.instanciateClass(className);
} catch (Exception ex) {
// not much we can do here
}
}
return handler;
}
}