KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jboss > aop > HotSwapStrategy


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;
23
24 import gnu.trove.TLongObjectHashMap;
25
26 import java.lang.reflect.Constructor JavaDoc;
27 import java.util.ArrayList JavaDoc;
28 import java.util.Collection JavaDoc;
29 import java.util.Iterator JavaDoc;
30 import java.util.WeakHashMap JavaDoc;
31
32 import javassist.ClassPool;
33 import javassist.CodeConverter;
34 import javassist.CtClass;
35 import javassist.CtField;
36 import javassist.NotFoundException;
37
38 import org.jboss.aop.advice.Interceptor;
39 import org.jboss.aop.instrument.DynamicTransformationObserver;
40 import org.jboss.aop.instrument.HotSwapper;
41 import org.jboss.aop.instrument.Instrumentor;
42 import org.jboss.aop.instrument.InstrumentorFactory;
43 import org.jboss.aop.instrument.JoinpointClassifier;
44 import org.jboss.aop.instrument.JoinpointFullClassifier;
45 import org.jboss.aop.instrument.JoinpointStatusUpdate;
46
47 /**
48  * Dynamic AOP strategy that hot swaps a class code.
49  * A class code is hot swapped whenever one or more joinpoints
50  * become advised or unadvised through dynamic aop operations.
51  * @see org.jboss.aop.DynamicAOPStrategy
52  * @author Flavia Rainone
53  */

