0001: /**************************************************************************************
0002: * Copyright (c) Jonas BonŽr, Alexandre Vasseur. All rights reserved. *
0003: * http://aspectwerkz.codehaus.org *
0004: * ---------------------------------------------------------------------------------- *
0005: * The software in this package is published under the terms of the LGPL license *
0006: * a copy of which has been included with this distribution in the license.txt file. *
0007: **************************************************************************************/package org.codehaus.aspectwerkz.definition;
0008:
0009: import org.codehaus.aspectwerkz.util.Strings;
0010: import org.codehaus.aspectwerkz.aspect.AdviceType;
0011: import org.codehaus.aspectwerkz.DeploymentModel;
0012: import org.codehaus.aspectwerkz.intercept.AdvisableImpl;
0013: import org.codehaus.aspectwerkz.reflect.impl.asm.AsmClassInfo;
0014: import org.codehaus.aspectwerkz.reflect.impl.java.JavaMethodInfo;
0015: import org.codehaus.aspectwerkz.reflect.impl.java.JavaClassInfo;
0016: import org.codehaus.aspectwerkz.reflect.ClassInfo;
0017: import org.codehaus.aspectwerkz.reflect.ClassInfoHelper;
0018: import org.codehaus.aspectwerkz.reflect.MethodInfo;
0019: import org.codehaus.aspectwerkz.expression.regexp.Pattern;
0020: import org.codehaus.aspectwerkz.expression.ExpressionNamespace;
0021: import org.codehaus.aspectwerkz.expression.ExpressionInfo;
0022: import org.codehaus.aspectwerkz.annotation.AspectAnnotationParser;
0023: import org.codehaus.aspectwerkz.annotation.MixinAnnotationParser;
0024: import org.codehaus.aspectwerkz.exception.DefinitionException;
0025: import org.codehaus.aspectwerkz.transform.TransformationConstants;
0026: import org.codehaus.aspectwerkz.transform.inlining.AspectModelManager;
0027: import org.dom4j.Attribute;
0028: import org.dom4j.Document;
0029: import org.dom4j.Element;
0030:
0031: import java.util.ArrayList;
0032: import java.util.Iterator;
0033: import java.util.List;
0034: import java.util.Set;
0035: import java.util.HashSet;
0036: import java.util.StringTokenizer;
0037:
0038: /**
0039: * Parses the XML definition using <tt>dom4j</tt>.
0040: *
0041: * @author <a href="mailto:jboner@codehaus.org">Jonas BonŽr </a>
0042: * @author <a href="mailto:alex@gnilux.com">Alexandre Vasseur </a>
0043: */
0044: public class DocumentParser {
0045:
0046: /**
0047: * Parses aspect class names.
0048: *
0049: * @param document the defintion as a document
0050: * @return the aspect class names
0051: */
0052: public static List parseAspectClassNames(final Document document) {
0053: final List aspectClassNames = new ArrayList();
0054: for (Iterator it1 = document.getRootElement().elementIterator(
0055: "system"); it1.hasNext();) {
0056: Element system = (Element) it1.next();
0057: final String basePackage = getBasePackage(system);
0058: for (Iterator it11 = system.elementIterator("aspect"); it11
0059: .hasNext();) {
0060: String className = null;
0061: Element aspect = (Element) it11.next();
0062: for (Iterator it2 = aspect.attributeIterator(); it2
0063: .hasNext();) {
0064: Attribute attribute = (Attribute) it2.next();
0065: final String name = attribute.getName().trim();
0066: final String value = attribute.getValue().trim();
0067: if (name.equalsIgnoreCase("class")) {
0068: className = value;
0069: }
0070: }
0071: aspectClassNames.add(basePackage + className);
0072: }
0073: for (Iterator it11 = system.elementIterator("package"); it11
0074: .hasNext();) {
0075: final Element packageElement = ((Element) it11.next());
0076: final String packageName = getPackage(packageElement);
0077: for (Iterator it12 = packageElement
0078: .elementIterator("aspect"); it12.hasNext();) {
0079: String className = null;
0080: Element aspect = (Element) it12.next();
0081: for (Iterator it2 = aspect.attributeIterator(); it2
0082: .hasNext();) {
0083: Attribute attribute = (Attribute) it2.next();
0084: final String name = attribute.getName().trim();
0085: final String value = attribute.getValue()
0086: .trim();
0087: if (name.equalsIgnoreCase("class")) {
0088: className = value;
0089: }
0090: }
0091: aspectClassNames.add(packageName + className);
0092: }
0093: }
0094: }
0095: aspectClassNames.add(Virtual.class.getName());
0096:
0097: return aspectClassNames;
0098: }
0099:
0100: /**
0101: * Parses the definition DOM document.
0102: *
0103: * @param document the defintion as a document
0104: * @param systemDef the system definition
0105: * @param aspectClass the aspect class
0106: * @return the definition
0107: */
0108: public static AspectDefinition parseAspectDefinition(
0109: final Document document, final SystemDefinition systemDef,
0110: final Class aspectClass) {
0111:
0112: final Element aspect = document.getRootElement();
0113:
0114: if (!aspect.getName().equals("aspect")) {
0115: throw new DefinitionException(
0116: "XML definition for aspect is not well-formed: "
0117: + document.asXML());
0118: }
0119: String specialAspectName = null;
0120: String className = null;
0121: String deploymentModelAsString = null;
0122: String containerClassName = null;
0123: for (Iterator it2 = aspect.attributeIterator(); it2.hasNext();) {
0124: Attribute attribute = (Attribute) it2.next();
0125: final String name = attribute.getName().trim();
0126: final String value = attribute.getValue().trim();
0127: if (name.equalsIgnoreCase("class")) {
0128: className = value;
0129: } else if (name.equalsIgnoreCase("deployment-model")) {
0130: deploymentModelAsString = value;
0131: } else if (name.equalsIgnoreCase("name")) {
0132: specialAspectName = value;
0133: } else if (name.equalsIgnoreCase("container")) {
0134: containerClassName = value;
0135: }
0136: }
0137: if (specialAspectName == null) {
0138: specialAspectName = className;
0139: }
0140:
0141: final ClassInfo classInfo = JavaClassInfo
0142: .getClassInfo(aspectClass);
0143: final ClassLoader loader = aspectClass.getClassLoader();
0144:
0145: // create the aspect definition
0146: final AspectDefinition aspectDef = new AspectDefinition(
0147: specialAspectName, classInfo, systemDef);
0148: //TODO: if this XML centric deployment is supposed to PRESERVE @Aspect values, then it is broken
0149: aspectDef.setContainerClassName(containerClassName);
0150: aspectDef.setDeploymentModel(DeploymentModel
0151: .getDeploymentModelFor(deploymentModelAsString));
0152:
0153: parsePointcutElements(aspect, aspectDef); //needed to support undefined named pointcut in Attributes AW-152
0154:
0155: // load the different aspect model and let them define their aspects
0156: AspectModelManager.defineAspect(classInfo, aspectDef, loader);
0157:
0158: // parse the aspect info
0159: parseParameterElements(aspect, aspectDef);
0160: parsePointcutElements(aspect, aspectDef); //reparse pc for XML override (AW-152)
0161: parseAdviceElements(aspect, aspectDef, JavaClassInfo
0162: .getClassInfo(aspectClass));
0163: parseIntroduceElements(aspect, aspectDef, "", aspectClass
0164: .getClassLoader());
0165:
0166: systemDef.addAspect(aspectDef);
0167: return aspectDef;
0168: }
0169:
0170: /**
0171: * Parses the definition DOM document.
0172: *
0173: * @param loader the current class loader
0174: * @param document the defintion as a document
0175: * @return the definitions
0176: */
0177: public static Set parse(final ClassLoader loader,
0178: final Document document) {
0179: final Element root = document.getRootElement();
0180:
0181: // parse the transformation scopes
0182: return parseSystemElements(loader, root);
0183: }
0184:
0185: /**
0186: * Parses the <tt>system</tt> elements.
0187: *
0188: * @param loader the current class loader
0189: * @param root the root element
0190: */
0191: private static Set parseSystemElements(final ClassLoader loader,
0192: final Element root) {
0193: final Set systemDefs = new HashSet();
0194: for (Iterator it1 = root.elementIterator("system"); it1
0195: .hasNext();) {
0196: Element system = (Element) it1.next();
0197: SystemDefinition definition = parseSystemElement(loader,
0198: system, getBasePackage(system));
0199: if (definition != null) {
0200: systemDefs.add(definition);
0201: }
0202: }
0203: return systemDefs;
0204: }
0205:
0206: /**
0207: * Parses the <tt>system</tt> elements.
0208: *
0209: * @param loader the current class loader
0210: * @param systemElement the system element
0211: * @param basePackage the base package
0212: * @return the definition for the system
0213: */
0214: private static SystemDefinition parseSystemElement(
0215: final ClassLoader loader, final Element systemElement,
0216: final String basePackage) {
0217: String uuid = systemElement.attributeValue("id");
0218: if ((uuid == null) || uuid.equals("")) {
0219: throw new DefinitionException(
0220: "system UUID must be specified");
0221: }
0222: final SystemDefinition definition = new SystemDefinition(uuid);
0223:
0224: // add the virtual aspect
0225: addVirtualAspect(definition);
0226:
0227: // parse the global pointcuts
0228: List globalPointcuts = parseGlobalPointcutDefs(systemElement);
0229: //FIXME: systemDef should link a namespace, + remove static hashmap in Namespace (uuid clash in parallel CL)
0230: ExpressionNamespace systemNamespace = ExpressionNamespace
0231: .getNamespace(definition.getUuid());
0232: for (Iterator iterator = globalPointcuts.iterator(); iterator
0233: .hasNext();) {
0234: PointcutInfo pointcutInfo = (PointcutInfo) iterator.next();
0235: systemNamespace.addExpressionInfo(pointcutInfo.name,
0236: new ExpressionInfo(pointcutInfo.expression,
0237: systemNamespace.getName()));
0238: }
0239:
0240: // parse the global deployment scopes definitions
0241: parseDeploymentScopeDefs(systemElement, definition);
0242:
0243: // parse the global advisable definitions
0244: parseAdvisableDefs(systemElement, definition);
0245:
0246: // parse the include, exclude and prepare elements
0247: parseIncludePackageElements(systemElement, definition,
0248: basePackage);
0249: parseExcludePackageElements(systemElement, definition,
0250: basePackage);
0251: parsePrepareElements(systemElement, definition, basePackage);
0252:
0253: // parse without package elements
0254: parseAspectElements(loader, systemElement, definition,
0255: basePackage, globalPointcuts);
0256:
0257: // parse without package elements
0258: parseMixinElements(loader, systemElement, definition,
0259: basePackage);
0260:
0261: // parse with package elements
0262: parsePackageElements(loader, systemElement, definition,
0263: basePackage, globalPointcuts);
0264:
0265: // add all deployment scopes to the virtual advice
0266: DefinitionParserHelper
0267: .attachDeploymentScopeDefsToVirtualAdvice(definition);
0268:
0269: return definition;
0270: }
0271:
0272: /**
0273: * Parses the global pointcuts.
0274: *
0275: * @param systemElement the system element
0276: * @return a list with the pointcuts
0277: */
0278: private static List parseGlobalPointcutDefs(
0279: final Element systemElement) {
0280: final List globalPointcuts = new ArrayList();
0281: for (Iterator it11 = systemElement.elementIterator("pointcut"); it11
0282: .hasNext();) {
0283: PointcutInfo pointcutInfo = new PointcutInfo();
0284: Element globalPointcut = (Element) it11.next();
0285: for (Iterator it2 = globalPointcut.attributeIterator(); it2
0286: .hasNext();) {
0287: Attribute attribute = (Attribute) it2.next();
0288: final String name = attribute.getName().trim();
0289: final String value = attribute.getValue().trim();
0290: if (name.equalsIgnoreCase("name")) {
0291: pointcutInfo.name = value;
0292: } else if (name.equalsIgnoreCase("expression")) {
0293: pointcutInfo.expression = value;
0294: }
0295: }
0296: // pointcut CDATA is expression unless already specified as an attribute
0297: if (pointcutInfo.expression == null) {
0298: pointcutInfo.expression = globalPointcut.getTextTrim();
0299: }
0300: globalPointcuts.add(pointcutInfo);
0301: }
0302: return globalPointcuts;
0303: }
0304:
0305: /**
0306: * Parses the global deployment-scope elements.
0307: *
0308: * @param systemElement the system element
0309: * @param definition
0310: */
0311: private static void parseDeploymentScopeDefs(
0312: final Element systemElement,
0313: final SystemDefinition definition) {
0314: for (Iterator it11 = systemElement
0315: .elementIterator("deployment-scope"); it11.hasNext();) {
0316: String expression = null;
0317: String name = null;
0318: Element globalPointcut = (Element) it11.next();
0319: for (Iterator it2 = globalPointcut.attributeIterator(); it2
0320: .hasNext();) {
0321: Attribute attribute = (Attribute) it2.next();
0322: final String attrName = attribute.getName().trim();
0323: final String attrValue = attribute.getValue().trim();
0324: if (attrName.equalsIgnoreCase("name")) {
0325: name = attrValue;
0326: } else if (attrName.equalsIgnoreCase("expression")) {
0327: expression = attrValue;
0328: }
0329: }
0330: // pointcut CDATA is expression unless already specified as an attribute
0331: if (expression == null) {
0332: expression = globalPointcut.getTextTrim();
0333: }
0334: DefinitionParserHelper.createAndAddDeploymentScopeDef(name,
0335: expression, definition);
0336: }
0337: }
0338:
0339: /**
0340: * Parses the global advisable elements.
0341: *
0342: * @param systemElement the system element
0343: * @param definition
0344: */
0345: private static void parseAdvisableDefs(final Element systemElement,
0346: final SystemDefinition definition) {
0347: for (Iterator it11 = systemElement.elementIterator("advisable"); it11
0348: .hasNext();) {
0349: Element advisableElement = (Element) it11.next();
0350: String expression = "";
0351: String pointcutTypes = "all";
0352: for (Iterator it2 = advisableElement.attributeIterator(); it2
0353: .hasNext();) {
0354: Attribute attribute = (Attribute) it2.next();
0355: final String name = attribute.getName().trim();
0356: final String value = attribute.getValue().trim();
0357: if (name.equalsIgnoreCase("expression")) {
0358: expression = value;
0359: } else if (name.equalsIgnoreCase("pointcut-type")) {
0360: pointcutTypes = value;
0361: }
0362: }
0363: // pointcut CDATA is expression unless already specified as an attribute
0364: if (expression == null) {
0365: expression = advisableElement.getTextTrim();
0366: }
0367: handleAdvisableDefinition(definition, expression,
0368: pointcutTypes);
0369: }
0370: }
0371:
0372: /**
0373: * Parses the definition DOM document.
0374: *
0375: * @param loader the current class loader
0376: * @param systemElement the system element
0377: * @param definition the definition
0378: * @param basePackage the base package
0379: * @param globalPointcuts the global pointcuts
0380: */
0381: private static void parsePackageElements(final ClassLoader loader,
0382: final Element systemElement,
0383: final SystemDefinition definition,
0384: final String basePackage, final List globalPointcuts) {
0385: for (Iterator it1 = systemElement.elementIterator("package"); it1
0386: .hasNext();) {
0387: final Element packageElement = ((Element) it1.next());
0388: final String packageName = basePackage
0389: + getPackage(packageElement);
0390: parseAspectElements(loader, packageElement, definition,
0391: packageName, globalPointcuts);
0392: parseMixinElements(loader, packageElement, definition,
0393: packageName);
0394: parseAdvisableDefs(packageElement, definition);
0395: }
0396: }
0397:
0398: /**
0399: * Parses the <tt>aspect</tt> elements.
0400: *
0401: * @param loader the current class loader
0402: * @param systemElement the system element
0403: * @param definition the definition object
0404: * @param packageName the package name
0405: * @param globalPointcuts the global pointcuts
0406: */
0407: private static void parseAspectElements(final ClassLoader loader,
0408: final Element systemElement,
0409: final SystemDefinition definition,
0410: final String packageName, final List globalPointcuts) {
0411:
0412: for (Iterator it1 = systemElement.elementIterator("aspect"); it1
0413: .hasNext();) {
0414: String aspectName = null;
0415: String className = null;
0416: String deploymentModel = null;
0417: String containerClassName = null;
0418: Element aspect = (Element) it1.next();
0419: for (Iterator it2 = aspect.attributeIterator(); it2
0420: .hasNext();) {
0421: Attribute attribute = (Attribute) it2.next();
0422: final String name = attribute.getName().trim();
0423: final String value = attribute.getValue().trim();
0424: if (name.equalsIgnoreCase("class")) {
0425: className = value;
0426: } else if (name.equalsIgnoreCase("deployment-model")) {
0427: deploymentModel = value;
0428: } else if (name.equalsIgnoreCase("name")) {
0429: aspectName = value;
0430: } else if (name.equalsIgnoreCase("container")) {
0431: containerClassName = value;
0432: }
0433: }
0434: String aspectClassName = packageName + className;
0435: if (aspectName == null) {
0436: aspectName = aspectClassName;
0437: }
0438:
0439: // create the aspect definition
0440: ClassInfo aspectClassInfo;
0441: try {
0442: aspectClassInfo = AsmClassInfo.getClassInfo(
0443: aspectClassName, loader);
0444: } catch (Exception e) {
0445: System.err.println("Warning: could not load aspect "
0446: + aspectClassName + " from " + loader
0447: + "due to: " + e.toString());
0448: e.printStackTrace();
0449: continue;
0450: }
0451:
0452: final AspectDefinition aspectDef = new AspectDefinition(
0453: aspectName, aspectClassInfo, definition);
0454:
0455: // add the global pointcuts to the aspect
0456: for (Iterator it = globalPointcuts.iterator(); it.hasNext();) {
0457: PointcutInfo pointcutInfo = (PointcutInfo) it.next();
0458: DefinitionParserHelper
0459: .createAndAddPointcutDefToAspectDef(
0460: pointcutInfo.name,
0461: pointcutInfo.expression, aspectDef);
0462: }
0463: parsePointcutElements(aspect, aspectDef); //needed to support undefined named pointcut in Attributes AW-152
0464:
0465: // load the different aspect model and let them define their aspects
0466: AspectModelManager.defineAspect(aspectClassInfo, aspectDef,
0467: loader);
0468:
0469: // parse the class bytecode annotations
0470: AspectAnnotationParser.parse(aspectClassInfo, aspectDef,
0471: loader);
0472:
0473: // XML definition settings always overrides attribute definition settings
0474: // AW-357
0475: if (!Strings.isNullOrEmpty(deploymentModel)) {
0476: aspectDef.setDeploymentModel(DeploymentModel
0477: .getDeploymentModelFor(deploymentModel));
0478: }
0479: if (!Strings.isNullOrEmpty(aspectName)) {
0480: aspectDef.setName(aspectName);
0481: }
0482: if (!Strings.isNullOrEmpty(containerClassName)) {
0483: aspectDef.setContainerClassName(containerClassName);
0484: }
0485:
0486: // parse the aspect info
0487: parseParameterElements(aspect, aspectDef);
0488: parsePointcutElements(aspect, aspectDef); //reparse pc for XML override (AW-152)
0489: parseAdviceElements(aspect, aspectDef, aspectClassInfo);
0490: parseIntroduceElements(aspect, aspectDef, packageName,
0491: loader);
0492:
0493: definition.addAspect(aspectDef);
0494: }
0495: }
0496:
0497: /**
0498: * Parses the <tt>mixin</tt> elements.
0499: *
0500: * @param loader the current class loader
0501: * @param systemElement the system element
0502: * @param systemDefinition the system definition
0503: * @param packageName the package name
0504: */
0505: private static void parseMixinElements(final ClassLoader loader,
0506: final Element systemElement,
0507: final SystemDefinition systemDefinition,
0508: final String packageName) {
0509:
0510: for (Iterator it1 = systemElement.elementIterator("mixin"); it1
0511: .hasNext();) {
0512: String className = null;
0513: String deploymentModelAsString = null;
0514: boolean isTransient = false;
0515: boolean isTransientSetInXML = false;
0516: String factoryClassName = null;
0517: String expression = null;
0518: Element mixin = (Element) it1.next();
0519: for (Iterator it2 = mixin.attributeIterator(); it2
0520: .hasNext();) {
0521: Attribute attribute = (Attribute) it2.next();
0522: final String name = attribute.getName().trim();
0523: final String value = attribute.getValue().trim();
0524: if (name.equalsIgnoreCase("class")) {
0525: className = value;
0526: } else if (name.equalsIgnoreCase("deployment-model")
0527: && value != null) {
0528: deploymentModelAsString = value;
0529: } else if (name.equalsIgnoreCase("transient")) {
0530: if (value != null && value.equalsIgnoreCase("true")) {
0531: isTransient = true;
0532: isTransientSetInXML = true;
0533: }
0534: } else if (name.equalsIgnoreCase("factory")) {
0535: factoryClassName = value;
0536: } else if (name.equalsIgnoreCase("bind-to")) {
0537: expression = value;
0538: }
0539: }
0540: String mixinClassName = packageName + className;
0541:
0542: // create the mixin definition
0543: ClassInfo mixinClassInfo;
0544: try {
0545: mixinClassInfo = AsmClassInfo.getClassInfo(
0546: mixinClassName, loader);
0547: } catch (Exception e) {
0548: System.err.println("Warning: could not load mixin "
0549: + mixinClassName + " from " + loader
0550: + "due to: " + e.toString());
0551: e.printStackTrace();
0552: continue;
0553: }
0554:
0555: final DeploymentModel deploymentModel = (deploymentModelAsString != null) ? DeploymentModel
0556: .getDeploymentModelFor(deploymentModelAsString)
0557: : DeploymentModel.PER_INSTANCE;
0558:
0559: final MixinDefinition mixinDefinition = DefinitionParserHelper
0560: .createAndAddMixinDefToSystemDef(mixinClassInfo,
0561: expression, deploymentModel, isTransient,
0562: systemDefinition);
0563:
0564: // parse the class bytecode annotations
0565: MixinAnnotationParser
0566: .parse(mixinClassInfo, mixinDefinition);
0567:
0568: // XML definition settings always overrides attribute definition settings if present
0569: if (!Strings.isNullOrEmpty(deploymentModelAsString)) {
0570: mixinDefinition
0571: .setDeploymentModel(DeploymentModel
0572: .getDeploymentModelFor(deploymentModelAsString));
0573: }
0574: if (!Strings.isNullOrEmpty(factoryClassName)) {
0575: mixinDefinition.setFactoryClassName(factoryClassName);
0576: }
0577: if (isTransientSetInXML) {
0578: mixinDefinition.setTransient(isTransient);
0579: }
0580:
0581: parseParameterElements(mixin, mixinDefinition);
0582: }
0583: }
0584:
0585: /**
0586: * Adds a virtual system aspect to the definition. Needed to do various tricks.
0587: *
0588: * @param definition
0589: */
0590: public static void addVirtualAspect(
0591: final SystemDefinition definition) {
0592: final Class clazz = Virtual.class;
0593: final String aspectName = clazz.getName();
0594: ClassInfo aspectClassInfo = JavaClassInfo.getClassInfo(clazz);
0595: final AspectDefinition aspectDef = new AspectDefinition(
0596: aspectName, aspectClassInfo, definition);
0597: try {
0598: MethodInfo methodInfo = JavaMethodInfo.getMethodInfo(clazz
0599: .getDeclaredMethod("virtual", new Class[] {}));
0600: aspectDef
0601: .addBeforeAdviceDefinition(new AdviceDefinition(
0602: methodInfo.getName(), AdviceType.BEFORE,
0603: null, aspectName, aspectName, null,
0604: methodInfo, aspectDef));
0605: } catch (NoSuchMethodException e) {
0606: throw new Error("virtual aspect [" + aspectName
0607: + "] does not have expected method: "
0608: + e.toString());
0609: }
0610: definition.addAspect(aspectDef);
0611: }
0612:
0613: /**
0614: * Parses the aspectElement parameters.
0615: *
0616: * @param aspectElement the aspect element
0617: * @param aspectDef the aspect def
0618: */
0619: private static void parseParameterElements(
0620: final Element aspectElement,
0621: final AspectDefinition aspectDef) {
0622: for (Iterator it2 = aspectElement.elementIterator(); it2
0623: .hasNext();) {
0624: Element parameterElement = (Element) it2.next();
0625: if (parameterElement.getName().trim().equals("param")) {
0626: aspectDef.addParameter(parameterElement
0627: .attributeValue("name"), parameterElement
0628: .attributeValue("value"));
0629: }
0630: }
0631: }
0632:
0633: /**
0634: * Parses the mixinElement parameters.
0635: *
0636: * @param mixinElement the mixin element
0637: * @param mixinDef the mixin def
0638: */
0639: private static void parseParameterElements(
0640: final Element mixinElement, final MixinDefinition mixinDef) {
0641: for (Iterator it2 = mixinElement.elementIterator(); it2
0642: .hasNext();) {
0643: Element parameterElement = (Element) it2.next();
0644: if (parameterElement.getName().trim().equals("param")) {
0645: mixinDef.addParameter(parameterElement
0646: .attributeValue("name"), parameterElement
0647: .attributeValue("value"));
0648: }
0649: }
0650: }
0651:
0652: /**
0653: * Parses the pointcuts.
0654: *
0655: * @param aspectElement the aspect element
0656: * @param aspectDef the system definition
0657: */
0658: private static void parsePointcutElements(
0659: final Element aspectElement,
0660: final AspectDefinition aspectDef) {
0661: for (Iterator it2 = aspectElement.elementIterator(); it2
0662: .hasNext();) {
0663: Element pointcutElement = (Element) it2.next();
0664: if (pointcutElement.getName().trim().equals("pointcut")) {
0665: String name = pointcutElement.attributeValue("name");
0666: String expression = pointcutElement
0667: .attributeValue("expression");
0668: // pointcut CDATA is expression unless already specified as an attribute
0669: if (expression == null) {
0670: expression = pointcutElement.getTextTrim();
0671: }
0672: DefinitionParserHelper
0673: .createAndAddPointcutDefToAspectDef(name,
0674: expression, aspectDef);
0675: } else if (pointcutElement.getName().trim().equals(
0676: "deployment-scope")) {
0677: String name = pointcutElement.attributeValue("name");
0678: String expression = pointcutElement
0679: .attributeValue("expression");
0680: // pointcut CDATA is expression unless already specified as an attribute
0681: if (expression == null) {
0682: expression = pointcutElement.getTextTrim();
0683: }
0684: DefinitionParserHelper.createAndAddDeploymentScopeDef(
0685: name, expression, aspectDef
0686: .getSystemDefinition());
0687: } else if (pointcutElement.getName().trim().equals(
0688: "advisable")) {
0689: String expression = pointcutElement
0690: .attributeValue("expression");
0691: String pointcutTypes = pointcutElement
0692: .attributeValue("pointcut-type");
0693: if (expression == null) {
0694: expression = pointcutElement.getTextTrim();
0695: }
0696: handleAdvisableDefinition(aspectDef
0697: .getSystemDefinition(), expression,
0698: pointcutTypes);
0699: }
0700: }
0701: }
0702:
0703: /**
0704: * Parses the advices.
0705: *
0706: * @param aspectElement the aspect element
0707: * @param aspectDef the system definition
0708: * @param aspectClassInfo the aspect class
0709: */
0710: private static void parseAdviceElements(
0711: final Element aspectElement,
0712: final AspectDefinition aspectDef,
0713: final ClassInfo aspectClassInfo) {
0714: List methodList = ClassInfoHelper
0715: .createMethodList(aspectClassInfo);
0716: for (Iterator it2 = aspectElement.elementIterator(); it2
0717: .hasNext();) {
0718: Element adviceElement = (Element) it2.next();
0719: if (adviceElement.getName().trim().equals("advice")) {
0720: String name = adviceElement.attributeValue("name");
0721: String type = adviceElement.attributeValue("type");
0722: String bindTo = adviceElement.attributeValue("bind-to");
0723:
0724: String adviceName = name;
0725: MethodInfo method = null;
0726: for (Iterator it3 = methodList.iterator(); it3
0727: .hasNext();) {
0728: MethodInfo methodCurrent = (MethodInfo) it3.next();
0729: if (aspectDef.isAspectWerkzAspect()) {
0730: if (matchMethodAsAdvice(methodCurrent, name)) {
0731: method = methodCurrent;
0732: break;
0733: }
0734: } else {
0735: // TODO support matchMethodAsAdvice(..) for all aspect models? if so use stuff below
0736: // AspectModel aspectModel = AspectModelManager.getModelFor(aspectDef.getAspectModel());
0737: // if (aspectModel.matchMethodAsAdvice(methodCurrent, name)) {
0738: // method = methodCurrent;
0739: // break;
0740: // }
0741: if (methodCurrent.getName().equals(name)) {
0742: method = methodCurrent;
0743: break;
0744: }
0745: }
0746: }
0747: if (method == null) {
0748: throw new DefinitionException(
0749: "could not find advice method ["
0750: + name
0751: + "] in ["
0752: + aspectClassInfo.getName()
0753: + "] (are you using a compiler extension that you have not registered?)"
0754: + " (are you using XML defined advice, with StaticJoinPoint bindings without specifying the full"
0755: + "source like signature?)");
0756: }
0757: createAndAddAdviceDefsToAspectDef(type, bindTo,
0758: adviceName, method, aspectDef);
0759: for (Iterator it1 = adviceElement
0760: .elementIterator("bind-to"); it1.hasNext();) {
0761: Element bindToElement = (Element) it1.next();
0762: String pointcut = bindToElement
0763: .attributeValue("pointcut");
0764: createAndAddAdviceDefsToAspectDef(type, pointcut,
0765: adviceName, method, aspectDef);
0766: }
0767: }
0768: }
0769: }
0770:
0771: /**
0772: * Parses the interface introductions.
0773: *
0774: * @param aspectElement the aspect element
0775: * @param aspectDef the system definition
0776: * @param packageName
0777: * @param loader
0778: */
0779: private static void parseIntroduceElements(
0780: final Element aspectElement,
0781: final AspectDefinition aspectDef, final String packageName,
0782: final ClassLoader loader) {
0783: for (Iterator it2 = aspectElement.elementIterator(); it2
0784: .hasNext();) {
0785: Element introduceElement = (Element) it2.next();
0786: if (introduceElement.getName().trim().equals("introduce")) {
0787: String klass = introduceElement.attributeValue("class");
0788: String name = introduceElement.attributeValue("name");
0789: String bindTo = introduceElement
0790: .attributeValue("bind-to");
0791:
0792: // default name = FQN
0793: final String fullClassName = packageName + klass;
0794: if ((name == null) || (name.length() <= 0)) {
0795: name = fullClassName;
0796: }
0797:
0798: // load the class info to determine if it is a pure interface introduction
0799: ClassInfo introductionClassInfo;
0800: try {
0801: introductionClassInfo = AsmClassInfo.getClassInfo(
0802: fullClassName, loader);
0803: } catch (Exception e) {
0804: throw new DefinitionException(
0805: "could not find interface introduction: "
0806: + packageName + klass + " "
0807: + e.getMessage());
0808: }
0809:
0810: // pure interface introduction
0811: if (introductionClassInfo.isInterface()) {
0812: DefinitionParserHelper
0813: .createAndAddInterfaceIntroductionDefToAspectDef(
0814: bindTo, name, fullClassName,
0815: aspectDef);
0816:
0817: // handles nested "bind-to" elements
0818: for (Iterator it1 = introduceElement
0819: .elementIterator("bind-to"); it1.hasNext();) {
0820: Element bindToElement = (Element) it1.next();
0821: String pointcut = bindToElement
0822: .attributeValue("pointcut");
0823: DefinitionParserHelper
0824: .createAndAddInterfaceIntroductionDefToAspectDef(
0825: pointcut, name, fullClassName,
0826: aspectDef);
0827: }
0828: }
0829: }
0830: }
0831: }
0832:
0833: /**
0834: * Creates the advice definitions and adds them to the aspect definition.
0835: *
0836: * @param type the type of advice
0837: * @param bindTo the pointcut expresion
0838: * @param name the name of the advice
0839: * @param method the method implementing the advice
0840: * @param aspectDef the aspect definition
0841: */
0842: private static void createAndAddAdviceDefsToAspectDef(
0843: final String type, final String bindTo, final String name,
0844: final MethodInfo method, final AspectDefinition aspectDef) {
0845: try {
0846: if (type.equalsIgnoreCase("around")) {
0847: final String aspectName = aspectDef.getName();
0848: AdviceDefinition adviceDef = DefinitionParserHelper
0849: .createAdviceDefinition(name,
0850: AdviceType.AROUND, bindTo, null,
0851: aspectName, aspectDef.getClassName(),
0852: method, aspectDef);
0853: aspectDef.addAroundAdviceDefinition(adviceDef);
0854:
0855: } else if (type.equalsIgnoreCase("before")) {
0856: final String aspectName = aspectDef.getName();
0857: AdviceDefinition adviceDef = DefinitionParserHelper
0858: .createAdviceDefinition(name,
0859: AdviceType.BEFORE, bindTo, null,
0860: aspectName, aspectDef.getClassName(),
0861: method, aspectDef);
0862: aspectDef.addBeforeAdviceDefinition(adviceDef);
0863:
0864: } else if (type.startsWith("after")) {
0865: String specialArgumentType = null;
0866: AdviceType adviceType = AdviceType.AFTER;
0867: if (type.startsWith("after returning(")) {
0868: adviceType = AdviceType.AFTER_RETURNING;
0869: int start = type.indexOf('(');
0870: int end = type.indexOf(')');
0871: specialArgumentType = type
0872: .substring(start + 1, end).trim();
0873: } else if (type.startsWith("after throwing(")) {
0874: adviceType = AdviceType.AFTER_THROWING;
0875: int start = type.indexOf('(');
0876: int end = type.indexOf(')');
0877: specialArgumentType = type
0878: .substring(start + 1, end).trim();
0879: } else if (type.startsWith("after returning")) {
0880: adviceType = AdviceType.AFTER_RETURNING;
0881: } else if (type.startsWith("after throwing")) {
0882: adviceType = AdviceType.AFTER_THROWING;
0883: } else if (type.startsWith("after")) {
0884: adviceType = AdviceType.AFTER_FINALLY;
0885: } else if (type.startsWith("after finally")) {
0886: adviceType = AdviceType.AFTER_FINALLY;
0887: }
0888: if (specialArgumentType != null
0889: && specialArgumentType.indexOf(' ') > 0) {
0890: throw new DefinitionException(
0891: "argument to after (returning/throwing) can only be a type (parameter name binding should be done using args(..))");
0892: }
0893: final String aspectName = aspectDef.getName();
0894: AdviceDefinition adviceDef = DefinitionParserHelper
0895: .createAdviceDefinition(name, adviceType,
0896: bindTo, specialArgumentType,
0897: aspectName, aspectDef.getClassName(),
0898: method, aspectDef);
0899:
0900: aspectDef.addAfterAdviceDefinition(adviceDef);
0901: } else {
0902: throw new DefinitionException(
0903: "Unkonw type for advice : " + type);
0904: }
0905: } catch (DefinitionException e) {
0906: System.err.println("WARNING: unable to register advice "
0907: + aspectDef.getName() + "." + name
0908: + " at pointcut [" + bindTo + "] due to: "
0909: + e.getMessage());
0910: // TODO ALEX - better handling of reg issue (f.e. skip the whole aspect, in DocumentParser, based on DefinitionE
0911: }
0912: }
0913:
0914: /**
0915: * Retrieves and returns the package.
0916: *
0917: * @param packageElement the package element
0918: * @return the package as a string ending with DOT, or empty string
0919: */
0920: private static String getPackage(final Element packageElement) {
0921: String packageName = "";
0922: for (Iterator it2 = packageElement.attributeIterator(); it2
0923: .hasNext();) {
0924: Attribute attribute = (Attribute) it2.next();
0925: if (attribute.getName().trim().equalsIgnoreCase("name")) {
0926: packageName = attribute.getValue().trim();
0927: if (packageName.endsWith(".*")) {
0928: packageName = packageName.substring(0, packageName
0929: .length() - 1);
0930: } else if (packageName.endsWith(".")) {
0931: ; // skip
0932: } else {
0933: packageName += ".";
0934: }
0935: break;
0936: } else {
0937: continue;
0938: }
0939: }
0940: return packageName;
0941: }
0942:
0943: /**
0944: * Parses the <tt>include</tt> elements.
0945: *
0946: * @param root the root element
0947: * @param definition the definition object
0948: * @param packageName the package name
0949: */
0950: private static void parseIncludePackageElements(final Element root,
0951: final SystemDefinition definition, final String packageName) {
0952: for (Iterator it1 = root.elementIterator("include"); it1
0953: .hasNext();) {
0954: String includePackage = "";
0955: Element includeElement = (Element) it1.next();
0956: for (Iterator it2 = includeElement.attributeIterator(); it2
0957: .hasNext();) {
0958: Attribute attribute = (Attribute) it2.next();
0959: if (attribute.getName().trim().equalsIgnoreCase(
0960: "package")) {
0961: // handle base package
0962: if (packageName.endsWith(".*")) {
0963: includePackage = packageName.substring(0,
0964: packageName.length() - 2);
0965: } else if (packageName.endsWith(".")) {
0966: includePackage = packageName.substring(0,
0967: packageName.length() - 1);
0968: }
0969:
0970: // handle exclude package
0971: includePackage = packageName
0972: + attribute.getValue().trim();
0973: if (includePackage.endsWith(".*")) {
0974: includePackage = includePackage.substring(0,
0975: includePackage.length() - 2);
0976: } else if (includePackage.endsWith(".")) {
0977: includePackage = includePackage.substring(0,
0978: includePackage.length() - 1);
0979: }
0980: break;
0981: } else {
0982: continue;
0983: }
0984: }
0985: if (includePackage.length() != 0) {
0986: definition.addIncludePackage(includePackage);
0987: }
0988: }
0989: }
0990:
0991: /**
0992: * Parses the <tt>exclude</tt> elements.
0993: *
0994: * @param root the root element
0995: * @param definition the definition object
0996: * @param packageName the package name
0997: */
0998: private static void parseExcludePackageElements(final Element root,
0999: final SystemDefinition definition, final String packageName) {
1000: for (Iterator it1 = root.elementIterator("exclude"); it1
1001: .hasNext();) {
1002: String excludePackage = "";
1003: Element excludeElement = (Element) it1.next();
1004: for (Iterator it2 = excludeElement.attributeIterator(); it2
1005: .hasNext();) {
1006: Attribute attribute = (Attribute) it2.next();
1007: if (attribute.getName().trim().equalsIgnoreCase(
1008: "package")) {
1009: // handle base package
1010: if (packageName.endsWith(".*")) {
1011: excludePackage = packageName.substring(0,
1012: packageName.length() - 2);
1013: } else if (packageName.endsWith(".")) {
1014: excludePackage = packageName.substring(0,
1015: packageName.length() - 1);
1016: }
1017:
1018: // handle exclude package
1019: excludePackage = packageName
1020: + attribute.getValue().trim();
1021: if (excludePackage.endsWith(".*")) {
1022: excludePackage = excludePackage.substring(0,
1023: excludePackage.length() - 2);
1024: } else if (excludePackage.endsWith(".")) {
1025: excludePackage = excludePackage.substring(0,
1026: excludePackage.length() - 1);
1027: }
1028: break;
1029: } else {
1030: continue;
1031: }
1032: }
1033: if (excludePackage.length() != 0) {
1034: definition.addExcludePackage(excludePackage);
1035: }
1036: }
1037: }
1038:
1039: /**
1040: * Parses the <tt>prepare</tt> elements.
1041: *
1042: * @param root the root element
1043: * @param definition the definition object
1044: * @param packageName the base package name
1045: */
1046: public static void parsePrepareElements(final Element root,
1047: final SystemDefinition definition, final String packageName) {
1048: for (Iterator it1 = root.elementIterator("prepare"); it1
1049: .hasNext();) {
1050: String preparePackage = "";
1051: Element prepareElement = (Element) it1.next();
1052: for (Iterator it2 = prepareElement.attributeIterator(); it2
1053: .hasNext();) {
1054: Attribute attribute = (Attribute) it2.next();
1055: if (attribute.getName().trim().equals("package")) {
1056: // handle base package
1057: if (packageName.endsWith(".*")) {
1058: preparePackage = packageName.substring(0,
1059: packageName.length() - 2);
1060: } else if (packageName.endsWith(".")) {
1061: preparePackage = packageName.substring(0,
1062: packageName.length() - 1);
1063: }
1064:
1065: // handle prepare package
1066: preparePackage = packageName
1067: + attribute.getValue().trim();
1068: if (preparePackage.endsWith(".*")) {
1069: preparePackage = preparePackage.substring(0,
1070: preparePackage.length() - 2);
1071: } else if (preparePackage.endsWith(".")) {
1072: preparePackage = preparePackage.substring(0,
1073: preparePackage.length() - 1);
1074: }
1075: break;
1076: } else {
1077: continue;
1078: }
1079: }
1080: if (preparePackage.length() != 0) {
1081: definition.addPreparePackage(preparePackage);
1082: }
1083: }
1084: }
1085:
1086: /**
1087: * Retrieves and returns the base package for a system element
1088: *
1089: * @param system a system element
1090: * @return the base package
1091: */
1092: private static String getBasePackage(final Element system) {
1093: String basePackage = "";
1094: for (Iterator it2 = system.attributeIterator(); it2.hasNext();) {
1095: Attribute attribute = (Attribute) it2.next();
1096: if (attribute.getName().trim().equalsIgnoreCase(
1097: "base-package")) {
1098: basePackage = attribute.getValue().trim();
1099: if (basePackage.endsWith(".*")) {
1100: basePackage = basePackage.substring(0, basePackage
1101: .length() - 1);
1102: } else if (basePackage.endsWith(".")) {
1103: ; // skip
1104: } else {
1105: basePackage += ".";
1106: }
1107: break;
1108: } else {
1109: continue;
1110: }
1111: }
1112: return basePackage;
1113: }
1114:
1115: /**
1116: * Struct with pointcut info.
1117: */
1118: private static class PointcutInfo {
1119: public String name;
1120: public String expression;
1121: }
1122:
1123: /**
1124: * Check if a method from an aspect class match a given advice signature.
1125: * <br/>
1126: * If the signature is just a method name, then we have a match even if JoinPoint is sole method parameter.
1127: * Else we match both method name and parameters type, with abbreviation support (java.lang.* and JoinPoint)
1128: *
1129: * @param method
1130: * @param adviceSignature
1131: * @return
1132: */
1133: private static boolean matchMethodAsAdvice(MethodInfo method,
1134: String adviceSignature) {
1135: // grab components from adviceSignature
1136: //TODO catch AOOBE for better syntax error reporting
1137: String[] signatureElements = Strings
1138: .extractMethodSignature(adviceSignature);
1139:
1140: // check method name
1141: if (!method.getName().equals(signatureElements[0])) {
1142: return false;
1143: }
1144: // check number of args
1145: if (method.getParameterTypes().length * 2 != signatureElements.length - 1) {
1146: // we still match if method has "JoinPoint" has sole parameter
1147: // and adviceSignature has none
1148: if (signatureElements.length == 1
1149: && method.getParameterTypes().length == 1
1150: && (method.getParameterTypes()[0]
1151: .getName()
1152: .equals(
1153: TransformationConstants.JOIN_POINT_JAVA_CLASS_NAME) || method
1154: .getParameterTypes()[0]
1155: .getName()
1156: .equals(
1157: TransformationConstants.STATIC_JOIN_POINT_JAVA_CLASS_NAME))) {
1158: return true;
1159: } else {
1160: return false;
1161: }
1162: }
1163: int argIndex = 0;
1164: for (int i = 1; i < signatureElements.length; i++) {
1165: String paramType = signatureElements[i++];
1166: String methodParamType = method.getParameterTypes()[argIndex++]
1167: .getName();
1168: // handle shortcuts for java.lang.* and JoinPoint, StaticJoinPoint and Rtti
1169: String paramTypeResolved = (String) Pattern.ABBREVIATIONS
1170: .get(paramType);
1171: if (methodParamType.equals(paramType)
1172: || methodParamType.equals(paramTypeResolved)) {
1173: continue;
1174: } else {
1175: return false;
1176: }
1177: }
1178: return true;
1179: }
1180:
1181: /**
1182: * Handles the advisable definition.
1183: *
1184: * @param definition
1185: * @param withinPointcut
1186: * @param pointcutTypes
1187: */
1188: private static void handleAdvisableDefinition(
1189: final SystemDefinition definition,
1190: final String withinPointcut, final String pointcutTypes) {
1191: // add the Advisable Mixin with the expression defined to the system definitions
1192: definition.addMixinDefinition(DefinitionParserHelper
1193: .createAndAddMixinDefToSystemDef(
1194: AdvisableImpl.CLASS_INFO, withinPointcut,
1195: DeploymentModel.PER_INSTANCE, false, // advisble mixin is NOT transient
1196: definition));
1197:
1198: boolean hasAllPointcuts = false;
1199: boolean hasExecutionPointcut = false;
1200: boolean hasCallPointcut = false;
1201: boolean hasSetPointcut = false;
1202: boolean hasGetPointcut = false;
1203: boolean hasHandlerPointcut = false;
1204: if (pointcutTypes == null || pointcutTypes.equals("")
1205: || pointcutTypes.equalsIgnoreCase("all")) {
1206: hasAllPointcuts = true;
1207: } else {
1208: StringTokenizer tokenizer = new StringTokenizer(
1209: pointcutTypes, "|");
1210: while (tokenizer.hasMoreTokens()) {
1211: String token = tokenizer.nextToken();
1212: if (token.trim().equalsIgnoreCase("all")) {
1213: hasAllPointcuts = true;
1214: break;
1215: } else if (token.trim().equalsIgnoreCase("execution")) {
1216: hasExecutionPointcut = true;
1217: } else if (token.trim().equalsIgnoreCase("call")) {
1218: hasCallPointcut = true;
1219: } else if (token.trim().equalsIgnoreCase("set")) {
1220: hasSetPointcut = true;
1221: } else if (token.trim().equalsIgnoreCase("get")) {
1222: hasGetPointcut = true;
1223: } else if (token.trim().equalsIgnoreCase("handler")) {
1224: hasHandlerPointcut = true;
1225: }
1226: }
1227: }
1228: if (hasAllPointcuts || hasExecutionPointcut) {
1229: DefinitionParserHelper.createAndAddAdvisableDef(
1230: // TODO add ctor to expression - BUT: problem with mixin and ctor, ordering issue, Jp.invoke() calls field instance that has not been init yet in ctor (since body not invoked)
1231: //"(( execution(!static * *.*(..)) || execution(*.new(..)) ) && " + withinPointcut + ')',
1232: // we exclude static method execution since we need the advisable instance
1233: "(execution(!static * *.*(..)) && "
1234: + withinPointcut + ')', definition);
1235: }
1236: if (hasAllPointcuts || hasCallPointcut) {
1237: DefinitionParserHelper.createAndAddAdvisableDef(
1238: // TODO add ctor to expression - BUT: problem with mixin and ctor, ordering issue, Jp.invoke() calls field instance that has not been init yet in ctor (since body not invoked) //"(call(!static * " + typePattern + ".*(..)) || call(" + typePattern + ".new(..)))",
1239: // we exclude static method withincode since we need the advisable instance
1240: // as a consequence, withincode(staticinitialization(..)) is also excluded
1241: "(call(* *.*(..)) && withincode(!static * *.*(..)) && "
1242: + withinPointcut + ')', definition);
1243: }
1244: if (hasAllPointcuts || hasSetPointcut) {
1245: DefinitionParserHelper.createAndAddAdvisableDef(
1246: // we exclude static method withincode since we need the advisable instance
1247: // as a consequence, withincode(staticinitialization(..)) is also excluded
1248: "(set(* *.*) && withincode(!static * *.*(..)) && "
1249: + withinPointcut + ')', definition);
1250: }
1251: if (hasAllPointcuts || hasGetPointcut) {
1252: DefinitionParserHelper.createAndAddAdvisableDef(
1253: // we exclude static method withincode since we need the advisable instance
1254: // as a consequence, withincode(staticinitialization(..)) is also excluded
1255: "(get(* *.*) && withincode(!static * *.*(..)) && "
1256: + withinPointcut + ')', definition);
1257: }
1258: if (hasAllPointcuts || hasHandlerPointcut) {
1259: DefinitionParserHelper.createAndAddAdvisableDef(
1260: // we exclude static method withincode since we need the advisable instance
1261: // as a consequence, withincode(staticinitialization(..)) is also excluded
1262: "(handler(*..*) && withincode(!static * *.*(..)) && "
1263: + withinPointcut + ')', definition);
1264: }
1265: }
1266: }
|