/* Copyright (2006-2012) Schibsted ASA
* This file is part of Possom.
*
* Possom is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Possom 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Possom. If not, see <http://www.gnu.org/licenses/>.
*/
package no.sesat.search.site.config;
import no.sesat.search.site.SiteContext;
import no.sesat.search.site.Site;
import org.apache.log4j.Logger;
/**
*
* @version $Id$
*/
public final class SpiClassLoader extends ResourceClassLoader {
/** The context this class needs. */
public interface Context extends ResourceClassLoader.Context, SpiContext {}
private static final String CLASS_LOADER_FOR = "Class loader for (";
private static final Logger LOG = Logger.getLogger(SpiClassLoader.class);
private final String jarName;
private final ClassLoader parentSite;
private final ClassLoader parentSpi;
private final Context context;
private final Site site;
private final Spi spi;
/**
* Creates a new spi class loader.
* {@todo describe hierarchy.}
*
* @param context the context.
*/
public SpiClassLoader(final Context context) {
super(context);
this.context = context;
this.site = context.getSite();
this.spi = context.getSpi();
jarName = spi + ".jar";
// The sidekick classloader is used to load auxiliary (called by jvm when linking) classes from the skin. Tried last.
parentSpi = spi.getParent() != null ? parentSpiClassLoader() : new SidekickClassLoader();
parentSite = site.getParent() != null ? parentSiteClassLoader() : this.getClass().getClassLoader();
}
@Override
public String toString() {
return CLASS_LOADER_FOR + site.toString() + ", " + spi + "=>" + spi.getParent() +')';
}
/**
* Tries to load class, delegating to parent class loaders in the following order.
*
* @param name the name of the class to find.
* @param resolve if to resolve it.
* @return the class
* @throws ClassNotFoundException if the class could not be found in any class loader.
*/
@Override
public Class<?> loadClass(final String name, final boolean resolve) throws ClassNotFoundException{
// First, check if the class has already been loaded
Class c = findLoadedClass(name);
if (c == null) {
LOG.debug("Searching for class " + name + " in " + jarName);
try {
c = findClass(name);
} catch (ClassNotFoundException e) {
try {
c = parentSite.loadClass(name);
} catch (ClassNotFoundException e1) {
c = parentSpi.loadClass(name);
}
}
}else{
LOG.debug(name + " already in " + toString());
}
if (resolve) {
resolveClass(c);
}
return resolve ? findLoadedClass(name) : c;
}
/** {@inheritDoc} */
protected String getJarName() {
return jarName;
}
@Override
protected Class<?> findClass(final String className) throws ClassNotFoundException {
final Class clazz = super.findClass(className);
LOG.info("Class " + className + " loaded by " + toString());
return clazz;
}
private ClassLoader parentSpiClassLoader() {
final SiteClassLoaderFactory.Context factoryContext = new SiteClassLoaderFactory.Context() {
public BytecodeLoader newBytecodeLoader(final SiteContext siteCxt, final String name, final String jar) {
return context.newBytecodeLoader(siteCxt, name, jar);
}
public Site getSite() {
return site;
}
public Spi getSpi() {
return spi.getParent();
}
};
return SiteClassLoaderFactory.instanceOf(factoryContext).getClassLoader();
}
private ClassLoader parentSiteClassLoader() {
final SiteClassLoaderFactory.Context parentContext = new SiteClassLoaderFactory.Context() {
public BytecodeLoader newBytecodeLoader(final SiteContext siteCxt, final String name, final String jar) {
return context.newBytecodeLoader(siteCxt, name, jar);
}
public Site getSite() {
return site.getParent();
}
public Spi getSpi() {
return spi;
}
};
return SiteClassLoaderFactory.instanceOf(parentContext).getClassLoader();
}
/**
* This class loader will do "resource" class loading from the skin. Any class available to the class loader of
* the resource servlet (commons-resourcefeed) will be found.
*/
private class SidekickClassLoader extends ResourceClassLoader {
public SidekickClassLoader() {
super(context, SpiClassLoader.this.getClass().getClassLoader());
}
protected String getJarName() {
return null;
}
}
}