You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@aries.apache.org by zo...@apache.org on 2011/02/28 11:38:38 UTC

svn commit: r1075271 [2/3] - in /aries/branches/experimental-release-by-module/proxy/proxy-distro/src/main/resources/source: ./ META-INF/ proxy-api/ proxy-api/src/ proxy-api/src/main/ proxy-api/src/main/java/ proxy-api/src/main/java/org/ proxy-api/src/...

Added: aries/branches/experimental-release-by-module/proxy/proxy-distro/src/main/resources/source/proxy-impl/src/main/java/org/apache/aries/proxy/impl/ProxyHandler.java
URL: http://svn.apache.org/viewvc/aries/branches/experimental-release-by-module/proxy/proxy-distro/src/main/resources/source/proxy-impl/src/main/java/org/apache/aries/proxy/impl/ProxyHandler.java?rev=1075271&view=auto
==============================================================================
--- aries/branches/experimental-release-by-module/proxy/proxy-distro/src/main/resources/source/proxy-impl/src/main/java/org/apache/aries/proxy/impl/ProxyHandler.java (added)
+++ aries/branches/experimental-release-by-module/proxy/proxy-distro/src/main/resources/source/proxy-impl/src/main/java/org/apache/aries/proxy/impl/ProxyHandler.java Mon Feb 28 10:38:36 2011
@@ -0,0 +1,85 @@
+/*
+ * 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.proxy.impl;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.concurrent.Callable;
+
+import org.apache.aries.proxy.InvocationHandlerWrapper;
+
+public final class ProxyHandler implements InvocationHandler {
+  private final Callable<Object> target;
+  private final InvocationHandler core;
+  private final InvocationHandlerWrapper wrapper;
+  private final AbstractProxyManager proxyManager;
+
+  public ProxyHandler(AbstractProxyManager abstractProxyManager, Callable<Object> dispatcher, InvocationHandlerWrapper wrapper)
+  {
+    target = dispatcher;
+    proxyManager = abstractProxyManager;
+    if (wrapper == null) {
+      this.wrapper = new DefaultWrapper();
+    } else {
+      this.wrapper = wrapper;
+    }
+    
+    core = new InvocationHandler() {
+      public Object invoke(Object proxy, Method method, Object[] args)
+          throws Throwable 
+      {
+          Object result;
+          try {
+              result = method.invoke(target.call(), args);
+          } catch (InvocationTargetException ite) {
+              // We are invisible, so unwrap and throw the cause as
+              // though we called the method directly.
+              throw ite.getCause();
+          } catch (IllegalAccessException e) {
+              throw new IllegalAccessError(e.getMessage());
+          }
+
+          return result;
+      }
+    };
+  }
+
+  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable 
+  {
+    // Unwrap calls for equals
+    if (method.getName().equals("equals")
+            && method.getDeclaringClass() == Object.class) {
+        Object targetObject = args[0];
+        if (proxyManager.isProxy(targetObject)) {
+          args[0] = proxyManager.unwrap(targetObject).call();
+        }
+    } else if (method.getName().equals("finalize") && method.getParameterTypes().length == 0) {
+        // special case finalize, don't route through to delegate because that will get its own call
+        return null;
+    }
+    
+    return wrapper.invoke(proxy, method, args, core);
+  }
+
+  public Callable<Object> getTarget() 
+  {
+    return target;
+  }
+}
\ No newline at end of file

Added: aries/branches/experimental-release-by-module/proxy/proxy-distro/src/main/resources/source/proxy-impl/src/main/java/org/apache/aries/proxy/impl/ProxyManagerActivator.java
URL: http://svn.apache.org/viewvc/aries/branches/experimental-release-by-module/proxy/proxy-distro/src/main/resources/source/proxy-impl/src/main/java/org/apache/aries/proxy/impl/ProxyManagerActivator.java?rev=1075271&view=auto
==============================================================================
--- aries/branches/experimental-release-by-module/proxy/proxy-distro/src/main/resources/source/proxy-impl/src/main/java/org/apache/aries/proxy/impl/ProxyManagerActivator.java (added)
+++ aries/branches/experimental-release-by-module/proxy/proxy-distro/src/main/resources/source/proxy-impl/src/main/java/org/apache/aries/proxy/impl/ProxyManagerActivator.java Mon Feb 28 10:38:36 2011
@@ -0,0 +1,58 @@
+/*
+ * 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.proxy.impl;
+
+import org.apache.aries.proxy.ProxyManager;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+
+public class ProxyManagerActivator implements BundleActivator 
+{
+  private static final boolean ASM_PROXY_SUPPORTED;
+  private AbstractProxyManager managerService;
+  
+  static
+  {
+    boolean classProxy = false;
+    try {
+      // Try load load a asm class (to make sure it's actually available
+      // then create the asm factory
+      Class.forName("org.objectweb.asm.ClassVisitor");
+      classProxy = true;
+    } catch (Throwable t) {
+    }
+    
+    ASM_PROXY_SUPPORTED = classProxy;
+  }
+  
+  public void start(BundleContext context)
+  {
+    if (ASM_PROXY_SUPPORTED) {
+      managerService = new AsmProxyManager();
+    } else {
+      managerService = new JdkProxyManager();
+    }
+    
+    context.registerService(ProxyManager.class.getName(), managerService, null);
+  }
+
+  public void stop(BundleContext context)
+  {
+  }
+}
\ No newline at end of file

Added: aries/branches/experimental-release-by-module/proxy/proxy-distro/src/main/resources/source/proxy-impl/src/main/java/org/apache/aries/proxy/impl/gen/Constants.java
URL: http://svn.apache.org/viewvc/aries/branches/experimental-release-by-module/proxy/proxy-distro/src/main/resources/source/proxy-impl/src/main/java/org/apache/aries/proxy/impl/gen/Constants.java?rev=1075271&view=auto
==============================================================================
--- aries/branches/experimental-release-by-module/proxy/proxy-distro/src/main/resources/source/proxy-impl/src/main/java/org/apache/aries/proxy/impl/gen/Constants.java (added)
+++ aries/branches/experimental-release-by-module/proxy/proxy-distro/src/main/resources/source/proxy-impl/src/main/java/org/apache/aries/proxy/impl/gen/Constants.java Mon Feb 28 10:38:36 2011
@@ -0,0 +1,26 @@
+/*
+ * 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.proxy.impl.gen;
+
+public interface Constants 
+{
+  final static String LOG_ENTRY = "Method entry: {}, args {}";
+  final static String LOG_EXIT = "Method exit: {}, returning {}";
+  final static String LOG_EXCEPTION = "Caught exception";
+}

Added: aries/branches/experimental-release-by-module/proxy/proxy-distro/src/main/resources/source/proxy-impl/src/main/java/org/apache/aries/proxy/impl/gen/ProxyClassBytecodeGenerationException.java
URL: http://svn.apache.org/viewvc/aries/branches/experimental-release-by-module/proxy/proxy-distro/src/main/resources/source/proxy-impl/src/main/java/org/apache/aries/proxy/impl/gen/ProxyClassBytecodeGenerationException.java?rev=1075271&view=auto
==============================================================================
--- aries/branches/experimental-release-by-module/proxy/proxy-distro/src/main/resources/source/proxy-impl/src/main/java/org/apache/aries/proxy/impl/gen/ProxyClassBytecodeGenerationException.java (added)
+++ aries/branches/experimental-release-by-module/proxy/proxy-distro/src/main/resources/source/proxy-impl/src/main/java/org/apache/aries/proxy/impl/gen/ProxyClassBytecodeGenerationException.java Mon Feb 28 10:38:36 2011
@@ -0,0 +1,35 @@
+/*
+ * 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.proxy.impl.gen;
+
+import org.apache.aries.proxy.UnableToProxyException;
+
+public class ProxyClassBytecodeGenerationException extends UnableToProxyException
+{
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -8015178382210046784L;
+
+  public ProxyClassBytecodeGenerationException(String string, Throwable throwable)
+  {
+    super(string, throwable);
+  }
+}

Added: aries/branches/experimental-release-by-module/proxy/proxy-distro/src/main/resources/source/proxy-impl/src/main/java/org/apache/aries/proxy/impl/gen/ProxyClassDefinitionException.java
URL: http://svn.apache.org/viewvc/aries/branches/experimental-release-by-module/proxy/proxy-distro/src/main/resources/source/proxy-impl/src/main/java/org/apache/aries/proxy/impl/gen/ProxyClassDefinitionException.java?rev=1075271&view=auto
==============================================================================
--- aries/branches/experimental-release-by-module/proxy/proxy-distro/src/main/resources/source/proxy-impl/src/main/java/org/apache/aries/proxy/impl/gen/ProxyClassDefinitionException.java (added)
+++ aries/branches/experimental-release-by-module/proxy/proxy-distro/src/main/resources/source/proxy-impl/src/main/java/org/apache/aries/proxy/impl/gen/ProxyClassDefinitionException.java Mon Feb 28 10:38:36 2011
@@ -0,0 +1,34 @@
+/*
+ * 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.proxy.impl.gen;
+
+import org.apache.aries.proxy.UnableToProxyException;
+
+public class ProxyClassDefinitionException extends UnableToProxyException
+{
+  /**
+   * 
+   */
+  private static final long serialVersionUID = 604215734831044743L;
+
+  public ProxyClassDefinitionException(String className, Exception e)
+  {
+    super(className, e);
+  }
+}

