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.aspect.management;
9   
10  import org.codehaus.aspectwerkz.aspect.DefaultMixinFactory;
11  import org.codehaus.aspectwerkz.aspect.MixinFactory;
12  import org.codehaus.aspectwerkz.util.ContextClassLoader;
13  import org.codehaus.aspectwerkz.DeploymentModel;
14  import org.codehaus.aspectwerkz.util.ContextClassLoader;
15  import org.codehaus.aspectwerkz.definition.SystemDefinition;
16  import org.codehaus.aspectwerkz.definition.SystemDefinitionContainer;
17  import org.codehaus.aspectwerkz.definition.MixinDefinition;
18  import org.codehaus.aspectwerkz.exception.DefinitionException;
19  import org.codehaus.aspectwerkz.DeploymentModel;
20  
21  import java.util.*;
22  import java.lang.reflect.Constructor;
23  import java.lang.reflect.InvocationTargetException;
24  
25  /***
26   * Manages the mixins, registry for the mixin factories (one factory per mixin type).
27   *
28   * @author <a href="mailto:jboner@codehaus.org">Jonas BonŽr </a>
29   * @author <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a>
30   */
31  public class Mixins {
32  
33      /***
34       * The default mixin factory class.
35       */
36      public static final String DEFAULT_MIXIN_FACTORY = DefaultMixinFactory.class.getName();
37  
38      /***
39       * Map with all the mixin factories mapped to the mixin class
40       */
41      private static final Map MIXIN_FACTORIES = new WeakHashMap();
42  
43      /***
44       * Returns the mixin factory for the mixin with the given name.
45       *
46       * @param mixinClass the class of the mixin
47       * @param mixinCalledFromLoader
48       * @return the factory, put in cache based on mixin class as a key
49       */
50      public static MixinFactory getFactory(final Class mixinClass, final ClassLoader mixinCalledFromLoader) {
51          synchronized (MIXIN_FACTORIES) {
52              MixinFactory factory = (MixinFactory) MIXIN_FACTORIES.get(mixinClass);
53              if (factory == null) {
54                  factory = createMixinFactory(mixinClass, mixinCalledFromLoader);
55                  //FIXME by using a lookup by uuid/aspectNickName
56                  // right now broken since we have 1 container per mixin CLASS while the definition
57                  // does allow for some mix (several mixin, several container, same mixin class)
58                  MIXIN_FACTORIES.put(mixinClass, factory);
59              }
60              return factory;
61          }
62      }
63  
64      /***
65       * Returns the per JVM mixin instance for the mixin with the given name
66       *
67       * @param name        the name of the mixin
68       * @param loader    target class classloader
69       * @return the per jvm mixin instance
70       */
71      public static Object mixinOf(final String name, ClassLoader loader) {
72          try {
73              Class mixinClass = Class.forName(name, false, loader);
74              return mixinOf(mixinClass);
75          } catch (ClassNotFoundException e) {
76              throw new RuntimeException("could not load mixin " + name + " from " + loader);
77          }
78      }
79  
80      /***
81       * Returns the per jvm mixin instance for the mixin with the given implementation class
82       * deployed using the perJVM model.
83       *
84       * @param mixinClass  the name of the mixin
85       * @return the per jvm mixin instance
86       */
87      public static Object mixinOf(final Class mixinClass) {
88          return getFactory(mixinClass, mixinClass.getClassLoader()).mixinOf();
89      }
90  
91      /***
92       * Returns the per class mixin instance for the mixin with the given name for the perClass model
93       *
94       * @param name        the name of the mixin
95       * @param targetClass the targetClass class
96       * @return the per class mixin instance
97       */
98      public static Object mixinOf(final String name, final Class targetClass) {
99          try {
100             Class mixinClass = Class.forName(name, false, targetClass.getClassLoader());
101             return mixinOf(mixinClass, targetClass);
102         } catch (ClassNotFoundException e) {
103             throw new RuntimeException("could not load mixin " + name + " from " + targetClass.getClassLoader());
104         }
105     }
106 
107     /***
108      * Returns the per class mixin instance for the mixin with the given implemnentation class
109      * deployed using the perClass model.
110      *
111      * @param mixinClass  the name of the mixin
112      * @param targetClass the targetClass class
113      * @return the per class mixin instance
114      */
115     public static Object mixinOf(final Class mixinClass, final Class targetClass) {
116         return getFactory(mixinClass, targetClass.getClassLoader()).mixinOf(targetClass);
117     }
118 
119     /***
120      * Returns the per targetClass instance mixin instance for the mixin with the given name for the perInstance model.
121      *
122      * @param name           the name of the mixin
123      * @param targetInstance the targetClass instance, can be null (static method, ctor call)
124      * @return the per instance mixin instance, fallback on perClass if targetInstance is null
125      */
126     public static Object mixinOf(final String name, final Object targetInstance) {
127         try {
128             Class mixinClass = Class.forName(name, false, targetInstance.getClass().getClassLoader());
129             return mixinOf(mixinClass, targetInstance);
130         } catch (ClassNotFoundException e) {
131             throw new RuntimeException(
132                     "could not load mixin " + name + " from " + targetInstance.getClass().getClassLoader()
133             );
134         }
135     }
136 
137     /***
138      * Returns the per class mixin instance for the mixin with the given implemnentation class
139      * deployed using the perClass model.
140      *
141      * @param mixinClass     the name of the mixin
142      * @param targetInstance the targetClass instance, can be null
143      * @return the per targetClass instance mixin instance, fallback to perClass if targetInstance is null
144      */
145     public static Object mixinOf(final Class mixinClass, final Object targetInstance) {
146         //TODO WHAT IF targetInstance is null ? f.e. ITD static methods 
147         return getFactory(mixinClass, targetInstance.getClass().getClassLoader()).mixinOf(targetInstance);
148     }
149 
150     /***
151      * Creates a new mixin factory.
152      *
153      * @param mixinClass the mixin class
154      * @param mixinCalledFromLoader classloader of the target class advised by the mixin (app server packaging)
155      */
156     private static MixinFactory createMixinFactory(final Class mixinClass, final ClassLoader mixinCalledFromLoader) {
157         final MixinDefinition mixinDefinition = getMixinDefinition(mixinClass, mixinCalledFromLoader);
158 
159         String factoryClassName = mixinDefinition.getFactoryClassName();
160         try {
161             Class containerClass;
162             if (factoryClassName == null) {
163                 containerClass = ContextClassLoader.forName(mixinClass.getClassLoader(), DEFAULT_MIXIN_FACTORY);
164             } else {
165                 containerClass = ContextClassLoader.forName(mixinClass.getClassLoader(), factoryClassName);
166             }
167             Constructor constructor = containerClass.getConstructor(new Class[]{Class.class, DeploymentModel.class});
168             final MixinFactory factory = (MixinFactory) constructor.newInstance(
169                     new Object[]{mixinClass, mixinDefinition.getDeploymentModel()}
170             );
171             return factory;
172         } catch (InvocationTargetException e) {
173             throw new DefinitionException(e.getTargetException().toString());
174         } catch (NoSuchMethodException e) {
175             throw new DefinitionException(
176                     "mixin factory does not have a valid constructor ["
177                     + factoryClassName
178                     + "] need to have a signature like this [MyMixinFactory(Class mixin, DeploymentModel scope)]: "
179                     + e.toString()
180             );
181         } catch (Throwable e) {
182             StringBuffer cause = new StringBuffer();
183             cause.append("could not create mixin container using the implementation specified [");
184             cause.append(factoryClassName);
185             cause.append("] due to: ");
186             cause.append(e.toString());
187             throw new DefinitionException(cause.toString());
188         }
189     }
190 
191     /***
192      * Returns the parameter for a mixin based on the mixin implementation class and a classloader from
193      * where the mixin is visible (the classloader that owns the aop.xml with the "mixin" element, or a child of it).
194      * <p/>
195      * Note: the mixinClass classloader can be different, if you place the mixin in the system classpath, and reference
196      * it only from a deployed application.
197      * <p/>
198      * Note: you should not use a mixin more than once. Consider subclassing the mixin in this case. The returned parameters
199      * are the one from the first mixin found.
200      *
201      * @param mixinClass
202      * @return
203      */
204     public static Map getParameters(Class mixinClass, ClassLoader loader) {
205         MixinDefinition mixinDefinition = getMixinDefinition(mixinClass,  loader);
206         return mixinDefinition.getParameters();
207     }
208 
209     /***
210      * Lookups a mixin definition based on the mixin impl class and a classloader from where the mixin is
211      * visible. The given classloader can be different from the mixin class classloader.
212      *
213      * @param mixinClass
214      * @param visibleFrom
215      * @return
216      */
217     public static MixinDefinition getMixinDefinition(Class mixinClass, ClassLoader visibleFrom) {
218         MixinDefinition mixinDefinition = null;
219 
220         Set definitions = SystemDefinitionContainer.getDefinitionsFor(visibleFrom);
221         for (Iterator iterator = definitions.iterator(); iterator.hasNext() && mixinDefinition == null;) {
222             SystemDefinition systemDefinition = (SystemDefinition) iterator.next();
223             for (Iterator iterator1 = systemDefinition.getMixinDefinitions().iterator(); iterator1.hasNext();) {
224                 MixinDefinition mixinDef = (MixinDefinition) iterator1.next();
225                 if (mixinClass.getName().replace('/', '.').equals(mixinDef.getMixinImpl().getName())) {
226                     mixinDefinition = mixinDef;
227                     break;
228                 }
229             }
230         }
231         if (mixinDefinition == null) {
232             throw new DefinitionException("could not find definition for mixin: " + mixinClass.getName()
233                         + " (loader " + mixinClass.getClassLoader() + ")"
234                         + " from loader " + visibleFrom);
235         }
236         return mixinDefinition;
237     }
238 
239     /***
240      * Class is non-instantiable.
241      */
242     private Mixins() {
243     }
244 }