/************************************************************************************** * Copyright (c) Jonas Bon�r, Alexandre Vasseur. All rights reserved. * * http://aspectwerkz.codehaus.org * * ---------------------------------------------------------------------------------- * * The software in this package is published under the terms of the LGPL license * * a copy of which has been included with this distribution in the license.txt file. * **************************************************************************************/ package org.codehaus.aspectwerkz.definition; import org.codehaus.aspectwerkz.util.Strings; import org.codehaus.aspectwerkz.aspect.AdviceType; import org.codehaus.aspectwerkz.DeploymentModel; import org.codehaus.aspectwerkz.intercept.AdvisableImpl; import org.codehaus.aspectwerkz.reflect.impl.asm.AsmClassInfo; import org.codehaus.aspectwerkz.reflect.impl.java.JavaMethodInfo; import org.codehaus.aspectwerkz.reflect.impl.java.JavaClassInfo; import org.codehaus.aspectwerkz.reflect.ClassInfo; import org.codehaus.aspectwerkz.reflect.ClassInfoHelper; import org.codehaus.aspectwerkz.reflect.MethodInfo; import org.codehaus.aspectwerkz.expression.regexp.Pattern; import org.codehaus.aspectwerkz.expression.ExpressionNamespace; import org.codehaus.aspectwerkz.expression.ExpressionInfo; import org.codehaus.aspectwerkz.annotation.AspectAnnotationParser; import org.codehaus.aspectwerkz.annotation.MixinAnnotationParser; import org.codehaus.aspectwerkz.exception.DefinitionException; import org.codehaus.aspectwerkz.transform.TransformationConstants; import org.codehaus.aspectwerkz.transform.inlining.AspectModelManager; import org.dom4j.Attribute; import org.dom4j.Document; import org.dom4j.Element; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Set; import java.util.HashSet; import java.util.StringTokenizer; /** * Parses the XML definition using <tt>dom4j</tt>. * * @author <a href="mailto:jboner@codehaus.org">Jonas Bon�r </a> * @author <a href="mailto:alex@gnilux.com">Alexandre Vasseur </a> */ public class DocumentParser { /** * Parses aspect class names. * * @param document the defintion as a document * @return the aspect class names */ public static List parseAspectClassNames(final Document document) { final List aspectClassNames = new ArrayList(); for (Iterator it1 = document.getRootElement().elementIterator("system"); it1.hasNext();) { Element system = (Element) it1.next(); final String basePackage = getBasePackage(system); for (Iterator it11 = system.elementIterator("aspect"); it11.hasNext();) { String className = null; Element aspect = (Element) it11.next(); for (Iterator it2 = aspect.attributeIterator(); it2.hasNext();) { Attribute attribute = (Attribute) it2.next(); final String name = attribute.getName().trim(); final String value = attribute.getValue().trim(); if (name.equalsIgnoreCase("class")) { className = value; } } aspectClassNames.add(basePackage + className); } for (Iterator it11 = system.elementIterator("package"); it11.hasNext();) { final Element packageElement = ((Element) it11.next()); final String packageName = getPackage(packageElement); for (Iterator it12 = packageElement.elementIterator("aspect"); it12.hasNext();) { String className = null; Element aspect = (Element) it12.next(); for (Iterator it2 = aspect.attributeIterator(); it2.hasNext();) { Attribute attribute = (Attribute) it2.next(); final String name = attribute.getName().trim(); final String value = attribute.getValue().trim(); if (name.equalsIgnoreCase("class")) { className = value; } } aspectClassNames.add(packageName + className); } } } aspectClassNames.add(Virtual.class.getName()); return aspectClassNames; } /** * Parses the definition DOM document. * * @param document the defintion as a document * @param systemDef the system definition * @param aspectClass the aspect class * @return the definition */ public static AspectDefinition parseAspectDefinition(final Document document, final SystemDefinition systemDef, final Class aspectClass) { final Element aspect = document.getRootElement(); if (!aspect.getName().equals("aspect")) { throw new DefinitionException("XML definition for aspect is not well-formed: " + document.asXML()); } String specialAspectName = null; String className = null; String deploymentModelAsString = null; String containerClassName = null; for (Iterator it2 = aspect.attributeIterator(); it2.hasNext();) { Attribute attribute = (Attribute) it2.next(); final String name = attribute.getName().trim(); final String value = attribute.getValue().trim(); if (name.equalsIgnoreCase("class")) { className = value; } else if (name.equalsIgnoreCase("deployment-model")) { deploymentModelAsString = value; } else if (name.equalsIgnoreCase("name")) { specialAspectName = value; } else if (name.equalsIgnoreCase("container")) { containerClassName = value; } } if (specialAspectName == null) { specialAspectName = className; } final ClassInfo classInfo = JavaClassInfo.getClassInfo(aspectClass); final ClassLoader loader = aspectClass.getClassLoader(); // create the aspect definition final AspectDefinition aspectDef = new AspectDefinition(specialAspectName, classInfo, systemDef); //TODO: if this XML centric deployment is supposed to PRESERVE @Aspect values, then it is broken aspectDef.setContainerClassName(containerClassName); aspectDef.setDeploymentModel(DeploymentModel.getDeploymentModelFor(deploymentModelAsString)); parsePointcutElements(aspect, aspectDef); //needed to support undefined named pointcut in Attributes AW-152 // load the different aspect model and let them define their aspects AspectModelManager.defineAspect(classInfo, aspectDef, loader); // parse the aspect info parseParameterElements(aspect, aspectDef); parsePointcutElements(aspect, aspectDef); //reparse pc for XML override (AW-152) parseAdviceElements(aspect, aspectDef, JavaClassInfo.getClassInfo(aspectClass)); parseIntroduceElements(aspect, aspectDef, "", aspectClass.getClassLoader()); systemDef.addAspect(aspectDef); return aspectDef; } /** * Parses the definition DOM document. * * @param loader the current class loader * @param document the defintion as a document * @return the definitions */ public static Set parse(final ClassLoader loader, final Document document) { final Element root = document.getRootElement(); // parse the transformation scopes return parseSystemElements(loader, root); } /** * Parses the <tt>system</tt> elements. * * @param loader the current class loader * @param root the root element */ private static Set parseSystemElements(final ClassLoader loader, final Element root) { final Set systemDefs = new HashSet(); for (Iterator it1 = root.elementIterator("system"); it1.hasNext();) { Element system = (Element) it1.next(); SystemDefinition definition = parseSystemElement(loader, system, getBasePackage(system)); if (definition != null) { systemDefs.add(definition); } } return systemDefs; } /** * Parses the <tt>system</tt> elements. * * @param loader the current class loader * @param systemElement the system element * @param basePackage the base package * @return the definition for the system */ private static SystemDefinition parseSystemElement(final ClassLoader loader, final Element systemElement, final String basePackage) { String uuid = systemElement.attributeValue("id"); if ((uuid == null) || uuid.equals("")) { throw new DefinitionException("system UUID must be specified"); } final SystemDefinition definition = new SystemDefinition(uuid); // add the virtual aspect addVirtualAspect(definition); // parse the global pointcuts List globalPointcuts = parseGlobalPointcutDefs(systemElement); //FIXME: systemDef should link a namespace, + remove static hashmap in Namespace (uuid clash in parallel CL) ExpressionNamespace systemNamespace = ExpressionNamespace.getNamespace(definition.getUuid()); for (Iterator iterator = globalPointcuts.iterator(); iterator.hasNext();) { PointcutInfo pointcutInfo = (PointcutInfo) iterator.next(); systemNamespace.addExpressionInfo( pointcutInfo.name, new ExpressionInfo(pointcutInfo.expression, systemNamespace.getName()) ); } // parse the global deployment scopes definitions parseDeploymentScopeDefs(systemElement, definition); // parse the global advisable definitions parseAdvisableDefs(systemElement, definition); // parse the include, exclude and prepare elements parseIncludePackageElements(systemElement, definition, basePackage); parseExcludePackageElements(systemElement, definition, basePackage); parsePrepareElements(systemElement, definition, basePackage); // parse without package elements parseAspectElements(loader, systemElement, definition, basePackage, globalPointcuts); // parse without package elements parseMixinElements(loader, systemElement, definition, basePackage); // parse with package elements parsePackageElements(loader, systemElement, definition, basePackage, globalPointcuts); // add all deployment scopes to the virtual advice DefinitionParserHelper.attachDeploymentScopeDefsToVirtualAdvice(definition); return definition; } /** * Parses the global pointcuts. * * @param systemElement the system element * @return a list with the pointcuts */ private static List parseGlobalPointcutDefs(final Element systemElement) { final List globalPointcuts = new ArrayList(); for (Iterator it11 = systemElement.elementIterator("pointcut"); it11.hasNext();) { PointcutInfo pointcutInfo = new PointcutInfo(); Element globalPointcut = (Element) it11.next(); for (Iterator it2 = globalPointcut.attributeIterator(); it2.hasNext();) { Attribute attribute = (Attribute) it2.next(); final String name = attribute.getName().trim(); final String value = attribute.getValue().trim(); if (name.equalsIgnoreCase("name")) { pointcutInfo.name = value; } else if (name.equalsIgnoreCase("expression")) { pointcutInfo.expression = value; } } // pointcut CDATA is expression unless already specified as an attribute if (pointcutInfo.expression == null) { pointcutInfo.expression = globalPointcut.getTextTrim(); } globalPointcuts.add(pointcutInfo); } return globalPointcuts; } /** * Parses the global deployment-scope elements. * * @param systemElement the system element * @param definition */ private static void parseDeploymentScopeDefs(final Element systemElement, final SystemDefinition definition) { for (Iterator it11 = systemElement.elementIterator("deployment-scope"); it11.hasNext();) { String expression = null; String name = null; Element globalPointcut = (Element) it11.next(); for (Iterator it2 = globalPointcut.attributeIterator(); it2.hasNext();) { Attribute attribute = (Attribute) it2.next(); final String attrName = attribute.getName().trim(); final String attrValue = attribute.getValue().trim(); if (attrName.equalsIgnoreCase("name")) { name = attrValue; } else if (attrName.equalsIgnoreCase("expression")) { expression = attrValue; } } // pointcut CDATA is expression unless already specified as an attribute if (expression == null) { expression = globalPointcut.getTextTrim(); } DefinitionParserHelper.createAndAddDeploymentScopeDef(name, expression, definition); } } /** * Parses the global advisable elements. * * @param systemElement the system element * @param definition */ private static void parseAdvisableDefs(final Element systemElement, final SystemDefinition definition) { for (Iterator it11 = systemElement.elementIterator("advisable"); it11.hasNext();) { Element advisableElement = (Element) it11.next(); String expression = ""; String pointcutTypes = "all"; for (Iterator it2 = advisableElement.attributeIterator(); it2.hasNext();) { Attribute attribute = (Attribute) it2.next(); final String name = attribute.getName().trim(); final String value = attribute.getValue().trim(); if (name.equalsIgnoreCase("expression")) { expression = value; } else if (name.equalsIgnoreCase("pointcut-type")) { pointcutTypes = value; } } // pointcut CDATA is expression unless already specified as an attribute if (expression == null) { expression = advisableElement.getTextTrim(); } handleAdvisableDefinition(definition, expression, pointcutTypes); } } /** * Parses the definition DOM document. * * @param loader the current class loader * @param systemElement the system element * @param definition the definition * @param basePackage the base package * @param globalPointcuts the global pointcuts */ private static void parsePackageElements(final ClassLoader loader, final Element systemElement, final SystemDefinition definition, final String basePackage, final List globalPointcuts) { for (Iterator it1 = systemElement.elementIterator("package"); it1.hasNext();) { final Element packageElement = ((Element) it1.next()); final String packageName = basePackage + getPackage(packageElement); parseAspectElements(loader, packageElement, definition, packageName, globalPointcuts); parseMixinElements(loader, packageElement, definition, packageName); parseAdvisableDefs(packageElement, definition); } } /** * Parses the <tt>aspect</tt> elements. * * @param loader the current class loader * @param systemElement the system element * @param definition the definition object * @param packageName the package name * @param globalPointcuts the global pointcuts */ private static void parseAspectElements(final ClassLoader loader, final Element systemElement, final SystemDefinition definition, final String packageName, final List globalPointcuts) { for (Iterator it1 = systemElement.elementIterator("aspect"); it1.hasNext();) { String aspectName = null; String className = null; String deploymentModel = null; String containerClassName = null; Element aspect = (Element) it1.next(); for (Iterator it2 = aspect.attributeIterator(); it2.hasNext();) { Attribute attribute = (Attribute) it2.next(); final String name = attribute.getName().trim(); final String value = attribute.getValue().trim(); if (name.equalsIgnoreCase("class")) { className = value; } else if (name.equalsIgnoreCase("deployment-model")) { deploymentModel = value; } else if (name.equalsIgnoreCase("name")) { aspectName = value; } else if (name.equalsIgnoreCase("container")) { containerClassName = value; } } // class is mandatory if (Strings.isNullOrEmpty(className)) { System.err.println("Warning: could not load aspect without 'class=..' attribute"); new Exception().printStackTrace(); continue; } String aspectClassName = packageName + className; if (aspectName == null) { aspectName = aspectClassName; } // create the aspect definition ClassInfo aspectClassInfo; try { aspectClassInfo = AsmClassInfo.getClassInfo(aspectClassName, loader); } catch (Exception e) { System.err.println( "Warning: could not load aspect " + aspectClassName + " from " + loader + "due to: " + e.toString() ); e.printStackTrace(); continue; } final AspectDefinition aspectDef = new AspectDefinition(aspectName, aspectClassInfo, definition); // add the global pointcuts to the aspect for (Iterator it = globalPointcuts.iterator(); it.hasNext();) { PointcutInfo pointcutInfo = (PointcutInfo) it.next(); DefinitionParserHelper.createAndAddPointcutDefToAspectDef( pointcutInfo.name, pointcutInfo.expression, aspectDef ); } parsePointcutElements(aspect, aspectDef); //needed to support undefined named pointcut in Attributes AW-152 // load the different aspect model and let them define their aspects AspectModelManager.defineAspect(aspectClassInfo, aspectDef, loader); // parse the class bytecode annotations AspectAnnotationParser.parse(aspectClassInfo, aspectDef, loader); // XML definition settings always overrides attribute definition settings // AW-357 if (!Strings.isNullOrEmpty(deploymentModel)) { aspectDef.setDeploymentModel(DeploymentModel.getDeploymentModelFor(deploymentModel)); } if (!Strings.isNullOrEmpty(aspectName)) { aspectDef.setName(aspectName); } if (!Strings.isNullOrEmpty(containerClassName)) { aspectDef.setContainerClassName(containerClassName); } // parse the aspect info parseParameterElements(aspect, aspectDef); parsePointcutElements(aspect, aspectDef); //reparse pc for XML override (AW-152) parseAdviceElements(aspect, aspectDef, aspectClassInfo); parseIntroduceElements(aspect, aspectDef, packageName, loader); definition.addAspect(aspectDef); } } /** * Parses the <tt>mixin</tt> elements. * * @param loader the current class loader * @param systemElement the system element * @param systemDefinition the system definition * @param packageName the package name */ private static void parseMixinElements(final ClassLoader loader, final Element systemElement, final SystemDefinition systemDefinition, final String packageName) { for (Iterator it1 = systemElement.elementIterator("mixin"); it1.hasNext();) { String className = null; String deploymentModelAsString = null; boolean isTransient = false; boolean isTransientSetInXML = false; String factoryClassName = null; String expression = null; Element mixin = (Element) it1.next(); for (Iterator it2 = mixin.attributeIterator(); it2.hasNext();) { Attribute attribute = (Attribute) it2.next(); final String name = attribute.getName().trim(); final String value = attribute.getValue().trim(); if (name.equalsIgnoreCase("class")) { className = value; } else if (name.equalsIgnoreCase("deployment-model") && value != null) { deploymentModelAsString = value; } else if (name.equalsIgnoreCase("transient")) { if (value != null && value.equalsIgnoreCase("true")) { isTransient = true; isTransientSetInXML = true; } } else if (name.equalsIgnoreCase("factory")) { factoryClassName = value; } else if (name.equalsIgnoreCase("bind-to")) { expression = value; } } String mixinClassName = packageName + className; // create the mixin definition ClassInfo mixinClassInfo; try { mixinClassInfo = AsmClassInfo.getClassInfo(mixinClassName, loader); } catch (Exception e) { System.err.println( "Warning: could not load mixin " + mixinClassName + " from " + loader + "due to: " + e.toString() ); e.printStackTrace(); continue; } final DeploymentModel deploymentModel = (deploymentModelAsString != null) ? DeploymentModel.getDeploymentModelFor(deploymentModelAsString) : DeploymentModel.PER_INSTANCE; final MixinDefinition mixinDefinition = DefinitionParserHelper.createAndAddMixinDefToSystemDef( mixinClassInfo, expression, deploymentModel, isTransient, systemDefinition ); // parse the class bytecode annotations MixinAnnotationParser.parse(mixinClassInfo, mixinDefinition); // XML definition settings always overrides attribute definition settings if present if (!Strings.isNullOrEmpty(deploymentModelAsString)) { mixinDefinition.setDeploymentModel(DeploymentModel.getDeploymentModelFor(deploymentModelAsString)); } if (!Strings.isNullOrEmpty(factoryClassName)) { mixinDefinition.setFactoryClassName(factoryClassName); } if (isTransientSetInXML) { mixinDefinition.setTransient(isTransient); } parseParameterElements(mixin, mixinDefinition); } } /** * Adds a virtual system aspect to the definition. Needed to do various tricks. * * @param definition */ public static void addVirtualAspect(final SystemDefinition definition) { final Class clazz = Virtual.class; final String aspectName = clazz.getName(); ClassInfo aspectClassInfo = JavaClassInfo.getClassInfo(clazz); final AspectDefinition aspectDef = new AspectDefinition(aspectName, aspectClassInfo, definition); try { MethodInfo methodInfo = JavaMethodInfo.getMethodInfo(clazz.getDeclaredMethod("virtual", new Class[]{})); aspectDef.addBeforeAdviceDefinition( new AdviceDefinition( methodInfo.getName(), AdviceType.BEFORE, null, aspectName, aspectName, null, methodInfo, aspectDef ) ); } catch (NoSuchMethodException e) { throw new Error("virtual aspect [" + aspectName + "] does not have expected method: " + e.toString()); } definition.addAspect(aspectDef); } /** * Parses the aspectElement parameters. * * @param aspectElement the aspect element * @param aspectDef the aspect def */ private static void parseParameterElements(final Element aspectElement, final AspectDefinition aspectDef) { for (Iterator it2 = aspectElement.elementIterator(); it2.hasNext();) { Element parameterElement = (Element) it2.next(); if (parameterElement.getName().trim().equals("param")) { aspectDef.addParameter( parameterElement.attributeValue("name"), parameterElement.attributeValue("value") ); } } } /** * Parses the mixinElement parameters. * * @param mixinElement the mixin element * @param mixinDef the mixin def */ private static void parseParameterElements(final Element mixinElement, final MixinDefinition mixinDef) { for (Iterator it2 = mixinElement.elementIterator(); it2.hasNext();) { Element parameterElement = (Element) it2.next(); if (parameterElement.getName().trim().equals("param")) { mixinDef.addParameter( parameterElement.attributeValue("name"), parameterElement.attributeValue("value") ); } } } /** * Parses the pointcuts. * * @param aspectElement the aspect element * @param aspectDef the system definition */ private static void parsePointcutElements(final Element aspectElement, final AspectDefinition aspectDef) { for (Iterator it2 = aspectElement.elementIterator(); it2.hasNext();) { Element pointcutElement = (Element) it2.next(); if (pointcutElement.getName().trim().equals("pointcut")) { String name = pointcutElement.attributeValue("name"); String expression = pointcutElement.attributeValue("expression"); // pointcut CDATA is expression unless already specified as an attribute if (expression == null) { expression = pointcutElement.getTextTrim(); } DefinitionParserHelper.createAndAddPointcutDefToAspectDef(name, expression, aspectDef); } else if (pointcutElement.getName().trim().equals("deployment-scope")) { String name = pointcutElement.attributeValue("name"); String expression = pointcutElement.attributeValue("expression"); // pointcut CDATA is expression unless already specified as an attribute if (expression == null) { expression = pointcutElement.getTextTrim(); } DefinitionParserHelper.createAndAddDeploymentScopeDef( name, expression, aspectDef.getSystemDefinition() ); } else if (pointcutElement.getName().trim().equals("advisable")) { String expression = pointcutElement.attributeValue("expression"); String pointcutTypes = pointcutElement.attributeValue("pointcut-type"); if (expression == null) { expression = pointcutElement.getTextTrim(); } handleAdvisableDefinition(aspectDef.getSystemDefinition(), expression, pointcutTypes); } } } /** * Parses the advices. * * @param aspectElement the aspect element * @param aspectDef the system definition * @param aspectClassInfo the aspect class */ private static void parseAdviceElements(final Element aspectElement, final AspectDefinition aspectDef, final ClassInfo aspectClassInfo) { List methodList = ClassInfoHelper.createMethodList(aspectClassInfo); for (Iterator it2 = aspectElement.elementIterator(); it2.hasNext();) { Element adviceElement = (Element) it2.next(); if (adviceElement.getName().trim().equals("advice")) { String name = adviceElement.attributeValue("name"); String type = adviceElement.attributeValue("type"); String bindTo = adviceElement.attributeValue("bind-to"); String adviceName = name; MethodInfo method = null; for (Iterator it3 = methodList.iterator(); it3.hasNext();) { MethodInfo methodCurrent = (MethodInfo) it3.next(); if (aspectDef.isAspectWerkzAspect()) { if (matchMethodAsAdvice(methodCurrent, name)) { method = methodCurrent; break; } } else { // TODO support matchMethodAsAdvice(..) for all aspect models? if so use stuff below // AspectModel aspectModel = AspectModelManager.getModelFor(aspectDef.getAspectModel()); // if (aspectModel.matchMethodAsAdvice(methodCurrent, name)) { // method = methodCurrent; // break; // } if (methodCurrent.getName().equals(name)) { method = methodCurrent; break; } } } if (method == null) { throw new DefinitionException( "could not find advice method [" + name + "] in [" + aspectClassInfo.getName() + "] (are you using a compiler extension that you have not registered?)"+ " (are you using XML defined advice, with StaticJoinPoint bindings without specifying the full" + "source like signature?)" ); } createAndAddAdviceDefsToAspectDef(type, bindTo, adviceName, method, aspectDef); for (Iterator it1 = adviceElement.elementIterator("bind-to"); it1.hasNext();) { Element bindToElement = (Element) it1.next(); String pointcut = bindToElement.attributeValue("pointcut"); createAndAddAdviceDefsToAspectDef(type, pointcut, adviceName, method, aspectDef); } } } } /** * Parses the interface introductions. * * @param aspectElement the aspect element * @param aspectDef the system definition * @param packageName * @param loader */ private static void parseIntroduceElements(final Element aspectElement, final AspectDefinition aspectDef, final String packageName, final ClassLoader loader) { for (Iterator it2 = aspectElement.elementIterator(); it2.hasNext();) { Element introduceElement = (Element) it2.next(); if (introduceElement.getName().trim().equals("introduce")) { String klass = introduceElement.attributeValue("class"); String name = introduceElement.attributeValue("name"); String bindTo = introduceElement.attributeValue("bind-to"); // default name = FQN final String fullClassName = packageName + klass; if ((name == null) || (name.length() <= 0)) { name = fullClassName; } // load the class info to determine if it is a pure interface introduction ClassInfo introductionClassInfo; try { introductionClassInfo = AsmClassInfo.getClassInfo(fullClassName, loader); } catch (Exception e) { throw new DefinitionException( "could not find interface introduction: " + packageName + klass + " " + e.getMessage() ); } // pure interface introduction if (introductionClassInfo.isInterface()) { DefinitionParserHelper.createAndAddInterfaceIntroductionDefToAspectDef( bindTo, name, fullClassName, aspectDef ); // handles nested "bind-to" elements for (Iterator it1 = introduceElement.elementIterator("bind-to"); it1.hasNext();) { Element bindToElement = (Element) it1.next(); String pointcut = bindToElement.attributeValue("pointcut"); DefinitionParserHelper.createAndAddInterfaceIntroductionDefToAspectDef( pointcut, name, fullClassName, aspectDef ); } } } } } /** * Creates the advice definitions and adds them to the aspect definition. * * @param type the type of advice * @param bindTo the pointcut expresion * @param name the name of the advice * @param method the method implementing the advice * @param aspectDef the aspect definition */ private static void createAndAddAdviceDefsToAspectDef(final String type, final String bindTo, final String name, final MethodInfo method, final AspectDefinition aspectDef) { try { if (type.equalsIgnoreCase("around")) { final String aspectName = aspectDef.getName(); AdviceDefinition adviceDef = DefinitionParserHelper.createAdviceDefinition( name, AdviceType.AROUND, bindTo, null, aspectName, aspectDef.getClassName(), method, aspectDef ); aspectDef.addAroundAdviceDefinition(adviceDef); } else if (type.equalsIgnoreCase("before")) { final String aspectName = aspectDef.getName(); AdviceDefinition adviceDef = DefinitionParserHelper.createAdviceDefinition( name, AdviceType.BEFORE, bindTo, null, aspectName, aspectDef.getClassName(), method, aspectDef ); aspectDef.addBeforeAdviceDefinition(adviceDef); } else if (type.startsWith("after")) { String specialArgumentType = null; AdviceType adviceType = AdviceType.AFTER; if (type.startsWith("after returning(")) { adviceType = AdviceType.AFTER_RETURNING; int start = type.indexOf('('); int end = type.indexOf(')'); specialArgumentType = type.substring(start + 1, end).trim(); } else if (type.startsWith("after throwing(")) { adviceType = AdviceType.AFTER_THROWING; int start = type.indexOf('('); int end = type.indexOf(')'); specialArgumentType = type.substring(start + 1, end).trim(); } else if (type.startsWith("after returning")) { adviceType = AdviceType.AFTER_RETURNING; } else if (type.startsWith("after throwing")) { adviceType = AdviceType.AFTER_THROWING; } else if (type.startsWith("after")) { adviceType = AdviceType.AFTER_FINALLY; } else if (type.startsWith("after finally")) { adviceType = AdviceType.AFTER_FINALLY; } if (specialArgumentType != null && specialArgumentType.indexOf(' ') > 0) { throw new DefinitionException( "argument to after (returning/throwing) can only be a type (parameter name binding should be done using args(..))" ); } final String aspectName = aspectDef.getName(); AdviceDefinition adviceDef = DefinitionParserHelper.createAdviceDefinition( name, adviceType, bindTo, specialArgumentType, aspectName, aspectDef.getClassName(), method, aspectDef ); aspectDef.addAfterAdviceDefinition(adviceDef); } else { throw new DefinitionException("Unkonw type for advice : " + type); } } catch (DefinitionException e) { System.err.println( "WARNING: unable to register advice " + aspectDef.getName() + "." + name + " at pointcut [" + bindTo + "] due to: " + e.getMessage() ); // TODO ALEX - better handling of reg issue (f.e. skip the whole aspect, in DocumentParser, based on DefinitionE } } /** * Retrieves and returns the package. * * @param packageElement the package element * @return the package as a string ending with DOT, or empty string */ private static String getPackage(final Element packageElement) { String packageName = ""; for (Iterator it2 = packageElement.attributeIterator(); it2.hasNext();) { Attribute attribute = (Attribute) it2.next(); if (attribute.getName().trim().equalsIgnoreCase("name")) { packageName = attribute.getValue().trim(); if (packageName.endsWith(".*")) { packageName = packageName.substring(0, packageName.length() - 1); } else if (packageName.endsWith(".")) { ; // skip } else { packageName += "."; } break; } else { continue; } } return packageName; } /** * Parses the <tt>include</tt> elements. * * @param root the root element * @param definition the definition object * @param packageName the package name */ private static void parseIncludePackageElements(final Element root, final SystemDefinition definition, final String packageName) { for (Iterator it1 = root.elementIterator("include"); it1.hasNext();) { String includePackage = ""; Element includeElement = (Element) it1.next(); for (Iterator it2 = includeElement.attributeIterator(); it2.hasNext();) { Attribute attribute = (Attribute) it2.next(); if (attribute.getName().trim().equalsIgnoreCase("package")) { // handle base package if (packageName.endsWith(".*")) { includePackage = packageName.substring(0, packageName.length() - 2); } else if (packageName.endsWith(".")) { includePackage = packageName.substring(0, packageName.length() - 1); } // handle exclude package includePackage = packageName + attribute.getValue().trim(); if (includePackage.endsWith(".*")) { includePackage = includePackage.substring(0, includePackage.length() - 2); } else if (includePackage.endsWith(".")) { includePackage = includePackage.substring(0, includePackage.length() - 1); } break; } else { continue; } } if (includePackage.length() != 0) { definition.addIncludePackage(includePackage); } } } /** * Parses the <tt>exclude</tt> elements. * * @param root the root element * @param definition the definition object * @param packageName the package name */ private static void parseExcludePackageElements(final Element root, final SystemDefinition definition, final String packageName) { for (Iterator it1 = root.elementIterator("exclude"); it1.hasNext();) { String excludePackage = ""; Element excludeElement = (Element) it1.next(); for (Iterator it2 = excludeElement.attributeIterator(); it2.hasNext();) { Attribute attribute = (Attribute) it2.next(); if (attribute.getName().trim().equalsIgnoreCase("package")) { // handle base package if (packageName.endsWith(".*")) { excludePackage = packageName.substring(0, packageName.length() - 2); } else if (packageName.endsWith(".")) { excludePackage = packageName.substring(0, packageName.length() - 1); } // handle exclude package excludePackage = packageName + attribute.getValue().trim(); if (excludePackage.endsWith(".*")) { excludePackage = excludePackage.substring(0, excludePackage.length() - 2); } else if (excludePackage.endsWith(".")) { excludePackage = excludePackage.substring(0, excludePackage.length() - 1); } break; } else { continue; } } if (excludePackage.length() != 0) { definition.addExcludePackage(excludePackage); } } } /** * Parses the <tt>prepare</tt> elements. * * @param root the root element * @param definition the definition object * @param packageName the base package name */ public static void parsePrepareElements(final Element root, final SystemDefinition definition, final String packageName) { for (Iterator it1 = root.elementIterator("prepare"); it1.hasNext();) { String preparePackage = ""; Element prepareElement = (Element) it1.next(); for (Iterator it2 = prepareElement.attributeIterator(); it2.hasNext();) { Attribute attribute = (Attribute) it2.next(); if (attribute.getName().trim().equals("package")) { // handle base package if (packageName.endsWith(".*")) { preparePackage = packageName.substring(0, packageName.length() - 2); } else if (packageName.endsWith(".")) { preparePackage = packageName.substring(0, packageName.length() - 1); } // handle prepare package preparePackage = packageName + attribute.getValue().trim(); if (preparePackage.endsWith(".*")) { preparePackage = preparePackage.substring(0, preparePackage.length() - 2); } else if (preparePackage.endsWith(".")) { preparePackage = preparePackage.substring(0, preparePackage.length() - 1); } break; } else { continue; } } if (preparePackage.length() != 0) { definition.addPreparePackage(preparePackage); } } } /** * Retrieves and returns the base package for a system element * * @param system a system element * @return the base package */ private static String getBasePackage(final Element system) { String basePackage = ""; for (Iterator it2 = system.attributeIterator(); it2.hasNext();) { Attribute attribute = (Attribute) it2.next(); if (attribute.getName().trim().equalsIgnoreCase("base-package")) { basePackage = attribute.getValue().trim(); if (basePackage.endsWith(".*")) { basePackage = basePackage.substring(0, basePackage.length() - 1); } else if (basePackage.endsWith(".")) { ; // skip } else { basePackage += "."; } break; } else { continue; } } return basePackage; } /** * Struct with pointcut info. */ private static class PointcutInfo { public String name; public String expression; } /** * Check if a method from an aspect class match a given advice signature. * <br/> * If the signature is just a method name, then we have a match even if JoinPoint is sole method parameter. * Else we match both method name and parameters type, with abbreviation support (java.lang.* and JoinPoint) * * @param method * @param adviceSignature * @return */ private static boolean matchMethodAsAdvice(MethodInfo method, String adviceSignature) { // grab components from adviceSignature //TODO catch AOOBE for better syntax error reporting String[] signatureElements = Strings.extractMethodSignature(adviceSignature); // check method name if (!method.getName().equals(signatureElements[0])) { return false; } // check number of args if (method.getParameterTypes().length * 2 != signatureElements.length - 1) { // we still match if method has "JoinPoint" has sole parameter // and adviceSignature has none if (signatureElements.length == 1 && method.getParameterTypes().length == 1 && (method.getParameterTypes()[0].getName().equals(TransformationConstants.JOIN_POINT_JAVA_CLASS_NAME) || method.getParameterTypes()[0].getName().equals(TransformationConstants.STATIC_JOIN_POINT_JAVA_CLASS_NAME))) { return true; } else { return false; } } int argIndex = 0; for (int i = 1; i < signatureElements.length; i++) { String paramType = signatureElements[i++]; String methodParamType = method.getParameterTypes()[argIndex++].getName(); // handle shortcuts for java.lang.* and JoinPoint, StaticJoinPoint and Rtti String paramTypeResolved = (String) Pattern.ABBREVIATIONS.get(paramType); if (methodParamType.equals(paramType) || methodParamType.equals(paramTypeResolved)) { continue; } else { return false; } } return true; } /** * Handles the advisable definition. * * @param definition * @param withinPointcut * @param pointcutTypes */ private static void handleAdvisableDefinition(final SystemDefinition definition, final String withinPointcut, final String pointcutTypes) { // add the Advisable Mixin with the expression defined to the system definitions definition.addMixinDefinition( DefinitionParserHelper.createAndAddMixinDefToSystemDef( AdvisableImpl.CLASS_INFO, withinPointcut, DeploymentModel.PER_INSTANCE, false, // advisble mixin is NOT transient definition ) ); boolean hasAllPointcuts = false; boolean hasExecutionPointcut = false; boolean hasCallPointcut = false; boolean hasSetPointcut = false; boolean hasGetPointcut = false; boolean hasHandlerPointcut = false; if (pointcutTypes == null || pointcutTypes.equals("") || pointcutTypes.equalsIgnoreCase("all")) { hasAllPointcuts = true; } else { StringTokenizer tokenizer = new StringTokenizer(pointcutTypes, "|"); while (tokenizer.hasMoreTokens()) { String token = tokenizer.nextToken(); if (token.trim().equalsIgnoreCase("all")) { hasAllPointcuts = true; break; } else if (token.trim().equalsIgnoreCase("execution")) { hasExecutionPointcut = true; } else if (token.trim().equalsIgnoreCase("call")) { hasCallPointcut = true; } else if (token.trim().equalsIgnoreCase("set")) { hasSetPointcut = true; } else if (token.trim().equalsIgnoreCase("get")) { hasGetPointcut = true; } else if (token.trim().equalsIgnoreCase("handler")) { hasHandlerPointcut = true; } } } if (hasAllPointcuts || hasExecutionPointcut) { DefinitionParserHelper.createAndAddAdvisableDef( // 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) //"(( execution(!static * *.*(..)) || execution(*.new(..)) ) && " + withinPointcut + ')', // we exclude static method execution since we need the advisable instance "(execution(!static * *.*(..)) && " + withinPointcut + ')', definition ); } if (hasAllPointcuts || hasCallPointcut) { DefinitionParserHelper.createAndAddAdvisableDef( // 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(..)))", // we exclude static method withincode since we need the advisable instance // as a consequence, withincode(staticinitialization(..)) is also excluded "(call(* *.*(..)) && withincode(!static * *.*(..)) && " + withinPointcut + ')', definition ); } if (hasAllPointcuts || hasSetPointcut) { DefinitionParserHelper.createAndAddAdvisableDef( // we exclude static method withincode since we need the advisable instance // as a consequence, withincode(staticinitialization(..)) is also excluded "(set(* *.*) && withincode(!static * *.*(..)) && " + withinPointcut + ')', definition ); } if (hasAllPointcuts || hasGetPointcut) { DefinitionParserHelper.createAndAddAdvisableDef( // we exclude static method withincode since we need the advisable instance // as a consequence, withincode(staticinitialization(..)) is also excluded "(get(* *.*) && withincode(!static * *.*(..)) && " + withinPointcut + ')', definition ); } if (hasAllPointcuts || hasHandlerPointcut) { DefinitionParserHelper.createAndAddAdvisableDef( // we exclude static method withincode since we need the advisable instance // as a consequence, withincode(staticinitialization(..)) is also excluded "(handler(*..*) && withincode(!static * *.*(..)) && " + withinPointcut + ')', definition ); } } }