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
56
57
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
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 }