]> source.dussan.org Git - aspectj.git/commitdiff
281654: concurrency problem
authoraclement <aclement>
Fri, 4 Sep 2009 18:42:08 +0000 (18:42 +0000)
committeraclement <aclement>
Fri, 4 Sep 2009 18:42:08 +0000 (18:42 +0000)
bcel-builder/src/org/aspectj/apache/bcel/util/NonCachingClassLoaderRepository.java
bcel-builder/testsrc/org/aspectj/apache/bcel/classfile/tests/AllTests.java
bcel-builder/testsrc/org/aspectj/apache/bcel/classfile/tests/NonCachingClassLoaderRepositoryTest.java [new file with mode: 0644]

index 401e86e76c82d7d7930262421740f9865e47f4c3..489475632330adabd76f75729546ade9d394e631 100644 (file)
@@ -70,177 +70,193 @@ import org.aspectj.apache.bcel.classfile.ClassParser;
 import org.aspectj.apache.bcel.classfile.JavaClass;
 
 /**
- * The repository maintains information about which classes have
- * been loaded.
- *
- * It loads its data from the ClassLoader implementation
- * passed into its constructor.
- *
+ * The repository maintains information about which classes have been loaded.
+ * 
+ * It loads its data from the ClassLoader implementation passed into its constructor.
+ * 
  * @see org.aspectj.apache.bcel.Repository
- *
- * @version $Id: NonCachingClassLoaderRepository.java,v 1.4 2008/08/26 15:02:51 aclement Exp $
+ * 
+ * @version $Id: NonCachingClassLoaderRepository.java,v 1.5 2009/09/04 18:42:08 aclement Exp $
  * @author <A HREF="mailto:markus.dahm@berlin.de">M. Dahm</A>
  * @author David Dixon-Peugh
  * 
  */
