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ø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 }