54 public class HotSwapStrategy implements DynamicAOPStrategy
55 {
56    private HotSwapper hotSwapper;
57    private Collection JavaDoc joinpointUpdates;
58    private Instrumentor instrumentor;
59
60    /**
61     * Constructor.
62     * @param hotSwapper a hot swapper is necessary for this strategy.
63     */

64    public HotSwapStrategy(HotSwapper hotSwapper)
65    {
66       this.hotSwapper = hotSwapper;
67       this.joinpointUpdates = new ArrayList JavaDoc();
68       this.instrumentor = InstrumentorFactory.getInstrumentor(AspectManager.instance(), getJoinpointClassifier());
69    }
70    
71    /**
72     * Notifies the strategy that one or more interceptor chains in the
73     * system may have been updated. This method hot swaps all classes
74     * whose joinpoints became advised or unadvised due to recent dynamic
75     * aop operations.
76     * @see org.jboss.aop.DynamicAOPStrategy#interceptorChainsUpdated()
77     */

78    public synchronized void interceptorChainsUpdated()
79    {
80       synchronized(joinpointUpdates)
81       {
82          if (!joinpointUpdates.isEmpty())
83          {
84             instrumentor.interceptorChainsUpdated(new ArrayList JavaDoc(joinpointUpdates), hotSwapper);
85             joinpointUpdates.clear();
86          }
87       }
88    }
89    
90    /**
91     * Returns a <code>org.jboss.aop.instrument.JoinpointFullClassifier</code>
92     * instance.
93     * @see org.jboss.aop.DynamicAOPStrategy#getJoinpointClassifier()
94     */

95    public JoinpointClassifier getJoinpointClassifier()
96    {
97       return new JoinpointFullClassifier();
98    }
99
100    /**
101     * Notifies the dynamic strategy that some joinpoints were wrapped due to only
102     * dynamic aop operations.
103     * @param clazz class containing the joinpoints.
104     * @param constructor <code>true</code> if the constructor execution was wrapped due
105     * to only dynamic aop operations.
106     * @param fieldRead collection of <code>CtField</code> instances whose read joinpoint
107     * was wrapped due to only dynamic aop operations.
108     * @param fieldWrite collection of <code>CtField</code> instances whose write joinpoint
109     * was wrapped due to only dynamic aop operations.
110     */

111    
112    /**
113     * Returns a dynamic transformation observer for <code>clazz</code>.
114     * @see org.jboss.aop.DynamicAOPStrategy#getDynamicTransformationObserver
115     */

116    public DynamicTransformationObserver getDynamicTransformationObserver(CtClass clazz)
117    {
118       return new DynamicTransformationTracker(clazz);
119    }
120    
121    /**
122     * Returns an interceptor chain observer for <code>clazz</code>.
123     * @see org.jboss.aop.DynamicAOPStrategy#getInterceptorChainObserver
124     */

125    public InterceptorChainObserver getInterceptorChainObserver(Class JavaDoc clazz)
126    {
127       ClassPool classPool = AspectManager.instance().findClassPool(clazz.getClassLoader());
128       CtClass ctClass = null;
129       try
130       {
131          ctClass = classPool.get(clazz.getName());
132       } catch(NotFoundException e)
133       {
134          throw new RuntimeException JavaDoc("Class " + clazz.getName() + " was not found at class pool.");
135       }
136       return new JoinpointStatusUpdater(ctClass);
137    }
138    
139
140    /**
141     * Register a joinpoint status update. This registered update will be used in <code>
142     * interceptorChainsUpdated</code> to notify <code>org.jboss.aop.Instrumentor</code>
143     * of the interceptor chains changes.
144     * @param update the update to be registered.
145     */

146    private synchronized void newJoinpointUpdate(JoinpointStatusUpdate update)
147    {
148       synchronized(this.joinpointUpdates)
149       {
150          this.joinpointUpdates.add(update);
151       }
152    }
153
154    /**
155     * Implements the <code>org.jboss.aop.instrument.DynamicTransformationObserver</code>
156     * interface in order to update callers code when the callee is instrumented due
157     * to only dynamicaly added bindings.
158     * This instance is associated with a class, and should be used to observe the
159     * transformation process of this class.
160     */

161    private class DynamicTransformationTracker implements DynamicTransformationObserver
162    {
163       private CtClass clazz;
164       private Collection JavaDoc fieldReads;
165       private Collection JavaDoc fieldWrites;
166       private boolean constructor;
167
168       /**
169        * Constructor.
170        * @param clazz to class whose transformation process this object will observe.
171        */

172       public DynamicTransformationTracker(CtClass clazz)
173       {
174          this.clazz = clazz;
175          this.fieldReads = new ArrayList JavaDoc();
176          this.fieldWrites = new ArrayList JavaDoc();
177          this.constructor = false;
178       }
179       
180       /**
181        * Notifies that during the transformation of <code>clazz</code>, the <code>
182        * field</code> read joinpoint was wrapped due only to bindings added
183        * dynamicaly.
184        * @param field the field whose read joinpoint was dynamicaly wrapped.
185        * @see DynamicTransformationObserver#fieldReadDynamicalyWrapped(CtField)
186        */

187       public void fieldReadDynamicalyWrapped(CtField field)
188       {
189          this.fieldReads.add(field);
190       }
191
192       /**
193        * Notifies that during the transformation of <code>clazz</code>, the <code>
194        * field</code> read joinpoint was wrapped due only to bindings added
195        * dynamicaly.
196        * @param field the field whose read joinpoint was dynamicaly wrapped.
197        * @see DynamicTransformationObserver#fieldWriteDynamicalyWrapped(CtField)
198        */

199       public void fieldWriteDynamicalyWrapped(CtField field)
200       {
201          this.fieldWrites.add(field);
202       }
203
204       /**
205        * Notifies that during the transformation of <code>clazz</code>, the <code>
206        * constructor</code> execution joinpoints were wrapped due only to bindings added
207        * dynamicaly.
208        * @see DynamicTransformationObserver#constructorDynamicalyWrapped(CtField)
209        */

210       public void constructorDynamicalyWrapped()
211       {
212        this.constructor = true;
213       }
214       
215       /**
216        * Notifies that the transformation of <code>clazz</code> is finished.
217        */

218       public void transformationFinished(CtClass clazz, CodeConverter converter)
219       {
220          if (constructor || !fieldReads.isEmpty() || !fieldWrites.isEmpty())
221          {
222             instrumentor.convertProcessedClasses(hotSwapper, clazz, fieldReads, fieldWrites, constructor);
223          }
224       }
225    }
226    
227    /**
228     * Implements the <code>org.jboss.aop.InteceptorChainObserver</code>
229     * interface in order to register joinpoint status updates in the
230     * <code>HotSwapStrategy</code>.
231     */

232    private class JoinpointStatusUpdater implements InterceptorChainObserver
233    {
234       private JoinpointStatusUpdate.ClassJoinpoints newlyAdvised;
235       private JoinpointStatusUpdate.ClassJoinpoints newlyUnadvised;
236       
237       private int instanceInterceptors;
238       private WeakHashMap JavaDoc instanceAdvisors;
239       
240       private CtClass clazz;
241       private int fields;
242       private int constructors;
243       private int methods;
244       private Interceptor[][] fieldReadInterceptors;
245       private Interceptor[][] fieldWriteInterceptors;
246       private Interceptor[][] constructorInterceptors;
247       private TLongObjectHashMap methodInterceptors;
248       private int[] constructorIndexMap;
249       
250       /**
251        * Constructor.
252        * @param clazz the class associated to this observer.
253        */

254       public JoinpointStatusUpdater(CtClass clazz)
255       {
256          this.clazz = clazz;
257          this.instanceAdvisors = new WeakHashMap JavaDoc();
258       }
259       
260       /**
261        * This method must be called before any other notification method is invoked.
262        * @see org.jboss.aop.InterceptorChainObserver#initialInterceptorChains(Interceptor[][], Interceptor[][], Interceptor[][], TLongObjectHashMap)
263        */

264       public synchronized void initialInterceptorChains(Class JavaDoc reflectionClass, Interceptor[][] fieldReadInterceptors, Interceptor[][] fieldWriteInterceptors,
265             Interceptor[][] constructorInterceptors, TLongObjectHashMap methodInterceptors)
266       {
267          Constructor JavaDoc[] declaredConstructors = reflectionClass.getDeclaredConstructors();
268          constructorIndexMap = new int[declaredConstructors.length];
269          Collection JavaDoc constructorList = new ArrayList JavaDoc();
270          int javassistIndex = 0;
271          for (int reflectionIndex = 0; reflectionIndex < declaredConstructors.length; reflectionIndex++)
272          {
273             Class JavaDoc[] params = declaredConstructors[reflectionIndex].getParameterTypes();
274             if (params.length > 0 && params[params.length-1].getName().equals("javassist.runtime.Inner"))
275             {
276                constructorIndexMap[reflectionIndex] = -1;
277             }
278             else
279             {
280                
281                constructorIndexMap[reflectionIndex] = javassistIndex ++ ;
282             }
283          }
284          
285          this.fieldReadInterceptors = fieldReadInterceptors;
286          this.fieldWriteInterceptors = fieldWriteInterceptors;
287          this.constructorInterceptors = constructorInterceptors;
288          this.methodInterceptors = methodInterceptors;
289          this.fields = fieldReadInterceptors.length;
290          this.constructors = constructorInterceptors.length;
291          this.methods = methodInterceptors.size();
292          this.newlyAdvised = new JoinpointStatusUpdate.ClassJoinpoints(fields, constructors, methods);
293          this.newlyUnadvised = new JoinpointStatusUpdate.ClassJoinpoints(fields, constructors, methods);
294       }
295       
296       /**
297        * Notification method.
298        * @see InterceptorChainObserver#interceptorChainsUpdated(Interceptor[][], Interceptor[][], Interceptor[][], TLongObjectHashMap)
299        */

300       public synchronized void interceptorChainsUpdated(Interceptor[][] newFieldReadInterceptors, Interceptor[][] newFieldWriteInterceptors,
301             Interceptor[][] newConstructorInterceptors, TLongObjectHashMap newMethodInterceptors)
302       {
303          if (instanceInterceptors == 0)
304          {
305             long[] methodKeys = methodInterceptors.keys();
306             for (int i = 0; i < methodKeys.length; i++)
307             {
308                long key = methodKeys[i];
309                MethodInfo oldMethodInfo = (MethodInfo) methodInterceptors.get(key);
310                MethodInfo newMethodInfo = (MethodInfo) newMethodInterceptors.get(key);
311                if (oldMethodInfo.getInterceptorChain().isEmpty() && !newMethodInfo.getInterceptorChain().isEmpty())
312                {
313                   newlyAdvised.methodExecutions.add(newMethodInfo);
314                }
315                else if (!oldMethodInfo.getInterceptorChain().isEmpty() && newMethodInfo.getInterceptorChain().isEmpty())
316                {
317                   newlyUnadvised.methodExecutions.add(newMethodInfo);
318                }
319             }
320             fillNewStateCollections(fieldReadInterceptors, newFieldReadInterceptors, newlyAdvised.fieldReads, newlyUnadvised.fieldReads, null);
321             fillNewStateCollections(fieldWriteInterceptors, newFieldWriteInterceptors, newlyAdvised.fieldWrites, newlyUnadvised.fieldWrites, null);
322             fillNewStateCollections(constructorInterceptors, newConstructorInterceptors, newlyAdvised.constructorExecutions, newlyUnadvised.constructorExecutions, this.constructorIndexMap);
323             newJoinpointUpdate(this.getJoinpointStatusUpdate());
324          }
325          this.fieldReadInterceptors = newFieldReadInterceptors;
326          this.fieldWriteInterceptors = newFieldWriteInterceptors;
327          this.constructorInterceptors = newConstructorInterceptors;
328          this.methodInterceptors = newMethodInterceptors;
329       }
330       
331       /**
332        * Notification method.
333        * @see InterceptorChainObserver#instanceInterceptorAdded(InstanceAdvisor)
334        */

335       public synchronized void instanceInterceptorAdded(InstanceAdvisor instanceAdvisor)
336       {
337          this.instanceInterceptorsAdded(instanceAdvisor, 1);
338       }
339
340       /**
341        * Notification method.
342        * @see InterceptorChainObserver#instanceInterceptorsAdded(InstanceAdvisor, int)
343        */

344       public synchronized void instanceInterceptorsAdded(InstanceAdvisor instanceAdvisor, int howMany)
345       {
346          updateInstanceInterceptorsTable(instanceAdvisor, howMany);
347          updateAdvisenessStatus(this.newlyAdvised);
348          this.instanceInterceptors += howMany;
349          HotSwapStrategy.this.interceptorChainsUpdated();
350       }
351       
352       /**
353        * Notification method.
354        * @see InterceptorChainObserver#instanceInterceptorRemoved(InstanceAdvisor)
355        */

356       public synchronized void instanceInterceptorRemoved(InstanceAdvisor instanceAdvisor)
357       {
358          this.instanceInterceptorsRemoved(instanceAdvisor, 1);
359       }
360       
361       /**
362        * Notification method.
363        * @see InterceptorChainObserver#instanceInterceptorsRemoved(InstanceAdvisor, int)
364        */

365       public synchronized void instanceInterceptorsRemoved(InstanceAdvisor instanceAdvisor, int howMany)
366       {
367          updateInstanceInterceptorsTable(instanceAdvisor, -howMany);
368          this.instanceInterceptors -= howMany;
369          updateAdvisenessStatus(this.newlyUnadvised);
370          HotSwapStrategy.this.interceptorChainsUpdated();
371       }
372       
373       /**
374        * Notification method.
375        * @see InterceptorChainObserver#allInstanceInterceptorsRemoved(InstanceAdvisor)
376        */

377       public synchronized void allInstanceInterceptorsRemoved(InstanceAdvisor instanceAdvisor)
378       {
379          if (this.instanceAdvisors.containsKey(instanceAdvisor))
380          {
381             this.instanceAdvisors.remove(instanceAdvisor);
382          }
383          if (this.instanceInterceptors == 0)
384             return;
385          this.instanceInterceptors = 0;
386          for (Iterator JavaDoc iterator = instanceAdvisors.values().iterator(); iterator.hasNext(); )
387          {
388             Integer JavaDoc interceptors = (Integer JavaDoc) iterator.next();
389             instanceInterceptors += interceptors.intValue();
390          }
391          if (this.instanceInterceptors > 0)
392             return;
393          updateAdvisenessStatus(this.newlyUnadvised);
394          HotSwapStrategy.this.interceptorChainsUpdated();
395       }
396
397       /**
398        * Gets the joinpoint status update containing all the observed interceptor
399        * chain changes information.
400        * @return the joinpoint status update.
401        */

402       private JoinpointStatusUpdate getJoinpointStatusUpdate() {
403          JoinpointStatusUpdate update = new JoinpointStatusUpdate();
404          update.clazz = this.clazz;
405          update.newlyAdvisedJoinpoints = this.newlyAdvised;
406          update.newlyUnadvisedJoinpoints = this.newlyUnadvised;
407          this.newlyAdvised = new JoinpointStatusUpdate.ClassJoinpoints(this.fields, this.constructors, this.methods);
408          this.newlyUnadvised = new JoinpointStatusUpdate.ClassJoinpoints(this.fields, this.constructors, this.methods);
409          return update;
410       }
411
412       
413       /**
414        * Compares the two interceptors chains and adds the newly advised joinpoints
415        * to <code>newlyAdvised</code> and the newly unadvised to <code>newlyUnadvised</code>.
416        * @param interceptors old interceptors chain.
417        * @param newInterceptors new interceptors chain.
418        * @param newlyAdvised collection to which the newly advised joinpoints will be added.
419        * @param newlyAdvised collection to which the newly unadvised joinpoints will be added.
420        */

421       private void fillNewStateCollections(Interceptor[][] interceptors, Interceptor[][] newInterceptors,
422             Collection JavaDoc newlyAdvised, Collection JavaDoc newlyUnadvised, int[] indexMap)
423       {
424          if (instanceInterceptors > 0)
425             return;
426          for (int i = 0; i < interceptors.length; i++) {
427             Interceptor[] oldInterceptorsChain = interceptors[i];
428             Interceptor[] newInterceptorsChain = newInterceptors[i];
429             boolean interceptedBefore = oldInterceptorsChain != null && oldInterceptorsChain.length > 0;
430             boolean interceptedNow = newInterceptorsChain != null && newInterceptorsChain.length > 0;
431             if (!interceptedBefore && interceptedNow)
432             {
433                if (indexMap != null)
434                {
435                   if(indexMap[i] != -1)
436                   {
437                      newlyAdvised.add(new Integer JavaDoc(indexMap[i]));
438                   }
439                }
440                else
441                {
442                   newlyAdvised.add(new Integer JavaDoc(i));
443                }
444
445             }
446             else if (interceptedBefore && !interceptedNow)
447             {
448                if (indexMap != null)
449                {
450                   if(indexMap[i] != -1)
451                   {
452                      newlyUnadvised.add(new Integer JavaDoc(indexMap[i]));
453                   }
454                }
455                else
456                {
457                   newlyUnadvised.add(new Integer JavaDoc(i));
458                }
459             }
460          }
461       }
462
463       /**
464        * Updates the instance interceptor table to contain the <code>interceptorsAdded</code> information.
465        * @param instanceAdvisor the <code>org.jboss.aop.InstanceAdvisor</code> whose interceptors chain
466        * size is being updated.
467        * @param interceptorsAdded the number of interceptors added (or removed, if <code>interceptorsAdded</code>
468        * is negative) to <code>instanceAdvisor</code>.
469        */

470       private void updateInstanceInterceptorsTable(InstanceAdvisor instanceAdvisor, int interceptorsAdded)
471       {
472          if (this.instanceAdvisors.containsKey(instanceAdvisor))
473          {
474             Integer JavaDoc interceptors = (Integer JavaDoc) instanceAdvisors.get(instanceAdvisor);
475             instanceAdvisors.put(instanceAdvisor, new Integer JavaDoc(interceptors.intValue() + interceptorsAdded));
476          }
477          else
478          {
479             instanceAdvisors.put(instanceAdvisor, new Integer JavaDoc(interceptorsAdded));
480          }
481       }
482
483       /**
484        * If there are no instance interceptors to any instance of the class associated to this observer,
485        * this method adds all unadvised joinpoints to <code>joinpoints</code> and records the joinpoint
486        * status update in the <code>HotSwapStrategy</code>.
487        * @param joinpoints <code>ClassJoinpoints</code> to which the unadvised joinpoints will be added.
488        */

489       private void updateAdvisenessStatus(JoinpointStatusUpdate.ClassJoinpoints joinpoints)
490       {
491          if (this.instanceInterceptors == 0)
492          {
493             long[] methodKeys = this.methodInterceptors.keys();
494             for (int i = 0; i < methodKeys.length; i++)
495             {
496                long key = methodKeys[i];
497                MethodInfo methodInfo = (MethodInfo) this.methodInterceptors.get(key);
498                if (methodInfo.getInterceptorChain().isEmpty())
499                {
500                   joinpoints.methodExecutions.add(methodInfo);
501                }
502             }
503             findUnadvisedJoinpoints(this.fieldReadInterceptors, joinpoints.fieldReads);
504             findUnadvisedJoinpoints(this.fieldWriteInterceptors, joinpoints.fieldWrites);
505             findUnadvisedJoinpoints(this.constructorInterceptors, joinpoints.constructorExecutions);
506             newJoinpointUpdate(getJoinpointStatusUpdate());
507          }
508       }
509       
510       /**
511        * Finds all unadvised joinponts.
512        * @param interceptors the interceptors chains applied to the joinponts.
513        * @param joinpointsFound the collection to which the found joinpoints will be added.
514        */

515       private void findUnadvisedJoinpoints(Interceptor[][] interceptors, Collection JavaDoc joinpointsFound)
516       {
517          for (int i = 0; i < interceptors.length; i++)
518          {
519             if (interceptors[i] == null || interceptors[i].length == 0)
520             {
521                joinpointsFound.add(new Integer JavaDoc(i));
522             }
523          }
524       }
525    }
526 }
Popular Tags