This is an automated email from the ASF dual-hosted git repository.

ggregory pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-bcel.git


The following commit(s) were added to refs/heads/master by this push:
     new e6c768e  [BCEL-321] AbstractClassPathRepository to share findClass 
logic for repositories (#29)
e6c768e is described below

commit e6c768ec51eadc2662c198937e8ef6da7e4ec8d4
Author: Tomo Suzuki <[email protected]>
AuthorDate: Wed Jul 10 08:41:28 2019 -0400

    [BCEL-321] AbstractClassPathRepository to share findClass logic for 
repositories (#29)
    
    * AbstractClassPathRepository
    
    * MemorySensitiveClassPathRepository test
    
    * reverted BcelBenchmark
    
    * javadoc
    
    * more test for abstract class
    
    * Format
    
    * p tag outside ul
    
    * Test coverage
---
 .../org/apache/bcel/classfile/ConstantUtf8.java    |   2 +-
 ...itory.java => AbstractClassPathRepository.java} |  82 +++++----------
 .../org/apache/bcel/util/ClassPathRepository.java  |  99 +-----------------
 .../bcel/util/LruCacheClassPathRepository.java     |  12 ++-
 .../util/MemorySensitiveClassPathRepository.java   |  99 +-----------------
 .../bcel/util/ClassPathRepositoryTestCase.java     | 114 +++++++++++++++++++++
 6 files changed, 160 insertions(+), 248 deletions(-)

diff --git a/src/main/java/org/apache/bcel/classfile/ConstantUtf8.java 
b/src/main/java/org/apache/bcel/classfile/ConstantUtf8.java
index c1dcba5..b941981 100644
--- a/src/main/java/org/apache/bcel/classfile/ConstantUtf8.java
+++ b/src/main/java/org/apache/bcel/classfile/ConstantUtf8.java
@@ -30,13 +30,13 @@ import org.apache.bcel.Const;
  * Extends the abstract {@link Constant} to represent a reference to a UTF-8 
encoded string.
  * <p>
  * The following system properties govern caching this class performs.
+ * </p>
  * <ul>
  * <li>{@value #SYS_PROP_CACHE_MAX_ENTRIES} (since 6.4): The size of the 
cache, by default 0, meaning caching is disabled.</li>
  * <li>{@value #SYS_PROP_CACHE_MAX_ENTRY_SIZE} (since 6.0): The maximum size 
of the values to cache, by default 200, 0 disables
  * caching. Values larger than this are <em>not</em> cached.</li>
  * <li>{@value #SYS_PROP_STATISTICS} (since 6.0): Prints statistics on the 
console when the JVM exits.</li>
  * </ul>
- * </p>
  * <p>
  * Here is a sample Maven invocation with caching disabled:
  * </p>
diff --git a/src/main/java/org/apache/bcel/util/ClassPathRepository.java 
b/src/main/java/org/apache/bcel/util/AbstractClassPathRepository.java
similarity index 63%
copy from src/main/java/org/apache/bcel/util/ClassPathRepository.java
copy to src/main/java/org/apache/bcel/util/AbstractClassPathRepository.java
index 3a52b4c..1daf87f 100644
--- a/src/main/java/org/apache/bcel/util/ClassPathRepository.java
+++ b/src/main/java/org/apache/bcel/util/AbstractClassPathRepository.java
@@ -17,57 +17,43 @@
  */
 package org.apache.bcel.util;
 
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.HashMap;
-import java.util.Map;
-
 import org.apache.bcel.classfile.ClassParser;
 import org.apache.bcel.classfile.JavaClass;
 
+import java.io.IOException;
+import java.io.InputStream;
+
 /**
- * This repository is used in situations where a Class is created outside the 
realm of a ClassLoader. Classes are loaded from the file systems using the paths
- * specified in the given class path. By default, this is the value returned 
by ClassPath.getClassPath().
+ * This abstract class provides a logic of a loading {@link JavaClass} objects 
class names via {@link ClassPath}.
+ *
+ * <p>Subclasses can choose caching strategy of the objects by implementing 
the abstract methods (e.g., {@link
+ * #storeClass(JavaClass)} and {@link #findClass(String)}).</p>
  *
- * @see org.apache.bcel.Repository
+ * @since 6.4.0
  */
-public class ClassPathRepository implements Repository {
+abstract class AbstractClassPathRepository implements Repository {
 
-    private ClassPath _path = null;
-    private final Map<String, JavaClass> _loadedClasses = new HashMap<>(); // 
CLASSNAME X JAVACLASS
+    private final ClassPath _path;
 
-    public ClassPathRepository(final ClassPath classPath) {
+    AbstractClassPathRepository(final ClassPath classPath) {
         _path = classPath;
     }
 
-    /**
-     * Stores a new JavaClass instance into this Repository.
-     */
     @Override
-    public void storeClass(final JavaClass javaClass) {
-        _loadedClasses.put(javaClass.getClassName(), javaClass);
-        javaClass.setRepository(this);
-    }
+    public abstract void storeClass(final JavaClass javaClass);
 
-    /**
-     * Removes class from repository
-     */
     @Override
-    public void removeClass(final JavaClass javaClass) {
-        _loadedClasses.remove(javaClass.getClassName());
-    }
+    public abstract void removeClass(final JavaClass javaClass);
 
-    /**
-     * Finds an already defined (cached) JavaClass object by name.
-     */
     @Override
-    public JavaClass findClass(final String className) {
-        return _loadedClasses.get(className);
-    }
+    public abstract JavaClass findClass(final String className);
+
+    @Override
+    public abstract void clear();
 
     /**
-     * Finds a JavaClass object by name. If it is already in this Repository, 
the Repository version is returned. Otherwise, the Repository's classpath is
-     * searched for the class (and it is added to the Repository if found).
+     * Finds a JavaClass object by name. If it is already in this Repository, 
the Repository version is returned.
+     * Otherwise, the Repository's classpath is searched for the class (and it 
is added to the Repository if found).
      *
      * @param className
      *            the name of the class
@@ -77,7 +63,7 @@ public class ClassPathRepository implements Repository {
      */
     @Override
     public JavaClass loadClass(String className) throws ClassNotFoundException 
{
-        if ((className == null) || className.isEmpty()) {
+        if (className == null || className.isEmpty()) {
             throw new IllegalArgumentException("Invalid class name " + 
className);
         }
         className = className.replace('/', '.'); // Just in case, canonical 
form
@@ -93,13 +79,12 @@ public class ClassPathRepository implements Repository {
     }
 
     /**
-     * Finds the JavaClass object for a runtime Class object. If a class with 
the same name is already in this Repository, the Repository version is returned.
-     * Otherwise, getResourceAsStream() is called on the Class object to find 
the class's representation. If the representation is found, it is added to the
-     * Repository.
+     * Finds the JavaClass object for a runtime Class object. If a class with 
the same name is already in this
+     * Repository, the Repository version is returned. Otherwise, 
getResourceAsStream() is called on the Class object to
+     * find the class's representation. If the representation is found, it is 
added to the Repository.
      *
      * @see Class
-     * @param clazz
-     *            the runtime Class object
+     * @param clazz the runtime Class object
      * @return JavaClass object for given runtime class
      * @throws ClassNotFoundException
      *             if the class is not in the Repository, and its 
representation could not be found
@@ -116,11 +101,11 @@ public class ClassPathRepository implements Repository {
         if (i > 0) {
             name = name.substring(i + 1);
         }
-        JavaClass cls = null;
+
         try (InputStream clsStream = clazz.getResourceAsStream(name + 
".class")) {
-            return cls = loadClass(clsStream, className);
+            return loadClass(clsStream, className);
         } catch (final IOException e) {
-            return cls;
+            return null;
         }
     }
 
@@ -143,22 +128,11 @@ public class ClassPathRepository implements Repository {
                 }
             }
         }
-        throw new ClassNotFoundException("SyntheticRepository could not load " 
+ className);
+        throw new ClassNotFoundException("ClassRepository could not load " + 
className);
     }
 
-    /**
-     * ClassPath associated with the Repository.
-     */
     @Override
     public ClassPath getClassPath() {
         return _path;
     }
-
-    /**
-     * Clears all entries from cache.
-     */
-    @Override
-    public void clear() {
-        _loadedClasses.clear();
-    }
 }
diff --git a/src/main/java/org/apache/bcel/util/ClassPathRepository.java 
b/src/main/java/org/apache/bcel/util/ClassPathRepository.java
index 3a52b4c..b767292 100644
--- a/src/main/java/org/apache/bcel/util/ClassPathRepository.java
+++ b/src/main/java/org/apache/bcel/util/ClassPathRepository.java
@@ -17,12 +17,9 @@
  */
 package org.apache.bcel.util;
 
-import java.io.IOException;
-import java.io.InputStream;
 import java.util.HashMap;
 import java.util.Map;
 
-import org.apache.bcel.classfile.ClassParser;
 import org.apache.bcel.classfile.JavaClass;
 
 /**
@@ -31,13 +28,12 @@ import org.apache.bcel.classfile.JavaClass;
  *
  * @see org.apache.bcel.Repository
  */
-public class ClassPathRepository implements Repository {
+public class ClassPathRepository extends AbstractClassPathRepository {
 
-    private ClassPath _path = null;
     private final Map<String, JavaClass> _loadedClasses = new HashMap<>(); // 
CLASSNAME X JAVACLASS
 
     public ClassPathRepository(final ClassPath classPath) {
-        _path = classPath;
+        super(classPath);
     }
 
     /**
@@ -50,7 +46,7 @@ public class ClassPathRepository implements Repository {
     }
 
     /**
-     * Removes class from repository
+     * Removes class from repository.
      */
     @Override
     public void removeClass(final JavaClass javaClass) {
@@ -66,95 +62,6 @@ public class ClassPathRepository implements Repository {
     }
 
     /**
-     * Finds a JavaClass object by name. If it is already in this Repository, 
the Repository version is returned. Otherwise, the Repository's classpath is
-     * searched for the class (and it is added to the Repository if found).
-     *
-     * @param className
-     *            the name of the class
-     * @return the JavaClass object
-     * @throws ClassNotFoundException
-     *             if the class is not in the Repository, and could not be 
found on the classpath
-     */
-    @Override
-    public JavaClass loadClass(String className) throws ClassNotFoundException 
{
-        if ((className == null) || className.isEmpty()) {
-            throw new IllegalArgumentException("Invalid class name " + 
className);
-        }
-        className = className.replace('/', '.'); // Just in case, canonical 
form
-        final JavaClass clazz = findClass(className);
-        if (clazz != null) {
-            return clazz;
-        }
-        try {
-            return loadClass(_path.getInputStream(className), className);
-        } catch (final IOException e) {
-            throw new ClassNotFoundException("Exception while looking for 
class " + className + ": " + e, e);
-        }
-    }
-
-    /**
-     * Finds the JavaClass object for a runtime Class object. If a class with 
the same name is already in this Repository, the Repository version is returned.
-     * Otherwise, getResourceAsStream() is called on the Class object to find 
the class's representation. If the representation is found, it is added to the
-     * Repository.
-     *
-     * @see Class
-     * @param clazz
-     *            the runtime Class object
-     * @return JavaClass object for given runtime class
-     * @throws ClassNotFoundException
-     *             if the class is not in the Repository, and its 
representation could not be found
-     */
-    @Override
-    public JavaClass loadClass(final Class<?> clazz) throws 
ClassNotFoundException {
-        final String className = clazz.getName();
-        final JavaClass repositoryClass = findClass(className);
-        if (repositoryClass != null) {
-            return repositoryClass;
-        }
-        String name = className;
-        final int i = name.lastIndexOf('.');
-        if (i > 0) {
-            name = name.substring(i + 1);
-        }
-        JavaClass cls = null;
-        try (InputStream clsStream = clazz.getResourceAsStream(name + 
".class")) {
-            return cls = loadClass(clsStream, className);
-        } catch (final IOException e) {
-            return cls;
-        }
-    }
-
-    private JavaClass loadClass(final InputStream inputStream, final String 
className) throws ClassNotFoundException {
-        try {
-            if (inputStream != null) {
-                final ClassParser parser = new ClassParser(inputStream, 
className);
-                final JavaClass clazz = parser.parse();
-                storeClass(clazz);
-                return clazz;
-            }
-        } catch (final IOException e) {
-            throw new ClassNotFoundException("Exception while looking for 
class " + className + ": " + e, e);
-        } finally {
-            if (inputStream != null) {
-                try {
-                    inputStream.close();
-                } catch (final IOException e) {
-                    // ignored
-                }
-            }
-        }
-        throw new ClassNotFoundException("SyntheticRepository could not load " 
+ className);
-    }
-
-    /**
-     * ClassPath associated with the Repository.
-     */
-    @Override
-    public ClassPath getClassPath() {
-        return _path;
-    }
-
-    /**
      * Clears all entries from cache.
      */
     @Override
diff --git 
a/src/main/java/org/apache/bcel/util/LruCacheClassPathRepository.java 
b/src/main/java/org/apache/bcel/util/LruCacheClassPathRepository.java
index 4a714ad..ed2c808 100644
--- a/src/main/java/org/apache/bcel/util/LruCacheClassPathRepository.java
+++ b/src/main/java/org/apache/bcel/util/LruCacheClassPathRepository.java
@@ -32,7 +32,7 @@ import org.apache.bcel.classfile.JavaClass;
  * 
  * @since 6.4.0
  */
-public class LruCacheClassPathRepository extends ClassPathRepository {
+public class LruCacheClassPathRepository extends AbstractClassPathRepository {
 
     private final LinkedHashMap<String, JavaClass> loadedClasses;
 
@@ -66,4 +66,14 @@ public class LruCacheClassPathRepository extends 
ClassPathRepository {
         loadedClasses.put(javaClass.getClassName(), javaClass);
         javaClass.setRepository(this);
     }
+
+    @Override
+    public void removeClass(final JavaClass javaClass) {
+        loadedClasses.remove(javaClass.getClassName());
+    }
+
+    @Override
+    public void clear() {
+        loadedClasses.clear();
+    }
 }
diff --git 
a/src/main/java/org/apache/bcel/util/MemorySensitiveClassPathRepository.java 
b/src/main/java/org/apache/bcel/util/MemorySensitiveClassPathRepository.java
index 88b19a8..322e935 100644
--- a/src/main/java/org/apache/bcel/util/MemorySensitiveClassPathRepository.java
+++ b/src/main/java/org/apache/bcel/util/MemorySensitiveClassPathRepository.java
@@ -17,13 +17,10 @@
  */
 package org.apache.bcel.util;
 
-import java.io.IOException;
-import java.io.InputStream;
 import java.lang.ref.SoftReference;
 import java.util.HashMap;
 import java.util.Map;
 
-import org.apache.bcel.classfile.ClassParser;
 import org.apache.bcel.classfile.JavaClass;
 
 /**
@@ -33,13 +30,12 @@ import org.apache.bcel.classfile.JavaClass;
  *
  * @see org.apache.bcel.Repository
  */
-public class MemorySensitiveClassPathRepository implements Repository {
+public class MemorySensitiveClassPathRepository extends 
AbstractClassPathRepository {
 
-    private ClassPath _path = null;
     private final Map<String, SoftReference<JavaClass>> _loadedClasses = new 
HashMap<>(); // CLASSNAME X JAVACLASS
 
     public MemorySensitiveClassPathRepository(final ClassPath path) {
-        this._path = path;
+        super(path);
     }
 
     /**
@@ -47,6 +43,7 @@ public class MemorySensitiveClassPathRepository implements 
Repository {
      */
     @Override
     public void storeClass(final JavaClass clazz) {
+        // Not calling super.storeClass because this subclass maintains the 
mapping.
         _loadedClasses.put(clazz.getClassName(), new SoftReference<>(clazz));
         clazz.setRepository(this);
     }
@@ -72,96 +69,6 @@ public class MemorySensitiveClassPathRepository implements 
Repository {
     }
 
     /**
-     * Find a JavaClass object by name. If it is already in this Repository, 
the Repository version is returned. Otherwise, the Repository's classpath is
-     * searched for the class (and it is added to the Repository if found).
-     *
-     * @param className
-     *            the name of the class
-     * @return the JavaClass object
-     * @throws ClassNotFoundException
-     *             if the class is not in the Repository, and could not be 
found on the classpath
-     */
-    @Override
-    public JavaClass loadClass(String className) throws ClassNotFoundException 
{
-        if ((className == null) || className.isEmpty()) {
-            throw new IllegalArgumentException("Invalid class name " + 
className);
-        }
-        className = className.replace('/', '.'); // Just in case, canonical 
form
-        final JavaClass clazz = findClass(className);
-        if (clazz != null) {
-            return clazz;
-        }
-        try {
-            return loadClass(_path.getInputStream(className), className);
-        } catch (final IOException e) {
-            throw new ClassNotFoundException("Exception while looking for 
class " + className + ": " + e, e);
-        }
-    }
-
-    /**
-     * Find the JavaClass object for a runtime Class object. If a class with 
the same name is already in this Repository, the Repository version is returned.
-     * Otherwise, getResourceAsStream() is called on the Class object to find 
the class's representation. If the representation is found, it is added to the
-     * Repository.
-     *
-     * @see Class
-     * @param clazz
-     *            the runtime Class object
-     * @return JavaClass object for given runtime class
-     * @throws ClassNotFoundException
-     *             if the class is not in the Repository, and its 
representation could not be found
-     */
-    @Override
-    public JavaClass loadClass(final Class<?> clazz) throws 
ClassNotFoundException {
-        final String className = clazz.getName();
-        final JavaClass repositoryClass = findClass(className);
-        if (repositoryClass != null) {
-            return repositoryClass;
-        }
-        String name = className;
-        final int i = name.lastIndexOf('.');
-        if (i > 0) {
-            name = name.substring(i + 1);
-        }
-        JavaClass cls = null;
-        try (InputStream clsStream = clazz.getResourceAsStream(name + 
".class")) {
-            return cls = loadClass(clsStream, className);
-        } catch (final IOException e) {
-            return cls;
-        }
-
-    }
-
-    private JavaClass loadClass(final InputStream is, final String className) 
throws ClassNotFoundException {
-        try {
-            if (is != null) {
-                final ClassParser parser = new ClassParser(is, className);
-                final JavaClass clazz = parser.parse();
-                storeClass(clazz);
-                return clazz;
-            }
-        } catch (final IOException e) {
-            throw new ClassNotFoundException("Exception while looking for 
class " + className + ": " + e, e);
-        } finally {
-            if (is != null) {
-                try {
-                    is.close();
-                } catch (final IOException e) {
-                    // ignored
-                }
-            }
-        }
-        throw new ClassNotFoundException("SyntheticRepository could not load " 
+ className);
-    }
-
-    /**
-     * ClassPath associated with the Repository.
-     */
-    @Override
-    public ClassPath getClassPath() {
-        return _path;
-    }
-
-    /**
      * Clear all entries from cache.
      */
     @Override
diff --git 
a/src/test/java/org/apache/bcel/util/ClassPathRepositoryTestCase.java 
b/src/test/java/org/apache/bcel/util/ClassPathRepositoryTestCase.java
new file mode 100644
index 0000000..b456caa
--- /dev/null
+++ b/src/test/java/org/apache/bcel/util/ClassPathRepositoryTestCase.java
@@ -0,0 +1,114 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+package org.apache.bcel.util;
+
+import java.io.IOException;
+
+import org.apache.bcel.classfile.JavaClass;
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * Tests {@link ClassPathRepository}, {@link 
MemorySensitiveClassPathRepository}, and {@link
+ * LruCacheClassPathRepository} for their common attributes of caching.
+ *
+ * <p>Without memory scarcity, these classes behave in the same manner.
+ */
+public class ClassPathRepositoryTestCase {
+
+    private void verifyCaching(AbstractClassPathRepository repository) throws 
ClassNotFoundException {
+        // Tests loadClass()
+        JavaClass class1 = repository.loadClass("java.lang.String");
+        Assert.assertNotNull(class1);
+        JavaClass class2 = repository.loadClass("java/lang/Long"); // Slashes 
should work
+        Assert.assertNotNull(class2);
+
+        // Tests findClass()
+        Assert.assertEquals(class1, repository.findClass("java.lang.String"));
+        Assert.assertEquals(class2, repository.findClass("java.lang.Long"));
+
+        // Tests removeClass()
+        repository.removeClass(class1);
+        Assert.assertNull(repository.findClass("java.lang.String"));
+
+        // Tests clear()
+        repository.clear();
+        Assert.assertNull(repository.findClass("java.lang.Long"));
+    }
+
+    @Test
+    public void testClassPathRepository() throws ClassNotFoundException, 
IOException {
+        try (final ClassPath classPath = new ClassPath("")) {
+            verifyCaching(new ClassPathRepository(classPath));
+        }
+    }
+
+    @Test
+    public void testMemorySensitiveClassPathRepository() throws 
ClassNotFoundException, IOException {
+        try (final ClassPath classPath = new ClassPath("")) {
+            verifyCaching(new MemorySensitiveClassPathRepository(classPath));
+        }
+    }
+
+    @Test
+    public void testLruCacheClassPathRepository() throws 
ClassNotFoundException, IOException {
+        try (final ClassPath classPath = new ClassPath("")) {
+            verifyCaching(new LruCacheClassPathRepository(classPath, 10));
+        }
+    }
+
+    @Test
+    public void testClassPath() throws IOException {
+        try (final ClassPath classPath = new ClassPath("")) {
+            ClassPathRepository repository = new 
ClassPathRepository(classPath);
+            Assert.assertEquals(classPath, repository.getClassPath());
+        }
+    }
+
+    @Test(expected = ClassNotFoundException.class)
+    public void testNoClassNotFound() throws ClassNotFoundException, 
IOException {
+        try (final ClassPath classPath = new ClassPath("")) {
+            ClassPathRepository repository = new 
ClassPathRepository(classPath);
+            repository.loadClass("no.such.Class");
+        }
+    }
+
+    @Test(expected = ClassNotFoundException.class)
+    public void testClassWithoutPackage() throws ClassNotFoundException, 
IOException {
+        try (final ClassPath classPath = new ClassPath("")) {
+            ClassPathRepository repository = new 
ClassPathRepository(classPath);
+            repository.loadClass("ClassXYZ");
+        }
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testEmptyInput() throws ClassNotFoundException, IOException {
+        try (final ClassPath classPath = new ClassPath("")) {
+            ClassPathRepository repository = new 
ClassPathRepository(classPath);
+            repository.loadClass("");
+        }
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testNullInput() throws ClassNotFoundException, IOException {
+        try (final ClassPath classPath = new ClassPath("")) {
+            ClassPathRepository repository = new 
ClassPathRepository(classPath);
+            repository.loadClass((String) null);
+        }
+    }
+}

Reply via email to