/*
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership.
*
* This 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 2.1 of
* the License, or (at your option) any later version.
*
* This software 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 this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.xwiki.classloader.xwiki.internal;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URL;
import java.util.Enumeration;
import java.util.List;
import java.util.Objects;
import org.xwiki.classloader.ClassLoaderManager;
import org.xwiki.classloader.NamespaceURLClassLoader;
import org.xwiki.wiki.descriptor.WikiDescriptorManager;
/**
* Dynamic classloader which select the current class loader based on context wiki each time it's called.
*
* @version $Id: 6b30e095d789901db60a5a33600bbdbb8d8bcd6f $
* @since 6.4M2
*/
public class ContextNamespaceURLClassLoader extends NamespaceURLClassLoader
{
private WikiDescriptorManager wikis;
private final ClassLoaderManager classLoaderManager;
private String cachedCurrentWiki;
private NamespaceURLClassLoader currentClassLoader;
/**
* @param wikis the component used to access current wiki
* @param classLoaderManager the component used to get the ClassLoader associated to current wiki
* @since 7.2M1
*/
public ContextNamespaceURLClassLoader(WikiDescriptorManager wikis, ClassLoaderManager classLoaderManager)
{
// Note: it's important to set the parent CL to be the Context CL since some third party frameworks can use
// that information. For example the Apache Naming (JNDI) implementation will get the context CL to check if
// there's any NamingContext instance bound to it and at Tomcat's init (for example), Tomcat binds the defined
// DataSource to the Tomcat CL (WebappClassLoader). Thus if we loose the path from our CL to the Tomcat CL, all
// the DataSources defined in Tomcat will fail to be usable from our Hibernate code.
super(new URI[] {}, Thread.currentThread().getContextClassLoader(), null);
this.wikis = wikis;
this.classLoaderManager = classLoaderManager;
}
// TODO: Add support for context user ?
private NamespaceURLClassLoader getCurrentClassLoader()
{
String currentWiki = this.wikis.getCurrentWikiId();
if (this.currentClassLoader == null || !Objects.equals(currentWiki, this.cachedCurrentWiki)) {
this.currentClassLoader =
this.classLoaderManager.getURLClassLoader(currentWiki != null ? "wiki:" + currentWiki : null, false);
if (this.currentClassLoader == null) {
// Fallback on system classloader in the very weird edge case where ClassLoaderManager does not return
// any (which is already supposed to fallback on system classloader)
this.currentClassLoader = new NamespaceURLClassLoader(new URI[] {}, getSystemClassLoader(), null);
}
this.cachedCurrentWiki = currentWiki;
}
return this.currentClassLoader;
}
@Override
public String getNamespace()
{
return getCurrentClassLoader().getNamespace();
}
@Override
public void addURL(URL url)
{
getCurrentClassLoader().addURL(url);
}
@Override
public void addURLs(List<URL> urls)
{
getCurrentClassLoader().addURLs(urls);
}
@Override
public URL[] getURLs()
{
return getCurrentClassLoader().getURLs();
}
@Override
public URL getResource(String name)
{
return getCurrentClassLoader().getResource(name);
}
@Override
public Enumeration<URL> getResources(String name) throws IOException
{
return getCurrentClassLoader().getResources(name);
}
@Override
public InputStream getResourceAsStream(String name)
{
return getCurrentClassLoader().getResourceAsStream(name);
}
@Override
public URL findResource(String name)
{
return getCurrentClassLoader().findResource(name);
}
@Override
public Enumeration<URL> findResources(String name) throws IOException
{
return getCurrentClassLoader().findResources(name);
}
@Override
public void close() throws IOException
{
getCurrentClassLoader().close();
}
@Override
public void clearAssertionStatus()
{
getCurrentClassLoader().clearAssertionStatus();
}
@Override
public void setClassAssertionStatus(String className, boolean enabled)
{
getCurrentClassLoader().setClassAssertionStatus(className, enabled);
}
@Override
public void setDefaultAssertionStatus(boolean enabled)
{
getCurrentClassLoader().setDefaultAssertionStatus(enabled);
}
@Override
public void setPackageAssertionStatus(String packageName, boolean enabled)
{
getCurrentClassLoader().setPackageAssertionStatus(packageName, enabled);
}
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException
{
return getCurrentClassLoader().loadClass(name);
}
// protected
/**
* {@inheritDoc}
* <p>
* Yes it's a protected method but it's sometime called directly from {@link ClassLoader#loadClass(String, String)}.
* </p>
*
* @see java.lang.ClassLoader#loadClass(java.lang.String, boolean)
*/
@Override
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException
{
return getCurrentClassLoader().loadClass(name);
}
}