You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by mb...@apache.org on 2013/08/19 18:34:12 UTC
svn commit: r1515503 - in
/commons/proper/proxy/branches/version-2.0-work/stub/src:
main/java/org/apache/commons/proxy2/stub/
test/java/org/apache/commons/proxy2/stub/
Author: mbenson
Date: Mon Aug 19 16:34:12 2013
New Revision: 1515503
URL: http://svn.apache.org/r1515503
Log:
annotation stubbing support
Added:
commons/proper/proxy/branches/version-2.0-work/stub/src/main/java/org/apache/commons/proxy2/stub/AnnotationBuilder.java
commons/proper/proxy/branches/version-2.0-work/stub/src/main/java/org/apache/commons/proxy2/stub/AnnotationInvoker.java
commons/proper/proxy/branches/version-2.0-work/stub/src/main/java/org/apache/commons/proxy2/stub/AnnotationTrainer.java
commons/proper/proxy/branches/version-2.0-work/stub/src/test/java/org/apache/commons/proxy2/stub/AnnotationBuilderTest.java
Added: commons/proper/proxy/branches/version-2.0-work/stub/src/main/java/org/apache/commons/proxy2/stub/AnnotationBuilder.java
URL: http://svn.apache.org/viewvc/commons/proper/proxy/branches/version-2.0-work/stub/src/main/java/org/apache/commons/proxy2/stub/AnnotationBuilder.java?rev=1515503&view=auto
==============================================================================
--- commons/proper/proxy/branches/version-2.0-work/stub/src/main/java/org/apache/commons/proxy2/stub/AnnotationBuilder.java (added)
+++ commons/proper/proxy/branches/version-2.0-work/stub/src/main/java/org/apache/commons/proxy2/stub/AnnotationBuilder.java Mon Aug 19 16:34:12 2013
@@ -0,0 +1,215 @@
+/*
+ * 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.commons.proxy2.stub;
+
+import java.io.Serializable;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+
+import org.apache.commons.lang3.AnnotationUtils;
+import org.apache.commons.proxy2.Interceptor;
+import org.apache.commons.proxy2.Invocation;
+import org.apache.commons.proxy2.Invoker;
+import org.apache.commons.proxy2.ObjectProvider;
+import org.apache.commons.proxy2.ProxyFactory;
+import org.apache.commons.proxy2.ProxyUtils;
+import org.apache.commons.proxy2.impl.AbstractProxyFactory;
+import org.apache.commons.proxy2.provider.ObjectProviderUtils;
+
+public class AnnotationBuilder<A extends Annotation> extends StubBuilder<A>
+{
+ // underlying proxyfactory implementation based on org.apache.commons.proxy2.jdk.JdkProxyFactory
+
+ private static class InterceptorInvocationHandler implements InvocationHandler, Serializable
+ {
+ /** Serialization version */
+ private static final long serialVersionUID = 1L;
+
+ private final ObjectProvider<?> provider;
+ private final Interceptor methodInterceptor;
+
+ public InterceptorInvocationHandler(ObjectProvider<?> provider, Interceptor methodInterceptor)
+ {
+ this.provider = provider;
+ this.methodInterceptor = methodInterceptor;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
+ {
+ if (ProxyUtils.isHashCode(method))
+ {
+ return AnnotationUtils.hashCode((Annotation) proxy);
+ }
+ if (ProxyUtils.isEqualsMethod(method))
+ {
+ return args[0] instanceof Annotation
+ && AnnotationUtils.equals((Annotation) proxy, (Annotation) args[0]);
+ }
+ if ("toString".equals(method.getName()) && method.getParameterTypes().length == 0)
+ {
+ return AnnotationUtils.toString((Annotation) proxy);
+ }
+ final ReflectionInvocation invocation = new ReflectionInvocation(provider.getObject(), method, args);
+ return methodInterceptor.intercept(invocation);
+ }
+
+ }
+
+ private static class ReflectionInvocation implements Invocation, Serializable
+ {
+ /** Serialization version */
+ private static final long serialVersionUID = 1L;
+
+ private final Method method;
+ private final Object[] arguments;
+ private final Object target;
+
+ public ReflectionInvocation(Object target, Method method, Object[] arguments)
+ {
+ this.method = method;
+ this.arguments = (arguments == null ? ProxyUtils.EMPTY_ARGUMENTS : arguments);
+ this.target = target;
+ }
+
+ public Object[] getArguments()
+ {
+ return arguments;
+ }
+
+ public Method getMethod()
+ {
+ return method;
+ }
+
+ public Object getProxy()
+ {
+ return target;
+ }
+
+ public Object proceed() throws Throwable
+ {
+ try
+ {
+ return method.invoke(target, arguments);
+ } catch (InvocationTargetException e)
+ {
+ throw e.getTargetException();
+ }
+ }
+ }
+
+ private static final ProxyFactory PROXY_FACTORY = new AbstractProxyFactory()
+ {
+ @SuppressWarnings("unchecked")
+ public <T> T createInvokerProxy(ClassLoader classLoader, final Invoker invoker, Class<?>... proxyClasses)
+ {
+ return (T) Proxy.newProxyInstance(classLoader, proxyClasses, new InvocationHandler()
+ {
+ @Override
+ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
+ {
+ return invoker.invoke(proxy, method, args);
+ }
+ });
+ }
+
+ @SuppressWarnings("unchecked")
+ public <T> T createInterceptorProxy(ClassLoader classLoader, Object target, Interceptor interceptor,
+ Class<?>... proxyClasses)
+ {
+ return (T) Proxy.newProxyInstance(classLoader, proxyClasses, new InterceptorInvocationHandler(
+ ObjectProviderUtils.constant(target), interceptor));
+ }
+
+ @SuppressWarnings("unchecked")
+ public <T> T createDelegatorProxy(ClassLoader classLoader, final ObjectProvider<?> delegateProvider,
+ Class<?>... proxyClasses)
+ {
+ return (T) Proxy.newProxyInstance(classLoader, proxyClasses, new InterceptorInvocationHandler(
+ delegateProvider, new Interceptor()
+ {
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ public Object intercept(Invocation invocation) throws Throwable
+ {
+ return invocation.proceed();
+ }
+ }));
+ }
+ };
+
+ public static <A extends Annotation> A buildDefault(Class<A> type)
+ {
+ return of(type).build();
+ }
+
+ public static <A extends Annotation> AnnotationBuilder<A> of(Class<A> type)
+ {
+ return new AnnotationBuilder<A>(type, AnnotationInvoker.INSTANCE);
+ }
+
+ public static <A extends Annotation> AnnotationBuilder<A> of(Class<A> type, ObjectProvider<? extends A> provider)
+ {
+ return new AnnotationBuilder<A>(type, provider);
+ }
+
+ public static <A extends Annotation> AnnotationBuilder<A> of(Class<A> type, A target)
+ {
+ return new AnnotationBuilder<A>(type, target);
+ }
+
+ private AnnotationBuilder(Class<A> type, Invoker invoker)
+ {
+ super(PROXY_FACTORY, type, invoker);
+ }
+
+ private AnnotationBuilder(Class<A> type, ObjectProvider<? extends A> provider)
+ {
+ super(PROXY_FACTORY, type, provider);
+ }
+
+ private AnnotationBuilder(Class<A> type, A target)
+ {
+ super(PROXY_FACTORY, type, target);
+ }
+
+ @Override
+ public AnnotationBuilder<A> train(BaseTrainer<?, A> trainer)
+ {
+ return (AnnotationBuilder<A>) super.train(trainer);
+ }
+
+ @Override
+ public A build() {
+ train(new AnnotationTrainer<A>(type)
+ {
+ @Override
+ protected void train(A trainee)
+ {
+ when(trainee.annotationType()).thenReturn(type);
+ }
+ });
+ return super.build();
+ }
+}
Added: commons/proper/proxy/branches/version-2.0-work/stub/src/main/java/org/apache/commons/proxy2/stub/AnnotationInvoker.java
URL: http://svn.apache.org/viewvc/commons/proper/proxy/branches/version-2.0-work/stub/src/main/java/org/apache/commons/proxy2/stub/AnnotationInvoker.java?rev=1515503&view=auto
==============================================================================
--- commons/proper/proxy/branches/version-2.0-work/stub/src/main/java/org/apache/commons/proxy2/stub/AnnotationInvoker.java (added)
+++ commons/proper/proxy/branches/version-2.0-work/stub/src/main/java/org/apache/commons/proxy2/stub/AnnotationInvoker.java Mon Aug 19 16:34:12 2013
@@ -0,0 +1,38 @@
+/*
+ * 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.commons.proxy2.stub;
+
+import java.lang.reflect.Method;
+
+import org.apache.commons.proxy2.Invoker;
+import org.apache.commons.proxy2.ProxyUtils;
+
+public final class AnnotationInvoker implements Invoker
+{
+ private static final long serialVersionUID = 1L;
+
+ public static final AnnotationInvoker INSTANCE = new AnnotationInvoker();
+
+ @Override
+ public Object invoke(Object proxy, Method method, Object[] arguments) throws Throwable {
+ final Object result = method.getDefaultValue();
+ return result == null && method.getReturnType().isPrimitive() ? ProxyUtils
+ .nullValue(method.getReturnType()) : result;
+ }
+
+}
\ No newline at end of file
Added: commons/proper/proxy/branches/version-2.0-work/stub/src/main/java/org/apache/commons/proxy2/stub/AnnotationTrainer.java
URL: http://svn.apache.org/viewvc/commons/proper/proxy/branches/version-2.0-work/stub/src/main/java/org/apache/commons/proxy2/stub/AnnotationTrainer.java?rev=1515503&view=auto
==============================================================================
--- commons/proper/proxy/branches/version-2.0-work/stub/src/main/java/org/apache/commons/proxy2/stub/AnnotationTrainer.java (added)
+++ commons/proper/proxy/branches/version-2.0-work/stub/src/main/java/org/apache/commons/proxy2/stub/AnnotationTrainer.java Mon Aug 19 16:34:12 2013
@@ -0,0 +1,56 @@
+/*
+ * 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.commons.proxy2.stub;
+
+import java.lang.annotation.Annotation;
+
+import org.apache.commons.proxy2.interceptor.InterceptorUtils;
+
+public abstract class AnnotationTrainer<A extends Annotation> extends BaseTrainer<AnnotationTrainer<A>, A>
+{
+ protected AnnotationTrainer() {
+ super();
+ }
+
+ protected AnnotationTrainer(Class<A> traineeType) {
+ super(traineeType);
+ }
+
+ protected class WhenAnnotation<R> extends WhenObject<R>
+ {
+ protected AnnotationTrainer<A> thenStub(Class<R> type) {
+ trainingContext().push(type);
+ trainingContext().then(InterceptorUtils.constant(trainingContext().pop(AnnotationInvoker.INSTANCE)));
+ return AnnotationTrainer.this;
+ }
+
+ @Override
+ protected AnnotationTrainer<A> thenStub(BaseTrainer<?, R> trainer) {
+ final R trainee = trainingContext().push(trainer.traineeType);
+ trainer.train(trainee);
+ trainingContext().then(InterceptorUtils.constant(trainingContext().pop(AnnotationInvoker.INSTANCE)));
+ return AnnotationTrainer.this;
+ }
+ }
+
+ @Override
+ protected <R> WhenAnnotation<R> when(R expression) {
+ return new WhenAnnotation<R>();
+ }
+
+}
Added: commons/proper/proxy/branches/version-2.0-work/stub/src/test/java/org/apache/commons/proxy2/stub/AnnotationBuilderTest.java
URL: http://svn.apache.org/viewvc/commons/proper/proxy/branches/version-2.0-work/stub/src/test/java/org/apache/commons/proxy2/stub/AnnotationBuilderTest.java?rev=1515503&view=auto
==============================================================================
--- commons/proper/proxy/branches/version-2.0-work/stub/src/test/java/org/apache/commons/proxy2/stub/AnnotationBuilderTest.java (added)
+++ commons/proper/proxy/branches/version-2.0-work/stub/src/test/java/org/apache/commons/proxy2/stub/AnnotationBuilderTest.java Mon Aug 19 16:34:12 2013
@@ -0,0 +1,102 @@
+/*
+ * 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.commons.proxy2.stub;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+import org.junit.Test;
+
+/**
+ * Test {@link AnnotationFactory}.
+ */
+public class AnnotationBuilderTest
+{
+ @Test
+ public void testDefaultAnnotation()
+ {
+ final CustomAnnotation customAnnotation = AnnotationBuilder.buildDefault(CustomAnnotation.class);
+ assertEquals(CustomAnnotation.class, customAnnotation.annotationType());
+ assertEquals("", customAnnotation.annString());
+ assertEquals(0, customAnnotation.finiteValues().length);
+ assertNull(customAnnotation.someType());
+ }
+
+ @Test
+ public void testStubbedAnnotation()
+ {
+ final CustomAnnotation customAnnotation =
+ AnnotationBuilder.of(CustomAnnotation.class).train(new AnnotationTrainer<CustomAnnotation>()
+ {
+ @Override
+ protected void train(CustomAnnotation trainee)
+ {
+ when(trainee.someType()).thenReturn(Object.class).when(trainee.finiteValues())
+ .thenReturn(FiniteValues.ONE, FiniteValues.THREE).when(trainee.annString()).thenReturn("hey");
+ }
+ }).build();
+
+ assertEquals(CustomAnnotation.class, customAnnotation.annotationType());
+ assertEquals("hey", customAnnotation.annString());
+ assertArrayEquals(new FiniteValues[] { FiniteValues.ONE, FiniteValues.THREE }, customAnnotation.finiteValues());
+ assertEquals(Object.class, customAnnotation.someType());
+ }
+
+ @Test
+ public void testNestedStubbedAnnotation()
+ {
+ final NestingAnnotation nestingAnnotation =
+ AnnotationBuilder.of(NestingAnnotation.class).train(new AnnotationTrainer<NestingAnnotation>()
+ {
+ @Override
+ protected void train(NestingAnnotation trainee)
+ {
+ when(trainee.child()).thenStub(CustomAnnotation.class).when(trainee.somethingElse())
+ .thenReturn("somethingElse");
+ }
+ }).build();
+
+ assertEquals("", nestingAnnotation.child().annString());
+ assertEquals(0, nestingAnnotation.child().finiteValues().length);
+ assertEquals(null, nestingAnnotation.child().someType());
+ assertEquals("somethingElse", nestingAnnotation.somethingElse());
+ }
+
+ public @interface NestingAnnotation
+ {
+ CustomAnnotation child();
+
+ String somethingElse();
+ }
+
+ public @interface CustomAnnotation
+ {
+ String annString() default "";
+
+ FiniteValues[] finiteValues() default {};
+
+ Class<?> someType();
+ }
+
+ public enum FiniteValues
+ {
+ ONE, TWO, THREE;
+ }
+
+}
\ No newline at end of file