You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@aries.apache.org by oz...@apache.org on 2010/01/11 15:37:47 UTC

svn commit: r897881 [1/2] - in /incubator/aries/trunk/blueprint/blueprint-core: ./ src/main/java/org/apache/aries/blueprint/container/ src/main/java/org/apache/aries/blueprint/proxy/ src/test/java/org/apache/aries/blueprint/proxy/

Author: ozzy
Date: Mon Jan 11 14:37:45 2010
New Revision: 897881

URL: http://svn.apache.org/viewvc?rev=897881&view=rev
Log:
ARIES-85 integrate asm interceptor patch

Added:
    incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/proxy/AsmInterceptorWrapper.java
    incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/proxy/FinalModifierException.java
    incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/proxy/ProxyClassBytecodeGenerationException.java
    incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/proxy/ProxyClassDefinitionException.java
    incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/proxy/ProxyClassInstantiationException.java
    incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/proxy/ProxySubclassAdapter.java
    incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/proxy/ProxySubclassGenerator.java
    incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/proxy/ProxySubclassHierarchyAdapter.java
    incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/proxy/ProxySubclassMethodHashSet.java
    incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/proxy/UnableToLoadProxyException.java
    incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/proxy/UnableToProxyException.java
    incubator/aries/trunk/blueprint/blueprint-core/src/test/java/org/apache/aries/blueprint/proxy/
    incubator/aries/trunk/blueprint/blueprint-core/src/test/java/org/apache/aries/blueprint/proxy/ProxySubclassGeneratorTest.java
    incubator/aries/trunk/blueprint/blueprint-core/src/test/java/org/apache/aries/blueprint/proxy/ProxyTestClassCovariant.java
    incubator/aries/trunk/blueprint/blueprint-core/src/test/java/org/apache/aries/blueprint/proxy/ProxyTestClassCovariantOverride.java
    incubator/aries/trunk/blueprint/blueprint-core/src/test/java/org/apache/aries/blueprint/proxy/ProxyTestClassFinal.java
    incubator/aries/trunk/blueprint/blueprint-core/src/test/java/org/apache/aries/blueprint/proxy/ProxyTestClassFinalMethod.java
    incubator/aries/trunk/blueprint/blueprint-core/src/test/java/org/apache/aries/blueprint/proxy/ProxyTestClassGeneral.java
    incubator/aries/trunk/blueprint/blueprint-core/src/test/java/org/apache/aries/blueprint/proxy/ProxyTestClassGeneric.java
    incubator/aries/trunk/blueprint/blueprint-core/src/test/java/org/apache/aries/blueprint/proxy/ProxyTestClassGenericSuper.java
    incubator/aries/trunk/blueprint/blueprint-core/src/test/java/org/apache/aries/blueprint/proxy/ProxyTestClassPrivateConstructor.java
    incubator/aries/trunk/blueprint/blueprint-core/src/test/java/org/apache/aries/blueprint/proxy/ProxyTestClassSuper.java
Modified:
    incubator/aries/trunk/blueprint/blueprint-core/pom.xml
    incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/container/BeanRecipe.java
    incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/proxy/Collaborator.java

Modified: incubator/aries/trunk/blueprint/blueprint-core/pom.xml
URL: http://svn.apache.org/viewvc/incubator/aries/trunk/blueprint/blueprint-core/pom.xml?rev=897881&r1=897880&r2=897881&view=diff
==============================================================================
--- incubator/aries/trunk/blueprint/blueprint-core/pom.xml (original)
+++ incubator/aries/trunk/blueprint/blueprint-core/pom.xml Mon Jan 11 14:37:45 2010
@@ -70,6 +70,11 @@
           <artifactId>slf4j-simple</artifactId>
           <scope>test</scope>
       </dependency>
+      <dependency>
+          <groupId>asm</groupId>
+          <artifactId>asm-all</artifactId>
+          <optional>true</optional>
+      </dependency>
   </dependencies>
 
     <build>

Modified: incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/container/BeanRecipe.java
URL: http://svn.apache.org/viewvc/incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/container/BeanRecipe.java?rev=897881&r1=897880&r2=897881&view=diff
==============================================================================
--- incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/container/BeanRecipe.java (original)
+++ incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/container/BeanRecipe.java Mon Jan 11 14:37:45 2010
@@ -36,10 +36,9 @@
 import org.apache.aries.blueprint.ComponentDefinitionRegistry;
 import org.apache.aries.blueprint.ExtendedBlueprintContainer;
 import org.apache.aries.blueprint.Interceptor;
-import org.apache.aries.blueprint.container.AbstractServiceReferenceRecipe.CgLibProxyFactory;
-import org.apache.aries.blueprint.container.AbstractServiceReferenceRecipe.JdkProxyFactory;
 import org.apache.aries.blueprint.di.AbstractRecipe;
 import org.apache.aries.blueprint.di.Recipe;
+import org.apache.aries.blueprint.proxy.AsmInterceptorWrapper;
 import org.apache.aries.blueprint.proxy.CgLibInterceptorWrapper;
 import org.apache.aries.blueprint.utils.ReflectionUtils;
 import org.osgi.service.blueprint.container.ComponentDefinitionException;
@@ -613,27 +612,53 @@
         return obj;
     }    
     
