| /* |
| * 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; |
| } |
| }; |
| } |
| } |