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