blob: 58ae17772ae15bab94afd8b3668f1c82e786316e [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.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 java.util.Map;
import org.apache.commons.lang3.AnnotationUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.Validate;
import org.apache.commons.lang3.reflect.TypeUtils;
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
{
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 = ObjectUtils.defaultIfNull(ArrayUtils.clone(arguments), ProxyUtils.EMPTY_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();
}
}));
}
};
private class MapAnnotationTrainer extends AnnotationTrainer<A>
{
final Map<String, ?> members;
MapAnnotationTrainer(Map<String, ?> members)
{
super(annotationType);
this.members = members;
}
@Override
protected void train(A trainee)
{
WhenObject<Object> bud;
AnnotationTrainer<A> dy = this;
for (Map.Entry<String, ?> attr : members.entrySet())
{
final Method m;
try
{
m = traineeType.getDeclaredMethod(attr.getKey());
}
catch (Exception e1)
{
throw new IllegalArgumentException(String.format("Could not detect annotation member %1$s",
attr.getKey()));
}
try
{
bud = dy.when(m.invoke(trainee));
}
catch (Exception e)
{
// it must have happened on the invoke, so we didn't call
// when... it shouldn't happen, but we'll simply skip:
continue;
}
final Object value = attr.getValue();
Validate.isTrue(TypeUtils.isInstance(value, m.getReturnType()), "Value %s can not be assigned to %s",
value, m.getReturnType());
dy = bud.thenReturn(value);
}
}
}
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 final Class<A> annotationType;
private AnnotationBuilder(Class<A> type, Invoker invoker)
{
super(PROXY_FACTORY, type, invoker);
this.annotationType = type;
train(new AnnotationTypeTrainer<A>(type));
}
private AnnotationBuilder(Class<A> type, ObjectProvider<? extends A> provider)
{
super(PROXY_FACTORY, type, provider);
this.annotationType = type;
train(new AnnotationTypeTrainer<A>(type));
}
private AnnotationBuilder(Class<A> type, A target)
{
super(PROXY_FACTORY, type, target);
this.annotationType = type;
train(new AnnotationTypeTrainer<A>(type));
}
public AnnotationBuilder<A> withMembers(Map<String, ?> members)
{
return train(new MapAnnotationTrainer(members));
}
@Override
public <O> AnnotationBuilder<A> train(BaseTrainer<?, O> trainer)
{
return (AnnotationBuilder<A>) super.train(trainer);
}
}