Author: gnodet
Date: Thu Jun 19 07:01:32 2014
New Revision: 1603727

URL: http://svn.apache.org/r1603727
Log:
[ARIES-1216] Error when creating proxies with an invisible super class in the 
hierarchy

Added:
    aries/trunk/proxy/proxy-impl/src/test/java/org/apache/aries/proxy/test/
    aries/trunk/proxy/proxy-impl/src/test/java/org/apache/aries/proxy/test/a/
    
aries/trunk/proxy/proxy-impl/src/test/java/org/apache/aries/proxy/test/a/ProxyTestClassA.java
    aries/trunk/proxy/proxy-impl/src/test/java/org/apache/aries/proxy/test/b/
    
aries/trunk/proxy/proxy-impl/src/test/java/org/apache/aries/proxy/test/b/ProxyTestClassB.java
    aries/trunk/proxy/proxy-impl/src/test/java/org/apache/aries/proxy/test/c/
    
aries/trunk/proxy/proxy-impl/src/test/java/org/apache/aries/proxy/test/c/ProxyTestClassC.java
Modified:
    
aries/trunk/proxy/proxy-impl/src/main/java/org/apache/aries/proxy/impl/AsmProxyManager.java
    
aries/trunk/proxy/proxy-impl/src/main/java/org/apache/aries/proxy/impl/gen/ProxySubclassAdapter.java
    
aries/trunk/proxy/proxy-impl/src/main/java/org/apache/aries/proxy/impl/gen/ProxySubclassGenerator.java
    
aries/trunk/proxy/proxy-impl/src/test/java/org/apache/aries/blueprint/proxy/ProxySubclassGeneratorTest.java

Modified: 
aries/trunk/proxy/proxy-impl/src/main/java/org/apache/aries/proxy/impl/AsmProxyManager.java
URL: 
http://svn.apache.org/viewvc/aries/trunk/proxy/proxy-impl/src/main/java/org/apache/aries/proxy/impl/AsmProxyManager.java?rev=1603727&r1=1603726&r2=1603727&view=diff
==============================================================================
--- 
aries/trunk/proxy/proxy-impl/src/main/java/org/apache/aries/proxy/impl/AsmProxyManager.java
 (original)
+++ 
aries/trunk/proxy/proxy-impl/src/main/java/org/apache/aries/proxy/impl/AsmProxyManager.java
 Thu Jun 19 07:01:32 2014
@@ -18,13 +18,19 @@
  */
 package org.apache.aries.proxy.impl;
 
+import java.io.IOException;
 import java.lang.reflect.Constructor;
 import java.lang.reflect.InvocationHandler;
 import java.lang.reflect.Modifier;
 import java.lang.reflect.Proxy;
+import java.net.URL;
+import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Enumeration;
 import java.util.HashSet;
 import java.util.Iterator;
+import java.util.List;
+import java.util.NoSuchElementException;
 import java.util.Set;
 import java.util.concurrent.Callable;
 
@@ -100,13 +106,100 @@ public final class AsmProxyManager exten
         }
       } 
       if(proxyObject == null){
-        proxyObject = 
ProxySubclassGenerator.newProxySubclassInstance(classToProxy, new 
ProxyHandler(this, dispatcher, listener));
+        // ARIES-1216 : in some cases, some class can not be visible from the 
root class classloader.
+        // If that's the case, we need to build a custom classloader that has 
visibility on all those classes
+        // If we could generate a proper constructor this would not be 
necessary, but since we have to rely
+        // on the generated serialization constructor to bypass the JVM 
verifier, we don't have much choice
+        ClassLoader classLoader = classToProxy.getClassLoader();
+        boolean allVisible = true;
+        for (Class<?> clazz : classes) {
+          try {
+            if (classLoader.loadClass(clazz.getName()) != clazz) {
+              throw new UnableToProxyException(classToProxy, "The requested 
class " + clazz + " is different from the one seen by by " + classToProxy);
+            }
+          } catch (ClassNotFoundException e) {
+            allVisible = false;
+            break;
+          }
+        }
+        if (!allVisible) {
+          List<ClassLoader> classLoaders = new ArrayList<ClassLoader>();
+          for (Class<?> clazz : classes) {
+            ClassLoader cl = clazz.getClassLoader();
+            if (cl != null && !classLoaders.contains(cl)) {
+              classLoaders.add(cl);
+            }
+          }
+          classLoader = new MultiClassLoader(classLoaders);
+        }
+        proxyObject = 
ProxySubclassGenerator.newProxySubclassInstance(classToProxy, classLoader, new 
ProxyHandler(this, dispatcher, listener));
       }
     }
 
     return proxyObject;
   }
 
