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: *****************************************************************************/package org.nanocontainer.aop.defaults;
010:
011: import dynaop.Aspects;
012: import dynaop.Pointcuts;
013: import dynaop.ProxyFactory;
014: import org.aopalliance.intercept.MethodInterceptor;
015: import org.nanocontainer.aop.AspectablePicoContainer;
016: import org.nanocontainer.aop.AspectsApplicator;
017: import org.nanocontainer.aop.AspectsContainer;
018: import org.nanocontainer.aop.AspectsManager;
019: import org.nanocontainer.aop.ClassPointcut;
020: import org.nanocontainer.aop.ComponentPointcut;
021: import org.nanocontainer.aop.MethodPointcut;
022: import org.nanocontainer.aop.dynaop.InstanceMixinFactory;
023: import org.nanocontainer.script.NodeBuilderDecorationDelegate;
024: import org.nanocontainer.script.NanoContainerMarkupException;
025: import org.picocontainer.MutablePicoContainer;
026: import org.picocontainer.defaults.ComponentAdapterFactory;
027:
028: import java.util.List;
029: import java.util.Map;
030:
031: /**
032: * @author Aslak Hellesøy
033: * @author Paul Hammant
034: * @version $Revision: 3144 $
035: */
036: public class AopNodeBuilderDecorationDelegate implements
037: NodeBuilderDecorationDelegate {
038:
039: private final AspectsManager aspectsManager;
040: private Object currentKey;
041: private AspectablePicoContainer currentPico;
042: private ClassPointcut currentClassCut;
043: private MethodPointcut currentMethodCut;
044:
045: public AopNodeBuilderDecorationDelegate(
046: AspectsManager aspectsManager) {
047: this .aspectsManager = aspectsManager;
048: }
049:
050: public ComponentAdapterFactory decorate(
051: ComponentAdapterFactory componentAdapterFactory,
052: Map attributes) {
053: AspectsComponentAdapterFactory aspectsComponentAdapterFactory = createAdapterFactory(
054: aspectsManager, componentAdapterFactory);
055: return aspectsComponentAdapterFactory;
056: }
057:
058: public MutablePicoContainer decorate(
059: MutablePicoContainer picoContainer) {
060: currentPico = mixinAspectablePicoContainer(aspectsManager,
061: picoContainer);
062: return currentPico;
063: }
064:
065: public Object createNode(Object name, Map attributes,
066: Object parentElement) {
067: if (name.equals("aspect")) {
068: return createAspectNode(attributes, name);
069: } else if (name.equals("pointcut")) {
070: return createPointcutNode(attributes, name);
071: } else {
072: throw new NanoContainerMarkupException(
073: "Don't know how to create a '" + name
074: + "' child of a '"
075: + parentElement.toString() + "' element");
076: }
077: }
078:
079: private Object createPointcutNode(Map attributes, Object name) {
080: currentClassCut = (ClassPointcut) attributes.remove("classCut");
081: currentMethodCut = (MethodPointcut) attributes
082: .remove("methodCut");
083: return name;
084: }
085:
086: private Object createAspectNode(Map attributes, Object name) {
087: ClassPointcut classCut = (ClassPointcut) attributes
088: .remove("classCut");
089: if (classCut != null) {
090: currentClassCut = classCut;
091: }
092: MethodPointcut methodCut = (MethodPointcut) attributes
093: .remove("methodCut");
094: if (methodCut != null) {
095: currentMethodCut = methodCut;
096: }
097:
098: MethodInterceptor interceptor = (MethodInterceptor) attributes
099: .remove("interceptor");
100: Object interceptorKey = attributes.remove("interceptorKey");
101: Class mixinClass = (Class) attributes.remove("mixinClass");
102: List mixinInterfaces = (List) attributes
103: .remove("mixinInterfaces");
104:
105: ComponentPointcut componentCut = (ComponentPointcut) attributes
106: .remove("componentCut");
107: if (componentCut == null && currentKey != null) {
108: componentCut = currentPico.getPointcutsFactory().component(
109: currentKey);
110: }
111:
112: if (interceptor != null || interceptorKey != null) {
113: registerInterceptor(currentPico, currentClassCut,
114: componentCut, currentMethodCut, interceptor,
115: interceptorKey);
116: } else if (mixinClass != null) {
117: registerMixin(currentPico, currentClassCut, componentCut,
118: toClassArray(mixinInterfaces), mixinClass);
119: } else {
120: throw new NanoContainerMarkupException(
121: "No advice specified - must specify one of interceptor, interceptorKey, mixinClass, or mixinKey");
122: }
123:
124: return name;
125: }
126:
127: private AspectsComponentAdapterFactory createAdapterFactory(
128: AspectsApplicator aspectsApplicator,
129: ComponentAdapterFactory delegateAdapterFactory) {
130: if (delegateAdapterFactory != null) {
131: return new AspectsComponentAdapterFactory(
132: aspectsApplicator, delegateAdapterFactory);
133: } else {
134: return new AspectsComponentAdapterFactory(aspectsApplicator);
135: }
136: }
137:
138: private AspectablePicoContainer mixinAspectablePicoContainer(
139: AspectsManager aspectsManager, MutablePicoContainer pico) {
140: Aspects aspects = new Aspects();
141: aspects.mixin(Pointcuts.ALL_CLASSES,
142: new Class[] { AspectsContainer.class },
143: new InstanceMixinFactory(aspectsManager));
144: aspects.interfaces(Pointcuts.ALL_CLASSES,
145: new Class[] { AspectablePicoContainer.class });
146: return (AspectablePicoContainer) ProxyFactory.getInstance(
147: aspects).wrap(pico);
148: }
149:
150: private void registerInterceptor(AspectablePicoContainer pico,
151: ClassPointcut classCut, ComponentPointcut componentCut,
152: MethodPointcut methodCut, MethodInterceptor interceptor,
153: Object interceptorKey) {
154: // precondition:
155: if (interceptor == null && interceptorKey == null) {
156: throw new RuntimeException(
157: "assertion failed -- non-null interceptor or interceptorKey expected");
158: }
159:
160: // validate script:
161: if (classCut == null && componentCut == null) {
162: throw new NanoContainerMarkupException(
163: "currentClassCut or componentCut required for interceptor advice");
164: }
165: if (methodCut == null) {
166: throw new NanoContainerMarkupException(
167: "currentMethodCut required for interceptor advice");
168: }
169:
170: if (classCut != null) {
171: if (interceptor != null) {
172: pico.registerInterceptor(classCut, methodCut,
173: interceptor);
174: } else {
175: pico.registerInterceptor(classCut, methodCut,
176: interceptorKey);
177: }
178: } else {
179: if (interceptor != null) {
180: pico.registerInterceptor(componentCut, methodCut,
181: interceptor);
182: } else {
183: pico.registerInterceptor(componentCut, methodCut,
184: interceptorKey);
185: }
186: }
187: }
188:
189: private void registerMixin(AspectablePicoContainer pico,
190: ClassPointcut classCut, ComponentPointcut componentCut,
191: Class[] mixinInterfaces, Class mixinClass) {
192: // precondition:
193: if (mixinClass == null) {
194: throw new RuntimeException(
195: "assertion failed -- mixinClass required");
196: }
197:
198: // validate script:
199: if (classCut == null && componentCut == null) {
200: throw new NanoContainerMarkupException(
201: "currentClassCut or componentCut required for mixin advice");
202: }
203:
204: if (classCut != null) {
205: if (mixinInterfaces != null) {
206: pico.registerMixin(classCut, mixinInterfaces,
207: mixinClass);
208: } else {
209: pico.registerMixin(classCut, mixinClass);
210: }
211: } else {
212: if (mixinInterfaces != null) {
213: pico.registerMixin(componentCut, mixinInterfaces,
214: mixinClass);
215: } else {
216: pico.registerMixin(componentCut, mixinClass);
217: }
218: }
219: }
220:
221: private Class[] toClassArray(List l) {
222: if (l == null) {
223: return null;
224: }
225: return (Class[]) l.toArray(new Class[l.size()]);
226: }
227:
228: public void rememberComponentKey(Map attributes) {
229: Object key = attributes.get("key");
230: Object clazz = attributes.get("class");
231: if (key != null) {
232: currentKey = key;
233: } else {
234: currentKey = clazz;
235: }
236: }
237: }
|