-    private Object addInterceptors(Object original) throws ComponentDefinitionException{
-        
+    private Object addInterceptors(Object original)
+            throws ComponentDefinitionException {
+
         Object intercepted = null;
         String beanName = getName();
-        ComponentDefinitionRegistry reg = blueprintContainer.getComponentDefinitionRegistry();
+        ComponentDefinitionRegistry reg = blueprintContainer
+                .getComponentDefinitionRegistry();
         ComponentMetadata metaData = reg.getComponentDefinition(beanName);
-        List<Interceptor> interceptors = reg.getInterceptors(metaData); 
-        if(interceptors!=null && interceptors.size()>0){
+        List<Interceptor> interceptors = reg.getInterceptors(metaData);
+        if (interceptors != null && interceptors.size() > 0) {
+            boolean asmAvailable = false;
             try {
-                // Try load load a cglib class (to make sure it's actually available
-                getClass().getClassLoader().loadClass("net.sf.cglib.proxy.Enhancer");
+                // Try load load an asm class (to make sure it's actually
+                // available)
+                getClass().getClassLoader().loadClass(
+                        "org.objectweb.asm.ClassVisitor");
+                LOGGER.debug("asm available for interceptors");
+                asmAvailable = true;
             } catch (Throwable t) {
-                throw new ComponentDefinitionException("Interceptors have been configured but cglib can not be used", t);
-            }            
-            
-            intercepted = CgLibInterceptorWrapper.createProxyObject(original.getClass().getClassLoader(), 
-                                                                metaData, 
-                                                                interceptors, 
-                                                                original, 
-                                                                original.getClass().getInterfaces());
-        }else{
+                try {
+                    // Try load load a cglib class (to make sure it's actually
+                    // available)
+                    getClass().getClassLoader().loadClass(
+                            "net.sf.cglib.proxy.Enhancer");
+                } catch (Throwable u) {
+                    throw new ComponentDefinitionException(
+                            "Interceptors have been configured but neither asm nor cglib are available",
+                            u);
+                }
+            }
+            if (asmAvailable) {
+                // if asm is available we can proxy the original object with the
+                // AsmInterceptorWrapper
+                intercepted = AsmInterceptorWrapper.createProxyObject(original
+                        .getClass().getClassLoader(), metaData, interceptors,
+                        original, original.getClass());
+            } else {
+                LOGGER.debug("cglib available for interceptors");
+                // otherwise we're using cglib and need to use the interfaces
+                // with the CgLibInterceptorWrapper
+                intercepted = CgLibInterceptorWrapper.createProxyObject(
+                        original.getClass().getClassLoader(), metaData,
+                        interceptors, original, original.getClass()
+                                .getInterfaces());
+            }
+
+        } else {
             intercepted = original;
         }
         return intercepted;

Added: incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/proxy/AsmInterceptorWrapper.java
URL: http://svn.apache.org/viewvc/incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/proxy/AsmInterceptorWrapper.java?rev=897881&view=auto
==============================================================================
--- incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/proxy/AsmInterceptorWrapper.java (added)
+++ incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/proxy/AsmInterceptorWrapper.java Mon Jan 11 14:37:45 2010
@@ -0,0 +1,202 @@
+/*
+ * 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.aries.blueprint.proxy;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Proxy;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.aries.blueprint.Interceptor;
+import org.osgi.service.blueprint.container.ComponentDefinitionException;
+import org.osgi.service.blueprint.reflect.ComponentMetadata;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class AsmInterceptorWrapper
+{
+  private static final Logger LOGGER = LoggerFactory.getLogger(AsmInterceptorWrapper.class);
+  final static String LOG_ENTRY = "Method entry: {}, args {}";
+  final static String LOG_EXIT = "Method exit: {}, returning {}";
+  final static String LOG_EXCEPTION = "Caught exception";
+
+  public static Object createProxyObject(ClassLoader cl, ComponentMetadata cm,
+      List<Interceptor> interceptors, Object delegate, Class<?>... classesToProxy)
+  {
+
+    LOGGER.debug(LOG_ENTRY, "createProxyObject", new Object[] { cl, cm, interceptors, delegate,
+        classesToProxy });
+
+    Object proxyObject = null;
+    try {
+      if (classesToProxy.length == 1 && !classesToProxy[0].isInterface()) {
+
+        Class<?> classToProxy = classesToProxy[0];
+        LOGGER.debug("Single class to proxy: {}", classToProxy.getName());
+
+        boolean isProxy = isProxyClass(classToProxy);
+        LOGGER.debug("Class already a proxy: {}", isProxy);
+
+        if (isProxy) {
+          try {
+            LOGGER.debug("Get a new instance of existing proxy class");
+            /*
+             * the class is already a proxy, we should just invoke
+             * the constructor to get a new instance of the proxy
+             * with a new Collaborator using the specified delegate
+             */
+            proxyObject = classToProxy.getConstructor(InvocationHandler.class).newInstance(
+                new Collaborator(cm, interceptors, delegate));
+            LOGGER.debug("New proxy object instance {}", proxyObject);
+          } catch (InvocationTargetException e) {
+            LOGGER.debug(LOG_EXCEPTION, e);
+          } catch (NoSuchMethodException e) {
+            LOGGER.debug(LOG_EXCEPTION, e);
+          } catch (InstantiationException e) {
+            LOGGER.debug(LOG_EXCEPTION, e);
+          } catch (IllegalArgumentException e) {
+            LOGGER.debug(LOG_EXCEPTION, e);
+          } catch (SecurityException e) {
+            LOGGER.debug(LOG_EXCEPTION, e);
+          } catch (IllegalAccessException e) {
+            LOGGER.debug(LOG_EXCEPTION, e);
+          }
+        } else {
+          // we should generate a subclass proxy of the given class
+          LOGGER.debug("Generating a subclass proxy for: {}", classToProxy.getName());
+          proxyObject = createSubclassProxy(classToProxy, cm, interceptors, delegate);
+        }
+
+      } else {
+        // we had more than one class specified or only an interface
+        LOGGER.debug("Multiple classes or interface(s) to proxy: {}", classesToProxy);
+        // if we just have interfaces and no classes we default to using
+        // the interface proxy because we can't dynamically
+        // subclass more than one interface
+        // unless we have a class
+        // that implements all of them
+
+        // default to not subclass
+        boolean useSubclassProxy = false;
+
+        // loop through the classes checking if they are java interfaces
+        // if we find any class that isn't an interface we need to use
+        // the subclass proxy
+        Set<Class<?>> notInterfaces = new HashSet<Class<?>>();
+        for (Class<?> clazz : classesToProxy) {
+          if (!clazz.isInterface()) {
+            useSubclassProxy = true;
+            notInterfaces.add(clazz);
+          }
+        }
+
+        if (useSubclassProxy) {
+          LOGGER.debug("Going to use subclass proxy");
+          // if we need to use the subclass proxy then we need to find
+          // the most specific class
+          Class<?> classToProxy = null;
+          int deepest = 0;
+          // for each of the classes find out how deep it is in the
+          // hierarchy
+          for (Class<?> clazz : notInterfaces) {
+            Class<?> nextHighestClass = clazz;
+            int depth = 0;
+            do {
+              nextHighestClass = nextHighestClass.getSuperclass();
+              depth++;
+            } while (nextHighestClass != null);
+            if (depth > deepest) {
+              // if we find a class deeper than the one we already
+              // had
+              // it becomes the new most specific
+              deepest = depth;
+              classToProxy = clazz;
+            }
+          }
+          LOGGER.debug("Most specific class to proxy: {}", classToProxy);
+          proxyObject = createSubclassProxy(classToProxy, cm, interceptors, delegate);
+        } else {
+          LOGGER.debug("Going to use interface proxy");
+          proxyObject = Proxy.newProxyInstance(cl, classesToProxy, new Collaborator(cm,
+              interceptors, delegate));
+        }
+
+      }
+    } catch (UnableToProxyException e) {
+      // translate UnableToProxyException into
+      // ComponentDefinitionException
+      // if the bean is final, or otherwise unable to be proxied.
+      LOGGER.debug(LOG_EXIT, "createProxyObject", e);
+      throw new ComponentDefinitionException("Unable to proxy bean for interceptors: " + e);
+    }
+
+    LOGGER.debug(LOG_EXIT, "createProxyObject", proxyObject);
+
+    return proxyObject;
+  }
+
+  private static Object createSubclassProxy(Class<?> classToProxy, ComponentMetadata cm,
+      List<Interceptor> interceptors, Object delegate) throws UnableToProxyException
+  {
+    LOGGER.debug(LOG_ENTRY, "createSubclassProxy", new Object[] { classToProxy, cm, interceptors,
+        delegate });
+    LOGGER.debug("Generating a subclass proxy for: {}", classToProxy.getName());
+    try {
+      Object proxyObject = ProxySubclassGenerator.newProxySubclassInstance(classToProxy,
+          new Collaborator(cm, interceptors, delegate));
+
+      LOGGER.debug("Generated subclass proxy object: {}", proxyObject);
+      LOGGER.debug(LOG_EXIT, "createSubclassProxy", proxyObject);
+      return proxyObject;
+    } catch (UnableToProxyException e) {
+      LOGGER.debug(LOG_EXCEPTION, e);
+      LOGGER.debug(LOG_EXIT, "createSubclassProxy", e);
+      throw e;
+    }
+  }
+
+  static boolean isProxyClass(Class<?> clazz)
+  {
+    LOGGER.debug(LOG_ENTRY, "isProxyClass", new Object[] { clazz });
+    boolean isProxyObject = false;
+    isProxyObject = ProxySubclassGenerator.isProxySubclass(clazz);
+    LOGGER.debug(LOG_EXIT, "isProxyClass", isProxyObject);
+    return isProxyObject;
+  }
+
+  static Object unwrapObject(Object o)
+  {
+    LOGGER.debug(LOG_ENTRY, "unwrapObject", new Object[] { o });
+    InvocationHandler ih = null;
+    Object unwrappedObject = null;
+    if (ProxySubclassGenerator.isProxySubclass(o.getClass())) {
+      ih = ProxySubclassGenerator.getInvocationHandler(o);
+    } else {
+      ih = Proxy.getInvocationHandler(o);
+    }
+    if (ih instanceof Collaborator) {
+      unwrappedObject = ((Collaborator) ih).object;
+    }
+    LOGGER.debug(LOG_EXIT, "unwrapObject", unwrappedObject);
+    return unwrappedObject;
+  }
+
+}