+  private static class MultiClassLoader extends ClassLoader {
+    private final List<ClassLoader> parents;
+    private MultiClassLoader(List<ClassLoader> parents) {
+      this.parents = parents;
+    }
+
+    @Override
+    public Class<?> loadClass(String name, boolean resolve) throws 
ClassNotFoundException {
+      for (ClassLoader cl : parents) {
+         try {
+           return cl.loadClass(name);
+         } catch (ClassNotFoundException e) {
+           // Ignore
+         }
+        }
+        throw new ClassNotFoundException(name);
+    }
+
+    @Override
+    public URL getResource(String name) {
+      for (ClassLoader cl : parents) {
+         URL url = cl.getResource(name);
+         if (url != null) {
+           return url;
+         }
+       }
+       return null;
+    }
+
+    @Override
+    public Enumeration<URL> getResources(String name) throws IOException {
+      final List<Enumeration<URL>> tmp = new ArrayList<Enumeration<URL>>();
+      for (ClassLoader cl : parents) {
+        tmp.add(cl.getResources(name));
+      }
+      return new Enumeration<URL>() {
+        int index = 0;
+        @Override
+        public boolean hasMoreElements() {
+          return next();
+        }
+        @Override
+        public URL nextElement() {
+          if (!next()) {
+            throw new NoSuchElementException();
+          }
+          return tmp.get(index).nextElement();
+        }
+        private boolean next() {
+          while (index < tmp.size()) {
+            if (tmp.get(index) != null && tmp.get(index).hasMoreElements()) {
+              return true;
+            }
+            index++;
+          }
+          return false;
+        }
+      };
+    }
+  }
+
   private Class<?> getLowestSubclass(Set<Class<?>> notInterfaces) throws
        UnableToProxyException {
     

Modified: 
aries/trunk/proxy/proxy-impl/src/main/java/org/apache/aries/proxy/impl/gen/ProxySubclassAdapter.java
URL: 
http://svn.apache.org/viewvc/aries/trunk/proxy/proxy-impl/src/main/java/org/apache/aries/proxy/impl/gen/ProxySubclassAdapter.java?rev=1603727&r1=1603726&r2=1603727&view=diff
==============================================================================
--- 
aries/trunk/proxy/proxy-impl/src/main/java/org/apache/aries/proxy/impl/gen/ProxySubclassAdapter.java
 (original)
+++ 
aries/trunk/proxy/proxy-impl/src/main/java/org/apache/aries/proxy/impl/gen/ProxySubclassAdapter.java
 Thu Jun 19 07:01:32 2014
@@ -271,6 +271,10 @@ public class ProxySubclassAdapter extend
         // read the current class and use a
         // ProxySubclassHierarchyAdapter
         // to process only methods on that class that are in the list
+        ClassLoader loader = currentlyAnalysedClass.getClassLoader();
+        if (loader == null) {
+          loader = this.loader;
+        }
         ClassReader cr = new 
ClassReader(loader.getResourceAsStream(currentlyAnalysedClass
             .getName().replaceAll("\\.", "/")
             + ".class"));

Modified: 
aries/trunk/proxy/proxy-impl/src/main/java/org/apache/aries/proxy/impl/gen/ProxySubclassGenerator.java
URL: 
http://svn.apache.org/viewvc/aries/trunk/proxy/proxy-impl/src/main/java/org/apache/aries/proxy/impl/gen/ProxySubclassGenerator.java?rev=1603727&r1=1603726&r2=1603727&view=diff
==============================================================================
--- 
aries/trunk/proxy/proxy-impl/src/main/java/org/apache/aries/proxy/impl/gen/ProxySubclassGenerator.java
 (original)
+++ 
aries/trunk/proxy/proxy-impl/src/main/java/org/apache/aries/proxy/impl/gen/ProxySubclassGenerator.java
 Thu Jun 19 07:01:32 2014
@@ -72,9 +72,13 @@ public class ProxySubclassGenerator
 
   public static Class<?> getProxySubclass(Class<?> aClass) throws 
UnableToProxyException
   {
+    return getProxySubclass(aClass, aClass.getClassLoader());
+  }
+
+  public static Class<?> getProxySubclass(Class<?> aClass, ClassLoader loader) 
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;
@@ -152,13 +156,19 @@ public class ProxySubclassGenerator
   public static Object newProxySubclassInstance(Class<?> classToProxy, 
InvocationHandler ih)
       throws UnableToProxyException
   {
+    return newProxySubclassInstance(classToProxy, 
classToProxy.getClassLoader(), ih);
+  }
+
+  public static Object newProxySubclassInstance(Class<?> classToProxy, 
ClassLoader loader, InvocationHandler ih)
+      throws UnableToProxyException
+  {
 
     LOGGER.debug(Constants.LOG_ENTRY, "newProxySubclassInstance", new Object[] 
{
-        classToProxy, ih });
+        classToProxy, loader, ih });
 
     Object proxySubclassInstance = null;
     try {
-      Class<?> generatedProxySubclass = getProxySubclass(classToProxy);
+      Class<?> generatedProxySubclass = getProxySubclass(classToProxy, loader);
       LOGGER.debug("Getting the proxy subclass constructor");
       // Because the newer JVMs throw a VerifyError if a class attempts to in 
a constructor other than their superclasses constructor,
       // and because we can't know what objects/values we need to pass into 
the class being proxied constructor, 

Modified: 
aries/trunk/proxy/proxy-impl/src/test/java/org/apache/aries/blueprint/proxy/ProxySubclassGeneratorTest.java
URL: 
http://svn.apache.org/viewvc/aries/trunk/proxy/proxy-impl/src/test/java/org/apache/aries/blueprint/proxy/ProxySubclassGeneratorTest.java?rev=1603727&r1=1603726&r2=1603727&view=diff
==============================================================================
--- 
aries/trunk/proxy/proxy-impl/src/test/java/org/apache/aries/blueprint/proxy/ProxySubclassGeneratorTest.java
 (original)
+++ 
aries/trunk/proxy/proxy-impl/src/test/java/org/apache/aries/blueprint/proxy/ProxySubclassGeneratorTest.java
 Thu Jun 19 07:01:32 2014
@@ -23,12 +23,18 @@ import static org.junit.Assert.assertFal
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
 import java.lang.reflect.InvocationHandler;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
 import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
 import java.util.List;
+import java.util.Set;
 import java.util.concurrent.Callable;
 
 import org.apache.aries.proxy.FinalModifierException;
@@ -40,6 +46,7 @@ import org.apache.aries.proxy.impl.Proxy
 import org.apache.aries.proxy.impl.SingleInstanceDispatcher;
 import org.apache.aries.proxy.impl.gen.ProxySubclassGenerator;
 import org.apache.aries.proxy.impl.gen.ProxySubclassMethodHashSet;
+import org.apache.aries.util.io.IOUtils;
 import org.junit.Before;
 import org.junit.Test;
 
@@ -236,6 +243,86 @@ public class ProxySubclassGeneratorTest 
     ((FakeInvocationHandler)ih).setDelegate(GENERIC_CLASS.newInstance());
     super.testGenerics();
   }
