--- /dev/null
+<?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>
+
--- /dev/null
+/*******************************************************************************
+ * 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();
+ }
+
+}
--- /dev/null
+/*******************************************************************************
+ * 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;
+ }
+
+}
--- /dev/null
+/*******************************************************************************
+ * 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
--- /dev/null
+/*******************************************************************************
+ * 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;
+ }
+}
--- /dev/null
+/*******************************************************************************
+ * 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(' ');
+ }
+
+}
--- /dev/null
+/*******************************************************************************
+ * 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);
+ }
+
+
+}
--- /dev/null
+/*******************************************************************************
+ * 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());
+ }
+
+}
--- /dev/null
+/*******************************************************************************
+ * 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));
+
+ }
+
+}
--- /dev/null
+<aspectj>
+ <weaver options="-showWeaveInfo">
+ </weaver>
+ <aspects>
+ <aspect name="test.Aspect"/>
+ </aspects>
+</aspectj>
--- /dev/null
+<?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>