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