/*
* Copyright 2007 Alin Dreghiciu.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
* implied.
*
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.ops4j.pax.swissbox.core;
import java.io.IOException;
import java.net.URL;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.Enumeration;
import java.util.NoSuchElementException;
import org.ops4j.lang.NullArgumentException;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleReference;
/**
* Class loader that uses the a bundle in order to implement class loader functionality.
*
* @author Alin Dreghiciu
* @since 0.1.0, December 29, 2007
*/
public class BundleClassLoader extends ClassLoader implements BundleReference
{
private static final EmptyEnumeration<URL> EMPTY_URL_ENUMERATION = new EmptyEnumeration<URL>();
private static final class EmptyEnumeration<T>
implements Enumeration<T>
{
public boolean hasMoreElements()
{
return false;
}
public T nextElement()
{
throw new NoSuchElementException();
}
}
/**
* Bundle used for class loading.
*/
private final Bundle m_bundle;
/**
* Privileged factory method.
*
* @param bundle bundle to be used for class loading. Cannot be null.
*
* @return created bundle class loader
*
* @see BundleClassLoader#BundleClassLoader(Bundle)
*/
public static BundleClassLoader newPriviledged( final Bundle bundle )
{
return newPriviledged( bundle, null );
}
/**
* Privileged factory method.
*
* @param bundle bundle to be used for class loading. Cannot be null.
* @param parent parent class loader
*
* @return created bundle class loader
*
* @see BundleClassLoader#BundleClassLoader(Bundle,ClassLoader)
*/
public static BundleClassLoader newPriviledged( final Bundle bundle, final ClassLoader parent )
{
return AccessController.doPrivileged( new PrivilegedAction<BundleClassLoader>()
{
public BundleClassLoader run()
{
return new BundleClassLoader( bundle, parent );
}
} );
}
/**
* Creates a bundle class loader with no parent.
*
* @param bundle bundle to be used for class loading. Cannot be null.
*/
public BundleClassLoader( final Bundle bundle )
{
this( bundle, null );
}
/**
* Creates a bundle class loader.
*
* @param bundle bundle to be used for class loading. Cannot be null.
* @param parent parent class loader
*/
public BundleClassLoader( final Bundle bundle, final ClassLoader parent )
{
super( parent );
NullArgumentException.validateNotNull( bundle, "Bundle" );
m_bundle = bundle;
}
/**
* Getter.
*
* @return the bundle the class loader loads from
*/
public Bundle getBundle()
{
return m_bundle;
}
/**
* If there is a parent class loader use the super implementation that will first use the parent and as a fallback
* it will call findResource(). In case there is no parent directy use findResource() as if we call the super
* implementation it will use the VMClassLoader, fact that should be avoided.
*
* @see ClassLoader#getResource(String)
*/
@Override
public URL getResource( final String name )
{
if( getParent() != null )
{
return super.getResource( name );
}
return findResource( name );
}
/**
* If there is a parent class loader use the super implementation that will first use the parent and as a fallback
* it will call findResources(). In case there is no parent directy use findResources() as if we call the super
* implementation it will use the VMClassLoader, fact that should be avoided.
*
* @see ClassLoader#getResources(String)
*/
@Override
public Enumeration<URL> getResources( final String name )
throws IOException
{
if( getParent() != null )
{
return super.getResources( name );
}
else
{
return findResources( name );
}
}
/**
* Use bundle to find find the class.
*
* @see ClassLoader#findClass(String)
*/
@Override
protected Class<?> findClass( final String name )
throws ClassNotFoundException
{
return m_bundle.loadClass( name );
}
/**
* If there is a parent class loader use the super implementation that will first use the parent and as a fallback
* it will call findClass(). In case there is no parent directy use findClass() as if we call the super
* implementation it will use the VMClassLoader, fact that should be avoided.
*
* @see ClassLoader#getResource(String)
*/
@Override
protected Class<?> loadClass( final String name, final boolean resolve )
throws ClassNotFoundException
{
if( getParent() != null )
{
return super.loadClass( name, resolve );
}
final Class<?> classToLoad = findClass( name );
if( resolve )
{
resolveClass( classToLoad );
}
return classToLoad;
}
/**
* Use bundle to find resource.
*
* @see ClassLoader#findResource(String)
*/
@Override
protected URL findResource( final String name )
{
return m_bundle.getResource( name );
}
/**
* Use bundle to find resources.
*
* @see ClassLoader#findResources(String)
*/
@Override
@SuppressWarnings("unchecked")
protected Enumeration<URL> findResources( final String name )
throws IOException
{
Enumeration<URL> resources = m_bundle.getResources( name );
// Bundle.getResources may return null, in such case return empty enumeration
if( resources == null )
{
return EMPTY_URL_ENUMERATION;
}
else
{
return resources;
}
}
@Override
public String toString()
{
return new StringBuffer().append( this.getClass().getSimpleName() ).append( "{" ).append( "bundle=" ).append(
m_bundle ).append( ",parent=" ).append( getParent() ).append( "}" ).toString();
}
@Override
public boolean equals( Object o )
{
if( this == o )
{
return true;
}
if( o == null || getClass() != o.getClass() )
{
return false;
}
BundleClassLoader that = (BundleClassLoader) o;
if( m_bundle != null ? !m_bundle.equals( that.m_bundle ) : that.m_bundle != null )
{
return false;
}
if( getParent() != null ? !getParent().equals( that.getParent() ) : that.getParent() != null )
{
return false;
}
return true;
}
@Override
public int hashCode()
{
return (m_bundle != null ? m_bundle.hashCode() : 0) * 37 + (getParent() != null ? getParent().hashCode() : 0);
}
}