Modified: incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/proxy/Collaborator.java
URL: http://svn.apache.org/viewvc/incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/proxy/Collaborator.java?rev=897881&r1=897880&r2=897881&view=diff
==============================================================================
--- incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/proxy/Collaborator.java (original)
+++ incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/proxy/Collaborator.java Mon Jan 11 14:37:45 2010
@@ -116,19 +116,23 @@
     public Object invoke(Object proxy, Method method, Object[] args)
             throws Throwable {
         Object toReturn = null;
-
+        
         // Added method to unwrap from the collaborator.
         if (method.getName().equals("unwrapObject")
                 && method.getDeclaringClass() == WrapperedObject.class) {
             toReturn = object;
         } else
-        // Unwrap calls for equals 
+        // Unwrap calls for equals
         if (method.getName().equals("equals")
                 && method.getDeclaringClass() == Object.class) {
+            // replace the wrapper with the unwrapped object, to
+            // enable object identity etc to function.
             if (args[0] instanceof WrapperedObject) {
-                //replace the wrapper with the unwrapped object, to 
-                //enable object identity etc to function.
+                // unwrap in the WrapperedObject case
                 args[0] = ((WrapperedObject) args[0]).unwrapObject();
+            } else if (AsmInterceptorWrapper.isProxyClass(args[0].getClass())) {
+                // unwrap in the asm case
+                args[0] = AsmInterceptorWrapper.unwrapObject(args[0]);
             }
             toReturn = delegate.invoke(proxy, method, args);
         } else 

Added: incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/proxy/FinalModifierException.java
URL: http://svn.apache.org/viewvc/incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/proxy/FinalModifierException.java?rev=897881&view=auto
==============================================================================
--- incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/proxy/FinalModifierException.java (added)
+++ incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/proxy/FinalModifierException.java Mon Jan 11 14:37:45 2010
@@ -0,0 +1,51 @@
+/*
+ * 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.aries.blueprint.proxy;
+
+public class FinalModifierException extends UnableToProxyException
+{
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -3139392096074404448L;
+  public String finalMethods = null;
+
+  public FinalModifierException(Class<?> clazz)
+  {
+    super(clazz);
+  }
+
+  public FinalModifierException(Class<?> clazz, String finalMethods)
+  {
+    super(clazz);
+    this.finalMethods = finalMethods;
+  }
+
+  public boolean isFinalClass()
+  {
+    return (finalMethods == null || finalMethods.equals(""));
+  }
+
+  public String getFinalMethods()
+  {
+    return finalMethods;
+  }
+
+}

Added: incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/proxy/ProxyClassBytecodeGenerationException.java
URL: http://svn.apache.org/viewvc/incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/proxy/ProxyClassBytecodeGenerationException.java?rev=897881&view=auto
==============================================================================
--- incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/proxy/ProxyClassBytecodeGenerationException.java (added)
+++ incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/proxy/ProxyClassBytecodeGenerationException.java Mon Jan 11 14:37:45 2010
@@ -0,0 +1,33 @@
+/*
+ * 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.aries.blueprint.proxy;
+
+public class ProxyClassBytecodeGenerationException extends UnableToProxyException
+{
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -8015178382210046784L;
+
+  public ProxyClassBytecodeGenerationException(String string, Throwable throwable)
+  {
+    super(string, throwable);
+  }
+}

Added: incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/proxy/ProxyClassDefinitionException.java
URL: http://svn.apache.org/viewvc/incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/proxy/ProxyClassDefinitionException.java?rev=897881&view=auto
==============================================================================
--- incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/proxy/ProxyClassDefinitionException.java (added)
+++ incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/proxy/ProxyClassDefinitionException.java Mon Jan 11 14:37:45 2010
@@ -0,0 +1,32 @@
+/*
+ * 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.aries.blueprint.proxy;
+
+public class ProxyClassDefinitionException extends UnableToProxyException
+{
+  /**
+   * 
+   */
+  private static final long serialVersionUID = 604215734831044743L;
+
+  public ProxyClassDefinitionException(String className, Exception e)
+  {
+    super(className, e);
+  }
+}