Added: aries/branches/experimental-release-by-module/proxy/proxy-distro/src/main/resources/source/proxy-impl/src/main/java/org/apache/aries/proxy/impl/gen/ProxyClassInstantiationException.java
URL: http://svn.apache.org/viewvc/aries/branches/experimental-release-by-module/proxy/proxy-distro/src/main/resources/source/proxy-impl/src/main/java/org/apache/aries/proxy/impl/gen/ProxyClassInstantiationException.java?rev=1075271&view=auto
==============================================================================
--- aries/branches/experimental-release-by-module/proxy/proxy-distro/src/main/resources/source/proxy-impl/src/main/java/org/apache/aries/proxy/impl/gen/ProxyClassInstantiationException.java (added)
+++ aries/branches/experimental-release-by-module/proxy/proxy-distro/src/main/resources/source/proxy-impl/src/main/java/org/apache/aries/proxy/impl/gen/ProxyClassInstantiationException.java Mon Feb 28 10:38:36 2011
@@ -0,0 +1,35 @@
+/*
+ * 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.proxy.impl.gen;
+
+import org.apache.aries.proxy.UnableToProxyException;
+
+public class ProxyClassInstantiationException extends UnableToProxyException
+{
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -2303296601108980837L;
+
+  public ProxyClassInstantiationException(Class<?> clazz, Exception e)
+  {
+    super(clazz, e);
+  }
+
+}

Added: aries/branches/experimental-release-by-module/proxy/proxy-distro/src/main/resources/source/proxy-impl/src/main/java/org/apache/aries/proxy/impl/gen/ProxySubclassAdapter.java
URL: http://svn.apache.org/viewvc/aries/branches/experimental-release-by-module/proxy/proxy-distro/src/main/resources/source/proxy-impl/src/main/java/org/apache/aries/proxy/impl/gen/ProxySubclassAdapter.java?rev=1075271&view=auto
==============================================================================
--- aries/branches/experimental-release-by-module/proxy/proxy-distro/src/main/resources/source/proxy-impl/src/main/java/org/apache/aries/proxy/impl/gen/ProxySubclassAdapter.java (added)
+++ aries/branches/experimental-release-by-module/proxy/proxy-distro/src/main/resources/source/proxy-impl/src/main/java/org/apache/aries/proxy/impl/gen/ProxySubclassAdapter.java Mon Feb 28 10:38:36 2011
@@ -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.proxy.impl.gen;
+
+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(Constants.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(Constants.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(Constants.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(Constants.LOG_EXIT, "visit");
+  }
+
+  public void visitSource(String source, String debug)
+  {
+    LOGGER.debug(Constants.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(Constants.LOG_EXIT, "visitSource");
+  }
+
+  public void visitEnd()
+  {
+    LOGGER.debug(Constants.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(Constants.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(Constants.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(Constants.LOG_EXIT, "visitMethod", methodVisitorToReturn);
+
+    return methodVisitorToReturn;
+
+  }
+
+  private void processMethod(int access, String name, String desc, String signature,
+      String[] exceptions)
+  {
+    LOGGER.debug(Constants.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(Constants.LOG_EXIT, "processMethod");
+  }
+
+  private void addClassStaticField(String classBinaryName)
+  {
+    LOGGER.debug(Constants.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(Constants.LOG_ENTRY, "addClassStaticField");
+  }
+
+  private void setCurrentAnalysisClassFields(Class<?> aClass)
+  {
+    LOGGER.debug(Constants.LOG_ENTRY, "setCurrentAnalysisClassFields",
+        new Object[] { aClass });
+
+    currentlyAnalysedClassName = aClass.getName();
+    currentlyAnalysedClass = aClass;
+
+    LOGGER.debug(Constants.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: aries/branches/experimental-release-by-module/proxy/proxy-distro/src/main/resources/source/proxy-impl/src/main/java/org/apache/aries/proxy/impl/gen/ProxySubclassGenerator.java
URL: http://svn.apache.org/viewvc/aries/branches/experimental-release-by-module/proxy/proxy-distro/src/main/resources/source/proxy-impl/src/main/java/org/apache/aries/proxy/impl/gen/ProxySubclassGenerator.java?rev=1075271&view=auto
==============================================================================
--- aries/branches/experimental-release-by-module/proxy/proxy-distro/src/main/resources/source/proxy-impl/src/main/java/org/apache/aries/proxy/impl/gen/ProxySubclassGenerator.java (added)
+++ aries/branches/experimental-release-by-module/proxy/proxy-distro/src/main/resources/source/proxy-impl/src/main/java/org/apache/aries/proxy/impl/gen/ProxySubclassGenerator.java Mon Feb 28 10:38:36 2011
@@ -0,0 +1,362 @@
+/*
+ * 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.proxy.impl.gen;
+
+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.apache.aries.proxy.FinalModifierException;
+import org.apache.aries.proxy.UnableToProxyException;
+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;
+  
+  private static final ClassLoader defaultClassLoader = new ClassLoader() {};
+
+  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(Constants.LOG_ENTRY, "getProxySubclass", new Object[] { aClass });
+
+    ClassLoader loader = aClass.getClassLoader();
+    // in the special case where the loader is null we use a default classloader
+    // this is for subclassing java.* or javax.* packages, so that one will do
+    if (loader == null) loader = defaultClassLoader;
+
+    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(Constants.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(Constants.LOG_EXIT, "getProxySubclass", classToReturn);
+
+    return classToReturn;
+  }
+
+  public static Object newProxySubclassInstance(Class<?> classToProxy, InvocationHandler ih)
+      throws UnableToProxyException
+  {
+
+    LOGGER.debug(Constants.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(Constants.LOG_EXCEPTION, nsme);
+      throw new ProxyClassInstantiationException(classToProxy, nsme);
+    } catch (InvocationTargetException ite) {
+      LOGGER.debug(Constants.LOG_EXCEPTION, ite);
+      throw new ProxyClassInstantiationException(classToProxy, ite);
+    } catch (InstantiationException ie) {
+      LOGGER.debug(Constants.LOG_EXCEPTION, ie);
+      throw new ProxyClassInstantiationException(classToProxy, ie);
+    } catch (IllegalAccessException iae) {
+      LOGGER.debug(Constants.LOG_EXCEPTION, iae);
+      throw new ProxyClassInstantiationException(classToProxy, iae);
+    }
+
+    LOGGER.debug(Constants.LOG_EXIT, "newProxySubclassInstance", proxySubclassInstance);
+
+    return proxySubclassInstance;
+  }
+
+  private static Class<?> generateAndLoadSubclass(Class<?> aClass, ClassLoader loader)
+      throws UnableToProxyException
+  {
+    LOGGER.debug(Constants.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 = "org.apache.aries.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(Constants.LOG_EXCEPTION, ioe);
+      throw new ProxyClassBytecodeGenerationException(aClass.getName(), ioe);
+    } catch (TypeNotPresentException tnpe) {
+      LOGGER.debug(Constants.LOG_EXCEPTION, tnpe);
+      throw new ProxyClassBytecodeGenerationException(tnpe.typeName(), tnpe.getCause());
+    }
+
+    LOGGER.debug(Constants.LOG_EXIT, "generateAndLoadSubclass", clazz);
+
+    return clazz;
+  }
+
+  private static byte[] processClass(ClassReader cReader, ClassWriter cWriter, ClassVisitor cVisitor)
+  {
+    LOGGER.debug(Constants.LOG_ENTRY, "processClass", new Object[] { cReader, cWriter,
+        cVisitor });
+
+    cReader.accept(cVisitor, ClassReader.SKIP_DEBUG);
+    byte[] byteClassData = cWriter.toByteArray();
+
+    LOGGER.debug(Constants.LOG_EXIT, "processClass", byteClassData);
+
+    return byteClassData;
+  }
+
+  private static String getBinaryName(String name)
+  {
+    LOGGER.debug(Constants.LOG_ENTRY, "getBinaryName", name);
+
+    String binaryName = name.replaceAll("/", "\\.");
+
+    LOGGER.debug(Constants.LOG_EXIT, "getBinaryName", binaryName);
+
+    return binaryName;
+  }
+
+  private static Class<?> loadClassFromBytes(ClassLoader loader, String name, byte[] classData,
+      String classToProxyName) throws UnableToProxyException
+  {
+    LOGGER.debug(Constants.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(Constants.LOG_EXCEPTION, cnfe);
+      throw new ProxyClassDefinitionException(classToProxyName, cnfe);
+    } catch (NoSuchMethodException nsme) {
+      LOGGER.debug(Constants.LOG_EXCEPTION, nsme);
+      throw new ProxyClassDefinitionException(classToProxyName, nsme);
+    } catch (InvocationTargetException ite) {
+      LOGGER.debug(Constants.LOG_EXCEPTION, ite);
+      throw new ProxyClassDefinitionException(classToProxyName, ite);
+    } catch (IllegalAccessException iae) {
+      LOGGER.debug(Constants.LOG_EXCEPTION, iae);
+      throw new ProxyClassDefinitionException(classToProxyName, iae);
+    }
+
+    LOGGER.debug(Constants.LOG_EXIT, "loadClassFromBytes", clazz);
+
+    return clazz;
+  }
+
+  public static boolean isProxySubclass(Class<?> aClass)
+  {
+    LOGGER.debug(Constants.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(Constants.LOG_EXIT, "isProxySubclass", isProxySubclass);
+
+    return isProxySubclass;
+  }
+
+  private static void scanForFinalModifiers(Class<?> clazz) throws FinalModifierException
+  {
+    LOGGER.debug(Constants.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(Constants.LOG_EXIT, "scanForFinalModifiers");
+
+  }
+
+  public static InvocationHandler getInvocationHandler(Object o)
+  {
+    LOGGER.debug(Constants.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(Constants.LOG_EXCEPTION, e);
+      } catch (SecurityException e) {
+        LOGGER.debug(Constants.LOG_EXCEPTION, e);
+      } catch (IllegalAccessException e) {
+        LOGGER.debug(Constants.LOG_EXCEPTION, e);
+      } catch (InvocationTargetException e) {
+        LOGGER.debug(Constants.LOG_EXCEPTION, e);
+      } catch (NoSuchMethodException e) {
+        LOGGER.debug(Constants.LOG_EXCEPTION, e);
+      }
+    }
+    LOGGER.debug(Constants.LOG_EXIT, "getInvoationHandler", ih);
+    return ih;
+  }
+
+}

Added: aries/branches/experimental-release-by-module/proxy/proxy-distro/src/main/resources/source/proxy-impl/src/main/java/org/apache/aries/proxy/impl/gen/ProxySubclassHierarchyAdapter.java
URL: http://svn.apache.org/viewvc/aries/branches/experimental-release-by-module/proxy/proxy-distro/src/main/resources/source/proxy-impl/src/main/java/org/apache/aries/proxy/impl/gen/ProxySubclassHierarchyAdapter.java?rev=1075271&view=auto
==============================================================================
--- aries/branches/experimental-release-by-module/proxy/proxy-distro/src/main/resources/source/proxy-impl/src/main/java/org/apache/aries/proxy/impl/gen/ProxySubclassHierarchyAdapter.java (added)
+++ aries/branches/experimental-release-by-module/proxy/proxy-distro/src/main/resources/source/proxy-impl/src/main/java/org/apache/aries/proxy/impl/gen/ProxySubclassHierarchyAdapter.java Mon Feb 28 10:38:36 2011
@@ -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.proxy.impl.gen;
+
+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(Constants.LOG_ENTRY, "ProxySubclassHeirarchyAdapter", new Object[] {
+        this, adapter, methodsToImplement });
+
+    this.methodsToImplement = methodsToImplement;
+    this.adapter = adapter;
+
+    LOGGER.debug(Constants.LOG_EXIT, "ProxySubclassHeirarchyAdapter", this);
+  }
+
+  public MethodVisitor visitMethod(int access, String name, String desc, String signature,
+      String[] exceptions)
+  {
+    LOGGER.debug(Constants.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(Constants.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: aries/branches/experimental-release-by-module/proxy/proxy-distro/src/main/resources/source/proxy-impl/src/main/java/org/apache/aries/proxy/impl/gen/ProxySubclassMethodHashSet.java
URL: http://svn.apache.org/viewvc/aries/branches/experimental-release-by-module/proxy/proxy-distro/src/main/resources/source/proxy-impl/src/main/java/org/apache/aries/proxy/impl/gen/ProxySubclassMethodHashSet.java?rev=1075271&view=auto
==============================================================================
--- aries/branches/experimental-release-by-module/proxy/proxy-distro/src/main/resources/source/proxy-impl/src/main/java/org/apache/aries/proxy/impl/gen/ProxySubclassMethodHashSet.java (added)
+++ aries/branches/experimental-release-by-module/proxy/proxy-distro/src/main/resources/source/proxy-impl/src/main/java/org/apache/aries/proxy/impl/gen/ProxySubclassMethodHashSet.java Mon Feb 28 10:38:36 2011
@@ -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.proxy.impl.gen;
+
+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(Constants.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(Constants.LOG_EXIT, "addMethodArray");
+  }
+
+  static String typeArrayToStringArgDescriptor(Type[] argTypes)
+  {
+    StringBuilder descriptor = new StringBuilder();
+    for (Type t : argTypes) {
+      descriptor.append(t.toString());
+    }
+    return descriptor.toString();
+  }
+
+}

Added: aries/branches/experimental-release-by-module/proxy/proxy-distro/src/main/resources/source/proxy-impl/src/main/java/org/apache/aries/proxy/impl/gen/UnableToLoadProxyException.java
URL: http://svn.apache.org/viewvc/aries/branches/experimental-release-by-module/proxy/proxy-distro/src/main/resources/source/proxy-impl/src/main/java/org/apache/aries/proxy/impl/gen/UnableToLoadProxyException.java?rev=1075271&view=auto
==============================================================================
--- aries/branches/experimental-release-by-module/proxy/proxy-distro/src/main/resources/source/proxy-impl/src/main/java/org/apache/aries/proxy/impl/gen/UnableToLoadProxyException.java (added)
+++ aries/branches/experimental-release-by-module/proxy/proxy-distro/src/main/resources/source/proxy-impl/src/main/java/org/apache/aries/proxy/impl/gen/UnableToLoadProxyException.java Mon Feb 28 10:38:36 2011
@@ -0,0 +1,35 @@
+/*
+ * 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.proxy.impl.gen;
+
+import org.apache.aries.proxy.UnableToProxyException;
+
+public class UnableToLoadProxyException extends UnableToProxyException
+{
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = 506487573157016476L;
+
+  public UnableToLoadProxyException(String className, Exception e)
+  {
+    super(className, e);
+  }
+}

Added: aries/branches/experimental-release-by-module/proxy/proxy-distro/src/main/resources/source/proxy-impl/src/main/resources/org/apache/aries/proxy/nls/ProxyImplMessages.properties
URL: http://svn.apache.org/viewvc/aries/branches/experimental-release-by-module/proxy/proxy-distro/src/main/resources/source/proxy-impl/src/main/resources/org/apache/aries/proxy/nls/ProxyImplMessages.properties?rev=1075271&view=auto
==============================================================================
--- aries/branches/experimental-release-by-module/proxy/proxy-distro/src/main/resources/source/proxy-impl/src/main/resources/org/apache/aries/proxy/nls/ProxyImplMessages.properties (added)
+++ aries/branches/experimental-release-by-module/proxy/proxy-distro/src/main/resources/source/proxy-impl/src/main/resources/org/apache/aries/proxy/nls/ProxyImplMessages.properties Mon Feb 28 10:38:36 2011
@@ -0,0 +1,25 @@
+#
+# 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.
+#
+
+# {0} bundle symbolic name
+# {1} bundle version
+# {2} bundle id
+bundle.uninstalled=The bundle {0} at version {1} with id {2} has been uninstalled.
+# {0} name of class
+class.is.class=The class {0} is not an interface and therefore a proxy cannot be generated.

Added: aries/branches/experimental-release-by-module/proxy/proxy-distro/src/main/resources/source/proxy-impl/src/test/java/org/apache/aries/blueprint/proxy/ProxySubclassGeneratorTest.java
URL: http://svn.apache.org/viewvc/aries/branches/experimental-release-by-module/proxy/proxy-distro/src/main/resources/source/proxy-impl/src/test/java/org/apache/aries/blueprint/proxy/ProxySubclassGeneratorTest.java?rev=1075271&view=auto
==============================================================================
--- aries/branches/experimental-release-by-module/proxy/proxy-distro/src/main/resources/source/proxy-impl/src/test/java/org/apache/aries/blueprint/proxy/ProxySubclassGeneratorTest.java (added)
+++ aries/branches/experimental-release-by-module/proxy/proxy-distro/src/main/resources/source/proxy-impl/src/test/java/org/apache/aries/blueprint/proxy/ProxySubclassGeneratorTest.java Mon Feb 28 10:38:36 2011
@@ -0,0 +1,388 @@
+/*
+ * 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 org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.aries.proxy.FinalModifierException;
+import org.apache.aries.proxy.impl.gen.ProxySubclassGenerator;
+import org.apache.aries.proxy.impl.gen.ProxySubclassMethodHashSet;
+import org.junit.Before;
+import org.junit.Test;
+
+public class ProxySubclassGeneratorTest
+{
+  private static final Class<?> TEST_CLASS = ProxyTestClassGeneral.class;
+  private static final Class<?> FINAL_METHOD_CLASS = ProxyTestClassFinalMethod.class;
+  private static final Class<?> FINAL_CLASS = ProxyTestClassFinal.class;
+  private static final Class<?> GENERIC_CLASS = ProxyTestClassGeneric.class;
+  private static final Class<?> COVARIANT_CLASS = ProxyTestClassCovariantOverride.class;
+  private static ProxySubclassMethodHashSet<String> expectedMethods = new ProxySubclassMethodHashSet<String>(
+      12);
+  private InvocationHandler ih = null;
+  private Class<?> generatedProxySubclass = null;
+  private Object o = null;
+
+  /**
+   * @throws java.lang.Exception
+   */
+  @Before
+  public void setUp() throws Exception
+  {
+    ih = new FakeInvocationHandler();
+    ((FakeInvocationHandler)ih).setDelegate(TEST_CLASS.newInstance());
+    generatedProxySubclass = getGeneratedSubclass();
+    o = getSubclassInstance(generatedProxySubclass);
+  }
+
+  /**
+   * This test uses the ProxySubclassGenerator to generate and load a subclass
+   * of the specified TEST_CLASS.
+   * 
+   * Once the subclass is generated we check that it wasn't null. We check
+   * that the InvocationHandler constructor doesn't return a null object
+   * either
+   * 
+   * Test method for
+   * {@link org.apache.aries.proxy.impl.ProxySubclassGenerator#generateAndLoadSubclass()}
+   * .
+   */
+  @Test
+  public void testGenerateAndLoadSubclass() throws Exception
+  {
+    assertNotNull("Generated proxy subclass was null", generatedProxySubclass);
+    assertNotNull("Generated proxy subclass instance was null", o);
+  }
+
+  /**
+   * Test that the methods found declared on the generated proxy subclass are
+   * the ones that we expect.
+   */
+  @Test
+  public void testExpectedMethods() throws Exception
+  {
+    Class<?> superclass = TEST_CLASS;
+
+    do {
+      Method[] declaredMethods = superclass.getDeclaredMethods();
+      List<Method> listOfDeclaredMethods = new ArrayList<Method>();
+      for (Method m : declaredMethods) {
+        int i = m.getModifiers();
+        if (Modifier.isPrivate(i) || Modifier.isFinal(i)) {
+          // private or final don't get added
+        } else if (!(Modifier.isPublic(i) || Modifier.isPrivate(i) || Modifier.isProtected(i))) {
+          // the method is default visibility, check the package
+          if (m.getDeclaringClass().getPackage().equals(TEST_CLASS.getPackage())) {
+            // default vis with same package gets added
+            listOfDeclaredMethods.add(m);
+          }
+        } else {
+          listOfDeclaredMethods.add(m);
+        }
+      }
+
+      declaredMethods = listOfDeclaredMethods.toArray(new Method[] {});
+      ProxySubclassMethodHashSet<String> foundMethods = new ProxySubclassMethodHashSet<String>(
+          declaredMethods.length);
+      foundMethods.addMethodArray(declaredMethods);
+      // as we are using a set we shouldn't get duplicates
+      expectedMethods.addAll(foundMethods);
+      superclass = superclass.getSuperclass();
+    } while (superclass != null);
+
+    // add the getter and setter for the invocation handler to the expected
+    // set
+    // and the unwrapObject method
+    Method[] ihMethods = new Method[] {
+        generatedProxySubclass.getMethod("setInvocationHandler",
+            new Class[] { InvocationHandler.class }),
+        generatedProxySubclass.getMethod("getInvocationHandler", new Class[] {}) };
+    expectedMethods.addMethodArray(ihMethods);
+
+    Method[] generatedProxySubclassMethods = generatedProxySubclass.getDeclaredMethods();
+    ProxySubclassMethodHashSet<String> generatedMethods = new ProxySubclassMethodHashSet<String>(
+        generatedProxySubclassMethods.length);
+    generatedMethods.addMethodArray(generatedProxySubclassMethods);
+
+    // check that all the methods we have generated were expected
+    for (String gen : generatedMethods) {
+      assertTrue("Unexpected method: " + gen, expectedMethods.contains(gen));
+    }
+    // check that all the expected methods were generated
+    for (String exp : expectedMethods) {
+      assertTrue("Method was not generated: " + exp, generatedMethods.contains(exp));
+    }
+    // check the sets were the same
+    assertEquals("Sets were not the same", expectedMethods, generatedMethods);
+
+  }
+
+  /**
+   * Test a basic method invocation on the proxy subclass
+   */
+  @Test
+  public void testMethodInvocation() throws Exception
+  {
+    Method m = generatedProxySubclass.getDeclaredMethod("testMethod", new Class[] { String.class,
+        int.class, Object.class });
+    String x = "x";
+    String returned = (String) m.invoke(o, x, 1, new Object());
+    assertEquals("Object returned from invocation was not correct.", x, returned);
+  }
+
+  /**
+   * Test different argument types on a method invocation
+   */
+  @Test
+  public void testMethodArgs() throws Exception
+  {
+    Method m = generatedProxySubclass.getDeclaredMethod("testArgs", new Class[] { double.class,
+        short.class, long.class, char.class, byte.class, boolean.class });
+    Character xc = Character.valueOf('x');
+    String x = xc.toString();
+    String returned = (String) m.invoke(o, Double.MAX_VALUE, Short.MIN_VALUE, Long.MAX_VALUE, xc
+        .charValue(), Byte.MIN_VALUE, false);
+    assertEquals("Object returned from invocation was not correct.", x, returned);
+  }
+
+  /**
+   * Test a method that returns void
+   */
+  @Test
+  public void testReturnVoid() throws Exception
+  {
+    Method m = generatedProxySubclass.getDeclaredMethod("testReturnVoid", new Class[] {});
+    m.invoke(o);
+  }
+
+  /**
+   * Test a method that returns an int
+   */
+  @Test
+  public void testReturnInt() throws Exception
+  {
+    Method m = generatedProxySubclass.getDeclaredMethod("testReturnInt", new Class[] {});
+    Integer returned = (Integer) m.invoke(o);
+    assertEquals("Expected object was not returned from invocation", Integer.valueOf(17), returned);
+  }
+
+  /**
+   * Test a method that returns an Integer
+   */
+  @Test
+  public void testReturnInteger() throws Exception
+  {
+    Method m = generatedProxySubclass.getDeclaredMethod("testReturnInteger", new Class[] {});
+    Integer returned = (Integer) m.invoke(o);
+    assertEquals("Expected object was not returned from invocation", Integer.valueOf(1), returned);
+  }
+
+  /**
+   * Test a public method declared higher up the superclass hierarchy
+   */
+  @Test
+  public void testPublicHierarchyMethod() throws Exception
+  {
+    Method m = generatedProxySubclass.getDeclaredMethod("bMethod", new Class[] {});
+    m.invoke(o);
+  }
+
+  /**
+   * Test a protected method declared higher up the superclass hierarchy
+   */
+  @Test
+  public void testProtectedHierarchyMethod() throws Exception
+  {
+    Method m = generatedProxySubclass.getDeclaredMethod("bProMethod", new Class[] {});
+    m.invoke(o);
+  }
+
+  /**
+   * Test a default method declared higher up the superclass hierarchy
+   */
+  @Test
+  public void testDefaultHierarchyMethod() throws Exception
+  {
+    Method m = generatedProxySubclass.getDeclaredMethod("bDefMethod", new Class[] {});
+    m.invoke(o);
+  }
+
+  /**
+   * Test a covariant override method
+   */
+  @Test
+  public void testCovariant() throws Exception
+  {
+    ((FakeInvocationHandler)ih).setDelegate(COVARIANT_CLASS.newInstance());
+    o = ProxySubclassGenerator.newProxySubclassInstance(COVARIANT_CLASS, ih);
+    generatedProxySubclass = o.getClass();
+    Method m = generatedProxySubclass.getDeclaredMethod("getCovariant", new Class[] {});
+    Object returned = m.invoke(o);
+    assertTrue("Object was of wrong type: " + returned.getClass().getSimpleName(), COVARIANT_CLASS
+        .isInstance(returned));
+  }
+
+  /**
+   * Test a method with generics
+   */
+  @Test
+  public void testGenerics() throws Exception
+  {
+    ((FakeInvocationHandler)ih).setDelegate(GENERIC_CLASS.newInstance());
+    o = ProxySubclassGenerator.newProxySubclassInstance(GENERIC_CLASS, ih);
+    generatedProxySubclass = o.getClass();
+    Method m = generatedProxySubclass.getDeclaredMethod("setSomething",
+        new Class[] { String.class });
+    m.invoke(o, "aString");
+    m = generatedProxySubclass.getDeclaredMethod("getSomething", new Class[] {});
+    Object returned = m.invoke(o);
+    assertTrue("Object was of wrong type", String.class.isInstance(returned));
+    assertEquals("String had wrong value", "aString", returned);
+  }
+
+  /**
+   * Test a method marked final
+   */
+  @Test
+  public void testFinalMethod() throws Exception
+  {
+    try {
+      ProxySubclassGenerator.getProxySubclass(FINAL_METHOD_CLASS);
+    } catch (FinalModifierException e) {
+      assertFalse("Should have found final method not final class", e.isFinalClass());
+    }
+  }
+
+  /**
+   * Test a class marked final
+   */
+  @Test
+  public void testFinalClass() throws Exception
+  {
+    try {
+      ProxySubclassGenerator.getProxySubclass(FINAL_CLASS);
+    } catch (FinalModifierException e) {
+      assertTrue("Should have found final class", e.isFinalClass());
+    }
+  }
+
+  /**
+   * Test that we don't generate classes twice
+   */
+  @Test
+  public void testRetrieveClass() throws Exception
+  {
+    Class<?> retrieved = ProxySubclassGenerator.getProxySubclass(TEST_CLASS);
+    assertNotNull("The new class was null", retrieved);
+    assertEquals("The same class was not returned", generatedProxySubclass, retrieved);
+
+  }
+
+  /**
+   * Test a private constructor
+   */
+  @Test
+  public void testPrivateConstructor() throws Exception
+  {
+    Object o = ProxySubclassGenerator.newProxySubclassInstance(
+        ProxyTestClassPrivateConstructor.class, ih);
+    assertNotNull("The new instance was null", o);
+
+  }
+  
+//  /**
+//   * Test object equality between real and proxy using a Collaborator
+//   */
+//  @Test
+//  public void testObjectEquality() throws Exception
+//  {
+//    Object delegate = TEST_CLASS.newInstance();
+//    InvocationHandler collaborator = new Collaborator(null, null, AsmInterceptorWrapper.passThrough(delegate));
+//    Object o = ProxySubclassGenerator.newProxySubclassInstance(TEST_CLASS, collaborator);
+//    //Calling equals on the proxy with an arg of the unwrapped object should be true
+//    assertTrue("The proxy object should be equal to its delegate",o.equals(delegate));
+//    InvocationHandler collaborator2 = new Collaborator(null, null, AsmInterceptorWrapper.passThrough(delegate));
+//    Object o2 = ProxySubclassGenerator.newProxySubclassInstance(TEST_CLASS, collaborator2);
+//    //The proxy of a delegate should equal another proxy of the same delegate
+//    assertTrue("The proxy object should be equal to another proxy instance of the same delegate", o2.equals(o));
+//  }
+//  
+//  private static class ProxyTestOverridesFinalize {
+//      public boolean finalizeCalled = false;
+//      
+//      @Override
+//      protected void finalize() {
+//          finalizeCalled = true;
+//      }
+//  }
+//  
+//  @Test
+//  public void testFinalizeNotCalled() throws Exception {
+//      ProxyTestOverridesFinalize testObj = new ProxyTestOverridesFinalize();
+//      InvocationHandler ih = new Collaborator(null, null, AsmInterceptorWrapper.passThrough(testObj));
+//      Object o = ProxySubclassGenerator.newProxySubclassInstance(ProxyTestOverridesFinalize.class, ih);
+//      
+//      Method m = o.getClass().getDeclaredMethod("finalize");
+//      m.setAccessible(true);
+//      m.invoke(o);
+//      
+//      assertFalse(testObj.finalizeCalled);
+//  }
+  
+
+  private Class<?> getGeneratedSubclass() throws Exception
+  {
+    return ProxySubclassGenerator.getProxySubclass(TEST_CLASS);
+  }
+
+  private Object getSubclassInstance(Class<?> clazz) throws Exception
+  {
+    return clazz.getConstructor(InvocationHandler.class).newInstance(ih);
+  }
+
+  private class FakeInvocationHandler implements InvocationHandler
+  {
+    private Object delegate = null;
+    /*
+     * (non-Javadoc)
+     * 
+     * @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object,
+     * java.lang.reflect.Method, java.lang.Object[])
+     */
+    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
+    {
+      Object result = method.invoke(delegate, args);
+      return result;
+    }
+
+    void setDelegate(Object delegate){
+      this.delegate = delegate;
+    }
+    
+  }
+}

