git-svn-id: http://anonsvn.jboss.org/repos/javassist/trunk@298 30ef5769-5b8d-40dd-aea6-55b5d6557bb3tags/rel_3_17_1_ga
/* | /* | ||||
* Javassist, a Java-bytecode translator toolkit. | |||||
* Copyright (C) 2006 JBoss Inc., All Rights Reserved. | |||||
* | |||||
* The contents of this file are subject to the Mozilla Public License Version | |||||
* 1.1 (the "License"); you may not use this file except in compliance with | |||||
* the License. Alternatively, the contents of this file may be used under | |||||
* the terms of the GNU Lesser General Public License Version 2.1 or later. | |||||
* | |||||
* Software distributed under the License is distributed on an "AS IS" | |||||
basis, | |||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License | |||||
* for the specific language governing rights and limitations under the | |||||
* License. | |||||
*/ | |||||
* Javassist, a Java-bytecode translator toolkit. | |||||
* Copyright (C) 2006 JBoss Inc. All Rights Reserved. | |||||
* | |||||
* The contents of this file are subject to the Mozilla Public License Version | |||||
* 1.1 (the "License"); you may not use this file except in compliance with | |||||
* the License. Alternatively, the contents of this file may be used under | |||||
* the terms of the GNU Lesser General Public License Version 2.1 or later. | |||||
* | |||||
* Software distributed under the License is distributed on an "AS IS" basis, | |||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License | |||||
* for the specific language governing rights and limitations under the | |||||
* License. | |||||
*/ | |||||
package javassist.scopedpool; | package javassist.scopedpool; | ||||
import java.lang.ref.WeakReference; | import java.lang.ref.WeakReference; | ||||
import javassist.NotFoundException; | import javassist.NotFoundException; | ||||
/** | /** | ||||
* A scoped class pool | |||||
* A scoped class pool. | |||||
* | * | ||||
* @author <a href="mailto:bill@jboss.org">Bill Burke</a> | * @author <a href="mailto:bill@jboss.org">Bill Burke</a> | ||||
* @author <a href="adrian@jboss.com">Adrian Brock</a> | * @author <a href="adrian@jboss.com">Adrian Brock</a> | ||||
* @author <a href="kabir.khan@jboss.com">Kabir Khan</a> | * @author <a href="kabir.khan@jboss.com">Kabir Khan</a> | ||||
* @version $Revision: 1.2 $ | |||||
* @version $Revision: 1.3 $ | |||||
*/ | */ | ||||
public class ScopedClassPool extends ClassPool | |||||
{ | |||||
protected ScopedClassPoolRepository repository; | |||||
protected WeakReference classLoader; | |||||
protected LoaderClassPath classPath; | |||||
protected SoftValueHashMap softcache = new SoftValueHashMap(); | |||||
public class ScopedClassPool extends ClassPool { | |||||
protected ScopedClassPoolRepository repository; | |||||
static | |||||
{ | |||||
ClassPool.doPruning = false; | |||||
ClassPool.releaseUnmodifiedClassFile = false; | |||||
} | |||||
/** | |||||
* Create a new ScopedClassPool. | |||||
* | |||||
* @param cl the classloader | |||||
* @param src the original class pool | |||||
* @param repository the repository | |||||
*/ | |||||
protected ScopedClassPool(ClassLoader cl, ClassPool src, ScopedClassPoolRepository repository) | |||||
{ | |||||
super(src); | |||||
this.repository = repository; | |||||
this.classLoader = new WeakReference(cl); | |||||
if (cl != null) | |||||
{ | |||||
classPath = new LoaderClassPath(cl); | |||||
this.insertClassPath(classPath); | |||||
} | |||||
childFirstLookup = true; | |||||
} | |||||
protected WeakReference classLoader; | |||||
/** | |||||
* Get the class loader | |||||
* | |||||
* @return the class loader | |||||
*/ | |||||
public ClassLoader getClassLoader() | |||||
{ | |||||
return getClassLoader0(); | |||||
} | |||||
protected LoaderClassPath classPath; | |||||
private ClassLoader getClassLoader0() | |||||
{ | |||||
ClassLoader cl = (ClassLoader) classLoader.get(); | |||||
if (cl == null) | |||||
throw new IllegalStateException("ClassLoader has been garbage collected"); | |||||
return cl; | |||||
} | |||||
/** | |||||
* Close the class pool | |||||
*/ | |||||
public void close() | |||||
{ | |||||
this.removeClassPath(classPath); | |||||
classPath.close(); | |||||
classes.clear(); | |||||
softcache.clear(); | |||||
} | |||||
protected SoftValueHashMap softcache = new SoftValueHashMap(); | |||||
/** | |||||
* Flush a class | |||||
* | |||||
* @param classname the class to flush | |||||
*/ | |||||
public synchronized void flushClass(String classname) | |||||
{ | |||||
classes.remove(classname); | |||||
softcache.remove(classname); | |||||
} | |||||
static { | |||||
ClassPool.doPruning = false; | |||||
ClassPool.releaseUnmodifiedClassFile = false; | |||||
} | |||||
/** | |||||
* Soften a class | |||||
* | |||||
* @param clazz the class | |||||
*/ | |||||
public synchronized void soften(CtClass clazz) | |||||
{ | |||||
if (repository.isPrune()) clazz.prune(); | |||||
classes.remove(clazz.getName()); | |||||
softcache.put(clazz.getName(), clazz); | |||||
} | |||||
/** | |||||
* Create a new ScopedClassPool. | |||||
* | |||||
* @param cl | |||||
* the classloader | |||||
* @param src | |||||
* the original class pool | |||||
* @param repository | |||||
* the repository | |||||
*/ | |||||
protected ScopedClassPool(ClassLoader cl, ClassPool src, | |||||
ScopedClassPoolRepository repository) { | |||||
super(src); | |||||
this.repository = repository; | |||||
this.classLoader = new WeakReference(cl); | |||||
if (cl != null) { | |||||
classPath = new LoaderClassPath(cl); | |||||
this.insertClassPath(classPath); | |||||
} | |||||
childFirstLookup = true; | |||||
} | |||||
/** | |||||
* Whether the classloader is loader | |||||
* | |||||
* @return false always | |||||
*/ | |||||
public boolean isUnloadedClassLoader() | |||||
{ | |||||
return false; | |||||
} | |||||
/** | |||||
* Get the class loader | |||||
* | |||||
* @return the class loader | |||||
*/ | |||||
public ClassLoader getClassLoader() { | |||||
return getClassLoader0(); | |||||
} | |||||
/** | |||||
* Get the cached class | |||||
* | |||||
* @param classname the class name | |||||
* @return the class | |||||
*/ | |||||
protected CtClass getCached(String classname) | |||||
{ | |||||
CtClass clazz = getCachedLocally(classname); | |||||
if (clazz == null) | |||||
{ | |||||
boolean isLocal = false; | |||||
ClassLoader dcl = getClassLoader0(); | |||||
if (dcl != null) | |||||
{ | |||||
final int lastIndex = classname.lastIndexOf('$'); | |||||
String classResourceName = null; | |||||
if (lastIndex < 0) | |||||
{ | |||||
classResourceName = classname.replaceAll("[\\.]", "/") + ".class"; | |||||
} | |||||
else | |||||
{ | |||||
classResourceName = classname.substring(0, lastIndex).replaceAll("[\\.]", "/") + classname.substring(lastIndex) + ".class"; | |||||
private ClassLoader getClassLoader0() { | |||||
ClassLoader cl = (ClassLoader)classLoader.get(); | |||||
if (cl == null) | |||||
throw new IllegalStateException( | |||||
"ClassLoader has been garbage collected"); | |||||
return cl; | |||||
} | |||||
/** | |||||
* Close the class pool | |||||
*/ | |||||
public void close() { | |||||
this.removeClassPath(classPath); | |||||
classPath.close(); | |||||
classes.clear(); | |||||
softcache.clear(); | |||||
} | |||||
/** | |||||
* Flush a class | |||||
* | |||||
* @param classname | |||||
* the class to flush | |||||
*/ | |||||
public synchronized void flushClass(String classname) { | |||||
classes.remove(classname); | |||||
softcache.remove(classname); | |||||
} | |||||
/** | |||||
* Soften a class | |||||
* | |||||
* @param clazz | |||||
* the class | |||||
*/ | |||||
public synchronized void soften(CtClass clazz) { | |||||
if (repository.isPrune()) | |||||
clazz.prune(); | |||||
classes.remove(clazz.getName()); | |||||
softcache.put(clazz.getName(), clazz); | |||||
} | |||||
/** | |||||
* Whether the classloader is loader | |||||
* | |||||
* @return false always | |||||
*/ | |||||
public boolean isUnloadedClassLoader() { | |||||
return false; | |||||
} | |||||
/** | |||||
* Get the cached class | |||||
* | |||||
* @param classname | |||||
* the class name | |||||
* @return the class | |||||
*/ | |||||
protected CtClass getCached(String classname) { | |||||
CtClass clazz = getCachedLocally(classname); | |||||
if (clazz == null) { | |||||
boolean isLocal = false; | |||||
ClassLoader dcl = getClassLoader0(); | |||||
if (dcl != null) { | |||||
final int lastIndex = classname.lastIndexOf('$'); | |||||
String classResourceName = null; | |||||
if (lastIndex < 0) { | |||||
classResourceName = classname.replaceAll("[\\.]", "/") | |||||
+ ".class"; | |||||
} | |||||
else { | |||||
classResourceName = classname.substring(0, lastIndex) | |||||
.replaceAll("[\\.]", "/") | |||||
+ classname.substring(lastIndex) + ".class"; | |||||
} | |||||
isLocal = dcl.getResource(classResourceName) != null; | |||||
} | } | ||||
isLocal = dcl.getResource(classResourceName) != null; | |||||
} | |||||
if (!isLocal) | |||||
{ | |||||
Map registeredCLs = repository.getRegisteredCLs(); | |||||
synchronized (registeredCLs) | |||||
{ | |||||
Iterator it = registeredCLs.values().iterator(); | |||||
while (it.hasNext()) | |||||
{ | |||||
ScopedClassPool pool = (ScopedClassPool) it.next(); | |||||
if (pool.isUnloadedClassLoader()) | |||||
{ | |||||
repository.unregisterClassLoader(pool.getClassLoader()); | |||||
continue; | |||||
} | |||||
clazz = pool.getCachedLocally(classname); | |||||
if (clazz != null) | |||||
{ | |||||
return clazz; | |||||
} | |||||
} | |||||
if (!isLocal) { | |||||
Map registeredCLs = repository.getRegisteredCLs(); | |||||
synchronized (registeredCLs) { | |||||
Iterator it = registeredCLs.values().iterator(); | |||||
while (it.hasNext()) { | |||||
ScopedClassPool pool = (ScopedClassPool)it.next(); | |||||
if (pool.isUnloadedClassLoader()) { | |||||
repository.unregisterClassLoader(pool | |||||
.getClassLoader()); | |||||
continue; | |||||
} | |||||
clazz = pool.getCachedLocally(classname); | |||||
if (clazz != null) { | |||||
return clazz; | |||||
} | |||||
} | |||||
} | |||||
} | } | ||||
} | |||||
} | |||||
// *NOTE* NEED TO TEST WHEN SUPERCLASS IS IN ANOTHER UCL!!!!!! | |||||
return clazz; | |||||
} | |||||
/** | |||||
* Cache a class | |||||
* | |||||
* @param classname the class name | |||||
* @param c the ctClass | |||||
* @param dynamic whether the class is dynamically generated | |||||
*/ | |||||
protected void cacheCtClass(String classname, CtClass c, boolean dynamic) | |||||
{ | |||||
if (dynamic) | |||||
{ | |||||
super.cacheCtClass(classname, c, dynamic); | |||||
} | |||||
else | |||||
{ | |||||
if (repository.isPrune()) c.prune(); | |||||
softcache.put(classname, c); | |||||
} | |||||
} | |||||
/** | |||||
* Lock a class into the cache | |||||
* | |||||
* @param c the class | |||||
*/ | |||||
public void lockInCache(CtClass c) | |||||
{ | |||||
super.cacheCtClass(c.getName(), c, false); | |||||
} | |||||
} | |||||
// *NOTE* NEED TO TEST WHEN SUPERCLASS IS IN ANOTHER UCL!!!!!! | |||||
return clazz; | |||||
} | |||||
/** | |||||
* Cache a class | |||||
* | |||||
* @param classname | |||||
* the class name | |||||
* @param c | |||||
* the ctClass | |||||
* @param dynamic | |||||
* whether the class is dynamically generated | |||||
*/ | |||||
protected void cacheCtClass(String classname, CtClass c, boolean dynamic) { | |||||
if (dynamic) { | |||||
super.cacheCtClass(classname, c, dynamic); | |||||
} | |||||
else { | |||||
if (repository.isPrune()) | |||||
c.prune(); | |||||
softcache.put(classname, c); | |||||
} | |||||
} | |||||
/** | |||||
* Lock a class into the cache | |||||
* | |||||
* @param c | |||||
* the class | |||||
*/ | |||||
public void lockInCache(CtClass c) { | |||||
super.cacheCtClass(c.getName(), c, false); | |||||
} | |||||
/** | |||||
* Whether the class is cached in this pooled | |||||
* | |||||
* @param classname the class name | |||||
* @return the cached class | |||||
*/ | |||||
protected CtClass getCachedLocally(String classname) | |||||
{ | |||||
CtClass cached = (CtClass) classes.get(classname); | |||||
if (cached != null) return cached; | |||||
synchronized (softcache) | |||||
{ | |||||
return (CtClass) softcache.get(classname); | |||||
} | |||||
} | |||||
/** | |||||
* Whether the class is cached in this pooled | |||||
* | |||||
* @param classname | |||||
* the class name | |||||
* @return the cached class | |||||
*/ | |||||
protected CtClass getCachedLocally(String classname) { | |||||
CtClass cached = (CtClass)classes.get(classname); | |||||
if (cached != null) | |||||
return cached; | |||||
synchronized (softcache) { | |||||
return (CtClass)softcache.get(classname); | |||||
} | |||||
} | |||||
/** | |||||
* Get any local copy of the class | |||||
* | |||||
* @param classname the class name | |||||
* @return the class | |||||
* @throws NotFoundException when the class is not found | |||||
*/ | |||||
public synchronized CtClass getLocally(String classname) throws NotFoundException | |||||
{ | |||||
softcache.remove(classname); | |||||
CtClass clazz = (CtClass) classes.get(classname); | |||||
if (clazz == null) | |||||
{ | |||||
clazz = createCtClass(classname, true); | |||||
if (clazz == null) throw new NotFoundException(classname); | |||||
super.cacheCtClass(classname, clazz, false); | |||||
} | |||||
/** | |||||
* Get any local copy of the class | |||||
* | |||||
* @param classname | |||||
* the class name | |||||
* @return the class | |||||
* @throws NotFoundException | |||||
* when the class is not found | |||||
*/ | |||||
public synchronized CtClass getLocally(String classname) | |||||
throws NotFoundException { | |||||
softcache.remove(classname); | |||||
CtClass clazz = (CtClass)classes.get(classname); | |||||
if (clazz == null) { | |||||
clazz = createCtClass(classname, true); | |||||
if (clazz == null) | |||||
throw new NotFoundException(classname); | |||||
super.cacheCtClass(classname, clazz, false); | |||||
} | |||||
return clazz; | |||||
} | |||||
return clazz; | |||||
} | |||||
/** | |||||
* Convert a javassist class to a java class | |||||
* | |||||
* @param ct the javassist class | |||||
* @param loader the loader | |||||
* @throws CannotCompileException for any error | |||||
*/ | |||||
public Class toClass(CtClass ct, ClassLoader loader) throws CannotCompileException | |||||
{ | |||||
//We need to pass up the classloader stored in this pool, as the default implementation uses the Thread context cl. | |||||
//In the case of JSP's in Tomcat, org.apache.jasper.servlet.JasperLoader will be stored here, while it's parent | |||||
//org.jboss.web.tomcat.tc5.WebCtxLoader$ENCLoader is used as the Thread context cl. The invocation class needs to | |||||
// be generated in the JasperLoader classloader since in the case of method invocations, the package name will be | |||||
//the same as for the class generated from the jsp, i.e. org.apache.jsp. For classes belonging to org.apache.jsp, | |||||
//JasperLoader does NOT delegate to its parent if it cannot find them. | |||||
lockInCache(ct); | |||||
return super.toClass(ct, getClassLoader0()); | |||||
} | |||||
/** | |||||
* Convert a javassist class to a java class | |||||
* | |||||
* @param ct | |||||
* the javassist class | |||||
* @param loader | |||||
* the loader | |||||
* @throws CannotCompileException | |||||
* for any error | |||||
*/ | |||||
public Class toClass(CtClass ct, ClassLoader loader) | |||||
throws CannotCompileException { | |||||
// We need to pass up the classloader stored in this pool, as the | |||||
// default implementation uses the Thread context cl. | |||||
// In the case of JSP's in Tomcat, | |||||
// org.apache.jasper.servlet.JasperLoader will be stored here, while | |||||
// it's parent | |||||
// org.jboss.web.tomcat.tc5.WebCtxLoader$ENCLoader is used as the Thread | |||||
// context cl. The invocation class needs to | |||||
// be generated in the JasperLoader classloader since in the case of | |||||
// method invocations, the package name will be | |||||
// the same as for the class generated from the jsp, i.e. | |||||
// org.apache.jsp. For classes belonging to org.apache.jsp, | |||||
// JasperLoader does NOT delegate to its parent if it cannot find them. | |||||
lockInCache(ct); | |||||
return super.toClass(ct, getClassLoader0()); | |||||
} | |||||
} | } |
/* | /* | ||||
* Javassist, a Java-bytecode translator toolkit. | |||||
* Copyright (C) 2006 JBoss Inc., All Rights Reserved. | |||||
* | |||||
* The contents of this file are subject to the Mozilla Public License Version | |||||
* 1.1 (the "License"); you may not use this file except in compliance with | |||||
* the License. Alternatively, the contents of this file may be used under | |||||
* the terms of the GNU Lesser General Public License Version 2.1 or later. | |||||
* | |||||
* Software distributed under the License is distributed on an "AS IS" | |||||
basis, | |||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License | |||||
* for the specific language governing rights and limitations under the | |||||
* License. | |||||
*/ | |||||
* Javassist, a Java-bytecode translator toolkit. | |||||
* Copyright (C) 2006 JBoss Inc. All Rights Reserved. | |||||
* | |||||
* The contents of this file are subject to the Mozilla Public License Version | |||||
* 1.1 (the "License"); you may not use this file except in compliance with | |||||
* the License. Alternatively, the contents of this file may be used under | |||||
* the terms of the GNU Lesser General Public License Version 2.1 or later. | |||||
* | |||||
* Software distributed under the License is distributed on an "AS IS" basis, | |||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License | |||||
* for the specific language governing rights and limitations under the | |||||
* License. | |||||
*/ | |||||
package javassist.scopedpool; | package javassist.scopedpool; | ||||
import javassist.ClassPool; | import javassist.ClassPool; | ||||
/** | /** | ||||
* | |||||
* A factory interface. | |||||
* | |||||
* @author <a href="kabir.khan@jboss.com">Kabir Khan</a> | * @author <a href="kabir.khan@jboss.com">Kabir Khan</a> | ||||
* @version $Revision: 1.2 $ | |||||
* @version $Revision: 1.3 $ | |||||
*/ | */ | ||||
public interface ScopedClassPoolFactory | |||||
{ | |||||
ScopedClassPool create(ClassLoader cl, ClassPool src, ScopedClassPoolRepository repository); | |||||
ScopedClassPool create(ClassPool src, ScopedClassPoolRepository repository); | |||||
public interface ScopedClassPoolFactory { | |||||
/** | |||||
* Makes an instance. | |||||
*/ | |||||
ScopedClassPool create(ClassLoader cl, ClassPool src, | |||||
ScopedClassPoolRepository repository); | |||||
/** | |||||
* Makes an instance. | |||||
*/ | |||||
ScopedClassPool create(ClassPool src, | |||||
ScopedClassPoolRepository repository); | |||||
} | } |
/* | /* | ||||
* Javassist, a Java-bytecode translator toolkit. | |||||
* Copyright (C) 2006 JBoss Inc., All Rights Reserved. | |||||
* | |||||
* The contents of this file are subject to the Mozilla Public License Version | |||||
* 1.1 (the "License"); you may not use this file except in compliance with | |||||
* the License. Alternatively, the contents of this file may be used under | |||||
* the terms of the GNU Lesser General Public License Version 2.1 or later. | |||||
* | |||||
* Software distributed under the License is distributed on an "AS IS" | |||||
basis, | |||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License | |||||
* for the specific language governing rights and limitations under the | |||||
* License. | |||||
*/ | |||||
* Javassist, a Java-bytecode translator toolkit. | |||||
* Copyright (C) 2006 JBoss Inc. All Rights Reserved. | |||||
* | |||||
* The contents of this file are subject to the Mozilla Public License Version | |||||
* 1.1 (the "License"); you may not use this file except in compliance with | |||||
* the License. Alternatively, the contents of this file may be used under | |||||
* the terms of the GNU Lesser General Public License Version 2.1 or later. | |||||
* | |||||
* Software distributed under the License is distributed on an "AS IS" basis, | |||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License | |||||
* for the specific language governing rights and limitations under the | |||||
* License. | |||||
*/ | |||||
package javassist.scopedpool; | package javassist.scopedpool; | ||||
import javassist.ClassPool; | import javassist.ClassPool; | ||||
/** | /** | ||||
* | |||||
* An implementation of factory. | |||||
* | |||||
* @author <a href="kabir.khan@jboss.com">Kabir Khan</a> | * @author <a href="kabir.khan@jboss.com">Kabir Khan</a> | ||||
* @version $Revision: 1.2 $ | |||||
* @version $Revision: 1.3 $ | |||||
*/ | */ | ||||
public class ScopedClassPoolFactoryImpl implements ScopedClassPoolFactory | |||||
{ | |||||
public ScopedClassPool create(ClassLoader cl, ClassPool src, ScopedClassPoolRepository repository) | |||||
{ | |||||
return new ScopedClassPool(cl, src, repository); | |||||
} | |||||
public class ScopedClassPoolFactoryImpl implements ScopedClassPoolFactory { | |||||
/** | |||||
* Makes an instance. | |||||
*/ | |||||
public ScopedClassPool create(ClassLoader cl, ClassPool src, | |||||
ScopedClassPoolRepository repository) { | |||||
return new ScopedClassPool(cl, src, repository); | |||||
} | |||||
public ScopedClassPool create(ClassPool src, ScopedClassPoolRepository repository) | |||||
{ | |||||
return new ScopedClassPool(null, src, repository); | |||||
} | |||||
/** | |||||
* Makes an instance. | |||||
*/ | |||||
public ScopedClassPool create(ClassPool src, | |||||
ScopedClassPoolRepository repository) { | |||||
return new ScopedClassPool(null, src, repository); | |||||
} | |||||
} | } |
/* | /* | ||||
* Javassist, a Java-bytecode translator toolkit. | |||||
* Copyright (C) 2006 JBoss Inc., All Rights Reserved. | |||||
* | |||||
* The contents of this file are subject to the Mozilla Public License Version | |||||
* 1.1 (the "License"); you may not use this file except in compliance with | |||||
* the License. Alternatively, the contents of this file may be used under | |||||
* the terms of the GNU Lesser General Public License Version 2.1 or later. | |||||
* | |||||
* Software distributed under the License is distributed on an "AS IS" | |||||
basis, | |||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License | |||||
* for the specific language governing rights and limitations under the | |||||
* License. | |||||
*/ | |||||
* Javassist, a Java-bytecode translator toolkit. | |||||
* Copyright (C) 2006 JBoss Inc. All Rights Reserved. | |||||
* | |||||
* The contents of this file are subject to the Mozilla Public License Version | |||||
* 1.1 (the "License"); you may not use this file except in compliance with | |||||
* the License. Alternatively, the contents of this file may be used under | |||||
* the terms of the GNU Lesser General Public License Version 2.1 or later. | |||||
* | |||||
* Software distributed under the License is distributed on an "AS IS" basis, | |||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License | |||||
* for the specific language governing rights and limitations under the | |||||
* License. | |||||
*/ | |||||
package javassist.scopedpool; | package javassist.scopedpool; | ||||
import java.util.Map; | import java.util.Map; | ||||
import javassist.ClassPool; | import javassist.ClassPool; | ||||
/** | /** | ||||
* | |||||
* An interface to <code>ScopedClassPoolRepositoryImpl</code>. | |||||
* | |||||
* @author <a href="kabir.khan@jboss.com">Kabir Khan</a> | * @author <a href="kabir.khan@jboss.com">Kabir Khan</a> | ||||
* @version $Revision: 1.2 $ | |||||
* @version $Revision: 1.3 $ | |||||
*/ | */ | ||||
public interface ScopedClassPoolRepository | |||||
{ | |||||
void setClassPoolFactory(ScopedClassPoolFactory factory); | |||||
ScopedClassPoolFactory getClassPoolFactory(); | |||||
public interface ScopedClassPoolRepository { | |||||
/** | |||||
* Records a factory. | |||||
*/ | |||||
void setClassPoolFactory(ScopedClassPoolFactory factory); | |||||
/** | |||||
* Obtains the recorded factory. | |||||
*/ | |||||
ScopedClassPoolFactory getClassPoolFactory(); | |||||
/** | |||||
* Returns whether or not the class pool is pruned. | |||||
* | |||||
* @return the prune. | |||||
*/ | |||||
boolean isPrune(); | |||||
/** | |||||
* Get the prune. | |||||
* | |||||
* @return the prune. | |||||
*/ | |||||
boolean isPrune(); | |||||
/** | |||||
* Sets the prune flag. | |||||
* | |||||
* @param prune a new value. | |||||
*/ | |||||
void setPrune(boolean prune); | |||||
/** | |||||
* Set the prune. | |||||
* | |||||
* @param prune the prune. | |||||
*/ | |||||
void setPrune(boolean prune); | |||||
/** | |||||
* Create a scoped classpool | |||||
* | |||||
* @param cl the classloader | |||||
* @param src the original classpool | |||||
* @return the classpool | |||||
*/ | |||||
ScopedClassPool createScopedClassPool(ClassLoader cl, ClassPool src); | |||||
/** | |||||
* Create a scoped classpool. | |||||
* | |||||
* @param cl the classloader. | |||||
* @param src the original classpool. | |||||
* @return the classpool. | |||||
*/ | |||||
ScopedClassPool createScopedClassPool(ClassLoader cl, ClassPool src); | |||||
/** | |||||
* Finds a scoped classpool registered under the passed in classloader | |||||
* @param the classloader | |||||
* @return the classpool | |||||
*/ | |||||
ClassPool findClassPool(ClassLoader cl); | |||||
/** | |||||
* Finds a scoped classpool registered under the passed in classloader. | |||||
* | |||||
* @param cl the classloader. | |||||
* @return the classpool. | |||||
*/ | |||||
ClassPool findClassPool(ClassLoader cl); | |||||
/** | |||||
* Register a classloader | |||||
* | |||||
* @param ucl the classloader | |||||
* @return the classpool | |||||
*/ | |||||
ClassPool registerClassLoader(ClassLoader ucl); | |||||
/** | |||||
* Register a classloader. | |||||
* | |||||
* @param ucl the classloader. | |||||
* @return the classpool. | |||||
*/ | |||||
ClassPool registerClassLoader(ClassLoader ucl); | |||||
/** | |||||
* Get the registered classloaders | |||||
* | |||||
* @return the registered classloaders | |||||
*/ | |||||
Map getRegisteredCLs(); | |||||
/** | |||||
* Get the registered classloaders. | |||||
* | |||||
* @return the registered classloaders. | |||||
*/ | |||||
Map getRegisteredCLs(); | |||||
/** | |||||
* This method will check to see if a register classloader has been undeployed (as in JBoss) | |||||
*/ | |||||
void clearUnregisteredClassLoaders(); | |||||
/** | |||||
* This method will check to see if a register classloader has been | |||||
* undeployed (as in JBoss). | |||||
*/ | |||||
void clearUnregisteredClassLoaders(); | |||||
/** | |||||
* Unregisters a classpool and unregisters its classloader. | |||||
* @ClassLoader the classloader the pool is stored under | |||||
*/ | |||||
void unregisterClassLoader(ClassLoader cl); | |||||
} | |||||
/** | |||||
* Unregisters a classpool and unregisters its classloader. | |||||
* | |||||
* @param cl the classloader the pool is stored under. | |||||
*/ | |||||
void unregisterClassLoader(ClassLoader cl); | |||||
} |
/* | /* | ||||
* Javassist, a Java-bytecode translator toolkit. | |||||
* Copyright (C) 2006 JBoss Inc., All Rights Reserved. | |||||
* | |||||
* The contents of this file are subject to the Mozilla Public License Version | |||||
* 1.1 (the "License"); you may not use this file except in compliance with | |||||
* the License. Alternatively, the contents of this file may be used under | |||||
* the terms of the GNU Lesser General Public License Version 2.1 or later. | |||||
* | |||||
* Software distributed under the License is distributed on an "AS IS" | |||||
basis, | |||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License | |||||
* for the specific language governing rights and limitations under the | |||||
* License. | |||||
*/ | |||||
* Javassist, a Java-bytecode translator toolkit. | |||||
* Copyright (C) 2006 JBoss Inc. All Rights Reserved. | |||||
* | |||||
* The contents of this file are subject to the Mozilla Public License Version | |||||
* 1.1 (the "License"); you may not use this file except in compliance with | |||||
* the License. Alternatively, the contents of this file may be used under | |||||
* the terms of the GNU Lesser General Public License Version 2.1 or later. | |||||
* | |||||
* Software distributed under the License is distributed on an "AS IS" basis, | |||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License | |||||
* for the specific language governing rights and limitations under the | |||||
* License. | |||||
*/ | |||||
package javassist.scopedpool; | package javassist.scopedpool; | ||||
import java.util.ArrayList; | import java.util.ArrayList; | ||||
import javassist.LoaderClassPath; | import javassist.LoaderClassPath; | ||||
/** | /** | ||||
* | |||||
* An implementation of <code>ScopedClassPoolRepository</code>. | |||||
* It is an singleton. | |||||
* | |||||
* @author <a href="kabir.khan@jboss.com">Kabir Khan</a> | * @author <a href="kabir.khan@jboss.com">Kabir Khan</a> | ||||
* @version $Revision: 1.2 $ | |||||
* @version $Revision: 1.3 $ | |||||
*/ | */ | ||||
public class ScopedClassPoolRepositoryImpl implements ScopedClassPoolRepository | |||||
{ | |||||
/** The instance */ | |||||
private static final ScopedClassPoolRepositoryImpl instance = new ScopedClassPoolRepositoryImpl(); | |||||
/** Whether to prune */ | |||||
private boolean prune = true; | |||||
/** Whether to prune when added to the classpool's cache */ | |||||
boolean pruneWhenCached; | |||||
/** The registered classloaders */ | |||||
protected Map registeredCLs = Collections.synchronizedMap(new WeakHashMap()); | |||||
/** The default class pool */ | |||||
protected ClassPool classpool; | |||||
/** The factory for creating class pools */ | |||||
protected ScopedClassPoolFactory factory = new ScopedClassPoolFactoryImpl(); | |||||
/** | |||||
* Get the instance | |||||
* | |||||
* @return the instance | |||||
*/ | |||||
public static ScopedClassPoolRepository getInstance() | |||||
{ | |||||
return instance; | |||||
} | |||||
/** | |||||
* Singleton | |||||
*/ | |||||
private ScopedClassPoolRepositoryImpl() | |||||
{ | |||||
classpool = ClassPool.getDefault(); | |||||
// FIXME This doesn't look correct | |||||
ClassLoader cl = Thread.currentThread().getContextClassLoader(); | |||||
classpool.insertClassPath(new LoaderClassPath(cl)); | |||||
} | |||||
/** | |||||
* Get the prune. | |||||
* | |||||
* @return the prune. | |||||
*/ | |||||
public boolean isPrune() | |||||
{ | |||||
return prune; | |||||
} | |||||
/** | |||||
* Set the prune. | |||||
* | |||||
* @param prune the prune. | |||||
*/ | |||||
public void setPrune(boolean prune) | |||||
{ | |||||
this.prune = prune; | |||||
} | |||||
/** | |||||
* Create a scoped classpool | |||||
* | |||||
* @param cl the classloader | |||||
* @param src the original classpool | |||||
* @return the classpool | |||||
*/ | |||||
public ScopedClassPool createScopedClassPool(ClassLoader cl, ClassPool src) | |||||
{ | |||||
return factory.create(cl, src, this); | |||||
} | |||||
public ClassPool findClassPool(ClassLoader cl) | |||||
{ | |||||
if (cl == null) | |||||
return registerClassLoader(ClassLoader.getSystemClassLoader()); | |||||
return registerClassLoader(cl); | |||||
} | |||||
/** | |||||
* Register a classloader | |||||
* | |||||
* @param ucl the classloader | |||||
* @return the classpool | |||||
*/ | |||||
public ClassPool registerClassLoader(ClassLoader ucl) | |||||
{ | |||||
synchronized (registeredCLs) | |||||
{ | |||||
// FIXME: Probably want to take this method out later | |||||
// so that AOP framework can be independent of JMX | |||||
// This is in here so that we can remove a UCL from the ClassPool as a | |||||
// ClassPool.classpath | |||||
if (registeredCLs.containsKey(ucl)) | |||||
{ | |||||
return (ClassPool) registeredCLs.get(ucl); | |||||
} | |||||
ScopedClassPool pool = createScopedClassPool(ucl, classpool); | |||||
registeredCLs.put(ucl, pool); | |||||
return pool; | |||||
} | |||||
} | |||||
/** | |||||
* Get the registered classloaders | |||||
* | |||||
* @return the registered classloaders | |||||
*/ | |||||
public Map getRegisteredCLs() | |||||
{ | |||||
clearUnregisteredClassLoaders(); | |||||
return registeredCLs; | |||||
} | |||||
/** | |||||
* This method will check to see if a register classloader has been undeployed (as in JBoss) | |||||
*/ | |||||
public void clearUnregisteredClassLoaders() | |||||
{ | |||||
ArrayList toUnregister = null; | |||||
synchronized (registeredCLs) | |||||
{ | |||||
Iterator it = registeredCLs.values().iterator(); | |||||
while (it.hasNext()) | |||||
{ | |||||
ScopedClassPool pool = (ScopedClassPool) it.next(); | |||||
if (pool.isUnloadedClassLoader()) | |||||
{ | |||||
it.remove(); | |||||
ClassLoader cl = pool.getClassLoader(); | |||||
if (cl != null) | |||||
{ | |||||
if (toUnregister == null) | |||||
{ | |||||
toUnregister = new ArrayList(); | |||||
} | |||||
toUnregister.add(cl); | |||||
} | |||||
public class ScopedClassPoolRepositoryImpl implements ScopedClassPoolRepository { | |||||
/** The instance */ | |||||
private static final ScopedClassPoolRepositoryImpl instance = new ScopedClassPoolRepositoryImpl(); | |||||
/** Whether to prune */ | |||||
private boolean prune = true; | |||||
/** Whether to prune when added to the classpool's cache */ | |||||
boolean pruneWhenCached; | |||||
/** The registered classloaders */ | |||||
protected Map registeredCLs = Collections | |||||
.synchronizedMap(new WeakHashMap()); | |||||
/** The default class pool */ | |||||
protected ClassPool classpool; | |||||
/** The factory for creating class pools */ | |||||
protected ScopedClassPoolFactory factory = new ScopedClassPoolFactoryImpl(); | |||||
/** | |||||
* Get the instance. | |||||
* | |||||
* @return the instance. | |||||
*/ | |||||
public static ScopedClassPoolRepository getInstance() { | |||||
return instance; | |||||
} | |||||
/** | |||||
* Singleton. | |||||
*/ | |||||
private ScopedClassPoolRepositoryImpl() { | |||||
classpool = ClassPool.getDefault(); | |||||
// FIXME This doesn't look correct | |||||
ClassLoader cl = Thread.currentThread().getContextClassLoader(); | |||||
classpool.insertClassPath(new LoaderClassPath(cl)); | |||||
} | |||||
/** | |||||
* Returns the value of the prune attribute. | |||||
* | |||||
* @return the prune. | |||||
*/ | |||||
public boolean isPrune() { | |||||
return prune; | |||||
} | |||||
/** | |||||
* Set the prune attribute. | |||||
* | |||||
* @param prune a new value. | |||||
*/ | |||||
public void setPrune(boolean prune) { | |||||
this.prune = prune; | |||||
} | |||||
/** | |||||
* Create a scoped classpool. | |||||
* | |||||
* @param cl the classloader. | |||||
* @param src the original classpool. | |||||
* @return the classpool | |||||
*/ | |||||
public ScopedClassPool createScopedClassPool(ClassLoader cl, ClassPool src) { | |||||
return factory.create(cl, src, this); | |||||
} | |||||
public ClassPool findClassPool(ClassLoader cl) { | |||||
if (cl == null) | |||||
return registerClassLoader(ClassLoader.getSystemClassLoader()); | |||||
return registerClassLoader(cl); | |||||
} | |||||
/** | |||||
* Register a classloader. | |||||
* | |||||
* @param ucl the classloader. | |||||
* @return the classpool | |||||
*/ | |||||
public ClassPool registerClassLoader(ClassLoader ucl) { | |||||
synchronized (registeredCLs) { | |||||
// FIXME: Probably want to take this method out later | |||||
// so that AOP framework can be independent of JMX | |||||
// This is in here so that we can remove a UCL from the ClassPool as | |||||
// a | |||||
// ClassPool.classpath | |||||
if (registeredCLs.containsKey(ucl)) { | |||||
return (ClassPool)registeredCLs.get(ucl); | |||||
} | |||||
ScopedClassPool pool = createScopedClassPool(ucl, classpool); | |||||
registeredCLs.put(ucl, pool); | |||||
return pool; | |||||
} | |||||
} | |||||
/** | |||||
* Get the registered classloaders. | |||||
*/ | |||||
public Map getRegisteredCLs() { | |||||
clearUnregisteredClassLoaders(); | |||||
return registeredCLs; | |||||
} | |||||
/** | |||||
* This method will check to see if a register classloader has been | |||||
* undeployed (as in JBoss) | |||||
*/ | |||||
public void clearUnregisteredClassLoaders() { | |||||
ArrayList toUnregister = null; | |||||
synchronized (registeredCLs) { | |||||
Iterator it = registeredCLs.values().iterator(); | |||||
while (it.hasNext()) { | |||||
ScopedClassPool pool = (ScopedClassPool)it.next(); | |||||
if (pool.isUnloadedClassLoader()) { | |||||
it.remove(); | |||||
ClassLoader cl = pool.getClassLoader(); | |||||
if (cl != null) { | |||||
if (toUnregister == null) { | |||||
toUnregister = new ArrayList(); | |||||
} | |||||
toUnregister.add(cl); | |||||
} | |||||
} | |||||
} | } | ||||
} | |||||
if (toUnregister != null) | |||||
{ | |||||
for (int i = 0; i < toUnregister.size(); i++) | |||||
{ | |||||
unregisterClassLoader((ClassLoader) toUnregister.get(i)); | |||||
if (toUnregister != null) { | |||||
for (int i = 0; i < toUnregister.size(); i++) { | |||||
unregisterClassLoader((ClassLoader)toUnregister.get(i)); | |||||
} | |||||
} | } | ||||
} | |||||
} | |||||
} | |||||
public void unregisterClassLoader(ClassLoader cl) | |||||
{ | |||||
synchronized (registeredCLs) | |||||
{ | |||||
ScopedClassPool pool = (ScopedClassPool) registeredCLs.remove(cl); | |||||
if (pool != null) pool.close(); | |||||
} | |||||
} | |||||
public void insertDelegate(ScopedClassPoolRepository delegate) | |||||
{ | |||||
//Noop - this is the end | |||||
} | |||||
public void setClassPoolFactory(ScopedClassPoolFactory factory) | |||||
{ | |||||
this.factory = factory; | |||||
} | |||||
public ScopedClassPoolFactory getClassPoolFactory() | |||||
{ | |||||
return factory; | |||||
} | |||||
} | |||||
} | |||||
public void unregisterClassLoader(ClassLoader cl) { | |||||
synchronized (registeredCLs) { | |||||
ScopedClassPool pool = (ScopedClassPool)registeredCLs.remove(cl); | |||||
if (pool != null) | |||||
pool.close(); | |||||
} | |||||
} | |||||
public void insertDelegate(ScopedClassPoolRepository delegate) { | |||||
// Noop - this is the end | |||||
} | |||||
public void setClassPoolFactory(ScopedClassPoolFactory factory) { | |||||
this.factory = factory; | |||||
} | |||||
public ScopedClassPoolFactory getClassPoolFactory() { | |||||
return factory; | |||||
} | |||||
} | } |
/* | /* | ||||
* Javassist, a Java-bytecode translator toolkit. | |||||
* Copyright (C) 2006 JBoss Inc., All Rights Reserved. | |||||
* | |||||
* The contents of this file are subject to the Mozilla Public License Version | |||||
* 1.1 (the "License"); you may not use this file except in compliance with | |||||
* the License. Alternatively, the contents of this file may be used under | |||||
* the terms of the GNU Lesser General Public License Version 2.1 or later. | |||||
* | |||||
* Software distributed under the License is distributed on an "AS IS" | |||||
basis, | |||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License | |||||
* for the specific language governing rights and limitations under the | |||||
* License. | |||||
*/ | |||||
* Javassist, a Java-bytecode translator toolkit. | |||||
* Copyright (C) 2006 JBoss Inc. All Rights Reserved. | |||||
* | |||||
* The contents of this file are subject to the Mozilla Public License Version | |||||
* 1.1 (the "License"); you may not use this file except in compliance with | |||||
* the License. Alternatively, the contents of this file may be used under | |||||
* the terms of the GNU Lesser General Public License Version 2.1 or later. | |||||
* | |||||
* Software distributed under the License is distributed on an "AS IS" basis, | |||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License | |||||
* for the specific language governing rights and limitations under the | |||||
* License. | |||||
*/ | |||||
package javassist.scopedpool; | package javassist.scopedpool; | ||||
import java.lang.ref.ReferenceQueue; | import java.lang.ref.ReferenceQueue; | ||||
import java.util.Map; | import java.util.Map; | ||||
import java.util.Set; | import java.util.Set; | ||||
/** | /** | ||||
* This Map will remove entries when the value in the map has been | |||||
* cleaned from garbage collection | |||||
* | |||||
* @version <tt>$Revision: 1.2 $</tt> | |||||
* @author <a href="mailto:bill@jboss.org">Bill Burke</a> | |||||
* This Map will remove entries when the value in the map has been cleaned from | |||||
* garbage collection | |||||
* | |||||
* @version <tt>$Revision: 1.3 $</tt> | |||||
* @author <a href="mailto:bill@jboss.org">Bill Burke</a> | |||||
*/ | */ | ||||
public class SoftValueHashMap | |||||
extends AbstractMap | |||||
implements Map | |||||
{ | |||||
private static class SoftValueRef extends SoftReference | |||||
{ | |||||
public Object key; | |||||
private SoftValueRef(Object key, Object val, ReferenceQueue q) | |||||
{ | |||||
super(val, q); | |||||
this.key = key; | |||||
} | |||||
private static SoftValueRef create(Object key, Object val, ReferenceQueue q) | |||||
{ | |||||
if (val == null) return null; | |||||
else return new SoftValueRef(key, val, q); | |||||
} | |||||
} | |||||
public Set entrySet() | |||||
{ | |||||
processQueue(); | |||||
return hash.entrySet(); | |||||
} | |||||
/* Hash table mapping WeakKeys to values */ | |||||
private Map hash; | |||||
/* Reference queue for cleared WeakKeys */ | |||||
private ReferenceQueue queue = new ReferenceQueue(); | |||||
/* Remove all invalidated entries from the map, that is, remove all entries | |||||
whose values have been discarded. | |||||
*/ | |||||
private void processQueue() | |||||
{ | |||||
SoftValueRef ref; | |||||
while ((ref = (SoftValueRef)queue.poll()) != null) { | |||||
if (ref == (SoftValueRef) hash.get(ref.key)) { | |||||
// only remove if it is the *exact* same WeakValueRef | |||||
// | |||||
hash.remove(ref.key); | |||||
} | |||||
} | |||||
} | |||||
/* -- Constructors -- */ | |||||
/** | |||||
* Constructs a new, empty <code>WeakHashMap</code> with the given | |||||
* initial capacity and the given load factor. | |||||
* | |||||
* @param initialCapacity The initial capacity of the | |||||
* <code>WeakHashMap</code> | |||||
* | |||||
* @param loadFactor The load factor of the <code>WeakHashMap</code> | |||||
* | |||||
* @throws IllegalArgumentException If the initial capacity is less than | |||||
* zero, or if the load factor is | |||||
* nonpositive | |||||
*/ | |||||
public SoftValueHashMap(int initialCapacity, float loadFactor) | |||||
{ | |||||
hash = new HashMap(initialCapacity, loadFactor); | |||||
} | |||||
/** | |||||
* Constructs a new, empty <code>WeakHashMap</code> with the given | |||||
* initial capacity and the default load factor, which is | |||||
* <code>0.75</code>. | |||||
* | |||||
* @param initialCapacity The initial capacity of the | |||||
* <code>WeakHashMap</code> | |||||
* | |||||
* @throws IllegalArgumentException If the initial capacity is less than | |||||
* zero | |||||
*/ | |||||
public SoftValueHashMap(int initialCapacity) | |||||
{ | |||||
hash = new HashMap(initialCapacity); | |||||
} | |||||
/** | |||||
* Constructs a new, empty <code>WeakHashMap</code> with the default | |||||
* initial capacity and the default load factor, which is | |||||
* <code>0.75</code>. | |||||
*/ | |||||
public SoftValueHashMap() | |||||
{ | |||||
hash = new HashMap(); | |||||
} | |||||
/** | |||||
* Constructs a new <code>WeakHashMap</code> with the same mappings as the | |||||
* specified <tt>Map</tt>. The <code>WeakHashMap</code> is created with an | |||||
* initial capacity of twice the number of mappings in the specified map | |||||
* or 11 (whichever is greater), and a default load factor, which is | |||||
* <tt>0.75</tt>. | |||||
* | |||||
* @param t the map whose mappings are to be placed in this map. | |||||
* @since 1.3 | |||||
*/ | |||||
public SoftValueHashMap(Map t) | |||||
{ | |||||
this(Math.max(2*t.size(), 11), 0.75f); | |||||
putAll(t); | |||||
} | |||||
/* -- Simple queries -- */ | |||||
/** | |||||
* Returns the number of key-value mappings in this map. | |||||
* <strong>Note:</strong> <em>In contrast with most implementations of the | |||||
* <code>Map</code> interface, the time required by this operation is | |||||
* linear in the size of the map.</em> | |||||
*/ | |||||
public int size() | |||||
{ | |||||
processQueue(); | |||||
return hash.size(); | |||||
} | |||||
/** | |||||
* Returns <code>true</code> if this map contains no key-value mappings. | |||||
*/ | |||||
public boolean isEmpty() | |||||
{ | |||||
processQueue(); | |||||
return hash.isEmpty(); | |||||
} | |||||
/** | |||||
* Returns <code>true</code> if this map contains a mapping for the | |||||
* specified key. | |||||
* | |||||
* @param key The key whose presence in this map is to be tested | |||||
*/ | |||||
public boolean containsKey(Object key) | |||||
{ | |||||
processQueue(); | |||||
return hash.containsKey(key); | |||||
} | |||||
/* -- Lookup and modification operations -- */ | |||||
/** | |||||
* Returns the value to which this map maps the specified <code>key</code>. | |||||
* If this map does not contain a value for this key, then return | |||||
* <code>null</code>. | |||||
* | |||||
* @param key The key whose associated value, if any, is to be returned | |||||
*/ | |||||
public Object get(Object key) | |||||
{ | |||||
processQueue(); | |||||
SoftReference ref = (SoftReference)hash.get(key); | |||||
if (ref != null) return ref.get(); | |||||
return null; | |||||
} | |||||
/** | |||||
* Updates this map so that the given <code>key</code> maps to the given | |||||
* <code>value</code>. If the map previously contained a mapping for | |||||
* <code>key</code> then that mapping is replaced and the previous value is | |||||
* returned. | |||||
* | |||||
* @param key The key that is to be mapped to the given | |||||
* <code>value</code> | |||||
* @param value The value to which the given <code>key</code> is to be | |||||
* mapped | |||||
* | |||||
* @return The previous value to which this key was mapped, or | |||||
* <code>null</code> if if there was no mapping for the key | |||||
*/ | |||||
public Object put(Object key, Object value) | |||||
{ | |||||
processQueue(); | |||||
Object rtn = hash.put(key, SoftValueRef.create(key, value, queue)); | |||||
if (rtn != null) rtn = ((SoftReference)rtn).get(); | |||||
return rtn; | |||||
} | |||||
/** | |||||
* Removes the mapping for the given <code>key</code> from this map, if | |||||
* present. | |||||
* | |||||
* @param key The key whose mapping is to be removed | |||||
* | |||||
* @return The value to which this key was mapped, or <code>null</code> if | |||||
* there was no mapping for the key | |||||
*/ | |||||
public Object remove(Object key) | |||||
{ | |||||
processQueue(); | |||||
return hash.remove(key); | |||||
} | |||||
/** | |||||
* Removes all mappings from this map. | |||||
*/ | |||||
public void clear() | |||||
{ | |||||
processQueue(); | |||||
hash.clear(); | |||||
} | |||||
public class SoftValueHashMap extends AbstractMap implements Map { | |||||
private static class SoftValueRef extends SoftReference { | |||||
public Object key; | |||||
private SoftValueRef(Object key, Object val, ReferenceQueue q) { | |||||
super(val, q); | |||||
this.key = key; | |||||
} | |||||
private static SoftValueRef create(Object key, Object val, | |||||
ReferenceQueue q) { | |||||
if (val == null) | |||||
return null; | |||||
else | |||||
return new SoftValueRef(key, val, q); | |||||
} | |||||
} | |||||
/** | |||||
* Returns a set of the mappings contained in this hash table. | |||||
*/ | |||||
public Set entrySet() { | |||||
processQueue(); | |||||
return hash.entrySet(); | |||||
} | |||||
/* Hash table mapping WeakKeys to values */ | |||||
private Map hash; | |||||
/* Reference queue for cleared WeakKeys */ | |||||
private ReferenceQueue queue = new ReferenceQueue(); | |||||
/* | |||||
* Remove all invalidated entries from the map, that is, remove all entries | |||||
* whose values have been discarded. | |||||
*/ | |||||
private void processQueue() { | |||||
SoftValueRef ref; | |||||
while ((ref = (SoftValueRef)queue.poll()) != null) { | |||||
if (ref == (SoftValueRef)hash.get(ref.key)) { | |||||
// only remove if it is the *exact* same WeakValueRef | |||||
// | |||||
hash.remove(ref.key); | |||||
} | |||||
} | |||||
} | |||||
/* -- Constructors -- */ | |||||
/** | |||||
* Constructs a new, empty <code>WeakHashMap</code> with the given initial | |||||
* capacity and the given load factor. | |||||
* | |||||
* @param initialCapacity | |||||
* The initial capacity of the <code>WeakHashMap</code> | |||||
* | |||||
* @param loadFactor | |||||
* The load factor of the <code>WeakHashMap</code> | |||||
* | |||||
* @throws IllegalArgumentException | |||||
* If the initial capacity is less than zero, or if the load | |||||
* factor is nonpositive | |||||
*/ | |||||
public SoftValueHashMap(int initialCapacity, float loadFactor) { | |||||
hash = new HashMap(initialCapacity, loadFactor); | |||||
} | |||||
/** | |||||
* Constructs a new, empty <code>WeakHashMap</code> with the given initial | |||||
* capacity and the default load factor, which is <code>0.75</code>. | |||||
* | |||||
* @param initialCapacity | |||||
* The initial capacity of the <code>WeakHashMap</code> | |||||
* | |||||
* @throws IllegalArgumentException | |||||
* If the initial capacity is less than zero | |||||
*/ | |||||
public SoftValueHashMap(int initialCapacity) { | |||||
hash = new HashMap(initialCapacity); | |||||
} | |||||
/** | |||||
* Constructs a new, empty <code>WeakHashMap</code> with the default | |||||
* initial capacity and the default load factor, which is <code>0.75</code>. | |||||
*/ | |||||
public SoftValueHashMap() { | |||||
hash = new HashMap(); | |||||
} | |||||
/** | |||||
* Constructs a new <code>WeakHashMap</code> with the same mappings as the | |||||
* specified <tt>Map</tt>. The <code>WeakHashMap</code> is created with | |||||
* an initial capacity of twice the number of mappings in the specified map | |||||
* or 11 (whichever is greater), and a default load factor, which is | |||||
* <tt>0.75</tt>. | |||||
* | |||||
* @param t the map whose mappings are to be placed in this map. | |||||
*/ | |||||
public SoftValueHashMap(Map t) { | |||||
this(Math.max(2 * t.size(), 11), 0.75f); | |||||
putAll(t); | |||||
} | |||||
/* -- Simple queries -- */ | |||||
/** | |||||
* Returns the number of key-value mappings in this map. <strong>Note:</strong> | |||||
* <em>In contrast with most implementations of the | |||||
* <code>Map</code> interface, the time required by this operation is | |||||
* linear in the size of the map.</em> | |||||
*/ | |||||
public int size() { | |||||
processQueue(); | |||||
return hash.size(); | |||||
} | |||||
/** | |||||
* Returns <code>true</code> if this map contains no key-value mappings. | |||||
*/ | |||||
public boolean isEmpty() { | |||||
processQueue(); | |||||
return hash.isEmpty(); | |||||
} | |||||
/** | |||||
* Returns <code>true</code> if this map contains a mapping for the | |||||
* specified key. | |||||
* | |||||
* @param key | |||||
* The key whose presence in this map is to be tested. | |||||
*/ | |||||
public boolean containsKey(Object key) { | |||||
processQueue(); | |||||
return hash.containsKey(key); | |||||
} | |||||
/* -- Lookup and modification operations -- */ | |||||
/** | |||||
* Returns the value to which this map maps the specified <code>key</code>. | |||||
* If this map does not contain a value for this key, then return | |||||
* <code>null</code>. | |||||
* | |||||
* @param key | |||||
* The key whose associated value, if any, is to be returned. | |||||
*/ | |||||
public Object get(Object key) { | |||||
processQueue(); | |||||
SoftReference ref = (SoftReference)hash.get(key); | |||||
if (ref != null) | |||||
return ref.get(); | |||||
return null; | |||||
} | |||||
/** | |||||
* Updates this map so that the given <code>key</code> maps to the given | |||||
* <code>value</code>. If the map previously contained a mapping for | |||||
* <code>key</code> then that mapping is replaced and the previous value | |||||
* is returned. | |||||
* | |||||
* @param key | |||||
* The key that is to be mapped to the given <code>value</code> | |||||
* @param value | |||||
* The value to which the given <code>key</code> is to be | |||||
* mapped | |||||
* | |||||
* @return The previous value to which this key was mapped, or | |||||
* <code>null</code> if if there was no mapping for the key | |||||
*/ | |||||
public Object put(Object key, Object value) { | |||||
processQueue(); | |||||
Object rtn = hash.put(key, SoftValueRef.create(key, value, queue)); | |||||
if (rtn != null) | |||||
rtn = ((SoftReference)rtn).get(); | |||||
return rtn; | |||||
} | |||||
/** | |||||
* Removes the mapping for the given <code>key</code> from this map, if | |||||
* present. | |||||
* | |||||
* @param key | |||||
* The key whose mapping is to be removed. | |||||
* | |||||
* @return The value to which this key was mapped, or <code>null</code> if | |||||
* there was no mapping for the key. | |||||
*/ | |||||
public Object remove(Object key) { | |||||
processQueue(); | |||||
return hash.remove(key); | |||||
} | |||||
/** | |||||
* Removes all mappings from this map. | |||||
*/ | |||||
public void clear() { | |||||
processQueue(); | |||||
hash.clear(); | |||||
} | |||||
} | } |
<html> | |||||
<body> | |||||
<p>A custom class pool for several JBoss products. | |||||
It is not part of Javassist. | |||||
</p> | |||||
</body> | |||||
</html> |