Added: incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/proxy/ProxyClassInstantiationException.java
URL: http://svn.apache.org/viewvc/incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/proxy/ProxyClassInstantiationException.java?rev=897881&view=auto
==============================================================================
--- incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/proxy/ProxyClassInstantiationException.java (added)
+++ incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/proxy/ProxyClassInstantiationException.java Mon Jan 11 14:37:45 2010
@@ -0,0 +1,33 @@
+/*
+ * 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.aries.blueprint.proxy;
+
+public class ProxyClassInstantiationException extends UnableToProxyException
+{
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -2303296601108980837L;
+
+  public ProxyClassInstantiationException(Class<?> clazz, Exception e)
+  {
+    super(clazz, e);
+  }
+
+}

Added: incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/proxy/ProxySubclassAdapter.java
URL: http://svn.apache.org/viewvc/incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/proxy/ProxySubclassAdapter.java?rev=897881&view=auto
==============================================================================
--- incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/proxy/ProxySubclassAdapter.java (added)
+++ incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/proxy/ProxySubclassAdapter.java Mon Jan 11 14:37:45 2010
@@ -0,0 +1,618 @@
+/*
+ * 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.aries.blueprint.proxy;
+
+import java.io.IOException;
+import java.lang.reflect.InvocationHandler;
+
+import org.objectweb.asm.AnnotationVisitor;
+import org.objectweb.asm.Attribute;
+import org.objectweb.asm.ClassAdapter;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.FieldVisitor;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+import org.objectweb.asm.commons.GeneratorAdapter;
+import org.objectweb.asm.commons.Method;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ProxySubclassAdapter extends ClassAdapter implements Opcodes
+{
+
+  private static final Type STRING_TYPE = Type.getType(String.class);
+  private static final Type CLASS_TYPE = Type.getType(Class.class);
+  private static final Type OBJECT_TYPE = Type.getType(Object.class);
+  private static final Type METHOD_TYPE = Type.getType(java.lang.reflect.Method.class);
+  private static final Type IH_TYPE = Type.getType(InvocationHandler.class);
+  private static final Type[] NO_ARGS = new Type[] {};
+
+  private static final String IH_FIELD = "ih";
+
+  private static Logger LOGGER = LoggerFactory.getLogger(ProxySubclassAdapter.class);
+
+  private String newClassName = null;
+  private String superclassBinaryName = null;
+  private Class<?> superclassClass = null;
+  private ClassLoader loader = null;
+  private Type newClassType = null;
+  private GeneratorAdapter staticAdapter = null;
+  private String currentlyAnalysedClassName = null;
+  private Class<?> currentlyAnalysedClass = null;
+  private String currentClassFieldName = null;
+
+  public ProxySubclassAdapter(ClassVisitor writer, String newClassName, ClassLoader loader)
+  {
+    // call the superclass constructor
+    super(writer);
+    // the writer is now the cv in the superclass of ClassAdapter
+
+    LOGGER.debug(AsmInterceptorWrapper.LOG_ENTRY, "ProxySubclassAdapter", new Object[] { this, writer,
+        newClassName });
+
+    // set the newClassName field
+    this.newClassName = newClassName;
+    // set the newClassType descriptor
+    newClassType = Type.getType("L" + newClassName + ";");
+
+    // set the classloader
+    this.loader = loader;
+
+    LOGGER.debug(AsmInterceptorWrapper.LOG_EXIT, "ProxySubclassAdapter", this);
+  }
+
+  /*
+   * This method visits the class to generate the new subclass.
+   * 
+   * The following things happen here: 1. The class is renamed to a dynamic
+   * name 2. The existing class name is changed to be the superclass name so
+   * that the generated class extends the original class. 3. A private field
+   * is added to store an invocation handler 4. A constructor is added that
+   * takes an invocation handler as an argument 5. The constructor method
+   * instantiates an instance of the superclass 6. The constructor method sets
+   * the invocation handler so the invoke method can be called from all the
+   * subsequently rewritten methods 7. Add a getInvocationHandler() method 8.
+   * store a static Class object of the superclass so we can reflectively find
+   * methods later
+   */
+  public void visit(int version, int access, String name, String signature, String superName,
+      String[] interfaces)
+  {
+    LOGGER.debug(AsmInterceptorWrapper.LOG_ENTRY, "visit", new Object[] { version, access, name,
+        signature, superName, interfaces });
+
+    // store the superclass binary name
+    this.superclassBinaryName = name.replaceAll("/", "\\.");
+
+    try {
+      this.superclassClass = Class.forName(superclassBinaryName, false, loader);
+    } catch (ClassNotFoundException cnfe) {
+      throw new TypeNotPresentException(superclassBinaryName, cnfe);
+    }
+
+    // move the existing class name to become the superclass
+    // modify the version of the dynamic subclass to be Java 1.6
+    int newVersion = Opcodes.V1_6;
+    // keep the same access and signature as the superclass
+    // remove all the superclass interfaces because they will be inherited
+    // from the superclass anyway
+    cv.visit(newVersion, access, newClassName, signature, name, null);
+
+    // add a private field for the invocation handler
+    // this isn't static in case we have multiple instances of the same
+    // proxy
+    cv.visitField(ACC_PRIVATE, IH_FIELD, Type.getDescriptor(InvocationHandler.class), null, null);
+
+    // create a static adapter for generating a static initialiser method in
+    // the generated subclass
+    staticAdapter = new GeneratorAdapter(ACC_STATIC,
+        new Method("<clinit>", Type.VOID_TYPE, NO_ARGS), null, null, cv);
+
+    // add a constructor method that takes an invocation handler as an
+    // argument
+    Method m = new Method("<init>", Type.VOID_TYPE, new Type[] { IH_TYPE });
+    GeneratorAdapter methodAdapter = new GeneratorAdapter(ACC_PUBLIC, m, null, null, cv);
+    // loadthis
+    methodAdapter.loadThis();
+    // if we have java.* as a supertype call that zero args constructor
+    if (superclassBinaryName.startsWith("java.") || superclassBinaryName.startsWith("javax.")) {
+      methodAdapter.invokeConstructor(Type.getType(superclassClass), new Method("<init>",
+          Type.VOID_TYPE, NO_ARGS));
+    }
+    // otherwise invoke the java.lang.Object no args constructor
+    else {
+      methodAdapter.invokeConstructor(OBJECT_TYPE, new Method("<init>", Type.VOID_TYPE, NO_ARGS));
+    }
+    // call from the constructor to setInvocationHandler
+    Method setter = new Method("setInvocationHandler", Type.VOID_TYPE, new Type[] { IH_TYPE });
+    // load this
+    methodAdapter.loadThis();
+    // load the supplied invocation handler arg
+    methodAdapter.loadArgs();
+    // invoke the setter method
+    methodAdapter.invokeVirtual(newClassType, setter);
+    methodAdapter.returnValue();
+    methodAdapter.endMethod();
+
+    // add a method for getting the invocation handler
+    m = new Method("getInvocationHandler", IH_TYPE, NO_ARGS);
+    methodAdapter = new GeneratorAdapter(ACC_PUBLIC | ACC_FINAL, m, null, null, cv);
+    // load this to get the field
+    methodAdapter.loadThis();
+    // get the ih field and return
+    methodAdapter.getField(newClassType, IH_FIELD, IH_TYPE);
+    methodAdapter.returnValue();
+    methodAdapter.endMethod();
+
+    // add a method for setting the invocation handler
+    methodAdapter = new GeneratorAdapter(ACC_PUBLIC | ACC_FINAL, setter, null, null, cv);
+    // load this to put the field
+    methodAdapter.loadThis();
+    // load the method arguments (i.e. the invocation handler) to the stack
+    methodAdapter.loadArgs();
+    // set the ih field using the method argument
+    methodAdapter.putField(newClassType, IH_FIELD, IH_TYPE);
+    methodAdapter.returnValue();
+    methodAdapter.endMethod();
+
+    // loop through the class hierarchy to get any needed methods off the
+    // supertypes
+    // start by finding the methods declared on the class of interest (the
+    // superclass of our dynamic subclass)
+    java.lang.reflect.Method[] observedMethods = superclassClass.getDeclaredMethods();
+    // add the methods to a set of observedMethods
+    ProxySubclassMethodHashSet<String> setOfObservedMethods = new ProxySubclassMethodHashSet<String>(
+        observedMethods.length);
+    setOfObservedMethods.addMethodArray(observedMethods);
+    // get the next superclass in the hierarchy
+    Class<?> nextSuperClass = superclassClass.getSuperclass();
+    while (nextSuperClass != null) {
+      // set the fields for the current class
+      setCurrentAnalysisClassFields(nextSuperClass);
+
+      // add a static field and static initializer code to the generated
+      // subclass
+      // for each of the superclasses in the hierarchy
+      addClassStaticField(currentlyAnalysedClassName);
+
+      LOGGER.debug("Class currently being analysed: {} {}", currentlyAnalysedClassName,
+          currentlyAnalysedClass);
+
+      // now find the methods declared on the current class and add them
+      // to a set of foundMethods
+      java.lang.reflect.Method[] foundMethods = currentlyAnalysedClass.getDeclaredMethods();
+      ProxySubclassMethodHashSet<String> setOfFoundMethods = new ProxySubclassMethodHashSet<String>(
+          foundMethods.length);
+      setOfFoundMethods.addMethodArray(foundMethods);
+      // remove from the set of foundMethods any methods we saw on a
+      // subclass
+      // because we want to use the lowest level declaration of a method
+      setOfFoundMethods.removeAll(setOfObservedMethods);
+      try {
+        // read the current class and use a
+        // ProxySubclassHierarchyAdapter
+        // to process only methods on that class that are in the list
+        ClassReader cr = new ClassReader(loader.getResourceAsStream(currentlyAnalysedClass
+            .getName().replaceAll("\\.", "/")
+            + ".class"));
+        ClassVisitor hierarchyAdapter = new ProxySubclassHierarchyAdapter(this, setOfFoundMethods);
+        cr.accept(hierarchyAdapter, ClassReader.SKIP_DEBUG);
+      } catch (IOException e) {
+        throw new TypeNotPresentException(currentlyAnalysedClassName, e);
+      }
+      // now add the foundMethods to the overall list of observed methods
+      setOfObservedMethods.addAll(setOfFoundMethods);
+      // get the next class up in the hierarchy and go again
+      nextSuperClass = currentlyAnalysedClass.getSuperclass();
+    }
+
+    // we've finished looking at the superclass hierarchy
+    // set the fields for the immediate superclass of our dynamic subclass
+    setCurrentAnalysisClassFields(superclassClass);
+
+    // add the class static field
+    addClassStaticField(currentlyAnalysedClassName);
+    // we do the lowest class last because we are already visiting the class
+    // when in this adapter code
+    // now we are ready to visit all the methods on the lowest class
+    // which will happen by the ASM ClassVisitor implemented in this adapter
+
+    LOGGER.debug(AsmInterceptorWrapper.LOG_EXIT, "visit");
+  }
+
+  public void visitSource(String source, String debug)
+  {
+    LOGGER.debug(AsmInterceptorWrapper.LOG_ENTRY, "visitSource", new Object[] { source, debug });
+
+    // set the source to null since the class is generated on the fly and
+    // not compiled
+    cv.visitSource(null, null);
+
+    LOGGER.debug(AsmInterceptorWrapper.LOG_EXIT, "visitSource");
+  }
+
+  public void visitEnd()
+  {
+    LOGGER.debug(AsmInterceptorWrapper.LOG_ENTRY, "visitEnd");
+
+    // this method is called when we reach the end of the class
+    // so it is time to make sure the static initialiser method is closed
+    staticAdapter.returnValue();
+    staticAdapter.endMethod();
+    // now delegate to the cv
+    cv.visitEnd();
+
+    LOGGER.debug(AsmInterceptorWrapper.LOG_EXIT, "visitEnd");
+  }
+
+  /*
+   * This method is called on each method of the superclass (and all parent
+   * classes up to Object) Each of these methods is visited in turn and the
+   * code here generates the byte code for the InvocationHandler to call the
+   * methods on the superclass.
+   */
+  public MethodVisitor visitMethod(int access, String name, String desc, String signature,
+      String[] exceptions)
+  {
+    LOGGER.debug(AsmInterceptorWrapper.LOG_ENTRY, "visitMethod", new Object[] { access, name, desc,
+        signature, exceptions });
+
+    /*
+     * Check the method access and handle the method types we don't want to
+     * copy: final methods (issue warnings if these are not methods from
+     * java.* classes) static methods (initialiser and others) private
+     * methods constructors (for now we don't copy any constructors)
+     * abstract (we don't proxy/implement but we must copy the method or the
+     * subclass is invalid) everything else we process to proxy
+     */
+
+    LOGGER.debug("Method name: {} with descriptor: {}", name, desc);
+
+    MethodVisitor methodVisitorToReturn = null;
+
+    if (name.equals("<init>")) {
+      // we may need to do something extra with constructors later
+      // e.g. include bytecode for calling super with the same args
+      // since we currently rely on the super having a zero args
+      // constructor
+      // we need to issue an error if we don't find one
+
+      // for now we return null to ignore them
+      methodVisitorToReturn = null;
+    } else if (name.equals("<clinit>")) {
+      // don't copy static initialisers from the superclass into the new
+      // subclass
+      methodVisitorToReturn = null;
+    } else if ((access & ACC_FINAL) != 0) {
+      // since we check for final methods in the ProxySubclassGenerator we
+      // should never get here
+      methodVisitorToReturn = null;
+    } else if ((access & ACC_SYNTHETIC) != 0) {
+      // synthetic methods are generated by the compiler for covariance
+      // etc
+      // we shouldn't copy them or we will have duplicate methods
+      methodVisitorToReturn = null;
+    } else if ((access & ACC_PRIVATE) != 0) {
+      // don't copy private methods from the superclass
+      methodVisitorToReturn = null;
+    } else if ((access & ACC_STATIC) != 0) {
+      // don't copy static methods
+      methodVisitorToReturn = null;
+    } else if ((access & ACC_ABSTRACT) != 0) {
+      // if we find an abstract method we need to copy it as is to make
+      // the subclass valid
+      methodVisitorToReturn = cv.visitMethod(access, name, desc, signature, exceptions);
+    } else if (!(((access & ACC_PUBLIC) != 0) || ((access & ACC_PROTECTED) != 0) || ((access & ACC_PRIVATE) != 0))) {
+      // the default (package) modifier value is 0, so by using & with any
+      // of the other
+      // modifier values and getting a result of zero means that we have
+      // default accessibility
+
+      // check the package in which the method is declared against the
+      // package
+      // where the generated subclass will be
+      // if they are the same process the method otherwise ignore it
+      if (currentlyAnalysedClass.getPackage().equals(superclassClass.getPackage())) {
+        processMethod(access, name, desc, signature, exceptions);
+        methodVisitorToReturn = null;
+      } else {
+        methodVisitorToReturn = null;
+      }
+    } else {
+      processMethod(access, name, desc, signature, exceptions);
+      // return null because we don't want the original method code from
+      // the superclass
+      methodVisitorToReturn = null;
+    }
+
+    LOGGER.debug(AsmInterceptorWrapper.LOG_EXIT, "visitMethod", methodVisitorToReturn);
+
+    return methodVisitorToReturn;
+
+  }
+
+  private void processMethod(int access, String name, String desc, String signature,
+      String[] exceptions)
+  {
+    LOGGER.debug(AsmInterceptorWrapper.LOG_ENTRY, "processMethod", new Object[] { access, name, desc,
+        signature, exceptions });
+
+    LOGGER.debug("Processing method: {} with descriptor {}", name, desc);
+
+    // identify the target method parameters and return type
+    Method currentTransformMethod = new Method(name, desc);
+    Type[] targetMethodParameters = currentTransformMethod.getArgumentTypes();
+    Type returnType = currentTransformMethod.getReturnType();
+
+    // we create a static field for each method we encounter with a name
+    // like method_parm1_parm2...
+    StringBuilder methodStaticFieldNameBuilder = new StringBuilder(name);
+    // for each a parameter get the name and add it to the field removing
+    // the dots first
+    for (Type t : targetMethodParameters) {
+      methodStaticFieldNameBuilder.append("_");
+      methodStaticFieldNameBuilder.append(t.getClassName().replaceAll("\\[\\]", "Array")
+          .replaceAll("\\.", ""));
+    }
+    String methodStaticFieldName = methodStaticFieldNameBuilder.toString();
+
+    // add a private static field for the method
+    cv.visitField(ACC_PRIVATE | ACC_STATIC, methodStaticFieldName, METHOD_TYPE.getDescriptor(),
+        null, null);
+
+    // visit the method using the class writer, delegated through the method
+    // visitor and generator
+    // modify the method access so that any native methods aren't
+    // described as native
+    // since they won't be native in proxy form
+    // also stop methods being marked synchronized on the proxy as they will
+    // be sync
+    // on the real object
+    int newAccess = access & (~ACC_NATIVE) & (~ACC_SYNCHRONIZED);
+    MethodVisitor mv = cv.visitMethod(newAccess, name, desc, signature, exceptions);
+    // use a GeneratorAdapter to build the invoke call directly in byte code
+    GeneratorAdapter methodAdapter = new GeneratorAdapter(mv, newAccess, name, desc);
+
+    /*
+     * Stage 1 creates the bytecode for adding the reflected method of the
+     * superclass to a static field in the subclass: private static Method
+     * methodName_parm1_parm2... = null; static{ methodName_parm1_parm2... =
+     * superClass.getDeclaredMethod(methodName,new Class[]{method args}; }
+     * 
+     * Stage 2 is to call the ih.invoke(this,methodName_parm1_parm2,args) in
+     * the new subclass methods Stage 3 is to cast the return value to the
+     * correct type
+     */
+
+    /*
+     * Stage 1 use superClass.getMethod(methodName,new Class[]{method args}
+     * from the Class object on the stack
+     */
+
+    // load the static superclass Class onto the stack
+    staticAdapter.getStatic(newClassType, currentClassFieldName, CLASS_TYPE);
+
+    // push the method name string arg onto the stack
+    staticAdapter.push(name);
+
+    // create an array of the method parm class[] arg
+    staticAdapter.push(targetMethodParameters.length);
+    staticAdapter.newArray(CLASS_TYPE);
+    int index = 0;
+    for (Type t : targetMethodParameters) {
+      staticAdapter.dup();
+      staticAdapter.push(index);
+      switch (t.getSort())
+      {
+        case Type.BOOLEAN:
+          staticAdapter.getStatic(Type.getType(java.lang.Boolean.class), "TYPE", CLASS_TYPE);
+          break;
+        case Type.BYTE:
+          staticAdapter.getStatic(Type.getType(java.lang.Byte.class), "TYPE", CLASS_TYPE);
+          break;
+        case Type.CHAR:
+          staticAdapter.getStatic(Type.getType(java.lang.Character.class), "TYPE", CLASS_TYPE);
+          break;
+        case Type.DOUBLE:
+          staticAdapter.getStatic(Type.getType(java.lang.Double.class), "TYPE", CLASS_TYPE);
+          break;
+        case Type.FLOAT:
+          staticAdapter.getStatic(Type.getType(java.lang.Float.class), "TYPE", CLASS_TYPE);
+          break;
+        case Type.INT:
+          staticAdapter.getStatic(Type.getType(java.lang.Integer.class), "TYPE", CLASS_TYPE);
+          break;
+        case Type.LONG:
+          staticAdapter.getStatic(Type.getType(java.lang.Long.class), "TYPE", CLASS_TYPE);
+          break;
+        case Type.SHORT:
+          staticAdapter.getStatic(Type.getType(java.lang.Short.class), "TYPE", CLASS_TYPE);
+          break;
+        default:
+        case Type.OBJECT:
+          staticAdapter.push(t);
+          break;
+      }
+      staticAdapter.arrayStore(CLASS_TYPE);
+      index++;
+    }
+
+    // invoke the getMethod
+    staticAdapter.invokeVirtual(CLASS_TYPE, new Method("getDeclaredMethod", METHOD_TYPE,
+        new Type[] { STRING_TYPE, Type.getType(java.lang.Class[].class) }));
+
+    // store the reflected method in the static field
+    staticAdapter.putStatic(newClassType, methodStaticFieldName, METHOD_TYPE);
+
+    /*
+     * Stage 2 call the ih.invoke(this,supermethod,parms)
+     */
+
+    // load this to get the ih field
+    methodAdapter.loadThis();
+    // load the invocation handler from the field (the location of the
+    // InvocationHandler.invoke)
+    methodAdapter.getField(newClassType, IH_FIELD, IH_TYPE);
+    // loadThis (the first arg of the InvocationHandler.invoke)
+    methodAdapter.loadThis();
+    // load the method to invoke (the second arg of the
+    // InvocationHandler.invoke)
+    methodAdapter.getStatic(newClassType, methodStaticFieldName, METHOD_TYPE);
+    // load all the method arguments onto the stack as an object array (the
+    // third arg of the InvocationHandler.invoke)
+    methodAdapter.loadArgArray();
+    // generate the invoke method
+    Method invocationHandlerInvokeMethod = new Method("invoke", OBJECT_TYPE, new Type[] {
+        OBJECT_TYPE, METHOD_TYPE, Type.getType(java.lang.Object[].class) });
+    // call the invoke method of the invocation handler
+    methodAdapter.invokeInterface(IH_TYPE, invocationHandlerInvokeMethod);
+
+    /*
+     * Stage 3 the returned object is now on the top of the stack We need to
+     * check the type and cast as necessary
+     */
+    switch (returnType.getSort())
+    {
+      case Type.BOOLEAN:
+        methodAdapter.cast(OBJECT_TYPE, Type.getType(Boolean.class));
+        methodAdapter.unbox(Type.BOOLEAN_TYPE);
+        break;
+      case Type.BYTE:
+        methodAdapter.cast(OBJECT_TYPE, Type.getType(Byte.class));
+        methodAdapter.unbox(Type.BYTE_TYPE);
+        break;
+      case Type.CHAR:
+        methodAdapter.cast(OBJECT_TYPE, Type.getType(Character.class));
+        methodAdapter.unbox(Type.CHAR_TYPE);
+        break;
+      case Type.DOUBLE:
+        methodAdapter.cast(OBJECT_TYPE, Type.getType(Double.class));
+        methodAdapter.unbox(Type.DOUBLE_TYPE);
+        break;
+      case Type.FLOAT:
+        methodAdapter.cast(OBJECT_TYPE, Type.getType(Float.class));
+        methodAdapter.unbox(Type.FLOAT_TYPE);
+        break;
+      case Type.INT:
+        methodAdapter.cast(OBJECT_TYPE, Type.getType(Integer.class));
+        methodAdapter.unbox(Type.INT_TYPE);
+        break;
+      case Type.LONG:
+        methodAdapter.cast(OBJECT_TYPE, Type.getType(Long.class));
+        methodAdapter.unbox(Type.LONG_TYPE);
+        break;
+      case Type.SHORT:
+        methodAdapter.cast(OBJECT_TYPE, Type.getType(Short.class));
+        methodAdapter.unbox(Type.SHORT_TYPE);
+        break;
+      case Type.VOID:
+        methodAdapter.cast(OBJECT_TYPE, Type.getType(Void.class));
+        methodAdapter.unbox(Type.VOID_TYPE);
+        break;
+      default:
+      case Type.OBJECT:
+        // in this case check the cast and cast the object to the return
+        // type
+        methodAdapter.checkCast(returnType);
+        methodAdapter.cast(OBJECT_TYPE, returnType);
+        break;
+    }
+    // return the (appropriately cast) result of the invocation from the
+    // stack
+    methodAdapter.returnValue();
+    // end the method
+    methodAdapter.endMethod();
+
+    LOGGER.debug(AsmInterceptorWrapper.LOG_EXIT, "processMethod");
+  }
+
+  private void addClassStaticField(String classBinaryName)
+  {
+    LOGGER.debug(AsmInterceptorWrapper.LOG_ENTRY, "addClassStaticField",
+        new Object[] { classBinaryName });
+
+    currentClassFieldName = classBinaryName.replaceAll("\\.", "_");
+
+    /*
+     * use Class.forName on the superclass so we can reflectively find
+     * methods later
+     * 
+     * produces bytecode for retrieving the superclass and storing in a
+     * private static field: private static Class superClass = null; static{
+     * superClass = Class.forName(superclass); }
+     */
+
+    // add a private static field for the superclass Class
+    cv.visitField(ACC_PRIVATE | ACC_STATIC, currentClassFieldName, CLASS_TYPE.getDescriptor(),
+        null, null);
+
+    // push the String arg for the Class.forName onto the stack
+    staticAdapter.push(classBinaryName);
+
+    // invoke the Class forName putting the Class on the stack
+    staticAdapter.invokeStatic(CLASS_TYPE, new Method("forName", CLASS_TYPE,
+        new Type[] { STRING_TYPE }));
+
+    // put the Class in the static field
+    staticAdapter.putStatic(newClassType, currentClassFieldName, CLASS_TYPE);
+
+    LOGGER.debug(AsmInterceptorWrapper.LOG_ENTRY, "addClassStaticField");
+  }
+
+  private void setCurrentAnalysisClassFields(Class<?> aClass)
+  {
+    LOGGER.debug(AsmInterceptorWrapper.LOG_ENTRY, "setCurrentAnalysisClassFields",
+        new Object[] { aClass });
+
+    currentlyAnalysedClassName = aClass.getName();
+    currentlyAnalysedClass = aClass;
+
+    LOGGER.debug(AsmInterceptorWrapper.LOG_EXIT, "setCurrentAnalysisClassFields");
+  }
+
+  // we don't want to copy fields from the class into the proxy
+  public FieldVisitor visitField(int access, String name, String desc, String signature,
+      Object value)
+  {
+    return null;
+  }
+
+  // for now we don't do any processing in these methods
+  public AnnotationVisitor visitAnnotation(String desc, boolean visible)
+  {
+    return null;
+  }
+
+  public void visitAttribute(Attribute attr)
+  {
+    // no-op
+  }
+
+  public void visitInnerClass(String name, String outerName, String innerName, int access)
+  {
+    // no-op
+  }
+
+  public void visitOuterClass(String owner, String name, String desc)
+  {
+    // no-op
+  }
+}