Added: aries/branches/experimental-release-by-module/proxy/proxy-distro/src/main/resources/source/proxy-impl/src/test/java/org/apache/aries/blueprint/proxy/ProxyTestClassCovariant.java
URL: http://svn.apache.org/viewvc/aries/branches/experimental-release-by-module/proxy/proxy-distro/src/main/resources/source/proxy-impl/src/test/java/org/apache/aries/blueprint/proxy/ProxyTestClassCovariant.java?rev=1075271&view=auto
==============================================================================
--- aries/branches/experimental-release-by-module/proxy/proxy-distro/src/main/resources/source/proxy-impl/src/test/java/org/apache/aries/blueprint/proxy/ProxyTestClassCovariant.java (added)
+++ aries/branches/experimental-release-by-module/proxy/proxy-distro/src/main/resources/source/proxy-impl/src/test/java/org/apache/aries/blueprint/proxy/ProxyTestClassCovariant.java Mon Feb 28 10:38:36 2011
@@ -0,0 +1,30 @@
+/*
+ * 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 ProxyTestClassCovariant
+{
+
+  //this method is here to make sure we don't break on covariant override
+  public ProxyTestClassCovariant getCovariant()
+  {
+    return this;
+  }
+
+}

Added: aries/branches/experimental-release-by-module/proxy/proxy-distro/src/main/resources/source/proxy-impl/src/test/java/org/apache/aries/blueprint/proxy/ProxyTestClassCovariantOverride.java
URL: http://svn.apache.org/viewvc/aries/branches/experimental-release-by-module/proxy/proxy-distro/src/main/resources/source/proxy-impl/src/test/java/org/apache/aries/blueprint/proxy/ProxyTestClassCovariantOverride.java?rev=1075271&view=auto
==============================================================================
--- aries/branches/experimental-release-by-module/proxy/proxy-distro/src/main/resources/source/proxy-impl/src/test/java/org/apache/aries/blueprint/proxy/ProxyTestClassCovariantOverride.java (added)
+++ aries/branches/experimental-release-by-module/proxy/proxy-distro/src/main/resources/source/proxy-impl/src/test/java/org/apache/aries/blueprint/proxy/ProxyTestClassCovariantOverride.java Mon Feb 28 10:38:36 2011
@@ -0,0 +1,29 @@
+/*
+ * 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 ProxyTestClassCovariantOverride extends ProxyTestClassCovariant
+{
+  //this method is here to make sure we don't break on covariant override
+  public ProxyTestClassCovariantOverride getCovariant()
+  {
+    return this;
+  }
+
+}