/* * 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.openejb.dyni; import org.apache.openejb.util.Join; import org.junit.Assert; import org.junit.Test; import java.lang.annotation.Annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.net.URI; import java.net.URL; import java.net.URLClassLoader; /** * Implementation of methods and constructors: * <p/> * - Ensure all abstract methods were implemented to delegate to the InvocationHandler method * - Ensure all constructors were carried forward to subclass * <p/> * Preservation of annotations * <p/> * - Ensure all annotations from the immediate parent class were copied * - Ensure all annotations from parent constructors and constructor params were copied * - Ensure all annotations from abstract ancestor methods and method params were copied * * @version $Rev$ $Date$ */ public class DynamicSubclassTest extends Assert { private static Invocation invocation; @Test public void test() throws Exception { final URLClassLoader loader = new URLClassLoader(new URL[0]); final Class subclass = DynamicSubclass.createSubclass(Blue.class, loader); final Constructor constructor = subclass.getConstructor(long.class); final Blue blue = (Blue) constructor.newInstance(1l); final Class<?> generatedClass = blue.getClass(); assertNotEquals(Blue.class, generatedClass); // Were class, constructor and constructor parameter annotations copied? { // class annotations? assertEquals("blue", generatedClass.getAnnotation(Circle.class).value()); // constructor annotations? assertEquals("blue()", generatedClass.getConstructor().getAnnotation(Oval.class).value()); assertEquals("blue(long)", generatedClass.getConstructor(long.class).getAnnotation(Oval.class).value()); // constructor parameter annotations? final Annotation annotation = generatedClass.getConstructor(long.class).getParameterAnnotations()[0][0]; assertEquals("1", ((Triangle) annotation).value()); } { // blue method blue.blue(1); assertNotNull(invocation); final Method method = Blue.class.getDeclaredMethod("blue", int.class); assertEquals(invocation.getMethod(), method); assertEquals("1", Join.join(",", invocation.args)); assertEquals("blue", method.getAnnotation(Square.class).value()); assertEquals("blue", ((Triangle) method.getParameterAnnotations()[0][0]).value()); } { // green method blue.green("hello"); assertNotNull(invocation); final Method method = Green.class.getDeclaredMethod("green", String.class); assertEquals(invocation.getMethod(), method); assertEquals("hello", Join.join(",", invocation.args)); assertEquals("green", method.getAnnotation(Square.class).value()); assertEquals("green", ((Triangle) method.getParameterAnnotations()[0][0]).value()); } { // blue method blue.red(URI.create("foo://bar")); assertNotNull(invocation); final Method method = Red.class.getDeclaredMethod("red", URI.class); assertEquals(invocation.getMethod(), method); assertEquals("foo://bar", Join.join(",", invocation.getArgs())); assertEquals("red", method.getAnnotation(Square.class).value()); assertEquals("red", ((Triangle) method.getParameterAnnotations()[0][0]).value()); } } public static class Invocation { private final Object proxy; private final Method method; private final Object[] args; public Invocation(final Object proxy, final Method method, final Object[] args) { this.proxy = proxy; this.method = method; this.args = args; } public Object getProxy() { return proxy; } public Method getMethod() { return method; } public Object[] getArgs() { return args; } } public static abstract class Color implements InvocationHandler { public Color() { } public Color(final URI uri, final long foo) { } // TODO: check to ensure this method is implemented, issue validation failure if not @Override public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable { invocation = new Invocation(proxy, method, args); return null; } } @Circle("red") public static abstract class Red extends Color { @Square("red") public abstract void red(@Triangle("red") URI uri); } @Circle("green") public static abstract class Green extends Red { @Square("green") public abstract void green(@Triangle("green") String v); } @Circle("blue") public static abstract class Blue extends Green { @Oval("blue()") public Blue() { } @Oval("blue(long)") public Blue(@Triangle("1") final long l) { } @Square("blue") public abstract void blue(@Triangle("blue") int v); } @Target(value = ElementType.TYPE) @Retention(value = RetentionPolicy.RUNTIME) public static @interface Circle { String value(); } @Target(value = ElementType.CONSTRUCTOR) @Retention(value = RetentionPolicy.RUNTIME) public static @interface Oval { String value(); } @Target(value = ElementType.METHOD) @Retention(value = RetentionPolicy.RUNTIME) public static @interface Square { String value(); } @Target(value = ElementType.PARAMETER) @Retention(value = RetentionPolicy.RUNTIME) public static @interface Triangle { String value(); } }