1 /***************************************************************************************
2 * Copyright (c) Jonas Bonér, Alexandre Vasseur. All rights reserved. *
3 * http://aspectwerkz.codehaus.org *
4 * ---------------------------------------------------------------------------------- *
5 * The software in this package is published under the terms of the LGPL license *
6 * a copy of which has been included with this distribution in the license.txt file. *
7 **************************************************************************************/
8 package org.codehaus.aspectwerkz.annotation;
9
10 import org.codehaus.aspectwerkz.reflect.MethodInfo;
11 import org.codehaus.aspectwerkz.reflect.ClassInfo;
12 import org.codehaus.aspectwerkz.reflect.impl.asm.AsmClassInfo;
13 import org.codehaus.aspectwerkz.exception.WrappedRuntimeException;
14 import org.objectweb.asm.attrs.*;
15 import org.objectweb.asm.attrs.Annotation;
16 import org.objectweb.asm.Type;
17
18 import java.lang.reflect.InvocationHandler;
19 import java.lang.reflect.Method;
20 import java.lang.reflect.Proxy;
21 import java.lang.reflect.Field;
22 import java.lang.reflect.Array;
23 import java.util.List;
24 import java.util.Collection;
25 import java.util.ArrayList;
26 import java.util.Iterator;
27 import java.util.Map;
28 import java.util.HashMap;
29 import java.util.Arrays;
30 import java.io.Serializable;
31
32 /***
33 * Dynamic proxy handler for ASM Annotations we extract
34 * The handler resolve the LazyClass to a concrete Class so that the proxy creation does not trigger
35 * any class loading.
36 * <p/>
37 *
38 * @author <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a>
39 */
40 public class Java5AnnotationInvocationHandler implements InvocationHandler {
41
42 /***
43 * The annotation class name
44 */
45 private final String m_annotationClassName;
46
47 /***
48 * A list of AnnotationElement containing the annotation instance element values
49 * (including the defaulted value)
50 */
51 private final List m_annotationElements;
52
53 /***
54 * private ctor - see getAnnotationProxy()
55 *
56 * @param annotationClassName
57 * @param annotationElements
58 */
59 private Java5AnnotationInvocationHandler(String annotationClassName, Collection annotationElements) {
60 m_annotationClassName = annotationClassName;
61 m_annotationElements = new ArrayList(annotationElements.size());
62 for (Iterator iterator = annotationElements.iterator(); iterator.hasNext();) {
63 m_annotationElements.add(iterator.next());
64 }
65 }
66
67 /***
68 * Dynamic proxy based implementation
69 * toString(), annotationType() and value() have a specific behavior
70 *
71 * @param proxy
72 * @param method
73 * @param args
74 * @return
75 * @throws Throwable
76 */
77 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
78 String name = method.getName();
79 if ("toString".equals(name)) {
80 StringBuffer sb = new StringBuffer();
81 sb.append('@').append(m_annotationClassName);
82 sb.append("(");
83 String sep = "";
84 for (Iterator iterator = m_annotationElements.iterator(); iterator.hasNext();) {
85 AnnotationElement annotationElement = (AnnotationElement) iterator.next();
86 sb.append(sep).append(annotationElement.name + "=" + annotationElement.toString());
87 sep = ", ";
88 }
89 sb.append(")");
90 return sb.toString();
91 } else if ("annotationType".equals(name)) {
92
93
94 return Class.forName(m_annotationClassName, false, proxy.getClass().getClassLoader());
95 } else if ("value".equals(name)) {
96 if (m_annotationElements.isEmpty()) {
97 return null;
98 } else {
99
100
101 return ((AnnotationElement) m_annotationElements.get(0)).resolveValueHolderFrom(
102 proxy.getClass().getClassLoader()
103 );
104 }
105 } else {
106 for (Iterator iterator = m_annotationElements.iterator(); iterator.hasNext();) {
107 AnnotationElement annotationElement = (AnnotationElement) iterator.next();
108 if (name.equals(annotationElement.name)) {
109 return annotationElement.resolveValueHolderFrom(proxy.getClass().getClassLoader());
110 }
111 }
112
113 throw new RuntimeException("No such element on Annotation @" + m_annotationClassName + " : " + name);
114 }
115 }
116
117 /***
118 * Build and return a dynamic proxy representing the given ASM Annotation.
119 * The proxy implements the AspectWerkz Annotation interface, as well as the user type Annotation.
120 * Each elements of the annotation is proxied if needed or agressively created unless Class types to not trigger
121 * any nested loading.
122 *
123 * Note: JSR-175 does not support Annotation value of N-dimensional array. At most 1 dimension is supported and
124 * only for a subset of Java types.
125 *
126 * @param annotation
127 * @param loader the classloader of the annotatED component (can be different from the one of the annotation class)
128 * @return
129 */
130 public static org.codehaus.aspectwerkz.annotation.Annotation getAnnotationProxy(org.objectweb.asm.attrs.Annotation annotation, ClassLoader loader) {
131 String annotationClassName = Type.getType(annotation.type).getClassName();
132
133
134
135
136 ClassInfo annotationClassInfo = AsmClassInfo.getClassInfo(annotationClassName, loader);
137 Map annotationElementValueHoldersByName = new HashMap();
138
139
140 MethodInfo[] annotationMethods = annotationClassInfo.getMethods();
141 for (int i = 0; i < annotationMethods.length; i++) {
142 MethodInfo annotationMethod = annotationMethods[i];
143 for (Iterator iterator = annotationMethod.getAnnotations().iterator(); iterator.hasNext();) {
144 AnnotationInfo annotationInfo = (AnnotationInfo) iterator.next();
145
146 if (annotationInfo.getName().equals(AnnotationDefault.NAME)) {
147 Object value = ((AnnotationDefault)annotationInfo.getAnnotation()).value();
148 Object valueHolder = getAnnotationValueHolder(value, loader);
149 annotationElementValueHoldersByName.put(annotationMethod.getName(),
150 new AnnotationElement(annotationMethod.getName(),
151 valueHolder)
152 );
153 }
154 }
155 }
156
157
158 List settedElementValues = annotation.elementValues;
159 for (int i = 0; i < settedElementValues.size(); i++) {
160 Object[] element = (Object[]) settedElementValues.get(i);
161 String name = (String) element[0];
162 Object valueHolder = getAnnotationValueHolder(element[1], loader);
163 annotationElementValueHoldersByName.put(name, new AnnotationElement(name, valueHolder));
164 }
165
166
167 try {
168 Class typeClass = Class.forName(annotationClassName, false, loader);
169 Object proxy = Proxy.newProxyInstance(
170 loader,
171 new Class[]{org.codehaus.aspectwerkz.annotation.Annotation.class, typeClass},
172 new Java5AnnotationInvocationHandler(annotationClassName,
173 annotationElementValueHoldersByName.values()
174 )
175 );
176 return (org.codehaus.aspectwerkz.annotation.Annotation) proxy;
177 } catch (ClassNotFoundException e) {
178 throw new WrappedRuntimeException(e);
179 }
180 }
181
182 /***
183 * Turn an ASM Annotation value into a concrete Java value holder, unless the value is of type
184 * Class, in which case we wrap it behind a LazyClass() object so that actual loading of the class
185 * will be done lazily
186 *
187 * @param value
188 * @param loader
189 * @return
190 */
191 private static Object getAnnotationValueHolder(Object value, ClassLoader loader) {
192 if (value instanceof Annotation.EnumConstValue) {
193 Annotation.EnumConstValue enumAsmValue = (Annotation.EnumConstValue) value;
194 try {
195 Class enumClass = Class.forName(Type.getType(enumAsmValue.typeName).getClassName(), false, loader);
196 Field enumConstValue = enumClass.getField(enumAsmValue.constName);
197 return enumConstValue.get(null);
198 } catch (Exception e) {
199 throw new WrappedRuntimeException(e);
200 }
201 } else if (value instanceof Type) {
202
203 return new AnnotationElement.LazyClass(((Type) value).getClassName());
204 } else if (value instanceof Annotation) {
205 return getAnnotationProxy(((Annotation) value), loader);
206 } else if (value instanceof Object[]) {
207 Object[] values = (Object[]) value;
208 Object[] holders = new Object[values.length];
209 boolean isLazyClass = false;
210 for (int i = 0; i < values.length; i++) {
211 holders[i] = getAnnotationValueHolder(values[i], loader);
212 if (!isLazyClass && holders[i] instanceof AnnotationElement.LazyClass) {
213 isLazyClass = true;
214 }
215 }
216 if (isLazyClass) {
217
218 AnnotationElement.LazyClass[] typedHolders = (AnnotationElement.LazyClass[]) Array.newInstance(AnnotationElement.LazyClass.class, values.length);
219 for (int i = 0; i < holders.length; i++) {
220 typedHolders[i] = (AnnotationElement.LazyClass) holders[i];
221 }
222 return typedHolders;
223 } else {
224 return holders;
225 }
226 }
227 return value;
228 }
229
230 }
231