blob: a57882dcd9b4ac12b017205ab576bfcca01cd69b [file] [log] [blame]
// Copyright 2006, 2007 The Apache Software Foundation
//
// 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.apache.tapestry5.ioc.internal.services;
import javassist.*;
import static org.apache.tapestry5.ioc.internal.util.CollectionFactory.newMap;
import static org.apache.tapestry5.ioc.internal.util.CollectionFactory.newSet;
import org.apache.tapestry5.ioc.services.ClassFabUtils;
import java.util.Map;
import java.util.Set;
/**
* Used to ensure that {@link javassist.ClassPool#appendClassPath(javassist.ClassPath)} is invoked within a synchronized
* lock, and also handles tricky class loading issues (caused by the creation of classes, and class loaders, at
* runtime).
*/
public class ClassFactoryClassPool extends ClassPool
{
/**
* Used to identify which class loaders have already been integrated into the pool.
*/
private final Set<ClassLoader> allLoaders = newSet();
private final Map<ClassLoader, ClassPath> leafLoaders = newMap();
public ClassFactoryClassPool(ClassLoader contextClassLoader)
{
super(null);
addClassLoaderIfNeeded(contextClassLoader);
}
/**
* Returns the nearest super-class of the provided class that can be converted to a {@link CtClass}. This is used to
* filter out Hibernate-style proxies (created as subclasses of oridnary classes). This will automatically add the
* class' classLoader to the pool's class path.
*
* @param clazz class to import
* @return clazz, or a super-class of clazz
*/
public Class importClass(Class clazz)
{
addClassLoaderIfNeeded(clazz.getClassLoader());
while (true)
{
try
{
String name = ClassFabUtils.toJavaClassName(clazz);
get(name);
break;
}
catch (NotFoundException ex)
{
clazz = clazz.getSuperclass();
}
}
return clazz;
}
/**
* Convienience method for adding to the ClassPath for a particular class loader.
* <p/>
*
* @param loader the class loader to add (derived from a loaded class, and may be null for some system classes)
*/
public synchronized void addClassLoaderIfNeeded(ClassLoader loader)
{
Set<ClassLoader> leaves = leafLoaders.keySet();
if (loader == null || leaves.contains(loader) || allLoaders.contains(loader)) return;
// Work out if this loader is a child of a loader we have already.
ClassLoader existingLeaf = loader;
while (existingLeaf != null && !leaves.contains(existingLeaf))
{
existingLeaf = existingLeaf.getParent();
}
if (existingLeaf != null)
{
// The new loader is a child of an existing leaf.
// So we remove the old leaf before we add the new loader
ClassPath priorPath = leafLoaders.get(existingLeaf);
removeClassPath(priorPath);
leafLoaders.remove(existingLeaf);
}
ClassPath path = new LoaderClassPath(loader);
leafLoaders.put(loader, path);
insertClassPath(path);
ClassLoader l = loader;
while (l != null)
{
allLoaders.add(l);
l = l.getParent();
}
}
}