diff options
11 files changed, 1091 insertions, 0 deletions
diff --git a/loadtime/build.xml b/loadtime/build.xml new file mode 100644 index 000000000..dbdfa8ef6 --- /dev/null +++ b/loadtime/build.xml @@ -0,0 +1,62 @@ +<?xml version="1.0"?> +<project name="loadtime" default="all" basedir="."> + + <import file="../build/build-common.xml"/> + <import file="../asm/build.xml"/> + <import file="../bridge/build.xml"/> + <import file="../util/build.xml"/> + <import file="../weaver/build.xml"/> + + + <path id="loadtime.test.src.path"> + <fileset dir="${basedir}/../lib"> + <include name="junit/*.jar"/> + </fileset> + <path refid="loadtime.src.path"/> + </path> + + <path id="loadtime.src.path"> + <pathelement path="../asm/bin"/> + <pathelement path="../bridge/bin"/> + <pathelement path="../util/bin"/> + <pathelement path="../weaver/bin"/> + <fileset dir="${basedir}/../lib"> + <include name="bcel/*.jar"/> + </fileset> + </path> + + <target name="compile" depends="init, + asm.compile, + bridge.compile, + util.compile, + weaver.compile"> + <srccompile project="loadtime" path="loadtime.src.path"/> + <!-- copy resources --> + <copy todir="../loadtime/bin"> + <fileset dir="../loadtime/src" includes="**/*.dtd"/> + </copy> + </target> + + <target name="test:compile" depends="compile"> + <testcompile project="loadtime" path="loadtime.src.path"/> + <!-- copy resources --> + <copy todir="../loadtime/bin"> + <fileset dir="../loadtime/testsrc" includes="**/*.xml"/> + </copy> + </target> + + <target name="test" depends="test:compile"> + <testrun project="loadtime" path="loadtime.test.src.path" suite="LoadtimeModuleTests"/> + </target> + + <target name="jar" depends="compile"> + <delete file="${build.ajdir}/jars/loadtime.jar"/> + <jar destfile="${build.ajdir}/jars/loadtime.jar"> + <fileset dir="bin"> + <include name="**/*"/> + </fileset> + </jar> + </target> + +</project> + diff --git a/loadtime/src/org/aspectj/weaver/loadtime/Aj.java b/loadtime/src/org/aspectj/weaver/loadtime/Aj.java new file mode 100644 index 000000000..057669d2f --- /dev/null +++ b/loadtime/src/org/aspectj/weaver/loadtime/Aj.java @@ -0,0 +1,145 @@ +/******************************************************************************* + * Copyright (c) 2005 Contributors. + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://eclipse.org/legal/epl-v10.html + * + * Contributors: + * Alexandre Vasseur initial implementation + *******************************************************************************/ +package org.aspectj.weaver.loadtime; + +import org.aspectj.asm.IRelationship; +import org.aspectj.bridge.IMessage; +import org.aspectj.bridge.ISourceLocation; +import org.aspectj.bridge.Message; +import org.aspectj.weaver.ICrossReferenceHandler; +import org.aspectj.weaver.World; +import org.aspectj.weaver.patterns.PatternParser; +import org.aspectj.weaver.patterns.TypePattern; +import org.aspectj.weaver.bcel.BcelWeaver; +import org.aspectj.weaver.bcel.BcelWorld; +import org.aspectj.weaver.loadtime.definition.Definition; +import org.aspectj.weaver.loadtime.definition.DocumentParser; +import org.aspectj.weaver.tools.GeneratedClassHandler; +import org.aspectj.weaver.tools.WeavingAdaptor; + +import java.io.File; +import java.io.FileOutputStream; +import java.lang.reflect.Method; +import java.net.URL; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.WeakHashMap; + +/** + * Adapter between the generic class pre processor interface and the AspectJ weaver + * Load time weaving consistency relies on Bcel.setRepository + * + * @author <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a> + */ +public class Aj implements ClassPreProcessor { + + /** + * Initialization + */ + public void initialize() { + ; + } + + /** + * Weave + * + * @param className + * @param bytes + * @param loader + * @return + */ + public byte[] preProcess(String className, byte[] bytes, ClassLoader loader) { + //TODO AV needs to doc that + if (loader == null || className == null) { + // skip boot loader or null classes (hibernate) + return bytes; + } + + try { + byte[] weaved = WeaverContainer.getWeaver(loader).weaveClass(className, bytes); + //FIXME AV make dump optionnal and configurable + __dump(className, weaved); + return weaved; + } catch (Throwable t) { + t.printStackTrace(); + return bytes; + } + } + + /** + * Cache of weaver + * There is one weaver per classloader + */ + static class WeaverContainer { + + private static Map weavingAdaptors = new WeakHashMap(); + + static WeavingAdaptor getWeaver(ClassLoader loader) { + synchronized (weavingAdaptors) { + WeavingAdaptor weavingAdaptor = (WeavingAdaptor) weavingAdaptors.get(loader); + if (weavingAdaptor == null) { + weavingAdaptor = new ClassLoaderWeavingAdaptor(loader); + weavingAdaptors.put(loader, weavingAdaptor); + } + return weavingAdaptor; + } + } + } + + static void defineClass(ClassLoader loader, String name, byte[] bytes) { + try { + //TODO av protection domain, and optimize + Method defineClass = ClassLoader.class.getDeclaredMethod( + "defineClass", new Class[]{ + String.class, bytes.getClass(), int.class, int.class + } + ); + defineClass.setAccessible(true); + defineClass.invoke( + loader, new Object[]{ + name, + bytes, + new Integer(0), + new Integer(bytes.length) + } + ); + } catch (Throwable t) { + t.printStackTrace(); + } + } + + /** + * Dump the given bytcode in _dump/... + * + * @param name + * @param b + * @throws Throwable + */ + static void __dump(String name, byte[] b) throws Throwable { + String className = name.replace('.', '/'); + final File dir; + if (className.indexOf('/') > 0) { + dir = new File("_dump" + File.separator + className.substring(0, className.lastIndexOf('/'))); + } else { + dir = new File("_dump"); + } + dir.mkdirs(); + String fileName = "_dump" + File.separator + className + ".class"; + FileOutputStream os = new FileOutputStream(fileName); + os.write(b); + os.close(); + } + +} diff --git a/loadtime/src/org/aspectj/weaver/loadtime/ClassLoaderWeavingAdaptor.java b/loadtime/src/org/aspectj/weaver/loadtime/ClassLoaderWeavingAdaptor.java new file mode 100644 index 000000000..2f74738a7 --- /dev/null +++ b/loadtime/src/org/aspectj/weaver/loadtime/ClassLoaderWeavingAdaptor.java @@ -0,0 +1,269 @@ +/******************************************************************************* + * Copyright (c) 2005 Contributors. + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://eclipse.org/legal/epl-v10.html + * + * Contributors: + * Alexandre Vasseur initial implementation + *******************************************************************************/ +package org.aspectj.weaver.loadtime; + +import org.aspectj.weaver.tools.WeavingAdaptor; +import org.aspectj.weaver.tools.GeneratedClassHandler; +import org.aspectj.weaver.patterns.TypePattern; +import org.aspectj.weaver.patterns.PatternParser; +import org.aspectj.weaver.bcel.BcelWorld; +import org.aspectj.weaver.bcel.BcelWeaver; +import org.aspectj.weaver.ICrossReferenceHandler; +import org.aspectj.weaver.World; +import org.aspectj.weaver.ResolvedTypeX; +import org.aspectj.weaver.TypeX; +import org.aspectj.weaver.loadtime.definition.DocumentParser; +import org.aspectj.weaver.loadtime.definition.Definition; +import org.aspectj.bridge.ISourceLocation; +import org.aspectj.bridge.Message; +import org.aspectj.bridge.IMessage; +import org.aspectj.asm.IRelationship; + +import java.util.List; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.Iterator; +import java.io.File; +import java.io.IOException; +import java.net.URL; + +/** + * @author <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a> + */ +public class ClassLoaderWeavingAdaptor extends WeavingAdaptor { + + //ATAJ LTW include/exclude + private List m_includeTypePattern = new ArrayList(); + private List m_excludeTypePattern = new ArrayList(); + private List m_aspectExcludeTypePattern = new ArrayList(); + public void addIncludeTypePattern(TypePattern typePattern) { + m_includeTypePattern.add(typePattern); + } + public void addExcludeTypePattern(TypePattern typePattern) { + m_excludeTypePattern.add(typePattern); + } + public void addAspectExcludeTypePattern(TypePattern typePattern) { + m_aspectExcludeTypePattern.add(typePattern); + } + + public ClassLoaderWeavingAdaptor(final ClassLoader loader) { + super(null);// at this stage we don't have yet a generatedClassHandler to define to the VM the closures + this.generatedClassHandler = new GeneratedClassHandler() { + /** + * Callback when we need to define a Closure in the JVM + * + * @param name + * @param bytes + */ + public void acceptClass(String name, byte[] bytes) { + //TODO av make dump configurable + try { + Aj.__dump(name, bytes); + } catch (Throwable throwable) { + throwable.printStackTrace(); + } + Aj.defineClass(loader, name, bytes);// could be done lazily using the hook + } + }; + + bcelWorld = new BcelWorld( + loader, messageHandler, new ICrossReferenceHandler() { + public void addCrossReference(ISourceLocation from, ISourceLocation to, IRelationship.Kind kind, boolean runtimeTest) { + ;// for tools only + } + } + ); + +// //TODO this AJ code will call +// //org.aspectj.apache.bcel.Repository.setRepository(this); +// //ie set some static things +// //==> bogus as Bcel is expected to be +// org.aspectj.apache.bcel.Repository.setRepository(new ClassLoaderRepository(loader)); + + weaver = new BcelWeaver(bcelWorld); + + // register the definitions + registerDefinitions(weaver, loader); + + // after adding aspects + weaver.prepareForWeave(); + } + + /** + * Load and cache the aop.xml/properties according to the classloader visibility rules + * + * @param weaver + * @param loader + */ + private void registerDefinitions(final BcelWeaver weaver, final ClassLoader loader) { + try { + //TODO av underoptimized: we will parse each XML once per CL that see it + Enumeration xmls = loader.getResources("/META-INF/aop.xml"); + List definitions = new ArrayList(); + + //TODO av dev mode needed ? TBD -Daj5.def=... + if (loader != null && loader != ClassLoader.getSystemClassLoader().getParent()) { + String file = System.getProperty("aj5.def", null); + if (file != null) { + definitions.add(DocumentParser.parse((new File(file)).toURL())); + } + } + + while (xmls.hasMoreElements()) { + URL xml = (URL) xmls.nextElement(); + definitions.add(DocumentParser.parse(xml)); + } + registerOptions(weaver, loader, definitions); + registerAspectExclude(weaver, loader, definitions); + registerAspects(weaver, loader, definitions); + registerIncludeExclude(weaver, loader, definitions); + } catch (Exception e) { + weaver.getWorld().getMessageHandler().handleMessage( + new Message("Register definition failed", IMessage.WARNING, e, null) + ); + } + } + + /** + * Configure the weaver according to the option directives + * TODO av - don't know if it is that good to reuse, since we only allow a small subset of options in LTW + * + * @param weaver + * @param loader + * @param definitions + */ + private void registerOptions(final BcelWeaver weaver, final ClassLoader loader, final List definitions) { + StringBuffer allOptions = new StringBuffer(); + for (Iterator iterator = definitions.iterator(); iterator.hasNext();) { + Definition definition = (Definition) iterator.next(); + allOptions.append(definition.getWeaverOptions()).append(' '); + } + + Options.WeaverOption weaverOption = Options.parse(allOptions.toString(), loader); + + // configure the weaver and world + // AV - code duplicates AspectJBuilder.initWorldAndWeaver() + World world = weaver.getWorld(); + world.setMessageHandler(weaverOption.messageHandler); + world.setXlazyTjp(weaverOption.lazyTjp); + weaver.setReweavableMode(weaverOption.reWeavable, false); + world.setXnoInline(weaverOption.noInline); + world.setBehaveInJava5Way(weaverOption.java5); + //TODO proceedOnError option + } + + private void registerAspectExclude(final BcelWeaver weaver, final ClassLoader loader, final List definitions) { + for (Iterator iterator = definitions.iterator(); iterator.hasNext();) { + Definition definition = (Definition) iterator.next(); + for (Iterator iterator1 = definition.getAspectExcludePatterns().iterator(); iterator1.hasNext();) { + String exclude = (String) iterator1.next(); + TypePattern excludePattern = new PatternParser(exclude).parseTypePattern(); + m_aspectExcludeTypePattern.add(excludePattern); + } + } + } + + /** + * Register the aspect, following include / exclude rules + * + * @param weaver + * @param loader + * @param definitions + */ + private void registerAspects(final BcelWeaver weaver, final ClassLoader loader, final List definitions) { + //TODO: the exclude aspect allow to exclude aspect defined upper in the CL hierarchy - is it what we want ?? + // if not, review the getResource so that we track which resource is defined by which CL + + //it aspectClassNames + //exclude if in any of the exclude list + for (Iterator iterator = definitions.iterator(); iterator.hasNext();) { + Definition definition = (Definition) iterator.next(); + for (Iterator aspects = definition.getAspectClassNames().iterator(); aspects.hasNext();) { + String aspectClassName = (String) aspects.next(); + if (acceptAspect(aspectClassName)) { + weaver.addLibraryAspect(aspectClassName); + } + } + } + + //it concreteAspects + //exclude if in any of the exclude list + //TODO + } + + /** + * Register the include / exclude filters + * + * @param weaver + * @param loader + * @param definitions + */ + private void registerIncludeExclude(final BcelWeaver weaver, final ClassLoader loader, final List definitions) { + for (Iterator iterator = definitions.iterator(); iterator.hasNext();) { + Definition definition = (Definition) iterator.next(); + for (Iterator iterator1 = definition.getIncludePatterns().iterator(); iterator1.hasNext();) { + String include = (String) iterator1.next(); + TypePattern includePattern = new PatternParser(include).parseTypePattern(); + m_includeTypePattern.add(includePattern); + } + for (Iterator iterator1 = definition.getExcludePatterns().iterator(); iterator1.hasNext();) { + String exclude = (String) iterator1.next(); + TypePattern excludePattern = new PatternParser(exclude).parseTypePattern(); + m_excludeTypePattern.add(excludePattern); + } + } + } + + protected boolean accept(String className) { + // avoid ResolvedType if not needed + if (m_excludeTypePattern.isEmpty() && m_includeTypePattern.isEmpty()) { + return true; + } + //TODO AV - optimize for className.startWith only + ResolvedTypeX classInfo = weaver.getWorld().getCoreType(TypeX.forName(className)); + //exclude + for (Iterator iterator = m_excludeTypePattern.iterator(); iterator.hasNext();) { + TypePattern typePattern = (TypePattern) iterator.next(); + if (typePattern.matchesStatically(classInfo)) { + // exclude match - skip + return false; + } + } + for (Iterator iterator = m_includeTypePattern.iterator(); iterator.hasNext();) { + TypePattern typePattern = (TypePattern) iterator.next(); + if (! typePattern.matchesStatically(classInfo)) { + // include does not match - skip + return false; + } + } + return true; + } + + private boolean acceptAspect(String aspectClassName) { + // avoid ResolvedType if not needed + if (m_aspectExcludeTypePattern.isEmpty()) { + return true; + } + //TODO AV - optimize for className.startWith only + ResolvedTypeX classInfo = weaver.getWorld().getCoreType(TypeX.forName(aspectClassName)); + //exclude + for (Iterator iterator = m_aspectExcludeTypePattern.iterator(); iterator.hasNext();) { + TypePattern typePattern = (TypePattern) iterator.next(); + if (typePattern.matchesStatically(classInfo)) { + // exclude match - skip + return false; + } + } + return true; + } + +} diff --git a/loadtime/src/org/aspectj/weaver/loadtime/ClassPreProcessor.java b/loadtime/src/org/aspectj/weaver/loadtime/ClassPreProcessor.java new file mode 100644 index 000000000..d47f14106 --- /dev/null +++ b/loadtime/src/org/aspectj/weaver/loadtime/ClassPreProcessor.java @@ -0,0 +1,36 @@ +/******************************************************************************* + * Copyright (c) 2005 Contributors. + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://eclipse.org/legal/epl-v10.html + * + * Contributors: + * Alexandre Vasseur initial implementation + *******************************************************************************/ +package org.aspectj.weaver.loadtime; + +/** + * Generic class pre processor interface that allows to separate the AspectJ 5 load time weaving + * from Java 5 JVMTI interfaces for further use on Java 1.3 / 1.4 + * + * @author <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a> + */ +public interface ClassPreProcessor { + + /** + * Post constructor initialization, usually empty + */ + void initialize(); + + /** + * Weave + * + * @param className + * @param bytes + * @param classLoader + * @return + */ + byte[] preProcess(String className, byte[] bytes, ClassLoader classLoader); +}
\ No newline at end of file diff --git a/loadtime/src/org/aspectj/weaver/loadtime/Options.java b/loadtime/src/org/aspectj/weaver/loadtime/Options.java new file mode 100644 index 000000000..c466f9787 --- /dev/null +++ b/loadtime/src/org/aspectj/weaver/loadtime/Options.java @@ -0,0 +1,165 @@ +/******************************************************************************* + * Copyright (c) 2005 Contributors. + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://eclipse.org/legal/epl-v10.html + * + * Contributors: + * Alexandre Vasseur initial implementation + *******************************************************************************/ +package org.aspectj.weaver.loadtime; + +import org.aspectj.bridge.AbortException; +import org.aspectj.bridge.IMessage; +import org.aspectj.bridge.IMessageHandler; +import org.aspectj.bridge.Message; +import org.aspectj.util.LangUtil; + +import java.util.Collections; +import java.util.Iterator; +import java.util.List; + +/** + * A class that hanldes LTW options. + * Note: AV - I choosed to not reuse AjCompilerOptions and alike since those implies too many dependancies on + * jdt and ajdt modules. + * + * @author <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a> + */ +public class Options { + + private static class DefaultMessageHandler implements IMessageHandler { + + boolean isVerbose = false; + boolean showWeaveInfo = false; + boolean showWarn = true; + + public boolean handleMessage(IMessage message) throws AbortException { + return SYSTEM_OUT.handleMessage(message); + } + + public boolean isIgnoring(IMessage.Kind kind) { + if (kind.equals(IMessage.WEAVEINFO)) { + return !showWeaveInfo; + } + if (kind.isSameOrLessThan(IMessage.INFO)) { + return !isVerbose; + } + return !showWarn; + } + + public void dontIgnore(IMessage.Kind kind) { + if (kind.equals(IMessage.WEAVEINFO)) { + showWeaveInfo = true; + } else if (kind.equals(IMessage.DEBUG)) { + isVerbose = true; + } else if (kind.equals(IMessage.WARNING)) { + showWarn = false; + } + } + } + + private final static String OPTION_15 = "-1.5"; + private final static String OPTION_lazyTjp = "-XlazyTjp"; + private final static String OPTION_noWarn = "-nowarn"; + private final static String OPTION_noWarnNone = "-warn:none"; + private final static String OPTION_proceedOnError = "-proceedOnError"; + private final static String OPTION_verbose = "-verbose"; + private final static String OPTION_reweavable = "-Xreweavable"; + private final static String OPTION_noinline = "-Xnoinline"; + private final static String OPTION_showWeaveInfo = "-showWeaveInfo"; + private final static String OPTIONVALUED_messageHolder = "-XmessageHolderClass:";//TODO rename to Handler + + //FIXME dump option - dump what - dump before/after ? + + public static WeaverOption parse(String options, ClassLoader laoder) { + // the first option wins + List flags = LangUtil.anySplit(options, " "); + Collections.reverse(flags); + + WeaverOption weaverOption = new WeaverOption(); + weaverOption.messageHandler = new DefaultMessageHandler();//default + + + // do a first round on the message handler since it will report the options themselves + for (Iterator iterator = flags.iterator(); iterator.hasNext();) { + String arg = (String) iterator.next(); + if (arg.startsWith(OPTIONVALUED_messageHolder)) { + if (arg.length() > OPTIONVALUED_messageHolder.length()) { + String handlerClass = arg.substring(OPTIONVALUED_messageHolder.length()).trim(); + try { + Class handler = Class.forName(handlerClass, false, laoder); + weaverOption.messageHandler = ((IMessageHandler) handler.newInstance()); + } catch (Throwable t) { + weaverOption.messageHandler.handleMessage( + new Message( + "Cannot instantiate message handler " + handlerClass, + IMessage.ERROR, + t, + null + ) + ); + } + } + } + } + + // configure the other options + for (Iterator iterator = flags.iterator(); iterator.hasNext();) { + String arg = (String) iterator.next(); + if (arg.equals(OPTION_15)) { + weaverOption.java5 = true; + } else if (arg.equalsIgnoreCase(OPTION_lazyTjp)) { + weaverOption.lazyTjp = true; + } else if (arg.equalsIgnoreCase(OPTION_noinline)) { + weaverOption.noInline = true; + } else if (arg.equalsIgnoreCase(OPTION_noWarn) || arg.equalsIgnoreCase(OPTION_noWarnNone)) { + weaverOption.noWarn = true; + } else if (arg.equalsIgnoreCase(OPTION_proceedOnError)) { + weaverOption.proceedOnError = true; + } else if (arg.equalsIgnoreCase(OPTION_reweavable)) { + weaverOption.reWeavable = true; + } else if (arg.equalsIgnoreCase(OPTION_showWeaveInfo)) { + weaverOption.showWeaveInfo = true; + } else if (arg.equalsIgnoreCase(OPTION_verbose)) { + weaverOption.verbose = true; + } else { + weaverOption.messageHandler.handleMessage( + new Message( + "Cannot configure weaver with option " + arg + ": unknown option", + IMessage.WARNING, + null, + null + ) + ); + } + } + + // refine message handler configuration + if (weaverOption.noWarn) { + weaverOption.messageHandler.dontIgnore(IMessage.WARNING); + } + if (weaverOption.verbose) { + weaverOption.messageHandler.dontIgnore(IMessage.DEBUG); + } + if (weaverOption.showWeaveInfo) { + weaverOption.messageHandler.dontIgnore(IMessage.WEAVEINFO); + } + + return weaverOption; + } + + public static class WeaverOption { + boolean java5; + boolean lazyTjp; + boolean noWarn; + boolean proceedOnError; + boolean verbose; + boolean reWeavable; + boolean noInline; + boolean showWeaveInfo; + IMessageHandler messageHandler; + } +} diff --git a/loadtime/src/org/aspectj/weaver/loadtime/definition/Definition.java b/loadtime/src/org/aspectj/weaver/loadtime/definition/Definition.java new file mode 100644 index 000000000..acc3e5c87 --- /dev/null +++ b/loadtime/src/org/aspectj/weaver/loadtime/definition/Definition.java @@ -0,0 +1,97 @@ +/******************************************************************************* + * Copyright (c) 2005 Contributors. + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://eclipse.org/legal/epl-v10.html + * + * Contributors: + * Alexandre Vasseur initial implementation + *******************************************************************************/ +package org.aspectj.weaver.loadtime.definition; + +import org.aspectj.weaver.patterns.PatternParser; + +import java.util.List; +import java.util.ArrayList; +import java.util.Iterator; + +/** + * A POJO that contains raw strings from the XML (sort of XMLBean for our simple LTW DTD) + * + * @author <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a> + */ +public class Definition { + + private StringBuffer m_weaverOptions; + + private List m_includePatterns; + + private List m_excludePatterns; + + private List m_aspectClassNames; + + private List m_aspectExcludePatterns; + + private List m_concreteAspects; + + public Definition() { + m_weaverOptions = new StringBuffer(); + m_includePatterns = new ArrayList(0); + m_excludePatterns = new ArrayList(0); + m_aspectClassNames = new ArrayList(); + m_aspectExcludePatterns = new ArrayList(0); + m_concreteAspects = new ArrayList(0); + } + + public String getWeaverOptions() { + return m_weaverOptions.toString(); + } + + public List getIncludePatterns() { + return m_includePatterns; + } + + public List getExcludePatterns() { + return m_excludePatterns; + } + + public List getAspectClassNames() { + return m_aspectClassNames; + } + + public List getAspectExcludePatterns() { + return m_aspectExcludePatterns; + } + + public List getConcreteAspects() { + return m_concreteAspects; + } + + public static class ConcreteAspect { + String name; + String extend; + List pointcuts; + + public ConcreteAspect(String name, String extend) { + this.name = name; + this.extend = extend; + this.pointcuts = new ArrayList(); + } + } + + public static class Pointcut { + String name; + String expression; + public Pointcut(String name, String expression) { + this.name = name; + this.expression = expression; + } + } + + public void appendWeaverOptions(String option) { + m_weaverOptions.append(option.trim()).append(' '); + } + +} diff --git a/loadtime/src/org/aspectj/weaver/loadtime/definition/DocumentParser.java b/loadtime/src/org/aspectj/weaver/loadtime/definition/DocumentParser.java new file mode 100644 index 000000000..d78740fa6 --- /dev/null +++ b/loadtime/src/org/aspectj/weaver/loadtime/definition/DocumentParser.java @@ -0,0 +1,220 @@ +/******************************************************************************* + * Copyright (c) 2005 Contributors. + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://eclipse.org/legal/epl-v10.html + * + * Contributors: + * Alexandre Vasseur initial implementation + *******************************************************************************/ +package org.aspectj.weaver.loadtime.definition; + +import org.xml.sax.Attributes; +import org.xml.sax.ErrorHandler; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; +import org.xml.sax.SAXNotRecognizedException; +import org.xml.sax.SAXParseException; +import org.xml.sax.XMLReader; +import org.xml.sax.DTDHandler; +import org.xml.sax.helpers.DefaultHandler; +import org.xml.sax.helpers.XMLReaderFactory; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; + +import com.sun.org.apache.xerces.internal.impl.XMLEntityManager; + +/** + * FIXME AV - doc, concrete aspect + * + * @author <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a> + */ +public class DocumentParser extends DefaultHandler { + + /** + * The current DTD public id. The matching dtd will be searched as a resource. + */ + private final static String DTD_PUBLIC_ID = "-//AspectJ//DTD 1.5.0//EN"; + + /** + * The DTD alias, for better user experience. + */ + private final static String DTD_PUBLIC_ID_ALIAS = "-//AspectJ//DTD//EN"; + + /** + * A handler to the DTD stream so that we are only using one file descriptor + */ + private final static InputStream DTD_STREAM = DocumentParser.class.getResourceAsStream("/aspectj_1_5_0.dtd"); + + private final static String ASPECTJ_ELEMENT = "aspectj"; + private final static String WEAVER_ELEMENT = "weaver"; + private final static String INCLUDE_ELEMENT = "include"; + private final static String EXCLUDE_ELEMENT = "exclude"; + private final static String OPTIONS_ATTRIBUTE = "options"; + private final static String ASPECTS_ELEMENT = "aspects"; + private final static String ASPECT_ELEMENT = "aspect"; + private final static String CONCRETE_ASPECT_ELEMENT = "concrete-aspect"; + private final static String NAME_ATTRIBUTE = "name"; + private final static String EXTEND_ATTRIBUTE = "extends"; + private final static String POINTCUT_ELEMENT = "pointcut"; + private final static String WITHIN_ATTRIBUTE = "within"; + private final static String EXPRESSION_ATTRIBUTE = "expression"; + + + private final Definition m_definition; + + private boolean m_inAspectJ; + private boolean m_inWeaver; + private boolean m_inAspects; + + private Definition.ConcreteAspect m_lastConcreteAspect; + + private DocumentParser() { + m_definition = new Definition(); + } + + public static Definition parse(final URL url) throws Exception { + InputStream in = null; + try { + DocumentParser parser = new DocumentParser(); + + XMLReader xmlReader = XMLReaderFactory.createXMLReader(); + xmlReader.setContentHandler(parser); + xmlReader.setErrorHandler(parser); + + try { + xmlReader.setFeature("http://xml.org/sax/features/validation", false); + xmlReader.setFeature("http://xml.org/sax/features/external-general-entities", false); + xmlReader.setFeature("http://xml.org/sax/features/external-parameter-entities", false); + } catch (SAXNotRecognizedException e) { + ;//fine, the parser don't do validation + } + + xmlReader.setEntityResolver(parser); + in = url.openStream(); + xmlReader.parse(new InputSource(in)); + return parser.m_definition; + } finally { + try { + in.close(); + } catch (Throwable t) { + ; + } + } + } + + public InputSource resolveEntity(String publicId, String systemId) throws IOException, SAXException { + if (publicId.equals(DTD_PUBLIC_ID) || publicId.equals(DTD_PUBLIC_ID_ALIAS)) { + InputStream in = DTD_STREAM; + if (in == null) { + return null; + } else { + return new InputSource(in); + } + } else { + System.err.println( + "AspectJ - WARN - unknown DTD " + + publicId + + " - consider using " + + DTD_PUBLIC_ID + ); + return null; + } + } + + public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { + if (ASPECT_ELEMENT.equals(qName)) { + String name = attributes.getValue(NAME_ATTRIBUTE); + if (!isNull(name)) { + m_definition.getAspectClassNames().add(name); + } + } else if (WEAVER_ELEMENT.equals(qName)) { + String options = attributes.getValue(OPTIONS_ATTRIBUTE); + if (!isNull(options)) { + m_definition.appendWeaverOptions(options); + } + m_inWeaver = true; + } else if (CONCRETE_ASPECT_ELEMENT.equals(qName)) { + String name = attributes.getValue(NAME_ATTRIBUTE); + String extend = attributes.getValue(EXTEND_ATTRIBUTE); + if (!isNull(name) && !isNull(extend)) { + m_lastConcreteAspect = new Definition.ConcreteAspect(name, extend); + m_definition.getConcreteAspects().add(m_lastConcreteAspect); + } + } else if (POINTCUT_ELEMENT.equals(qName) && m_lastConcreteAspect != null) { + String name = attributes.getValue(NAME_ATTRIBUTE); + String expression = attributes.getValue(EXPRESSION_ATTRIBUTE); + if (!isNull(name) && !isNull(expression)) { + m_lastConcreteAspect.pointcuts.add(new Definition.Pointcut(name, replaceXmlAnd(expression))); + } + } else if (ASPECTJ_ELEMENT.equals(qName)) { + if (m_inAspectJ) { + throw new SAXException("Found nested <aspectj> element"); + } + m_inAspectJ = true; + } else if (ASPECTS_ELEMENT.equals(qName)) { + m_inAspects = true; + } else if (INCLUDE_ELEMENT.equals(qName) && m_inWeaver) { + String typePattern = attributes.getValue(WITHIN_ATTRIBUTE); + if (!isNull(typePattern)) { + m_definition.getIncludePatterns().add(typePattern); + } + } else if (EXCLUDE_ELEMENT.equals(qName) && m_inWeaver) { + String typePattern = attributes.getValue(WITHIN_ATTRIBUTE); + if (!isNull(typePattern)) { + m_definition.getExcludePatterns().add(typePattern); + } + } else if (EXCLUDE_ELEMENT.equals(qName) && m_inAspects) { + String typePattern = attributes.getValue(WITHIN_ATTRIBUTE); + if (!isNull(typePattern)) { + m_definition.getAspectExcludePatterns().add(typePattern); + } + } else { + throw new SAXException("Unknown element while parsing <aspectj> element: " + qName); + } + super.startElement(uri, localName, qName, attributes); + } + + public void endElement(String uri, String localName, String qName) throws SAXException { + if (CONCRETE_ASPECT_ELEMENT.equals(qName)) { + m_lastConcreteAspect = null; + } else if (ASPECTJ_ELEMENT.equals(qName)) { + m_inAspectJ = false; + } else if (WEAVER_ELEMENT.equals(qName)) { + m_inWeaver = false; + } else if (ASPECTS_ELEMENT.equals(qName)) { + m_inAspects = false; + } + super.endElement(uri, localName, qName); + } + + //TODO AV - define what we want for XML parser error - for now stderr + public void warning(SAXParseException e) throws SAXException { + super.warning(e); + } + + public void error(SAXParseException e) throws SAXException { + super.error(e); + } + + public void fatalError(SAXParseException e) throws SAXException { + super.fatalError(e); + } + + + private static String replaceXmlAnd(String expression) { + //TODO AV do we need to handle "..)AND" or "AND(.." ? + //FIXME AV Java 1.4 code - if KO, use some Strings util + return expression.replaceAll(" AND ", " && "); + } + + private boolean isNull(String s) { + return (s == null || s.length() <= 0); + } + + +} diff --git a/loadtime/testsrc/LoadtimeModuleTests.java b/loadtime/testsrc/LoadtimeModuleTests.java new file mode 100644 index 000000000..4cf2326dc --- /dev/null +++ b/loadtime/testsrc/LoadtimeModuleTests.java @@ -0,0 +1,36 @@ +/******************************************************************************* + * Copyright (c) 2005 Contributors. + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://eclipse.org/legal/epl-v10.html + * + * Contributors: + * Alexandre Vasseur initial implementation + *******************************************************************************/ + +import junit.framework.TestCase; +import junit.framework.Test; +import junit.framework.TestSuite; +import junit.textui.TestRunner; +import org.aspectj.weaver.loadtime.test.DocumentParserTest; + +/** + * @author <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a> + */ +public class LoadtimeModuleTests extends TestCase { + + public static Test suite() { + TestSuite suite = new TestSuite(LoadtimeModuleTests.class.getName()); + + suite.addTestSuite(DocumentParserTest.class); + + return suite; + } + + public static void main(String args[]) throws Throwable { + TestRunner.run(suite()); + } + +} diff --git a/loadtime/testsrc/org/aspectj/weaver/loadtime/test/DocumentParserTest.java b/loadtime/testsrc/org/aspectj/weaver/loadtime/test/DocumentParserTest.java new file mode 100644 index 000000000..c0f26ab99 --- /dev/null +++ b/loadtime/testsrc/org/aspectj/weaver/loadtime/test/DocumentParserTest.java @@ -0,0 +1,43 @@ +/******************************************************************************* + * Copyright (c) 2005 Contributors. + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://eclipse.org/legal/epl-v10.html + * + * Contributors: + * Alexandre Vasseur initial implementation + *******************************************************************************/ +package org.aspectj.weaver.loadtime.test; + +import junit.framework.TestCase; + +import java.net.URL; + +import org.aspectj.weaver.loadtime.definition.Definition; +import org.aspectj.weaver.loadtime.definition.DocumentParser; + +/** + * @author <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a> + */ +public class DocumentParserTest extends TestCase { + + public void testSimple() throws Throwable { + URL url = DocumentParserTest.class.getResource("simple.xml"); + Definition def = DocumentParser.parse(url); + assertEquals("-showWeaveInfo", def.getWeaverOptions().trim()); + } + + public void testSimpleWithDtd() throws Throwable { + URL url = DocumentParserTest.class.getResource("simpleWithDtd.xml"); + Definition def = DocumentParser.parse(url); + assertEquals("-showWeaveInfo", def.getWeaverOptions().trim()); + assertTrue(def.getAspectClassNames().contains("test.Aspect")); + + assertEquals("foo..bar.Goo+", def.getIncludePatterns().get(0)); + assertEquals("@Baz", def.getAspectExcludePatterns().get(0)); + + } + +} diff --git a/loadtime/testsrc/org/aspectj/weaver/loadtime/test/simple.xml b/loadtime/testsrc/org/aspectj/weaver/loadtime/test/simple.xml new file mode 100644 index 000000000..54d3dd7af --- /dev/null +++ b/loadtime/testsrc/org/aspectj/weaver/loadtime/test/simple.xml @@ -0,0 +1,7 @@ +<aspectj> + <weaver options="-showWeaveInfo"> + </weaver> + <aspects> + <aspect name="test.Aspect"/> + </aspects> +</aspectj> diff --git a/loadtime/testsrc/org/aspectj/weaver/loadtime/test/simpleWithDtd.xml b/loadtime/testsrc/org/aspectj/weaver/loadtime/test/simpleWithDtd.xml new file mode 100644 index 000000000..03b77ff3c --- /dev/null +++ b/loadtime/testsrc/org/aspectj/weaver/loadtime/test/simpleWithDtd.xml @@ -0,0 +1,11 @@ +<?xml version="1.0"?> +<!DOCTYPE aspectj PUBLIC "-//AspectJ//DTD//EN" "http://sswww.aspectj.org/dtd/aspectj_1_5_0.dtd"> +<aspectj> + <weaver options="-showWeaveInfo"> + <include within="foo..bar.Goo+"/> + </weaver> + <aspects> + <exclude within="@Baz"/> + <aspect name="test.Aspect"/> + </aspects> +</aspectj> |