/*
* Copyright 2010-2015 Institut Pasteur.
*
* This file is part of Icy.
*
* Icy is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Icy is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Icy. If not, see <http://www.gnu.org/licenses/>.
*/
package icy.plugin.classloader;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Abstract class loader that can load classes from different resources
*
* @author Kamran Zafar
* @author Stephane Dallongeville
*/
@SuppressWarnings("unchecked")
public abstract class AbstractClassLoader extends ClassLoader
{
protected final List<ProxyClassLoader> loaders = new ArrayList<ProxyClassLoader>();
private final ProxyClassLoader systemLoader = new SystemLoader();
private final ProxyClassLoader parentLoader = new ParentLoader();
private final ProxyClassLoader currentLoader = new CurrentLoader();
private final ProxyClassLoader threadLoader = new ThreadContextLoader();
/**
* Build a new instance of AbstractClassLoader.java.
*
* @param parent
* parent class loader
*/
public AbstractClassLoader(ClassLoader parent)
{
super(parent);
addDefaultLoaders();
}
public void addLoader(ProxyClassLoader loader)
{
loaders.add(loader);
Collections.sort(loaders);
}
protected void addDefaultLoaders()
{
// always add this one
loaders.add(systemLoader);
loaders.add(parentLoader);
loaders.add(currentLoader);
loaders.add(threadLoader);
Collections.sort(loaders);
}
@Override
public Class loadClass(String className) throws ClassNotFoundException
{
return loadClass(className, true);
}
/**
* Overrides the loadClass method to load classes from other resources,
* JarClassLoader is the only subclass in this project that loads classes
* from jar files
*
* @see java.lang.ClassLoader#loadClass(String, boolean)
*/
@Override
public Class loadClass(String className, boolean resolveIt) throws ClassNotFoundException
{
if (className == null || className.trim().equals(""))
return null;
final List<ProxyClassLoader> loadersDone = new ArrayList<ProxyClassLoader>();
for (ProxyClassLoader l : loaders)
{
// don't search in same loader
if (l.isEnabled() && !loadersDone.contains(l))
{
final Class clazz = l.loadClass(className, resolveIt);
if (clazz != null)
return clazz;
// loader done
loadersDone.add(l);
}
}
throw new ClassNotFoundException(className);
}
/**
* Overrides the getResourceAsStream method to load non-class resources from
* other sources, JarClassLoader is the only subclass in this project that
* loads non-class resources from jar files
*
* @see java.lang.ClassLoader#getResourceAsStream(java.lang.String)
*/
@Override
public InputStream getResourceAsStream(String name)
{
if (name == null || name.trim().equals(""))
return null;
final List<ProxyClassLoader> loadersDone = new ArrayList<ProxyClassLoader>();
for (ProxyClassLoader l : loaders)
{
// don't search in same loader
if (l.isEnabled() && !loadersDone.contains(l))
{
final InputStream is = l.getResourceAsStream(name);
if (is != null)
return is;
// loader done
loadersDone.add(l);
}
}
return null;
}
@Override
public URL getResource(String name)
{
if (name == null || name.trim().equals(""))
return null;
final List<ProxyClassLoader> loadersDone = new ArrayList<ProxyClassLoader>();
for (ProxyClassLoader l : loaders)
{
// don't search in same loader
if (l.isEnabled() && !loadersDone.contains(l))
{
final URL url = l.getResource(name);
if (url != null)
return url;
// loader done
loadersDone.add(l);
}
}
return null;
}
@Override
public Enumeration<URL> getResources(String name) throws IOException
{
if (name == null || name.trim().equals(""))
return null;
final Set<URL> result = new HashSet<URL>();
final List<ProxyClassLoader> loadersDone = new ArrayList<ProxyClassLoader>();
for (ProxyClassLoader l : loaders)
{
// don't search in same loader
if (l.isEnabled() && !loadersDone.contains(l))
{
final Enumeration<URL> urls = l.getResources(name);
if (urls != null)
{
// avoid duplicate using Set here
while (urls.hasMoreElements())
result.add(urls.nextElement());
}
// loader done
loadersDone.add(l);
}
}
return Collections.enumeration(result);
}
/**
* System class loader
*/
class SystemLoader extends ProxyClassLoader
{
private final Logger logger = Logger.getLogger(SystemLoader.class.getName());
public SystemLoader()
{
super(10);
enabled = Configuration.isSystemLoaderEnabled();
}
@Override
public Object getLoader()
{
return getSystemClassLoader();
}
@Override
public Class loadClass(String className, boolean resolveIt)
{
Class result;
try
{
result = findSystemClass(className);
}
catch (ClassNotFoundException e)
{
return null;
}
if (logger.isLoggable(Level.FINEST))
logger.finest("Returning system class " + className);
return result;
}
@Override
public InputStream getResourceAsStream(String name)
{
InputStream is = getSystemResourceAsStream(name);
if (is != null)
{
if (logger.isLoggable(Level.FINEST))
logger.finest("Returning system resource " + name);
return is;
}
return null;
}
@Override
public URL getResource(String name)
{
URL url = getSystemResource(name);
if (url != null)
{
if (logger.isLoggable(Level.FINEST))
logger.finest("Returning system resource " + name);
return url;
}
return null;
}
@Override
public Enumeration<URL> getResources(String name) throws IOException
{
Enumeration<URL> urls = getSystemResources(name);
if ((urls != null) && urls.hasMoreElements())
{
if (logger.isLoggable(Level.FINEST))
logger.finest("Returning system resources " + name);
return urls;
}
return null;
}
}
/**
* Parent class loader
*/
class ParentLoader extends ProxyClassLoader
{
private final Logger logger = Logger.getLogger(ParentLoader.class.getName());
public ParentLoader()
{
super(30);
enabled = Configuration.isParentLoaderEnabled();
}
@Override
public Object getLoader()
{
return getParent();
}
@Override
public Class loadClass(String className, boolean resolveIt)
{
Class result;
try
{
result = getParent().loadClass(className);
}
catch (ClassNotFoundException e)
{
return null;
}
if (logger.isLoggable(Level.FINEST))
logger.finest("Returning class " + className + " loaded with parent classloader");
return result;
}
@Override
public InputStream getResourceAsStream(String name)
{
InputStream is = getParent().getResourceAsStream(name);
if (is != null)
{
if (logger.isLoggable(Level.FINEST))
logger.finest("Returning resource " + name + " loaded with parent classloader");
return is;
}
return null;
}
@Override
public URL getResource(String name)
{
URL url = getParent().getResource(name);
if (url != null)
{
if (logger.isLoggable(Level.FINEST))
logger.finest("Returning resource " + name + " loaded with parent classloader");
return url;
}
return null;
}
@Override
public Enumeration<URL> getResources(String name) throws IOException
{
Enumeration<URL> urls = getParent().getResources(name);
if ((urls != null) && urls.hasMoreElements())
{
if (logger.isLoggable(Level.FINEST))
logger.finest("Returning resource " + name + " loaded with parent classloader");
return urls;
}
return null;
}
}
/**
* Current class loader
*/
class CurrentLoader extends ProxyClassLoader
{
private final Logger logger = Logger.getLogger(CurrentLoader.class.getName());
public CurrentLoader()
{
super(20);
enabled = Configuration.isCurrentLoaderEnabled();
}
@Override
public Object getLoader()
{
return getClass().getClassLoader();
}
@Override
public Class loadClass(String className, boolean resolveIt)
{
Class result;
try
{
result = getClass().getClassLoader().loadClass(className);
}
catch (ClassNotFoundException e)
{
return null;
}
if (logger.isLoggable(Level.FINEST))
logger.finest("Returning class " + className + " loaded with current classloader");
return result;
}
@Override
public InputStream getResourceAsStream(String name)
{
InputStream is = getClass().getClassLoader().getResourceAsStream(name);
if (is != null)
{
if (logger.isLoggable(Level.FINEST))
logger.finest("Returning resource " + name + " loaded with current classloader");
return is;
}
return null;
}
@Override
public URL getResource(String name)
{
URL url = getClass().getClassLoader().getResource(name);
if (url != null)
{
if (logger.isLoggable(Level.FINEST))
logger.finest("Returning resource " + name + " loaded with current classloader");
return url;
}
return null;
}
@Override
public Enumeration<URL> getResources(String name) throws IOException
{
Enumeration<URL> urls = getClass().getClassLoader().getResources(name);
if ((urls != null) && (urls.hasMoreElements()))
{
if (logger.isLoggable(Level.FINEST))
logger.finest("Returning resources " + name + " loaded with current classloader");
return urls;
}
return null;
}
}
/**
* Current class loader
*/
class ThreadContextLoader extends ProxyClassLoader
{
private final Logger logger = Logger.getLogger(ThreadContextLoader.class.getName());
public ThreadContextLoader()
{
super(40);
enabled = Configuration.isThreadContextLoaderEnabled();
}
@Override
public Object getLoader()
{
return Thread.currentThread().getContextClassLoader();
}
@Override
public Class loadClass(String className, boolean resolveIt)
{
Class result;
try
{
result = Thread.currentThread().getContextClassLoader().loadClass(className);
}
catch (ClassNotFoundException e)
{
return null;
}
if (logger.isLoggable(Level.FINEST))
logger.finest("Returning class " + className + " loaded with thread context classloader");
return result;
}
@Override
public InputStream getResourceAsStream(String name)
{
InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream(name);
if (is != null)
{
if (logger.isLoggable(Level.FINEST))
logger.finest("Returning resource " + name + " loaded with thread context classloader");
return is;
}
return null;
}
@Override
public URL getResource(String name)
{
URL url = Thread.currentThread().getContextClassLoader().getResource(name);
if (url != null)
{
if (logger.isLoggable(Level.FINEST))
logger.finest("Returning resource " + name + " loaded with thread context classloader");
return url;
}
return null;
}
@Override
public Enumeration<URL> getResources(String name) throws IOException
{
Enumeration<URL> urls = Thread.currentThread().getContextClassLoader().getResources(name);
if ((urls != null) && (urls.hasMoreElements()))
{
if (logger.isLoggable(Level.FINEST))
logger.finest("Returning resources " + name + " loaded with thread context classloader");
return urls;
}
return null;
}
}
public ProxyClassLoader getSystemLoader()
{
return systemLoader;
}
public ProxyClassLoader getParentLoader()
{
return parentLoader;
}
public ProxyClassLoader getCurrentLoader()
{
return currentLoader;
}
public ProxyClassLoader getThreadLoader()
{
return threadLoader;
}
}