View Javadoc

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.instrumentation.javassist;
9   
10  import org.codehaus.aspectwerkz.util.Base64;
11  import org.codehaus.aspectwerkz.annotation.instrumentation.AttributeExtractor;
12  import org.codehaus.aspectwerkz.annotation.instrumentation.asm.CustomAttributeHelper;
13  import org.codehaus.aspectwerkz.definition.DescriptorUtil;
14  
15  import java.util.ArrayList;
16  import java.util.Arrays;
17  import java.util.Iterator;
18  import java.util.List;
19  import java.lang.reflect.Method;
20  
21  import javassist.CtClass;
22  import javassist.CtConstructor;
23  import javassist.CtField;
24  import javassist.CtMethod;
25  import javassist.bytecode.AttributeInfo;
26  import javassist.bytecode.ClassFile;
27  import javassist.bytecode.AnnotationsAttribute;
28  import javassist.bytecode.annotation.StringMemberValue;
29  import javassist.bytecode.annotation.Annotation;
30  
31  /***
32   * Javassist implementation of the AttributeExtractor interface. Extracts attributes from the class file on class,
33   * method and field level.
34   * 
35   * @author <a href="mailto:jboner@codehaus.org">Jonas Bonér </a>
36   * @author <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a>
37   */
38  public class JavassistAttributeExtractor implements AttributeExtractor {
39  
40      private final static String RUNTIME_INVISIBLE_ANNOTATIONS = "RuntimeInvisibleAnnotations";
41      private final static String CUSTOM_ATTRIBUTE_CLASSNAME = "org.codehaus.aspectwerkz.annotation.instrumentation.asm.CustomAttribute";
42      private final static String VALUE = "value";
43  
44      /***
45       * The Javassist class.
46       */
47      private CtClass m_ctClass;
48  
49      /***
50       * Open the classfile and parse it in to the Javassist library.
51       * 
52       * @param ctClass the class
53       */
54      public void initialize(final CtClass ctClass) {
55          m_ctClass = ctClass;
56          if (!(m_ctClass.isPrimitive() || m_ctClass.isArray())) {
57              m_ctClass.stopPruning(true);
58              m_ctClass.defrost();
59          }
60      }
61  
62      /***
63       * Returns the class attributes.
64       * 
65       * @return the class attributes
66       */
67      public Object[] getClassAttributes() {
68          if (m_ctClass.isPrimitive() || m_ctClass.isArray()) {
69              return EMPTY_OBJECT_ARRAY;
70          }
71          List attributes = new ArrayList();
72          ClassFile classFile = m_ctClass.getClassFile();
73          List attrs = classFile.getAttributes();
74          for (Iterator it = attrs.iterator(); it.hasNext();) {
75              retrieveCustomAttributes((AttributeInfo) it.next(), attributes);
76          }
77          return attributes.toArray(new Object[attributes.size()]);
78      }
79  
80      public Object[] getMethodAttributes(final Method method) {
81          throw new RuntimeException("NA");
82          //return getMethodAttributes(method.getName(), null);
83      }
84      /***
85       * Return all the attributes associated with a method that have a particular method signature.
86       * 
87       * @param methodName The name of the method.
88       * @param methodParamTypes An array of parameter types as given by the reflection api.
89       * @return the method attributes.
90       */
91      public Object[] getMethodAttributes(final String methodName, final String[] methodParamTypes) {
92          if (m_ctClass.isPrimitive() || m_ctClass.isArray()) {
93              return EMPTY_OBJECT_ARRAY;
94          }
95          List attributes = new ArrayList();
96          CtMethod[] methods = m_ctClass.getDeclaredMethods();
97          for (int i = 0; i < methods.length; i++) {
98              CtMethod method = methods[i];
99              if (method.getName().equals(methodName)) {
100                 if (Arrays.equals(methodParamTypes, DescriptorUtil.getParameters(methods[i].getSignature()))) {
101                     for (Iterator it = method.getMethodInfo().getAttributes().iterator(); it.hasNext();) {
102                         retrieveCustomAttributes((AttributeInfo) it.next(), attributes);
103                     }
104                 }
105             }
106         }
107         return attributes.toArray(new Object[attributes.size()]);
108     }
109 
110     /***
111      * Return all the attributes associated with a constructor that have a particular method signature.
112      * 
113      * @param constructorParamTypes An array of parameter types as given by the reflection api.
114      * @return the constructor attributes.
115      */
116     public Object[] getConstructorAttributes(final String[] constructorParamTypes) {
117         if (m_ctClass.isPrimitive() || m_ctClass.isArray()) {
118             return EMPTY_OBJECT_ARRAY;
119         }
120         List attributes = new ArrayList();
121         CtConstructor[] constructors = m_ctClass.getDeclaredConstructors();
122         for (int i = 0; i < constructors.length; i++) {
123             CtConstructor constructor = constructors[i];
124             if (Arrays.equals(constructorParamTypes, DescriptorUtil.getParameters(constructors[i].getSignature()))) {
125                 for (Iterator it = constructor.getMethodInfo().getAttributes().iterator(); it.hasNext();) {
126                     retrieveCustomAttributes((AttributeInfo) it.next(), attributes);
127                 }
128             }
129         }
130         return attributes.toArray(new Object[attributes.size()]);
131     }
132 
133     /***
134      * Return all the attributes associated with a field.
135      * 
136      * @param fieldName The name of the field.
137      * @return the field attributes.
138      */
139     public Object[] getFieldAttributes(final String fieldName) {
140         if (m_ctClass.isPrimitive() || m_ctClass.isArray()) {
141             return EMPTY_OBJECT_ARRAY;
142         }
143         List attributes = new ArrayList();
144         CtField[] fields = m_ctClass.getDeclaredFields();
145         for (int i = 0; i < fields.length; i++) {
146             if (fields[i].getName().equals(fieldName)) {
147                 CtField field = fields[i];
148                 for (Iterator it = field.getFieldInfo().getAttributes().iterator(); it.hasNext();) {
149                     retrieveCustomAttributes((AttributeInfo) it.next(), attributes);
150                 }
151             }
152         }
153         return attributes.toArray(new Object[attributes.size()]);
154     }
155 
156     /***
157      * Retrieves custom attributes and puts them in a list.
158      * 
159      * @param attributeInfo
160      * @param listToPutAttributesIn
161      */
162     private void retrieveCustomAttributes(final AttributeInfo attributeInfo, final List listToPutAttributesIn) {
163         if (attributeInfo.getName().equals(RUNTIME_INVISIBLE_ANNOTATIONS)) {
164             // for weird reason, we may end up in not having an AnnotationsAttribute instance
165             if ( !(attributeInfo instanceof AnnotationsAttribute)) {
166                 return;
167             }
168             AnnotationsAttribute annotationAttribute = (AnnotationsAttribute)attributeInfo;
169             for (int i = 0; i < annotationAttribute.getAnnotations().length; i++) {
170                 Annotation annotation = annotationAttribute.getAnnotations()[i];
171                 // TODO: stuff is hard coded here - dump it with AW 2.0
172                 // TODO: when Javassist support primitive type array, then do not use BASE64
173                 if (annotation.getTypeName().equals(CUSTOM_ATTRIBUTE_CLASSNAME)) {
174                     String value = ((StringMemberValue)annotation.getMemberValue(VALUE)).getValue();
175                     byte[] bytes = Base64.decode(value);
176                     listToPutAttributesIn.add(CustomAttributeHelper.extractCustomAnnotation(bytes));
177                 }
178             }
179         }
180         // NOTE: Matching on 1.5 annotations is not supported in the delegation engine and will not be supported
181     }
182 
183 }