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 }