001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.commons.proxy2.stub; 018 019import java.io.Serializable; 020import java.lang.annotation.Annotation; 021import java.lang.reflect.InvocationHandler; 022import java.lang.reflect.InvocationTargetException; 023import java.lang.reflect.Method; 024import java.lang.reflect.Proxy; 025import java.util.Map; 026 027import org.apache.commons.lang3.AnnotationUtils; 028import org.apache.commons.lang3.ArrayUtils; 029import org.apache.commons.lang3.ObjectUtils; 030import org.apache.commons.lang3.Validate; 031import org.apache.commons.lang3.reflect.TypeUtils; 032import org.apache.commons.proxy2.Interceptor; 033import org.apache.commons.proxy2.Invocation; 034import org.apache.commons.proxy2.Invoker; 035import org.apache.commons.proxy2.ObjectProvider; 036import org.apache.commons.proxy2.ProxyFactory; 037import org.apache.commons.proxy2.ProxyUtils; 038import org.apache.commons.proxy2.impl.AbstractProxyFactory; 039import org.apache.commons.proxy2.provider.ObjectProviderUtils; 040 041public class AnnotationBuilder<A extends Annotation> extends StubBuilder<A> 042{ 043 // underlying proxyfactory implementation based on 044 // org.apache.commons.proxy2.jdk.JdkProxyFactory 045 046 private static class InterceptorInvocationHandler implements InvocationHandler, Serializable 047 { 048 /** Serialization version */ 049 private static final long serialVersionUID = 1L; 050 051 private final ObjectProvider<?> provider; 052 private final Interceptor methodInterceptor; 053 054 public InterceptorInvocationHandler(ObjectProvider<?> provider, Interceptor methodInterceptor) 055 { 056 this.provider = provider; 057 this.methodInterceptor = methodInterceptor; 058 } 059 060 /** 061 * {@inheritDoc} 062 */ 063 @Override 064 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable 065 { 066 if (ProxyUtils.isHashCode(method)) 067 { 068 return Integer.valueOf(AnnotationUtils.hashCode((Annotation) proxy)); 069 } 070 if (ProxyUtils.isEqualsMethod(method)) 071 { 072 return Boolean.valueOf(args[0] instanceof Annotation 073 && AnnotationUtils.equals((Annotation) proxy, (Annotation) args[0])); 074 } 075 if ("toString".equals(method.getName()) && method.getParameterTypes().length == 0) 076 { 077 return AnnotationUtils.toString((Annotation) proxy); 078 } 079 final ReflectionInvocation invocation = new ReflectionInvocation(provider.getObject(), method, args); 080 return methodInterceptor.intercept(invocation); 081 } 082 083 } 084 085 private static class ReflectionInvocation implements Invocation 086 { 087 private final Method method; 088 private final Object[] arguments; 089 private final Object target; 090 091 public ReflectionInvocation(Object target, Method method, Object[] arguments) 092 { 093 this.method = method; 094 this.arguments = ObjectUtils.defaultIfNull(ArrayUtils.clone(arguments), ProxyUtils.EMPTY_ARGUMENTS); 095 this.target = target; 096 } 097 098 @Override 099 public Object[] getArguments() 100 { 101 return arguments; 102 } 103 104 @Override 105 public Method getMethod() 106 { 107 return method; 108 } 109 110 @Override 111 public Object getProxy() 112 { 113 return target; 114 } 115 116 @Override 117 public Object proceed() throws Throwable 118 { 119 try 120 { 121 return method.invoke(target, arguments); 122 } 123 catch (InvocationTargetException e) 124 { 125 throw e.getTargetException(); 126 } 127 } 128 } 129 130 private static final ProxyFactory PROXY_FACTORY = new AbstractProxyFactory() 131 { 132 @Override 133 public <T> T createInvokerProxy(ClassLoader classLoader, final Invoker invoker, Class<?>... proxyClasses) 134 { 135 @SuppressWarnings("unchecked") // type inference 136 final T result = (T) Proxy.newProxyInstance(classLoader, proxyClasses, new InvocationHandler() 137 { 138 @Override 139 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable 140 { 141 return invoker.invoke(proxy, method, args); 142 } 143 }); 144 return result; 145 } 146 147 @Override 148 public <T> T createInterceptorProxy(ClassLoader classLoader, Object target, Interceptor interceptor, 149 Class<?>... proxyClasses) 150 { 151 @SuppressWarnings("unchecked") // type inference 152 final T result = (T) Proxy.newProxyInstance(classLoader, proxyClasses, new InterceptorInvocationHandler( 153 ObjectProviderUtils.constant(target), interceptor)); 154 return result; 155 } 156 157 @Override 158 public <T> T createDelegatorProxy(ClassLoader classLoader, final ObjectProvider<?> delegateProvider, 159 Class<?>... proxyClasses) 160 { 161 @SuppressWarnings("unchecked") // type inference 162 final T result = (T) Proxy.newProxyInstance(classLoader, proxyClasses, new InterceptorInvocationHandler( 163 delegateProvider, new Interceptor() 164 { 165 private static final long serialVersionUID = 1L; 166 167 @Override 168 public Object intercept(Invocation invocation) throws Throwable 169 { 170 return invocation.proceed(); 171 } 172 })); 173 return result; 174 } 175 }; 176 177 private class MapAnnotationTrainer extends AnnotationTrainer<A> 178 { 179 private final Map<String, ?> members; 180 181 MapAnnotationTrainer(Map<String, ?> members) 182 { 183 super(annotationType); 184 this.members = members; 185 } 186 187 @Override 188 protected void train(A trainee) 189 { 190 WhenObject<Object> bud; 191 AnnotationTrainer<A> dy = this; 192 for (Map.Entry<String, ?> attr : members.entrySet()) 193 { 194 final Method m; 195 try 196 { 197 m = traineeType.getDeclaredMethod(attr.getKey()); 198 } 199 catch (Exception e1) 200 { 201 throw new IllegalArgumentException(String.format("Could not detect annotation member %1$s", 202 attr.getKey())); 203 } 204 try 205 { 206 bud = dy.when(m.invoke(trainee)); 207 } 208 catch (Exception e) 209 { 210 // it must have happened on the invoke, so we didn't call 211 // when... it shouldn't happen, but we'll simply skip: 212 continue; 213 } 214 final Object value = attr.getValue(); 215 Validate.isTrue(TypeUtils.isInstance(value, m.getReturnType()), "Value %s can not be assigned to %s", 216 value, m.getReturnType()); 217 dy = bud.thenReturn(value); 218 } 219 } 220 } 221 222 public static <A extends Annotation> A buildDefault(Class<A> type) 223 { 224 return of(type).build(); 225 } 226 227 public static <A extends Annotation> AnnotationBuilder<A> of(Class<A> type) 228 { 229 return new AnnotationBuilder<A>(type, AnnotationInvoker.INSTANCE); 230 } 231 232 public static <A extends Annotation> AnnotationBuilder<A> of(Class<A> type, ObjectProvider<? extends A> provider) 233 { 234 return new AnnotationBuilder<A>(type, provider); 235 } 236 237 public static <A extends Annotation> AnnotationBuilder<A> of(Class<A> type, A target) 238 { 239 return new AnnotationBuilder<A>(type, target); 240 } 241 242 private final Class<A> annotationType; 243 244 private AnnotationBuilder(Class<A> type, Invoker invoker) 245 { 246 super(PROXY_FACTORY, type, invoker); 247 this.annotationType = type; 248 train(new AnnotationTypeTrainer<A>(type)); 249 } 250 251 private AnnotationBuilder(Class<A> type, ObjectProvider<? extends A> provider) 252 { 253 super(PROXY_FACTORY, type, provider); 254 this.annotationType = type; 255 train(new AnnotationTypeTrainer<A>(type)); 256 } 257 258 private AnnotationBuilder(Class<A> type, A target) 259 { 260 super(PROXY_FACTORY, type, target); 261 this.annotationType = type; 262 train(new AnnotationTypeTrainer<A>(type)); 263 } 264 265 public AnnotationBuilder<A> withMembers(Map<String, ?> members) 266 { 267 return train(new MapAnnotationTrainer(members)); 268 } 269 270 @Override 271 public <O> AnnotationBuilder<A> train(BaseTrainer<?, O> trainer) 272 { 273 return (AnnotationBuilder<A>) super.train(trainer); 274 } 275}