-public class NonCachingClassLoaderRepository
-  implements Repository
-{  
-  private static java.lang.ClassLoader bootClassLoader = null;
-
-  private ClassLoaderReference loaderRef;
-  private Map loadedClasses =
-    new SoftHashMap(); // CLASSNAME X JAVACLASS
-  
+public class NonCachingClassLoaderRepository implements Repository {
+       private static java.lang.ClassLoader bootClassLoader = null;
+
+       private final ClassLoaderReference loaderRef;
+       private final Map loadedClasses = new SoftHashMap(); // CLASSNAME X JAVACLASS
+
        public static class SoftHashMap extends AbstractMap {
-                 private Map map;
-                 private ReferenceQueue rq = new ReferenceQueue(); 
-                 
-             public SoftHashMap(Map map) { this.map = map; }
-                 public SoftHashMap() { this(new HashMap()); }
-                 public SoftHashMap(Map map, boolean b) { this(map); }
-               
-                 class SpecialValue extends SoftReference {
-                         private final Object key;
-                         SpecialValue(Object k,Object v) {
-                           super(v,rq);
-                           this.key = k;
-                         }
-                 }  
-
-                 private void processQueue() {
+               private Map map;
+               private ReferenceQueue rq = new ReferenceQueue();
+
+               public SoftHashMap(Map map) {
+                       this.map = map;
+               }
+
+               public SoftHashMap() {
+                       this(new HashMap());
+               }
+
+               public SoftHashMap(Map map, boolean b) {
+                       this(map);
+               }
+
+               class SpecialValue extends SoftReference {
+                       private final Object key;
+
+                       SpecialValue(Object k, Object v) {
+                               super(v, rq);
+                               this.key = k;
+                       }
+               }
+
+               private void processQueue() {
                        SpecialValue sv = null;
-                       while ((sv = (SpecialValue)rq.poll())!=null) {
+                       while ((sv = (SpecialValue) rq.poll()) != null) {
                                map.remove(sv.key);
                        }
-                 }
-               
-                 public Object get(Object key) {
-                       SpecialValue value = (SpecialValue)map.get(key);
-                       if (value==null) return null;
-                       if (value.get()==null) {
+               }
+
+               public Object get(Object key) {
+                       SpecialValue value = (SpecialValue) map.get(key);
+                       if (value == null)
+                               return null;
+                       if (value.get() == null) {
                                // it got GC'd
                                map.remove(value.key);
                                return null;
                        } else {
                                return value.get();
                        }
-                 }
+               }
 
-                 public Object put(Object k, Object v) {
+               public Object put(Object k, Object v) {
                        processQueue();
-                       return map.put(k, new SpecialValue(k,v));
-                 }
+                       return map.put(k, new SpecialValue(k, v));
+               }
 
-                 public Set entrySet() {
+               public Set entrySet() {
                        return map.entrySet();
-                 }
-               
-                 public void clear() {
+               }
+
+               public void clear() {
                        processQueue();
                        Set keys = map.keySet();
                        for (Iterator iterator = keys.iterator(); iterator.hasNext();) {
                                Object name = iterator.next();
                                map.remove(name);
                        }
-                 }
-               
-                 public int size() {
+               }
+
+               public int size() {
                        processQueue();
                        return map.size();
-                 }
-               
-                 public Object remove(Object k) {
+               }
+
+               public Object remove(Object k) {
                        processQueue();
-                       SpecialValue value = (SpecialValue)map.remove(k);
-                       if (value==null) return null;
-                       if (value.get()!=null) {
+                       SpecialValue value = (SpecialValue) map.remove(k);
+                       if (value == null)
+                               return null;
+                       if (value.get() != null) {
                                return value.get();
                        }
                        return null;
-                 }
-         }
-
-    public NonCachingClassLoaderRepository(java.lang.ClassLoader loader) {
-        this.loaderRef = new DefaultClassLoaderReference((loader != null) ? loader : getBootClassLoader());
-    }
-
-    public NonCachingClassLoaderRepository(ClassLoaderReference loaderRef) {
-        this.loaderRef = loaderRef;
-    }
-
-  private static synchronized java.lang.ClassLoader getBootClassLoader() {
-         if (bootClassLoader == null) {
-                 bootClassLoader = new URLClassLoader(new URL[0]);
-         }
-         return bootClassLoader;
-  }
-  
-  /**
-   * Store a new JavaClass into this Repository.
-   */
-  public void storeClass( JavaClass clazz ) {
-    loadedClasses.put( clazz.getClassName(),
-                      clazz );
-    clazz.setRepository( this );
-  }
-
-  /**
-   * Remove class from repository
-   */
-  public void removeClass(JavaClass clazz) {
-    loadedClasses.remove(clazz.getClassName());
-  }
-
-  /**
-   * Find an already defined JavaClass.
-   */
-  public JavaClass findClass( String className ) {
-    if ( loadedClasses.containsKey( className )) {
-      return (JavaClass) loadedClasses.get( className );
-    } else {
-      return null;
-    }
-  }
-
-  /**
-   * Lookup a JavaClass object from the Class Name provided.
-   */
-  public JavaClass loadClass( String className ) 
-    throws ClassNotFoundException
-  {
-    String classFile = className.replace('.', '/');
-
-    JavaClass RC = findClass( className );
-    if (RC != null) { return RC; }
-
-    try {
-      InputStream is = 
-          loaderRef.getClassLoader().getResourceAsStream(classFile + ".class");
-           
-      if(is == null) {
-       throw new ClassNotFoundException(className + " not found.");
-      }
-
-      ClassParser parser = new ClassParser( is, className );
-      RC = parser.parse();
-           
-      storeClass( RC );
-
-      return RC;
-    } catch (IOException e) {
-      throw new ClassNotFoundException( e.toString() );
-    }
-  }
-
-  public JavaClass loadClass(Class clazz) throws ClassNotFoundException {
-    return loadClass(clazz.getName());
-  }
-
-  /** Clear all entries from cache.
-   */
-  public void clear() {
-    loadedClasses.clear();
-  }
-}
+               }
+       }
+
+       public NonCachingClassLoaderRepository(java.lang.ClassLoader loader) {
+               this.loaderRef = new DefaultClassLoaderReference((loader != null) ? loader : getBootClassLoader());
+       }
+
+       public NonCachingClassLoaderRepository(ClassLoaderReference loaderRef) {
+               this.loaderRef = loaderRef;
+       }
+
+       private static synchronized java.lang.ClassLoader getBootClassLoader() {
+               if (bootClassLoader == null) {
+                       bootClassLoader = new URLClassLoader(new URL[0]);
+               }
+               return bootClassLoader;
+       }
+
+       /**
+        * Store a new JavaClass into this Repository.
+        */
+       public void storeClass(JavaClass clazz) {
+               synchronized (loadedClasses) {
+                       loadedClasses.put(clazz.getClassName(), clazz);
+               }
+               clazz.setRepository(this);
+       }
+
+       /**
+        * Remove class from repository
+        */
+       public void removeClass(JavaClass clazz) {
+               synchronized (loadedClasses) {
+                       loadedClasses.remove(clazz.getClassName());
+               }
+       }
+
+       /**
+        * Find an already defined JavaClass.
+        */
+       public JavaClass findClass(String className) {
+               synchronized (loadedClasses) {
+                       if (loadedClasses.containsKey(className)) {
+                               return (JavaClass) loadedClasses.get(className);
+                       } else {
+                               return null;
+                       }
+               }
+       }
+
+       /**
+        * Clear all entries from cache.
+        */
+       public void clear() {
+               synchronized (loadedClasses) {
+                       loadedClasses.clear();
+               }
+       }
 
+       /**
+        * Lookup a JavaClass object from the Class Name provided.
+        */
+       public JavaClass loadClass(String className) throws ClassNotFoundException {
+
+               JavaClass javaClass = findClass(className);
+               if (javaClass != null) {
+                       return javaClass;
+               }
+
+               javaClass = loadJavaClass(className);
+               storeClass(javaClass);
+
+               return javaClass;
+       }
+
+       public JavaClass loadClass(Class clazz) throws ClassNotFoundException {
+               return loadClass(clazz.getName());
+       }
+
+       private JavaClass loadJavaClass(String className) throws ClassNotFoundException {
+               String classFile = className.replace('.', '/');
+               try {
+                       InputStream is = loaderRef.getClassLoader().getResourceAsStream(classFile + ".class");
+
+                       if (is == null) {
+                               throw new ClassNotFoundException(className + " not found.");
+                       }
+
+                       ClassParser parser = new ClassParser(is, className);
+                       return parser.parse();
+               } catch (IOException e) {
+                       throw new ClassNotFoundException(e.toString());
+               }
+       }
+
+}
index cf270b5b09d193037690e10434eb6c0f2214f6b8..2979c12e36147bbfca6e7cf4fd22daa1fb62dabd 100644 (file)
@@ -15,24 +15,11 @@ package org.aspectj.apache.bcel.classfile.tests;
 import junit.framework.Test;
 import junit.framework.TestSuite;
 
-import org.aspectj.apache.bcel.classfile.tests.AnnotationAccessFlagTest;
-import org.aspectj.apache.bcel.classfile.tests.AnnotationDefaultAttributeTest;
-import org.aspectj.apache.bcel.classfile.tests.ElementValueGenTest;
-import org.aspectj.apache.bcel.classfile.tests.EnclosingMethodAttributeTest;
-import org.aspectj.apache.bcel.classfile.tests.EnumAccessFlagTest;
-import org.aspectj.apache.bcel.classfile.tests.FieldAnnotationsTest;
-import org.aspectj.apache.bcel.classfile.tests.GeneratingAnnotatedClassesTest;
-import org.aspectj.apache.bcel.classfile.tests.LocalVariableTypeTableTest;
-import org.aspectj.apache.bcel.classfile.tests.MethodAnnotationsTest;
-import org.aspectj.apache.bcel.classfile.tests.RuntimeVisibleAnnotationAttributeTest;
-import org.aspectj.apache.bcel.classfile.tests.RuntimeVisibleParameterAnnotationAttributeTest;
-import org.aspectj.apache.bcel.classfile.tests.VarargsTest;
-
 public class AllTests {
 
        public static Test suite() {
                TestSuite suite = new TestSuite("Tests for BCEL");
-               //$JUnit-BEGIN$
+               // $JUnit-BEGIN$
                suite.addTestSuite(Fundamentals.class);
                suite.addTestSuite(RuntimeVisibleParameterAnnotationAttributeTest.class);
                suite.addTestSuite(AnnotationDefaultAttributeTest.class);
@@ -40,6 +27,7 @@ public class AllTests {
                suite.addTestSuite(MethodAnnotationsTest.class);
                suite.addTestSuite(RuntimeVisibleAnnotationAttributeTest.class);
                suite.addTestSuite(ClassloaderRepositoryTest.class);
+               suite.addTestSuite(NonCachingClassLoaderRepositoryTest.class);
                suite.addTestSuite(EnumAccessFlagTest.class);
                suite.addTestSuite(LocalVariableTypeTableTest.class);
                suite.addTestSuite(VarargsTest.class);
@@ -54,7 +42,7 @@ public class AllTests {
                suite.addTestSuite(GenericSignatureParsingTest.class);
                suite.addTestSuite(GenericsErasureTesting.class);
                suite.addTestSuite(AnonymousClassTest.class);
-               //$JUnit-END$
+               // $JUnit-END$
                return suite;
        }
 }
\ No newline at end of file
diff --git a/bcel-builder/testsrc/org/aspectj/apache/bcel/classfile/tests/NonCachingClassLoaderRepositoryTest.java b/bcel-builder/testsrc/org/aspectj/apache/bcel/classfile/tests/NonCachingClassLoaderRepositoryTest.java
new file mode 100644 (file)
index 0000000..c777f75
--- /dev/null
@@ -0,0 +1,149 @@
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2001 The Apache Software Foundation.  All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The end-user documentation included with the redistribution,
+ *    if any, must include the following acknowledgment:
+ *       "This product includes software developed by the
+ *        Apache Software Foundation (http://www.apache.org/)."
+ *    Alternately, this acknowledgment may appear in the software itself,
+ *    if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "Apache" and "Apache Software Foundation" and
+ *    "Apache BCEL" must not be used to endorse or promote products
+ *    derived from this software without prior written permission. For
+ *    written permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache",
+ *    "Apache BCEL", nor may "Apache" appear in their name, without
+ *    prior written permission of the Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ */
+
+package org.aspectj.apache.bcel.classfile.tests;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import junit.framework.TestCase;
+
+import org.aspectj.apache.bcel.classfile.JavaClass;
+import org.aspectj.apache.bcel.util.NonCachingClassLoaderRepository;
+
+/**
+ * @author Kristian Rosenvold
+ */
+public class NonCachingClassLoaderRepositoryTest extends TestCase {
+
+       private final NonCachingClassLoaderRepository nonCachingClassLoaderRepository = new NonCachingClassLoaderRepository(
+                       NonCachingClassLoaderRepositoryTest.class.getClassLoader());
+
+       protected void setUp() throws Exception {
+               super.setUp();
+       }
+
+       abstract class DoneChecker implements Runnable {
+               private volatile boolean success = false;
+               private volatile boolean done = false;
+
+               public boolean isSuccess() {
+                       return success;
+               }
+
+               public boolean isDone() {
+                       return done;
+               }
+
+               protected void setDone(boolean successFully) {
+                       success = successFully;
+                       done = true;
+               }
+
+               public abstract void run();
+       }
+
+       class Loader extends DoneChecker implements Runnable {
+               public void run() {
+                       try {
+                               JavaClass javaClass = nonCachingClassLoaderRepository.loadClass(NonCachingClassLoaderRepositoryTest.class
+                                               .getCanonicalName());
+                               nonCachingClassLoaderRepository.clear();
+                               setDone(true);
+                       } catch (Throwable e) {
+                               e.printStackTrace(System.out);
+                               setDone(false);
+                       }
+               }
+       }
+
+       class Clearer extends DoneChecker implements Runnable {
+               public void run() {
+                       try {
+                               nonCachingClassLoaderRepository.clear();
+                               setDone(true);
+                       } catch (Throwable e) {
+                               e.printStackTrace(System.out);
+                               setDone(false);
+                       }
+               }
+       }
+
+       public void testConcurrency() throws ClassNotFoundException, InterruptedException {
+               List loaders = new ArrayList();
+               int i1 = 1000;
+               for (int i = 0; i < i1; i++) {
+                       DoneChecker loader = new Loader();
+                       loaders.add(loader);
+                       new Thread(loader).start();
+                       DoneChecker clearer = new Clearer();
+                       loaders.add(clearer);
+                       new Thread(clearer).start();
+               }
+
+               for (int i = 0; i < i1 * 2; i++) {
+                       DoneChecker loader = (DoneChecker) loaders.get(i);
+                       while (!loader.isDone()) {
+                               Thread.sleep(10);
+                       }
+                       assertTrue("Loader " + i + " is supposed to run successfully", loader.isSuccess());
+               }
+
+       }
+
+       protected void tearDown() throws Exception {
+               super.tearDown();
+       }
+
+}