Added: incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/proxy/ProxySubclassGenerator.java
URL: http://svn.apache.org/viewvc/incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/proxy/ProxySubclassGenerator.java?rev=897881&view=auto
==============================================================================
--- incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/proxy/ProxySubclassGenerator.java (added)
+++ incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/proxy/ProxySubclassGenerator.java Mon Jan 11 14:37:45 2010
@@ -0,0 +1,359 @@
+/*
+ * 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.aries.blueprint.proxy;
+
+import static java.lang.reflect.Modifier.isFinal;
+
+import java.io.IOException;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.security.ProtectionDomain;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.WeakHashMap;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.ClassWriter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ProxySubclassGenerator
+{
+
+  private final static Logger LOGGER = LoggerFactory.getLogger(ProxySubclassGenerator.class);
+
+  // This map holds references to the names of classes created by this Class
+  // It is a weak map (so when a ClassLoader is garbage collected we remove
+  // the map of
+  // Class names to sub-Class names)
+  private static final Map<ClassLoader, ConcurrentMap<String, String>> proxyClassesByClassLoader;
+
+  static {
+    // Ensure that this is a synchronized map as we may use it from multiple
+    // threads concurrently
+    //
+    proxyClassesByClassLoader = Collections
+        .synchronizedMap(new WeakHashMap<ClassLoader, ConcurrentMap<String, String>>());
+  }
+
+  private static final char FINAL_MODIFIER = '!';
+  private static final char UNABLE_TO_PROXY = '#';
+
+  public static Class<?> getProxySubclass(Class<?> aClass) throws UnableToProxyException
+  {
+    LOGGER.debug(AsmInterceptorWrapper.LOG_ENTRY, "getProxySubclass", new Object[] { aClass });
+
+    ClassLoader loader = aClass.getClassLoader();
+    // in the special case where the loader is null we use the thread
+    // ContextClassLoader
+    // this is for subclassing java.* or javax.* packages
+    if (loader == null) loader = Thread.currentThread().getContextClassLoader();
+
+    ConcurrentMap<String, String> proxyMap;
+    synchronized (loader) {
+      proxyMap = proxyClassesByClassLoader.get(loader);
+      if (proxyMap == null) {
+        proxyMap = new ConcurrentHashMap<String, String>();
+        proxyClassesByClassLoader.put(loader, proxyMap);
+      }
+    }
+
+    // check the map to see if we have already generated a subclass for this
+    // class
+    // if we have return the mapped class object
+    // if we haven't generate the subclass and return it
+    Class<?> classToReturn = null;
+    synchronized (aClass) {
+      String key = aClass.getName();
+      String className = proxyMap.get(key);
+      if (className != null) {
+
+        LOGGER.debug("Found proxy subclass with key {} and name {}.", key, className);
+
+        if (className.charAt(0) == FINAL_MODIFIER) {
+          String[] exceptionParts = className.substring(1).split(":");
+          if (exceptionParts.length == 1) {
+            throw new FinalModifierException(aClass);
+          } else {
+            throw new FinalModifierException(aClass, exceptionParts[1]);
+          }
+        } else if (className.charAt(0) == UNABLE_TO_PROXY) {
+          throw new UnableToProxyException(aClass);
+        }
+
+        try {
+          classToReturn = loader.loadClass(className);
+        } catch (ClassNotFoundException cnfe) {
+          LOGGER.debug(AsmInterceptorWrapper.LOG_EXCEPTION, cnfe);
+          throw new UnableToLoadProxyException(className, cnfe);
+        }
+      } else {
+
+        LOGGER.debug("Need to generate subclass. Using key {}.", key);
+        try {
+          scanForFinalModifiers(aClass);
+
+          classToReturn = generateAndLoadSubclass(aClass, loader);
+
+          if (classToReturn != null) {
+            proxyMap.put(key, classToReturn.getName());
+          } else {
+            proxyMap.put(key, UNABLE_TO_PROXY + aClass.getName());
+            throw new UnableToProxyException(aClass);
+          }
+        } catch (FinalModifierException e) {
+          if (e.isFinalClass()) {
+            proxyMap.put(key, FINAL_MODIFIER + e.getClassName());
+            throw e;
+          } else {
+            proxyMap.put(key, FINAL_MODIFIER + e.getClassName() + ':' + e.getFinalMethods());
+            throw e;
+          }
+        }
+
+      }
+    }
+
+    LOGGER.debug(AsmInterceptorWrapper.LOG_EXIT, "getProxySubclass", classToReturn);
+
+    return classToReturn;
+  }
+
+  public static Object newProxySubclassInstance(Class<?> classToProxy, InvocationHandler ih)
+      throws UnableToProxyException
+  {
+
+    LOGGER.debug(AsmInterceptorWrapper.LOG_ENTRY, "newProxySubclassInstance", new Object[] {
+        classToProxy, ih });
+
+    Object proxySubclassInstance = null;
+    try {
+      Class<?> generatedProxySubclass = getProxySubclass(classToProxy);
+      LOGGER.debug("Getting the proxy subclass constructor");
+      Constructor<?> subclassConstructor = generatedProxySubclass
+          .getConstructor(new Class[] { InvocationHandler.class });
+      LOGGER.debug("Invoking the proxy subclass constructor");
+      proxySubclassInstance = subclassConstructor.newInstance(ih);
+      LOGGER.debug("Invoked proxy subclass constructor");
+    } catch (NoSuchMethodException nsme) {
+      LOGGER.debug(AsmInterceptorWrapper.LOG_EXCEPTION, nsme);
+      throw new ProxyClassInstantiationException(classToProxy, nsme);
+    } catch (InvocationTargetException ite) {
+      LOGGER.debug(AsmInterceptorWrapper.LOG_EXCEPTION, ite);
+      throw new ProxyClassInstantiationException(classToProxy, ite);
+    } catch (InstantiationException ie) {
+      LOGGER.debug(AsmInterceptorWrapper.LOG_EXCEPTION, ie);
+      throw new ProxyClassInstantiationException(classToProxy, ie);
+    } catch (IllegalAccessException iae) {
+      LOGGER.debug(AsmInterceptorWrapper.LOG_EXCEPTION, iae);
+      throw new ProxyClassInstantiationException(classToProxy, iae);
+    }
+
+    LOGGER.debug(AsmInterceptorWrapper.LOG_EXIT, "newProxySubclassInstance", proxySubclassInstance);
+
+    return proxySubclassInstance;
+  }
+
+  private static Class<?> generateAndLoadSubclass(Class<?> aClass, ClassLoader loader)
+      throws UnableToProxyException
+  {
+    LOGGER.debug(AsmInterceptorWrapper.LOG_ENTRY, "generateAndLoadSubclass", new Object[] { aClass,
+        loader });
+
+    // set the newClassName
+    String newClassName = "$" + aClass.getSimpleName() + aClass.hashCode();
+    String packageName = aClass.getPackage().getName();
+    if (packageName.startsWith("java.") || packageName.startsWith("javax.")) {
+      packageName = "com.ibm.osgi.blueprint.proxy." + packageName;
+    }
+    String fullNewClassName = (packageName + "." + newClassName).replaceAll("\\.", "/");
+
+    LOGGER.debug("New class name: {}", newClassName);
+    LOGGER.debug("Full new class name: {}", fullNewClassName);
+
+    Class<?> clazz = null;
+    try {
+      ClassReader cReader = new ClassReader(loader.getResourceAsStream(aClass.getName().replaceAll(
+          "\\.", "/")
+          + ".class"));
+      ClassWriter cWriter = new ClassWriter(ClassWriter.COMPUTE_MAXS);
+      ClassVisitor dynamicSubclassAdapter = new ProxySubclassAdapter(cWriter, fullNewClassName,
+          loader);
+      byte[] byteClassData = processClass(cReader, cWriter, dynamicSubclassAdapter);
+      clazz = loadClassFromBytes(loader, getBinaryName(fullNewClassName), byteClassData, aClass
+          .getName());
+    } catch (IOException ioe) {
+      LOGGER.debug(AsmInterceptorWrapper.LOG_EXCEPTION, ioe);
+      throw new ProxyClassBytecodeGenerationException(aClass.getName(), ioe);
+    } catch (TypeNotPresentException tnpe) {
+      LOGGER.debug(AsmInterceptorWrapper.LOG_EXCEPTION, tnpe);
+      throw new ProxyClassBytecodeGenerationException(tnpe.typeName(), tnpe.getCause());
+    }
+
+    LOGGER.debug(AsmInterceptorWrapper.LOG_EXIT, "generateAndLoadSubclass", clazz);
+
+    return clazz;
+  }
+
+  private static byte[] processClass(ClassReader cReader, ClassWriter cWriter, ClassVisitor cVisitor)
+  {
+    LOGGER.debug(AsmInterceptorWrapper.LOG_ENTRY, "processClass", new Object[] { cReader, cWriter,
+        cVisitor });
+
+    cReader.accept(cVisitor, ClassReader.SKIP_DEBUG);
+    byte[] byteClassData = cWriter.toByteArray();
+
+    LOGGER.debug(AsmInterceptorWrapper.LOG_EXIT, "processClass", byteClassData);
+
+    return byteClassData;
+  }
+
+  private static String getBinaryName(String name)
+  {
+    LOGGER.debug(AsmInterceptorWrapper.LOG_ENTRY, "getBinaryName", name);
+
+    String binaryName = name.replaceAll("/", "\\.");
+
+    LOGGER.debug(AsmInterceptorWrapper.LOG_EXIT, "getBinaryName", binaryName);
+
+    return binaryName;
+  }
+
+  private static Class<?> loadClassFromBytes(ClassLoader loader, String name, byte[] classData,
+      String classToProxyName) throws UnableToProxyException
+  {
+    LOGGER.debug(AsmInterceptorWrapper.LOG_ENTRY, "loadClassFromBytes", new Object[] { loader, name,
+        classData });
+
+    Class<?> clazz = null;
+    try {
+      Method defineClassMethod = Class.forName("java.lang.ClassLoader").getDeclaredMethod(
+          "defineClass", String.class, byte[].class, int.class, int.class, ProtectionDomain.class);
+      defineClassMethod.setAccessible(true);
+      // define the class in the same classloader where aClass is loaded,
+      // but use the protection domain of our code
+      clazz = (Class<?>) defineClassMethod.invoke(loader, name, classData, 0, classData.length,
+          ProxySubclassGenerator.class.getProtectionDomain());
+      defineClassMethod.setAccessible(false);
+    } catch (ClassNotFoundException cnfe) {
+      LOGGER.debug(AsmInterceptorWrapper.LOG_EXCEPTION, cnfe);
+      throw new ProxyClassDefinitionException(classToProxyName, cnfe);
+    } catch (NoSuchMethodException nsme) {
+      LOGGER.debug(AsmInterceptorWrapper.LOG_EXCEPTION, nsme);
+      throw new ProxyClassDefinitionException(classToProxyName, nsme);
+    } catch (InvocationTargetException ite) {
+      LOGGER.debug(AsmInterceptorWrapper.LOG_EXCEPTION, ite);
+      throw new ProxyClassDefinitionException(classToProxyName, ite);
+    } catch (IllegalAccessException iae) {
+      LOGGER.debug(AsmInterceptorWrapper.LOG_EXCEPTION, iae);
+      throw new ProxyClassDefinitionException(classToProxyName, iae);
+    }
+
+    LOGGER.debug(AsmInterceptorWrapper.LOG_EXIT, "loadClassFromBytes", clazz);
+
+    return clazz;
+  }
+
+  public static boolean isProxySubclass(Class<?> aClass)
+  {
+    LOGGER.debug(AsmInterceptorWrapper.LOG_ENTRY, "isProxySubclass", new Object[] { aClass });
+
+    // We will always have a proxy map for the class loader of any proxy
+    // class, so if
+    // this is null we know to return false
+    Map<String, String> proxies = proxyClassesByClassLoader.get(aClass.getClassLoader());
+
+    boolean isProxySubclass = (proxies != null && proxies.containsValue(aClass.getName()));
+
+    LOGGER.debug(AsmInterceptorWrapper.LOG_EXIT, "isProxySubclass", isProxySubclass);
+
+    return isProxySubclass;
+  }
+
+  private static void scanForFinalModifiers(Class<?> clazz) throws FinalModifierException
+  {
+    LOGGER.debug(AsmInterceptorWrapper.LOG_ENTRY, "scanForFinalModifiers", new Object[] { clazz });
+
+    if (isFinal(clazz.getModifiers())) {
+      throw new FinalModifierException(clazz);
+    }
+
+    List<String> finalMethods = new ArrayList<String>();
+
+    // we don't want to check for final methods on java.* or javax.* Class
+    // also, clazz can never be null here (we will always hit
+    // java.lang.Object first)
+    while (!clazz.getName().startsWith("java.") && !clazz.getName().startsWith("javax.")) {
+      for (Method m : clazz.getDeclaredMethods()) {
+        if (isFinal(m.getModifiers())) {
+          finalMethods.add(m.toGenericString());
+        }
+      }
+      clazz = clazz.getSuperclass();
+    }
+
+    if (!finalMethods.isEmpty()) {
+
+      String methodList = finalMethods.toString();
+      methodList = methodList.substring(1, methodList.length() - 1);
+      throw new FinalModifierException(clazz, methodList);
+    }
+
+    LOGGER.debug(AsmInterceptorWrapper.LOG_EXIT, "scanForFinalModifiers");
+
+  }
+
+  public static InvocationHandler getInvocationHandler(Object o)
+  {
+    LOGGER.debug(AsmInterceptorWrapper.LOG_ENTRY, "getInvoationHandler", new Object[] { o });
+
+    InvocationHandler ih = null;
+    if (isProxySubclass(o.getClass())) {
+      // we have to catch exceptions here, but we just log them
+      // the reason for this is that it should be impossible to get these
+      // exceptions
+      // since the Object we are dealing with is a class we generated on
+      // the fly
+      try {
+        ih = (InvocationHandler) o.getClass().getDeclaredMethod("getInvocationHandler",
+            new Class[] {}).invoke(o, new Object[] {});
+      } catch (IllegalArgumentException e) {
+        LOGGER.debug(AsmInterceptorWrapper.LOG_EXCEPTION, e);
+      } catch (SecurityException e) {
+        LOGGER.debug(AsmInterceptorWrapper.LOG_EXCEPTION, e);
+      } catch (IllegalAccessException e) {
+        LOGGER.debug(AsmInterceptorWrapper.LOG_EXCEPTION, e);
+      } catch (InvocationTargetException e) {
+        LOGGER.debug(AsmInterceptorWrapper.LOG_EXCEPTION, e);
+      } catch (NoSuchMethodException e) {
+        LOGGER.debug(AsmInterceptorWrapper.LOG_EXCEPTION, e);
+      }
+    }
+    LOGGER.debug(AsmInterceptorWrapper.LOG_EXIT, "getInvoationHandler", ih);
+    return ih;
+  }
+
+}

Added: incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/proxy/ProxySubclassHierarchyAdapter.java
URL: http://svn.apache.org/viewvc/incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/proxy/ProxySubclassHierarchyAdapter.java?rev=897881&view=auto
==============================================================================
--- incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/proxy/ProxySubclassHierarchyAdapter.java (added)
+++ incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/proxy/ProxySubclassHierarchyAdapter.java Mon Jan 11 14:37:45 2010
@@ -0,0 +1,124 @@
+/*
+ * 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.aries.blueprint.proxy;
+
+import java.util.Collection;
+
+import org.objectweb.asm.AnnotationVisitor;
+import org.objectweb.asm.Attribute;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.FieldVisitor;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/*
+ * Although we implement ClassVisitor we are only interested in the methods of
+ * the superclasses in the hierarchy.  For this reason although visitMethod is 
+ * implemented the other methods of ClassVisitor are currently no-op.
+ *
+ *
+ */
+public class ProxySubclassHierarchyAdapter implements ClassVisitor, Opcodes
+{
+
+  private ProxySubclassAdapter adapter = null;
+  private Collection<String> methodsToImplement = null;
+
+  private static Logger LOGGER = LoggerFactory.getLogger(ProxySubclassHierarchyAdapter.class);
+
+  ProxySubclassHierarchyAdapter(ProxySubclassAdapter adapter, Collection<String> methodsToImplement)
+  {
+    LOGGER.debug(AsmInterceptorWrapper.LOG_ENTRY, "ProxySubclassHeirarchyAdapter", new Object[] {
+        this, adapter, methodsToImplement });
+
+    this.methodsToImplement = methodsToImplement;
+    this.adapter = adapter;
+
+    LOGGER.debug(AsmInterceptorWrapper.LOG_EXIT, "ProxySubclassHeirarchyAdapter", this);
+  }
+
+  public MethodVisitor visitMethod(int access, String name, String desc, String signature,
+      String[] exceptions)
+  {
+    LOGGER.debug(AsmInterceptorWrapper.LOG_ENTRY, "visitMethod", new Object[] { access, name, desc,
+        signature, exceptions });
+
+    // if the method we find in the superclass is one that is available on
+    // the class
+    // we are dynamically subclassing then we need to implement an
+    // invocation for it
+    String argDesc = ProxySubclassMethodHashSet.typeArrayToStringArgDescriptor(Type
+        .getArgumentTypes(desc));
+    if (methodsToImplement.contains(name + argDesc)) {
+      // create the method in bytecode
+      adapter.visitMethod(access, name, desc, signature, exceptions);
+    }
+
+    LOGGER.debug(AsmInterceptorWrapper.LOG_EXIT, "visitMethod");
+
+    // always return null because we don't want to copy any method code
+    return null;
+  }
+
+  public void visit(int arg0, int arg1, String arg2, String arg3, String arg4, String[] arg5)
+  {
+    // no-op
+  }
+
+  public AnnotationVisitor visitAnnotation(String arg0, boolean arg1)
+  {
+    // don't process any annotations at this stage
+    return null;
+  }
+
+  public void visitAttribute(Attribute arg0)
+  {
+    // no-op
+  }
+
+  public void visitEnd()
+  {
+    // no-op
+  }
+
+  public FieldVisitor visitField(int arg0, String arg1, String arg2, String arg3, Object arg4)
+  {
+    // don't process fields
+    return null;
+  }
+
+  public void visitInnerClass(String arg0, String arg1, String arg2, int arg3)
+  {
+    // no-op
+  }
+
+  public void visitOuterClass(String arg0, String arg1, String arg2)
+  {
+    // no-op
+  }
+
+  public void visitSource(String arg0, String arg1)
+  {
+    // no-op
+  }
+
+}

