KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jboss > aop > advice > AdviceMethodFactory


1 /*
2 * JBoss, Home of Professional Open Source
3 * Copyright 2005, JBoss Inc., and individual contributors as indicated
4 * by the @authors tag. See the copyright.txt in the distribution for a
5 * full listing of individual contributors.
6 *
7 * This is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU Lesser General Public License as
9 * published by the Free Software Foundation; either version 2.1 of
10 * the License, or (at your option) any later version.
11 *
12 * This software is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this software; if not, write to the Free
19 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
20 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
21 */

22 package org.jboss.aop.advice;
23
24 import java.lang.reflect.Method JavaDoc;
25 import java.util.ArrayList JavaDoc;
26
27 import javassist.NotFoundException;
28
29 import org.jboss.aop.JoinPointInfo;
30 import org.jboss.aop.joinpoint.Invocation;
31 import org.jboss.aop.util.ReflectUtils;
32
33 /**
34  * Utility class to figure out which advice method to use for a given joinpoint
35  *
36  * @author <a HREF="kabir.khan@jboss.com">Kabir Khan</a>
37  * @version $Revision: 44253 $
38  */

39 public class AdviceMethodFactory
40 {
41    private static final int IS_BEFORE = 1;
42    private static final int IS_AFTER = 2;
43    private static final int IS_THROWING = 3;
44    private static final int IS_AROUND = 4;
45    
46    public static final AdviceMethodFactory BEFORE = new AdviceMethodFactory(IS_BEFORE, false, true, false, false);
47    public static final AdviceMethodFactory AFTER = new AdviceMethodFactory(IS_AFTER, false, true, true, false);
48    public static final AdviceMethodFactory THROWING = new AdviceMethodFactory(IS_THROWING, false, true, false, true);
49    public static final AdviceMethodFactory AROUND = new AdviceMethodFactory(IS_AROUND, true, false, false, false);
50    
51    private static final Class JavaDoc INVOCATION = Invocation.class;
52    private static final Class JavaDoc THROWABLE = Throwable JavaDoc.class;
53          
54    int type;
55    boolean canHaveJoinpoint;
56    boolean canHaveInvocation;
57    boolean mustHaveReturnType;
58    boolean mustHaveThrowable;
59    
60    static final MatchData BEST_MATCH_START = new MatchData();
61    
62    private AdviceMethodFactory(int type, boolean canHaveInvocation, boolean canHaveJoinpoint, boolean canHaveReturnType, boolean canHaveThrowable)
63    {
64       this.type = type;
65       this.canHaveInvocation = canHaveInvocation;
66       this.canHaveJoinpoint = canHaveJoinpoint;
67       this.mustHaveReturnType = canHaveReturnType;
68       this.mustHaveThrowable = canHaveThrowable;
69    }
70    
71    public AdviceMethodProperties findAdviceMethod(AdviceMethodProperties properties)
72    {
73       Method JavaDoc[] methods = ReflectUtils.getMethodsWithName(properties.getAspectClass(), properties.getAdviceName());
74     
75       if (methods.length == 0) return null;
76       if (methods.length == 1 && methods[0].getParameterTypes().length == 0)
77       {
78          if (mustHaveReturnType && !properties.getJoinpointReturnType().equals(Void.TYPE))
79          {
80             return null;
81          }
82          if (mustHaveThrowable)
83          {
84             return null;
85          }
86          properties.setFoundProperties(methods[0], new ArrayList JavaDoc());
87          return properties;
88       }
89       
90       MatchData bestMatch = BEST_MATCH_START;
91       for (int i = 0 ; i < methods.length ; i++)
92       {
93          MatchData matchData = matchParameters(properties, methods[i], bestMatch);
94          if (matchData != null)
95          {
96             bestMatch = matchData;
97          }
98       }
99       
100       if (bestMatch.method != null)
101       {
102          return setupMethodAndArgsInProperties(properties, bestMatch);
103       }
104          
105       
106       return null;
107    }
108    
109    private AdviceMethodProperties setupMethodAndArgsInProperties(AdviceMethodProperties properties, MatchData matchData)
110    {
111       ArrayList JavaDoc args = new ArrayList JavaDoc();
112       if (matchData.invocationMatchDegree >= 0)
113       {
114          if (canHaveInvocation) args.add(AdviceMethodProperties.INVOCATION_ARG);
115          else if (canHaveJoinpoint) args.add(AdviceMethodProperties.JOINPOINT_ARG);
116       }
117
118       if (matchData.returnOrThrowingMatchDegree >= 0)
119       {
120          if (mustHaveReturnType) args.add(AdviceMethodProperties.RETURN_ARG);
121          else if (mustHaveThrowable) args.add(AdviceMethodProperties.THROWABLE_ARG);
122       }
123
124       if (matchData.argsIndices != null)
125       {
126          args.addAll(matchData.argsIndices);
127       }
128       
129       properties.setFoundProperties(matchData.method, args);
130       
131       return properties;
132    }
133
134    private MatchData matchParameters(AdviceMethodProperties properties, Method JavaDoc adviceMethod, MatchData bestMatch)
135    {
136       Class JavaDoc[] adviceParams = adviceMethod.getParameterTypes();
137       if (adviceParams.length == 0 && bestMatch.method == null) return new MatchData(adviceMethod);
138
139       MatchData currentMatch = lookForJoinPointInfoOrInvocation(properties, adviceMethod, bestMatch);
140       if (currentMatch == null)
141       {
142          return null;
143       }
144
145       currentMatch = lookForThrowingOrReturn(properties, adviceMethod, currentMatch, bestMatch);
146       if (currentMatch == null)
147       {
148          return null;
149       }
150
151       currentMatch = matchActualArgs(properties, adviceMethod, currentMatch, bestMatch);
152       if (currentMatch == null)
153       {
154          return null;
155       }
156
157       if (currentMatch.currentParam == adviceMethod.getParameterTypes().length) return currentMatch;
158       
159       return currentMatch;
160    }
161    
162    
163    
164    /**
165     * Looks for an Invocation or a JoinPointInfo in the first parameter of the looked for method.
166     * Returns -1 if it cannot find the required class, 0 means an exact match, 1 the superclass of what we would expect etc.
167     */

168    private MatchData lookForJoinPointInfoOrInvocation(AdviceMethodProperties properties, Method JavaDoc method, MatchData bestMatch)
169    {
170       Class JavaDoc[] adviceParams = method.getParameterTypes();
171       int index = 0;
172       int matchDegree = -1;
173       boolean firstIsSpecial = false;
174    
175       if (canHaveInvocation)
176       {
177          //Check if adviceParams[index] is invocation of correct type
178
if (isInvocation(adviceParams[index]))
179          {
180             firstIsSpecial = true;
181             matchDegree = matchClass(adviceParams[index], properties.getInvocationType());
182          }
183       }
184       else if (canHaveJoinpoint)
185       {
186          //Check if adviceParams[index] is JoinPoint of correct type
187
if (isInfo(adviceParams[index]))
188          {
189             firstIsSpecial = true;
190             matchDegree = matchClass(adviceParams[index], properties.getInfoType());
191          }
192       }
193       
194       if (firstIsSpecial)
195       {
196          if (matchDegree < 0)
197          {
198             //First param was an invocation/joinpoint, but not of the right type
199
return null;
200          }
201          else
202          {
203             index++;
204          }
205       }
206
207       //Check if the best match is still the best
208
if (bestMatch.invocationMatchDegree >= 0 && (bestMatch.invocationMatchDegree < matchDegree || !firstIsSpecial))
209       {
210          return null;
211       }
212
213       return new MatchData(method, index, matchDegree);
214    }
215    
216    private MatchData lookForTargetObject(AdviceMethodProperties properties, Method JavaDoc adviceMethod, MatchData currentMatch, MatchData bestMatch) throws NotFoundException
217    {
218       return currentMatch;
219    }
220    
221    private MatchData lookForThrowingOrReturn(AdviceMethodProperties properties, Method JavaDoc adviceMethod, MatchData currentMatch, MatchData bestMatch)
222    {
223       if (currentMatch.currentParam == adviceMethod.getParameterTypes().length)
224       {
225          return currentMatch;
226       }
227
228       if (mustHaveReturnType)
229       {
230          currentMatch = lookForReturn(properties, adviceMethod, currentMatch, bestMatch);
231          if (currentMatch == null)
232          {
233             return null;
234          }
235       }
236       else if (mustHaveThrowable)
237       {
238          //Check if adviceParams[index] is subclass of throwable/one of the exceptions thrown by the method/ctor
239
currentMatch.returnOrThrowingMatchDegree = matchClass(adviceMethod.getParameterTypes()[currentMatch.currentParam], THROWABLE);
240          currentMatch.currentParam++;
241       }
242
243       //Check if the best match is still the best
244
if (bestMatch.returnOrThrowingMatchDegree >= 0 && bestMatch.returnOrThrowingMatchDegree < currentMatch.returnOrThrowingMatchDegree)
245       {
246          return null;
247       }
248       return currentMatch;
249    }
250    
251    private MatchData lookForReturn(AdviceMethodProperties properties, Method JavaDoc adviceMethod, MatchData currentMatch, MatchData bestMatch)
252    {
253       //Check if adviceParams[index] has same return type as method/field get/ctor
254
Class JavaDoc returnType = properties.getJoinpointReturnType();
255       if (returnType == null || returnType.equals(Void.TYPE))
256       {
257       }
258       else
259       {
260          //Check that the return type is correct - the return type of the called method must be a
261
//subclass of the return type of the advice
262
currentMatch.returnOrThrowingMatchDegree = subClassMatch(returnType, adviceMethod.getReturnType());
263          
264          
265          
266          if (currentMatch.returnOrThrowingMatchDegree < 0)
267          {
268             return null;
269          }
270          
271          //Now check that we take the correct type of return parameter
272
Class JavaDoc param = adviceMethod.getParameterTypes()[currentMatch.currentParam];
273
274          if (!returnType.equals(param))
275          {
276             int match2 = subClassMatch(returnType, param);
277             if (match2 < 0)
278             {
279                return null;
280             }
281             
282             currentMatch.returnOrThrowingMatchDegree = (currentMatch.returnOrThrowingMatchDegree + match2)/2;
283          }
284          currentMatch.currentParam++;
285       }
286       
287       return currentMatch;
288    }
289    
290    private MatchData matchActualArgs(AdviceMethodProperties properties, Method JavaDoc adviceMethod, MatchData currentMatch, MatchData bestMatch)
291    {
292       int adviceParam = currentMatch.currentParam;
293       Class JavaDoc[] adviceParams = adviceMethod.getParameterTypes();
294       
295       Class JavaDoc[] targetParams = properties.getJoinpointParameters();
296       
297       if (adviceParams.length - currentMatch.currentParam > targetParams.length)
298       {
299          //The advice method takes more args than the target joinpoint itself!
300
return null;
301       }
302       
303       if (adviceParam == adviceParams.length)
304       {
305          //There are no more parameters, return without initialising the argsIndices
306
return nullOrCurrentMatch(currentMatch, bestMatch);
307       }
308       
309       currentMatch.argsIndices = new ArrayList JavaDoc();
310       for (int i = 0; i < targetParams.length && adviceParam < adviceParams.length; i++)
311       {
312          int match = matchClass(adviceParams[adviceParam], targetParams[i]);
313          if (match < 0)
314          {
315             continue;
316          }
317          else
318          {
319             adviceParam++;
320             currentMatch.argsIndices.add(new Integer JavaDoc(i));
321             currentMatch.argsDegreeSum += match;
322          }
323       }
324       
325       if (currentMatch.argsIndices.size() != adviceParams.length - currentMatch.currentParam)
326       {
327          return null;
328       }
329       
330       return nullOrCurrentMatch(currentMatch, bestMatch);
331    }
332    
333    /**
334     * Returns null if bestMatch was the best, otherwise returns currentMatch
335     */

336    private MatchData nullOrCurrentMatch(MatchData currentMatch, MatchData bestMatch)
337    {
338       if (currentMatch.compare(bestMatch, true) < 0)
339       {
340          return currentMatch;
341       }
342       
343       return null;
344    }
345    
346    private boolean isInvocation(Class JavaDoc clazz)
347    {
348       return Invocation.class.isAssignableFrom(clazz);
349    }
350    
351    private boolean isInfo(Class JavaDoc clazz)
352    {
353       return JoinPointInfo.class.isAssignableFrom(clazz);
354    }
355    
356    /**
357     * Checks if clazz is of type lookingFor. The returned "match degree" indicates how many superclasses/interfaces we
358     * had to go through until finding lookingFor
359     */

360    private int matchClass(Class JavaDoc clazz, Class JavaDoc lookingFor)
361    {
362       return matchClass(clazz, lookingFor, 0);
363    }
364
365    private int subClassMatch(Class JavaDoc clazz, Class JavaDoc superClass)
366    {
367       return matchClass(superClass, clazz);
368    }
369    
370    private int matchClass(Class JavaDoc wanted, Class JavaDoc candidate, int matchDegree)
371    {
372       if (candidate == null) return -1;
373       if (candidate.equals(wanted))
374       {
375          return matchDegree;
376       }
377
378       matchDegree++;
379       
380       Class JavaDoc[] interfaces = candidate.getInterfaces();
381       for (int i = 0 ; i < interfaces.length ; i++)
382       {
383          if (matchClass(wanted, interfaces[i], matchDegree) > 0) return matchDegree;
384       }
385       
386       if (matchClass(wanted, candidate.getSuperclass(), matchDegree) > 0) return matchDegree;
387       
388       return -1;
389    }
390    
391 }
392
393 class MatchData
394 {
395    Method JavaDoc method;
396    int invocationMatchDegree = -1;
397    int returnOrThrowingMatchDegree = -1;
398    ArrayList JavaDoc argsIndices = null;
399    int argsDegreeSum = 0;
400    
401    int currentParam;
402    
403    MatchData()
404    {
405    }
406    
407    MatchData(Method JavaDoc method)
408    {
409       this.method = method;
410    }
411    
412    MatchData(Method JavaDoc method, int currentParam, int firstParamMatchDegree)
413    {
414       this.method = method;
415       this.currentParam = currentParam;
416       this.invocationMatchDegree = firstParamMatchDegree;
417    }
418    
419    public String JavaDoc toString()
420    {
421       return "MatchData[invMatch="+ invocationMatchDegree + "rtnMatch=" + returnOrThrowingMatchDegree + "args=" + ((argsIndices != null )? argsIndices.size() : 0) + "sum=" + argsDegreeSum + "]";
422    }
423    
424    /**
425     * Return -1 if mine is a better match, 1 if other is, and 0 if equal
426     */

427    int compare(MatchData other, boolean checkArgs)
428    {
429       int invMatch = compareMatchDegrees(this.invocationMatchDegree, other.invocationMatchDegree);
430       if (invMatch != 0)
431       {
432          return invMatch;
433       }
434       
435       int retMatch = compareMatchDegrees(this.returnOrThrowingMatchDegree, other.returnOrThrowingMatchDegree);
436       if (retMatch != 0)
437       {
438          return retMatch;
439       }
440       
441       if (checkArgs)
442       {
443          if (this.argsIndices == null && other.argsIndices == null) return 0;
444          else if (this.argsIndices != null && other.argsIndices == null) return -1;
445          else if (this.argsIndices == null && other.argsIndices != null) return 1;
446          else
447          {
448             if (this.argsIndices.size() > other.argsIndices.size()) return -1;
449             else if (this.argsIndices.size() < other.argsIndices.size()) return 1;
450             else
451             {
452                return this.argsDegreeSum < other.argsDegreeSum ? -1 : 1;
453             }
454          }
455       }
456       
457       return 0;
458    }
459    
460    /**
461     * Return -1 if mine is a better match, 1 if other is, and 0 if equal
462     */

463    int compareMatchDegrees(int mine, int other)
464    {
465       if (mine < 0 && other >= 0)
466       {
467          return 1;
468       }
469       else if (mine >= 0 && other < 0)
470       {
471          return -1;
472       }
473       else if (mine >= 0 && other >= 0 && mine != other)
474       {
475          return mine > other ? -1 : 1;
476       }
477       
478       return 0;//They are equal
479
}
480    
481 }
482
Popular Tags