001: /*
002: * Copyright 2002-2007 the original author or authors.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package org.springframework.aop.config;
018:
019: import java.util.ArrayList;
020: import java.util.List;
021:
022: import org.w3c.dom.Element;
023: import org.w3c.dom.Node;
024: import org.w3c.dom.NodeList;
025:
026: import org.springframework.aop.aspectj.AspectJAfterAdvice;
027: import org.springframework.aop.aspectj.AspectJAfterReturningAdvice;
028: import org.springframework.aop.aspectj.AspectJAfterThrowingAdvice;
029: import org.springframework.aop.aspectj.AspectJAroundAdvice;
030: import org.springframework.aop.aspectj.AspectJExpressionPointcut;
031: import org.springframework.aop.aspectj.AspectJMethodBeforeAdvice;
032: import org.springframework.aop.aspectj.AspectJPointcutAdvisor;
033: import org.springframework.aop.aspectj.DeclareParentsAdvisor;
034: import org.springframework.aop.support.DefaultBeanFactoryPointcutAdvisor;
035: import org.springframework.beans.factory.config.BeanDefinition;
036: import org.springframework.beans.factory.config.BeanReference;
037: import org.springframework.beans.factory.config.ConstructorArgumentValues;
038: import org.springframework.beans.factory.config.RuntimeBeanNameReference;
039: import org.springframework.beans.factory.config.RuntimeBeanReference;
040: import org.springframework.beans.factory.parsing.CompositeComponentDefinition;
041: import org.springframework.beans.factory.parsing.ParseState;
042: import org.springframework.beans.factory.support.AbstractBeanDefinition;
043: import org.springframework.beans.factory.support.BeanDefinitionBuilder;
044: import org.springframework.beans.factory.support.BeanDefinitionRegistry;
045: import org.springframework.beans.factory.support.RootBeanDefinition;
046: import org.springframework.beans.factory.xml.BeanDefinitionParser;
047: import org.springframework.beans.factory.xml.ParserContext;
048: import org.springframework.util.StringUtils;
049: import org.springframework.util.xml.DomUtils;
050:
051: /**
052: * {@link BeanDefinitionParser} for the <code><aop:config></code> tag.
053: *
054: * @author Rob Harrop
055: * @author Juergen Hoeller
056: * @author Adrian Colyer
057: * @author Rod Johnson
058: * @author Mark Fisher
059: * @since 2.0
060: */
061: class ConfigBeanDefinitionParser implements BeanDefinitionParser {
062:
063: private static final String ASPECT = "aspect";
064:
065: private static final String EXPRESSION = "expression";
066:
067: private static final String ID = "id";
068:
069: private static final String POINTCUT = "pointcut";
070:
071: private static final String ADVICE_BEAN_NAME = "adviceBeanName";
072:
073: private static final String ADVISOR = "advisor";
074:
075: private static final String ADVICE_REF = "advice-ref";
076:
077: private static final String POINTCUT_REF = "pointcut-ref";
078:
079: private static final String REF = "ref";
080:
081: private static final String BEFORE = "before";
082:
083: private static final String DECLARE_PARENTS = "declare-parents";
084:
085: private static final String TYPE_PATTERN = "types-matching";
086:
087: private static final String DEFAULT_IMPL = "default-impl";
088:
089: private static final String IMPLEMENT_INTERFACE = "implement-interface";
090:
091: private static final String AFTER = "after";
092:
093: private static final String AFTER_RETURNING_ELEMENT = "after-returning";
094:
095: private static final String AFTER_THROWING_ELEMENT = "after-throwing";
096:
097: private static final String AROUND = "around";
098:
099: private static final String RETURNING = "returning";
100:
101: private static final String RETURNING_PROPERTY = "returningName";
102:
103: private static final String THROWING = "throwing";
104:
105: private static final String THROWING_PROPERTY = "throwingName";
106:
107: private static final String ARG_NAMES = "arg-names";
108:
109: private static final String ARG_NAMES_PROPERTY = "argumentNames";
110:
111: private static final String ASPECT_NAME_PROPERTY = "aspectName";
112:
113: private static final String DECLARATION_ORDER_PROPERTY = "declarationOrder";
114:
115: private static final String ORDER_PROPERTY = "order";
116:
117: private static final int METHOD_INDEX = 0;
118:
119: private static final int POINTCUT_INDEX = 1;
120:
121: private static final int ASPECT_INSTANCE_FACTORY_INDEX = 2;
122:
123: private ParseState parseState = new ParseState();
124:
125: public BeanDefinition parse(Element element,
126: ParserContext parserContext) {
127: CompositeComponentDefinition compositeDef = new CompositeComponentDefinition(
128: element.getTagName(), parserContext
129: .extractSource(element));
130: parserContext.pushContainingComponent(compositeDef);
131:
132: configureAutoProxyCreator(parserContext, element);
133:
134: NodeList childNodes = element.getChildNodes();
135: for (int i = 0; i < childNodes.getLength(); i++) {
136: Node node = childNodes.item(i);
137: if (node.getNodeType() == Node.ELEMENT_NODE) {
138: String localName = node.getLocalName();
139: if (POINTCUT.equals(localName)) {
140: parsePointcut((Element) node, parserContext);
141: } else if (ADVISOR.equals(localName)) {
142: parseAdvisor((Element) node, parserContext);
143: } else if (ASPECT.equals(localName)) {
144: parseAspect((Element) node, parserContext);
145: }
146: }
147: }
148:
149: parserContext.popAndRegisterContainingComponent();
150: return null;
151: }
152:
153: /**
154: * Configures the auto proxy creator needed to support the {@link BeanDefinition BeanDefinitions}
155: * created by the '<code><aop:config/></code>' tag. Will force class proxying if the
156: * '<code>proxy-target-class</code>' attribute is set to '<code>true</code>'.
157: * @see AopNamespaceUtils
158: */
159: private void configureAutoProxyCreator(ParserContext parserContext,
160: Element element) {
161: AopNamespaceUtils.registerAspectJAutoProxyCreatorIfNecessary(
162: parserContext, element);
163: }
164:
165: /**
166: * Parses the supplied <code><advisor></code> element and registers the resulting
167: * {@link org.springframework.aop.Advisor} and any resulting {@link org.springframework.aop.Pointcut}
168: * with the supplied {@link BeanDefinitionRegistry}.
169: */
170: private void parseAdvisor(Element advisorElement,
171: ParserContext parserContext) {
172: AbstractBeanDefinition advisorDef = createAdvisorBeanDefinition(
173: advisorElement, parserContext);
174: String id = advisorElement.getAttribute(ID);
175:
176: try {
177: this .parseState.push(new AdvisorEntry(id));
178: String advisorBeanName = id;
179: if (StringUtils.hasText(advisorBeanName)) {
180: parserContext.getRegistry().registerBeanDefinition(
181: advisorBeanName, advisorDef);
182: } else {
183: advisorBeanName = parserContext.getReaderContext()
184: .registerWithGeneratedName(advisorDef);
185: }
186:
187: Object pointcut = parsePointcutProperty(advisorElement,
188: parserContext);
189: if (pointcut instanceof BeanDefinition) {
190: advisorDef.getPropertyValues().addPropertyValue(
191: POINTCUT, pointcut);
192: parserContext
193: .registerComponent(new AdvisorComponentDefinition(
194: advisorBeanName, advisorDef,
195: (BeanDefinition) pointcut));
196: } else if (pointcut instanceof String) {
197: advisorDef.getPropertyValues().addPropertyValue(
198: POINTCUT,
199: new RuntimeBeanReference((String) pointcut));
200: parserContext
201: .registerComponent(new AdvisorComponentDefinition(
202: advisorBeanName, advisorDef));
203: }
204: } finally {
205: this .parseState.pop();
206: }
207: }
208:
209: /**
210: * Create a {@link RootBeanDefinition} for the advisor described in the supplied. Does <strong>not</strong>
211: * parse any associated '<code>pointcut</code>' or '<code>pointcut-ref</code>' attributes.
212: */
213: private AbstractBeanDefinition createAdvisorBeanDefinition(
214: Element advisorElement, ParserContext parserContext) {
215: RootBeanDefinition advisorDefinition = new RootBeanDefinition(
216: DefaultBeanFactoryPointcutAdvisor.class);
217: advisorDefinition.setSource(parserContext
218: .extractSource(advisorElement));
219:
220: String adviceRef = advisorElement.getAttribute(ADVICE_REF);
221: if (!StringUtils.hasText(adviceRef)) {
222: parserContext.getReaderContext().error(
223: "'advice-ref' attribute contains empty value.",
224: advisorElement, this .parseState.snapshot());
225: } else {
226: advisorDefinition.getPropertyValues().addPropertyValue(
227: ADVICE_BEAN_NAME,
228: new RuntimeBeanNameReference(adviceRef));
229: }
230:
231: if (advisorElement.hasAttribute(ORDER_PROPERTY)) {
232: advisorDefinition.getPropertyValues().addPropertyValue(
233: ORDER_PROPERTY,
234: advisorElement.getAttribute(ORDER_PROPERTY));
235: }
236:
237: return advisorDefinition;
238: }
239:
240: private void parseAspect(Element aspectElement,
241: ParserContext parserContext) {
242: String aspectId = aspectElement.getAttribute(ID);
243: String aspectName = aspectElement.getAttribute(REF);
244:
245: if (!StringUtils.hasText(aspectName)) {
246: parserContext
247: .getReaderContext()
248: .error(
249: "<aspect> tag needs aspect bean reference via 'ref' attribute.",
250: aspectElement, this .parseState.snapshot());
251: return;
252: }
253:
254: try {
255: this .parseState.push(new AspectEntry(aspectId, aspectName));
256: List beanDefinitions = new ArrayList();
257: List beanReferences = new ArrayList();
258: beanReferences.add(new RuntimeBeanReference(aspectName));
259:
260: List declareParents = DomUtils.getChildElementsByTagName(
261: aspectElement, DECLARE_PARENTS);
262: for (int i = METHOD_INDEX; i < declareParents.size(); i++) {
263: Element declareParentsElement = (Element) declareParents
264: .get(i);
265: beanDefinitions.add(parseDeclareParents(
266: declareParentsElement, parserContext));
267: }
268:
269: // We have to parse "advice" and all the advice kinds in one loop, to get the
270: // ordering semantics right.
271: NodeList nodeList = aspectElement.getChildNodes();
272: for (int i = 0; i < nodeList.getLength(); i++) {
273: Node node = nodeList.item(i);
274: if (isAdviceNode(node)) {
275: AbstractBeanDefinition advisorDefinition = parseAdvice(
276: aspectName, i, aspectElement,
277: (Element) node, parserContext,
278: beanDefinitions, beanReferences);
279: beanDefinitions.add(advisorDefinition);
280: }
281: }
282:
283: AspectComponentDefinition aspectComponentDefinition = createAspectComponentDefinition(
284: aspectElement, aspectId, beanDefinitions,
285: beanReferences, parserContext);
286: parserContext
287: .pushContainingComponent(aspectComponentDefinition);
288:
289: List pointcuts = DomUtils.getChildElementsByTagName(
290: aspectElement, POINTCUT);
291: for (int i = 0; i < pointcuts.size(); i++) {
292: Element pointcutElement = (Element) pointcuts.get(i);
293: parsePointcut(pointcutElement, parserContext);
294: }
295:
296: parserContext.popAndRegisterContainingComponent();
297: } finally {
298: this .parseState.pop();
299: }
300: }
301:
302: private AspectComponentDefinition createAspectComponentDefinition(
303: Element aspectElement, String aspectId, List beanDefs,
304: List beanRefs, ParserContext parserContext) {
305:
306: BeanDefinition[] beanDefArray = (BeanDefinition[]) beanDefs
307: .toArray(new BeanDefinition[beanDefs.size()]);
308: BeanReference[] beanRefArray = (BeanReference[]) beanRefs
309: .toArray(new BeanReference[beanRefs.size()]);
310: Object source = parserContext.extractSource(aspectElement);
311: return new AspectComponentDefinition(aspectId, beanDefArray,
312: beanRefArray, source);
313: }
314:
315: /**
316: * Return <code>true</code> if the supplied node describes an advice type. May be one of:
317: * '<code>before</code>', '<code>after</code>', '<code>after-returning</code>',
318: * '<code>after-throwing</code>' or '<code>around</code>'.
319: */
320: private boolean isAdviceNode(Node aNode) {
321: if (!(aNode instanceof Element)) {
322: return false;
323: } else {
324: String name = aNode.getLocalName();
325: return (BEFORE.equals(name) || AFTER.equals(name)
326: || AFTER_RETURNING_ELEMENT.equals(name)
327: || AFTER_THROWING_ELEMENT.equals(name) || AROUND
328: .equals(name));
329: }
330: }
331:
332: /**
333: * Parse a '<code>declare-parents</code>' element and register the appropriate
334: * DeclareParentsAdvisor with the BeanDefinitionRegistry encapsulated in the
335: * supplied ParserContext.
336: */
337: private AbstractBeanDefinition parseDeclareParents(
338: Element declareParentsElement, ParserContext parserContext) {
339: BeanDefinitionBuilder builder = BeanDefinitionBuilder
340: .rootBeanDefinition(DeclareParentsAdvisor.class);
341: builder.addConstructorArg(declareParentsElement
342: .getAttribute(IMPLEMENT_INTERFACE));
343: builder.addConstructorArg(declareParentsElement
344: .getAttribute(TYPE_PATTERN));
345: builder.addConstructorArg(declareParentsElement
346: .getAttribute(DEFAULT_IMPL));
347: builder.setSource(parserContext
348: .extractSource(declareParentsElement));
349: AbstractBeanDefinition definition = builder.getBeanDefinition();
350: parserContext.getReaderContext().registerWithGeneratedName(
351: definition);
352: return definition;
353: }
354:
355: /**
356: * Parses one of '<code>before</code>', '<code>after</code>', '<code>after-returning</code>',
357: * '<code>after-throwing</code>' or '<code>around</code>' and registers the resulting
358: * BeanDefinition with the supplied BeanDefinitionRegistry.
359: * @return the generated advice RootBeanDefinition
360: */
361: private AbstractBeanDefinition parseAdvice(String aspectName,
362: int order, Element aspectElement, Element adviceElement,
363: ParserContext parserContext, List beanDefinitions,
364: List beanReferences) {
365:
366: try {
367: this .parseState.push(new AdviceEntry(adviceElement
368: .getLocalName()));
369:
370: // create the method factory bean
371: RootBeanDefinition methodDefinition = new RootBeanDefinition(
372: MethodLocatingFactoryBean.class);
373: methodDefinition.getPropertyValues().addPropertyValue(
374: "targetBeanName", aspectName);
375: methodDefinition.getPropertyValues().addPropertyValue(
376: "methodName", adviceElement.getAttribute("method"));
377: methodDefinition.setSynthetic(true);
378:
379: // create instance factory definition
380: RootBeanDefinition aspectFactoryDef = new RootBeanDefinition(
381: SimpleBeanFactoryAwareAspectInstanceFactory.class);
382: aspectFactoryDef.getPropertyValues().addPropertyValue(
383: "aspectBeanName", aspectName);
384: aspectFactoryDef.setSynthetic(true);
385:
386: // register the pointcut
387: AbstractBeanDefinition adviceDef = createAdviceDefinition(
388: adviceElement, parserContext, aspectName, order,
389: methodDefinition, aspectFactoryDef,
390: beanDefinitions, beanReferences);
391:
392: // configure the advisor
393: RootBeanDefinition advisorDefinition = new RootBeanDefinition(
394: AspectJPointcutAdvisor.class);
395: advisorDefinition.setSource(parserContext
396: .extractSource(adviceElement));
397: advisorDefinition.getConstructorArgumentValues()
398: .addGenericArgumentValue(adviceDef);
399: if (aspectElement.hasAttribute(ORDER_PROPERTY)) {
400: advisorDefinition.getPropertyValues().addPropertyValue(
401: ORDER_PROPERTY,
402: aspectElement.getAttribute(ORDER_PROPERTY));
403: }
404:
405: // register the final advisor
406: parserContext.getReaderContext().registerWithGeneratedName(
407: advisorDefinition);
408:
409: return advisorDefinition;
410: } finally {
411: this .parseState.pop();
412: }
413: }
414:
415: /**
416: * Creates the RootBeanDefinition for a POJO advice bean. Also causes pointcut
417: * parsing to occur so that the pointcut may be associate with the advice bean.
418: * This same pointcut is also configured as the pointcut for the enclosing
419: * Advisor definition using the supplied MutablePropertyValues.
420: */
421: private AbstractBeanDefinition createAdviceDefinition(
422: Element adviceElement, ParserContext parserContext,
423: String aspectName, int order, RootBeanDefinition methodDef,
424: RootBeanDefinition aspectFactoryDef, List beanDefinitions,
425: List beanReferences) {
426:
427: RootBeanDefinition adviceDefinition = new RootBeanDefinition(
428: getAdviceClass(adviceElement));
429: adviceDefinition.setSource(parserContext
430: .extractSource(adviceElement));
431:
432: adviceDefinition.getPropertyValues().addPropertyValue(
433: ASPECT_NAME_PROPERTY, aspectName);
434: adviceDefinition.getPropertyValues().addPropertyValue(
435: DECLARATION_ORDER_PROPERTY, new Integer(order));
436:
437: if (adviceElement.hasAttribute(RETURNING)) {
438: adviceDefinition.getPropertyValues().addPropertyValue(
439: RETURNING_PROPERTY,
440: adviceElement.getAttribute(RETURNING));
441: }
442: if (adviceElement.hasAttribute(THROWING)) {
443: adviceDefinition.getPropertyValues().addPropertyValue(
444: THROWING_PROPERTY,
445: adviceElement.getAttribute(THROWING));
446: }
447: if (adviceElement.hasAttribute(ARG_NAMES)) {
448: adviceDefinition.getPropertyValues().addPropertyValue(
449: ARG_NAMES_PROPERTY,
450: adviceElement.getAttribute(ARG_NAMES));
451: }
452:
453: ConstructorArgumentValues cav = adviceDefinition
454: .getConstructorArgumentValues();
455: cav.addIndexedArgumentValue(METHOD_INDEX, methodDef);
456:
457: Object pointcut = parsePointcutProperty(adviceElement,
458: parserContext);
459: if (pointcut instanceof BeanDefinition) {
460: cav.addIndexedArgumentValue(POINTCUT_INDEX, pointcut);
461: beanDefinitions.add(pointcut);
462: } else if (pointcut instanceof String) {
463: RuntimeBeanReference pointcutRef = new RuntimeBeanReference(
464: (String) pointcut);
465: cav.addIndexedArgumentValue(POINTCUT_INDEX, pointcutRef);
466: beanReferences.add(pointcutRef);
467: }
468:
469: cav.addIndexedArgumentValue(ASPECT_INSTANCE_FACTORY_INDEX,
470: aspectFactoryDef);
471:
472: return adviceDefinition;
473: }
474:
475: /**
476: * Gets the advice implementation class corresponding to the supplied {@link Element}.
477: */
478: private Class getAdviceClass(Element adviceElement) {
479: String elementName = adviceElement.getLocalName();
480: if (BEFORE.equals(elementName)) {
481: return AspectJMethodBeforeAdvice.class;
482: } else if (AFTER.equals(elementName)) {
483: return AspectJAfterAdvice.class;
484: } else if (AFTER_RETURNING_ELEMENT.equals(elementName)) {
485: return AspectJAfterReturningAdvice.class;
486: } else if (AFTER_THROWING_ELEMENT.equals(elementName)) {
487: return AspectJAfterThrowingAdvice.class;
488: } else if (AROUND.equals(elementName)) {
489: return AspectJAroundAdvice.class;
490: } else {
491: throw new IllegalArgumentException("Unknown advice kind ["
492: + elementName + "].");
493: }
494: }
495:
496: /**
497: * Parses the supplied <code><pointcut></code> and registers the resulting
498: * Pointcut with the BeanDefinitionRegistry.
499: */
500: private AbstractBeanDefinition parsePointcut(
501: Element pointcutElement, ParserContext parserContext) {
502: String id = pointcutElement.getAttribute(ID);
503: String expression = pointcutElement.getAttribute(EXPRESSION);
504:
505: AbstractBeanDefinition pointcutDefinition = null;
506:
507: try {
508: this .parseState.push(new PointcutEntry(id));
509: pointcutDefinition = createPointcutDefinition(expression);
510: pointcutDefinition.setSource(parserContext
511: .extractSource(pointcutElement));
512:
513: String pointcutBeanName = id;
514: if (StringUtils.hasText(pointcutBeanName)) {
515: parserContext.getRegistry().registerBeanDefinition(
516: pointcutBeanName, pointcutDefinition);
517: } else {
518: pointcutBeanName = parserContext.getReaderContext()
519: .registerWithGeneratedName(pointcutDefinition);
520: }
521:
522: parserContext
523: .registerComponent(new PointcutComponentDefinition(
524: pointcutBeanName, pointcutDefinition,
525: expression));
526: } finally {
527: this .parseState.pop();
528: }
529:
530: return pointcutDefinition;
531: }
532:
533: /**
534: * Parses the <code>pointcut</code> or <code>pointcut-ref</code> attributes of the supplied
535: * {@link Element} and add a <code>pointcut</code> property as appropriate. Generates a
536: * {@link org.springframework.beans.factory.config.BeanDefinition} for the pointcut if necessary
537: * and returns its bean name, otherwise returns the bean name of the referred pointcut.
538: */
539: private Object parsePointcutProperty(Element element,
540: ParserContext parserContext) {
541: if (element.hasAttribute(POINTCUT)
542: && element.hasAttribute(POINTCUT_REF)) {
543: parserContext
544: .getReaderContext()
545: .error(
546: "Cannot define both 'pointcut' and 'pointcut-ref' on <advisor> tag.",
547: element, this .parseState.snapshot());
548: return null;
549: } else if (element.hasAttribute(POINTCUT)) {
550: // Create a pointcut for the anonymous pc and register it.
551: String expression = element.getAttribute(POINTCUT);
552: AbstractBeanDefinition pointcutDefinition = createPointcutDefinition(expression);
553: pointcutDefinition.setSource(parserContext
554: .extractSource(element));
555: return pointcutDefinition;
556: } else if (element.hasAttribute(POINTCUT_REF)) {
557: String pointcutRef = element.getAttribute(POINTCUT_REF);
558: if (!StringUtils.hasText(pointcutRef)) {
559: parserContext
560: .getReaderContext()
561: .error(
562: "'pointcut-ref' attribute contains empty value.",
563: element, this .parseState.snapshot());
564: return null;
565: }
566: return pointcutRef;
567: } else {
568: parserContext
569: .getReaderContext()
570: .error(
571: "Must define one of 'pointcut' or 'pointcut-ref' on <advisor> tag.",
572: element, this .parseState.snapshot());
573: return null;
574: }
575: }
576:
577: /**
578: * Creates a {@link BeanDefinition} for the {@link AspectJExpressionPointcut} class using
579: * the supplied pointcut expression.
580: */
581: protected AbstractBeanDefinition createPointcutDefinition(
582: String expression) {
583: RootBeanDefinition beanDefinition = new RootBeanDefinition(
584: AspectJExpressionPointcut.class);
585: beanDefinition.setSingleton(false);
586: beanDefinition.setSynthetic(true);
587: beanDefinition.getPropertyValues().addPropertyValue(EXPRESSION,
588: expression);
589: return beanDefinition;
590: }
591:
592: }
|