blob: 0fe299ecf3829a67f01e212eed6af35f81220e2a [file] [log] [blame]
/*
* 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.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.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Callable;
import org.apache.aries.proxy.FinalModifierException;
import org.apache.aries.proxy.InvocationListener;
import org.apache.aries.proxy.UnableToProxyException;
import org.apache.aries.proxy.impl.AbstractProxyManager;
import org.apache.aries.proxy.impl.AsmProxyManager;
import org.apache.aries.proxy.impl.ProxyHandler;
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;
/**
* This class uses the {@link ProxySubclassGenerator} to test
*/
public class ProxySubclassGeneratorTest extends AbstractProxyTest
{
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;
Class<?> generatedProxySubclass = null;
Object o = null;
/**
* @throws java.lang.Exception
*/
@Before
public void setUp() throws Exception
{
ih = new FakeInvocationHandler();
((FakeInvocationHandler)ih).setDelegate(getTestClass().newInstance());
generatedProxySubclass = getGeneratedSubclass();
o = getProxyInstance(generatedProxySubclass);
}
/**
* 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 = getTestClass();
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(getTestClass().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 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 a private constructor
*/
@Test
public void testPrivateConstructor() throws Exception
{
Object o = ProxySubclassGenerator.newProxySubclassInstance(
ProxyTestClassPrivateConstructor.class, ih);
assertNotNull("The new instance was null", o);
}
/**
* Test a generating proxy class of class with package access constructor.
*/
@SuppressWarnings("unchecked")
@Test
public void testPackageAccessCtor() throws Exception {
Class<ProxyTestClassPackageAccessCtor> proxyClass =
(Class<ProxyTestClassPackageAccessCtor>) ProxySubclassGenerator.getProxySubclass(ProxyTestClassPackageAccessCtor.class);
ProxyTestClassPackageAccessCtor proxy = (ProxyTestClassPackageAccessCtor) getProxyInstance(proxyClass);
assertNotNull("The new instance was null", proxy);
}
// /**
// * Test object equality between real and proxy using a Collaborator
// */
// @Test
// public void testObjectEquality() throws Exception
// {
// Object delegate = getTestClass().newInstance();
// InvocationHandler collaborator = new Collaborator(null, null, AsmInterceptorWrapper.passThrough(delegate));
// Object o = ProxySubclassGenerator.newProxySubclassInstance(getTestClass(), 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(getTestClass(), 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);
// }
/**
* 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 covariant override method
*/
@Test
public void testGenerics() throws Exception
{
((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();
}
final Map<String, Object> clLocks = new HashMap<String, Object>();
protected synchronized Object getClassLoadingLock (String name) {
if (!clLocks.containsKey(name)) {
clLocks.put(name, new Object());
}
return clLocks.get(name);
}
@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
{
return getProxyClass(getTestClass());
}
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
{
try {
Object result = (delegate instanceof Callable) ?
method.invoke(((Callable<?>)delegate).call(), args) :
method.invoke(delegate, args) ;
return result;
} catch (InvocationTargetException ite) {
throw ite.getTargetException();
}
}
void setDelegate(Object delegate){
this.delegate = delegate;
}
}
@Override
protected Object getProxyInstance(Class<?> proxyClass) {
return getProxyInstance(proxyClass, ih);
}
private Object getProxyInstance(Class<?> proxyClass, InvocationHandler ih) {
try {
if(proxyClass.equals(ProxyTestClassChildOfAbstract.class))
return proxyClass.newInstance();
Object proxyInstance = proxyClass.getConstructor().newInstance();
Method setIH = proxyInstance.getClass().getMethod("setInvocationHandler", InvocationHandler.class);
setIH.invoke(proxyInstance, ih);
return proxyInstance;
} catch (Exception e) {
return null;
}
}
@Override
protected Class<?> getProxyClass(Class<?> clazz) {
try {
return ProxySubclassGenerator.getProxySubclass(clazz);
} catch (UnableToProxyException e) {
return null;
}
}
@Override
protected Object setDelegate(Object proxy, Callable<Object> dispatcher) {
AbstractProxyManager apm = new AsmProxyManager();
return getProxyInstance(proxy.getClass(), new ProxyHandler(apm, dispatcher, null));
}
@Override
protected Object getProxyInstance(Class<?> proxyClass,
InvocationListener listener) {
AbstractProxyManager apm = new AsmProxyManager();
return getProxyInstance(proxyClass, new ProxyHandler(apm, new SingleInstanceDispatcher(getProxyInstance(proxyClass)), listener));
}
@Override
protected Object getP3() {
return new ProxyTestClassGeneral();
}
private Callable<Object> constantly(final Object result) {
return new Callable<Object>() {
public Object call() throws Exception {
return result;
}
};
}
}