    001    /*****************************************************************************
    002     * Copyright (C) NanoContainer Organization. All rights reserved.            *
    003     * ------------------------------------------------------------------------- *
    004     * The software in this package is published under the terms of the BSD      *
    005     * style license a copy of which has been included with this distribution in *
    006     * the LICENSE.txt file.                                                     *
    007     *                                                                           *
    008     * Original code by James Strachan                                           *
    009     *****************************************************************************/
    010
    011    package org.nanocontainer.aop.defaults;
    012
    013    import dynaop.Aspects;
    014    import dynaop.Pointcuts;
    015    import dynaop.ProxyFactory;
    016    import org.aopalliance.intercept.MethodInterceptor;
    017    import org.nanocontainer.aop.AspectablePicoContainer;
    018    import org.nanocontainer.aop.AspectsApplicator;
    019    import org.nanocontainer.aop.AspectsContainer;
    020    import org.nanocontainer.aop.AspectsManager;
    021    import org.nanocontainer.aop.ClassPointcut;
    022    import org.nanocontainer.aop.ComponentPointcut;
    023    import org.nanocontainer.aop.MethodPointcut;
    024    import org.nanocontainer.aop.dynaop.InstanceMixinFactory;
    025    import org.nanocontainer.script.NodeBuilderDecorationDelegate;
    026    import org.nanocontainer.script.NanoContainerMarkupException;
    027    import org.picocontainer.MutablePicoContainer;
    028    import org.picocontainer.defaults.ComponentAdapterFactory;
    029
    030    import java.util.List;
    031    import java.util.Map;
    032
    033    /**
    034     * @author Aslak Helles&oslash;y
    035     * @author Paul Hammant
    036     * @version $Revision: 3144 $
    037     */
    038    public class AopNodeBuilderDecorationDelegate implements NodeBuilderDecorationDelegate {
    039
    040        private final AspectsManager aspectsManager;
    041        private Object currentKey;
    042        private AspectablePicoContainer currentPico;
    043        private ClassPointcut currentClassCut;
    044        private MethodPointcut currentMethodCut;
    045
    046        public AopNodeBuilderDecorationDelegate(AspectsManager aspectsManager) {
    047            this.aspectsManager = aspectsManager;
    048        }
    049
    050        public ComponentAdapterFactory decorate(ComponentAdapterFactory componentAdapterFactory, Map attributes) {
    051            AspectsComponentAdapterFactory aspectsComponentAdapterFactory = createAdapterFactory(aspectsManager, componentAdapterFactory);
    052            return aspectsComponentAdapterFactory;
    053        }
    054
    055        public MutablePicoContainer decorate(MutablePicoContainer picoContainer) {
    056            currentPico = mixinAspectablePicoContainer(aspectsManager, picoContainer);
    057            return currentPico;
    058        }
    059
    060        public Object createNode(Object name, Map attributes, Object parentElement) {
    061            if (name.equals("aspect")) {
    062                return createAspectNode(attributes, name);
    063            } else if (name.equals("pointcut")) {
    064                return createPointcutNode(attributes, name);
    065            } else {
    066                throw new NanoContainerMarkupException("Don't know how to create a '" + name + "' child of a '" + parentElement.toString() + "' element");
    067            }
    068        }
    069
    070        private Object createPointcutNode(Map attributes, Object name) {
    071            currentClassCut = (ClassPointcut) attributes.remove("classCut");
    072            currentMethodCut = (MethodPointcut) attributes.remove("methodCut");
    073            return name;
    074        }
    075
    076        private Object createAspectNode(Map attributes, Object name) {
    077            ClassPointcut classCut = (ClassPointcut) attributes.remove("classCut");
    078            if(classCut != null) {
    079                currentClassCut = classCut;
    080            }
    081            MethodPointcut methodCut = (MethodPointcut) attributes.remove("methodCut");
    082            if(methodCut != null) {
    083                currentMethodCut = methodCut;
    084            }
    085
    086            MethodInterceptor interceptor = (MethodInterceptor) attributes.remove("interceptor");
    087            Object interceptorKey = attributes.remove("interceptorKey");
    088            Class mixinClass = (Class) attributes.remove("mixinClass");
    089            List mixinInterfaces = (List) attributes.remove("mixinInterfaces");
    090
    091            ComponentPointcut componentCut = (ComponentPointcut) attributes.remove("componentCut");
    092            if (componentCut == null && currentKey != null) {
    093                componentCut = currentPico.getPointcutsFactory().component(currentKey);
    094            }
    095
    096            if (interceptor != null || interceptorKey != null) {
    097                registerInterceptor(currentPico, currentClassCut, componentCut, currentMethodCut, interceptor, interceptorKey);
    098            } else if (mixinClass != null) {
    099                registerMixin(currentPico, currentClassCut, componentCut, toClassArray(mixinInterfaces), mixinClass);
    100            } else {
    101                throw new NanoContainerMarkupException("No advice specified - must specify one of interceptor, interceptorKey, mixinClass, or mixinKey");
    102            }
    103
    104            return name;
    105        }
    106
    107
    108        private AspectsComponentAdapterFactory createAdapterFactory(AspectsApplicator aspectsApplicator,
    109                                                                    ComponentAdapterFactory delegateAdapterFactory) {
    110            if (delegateAdapterFactory != null) {
    111                return new AspectsComponentAdapterFactory(aspectsApplicator, delegateAdapterFactory);
    112            } else {
    113                return new AspectsComponentAdapterFactory(aspectsApplicator);
    114            }
    115        }
    116
    117        private AspectablePicoContainer mixinAspectablePicoContainer(AspectsManager aspectsManager,
    118                                                                     MutablePicoContainer pico) {
    119            Aspects aspects = new Aspects();
    120            aspects.mixin(Pointcuts.ALL_CLASSES, new Class[]{AspectsContainer.class}, new InstanceMixinFactory(aspectsManager));
    121            aspects.interfaces(Pointcuts.ALL_CLASSES, new Class[]{AspectablePicoContainer.class});
    122            return (AspectablePicoContainer) ProxyFactory.getInstance(aspects).wrap(pico);
    123        }
    124
    125        private void registerInterceptor(AspectablePicoContainer pico, ClassPointcut classCut,
    126                                         ComponentPointcut componentCut, MethodPointcut methodCut, MethodInterceptor interceptor,
    127                                         Object interceptorKey) {
    128            // precondition:
    129            if (interceptor == null && interceptorKey == null) {
    130                throw new RuntimeException("assertion failed -- non-null interceptor or interceptorKey expected");
    131            }
    132
    133            // validate script:
    134            if (classCut == null && componentCut == null) {
    135                throw new NanoContainerMarkupException("currentClassCut or componentCut required for interceptor advice");
    136            }
    137            if (methodCut == null) {
    138                throw new NanoContainerMarkupException("currentMethodCut required for interceptor advice");
    139            }
    140
    141            if (classCut != null) {
    142                if (interceptor != null) {
    143                    pico.registerInterceptor(classCut, methodCut, interceptor);
    144                } else {
    145                    pico.registerInterceptor(classCut, methodCut, interceptorKey);
    146                }
    147            } else {
    148                if (interceptor != null) {
    149                    pico.registerInterceptor(componentCut, methodCut, interceptor);
    150                } else {
    151                    pico.registerInterceptor(componentCut, methodCut, interceptorKey);
    152                }
    153            }
    154        }
    155
    156        private void registerMixin(AspectablePicoContainer pico, ClassPointcut classCut, ComponentPointcut componentCut,
    157                                   Class[] mixinInterfaces, Class mixinClass) {
    158            // precondition:
    159            if (mixinClass == null) {
    160                throw new RuntimeException("assertion failed -- mixinClass required");
    161            }
    162
    163            // validate script:
    164            if (classCut == null && componentCut == null) {
    165                throw new NanoContainerMarkupException("currentClassCut or componentCut required for mixin advice");
    166            }
    167
    168            if (classCut != null) {
    169                if (mixinInterfaces != null) {
    170                    pico.registerMixin(classCut, mixinInterfaces, mixinClass);
    171                } else {
    172                    pico.registerMixin(classCut, mixinClass);
    173                }
    174            } else {
    175                if (mixinInterfaces != null) {
    176                    pico.registerMixin(componentCut, mixinInterfaces, mixinClass);
    177                } else {
    178                    pico.registerMixin(componentCut, mixinClass);
    179                }
    180            }
    181        }
    182
    183        private Class[] toClassArray(List l) {
    184            if (l == null) {
    185                return null;
    186            }
    187            return (Class[]) l.toArray(new Class[l.size()]);
    188        }
    189
    190        public void rememberComponentKey(Map attributes) {
    191            Object key = attributes.get("key");
    192            Object clazz = attributes.get("class");
    193            if (key != null) {
    194                currentKey = key;
    195            } else {
    196                currentKey = clazz;
    197            }
    198        }
    199    }