Added: incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/proxy/ProxySubclassMethodHashSet.java
URL: http://svn.apache.org/viewvc/incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/proxy/ProxySubclassMethodHashSet.java?rev=897881&view=auto
==============================================================================
--- incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/proxy/ProxySubclassMethodHashSet.java (added)
+++ incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/proxy/ProxySubclassMethodHashSet.java Mon Jan 11 14:37:45 2010
@@ -0,0 +1,69 @@
+/*
+ * 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.aries.blueprint.proxy;
+
+import java.util.HashSet;
+
+import org.objectweb.asm.Type;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ProxySubclassMethodHashSet<E> extends HashSet<String>
+{
+  private static final long serialVersionUID = 7674408912532811084L;
+
+  private static Logger LOGGER = LoggerFactory.getLogger(ProxySubclassMethodHashSet.class);
+
+  public ProxySubclassMethodHashSet(int i)
+  {
+    super(i);
+  }
+
+  public void addMethodArray(java.lang.reflect.Method[] arrayOfEntries)
+  {
+    LOGGER.debug(AsmInterceptorWrapper.LOG_ENTRY, "addMethodArray", new Object[] { arrayOfEntries });
+
+    for (java.lang.reflect.Method entry : arrayOfEntries) {
+      String methodName = entry.getName();
+
+      LOGGER.debug("Method name: {}", methodName);
+
+      Type[] methodArgTypes = Type.getArgumentTypes(entry);
+      String argDescriptor = typeArrayToStringArgDescriptor(methodArgTypes);
+
+      LOGGER.debug("Descriptor: {}", argDescriptor);
+
+      boolean added = super.add(methodName + argDescriptor);
+
+      LOGGER.debug("Added: {}", added);
+    }
+
+    LOGGER.debug(AsmInterceptorWrapper.LOG_EXIT, "addMethodArray");
+  }
+
+  static String typeArrayToStringArgDescriptor(Type[] argTypes)
+  {
+    StringBuilder descriptor = new StringBuilder();
+    for (Type t : argTypes) {
+      descriptor.append(t.toString());
+    }
+    return descriptor.toString();
+  }
+
+}

Added: incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/proxy/UnableToLoadProxyException.java
URL: http://svn.apache.org/viewvc/incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/proxy/UnableToLoadProxyException.java?rev=897881&view=auto
==============================================================================
--- incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/proxy/UnableToLoadProxyException.java (added)
+++ incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/proxy/UnableToLoadProxyException.java Mon Jan 11 14:37:45 2010
@@ -0,0 +1,33 @@
+/*
+ * 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.aries.blueprint.proxy;
+
+public class UnableToLoadProxyException extends UnableToProxyException
+{
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = 506487573157016476L;
+
+  public UnableToLoadProxyException(String className, Exception e)
+  {
+    super(className, e);
+  }
+}

Added: incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/proxy/UnableToProxyException.java
URL: http://svn.apache.org/viewvc/incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/proxy/UnableToProxyException.java?rev=897881&view=auto
==============================================================================
--- incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/proxy/UnableToProxyException.java (added)
+++ incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/proxy/UnableToProxyException.java Mon Jan 11 14:37:45 2010
@@ -0,0 +1,51 @@
+/*
+ * 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.aries.blueprint.proxy;
+
+public class UnableToProxyException extends Exception
+{
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -17516969014644128L;
+  String className = null;
+
+  public UnableToProxyException(Class<?> clazz)
+  {
+    className = clazz.getName();
+  }
+
+  public UnableToProxyException(Class<?> clazz, Exception e)
+  {
+    this(clazz.getName(), e);
+  }
+
+  public UnableToProxyException(String className, Throwable e)
+  {
+    super(e);
+    this.className = className;
+  }
+
+  public String getClassName()
+  {
+    return className;
+  }
+
+}