+
+  @Test
+  public void testClassLoaders() throws Exception {
+    ClassLoader clA = new LimitedClassLoader("org.apache.aries.proxy.test.a", 
null, null);
+    ClassLoader clB = new LimitedClassLoader("org.apache.aries.proxy.test.b", 
"org.apache.aries.proxy.test.a", clA);
+    ClassLoader clC = new LimitedClassLoader("org.apache.aries.proxy.test.c", 
"org.apache.aries.proxy.test.b", clB);
+
+    Class<?> clazzA = 
clA.loadClass("org.apache.aries.proxy.test.a.ProxyTestClassA");
+    Class<?> clazzB = 
clB.loadClass("org.apache.aries.proxy.test.b.ProxyTestClassB");
+    Class<?> clazzC = 
clC.loadClass("org.apache.aries.proxy.test.c.ProxyTestClassC");
+
+    final Object object = 
clazzC.getConstructor(String.class).newInstance("hello");
+
+    o = new AsmProxyManager().createNewProxy(null, Arrays.asList(clazzA, 
clazzB, clazzC), constantly(object), null);
+    generatedProxySubclass = o.getClass();
+    Method m = generatedProxySubclass.getDeclaredMethod("hello", new Class[] 
{});
+    Object returned = m.invoke(o);
+    assertEquals("hello", returned);
+  }
+
+  private static class LimitedClassLoader extends ClassLoader {
+    Set<String> providedPackages;
+    Set<String> importedPackages;
+    List<ClassLoader> parents;
+
+    public LimitedClassLoader(String provided, String imported, ClassLoader 
parent) {
+      providedPackages = Collections.singleton(provided);
+      importedPackages = imported != null ? Collections.singleton(imported) : 
Collections.<String>emptySet();
+      parents = parent != null ? Collections.singletonList(parent) : 
Collections.<ClassLoader>emptyList();
+    }
+
+    @Override
+    protected Class<?> loadClass(String name, boolean resolve) throws 
ClassNotFoundException {
+      synchronized (getClassLoadingLock(name)) {
+        // First, check if the class has already been loaded
+        Class<?> c = findLoadedClass(name);
+        if (c == null) {
+          String pkg = name.substring(0, name.lastIndexOf('.'));
+          if (pkg.startsWith("java.") || pkg.startsWith("sun.reflect")) {
+            return getClass().getClassLoader().loadClass(name);
+          } else if (providedPackages.contains(pkg)) {
+            c = findClass(name);
+          } else if (importedPackages.contains(pkg)) {
+            for (ClassLoader cl : parents) {
+              try {
+                c = cl.loadClass(name);
+              } catch (ClassNotFoundException e) {
+                // Ignore
+              }
+            }
+          }
+        }
+        if (c == null) {
+          throw new ClassNotFoundException(name);
+        }
+        if (resolve) {
+          resolveClass(c);
+        }
+        return c;
+      }
+    }
+
+    @Override
+    protected Class<?> findClass(String name) throws ClassNotFoundException {
+      String pkg = name.substring(0, name.lastIndexOf('.'));
+      if (getPackage(pkg) == null) {
+        definePackage(pkg, null, null, null, null, null, null, null);
+      }
+      String path = name.replace('.', '/').concat(".class");
+      InputStream is = 
LimitedClassLoader.class.getClassLoader().getResourceAsStream(path);
+      ByteArrayOutputStream baos = new ByteArrayOutputStream();
+      try {
+        IOUtils.copy(is, baos);
+      } catch (IOException e) {
+        throw new ClassNotFoundException(name, e);
+      }
+      byte[] buf = baos.toByteArray();
+      return defineClass(name, buf, 0, buf.length);
+    }
+  }
   
   private Class<?> getGeneratedSubclass() throws Exception
   {
@@ -317,4 +404,12 @@ public class ProxySubclassGeneratorTest 
   protected Object getP3() {
     return new ProxyTestClassGeneral();
   }
+
+  private Callable<Object> constantly(final Object result) {
+    return new Callable<Object>() {
+      public Object call() throws Exception {
+        return result;
+      }
+    };
+  }
 }

