]> source.dussan.org Git - javassist.git/commitdiff
Fixes to javassist.scopepool 159/head
authornickl- <github@jigsoft.co.za>
Sun, 29 Oct 2017 21:42:49 +0000 (23:42 +0200)
committernickl- <github@jigsoft.co.za>
Sun, 12 Nov 2017 21:52:01 +0000 (23:52 +0200)
Applied parameters and squashed all compiler warnings.
Changed loops to enhanced for where appropriate.
Fixed SoftValueHashMap:
Made it a raw type.
Overwritten implementation of the `get` method correctly unwrapped the reference but the methods
`containsValue` and `entrySet` needed to be fixed.
Implemented the remaining outstanding methods from the `java.util.Map` contract `values`, `putAll`
and `keySet`.
Dropped `AbstractMap` inheritance as we were not inheriting anything anymore.
Changed internal map to `ConcurrentHashMap' to be atomic and parallel safe.

Found original unit tests from jboss retro which are now updated to reflect the javassist
versions of the scoped pool classes. Added tests for the SoftValueHashMap including
test which overloads the heap to ensure the cache cleans itself appropriately.

14 files changed:
src/main/javassist/scopedpool/ScopedClassPool.java
src/main/javassist/scopedpool/ScopedClassPoolRepository.java
src/main/javassist/scopedpool/ScopedClassPoolRepositoryImpl.java
src/main/javassist/scopedpool/SoftValueHashMap.java
src/test/scoped/ScopedRepositoryTestCase.java [new file with mode: 0644]
src/test/scoped/TestAnnotation.java [new file with mode: 0644]
src/test/scoped/UnscopedAnnotationDefaultUsage.java [new file with mode: 0644]
src/test/scoped/UnscopedAnnotationUsage.java [new file with mode: 0644]
src/test/scoped/jar1/FullyScopedAnnotationDefaultUsage.java [new file with mode: 0644]
src/test/scoped/jar1/FullyScopedAnnotationUsage.java [new file with mode: 0644]
src/test/scoped/jar1/ScopedAnnotationDefaultUsage.java [new file with mode: 0644]
src/test/scoped/jar1/ScopedAnnotationUsage.java [new file with mode: 0644]
src/test/scoped/jar1/ScopedTestAnnotation.java [new file with mode: 0644]
src/test/scoped/jar1/TestClass1.java [new file with mode: 0644]

index bcda0dedf89959e4689e96a1e5c18370779819d4..fb87709d82da16574bc5db1ba19867dda4527f43 100644 (file)
@@ -16,9 +16,9 @@
 
 package javassist.scopedpool;
 
+import java.lang.ref.Reference;
 import java.lang.ref.WeakReference;
 import java.security.ProtectionDomain;
-import java.util.Iterator;
 import java.util.Map;
 import javassist.CannotCompileException;
 import javassist.ClassPool;
@@ -37,11 +37,11 @@ import javassist.NotFoundException;
 public class ScopedClassPool extends ClassPool {
     protected ScopedClassPoolRepository repository;
 
-    protected WeakReference classLoader;
+    protected Reference<ClassLoader> classLoader;
 
     protected LoaderClassPath classPath;
 
-    protected SoftValueHashMap softcache = new SoftValueHashMap();
+    protected Map<String,CtClass> softcache = new SoftValueHashMap<String,CtClass>();
     
     boolean isBootstrapCl = true;
 
@@ -59,10 +59,10 @@ public class ScopedClassPool extends ClassPool {
      *            the original class pool
      * @param repository
      *            the repository
-     *@deprecated
      */
     protected ScopedClassPool(ClassLoader cl, ClassPool src,
-            ScopedClassPoolRepository repository) {
+            ScopedClassPoolRepository repository)
+    {
        this(cl, src, repository, false);
     }
     
@@ -78,11 +78,12 @@ public class ScopedClassPool extends ClassPool {
      * @param isTemp
      *            Whether this is a temporary pool used to resolve references
      */
-    protected ScopedClassPool(ClassLoader cl, ClassPool src, ScopedClassPoolRepository repository, boolean isTemp)
+    protected ScopedClassPool(ClassLoader cl, ClassPool src,
+            ScopedClassPoolRepository repository, boolean isTemp)
     {
        super(src);
        this.repository = repository;
-       this.classLoader = new WeakReference(cl);
+       this.classLoader = new WeakReference<ClassLoader>(cl);
        if (cl != null) {
            classPath = new LoaderClassPath(cl);
            this.insertClassPath(classPath);
@@ -110,7 +111,7 @@ public class ScopedClassPool extends ClassPool {
     }
 
     protected ClassLoader getClassLoader0() {
-       return (ClassLoader)classLoader.get();
+       return classLoader.get();
     }
 
     /**
@@ -163,6 +164,7 @@ public class ScopedClassPool extends ClassPool {
      *            the class name
      * @return the class
      */
+    @Override
     protected CtClass getCached(String classname) {
         CtClass clazz = getCachedLocally(classname);
         if (clazz == null) {
@@ -186,11 +188,9 @@ public class ScopedClassPool extends ClassPool {
             }
 
             if (!isLocal) {
-                Map registeredCLs = repository.getRegisteredCLs();
+                Map<ClassLoader,ScopedClassPool> registeredCLs = repository.getRegisteredCLs();
                 synchronized (registeredCLs) {
-                    Iterator it = registeredCLs.values().iterator();
-                    while (it.hasNext()) {
-                        ScopedClassPool pool = (ScopedClassPool)it.next();
+                    for (ScopedClassPool pool:registeredCLs.values()) {
                         if (pool.isUnloadedClassLoader()) {
                             repository.unregisterClassLoader(pool
                                     .getClassLoader());
@@ -289,7 +289,7 @@ public class ScopedClassPool extends ClassPool {
      * @throws CannotCompileException
      *             for any error
      */
-    public Class toClass(CtClass ct, ClassLoader loader, ProtectionDomain domain)
+    public Class<?> toClass(CtClass ct, ClassLoader loader, ProtectionDomain domain)
             throws CannotCompileException {
         // We need to pass up the classloader stored in this pool, as the
         // default implementation uses the Thread context cl.
index 497501bed37db4443d52a6023fa98593ff1344e8..33e47124630651c2c37e66f3fea9c159e718d117 100644 (file)
@@ -81,7 +81,7 @@ public interface ScopedClassPoolRepository {
      * 
      * @return the registered classloaders.
      */
-    Map getRegisteredCLs();
+    Map<ClassLoader,ScopedClassPool> getRegisteredCLs();
 
     /**
      * This method will check to see if a register classloader has been
index a9a3c822a98d2e71e51b2ed859c8e10cb3eb7f14..8c20d97c5cddccfa15a805af28461e70b98da802 100644 (file)
@@ -18,7 +18,7 @@ package javassist.scopedpool;
 
 import java.util.ArrayList;
 import java.util.Collections;
-import java.util.Iterator;
+import java.util.List;
 import java.util.Map;
 import java.util.WeakHashMap;
 
@@ -43,8 +43,8 @@ public class ScopedClassPoolRepositoryImpl implements ScopedClassPoolRepository
     boolean pruneWhenCached;
 
     /** The registered classloaders */
-    protected Map registeredCLs = Collections
-            .synchronizedMap(new WeakHashMap());
+    protected Map<ClassLoader,ScopedClassPool> registeredCLs = Collections
+            .synchronizedMap(new WeakHashMap<ClassLoader,ScopedClassPool>());
 
     /** The default class pool */
     protected ClassPool classpool;
@@ -76,6 +76,7 @@ public class ScopedClassPoolRepositoryImpl implements ScopedClassPoolRepository
      * 
      * @return the prune.
      */
+    @Override
     public boolean isPrune() {
         return prune;
     }
@@ -85,6 +86,7 @@ public class ScopedClassPoolRepositoryImpl implements ScopedClassPoolRepository
      * 
      * @param prune     a new value.
      */
+    @Override
     public void setPrune(boolean prune) {
         this.prune = prune;
     }
@@ -96,10 +98,12 @@ public class ScopedClassPoolRepositoryImpl implements ScopedClassPoolRepository
      * @param src   the original classpool.
      * @return the classpool
      */
+    @Override
     public ScopedClassPool createScopedClassPool(ClassLoader cl, ClassPool src) {
         return factory.create(cl, src, this);
     }
 
+    @Override
     public ClassPool findClassPool(ClassLoader cl) {
         if (cl == null)
             return registerClassLoader(ClassLoader.getSystemClassLoader());
@@ -113,6 +117,7 @@ public class ScopedClassPoolRepositoryImpl implements ScopedClassPoolRepository
      * @param ucl       the classloader.
      * @return the classpool
      */
+    @Override
     public ClassPool registerClassLoader(ClassLoader ucl) {
         synchronized (registeredCLs) {
             // FIXME: Probably want to take this method out later
@@ -121,7 +126,7 @@ public class ScopedClassPoolRepositoryImpl implements ScopedClassPoolRepository
             // a
             // ClassPool.classpath
             if (registeredCLs.containsKey(ucl)) {
-                return (ClassPool)registeredCLs.get(ucl);
+                return registeredCLs.get(ucl);
             }
             ScopedClassPool pool = createScopedClassPool(ucl, classpool);
             registeredCLs.put(ucl, pool);
@@ -132,7 +137,8 @@ public class ScopedClassPoolRepositoryImpl implements ScopedClassPoolRepository
     /**
      * Get the registered classloaders.
      */
-    public Map getRegisteredCLs() {
+    @Override
+    public Map<ClassLoader,ScopedClassPool> getRegisteredCLs() {
         clearUnregisteredClassLoaders();
         return registeredCLs;
     }
@@ -141,34 +147,31 @@ public class ScopedClassPoolRepositoryImpl implements ScopedClassPoolRepository
      * This method will check to see if a register classloader has been
      * undeployed (as in JBoss)
      */
+    @Override
     public void clearUnregisteredClassLoaders() {
-        ArrayList toUnregister = null;
+        List<ClassLoader> 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();
+            for (Map.Entry<ClassLoader,ScopedClassPool> reg:registeredCLs.entrySet()) {
+                if (reg.getValue().isUnloadedClassLoader()) {
+                    ClassLoader cl = reg.getValue().getClassLoader();
                     if (cl != null) {
-                        if (toUnregister == null) {
-                            toUnregister = new ArrayList();
-                        }
+                        if (toUnregister == null)
+                            toUnregister = new ArrayList<ClassLoader>();
                         toUnregister.add(cl);
                     }
+                    registeredCLs.remove(reg.getKey());
                 }
             }
-            if (toUnregister != null) {
-                for (int i = 0; i < toUnregister.size(); i++) {
-                    unregisterClassLoader((ClassLoader)toUnregister.get(i));
-                }
-            }
+            if (toUnregister != null)
+                for (ClassLoader cl:toUnregister)
+                    unregisterClassLoader(cl);
         }
     }
 
+    @Override
     public void unregisterClassLoader(ClassLoader cl) {
         synchronized (registeredCLs) {
-            ScopedClassPool pool = (ScopedClassPool)registeredCLs.remove(cl);
+            ScopedClassPool pool = registeredCLs.remove(cl);
             if (pool != null)
                 pool.close();
         }
@@ -178,10 +181,12 @@ public class ScopedClassPoolRepositoryImpl implements ScopedClassPoolRepository
         // Noop - this is the end
     }
 
+    @Override
     public void setClassPoolFactory(ScopedClassPoolFactory factory) {
         this.factory = factory;
     }
 
+    @Override
     public ScopedClassPoolFactory getClassPoolFactory() {
         return factory;
     }
index df6ae5998e5d9015e38fd35e833312b4ccf74ea1..1154e62ed6cef2179236ad37f39dda9e8fdfce03 100644 (file)
@@ -18,10 +18,14 @@ package javassist.scopedpool;
 
 import java.lang.ref.ReferenceQueue;
 import java.lang.ref.SoftReference;
-import java.util.AbstractMap;
-import java.util.HashMap;
+import java.util.AbstractMap.SimpleImmutableEntry;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
 
 /**
  * This Map will remove entries when the value in the map has been cleaned from
@@ -30,21 +34,21 @@ import java.util.Set;
  * @version <tt>$Revision: 1.4 $</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;
+public class SoftValueHashMap<K,V> implements Map<K,V> {
+    private static class SoftValueRef<K,V> extends SoftReference<V> {
+        public K key;
 
-        private SoftValueRef(Object key, Object val, ReferenceQueue q) {
+        private SoftValueRef(K key, V val, ReferenceQueue<V> q) {
             super(val, q);
             this.key = key;
         }
 
-        private static SoftValueRef create(Object key, Object val,
-                ReferenceQueue q) {
+        private static <K,V> SoftValueRef<K,V> create(
+                            K key, V val, ReferenceQueue<V> q) {
             if (val == null)
                 return null;
             else
-                return new SoftValueRef(key, val, q);
+                return new SoftValueRef<K,V>(key, val, q);
         }
 
     }
@@ -52,30 +56,37 @@ public class SoftValueHashMap extends AbstractMap implements Map {
     /**
      * Returns a set of the mappings contained in this hash table.
      */
-    public Set entrySet() {
+    @Override
+    public Set<Map.Entry<K, V>> entrySet() {
         processQueue();
-        return hash.entrySet();
+        Set<Entry<K,V>> ret = new HashSet<Entry<K,V>>();
+        for (Entry<K,SoftValueRef<K,V>> e:hash.entrySet()) 
+                ret.add(new SimpleImmutableEntry<K,V> (
+                        e.getKey(), e.getValue().get()));
+        return ret;        
     }
 
     /* Hash table mapping WeakKeys to values */
-    private Map hash;
+    private Map<K,SoftValueRef<K,V>> hash;
 
     /* Reference queue for cleared WeakKeys */
-    private ReferenceQueue queue = new ReferenceQueue();
+    private ReferenceQueue<V> queue = new ReferenceQueue<V>();
 
     /*
      * 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);
+        Object ref;
+        if (!hash.isEmpty())
+        while ((ref = queue.poll()) != null)
+            if (ref instanceof SoftValueRef)  {
+                @SuppressWarnings("rawtypes")
+                SoftValueRef que =(SoftValueRef) ref;
+                if (ref == hash.get(que.key))
+                // only remove if it is the *exact* same SoftValueRef
+                    hash.remove(que.key);
             }
-        }
     }
 
     /* -- Constructors -- */
@@ -95,7 +106,7 @@ public class SoftValueHashMap extends AbstractMap implements Map {
      *             factor is nonpositive
      */
     public SoftValueHashMap(int initialCapacity, float loadFactor) {
-        hash = new HashMap(initialCapacity, loadFactor);
+        hash = new ConcurrentHashMap<K,SoftValueRef<K,V>>(initialCapacity, loadFactor);
     }
 
     /**
@@ -109,7 +120,7 @@ public class SoftValueHashMap extends AbstractMap implements Map {
      *             If the initial capacity is less than zero
      */
     public SoftValueHashMap(int initialCapacity) {
-        hash = new HashMap(initialCapacity);
+        hash = new ConcurrentHashMap<K,SoftValueRef<K,V>>(initialCapacity);
     }
 
     /**
@@ -117,7 +128,7 @@ public class SoftValueHashMap extends AbstractMap implements Map {
      * initial capacity and the default load factor, which is <code>0.75</code>.
      */
     public SoftValueHashMap() {
-        hash = new HashMap();
+        hash = new ConcurrentHashMap<K,SoftValueRef<K,V>>();
     }
 
     /**
@@ -129,7 +140,7 @@ public class SoftValueHashMap extends AbstractMap implements Map {
      * 
      * @param t     the map whose mappings are to be placed in this map.
      */
-    public SoftValueHashMap(Map t) {
+    public SoftValueHashMap(Map<K,V> t) {
         this(Math.max(2 * t.size(), 11), 0.75f);
         putAll(t);
     }
@@ -142,6 +153,7 @@ public class SoftValueHashMap extends AbstractMap implements Map {
      * <code>Map</code> interface, the time required by this operation is
      * linear in the size of the map.</em>
      */
+    @Override
     public int size() {
         processQueue();
         return hash.size();
@@ -150,6 +162,7 @@ public class SoftValueHashMap extends AbstractMap implements Map {
     /**
      * Returns <code>true</code> if this map contains no key-value mappings.
      */
+    @Override
     public boolean isEmpty() {
         processQueue();
         return hash.isEmpty();
@@ -162,6 +175,7 @@ public class SoftValueHashMap extends AbstractMap implements Map {
      * @param key
      *            The key whose presence in this map is to be tested.
      */
+    @Override
     public boolean containsKey(Object key) {
         processQueue();
         return hash.containsKey(key);
@@ -177,12 +191,10 @@ public class SoftValueHashMap extends AbstractMap implements Map {
      * @param key
      *            The key whose associated value, if any, is to be returned.
      */
-    public Object get(Object key) {
+    @Override
+    public V get(Object key) {
         processQueue();
-        SoftReference ref = (SoftReference)hash.get(key);
-        if (ref != null)
-            return ref.get();
-        return null;
+        return valueOrNull(hash.get(key));
     }
 
     /**
@@ -200,12 +212,10 @@ public class SoftValueHashMap extends AbstractMap implements Map {
      * @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) {
+    @Override
+    public V put(K key, V value) {
         processQueue();
-        Object rtn = hash.put(key, SoftValueRef.create(key, value, queue));
-        if (rtn != null)
-            rtn = ((SoftReference)rtn).get();
-        return rtn;
+        return valueOrNull(hash.put(key, SoftValueRef.create(key, value, queue)));
     }
 
     /**
@@ -218,16 +228,66 @@ public class SoftValueHashMap extends AbstractMap implements Map {
      * @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) {
+    @Override
+    public V remove(Object key) {
         processQueue();
-        return hash.remove(key);
+        return valueOrNull(hash.remove(key));
     }
 
     /**
      * Removes all mappings from this map.
      */
+    @Override
     public void clear() {
         processQueue();
         hash.clear();
     }
+
+    /*
+     * Check whether the supplied value exists.
+     * @param Object the value to compare.
+     * @return true if it was found or null. 
+     */
+    @Override
+    public boolean containsValue(Object arg0) {
+        processQueue();
+        if (null == arg0)
+            return false;
+        
+        for (SoftValueRef<K,V> e:hash.values())
+            if (null != e && arg0.equals(e.get()))
+                return true;
+        return false;
+    }
+
+    /* {@inheritDoc} */
+    @Override
+    public Set<K> keySet() {
+        processQueue();
+        return hash.keySet();
+    }
+    
+    /* {@inheritDoc} */
+    @Override
+    public void putAll(Map<? extends K,? extends V> arg0) {
+        processQueue();
+        for (K key:arg0.keySet())
+            put(key, arg0.get(key));
+    }
+
+    /* {@inheritDoc} */
+    @Override
+    public Collection<V> values() {
+        processQueue();
+        List<V> ret = new ArrayList<V>();
+        for (SoftValueRef<K,V> e:hash.values())
+            ret.add(e.get());
+        return ret;
+    }
+    
+    private V valueOrNull(SoftValueRef<K,V> rtn) { 
+        if (null == rtn)
+            return null;
+        return rtn.get();
+    }
 }
diff --git a/src/test/scoped/ScopedRepositoryTestCase.java b/src/test/scoped/ScopedRepositoryTestCase.java
new file mode 100644 (file)
index 0000000..9dca4f5
--- /dev/null
@@ -0,0 +1,242 @@
+/*
+ * Javassist, a Java-bytecode translator toolkit.
+ * Copyright (C) 1999- Shigeru Chiba. 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,
+ * or the Apache License Version 2.0.
+ *
+ * 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 scoped;
+
+import java.io.File;
+import java.lang.reflect.Method;
+import java.math.BigDecimal;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.Arrays;
+import java.util.Map;
+import java.util.stream.LongStream;
+
+import javassist.ClassPool;
+import javassist.CtClass;
+import javassist.CtConstructor;
+import javassist.CtField;
+import javassist.CtMethod;
+import javassist.scopedpool.ScopedClassPool;
+import javassist.scopedpool.ScopedClassPoolRepository;
+import javassist.scopedpool.ScopedClassPoolRepositoryImpl;
+import javassist.scopedpool.SoftValueHashMap;
+import junit.framework.TestCase;
+
+
+/**
+ * ScopedRepositoryTest.
+ * 
+ * @author <a href="adrian@jboss.com">Adrian Brock</a>
+ * @version $Revision$
+ */
+public class ScopedRepositoryTestCase extends TestCase
+{
+   private static final ScopedClassPoolRepository repository = ScopedClassPoolRepositoryImpl.getInstance();
+   
+   public void testJDKClasses() throws Exception
+   {
+      ClassPool poolClass = repository.findClassPool(Class.class.getClassLoader());
+      assertNotNull(poolClass);
+      ClassPool poolString = repository.findClassPool(String.class.getClassLoader());
+      assertNotNull(poolString);
+      assertEquals(poolClass, poolString);
+   }
+   
+   public void testScopedClasses() throws Exception
+   {
+      ClassLoader cl = getURLClassLoader("test-classes14-jar1");
+      ClassPool pool1 = repository.findClassPool(cl);
+      CtClass clazz = pool1.get("scoped.jar1.TestClass1");
+      assertNotNull(clazz);
+      ClassPool poolClass = repository.findClassPool(Class.class.getClassLoader());
+      assertNotNull(poolClass);
+      assertNotSame(pool1, poolClass);
+   }
+   
+   public void testUnscopedAnnotationUsage() throws Exception
+   {
+      CtClass clazz = getCtClass(UnscopedAnnotationUsage.class);
+      checkTestAnnotation(clazz, "notDefault");
+   }
+   
+   public void testUnscopedAnnotationDefaultUsage() throws Exception
+   {
+      CtClass clazz = getCtClass(UnscopedAnnotationDefaultUsage.class);
+      checkTestAnnotation(clazz, "defaultValue");
+   }
+   
+   public void testScopedAnnotationUsage() throws Exception
+   {
+      ClassLoader cl = getURLClassLoader("test-classes14-jar1");
+      CtClass clazz = getCtClass("scoped.jar1.ScopedAnnotationUsage", cl);
+      checkTestAnnotation(clazz, "notDefault");
+   }
+   
+   public void testScopedAnnotationDefaultUsage() throws Exception
+   {
+      ClassLoader cl = getURLClassLoader("test-classes14-jar1");
+      CtClass clazz = getCtClass("scoped.jar1.ScopedAnnotationDefaultUsage", cl);
+      checkTestAnnotation(clazz, "defaultValue");
+   }
+   
+   public void testFullyScopedAnnotationUsage() throws Exception
+   {
+      ClassLoader cl = getURLClassLoader("test-classes14-jar1");
+      CtClass clazz = getCtClass("scoped.jar1.FullyScopedAnnotationUsage", cl);
+      checkScopedAnnotation(cl, clazz, "notDefault");
+   }
+   
+   public void testFullyScopedAnnotationDefaultUsage() throws Exception
+   {
+      ClassLoader cl = getURLClassLoader("test-classes14-jar1");
+      CtClass clazz = getCtClass("scoped.jar1.FullyScopedAnnotationDefaultUsage", cl);
+      checkScopedAnnotation(cl, clazz, "defaultValue");
+   }
+   
+   public void testSoftValueHashMap() throws Exception {
+       Map<String,Class<?>> map = new SoftValueHashMap<>();
+       Class<?> cls = this.getClass();
+       assertTrue(map.put(cls.getName(), cls) == null);
+       assertTrue(map.put(cls.getName(), cls) == cls);
+       assertTrue(map.size() == 1);
+       assertTrue(map.get(cls.getName()) == cls);
+       assertTrue(map.values().iterator().next() == cls);
+       assertTrue(map.entrySet().iterator().next().getValue() == cls);
+       assertTrue(map.containsValue(cls));
+       assertTrue(map.remove(cls.getName()) == cls);
+       assertTrue(map.size() == 0);
+   }
+
+   public void testSoftCache() throws Exception {     
+       // Overload the heap to test that the map auto cleans
+       Map<String,long[]> map = new SoftValueHashMap<>();
+       // 12+8*30000000 = +- 252 MB 
+       long[] data = LongStream.range(0, 30000000).toArray();
+       int current = map.size();
+       while (current <= map.size()) {
+           current = map.size();
+           for (int ii = 0; ii < 5; ii++) {
+               map.put(current+"-"+ii, Arrays.copyOf(data, data.length));
+           }
+       }
+       assertTrue(current > map.size());
+   }
+   
+   protected CtClass getCtClass(Class<?> clazz) throws Exception
+   {
+      return getCtClass(clazz.getName(), clazz.getClassLoader());
+   }
+   
+   protected CtClass getCtClass(String name, ClassLoader cl) throws Exception
+   {
+      ClassPool pool = repository.findClassPool(cl);
+      assertNotNull(pool);
+      CtClass clazz = pool.get(name);
+      assertNotNull(clazz);
+      return clazz;
+   }
+   
+   protected void checkTestAnnotation(CtClass ctClass, String value) throws Exception
+   {
+      checkTestAnnotation(ctClass.getAnnotations(), value);
+      checkTestAnnotation(getFieldAnnotations(ctClass), value);
+      checkTestAnnotation(getConstructorAnnotations(ctClass), value);
+      checkTestAnnotation(getConstructorParameterAnnotations(ctClass), value);
+      checkTestAnnotation(getMethodAnnotations(ctClass), value);
+      checkTestAnnotation(getMethodParameterAnnotations(ctClass), value);
+   }
+   
+   protected void checkTestAnnotation(Object[] annotations, String value) throws Exception
+   {
+      assertNotNull(annotations);
+      assertEquals(1, annotations.length);
+      assertNotNull(annotations[0]);
+      assertTrue(annotations[0] instanceof TestAnnotation);
+      TestAnnotation annotation = (TestAnnotation) annotations[0];
+      assertEquals(value, annotation.something());
+   }
+   
+   protected void checkScopedAnnotation(ClassLoader cl, CtClass ctClass, String value) throws Exception
+   {
+      Class<?> annotationClass = cl.loadClass("scoped.jar1.ScopedTestAnnotation");
+      checkScopedAnnotation(annotationClass, ctClass.getAnnotations(), value);
+      checkScopedAnnotation(annotationClass, getFieldAnnotations(ctClass), value);
+      checkScopedAnnotation(annotationClass, getConstructorAnnotations(ctClass), value);
+      checkScopedAnnotation(annotationClass, getConstructorParameterAnnotations(ctClass), value);
+      checkScopedAnnotation(annotationClass, getMethodAnnotations(ctClass), value);
+      checkScopedAnnotation(annotationClass, getMethodParameterAnnotations(ctClass), value);
+   }
+   
+   protected void checkScopedAnnotation(Class<?> annotationClass, Object[] annotations, String value) throws Exception
+   {
+      assertNotNull(annotations);
+      assertEquals(1, annotations.length);
+      assertNotNull(annotations[0]);
+      assertTrue(annotationClass.isInstance(annotations[0]));
+      
+      Method method = annotationClass.getMethod("something", new Class<?>[0]);
+      assertEquals(value, method.invoke(annotations[0], (Object[]) null));
+   }
+   
+   protected Object[] getFieldAnnotations(CtClass clazz) throws Exception
+   {
+      CtField field = clazz.getField("aField");
+      assertNotNull(field);
+      return field.getAnnotations();
+   }
+   
+   protected Object[] getMethodAnnotations(CtClass clazz) throws Exception
+   {
+      CtMethod method = clazz.getMethod("doSomething", "(I)V");
+      assertNotNull(method);
+      return method.getAnnotations();
+   }
+   
+   protected Object[] getMethodParameterAnnotations(CtClass clazz) throws Exception
+   {
+      CtMethod method = clazz.getMethod("doSomething", "(I)V");
+      assertNotNull(method);
+      Object[] paramAnnotations = method.getParameterAnnotations();
+      assertNotNull(paramAnnotations);
+      assertEquals(1, paramAnnotations.length);
+      return (Object[]) paramAnnotations[0];
+   }
+   
+   protected Object[] getConstructorAnnotations(CtClass clazz) throws Exception
+   {
+      CtConstructor constructor = clazz.getConstructor("(I)V");
+      assertNotNull(constructor);
+      return constructor.getAnnotations();
+   }
+   
+   protected Object[] getConstructorParameterAnnotations(CtClass clazz) throws Exception
+   {
+      CtConstructor constructor = clazz.getConstructor("(I)V");
+      assertNotNull(constructor);
+      Object[] paramAnnotations = constructor.getParameterAnnotations();
+      assertNotNull(paramAnnotations);
+      assertEquals(1, paramAnnotations.length);
+      return (Object[]) paramAnnotations[0];
+   }
+   
+   protected ClassLoader getURLClassLoader(String context) throws Exception
+   {
+      String output = ".";
+      File file = new File(output + File.separator + context);
+      URL url = file.toURI().toURL();
+      return new URLClassLoader(new URL[] { url });
+   }
+}
diff --git a/src/test/scoped/TestAnnotation.java b/src/test/scoped/TestAnnotation.java
new file mode 100644 (file)
index 0000000..dd5c7b8
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * Javassist, a Java-bytecode translator toolkit.
+ * Copyright (C) 1999- Shigeru Chiba. 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,
+ * or the Apache License Version 2.0.
+ *
+ * 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 scoped;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * TestAnnotation.
+ * 
+ * @author <a href="adrian@jboss.com">Adrian Brock</a>
+ * @version $Revision$
+ */
+@Retention(value = RetentionPolicy.RUNTIME)
+@Target({ ElementType.TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.CONSTRUCTOR })
+public @interface TestAnnotation
+{
+   String something() default "defaultValue";
+}
diff --git a/src/test/scoped/UnscopedAnnotationDefaultUsage.java b/src/test/scoped/UnscopedAnnotationDefaultUsage.java
new file mode 100644 (file)
index 0000000..bcbecfe
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * Javassist, a Java-bytecode translator toolkit.
+ * Copyright (C) 1999- Shigeru Chiba. 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,
+ * or the Apache License Version 2.0.
+ *
+ * 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 scoped;
+
+@TestAnnotation
+public class UnscopedAnnotationDefaultUsage
+{
+   @TestAnnotation
+   public int aField;
+   
+   @TestAnnotation
+   public UnscopedAnnotationDefaultUsage(@TestAnnotation int param) {}
+
+   @TestAnnotation
+   public void doSomething(@TestAnnotation int param) {}
+}
diff --git a/src/test/scoped/UnscopedAnnotationUsage.java b/src/test/scoped/UnscopedAnnotationUsage.java
new file mode 100644 (file)
index 0000000..e63db33
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * Javassist, a Java-bytecode translator toolkit.
+ * Copyright (C) 1999- Shigeru Chiba. 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,
+ * or the Apache License Version 2.0.
+ *
+ * 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 scoped;
+
+@TestAnnotation(something="notDefault")
+public class UnscopedAnnotationUsage
+{
+   @TestAnnotation(something="notDefault")
+   public int aField;
+   
+   @TestAnnotation(something="notDefault")
+   public UnscopedAnnotationUsage(@TestAnnotation(something="notDefault") int param) {}
+   
+   @TestAnnotation(something="notDefault")
+   public void doSomething(@TestAnnotation(something="notDefault") int param) {}
+}
diff --git a/src/test/scoped/jar1/FullyScopedAnnotationDefaultUsage.java b/src/test/scoped/jar1/FullyScopedAnnotationDefaultUsage.java
new file mode 100644 (file)
index 0000000..663d18f
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * Javassist, a Java-bytecode translator toolkit.
+ * Copyright (C) 1999- Shigeru Chiba. 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,
+ * or the Apache License Version 2.0.
+ *
+ * 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 scoped.jar1;
+
+@ScopedTestAnnotation
+public class FullyScopedAnnotationDefaultUsage
+{
+   @ScopedTestAnnotation
+   public int aField;
+
+   @ScopedTestAnnotation
+   public FullyScopedAnnotationDefaultUsage(@ScopedTestAnnotation int value) {}
+
+   @ScopedTestAnnotation
+   public void doSomething(@ScopedTestAnnotation int value) {}
+}
diff --git a/src/test/scoped/jar1/FullyScopedAnnotationUsage.java b/src/test/scoped/jar1/FullyScopedAnnotationUsage.java
new file mode 100644 (file)
index 0000000..c44aeb7
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * Javassist, a Java-bytecode translator toolkit.
+ * Copyright (C) 1999- Shigeru Chiba. 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,
+ * or the Apache License Version 2.0.
+ *
+ * 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 scoped.jar1;
+
+@ScopedTestAnnotation(something="notDefault")
+public class FullyScopedAnnotationUsage
+{
+   @ScopedTestAnnotation(something="notDefault")
+   public int aField;
+
+   @ScopedTestAnnotation(something="notDefault")
+   public FullyScopedAnnotationUsage(@ScopedTestAnnotation(something="notDefault") int param) {}
+
+   @ScopedTestAnnotation(something="notDefault")
+   public void doSomething(@ScopedTestAnnotation(something="notDefault") int param) {}
+}
diff --git a/src/test/scoped/jar1/ScopedAnnotationDefaultUsage.java b/src/test/scoped/jar1/ScopedAnnotationDefaultUsage.java
new file mode 100644 (file)
index 0000000..284b086
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * Javassist, a Java-bytecode translator toolkit.
+ * Copyright (C) 1999- Shigeru Chiba. 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,
+ * or the Apache License Version 2.0.
+ *
+ * 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 scoped.jar1;
+
+import scoped.TestAnnotation;
+
+@TestAnnotation
+public class ScopedAnnotationDefaultUsage
+{
+   @TestAnnotation
+   public int aField;
+
+   @TestAnnotation
+   public ScopedAnnotationDefaultUsage(@TestAnnotation int param) {}
+
+   @TestAnnotation
+   public void doSomething(@TestAnnotation int param) {}
+}
diff --git a/src/test/scoped/jar1/ScopedAnnotationUsage.java b/src/test/scoped/jar1/ScopedAnnotationUsage.java
new file mode 100644 (file)
index 0000000..23400f2
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * Javassist, a Java-bytecode translator toolkit.
+ * Copyright (C) 1999- Shigeru Chiba. 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,
+ * or the Apache License Version 2.0.
+ *
+ * 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 scoped.jar1;
+
+import scoped.TestAnnotation;
+
+@TestAnnotation(something="notDefault")
+public class ScopedAnnotationUsage
+{
+   @TestAnnotation(something="notDefault")
+   public int aField;
+
+   @TestAnnotation(something="notDefault")
+   public ScopedAnnotationUsage(@TestAnnotation(something="notDefault") int param) {}
+
+   @TestAnnotation(something="notDefault")
+   public void doSomething(@TestAnnotation(something="notDefault") int param) {}
+}
diff --git a/src/test/scoped/jar1/ScopedTestAnnotation.java b/src/test/scoped/jar1/ScopedTestAnnotation.java
new file mode 100644 (file)
index 0000000..25a36a5
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * Javassist, a Java-bytecode translator toolkit.
+ * Copyright (C) 1999- Shigeru Chiba. 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,
+ * or the Apache License Version 2.0.
+ *
+ * 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 scoped.jar1;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * TestAnnotation.
+ * 
+ * @author <a href="adrian@jboss.com">Adrian Brock</a>
+ * @version $Revision$
+ */
+@Retention(value = RetentionPolicy.RUNTIME)
+@Target({ ElementType.TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.CONSTRUCTOR })
+public @interface ScopedTestAnnotation
+{
+   String something() default "defaultValue";
+}
diff --git a/src/test/scoped/jar1/TestClass1.java b/src/test/scoped/jar1/TestClass1.java
new file mode 100644 (file)
index 0000000..a538bf7
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * Javassist, a Java-bytecode translator toolkit.
+ * Copyright (C) 1999- Shigeru Chiba. 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,
+ * or the Apache License Version 2.0.
+ *
+ * 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 scoped.jar1;
+
+/**
+ * TestClass1.
+ * 
+ * @author <a href="adrian@jboss.com">Adrian Brock</a>
+ * @version $Revision$
+ */
+public class TestClass1
+{
+}