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);
+ }
+ }
+}