/************************************************************************************** * Copyright (c) Jonas Bon�r, Alexandre Vasseur. All rights reserved. * * http://aspectwerkz.codehaus.org * * ---------------------------------------------------------------------------------- * * The software in this package is published under the terms of the LGPL license * * a copy of which has been included with this distribution in the license.txt file. * **************************************************************************************/ package org.codehaus.aspectwerkz.aspect.management; import org.codehaus.aspectwerkz.aspect.DefaultMixinFactory; import org.codehaus.aspectwerkz.aspect.MixinFactory; import org.codehaus.aspectwerkz.util.ContextClassLoader; import org.codehaus.aspectwerkz.DeploymentModel; import org.codehaus.aspectwerkz.util.ContextClassLoader; import org.codehaus.aspectwerkz.definition.SystemDefinition; import org.codehaus.aspectwerkz.definition.SystemDefinitionContainer; import org.codehaus.aspectwerkz.definition.MixinDefinition; import org.codehaus.aspectwerkz.exception.DefinitionException; import org.codehaus.aspectwerkz.DeploymentModel; import java.util.*; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; /** * Manages the mixins, registry for the mixin factories (one factory per mixin type). * * @author <a href="mailto:jboner@codehaus.org">Jonas Bon�r </a> * @author <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a> */ public class Mixins { /** * The default mixin factory class. */ public static final String DEFAULT_MIXIN_FACTORY = DefaultMixinFactory.class.getName(); /** * Map with all the mixin factories mapped to the mixin class */ private static final Map MIXIN_FACTORIES = new WeakHashMap(); /** * Returns the mixin factory for the mixin with the given name. * * @param mixinClass the class of the mixin * @param mixinCalledFromLoader * @return the factory, put in cache based on mixin class as a key */ public static MixinFactory getFactory(final Class mixinClass, final ClassLoader mixinCalledFromLoader) { synchronized (MIXIN_FACTORIES) { MixinFactory factory = (MixinFactory) MIXIN_FACTORIES.get(mixinClass); if (factory == null) { factory = createMixinFactory(mixinClass, mixinCalledFromLoader); //FIXME by using a lookup by uuid/aspectNickName // right now broken since we have 1 container per mixin CLASS while the definition // does allow for some mix (several mixin, several container, same mixin class) MIXIN_FACTORIES.put(mixinClass, factory); } return factory; } } /** * Returns the per JVM mixin instance for the mixin with the given name * * @param name the name of the mixin * @param loader target class classloader * @return the per jvm mixin instance */ public static Object mixinOf(final String name, ClassLoader loader) { try { Class mixinClass = Class.forName(name, false, loader); return mixinOf(mixinClass); } catch (ClassNotFoundException e) { throw new RuntimeException("could not load mixin " + name + " from " + loader); } } /** * Returns the per jvm mixin instance for the mixin with the given implementation class * deployed using the perJVM model. * * @param mixinClass the name of the mixin * @return the per jvm mixin instance */ public static Object mixinOf(final Class mixinClass) { return getFactory(mixinClass, mixinClass.getClassLoader()).mixinOf(); } /** * Returns the per class mixin instance for the mixin with the given name for the perClass model * * @param name the name of the mixin * @param targetClass the targetClass class * @return the per class mixin instance */ public static Object mixinOf(final String name, final Class targetClass) { try { Class mixinClass = Class.forName(name, false, targetClass.getClassLoader()); return mixinOf(mixinClass, targetClass); } catch (ClassNotFoundException e) { throw new RuntimeException("could not load mixin " + name + " from " + targetClass.getClassLoader()); } } /** * Returns the per class mixin instance for the mixin with the given implemnentation class * deployed using the perClass model. * * @param mixinClass the name of the mixin * @param targetClass the targetClass class * @return the per class mixin instance */ public static Object mixinOf(final Class mixinClass, final Class targetClass) { return getFactory(mixinClass, targetClass.getClassLoader()).mixinOf(targetClass); } /** * Returns the per targetClass instance mixin instance for the mixin with the given name for the perInstance model. * * @param name the name of the mixin * @param targetInstance the targetClass instance, can be null (static method, ctor call) * @return the per instance mixin instance, fallback on perClass if targetInstance is null */ public static Object mixinOf(final String name, final Object targetInstance) { try { Class mixinClass = Class.forName(name, false, targetInstance.getClass().getClassLoader()); return mixinOf(mixinClass, targetInstance); } catch (ClassNotFoundException e) { throw new RuntimeException( "could not load mixin " + name + " from " + targetInstance.getClass().getClassLoader() ); } } /** * Returns the per class mixin instance for the mixin with the given implemnentation class * deployed using the perClass model. * * @param mixinClass the name of the mixin * @param targetInstance the targetClass instance, can be null * @return the per targetClass instance mixin instance, fallback to perClass if targetInstance is null */ public static Object mixinOf(final Class mixinClass, final Object targetInstance) { //TODO WHAT IF targetInstance is null ? f.e. ITD static methods return getFactory(mixinClass, targetInstance.getClass().getClassLoader()).mixinOf(targetInstance); } /** * Creates a new mixin factory. * * @param mixinClass the mixin class * @param mixinCalledFromLoader classloader of the target class advised by the mixin (app server packaging) */ private static MixinFactory createMixinFactory(final Class mixinClass, final ClassLoader mixinCalledFromLoader) { final MixinDefinition mixinDefinition = getMixinDefinition(mixinClass, mixinCalledFromLoader); String factoryClassName = mixinDefinition.getFactoryClassName(); try { Class containerClass; if (factoryClassName == null) { containerClass = ContextClassLoader.forName(mixinClass.getClassLoader(), DEFAULT_MIXIN_FACTORY); } else { containerClass = ContextClassLoader.forName(mixinClass.getClassLoader(), factoryClassName); } Constructor constructor = containerClass.getConstructor(new Class[]{Class.class, DeploymentModel.class}); final MixinFactory factory = (MixinFactory) constructor.newInstance( new Object[]{mixinClass, mixinDefinition.getDeploymentModel()} ); return factory; } catch (InvocationTargetException e) { throw new DefinitionException(e.getTargetException().toString()); } catch (NoSuchMethodException e) { throw new DefinitionException( "mixin factory does not have a valid constructor [" + factoryClassName + "] need to have a signature like this [MyMixinFactory(Class mixin, DeploymentModel scope)]: " + e.toString() ); } catch (Throwable e) { StringBuffer cause = new StringBuffer(); cause.append("could not create mixin container using the implementation specified ["); cause.append(factoryClassName); cause.append("] due to: "); cause.append(e.toString()); throw new DefinitionException(cause.toString()); } } /** * Returns the parameter for a mixin based on the mixin implementation class and a classloader from * where the mixin is visible (the classloader that owns the aop.xml with the "mixin" element, or a child of it). * <p/> * Note: the mixinClass classloader can be different, if you place the mixin in the system classpath, and reference * it only from a deployed application. * <p/> * Note: you should not use a mixin more than once. Consider subclassing the mixin in this case. The returned parameters * are the one from the first mixin found. * * @param mixinClass * @return */ public static Map getParameters(Class mixinClass, ClassLoader loader) { MixinDefinition mixinDefinition = getMixinDefinition(mixinClass, loader); return mixinDefinition.getParameters(); } /** * Lookups a mixin definition based on the mixin impl class and a classloader from where the mixin is * visible. The given classloader can be different from the mixin class classloader. * * @param mixinClass * @param visibleFrom * @return */ public static MixinDefinition getMixinDefinition(Class mixinClass, ClassLoader visibleFrom) { MixinDefinition mixinDefinition = null; Set definitions = SystemDefinitionContainer.getDefinitionsFor(visibleFrom); for (Iterator iterator = definitions.iterator(); iterator.hasNext() && mixinDefinition == null;) { SystemDefinition systemDefinition = (SystemDefinition) iterator.next(); for (Iterator iterator1 = systemDefinition.getMixinDefinitions().iterator(); iterator1.hasNext();) { MixinDefinition mixinDef = (MixinDefinition) iterator1.next(); if (mixinClass.getName().replace('/', '.').equals(mixinDef.getMixinImpl().getName())) { mixinDefinition = mixinDef; break; } } } if (mixinDefinition == null) { throw new DefinitionException("could not find definition for mixin: " + mixinClass.getName() + " (loader " + mixinClass.getClassLoader() + ")" + " from loader " + visibleFrom); } return mixinDefinition; } /** * Class is non-instantiable. */ private Mixins() { } }