Added: 
aries/trunk/proxy/proxy-impl/src/test/java/org/apache/aries/proxy/test/a/ProxyTestClassA.java
URL: 
http://svn.apache.org/viewvc/aries/trunk/proxy/proxy-impl/src/test/java/org/apache/aries/proxy/test/a/ProxyTestClassA.java?rev=1603727&view=auto
==============================================================================
--- 
aries/trunk/proxy/proxy-impl/src/test/java/org/apache/aries/proxy/test/a/ProxyTestClassA.java
 (added)
+++ 
aries/trunk/proxy/proxy-impl/src/test/java/org/apache/aries/proxy/test/a/ProxyTestClassA.java
 Thu Jun 19 07:01:32 2014
@@ -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.proxy.test.a;
+
+public class ProxyTestClassA {
+
+  private final String message;
+
+  public ProxyTestClassA(String message) {
+    this.message = message;
+  }
+
+  public String getMessage() {
+     return message;
+  }
+}

Added: 
aries/trunk/proxy/proxy-impl/src/test/java/org/apache/aries/proxy/test/b/ProxyTestClassB.java
URL: 
http://svn.apache.org/viewvc/aries/trunk/proxy/proxy-impl/src/test/java/org/apache/aries/proxy/test/b/ProxyTestClassB.java?rev=1603727&view=auto
==============================================================================
--- 
aries/trunk/proxy/proxy-impl/src/test/java/org/apache/aries/proxy/test/b/ProxyTestClassB.java
 (added)
+++ 
aries/trunk/proxy/proxy-impl/src/test/java/org/apache/aries/proxy/test/b/ProxyTestClassB.java
 Thu Jun 19 07:01:32 2014
@@ -0,0 +1,28 @@
+/*
+ * 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.test.b;
+
+import org.apache.aries.proxy.test.a.ProxyTestClassA;
+
+public class ProxyTestClassB extends ProxyTestClassA {
+
+  public ProxyTestClassB(String message) {
+    super(message);
+  }
+}

Added: 
aries/trunk/proxy/proxy-impl/src/test/java/org/apache/aries/proxy/test/c/ProxyTestClassC.java
URL: 
http://svn.apache.org/viewvc/aries/trunk/proxy/proxy-impl/src/test/java/org/apache/aries/proxy/test/c/ProxyTestClassC.java?rev=1603727&view=auto
==============================================================================
--- 
aries/trunk/proxy/proxy-impl/src/test/java/org/apache/aries/proxy/test/c/ProxyTestClassC.java
 (added)
+++ 
aries/trunk/proxy/proxy-impl/src/test/java/org/apache/aries/proxy/test/c/ProxyTestClassC.java
 Thu Jun 19 07:01:32 2014
@@ -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.proxy.test.c;
+
+import org.apache.aries.proxy.test.b.ProxyTestClassB;
+
+public class ProxyTestClassC extends ProxyTestClassB {
+
+  public ProxyTestClassC(String message) {
+    super(message);
+  }
+
+  public String hello() {
+    return getMessage();
+  }
+
+}


Reply via email to