001: /*
002: * All content copyright (c) 2003-2006 Terracotta, Inc., except as may otherwise be noted in a separate copyright notice. All rights reserved.
003: */
004: package com.tc.aspectwerkz.aspect.management;
005:
006: import com.tc.aspectwerkz.DeploymentModel;
007: import com.tc.aspectwerkz.aspect.DefaultMixinFactory;
008: import com.tc.aspectwerkz.aspect.MixinFactory;
009: import com.tc.aspectwerkz.definition.MixinDefinition;
010: import com.tc.aspectwerkz.definition.SystemDefinition;
011: import com.tc.aspectwerkz.definition.SystemDefinitionContainer;
012: import com.tc.aspectwerkz.exception.DefinitionException;
013: import com.tc.aspectwerkz.util.ContextClassLoader;
014:
015: import java.util.*;
016: import java.lang.reflect.Constructor;
017: import java.lang.reflect.InvocationTargetException;
018:
019: /**
020: * Manages the mixins, registry for the mixin factories (one factory per mixin type).
021: *
022: * @author <a href="mailto:jboner@codehaus.org">Jonas BonŽr </a>
023: * @author <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a>
024: */
025: public class Mixins {
026:
027: /**
028: * The default mixin factory class.
029: */
030: public static final String DEFAULT_MIXIN_FACTORY = DefaultMixinFactory.class
031: .getName();
032:
033: /**
034: * Map with all the mixin factories mapped to the mixin class
035: */
036: private static final Map MIXIN_FACTORIES = new WeakHashMap();
037:
038: /**
039: * Returns the mixin factory for the mixin with the given name.
040: *
041: * @param mixinClass the class of the mixin
042: * @param mixinCalledFromLoader
043: * @return the factory, put in cache based on mixin class as a key
044: */
045: public static MixinFactory getFactory(final Class mixinClass,
046: final ClassLoader mixinCalledFromLoader) {
047: synchronized (MIXIN_FACTORIES) {
048: MixinFactory factory = (MixinFactory) MIXIN_FACTORIES
049: .get(mixinClass);
050: if (factory == null) {
051: factory = createMixinFactory(mixinClass,
052: mixinCalledFromLoader);
053: //FIXME by using a lookup by uuid/aspectNickName
054: // right now broken since we have 1 container per mixin CLASS while the definition
055: // does allow for some mix (several mixin, several container, same mixin class)
056: MIXIN_FACTORIES.put(mixinClass, factory);
057: }
058: return factory;
059: }
060: }
061:
062: /**
063: * Returns the per JVM mixin instance for the mixin with the given name
064: *
065: * @param name the name of the mixin
066: * @param loader target class classloader
067: * @return the per jvm mixin instance
068: */
069: public static Object mixinOf(final String name, ClassLoader loader) {
070: try {
071: Class mixinClass = Class.forName(name, false, loader);
072: return mixinOf(mixinClass);
073: } catch (ClassNotFoundException e) {
074: throw new RuntimeException("could not load mixin " + name
075: + " from " + loader);
076: }
077: }
078:
079: /**
080: * Returns the per jvm mixin instance for the mixin with the given implementation class
081: * deployed using the perJVM model.
082: *
083: * @param mixinClass the name of the mixin
084: * @return the per jvm mixin instance
085: */
086: public static Object mixinOf(final Class mixinClass) {
087: return getFactory(mixinClass, mixinClass.getClassLoader())
088: .mixinOf();
089: }
090:
091: /**
092: * Returns the per class mixin instance for the mixin with the given name for the perClass model
093: *
094: * @param name the name of the mixin
095: * @param targetClass the targetClass class
096: * @return the per class mixin instance
097: */
098: public static Object mixinOf(final String name,
099: final Class targetClass) {
100: try {
101: Class mixinClass = Class.forName(name, false, targetClass
102: .getClassLoader());
103: return mixinOf(mixinClass, targetClass);
104: } catch (ClassNotFoundException e) {
105: throw new RuntimeException("could not load mixin " + name
106: + " from " + targetClass.getClassLoader());
107: }
108: }
109:
110: /**
111: * Returns the per class mixin instance for the mixin with the given implemnentation class
112: * deployed using the perClass model.
113: *
114: * @param mixinClass the name of the mixin
115: * @param targetClass the targetClass class
116: * @return the per class mixin instance
117: */
118: public static Object mixinOf(final Class mixinClass,
119: final Class targetClass) {
120: return getFactory(mixinClass, targetClass.getClassLoader())
121: .mixinOf(targetClass);
122: }
123:
124: /**
125: * Returns the per targetClass instance mixin instance for the mixin with the given name for the perInstance model.
126: *
127: * @param name the name of the mixin
128: * @param targetInstance the targetClass instance, can be null (static method, ctor call)
129: * @return the per instance mixin instance, fallback on perClass if targetInstance is null
130: */
131: public static Object mixinOf(final String name,
132: final Object targetInstance) {
133: try {
134: Class mixinClass = Class.forName(name, false,
135: targetInstance.getClass().getClassLoader());
136: return mixinOf(mixinClass, targetInstance);
137: } catch (ClassNotFoundException e) {
138: throw new RuntimeException("could not load mixin " + name
139: + " from "
140: + targetInstance.getClass().getClassLoader());
141: }
142: }
143:
144: /**
145: * Returns the per class mixin instance for the mixin with the given implemnentation class
146: * deployed using the perClass model.
147: *
148: * @param mixinClass the name of the mixin
149: * @param targetInstance the targetClass instance, can be null
150: * @return the per targetClass instance mixin instance, fallback to perClass if targetInstance is null
151: */
152: public static Object mixinOf(final Class mixinClass,
153: final Object targetInstance) {
154: //TODO WHAT IF targetInstance is null ? f.e. ITD static methods
155: return getFactory(mixinClass,
156: targetInstance.getClass().getClassLoader()).mixinOf(
157: targetInstance);
158: }
159:
160: /**
161: * Creates a new mixin factory.
162: *
163: * @param mixinClass the mixin class
164: * @param mixinCalledFromLoader classloader of the target class advised by the mixin (app server packaging)
165: */
166: private static MixinFactory createMixinFactory(
167: final Class mixinClass,
168: final ClassLoader mixinCalledFromLoader) {
169: final MixinDefinition mixinDefinition = getMixinDefinition(
170: mixinClass, mixinCalledFromLoader);
171:
172: String factoryClassName = mixinDefinition.getFactoryClassName();
173: try {
174: Class containerClass;
175: if (factoryClassName == null) {
176: containerClass = ContextClassLoader.forName(mixinClass
177: .getClassLoader(), DEFAULT_MIXIN_FACTORY);
178: } else {
179: containerClass = ContextClassLoader.forName(mixinClass
180: .getClassLoader(), factoryClassName);
181: }
182: Constructor constructor = containerClass
183: .getConstructor(new Class[] { Class.class,
184: DeploymentModel.class });
185: final MixinFactory factory = (MixinFactory) constructor
186: .newInstance(new Object[] { mixinClass,
187: mixinDefinition.getDeploymentModel() });
188: return factory;
189: } catch (InvocationTargetException e) {
190: throw new DefinitionException(e.getTargetException()
191: .toString());
192: } catch (NoSuchMethodException e) {
193: throw new DefinitionException(
194: "mixin factory does not have a valid constructor ["
195: + factoryClassName
196: + "] need to have a signature like this [MyMixinFactory(Class mixin, DeploymentModel scope)]: "
197: + e.toString());
198: } catch (Throwable e) {
199: StringBuffer cause = new StringBuffer();
200: cause
201: .append("could not create mixin container using the implementation specified [");
202: cause.append(factoryClassName);
203: cause.append("] due to: ");
204: cause.append(e.toString());
205: throw new DefinitionException(cause.toString());
206: }
207: }
208:
209: /**
210: * Returns the parameter for a mixin based on the mixin implementation class and a classloader from
211: * where the mixin is visible (the classloader that owns the aop.xml with the "mixin" element, or a child of it).
212: * <p/>
213: * Note: the mixinClass classloader can be different, if you place the mixin in the system classpath, and reference
214: * it only from a deployed application.
215: * <p/>
216: * Note: you should not use a mixin more than once. Consider subclassing the mixin in this case. The returned parameters
217: * are the one from the first mixin found.
218: *
219: * @param mixinClass
220: * @return
221: */
222: public static Map getParameters(Class mixinClass, ClassLoader loader) {
223: MixinDefinition mixinDefinition = getMixinDefinition(
224: mixinClass, loader);
225: return mixinDefinition.getParameters();
226: }
227:
228: /**
229: * Lookups a mixin definition based on the mixin impl class and a classloader from where the mixin is
230: * visible. The given classloader can be different from the mixin class classloader.
231: *
232: * @param mixinClass
233: * @param visibleFrom
234: * @return
235: */
236: public static MixinDefinition getMixinDefinition(Class mixinClass,
237: ClassLoader visibleFrom) {
238: MixinDefinition mixinDefinition = null;
239:
240: Set definitions = SystemDefinitionContainer
241: .getDefinitionsFor(visibleFrom);
242: for (Iterator iterator = definitions.iterator(); iterator
243: .hasNext()
244: && mixinDefinition == null;) {
245: SystemDefinition systemDefinition = (SystemDefinition) iterator
246: .next();
247: for (Iterator iterator1 = systemDefinition
248: .getMixinDefinitions().iterator(); iterator1
249: .hasNext();) {
250: MixinDefinition mixinDef = (MixinDefinition) iterator1
251: .next();
252: if (mixinClass.getName().replace('/', '.').equals(
253: mixinDef.getMixinImpl().getName())) {
254: mixinDefinition = mixinDef;
255: break;
256: }
257: }
258: }
259: if (mixinDefinition == null) {
260: throw new DefinitionException(
261: "could not find definition for mixin: "
262: + mixinClass.getName() + " (loader "
263: + mixinClass.getClassLoader() + ")"
264: + " from loader " + visibleFrom);
265: }
266: return mixinDefinition;
267: }
268:
269: /**
270: * Class is non-instantiable.
271: */
272: private Mixins() {
273: }
274: }
|