Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Change the way cached methods are implemented, so the same Method obj…

…ects can be re-used, and there are no reflection calls nessesarry in <clinit>
  • Loading branch information...
commit 0f366a454cf8fd092e6baa174a26b41b7ef3a039 1 parent 9f1a2ea
@stuartwdouglas stuartwdouglas authored
View
147 src/main/java/org/jboss/invocation/proxy/AbstractProxyFactory.java
@@ -24,10 +24,9 @@
import org.jboss.classfilewriter.AccessFlag;
import org.jboss.classfilewriter.ClassMethod;
-import org.jboss.classfilewriter.code.BranchEnd;
import org.jboss.classfilewriter.code.CodeAttribute;
-import org.jboss.classfilewriter.code.CodeLocation;
-import org.jboss.classfilewriter.util.DescriptorUtils;
+import org.jboss.invocation.proxy.classloading.MethodStore;
+import org.jboss.invocation.proxy.reflection.ReflectionMetadataSource;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
@@ -35,9 +34,10 @@
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.security.ProtectionDomain;
+import java.util.ArrayList;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
-import java.util.concurrent.atomic.AtomicInteger;
/**
* A subclass factory specializing in proxy generation.
@@ -50,15 +50,13 @@
private static final String METHOD_FIELD_DESCRIPTOR = "Ljava/lang/reflect/Method;";
- private final Map<Method, String> methodIdentifiers = new HashMap<Method, String>();
+ private final Map<Method, Integer> methodIdentifiers = new HashMap<Method, Integer>();
private int identifierCount = 0;
private ClassMethod staticConstructor;
- private volatile Method[] cachedMethods;
-
- private static final AtomicInteger count = new AtomicInteger();
+ private final List<Method> cachedMethods = new ArrayList<Method>(0);
/**
@@ -71,7 +69,7 @@
*/
protected AbstractProxyFactory(String className, Class<T> superClass, ClassLoader classLoader,
ProtectionDomain protectionDomain, final ReflectionMetadataSource reflectionMetadataSource) {
- super(className, superClass, classLoader, protectionDomain,reflectionMetadataSource);
+ super(className, superClass, classLoader, protectionDomain, reflectionMetadataSource);
staticConstructor = classFile.addMethod(AccessFlag.of(AccessFlag.PUBLIC, AccessFlag.STATIC), "<clinit>", "V");
}
@@ -79,6 +77,7 @@ protected AbstractProxyFactory(String className, Class<T> superClass, ClassLoade
* This method must be called by subclasses after they have finished generating the class.
*/
protected void finalizeStaticConstructor() {
+ setupCachedProxyFields();
staticConstructor.getCodeAttribute().returnInstruction();
}
@@ -88,7 +87,54 @@ protected void finalizeStaticConstructor() {
@Override
public void afterClassLoad(Class<?> clazz) {
super.afterClassLoad(clazz);
- cachedMethods = AccessController.doPrivileged(new CachedMethodGetter());
+ //we create a new instance of the proxy class
+ //this forces <clinit> to be run, while the correct ThreadLocal is set
+ //if we do not run this then <clinit> may be run later, perhaps even in
+ //another thread
+ try {
+ clazz.newInstance();
+ } catch (InstantiationException e) {
+ throw new RuntimeException(e);
+ } catch (IllegalAccessException e) {
+ throw new RuntimeException(e);
+ }
+ MethodStore.METHODS.remove();
+ }
+
+ private void setupCachedProxyFields() {
+ cachedMethods.addAll(methodIdentifiers.keySet());
+
+ //set the methods to be accessible
+ AccessController.doPrivileged(new PrivilegedAction<Object>() {
+ @Override
+ public Object run() {
+ for(Method method : cachedMethods) {
+ method.setAccessible(true);
+ }
+ return null;
+ }
+ });
+
+ //store the Method objects in a thread local, so that
+ //the proxies <clinit> method can access them
+ //this removes the need for reflection in the proxy <clinit> method
+ final Method[] methods = new Method[identifierCount];
+ for (Map.Entry<Method, Integer> entry : methodIdentifiers.entrySet()) {
+ methods[entry.getValue()] = entry.getKey();
+ }
+ MethodStore.METHODS.set(methods);
+
+ //add the bytecode to load the cached fields in the static constructor
+ CodeAttribute ca = staticConstructor.getCodeAttribute();
+ ca.getstatic(MethodStore.class.getName(), "METHODS", "Ljava/lang/ThreadLocal;");
+ ca.invokevirtual(ThreadLocal.class.getName(), "get", "()Ljava/lang/Object;");
+ ca.checkcast("[Ljava/lang/reflect/Method;");
+ for(int i = 0; i < identifierCount; ++i) {
+ ca.dup();
+ ca.ldc(i);
+ ca.aaload();
+ ca.putstatic(getClassName(), METHOD_FIELD_PREFIX + i, METHOD_FIELD_DESCRIPTOR);
+ }
}
/**
@@ -97,7 +143,7 @@ public void afterClassLoad(Class<?> clazz) {
*
* @return The cached methods
*/
- public Method[] getCachedMethods() {
+ public List<Method> getCachedMethods() {
defineClass();
return cachedMethods;
}
@@ -126,84 +172,11 @@ protected void loadMethodIdentifier(Method methodToLoad, ClassMethod method) {
int identifierNo = identifierCount++;
String fieldName = METHOD_FIELD_PREFIX + identifierNo;
classFile.addField(AccessFlag.PRIVATE | AccessFlag.STATIC, fieldName, Method.class);
- methodIdentifiers.put(methodToLoad, fieldName);
- // we need to create the method in the static constructor
- CodeAttribute ca = staticConstructor.getCodeAttribute();
- // we need to call getDeclaredMethods and then iterate
- ca.loadClass(methodToLoad.getDeclaringClass().getName());
- ca.invokevirtual("java.lang.Class", "getDeclaredMethods", "()[Ljava/lang/reflect/Method;");
- ca.dup();
- ca.arraylength();
- ca.dup();
- ca.istore(0);
- ca.aconstNull();
- ca.astore(1);
- ca.aconstNull();
- ca.astore(2);
- ca.aconstNull();
- ca.astore(3);
- // so here we have the array index on top of the stack, followed by the array
- CodeLocation loopBegin = ca.mark();
- BranchEnd loopEnd = ca.ifeq();
- ca.dup();
- ca.iinc(0, -1);
- ca.iload(0); // load the array index into the stack
- ca.dupX1(); // index, array, index, array
- ca.aaload();
- ca.checkcast("java.lang.reflect.Method");
- ca.dup();
- ca.astore(2); // Method, index, array
- // compare method names
- ca.invokevirtual("java.lang.reflect.Method", "getName", "()Ljava/lang/String;");
- ca.ldc(methodToLoad.getName());
- ca.invokevirtual("java.lang.Object", "equals", "(Ljava/lang/Object;)Z"); // int,index,array
- ca.ifEq(loopBegin);
- // compare return types
- ca.aload(2);
- ca.invokevirtual("java.lang.reflect.Method", "getReturnType", "()Ljava/lang/Class;");
- ca.loadType(DescriptorUtils.makeDescriptor(methodToLoad.getReturnType()));
- ca.invokevirtual("java.lang.Object", "equals", "(Ljava/lang/Object;)Z"); // int,index,array
- ca.ifEq(loopBegin);
- // load the method parameters
- Class<?>[] parameters = methodToLoad.getParameterTypes();
- ca.aload(2);
- ca.invokevirtual("java.lang.reflect.Method", "getParameterTypes", "()[Ljava/lang/Class;");
- ca.dup();
- ca.astore(3);
- ca.arraylength();
- ca.iconst(parameters.length);
- ca.ifIcmpne(loopBegin); // compare parameter array length
-
- for (int i = 0; i < parameters.length; ++i) {
- ca.aload(3);
- ca.iconst(i);
- ca.aaload();
- ca.loadType(DescriptorUtils.makeDescriptor(parameters[i]));
- ca.invokevirtual("java.lang.Object", "equals", "(Ljava/lang/Object;)Z"); // int,index,array
- ca.ifEq(loopBegin);
- }
- ca.pop();
-
- BranchEnd gotoEnd = ca.gotoInstruction(); // we have found the method, goto the pointwhere we write it to a static
- // field
-
- // throw runtime exception as we could not find the method.
- // this will only happen if the proxy isloaded into the wrong classloader
- ca.branchEnd(loopEnd);
- ca.newInstruction("java.lang.RuntimeException");
- ca.dup();
- ca.ldc("Could not find method " + methodToLoad);
- ca.invokespecial("java.lang.RuntimeException", "<init>", "(Ljava/lang/String;)V");
- ca.athrow();
- ca.branchEnd(gotoEnd);
- ca.pop();
- ca.aload(2);
- ca.checkcast("java.lang.reflect.Method");
- ca.putstatic(getClassName(), fieldName, METHOD_FIELD_DESCRIPTOR);
+ methodIdentifiers.put(methodToLoad, identifierNo);
}
- String fieldName = methodIdentifiers.get(methodToLoad);
- method.getCodeAttribute().getstatic(getClassName(), fieldName, METHOD_FIELD_DESCRIPTOR);
+ final Integer fieldNo = methodIdentifiers.get(methodToLoad);
+ method.getCodeAttribute().getstatic(getClassName(), METHOD_FIELD_PREFIX + fieldNo, METHOD_FIELD_DESCRIPTOR);
}
/**
View
57 src/main/java/org/jboss/invocation/proxy/AbstractSubclassFactory.java
@@ -25,6 +25,8 @@
import org.jboss.classfilewriter.AccessFlag;
import org.jboss.classfilewriter.ClassMethod;
import org.jboss.classfilewriter.util.DescriptorUtils;
+import org.jboss.invocation.proxy.reflection.ClassMetadataSource;
+import org.jboss.invocation.proxy.reflection.ReflectionMetadataSource;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
@@ -32,6 +34,7 @@
import java.security.ProtectionDomain;
import java.util.Collections;
import java.util.HashSet;
+import java.util.Iterator;
import java.util.Set;
/**
@@ -155,15 +158,20 @@ protected void cleanup() {
* @param override the method body creator to use
*/
protected void overridePublicMethods(MethodBodyCreator override) {
- ClassMetadataSource data = reflectionMetadataSource.getClassMetadata(getSuperClass());
- for (Method method : data.getMethods()) {
- MethodIdentifier identifier = MethodIdentifier.getIdentifierForMethod(method);
- if (Modifier.isFinal(method.getModifiers())) {
- continue;
- }
- if (!SKIP_BY_DEFAULT.contains(identifier)) {
- overrideMethod(method, identifier, override);
+ Class<?> c = getSuperClass();
+ while (c != null) {
+ ClassMetadataSource data = reflectionMetadataSource.getClassMetadata(c);
+
+ for (Method method : data.getDeclaredMethods()) {
+ MethodIdentifier identifier = MethodIdentifier.getIdentifierForMethod(method);
+ if (!Modifier.isPublic(method.getModifiers()) || Modifier.isFinal(method.getModifiers())) {
+ continue;
+ }
+ if (!SKIP_BY_DEFAULT.contains(identifier)) {
+ overrideMethod(method, identifier, override);
+ }
}
+ c = c.getSuperclass();
}
}
@@ -224,7 +232,7 @@ protected boolean overrideEquals() {
*/
protected boolean overrideEquals(MethodBodyCreator creator) {
Method equals = null;
- ClassMetadataSource data = reflectionMetadataSource.getClassMetadata(getSuperClass());
+ ClassMetadataSource data = reflectionMetadataSource.getClassMetadata(Object.class);
try {
equals = data.getMethod("equals", Boolean.TYPE, Object.class);
} catch (Exception e) {
@@ -251,7 +259,7 @@ protected boolean overrideHashcode() {
protected boolean overrideHashcode(MethodBodyCreator creator) {
Method hashCode = null;
- ClassMetadataSource data = reflectionMetadataSource.getClassMetadata(getSuperClass());
+ ClassMetadataSource data = reflectionMetadataSource.getClassMetadata(Object.class);
try {
hashCode = data.getMethod("hashCode", Integer.TYPE);
} catch (Exception e) {
@@ -277,7 +285,7 @@ protected boolean overrideToString() {
*/
protected boolean overrideToString(MethodBodyCreator creator) {
Method toString = null;
- ClassMetadataSource data = reflectionMetadataSource.getClassMetadata(getSuperClass());
+ final ClassMetadataSource data = reflectionMetadataSource.getClassMetadata(Object.class);
try {
toString = data.getMethod("toString", String.class);
} catch (Exception e) {
@@ -302,8 +310,9 @@ protected boolean overrideFinalize() {
*/
protected boolean overrideFinalize(MethodBodyCreator creator) {
Method finalize = null;
+ final ClassMetadataSource data = reflectionMetadataSource.getClassMetadata(Object.class);
try {
- finalize = Object.class.getDeclaredMethod("finalize");
+ finalize = data.getMethod("finalize", void.class);
} catch (Exception e) {
throw new RuntimeException(e);
}
@@ -332,9 +341,27 @@ protected boolean addInterface(MethodBodyCreator override, Class<?> interfaceCla
}
interfaces.add(interfaceClass);
classFile.addInterface(interfaceClass.getName());
- ClassMetadataSource data = reflectionMetadataSource.getClassMetadata(interfaceClass);
- for (Method method : data.getMethods()) {
- overrideMethod(method, MethodIdentifier.getIdentifierForMethod(method), override);
+ final Set<Class<?>> interfaces = new HashSet<Class<?>>();
+ final Set<Class<?>> toProcess = new HashSet<Class<?>>();
+ toProcess.add(interfaceClass);
+
+ //walk the interface hierarchy and get all methods
+ while(!toProcess.isEmpty()) {
+ Iterator<Class<?>> it = toProcess.iterator();
+ final Class<?> c = it.next();
+ it.remove();
+ interfaces.add(c);
+ for(Class<?> i : c.getInterfaces()) {
+ if(!interfaces.contains(i)) {
+ toProcess.add(i);
+ }
+ }
+ }
+ for(final Class<?> c : interfaces) {
+ ClassMetadataSource data = reflectionMetadataSource.getClassMetadata(c);
+ for (Method method : data.getDeclaredMethods()) {
+ overrideMethod(method, MethodIdentifier.getIdentifierForMethod(method), override);
+ }
}
return true;
}
View
3  src/main/java/org/jboss/invocation/proxy/ProxyConfiguration.java
@@ -21,6 +21,9 @@
*/
package org.jboss.invocation.proxy;
+import org.jboss.invocation.proxy.reflection.DefaultReflectionMetadataSource;
+import org.jboss.invocation.proxy.reflection.ReflectionMetadataSource;
+
import java.security.ProtectionDomain;
import java.util.ArrayList;
import java.util.Collections;
View
33 src/main/java/org/jboss/invocation/proxy/classloading/MethodStore.java
@@ -0,0 +1,33 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2010, Red Hat Inc., and individual contributors as indicated
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.invocation.proxy.classloading;
+
+import java.lang.reflect.Method;
+
+/**
+ * @author Stuart Douglas
+ */
+public class MethodStore {
+
+ public static final ThreadLocal<Method[]> METHODS = new ThreadLocal<Method[]>();
+
+}
View
4 src/main/java/org/jboss/invocation/proxy/classloading/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * This package contains classes that must be visible to the proxies class loader
+ */
+package org.jboss.invocation.proxy.classloading;
View
4 ...invocation/proxy/ClassMetadataSource.java → ...proxy/reflection/ClassMetadataSource.java
@@ -19,7 +19,7 @@
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
-package org.jboss.invocation.proxy;
+package org.jboss.invocation.proxy.reflection;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
@@ -34,8 +34,6 @@
Collection<Method> getDeclaredMethods();
- Collection<Method> getMethods();
-
Method getMethod(String methodName, Class<?> returnType, Class<?> ... parameters) throws NoSuchMethodException;
Collection<Constructor<?>> getConstructors();
View
18 ...ion/proxy/DefaultClassMetadataSource.java → ...eflection/DefaultClassMetadataSource.java
@@ -19,7 +19,7 @@
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
-package org.jboss.invocation.proxy;
+package org.jboss.invocation.proxy.reflection;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
@@ -34,13 +34,11 @@
private final Class<?> clazz;
private final List<Method> declaredMethods;
- private final List<Method> methods;
private final List<Constructor<?>> constructors;
public DefaultClassMetadataSource(final Class<?> clazz) {
this.clazz = clazz;
this.declaredMethods = Arrays.asList(clazz.getDeclaredMethods());
- this.methods = Arrays.asList(clazz.getMethods());
this.constructors = Arrays.asList(clazz.getConstructors());
}
@@ -50,14 +48,14 @@ public DefaultClassMetadataSource(final Class<?> clazz) {
}
@Override
- public Collection<Method> getMethods() {
- return methods;
- }
-
- @Override
public Method getMethod(final String methodName, final Class<?> returnType, final Class<?>... parameters) throws NoSuchMethodException {
- return clazz.getMethod(methodName, parameters);
- }
+ for(Method method : declaredMethods ) {
+ if(method.getName().equals(methodName) && returnType.equals(method.getReturnType()) && Arrays.equals(method.getParameterTypes(), parameters)) {
+ return method;
+ }
+ }
+ throw new NoSuchMethodException("Could not find method " + methodName + " on " + clazz);
+ }
@Override
public Collection<Constructor<?>> getConstructors() {
View
3  ...roxy/DefaultReflectionMetadataSource.java → ...tion/DefaultReflectionMetadataSource.java
@@ -19,14 +19,13 @@
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
-package org.jboss.invocation.proxy;
+package org.jboss.invocation.proxy.reflection;
/**
* @author Stuart Douglas
*/
public class DefaultReflectionMetadataSource implements ReflectionMetadataSource {
-
public static final DefaultReflectionMetadataSource INSTANCE = new DefaultReflectionMetadataSource();
private DefaultReflectionMetadataSource() {
View
5 ...ation/proxy/ReflectionMetadataSource.java → .../reflection/ReflectionMetadataSource.java
@@ -19,10 +19,9 @@
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
-package org.jboss.invocation.proxy;
+package org.jboss.invocation.proxy.reflection;
-import java.lang.reflect.Method;
-import java.util.Collection;
+import org.jboss.invocation.proxy.reflection.ClassMetadataSource;
/**
*
View
2  ...test/java/org/jboss/invocation/proxy/test/abstractsubclassfactory/SimpleClassFactory.java
@@ -22,7 +22,7 @@
package org.jboss.invocation.proxy.test.abstractsubclassfactory;
import org.jboss.invocation.proxy.AbstractSubclassFactory;
-import org.jboss.invocation.proxy.DefaultReflectionMetadataSource;
+import org.jboss.invocation.proxy.reflection.DefaultReflectionMetadataSource;
import java.security.ProtectionDomain;
View
5 src/test/java/org/jboss/invocation/proxy/test/proxyfactory/SimpleProxyFactoryTest.java
@@ -27,6 +27,7 @@
import org.junit.Test;
import java.lang.reflect.Method;
+import java.util.List;
public class SimpleProxyFactoryTest {
@@ -70,8 +71,8 @@ public void testRetrievingCachedMethods() {
.setProxyName(SimpleClass.class.getPackage(), "SimpleClass$$Proxy2")
.setClassLoader(SimpleClass.class.getClassLoader());
ProxyFactory<SimpleClass> proxyFactory = new ProxyFactory<SimpleClass>(proxyConfiguration);
- Method[] methods = proxyFactory.getCachedMethods();
- Assert.assertEquals(5, methods.length);
+ List<Method> methods = proxyFactory.getCachedMethods();
+ Assert.assertEquals(5, methods.size());
Method method1 = null;
for (Method m : methods) {
if (m.getName().equals("method1")) {
Please sign in to comment.
Something went wrong with that request. Please try again.