implemented a child first plugin classloader strategy

This commit is contained in:
Sebastian Sdorra
2013-03-13 20:51:44 +01:00
parent 15906a7e62
commit 19c48f0a6c
3 changed files with 252 additions and 7 deletions

View File

@@ -0,0 +1,187 @@
/**
* Copyright (c) 2010, Sebastian Sdorra
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer. 2. Redistributions in
* binary form must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution. 3. Neither the name of SCM-Manager;
* nor the names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* http://bitbucket.org/sdorra/scm-manager
*
*/
package sonia.scm.net;
//~--- JDK imports ------------------------------------------------------------
import java.net.URL;
import java.net.URLClassLoader;
import java.net.URLStreamHandlerFactory;
/**
* The ChildFirstURLClassLoader alters regular ClassLoader delegation and will
* check the URLs used in its initialization for matching classes before
* delegating to it's parent. Sometimes also referred to as a
* ParentLastClassLoader.
*
* @author Sebastian Sdorra
* @since 1.29
*/
public class ChildFirstURLClassLoader extends URLClassLoader
{
/**
* Constructs a new ChildFirstURLClassLoader for the specified URLs using the
* default delegation parent ClassLoader.
*
* @param urls the URLs from which to load classes and resources
*/
public ChildFirstURLClassLoader(URL[] urls)
{
super(urls);
}
/**
* Constructs a new ChildFirstURLClassLoader for the specified URLs using the
* given parent ClassLoader for delegation.
*
* @param urls the URLs from which to load classes and resources
* @param parent the parent class loader for delegation
*/
public ChildFirstURLClassLoader(URL[] urls, ClassLoader parent)
{
super(urls, parent);
}
/**
* Constructs a new URLClassLoader for the specified URLs, parent class
* loader, and URLStreamHandlerFactory. The parent argument will be used as
* the parent class loader for delegation. The factory argument will be used
* as the stream handler factory to obtain protocol handlers when creating
* new URLs.
*
* @param urls the URLs from which to load classes and resources
* @param parent the parent class loader for delegation
* @param factory the URLStreamHandlerFactory to use when creating URLs
*/
public ChildFirstURLClassLoader(URL[] urls, ClassLoader parent,
URLStreamHandlerFactory factory)
{
super(urls, parent, factory);
}
//~--- methods --------------------------------------------------------------
/**
* Loads the class with the specified name. The default implementation of this
* method searches for classes in the following order:
* <br />
* <br />
* <ol>
* <li>
* Invoke the {@link ClassLoader#findClass(String)} method to find the
* class.
* </li>
* <li>
* Invoke {@link ClassLoader#findLoadedClass(String)} to check if
* the class has already been loaded.
* </li>
* <li>
* Invoke the {@link ClassLoader#loadClass(String)} method on the parent
* class loader. If the parent is null the class loader built-in to the
* virtual machine is used, instead.
* </li>
* </ol>
*
* If the class was found using the above steps, and the {@code resolve} flag
* is true, this method will then invoke the {@link resolveClass(Class)}
* method on the resulting Class object. Subclasses of ClassLoader are
* encouraged to override {@link ClassLoader#findClass(String)}, rather than
* this method.
*
* @param name the name of the class
* @param resolve if true then resolve the class
*
* @return the resulting {@code Class} object
*
* @throws ClassNotFoundException if the class could not be found
*/
@Override
@SuppressWarnings("unchecked")
public synchronized Class loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
Class clazz = findLoadedClass(name);
if (clazz == null)
{
try
{
clazz = findClass(name);
}
catch (ClassNotFoundException e)
{
ClassLoader parent = getParent();
if (parent != null)
{
clazz = parent.loadClass(name);
}
else
{
clazz = getSystemClassLoader().loadClass(name);
}
}
}
if (resolve)
{
resolveClass(clazz);
}
return clazz;
}
//~--- get methods ----------------------------------------------------------
/**
* {@inheritDoc }
*/
@Override
public URL getResource(String name)
{
URL resource = findResource(name);
if (resource == null)
{
ClassLoader parent = getParent();
if (parent != null)
{
resource = parent.getResource(name);
}
}
return resource;
}
}

View File

@@ -47,7 +47,6 @@ import java.io.File;
import java.net.MalformedURLException; import java.net.MalformedURLException;
import java.net.URL; import java.net.URL;
import java.net.URLClassLoader;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
@@ -224,7 +223,7 @@ public class BootstrapListener implements ServletContextListener
} }
} }
return new URLClassLoader(classpathURLs.toArray(new URL[0]), return BootstrapUtil.createClassLoader(classpathURLs,
getParentClassLoader()); getParentClassLoader());
} }

View File

@@ -35,11 +35,20 @@ package sonia.scm.boot;
//~--- non-JDK imports -------------------------------------------------------- //~--- non-JDK imports --------------------------------------------------------
import com.google.common.base.Strings;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import sonia.scm.net.ChildFirstURLClassLoader;
//~--- JDK imports ------------------------------------------------------------ //~--- JDK imports ------------------------------------------------------------
import java.net.URL;
import java.net.URLClassLoader;
import java.util.List;
import javax.servlet.ServletContext; import javax.servlet.ServletContext;
/** /**
@@ -52,6 +61,16 @@ public final class BootstrapUtil
/** Field description */ /** Field description */
public static final String CLASSLOADER = "sonia.scm.BoostrapClassLoader"; public static final String CLASSLOADER = "sonia.scm.BoostrapClassLoader";
/** Field description */
private static final String STRATEGY =
"sonia.scm.plugin.classloader.strategy";
/** Field description */
private static final String STRATEGY_CHILDFIRST = "child-first";
/** Field description */
private static final String STRATEGY_PARENTFIRST = "parent-first";
/** the logger for BootstrapUtil */ /** the logger for BootstrapUtil */
private static final Logger logger = private static final Logger logger =
LoggerFactory.getLogger(BootstrapUtil.class); LoggerFactory.getLogger(BootstrapUtil.class);
@@ -66,6 +85,46 @@ public final class BootstrapUtil
//~--- methods -------------------------------------------------------------- //~--- methods --------------------------------------------------------------
/**
* Method description
*
*
* @param classpathURLs
* @param parent
*
* @return
*/
public static ClassLoader createClassLoader(List<URL> classpathURLs,
ClassLoader parent)
{
ClassLoader classLoader = null;
URL[] urls = classpathURLs.toArray(new URL[classpathURLs.size()]);
String strategy = System.getProperty(STRATEGY);
if (!Strings.isNullOrEmpty(strategy))
{
if (STRATEGY_CHILDFIRST.equals(strategy))
{
logger.info("using {} as plugin classloading strategy",
STRATEGY_CHILDFIRST);
classLoader = new ChildFirstURLClassLoader(urls, parent);
}
else if (!STRATEGY_PARENTFIRST.equals(strategy))
{
logger.warn("unknown plugin classloading strategy {}", strategy);
}
}
if (classLoader == null)
{
logger.info("using {} as plugin classloading strategy",
STRATEGY_PARENTFIRST);
classLoader = new URLClassLoader(urls, parent);
}
return classLoader;
}
/** /**
* Method description * Method description
* *