AspectJ sample code

This contains contributions from the AspectJ community of

Find complete source files in the AspectJ CVS repository at org.aspectj/modules/docs/sandbox. For instructions on downloading code from the CVS repository, see the FAQ entry "buildingsource".

Copyright 2003 Contributors. All Rights Reserved. This sample code is made available under the Eclipse Public License version 1.0 available at https://www.eclipse.org/legal/epl-v10.html. Contributors are listed in this document as authors. Permission to republish portions of this sample code is hereby granted if the publication acknowledges the author by name and the source by reference to the AspectJ project home page at https://eclipse.org/aspectj.

Generated on Apr 18, 2004 by SamplesGatherer

Contents

  • Author Index
  • Listings

    api

    back to top

    api-asm

    back to top

    Walk model to list affected files

    back to top

    |   Wes Isberg  |  api-clients org/aspectj/samples/AffectedFilesReporter.java:3  |

    
    package org.aspectj.samples;
    
    import org.aspectj.tools.ajc.Main;
    import org.aspectj.asm.*;
    import org.aspectj.bridge.*;
    
    import java.io.*;
    import java.util.*;
    
    /**
     * Run ajc and list files affected by advice or inter-type declarations.
     *
     * WARNING: Does not emit info messages for uses-pointcut dependencies.
     * @author Wes Isberg
     */
    public class AffectedFilesReporter implements Runnable {
    
        /*
         * Walk down asm hierarchy, looking for source files,
         * and check if any of their children has a relation of
         * kind ADVICE or INTER_TYPE_DECLARATION
         */
    
        /**
         * Wrapper for ajc that emits list of affected files.
         * @param args the String[] of args for ajc,
         *        optionally prefixed with -to {file}
         * @throws IOException if unable to write to {file}
         */
        public static void main(String[] args) throws IOException {
            Main main = new Main();
            PrintStream toConfig = System.out;
            FileOutputStream fin = null;
            if ((args.length > 1) && ("-to".equals(args[0]))) {
                File config = new File(args[1]);
                fin = new FileOutputStream(config);
                toConfig = new PrintStream(fin, true);
                String[] temp = new String[args.length-2];
                System.arraycopy(args, 2, temp, 0, temp.length);
                args = temp;
            }
            Runnable runner = new AffectedFilesReporter(toConfig);
            main.setCompletionRunner(runner);
            // should add -emacssym to args if not already there
            main.runMain(args, false);
            if (null != fin) {
                fin.close();
            }
        }
    
        final PrintStream sink;
    
        public AffectedFilesReporter(PrintStream sink) {
            this.sink = (null == sink ? System.out : sink);
        }
    
        public void run() {
            IHierarchy hierarchy = AsmManager.getDefault().getHierarchy();
            if (null == hierarchy) {
                sink.println("# no structure model - use -emacssym option");
                return;
            }
            List /*IProgramElement*/ nodes = new LinkedList();
            List /*IProgramElement*/ newNodes = new LinkedList();
            // root could be config file or blank root - either way, use kids
            nodes.addAll(hierarchy.getRoot().getChildren());
            while (0 < nodes.size()) {
                for (ListIterator it = nodes.listIterator(); it.hasNext();) {
                    IProgramElement node = (IProgramElement) it.next();
                    if (node.getKind().isSourceFileKind()) {
                        if (isAffected(node)) {
                            ISourceLocation loc = node.getSourceLocation();
                            sink.println(loc.getSourceFile().getPath());
                        }
                    } else {
                        // XXX uncertain of structure - traverse all??
                        newNodes.addAll(node.getChildren());
                    }
                    it.remove();
                }
                nodes.addAll(newNodes);
                newNodes.clear();
            }
        }
    
        /**
         * Return true if this file node is affected by any aspects.
         * Recursively search children for any effect,
         * and halt on first affect found.
         * @param node the IProgramElementNode for a source file
         * @return true if affected.
         */
        private boolean isAffected(final IProgramElement fileNode) {
            final IRelationshipMap map  =
                AsmManager.getDefault().getRelationshipMap();
            List /*IProgramElement*/ nodes = new LinkedList();
            List /*IProgramElement*/ newNodes = new LinkedList();
            nodes.add(fileNode);
            while (0 < nodes.size()) {
                for (ListIterator iter = nodes.listIterator();
                     iter.hasNext();) {
                    IProgramElement node = (IProgramElement) iter.next();
                    List relations = map.get(node);
                    if (null != relations) {
                        for (Iterator riter = relations.iterator();
                            riter.hasNext();) {
                            IRelationship.Kind kind =
                            ((IRelationship) riter.next()).getKind();
                            if ((kind == IRelationship.Kind.ADVICE)
                                || (kind == IRelationship.Kind.DECLARE_INTER_TYPE)) {
                                return true;
                            }
                        }
                    }
                    iter.remove();
                    newNodes.addAll(node.getChildren());
                }
                nodes.addAll(newNodes);
                newNodes.clear();
            }
            return false;
        }
    }
        

    caching

    back to top

    caching-dirty

    back to top

    Use getter/setter pattern to track dirtiness

    back to top

    |   Ricardo Giacomin, Wes Isberg  |  common caching/WatchSetters.java:5  |

    package caching;
    
    import java.lang.reflect.Method;
    
    /**
     * Watch setters to skip if new value is same as old
     * or to set a dirty flag otherwise.
     * Clients classes opt-in by implementing IWatched,
     * and anyone can read the dirty and dirty-valid flags.
     * <pre>
     * class Foo implements WatchSetters.IWatched {
     *    ...
     * }
     * Foo foo = new Foo();
     * ...
     * if (!foo.isDirtyValid() || foo.isDirty()) {
     *    foo.write();
     * }
     * </pre>
     *
     * (Initial draft was sent to aspectj-users@eclipse.org by
     * Ricardo on 5/13/2003
     * (https://dev.eclipse.org/mhonarc/lists/aspectj-users/msg00482.html)
     * but his email fails now, so we
     * did not get explicit authorization to post.)
     *
     * @author Ricardo Giacomin, Wes Isberg
     */
    public aspect WatchSetters {
        // just to invoke test code below
        public static void main(String[] args) {
            Client.handleTimer(new Timer());
        }
    
        private static final Class[] GETTER_ARG_TYPES = new Class[]{};
        private static final Object[] GETTER_ARGS = new Object[]{};
        private static final Object NONE = new Object();
    
        /** maintain dirty flag for any IWatched */
        public interface IWatched {}
    
        /** true if new value sent to any setter */
        private boolean IWatched.dirty;
    
        /** false if unable to maintain dirty b/c no privileges, no getter... */
        private boolean IWatched.dirtyValid = true;
    
        /** clients can use dirty flag */
        public boolean IWatched.isDirty() {
            return dirty;
        }
    
        /** clients handle case when dirty flag is invalid */
        public boolean IWatched.isDirtyValid() {
            return dirtyValid;
        }
    
        /** Setters are instance methods returning void,
         * prefixed "set..." and taking exactly 1 argument.
         * Does not use args(id), since that requires the
         * argument be non-null.
         */
        public pointcut setters(IWatched watched) : target(watched)
            && execution(void IWatched+.set*(*)); // advice uses args[0]
    
        /**
         * Skip setter if arg is same as current value;
         * otherwise, set dirty flag after proceeding with setter.
         * Skip this advice if we tried it but failed because
         * there wasn't a corresponding setter, we didn't
         * have the right security permissions, etc.
         */
        void around(IWatched watched) : setters(watched)
                && if(watched.dirtyValid) {
            // get value by invoking getter method
            Object value = NONE;
            try {
                String getterName = "g" +
                    thisJoinPoint.getSignature().getName().substring(1);
                Method method = watched.getClass()
                    .getMethod(getterName, GETTER_ARG_TYPES);
                value = method.invoke(watched, GETTER_ARGS);
            } catch (Throwable t) {
                // NoSuchMethodException, SecurityException,
                // InvocationTargetException...
            }
            if (NONE == value) {
                watched.dirtyValid = false;
                proceed(watched);
                return;
            }
    
            // compare value with arg being set - pointcut has exactly 1 parm
            Object arg = thisJoinPoint.getArgs()[0];
            if (!(null == arg ? value == null : arg.equals(value))) {
                proceed(watched);
                watched.dirty = true;
            }
        }
    }
    
    // ----------- sample clients of WatchSetter
    // classes may opt in - can also use aspects to declare.
    class Timer implements WatchSetters.IWatched {
        private static int ID;
        public final int id = ++ID;
        private int counter;
        public int getCounter() {
            return counter;
        }
        public void setCounter(int i) {
            counter = i;
        }
        public void write() {
            System.out.println("writing " + this);
        }
        public String toString() {
            return "Timer " + id + "==" + counter;
        }
    }
    
    // clients can use dirty flag directly
    class Client {
       static void handleTimer(Timer timer) {
           timer.setCounter(0); // should result in no write
           if (!timer.isDirtyValid() || timer.isDirty()) {
               timer.write();
           }
           timer.setCounter(2);
           if (!timer.isDirtyValid() || timer.isDirty()) {
               timer.write();
           }
       }
    }
    
    // ---- aspects use dirty to implement cache, etc.
    // Volatile things are flushed when dirty
    abstract aspect Volatile {
        // subaspects declare targets using Volatile.ITag
        protected interface ITag {}
        declare precedence : Volatile+, WatchSetters;
        after(WatchSetters.IWatched watched) returning :
                WatchSetters.setters(watched) {
            if (!watched.isDirtyValid() || watched.isDirty()) {
                flushCache(watched);
            }
        }
        abstract void flushCache(Object o);
    }
    
    // treat Timer as volatile, write when flushing
    aspect VolatileTimer extends Volatile {
        declare parents: Timer implements ITag;
        void flushCache(Object o) {
            Timer timer = (Timer) o;
            timer.write();
        }
    }
    
        

    declares

    back to top

    Using declare warning to find Exception-related code

    back to top

    |   Wes Isberg  |  common declares/Declares.java:44  |

    
    /**
     * List AppException catch blocks and callers as a way
     * of investigating a possibly-large code base.
     */
    aspect SeekAppExceptions {
        pointcut withinScope() : within(com.company..*);
    
        /**
         * Find calls to stuff that throws AppException.
         */
        declare warning : withinScope() &&
            (call(* *(..) throws AppException+)
             || call(new(..) throws AppException+)) :
            "fyi, another call to something that can throw IOException";
    
        /**
         * Find catch clauses handling AppException
         */
        declare warning : withinScope() && handler(AppException+):
            "fyi, code that handles AppException";
    }
        

    declares-inoculated

    back to top

    Warn when setting non-public field

    back to top

    |   Wes Isberg  |  inoculated/src com/xerox/printing/CompileTime.java:46  |

        /** warn if setting non-public field outside a setter */
        declare warning :
            within(com.xerox.printing..*)
            && set(!public * *) && !withincode(* set*(..))
            : "writing field outside setter" ;
        

    Error to have accessible sub-Point constructors

    back to top

    |   Wes Isberg  |  inoculated/src RunTime.java:29  |

        /** We make it an error for any Point subclasses to have non-private constructors */
        declare error : execution(!private Point+.new(..))
            && !within(java*..*) :
            "non-private Point subclass constructor";
        

    Error when subclass method handles exception

    back to top

    |   Wes Isberg  |  inoculated/src com/xerox/printing/CompileTime.java:55  |

        declare error : handler(IOException+)
                && withincode(* PrinterStream+.delegate(..))
            : "do not handle IOException in this method";
        

    Error when factory not used

    back to top

    |   Wes Isberg  |  inoculated/src com/xerox/printing/CompileTime.java:61  |

        declare error  : !withincode(Point+ SubPoint+.create(..))
                && within(com.xerox..*)
                && call(Point+.new(..))
            : "use SubPoint.create() to create Point";
        

    declares-softenRemoteException

    back to top

    |   Jim Hugunin, Wes Isberg  |  common declares/Declares.java:78  |

    
    /**
     * Convert RemoteExceptions to RuntimeRemoteException
     * and log them. Enable clients that don't handle
     * RemoteException.
     */
    aspect HandleRemoteException {
        /**
         * Declare RemoteException soft to enable use by clients
         * that are not declared to handle RemoteException.
         */
        declare soft: RemoteException: throwsRemoteException();
    
        /**
         * Pick out join points to convert RemoteException to
         * RuntimeRemoteException.
         * This implementation picks out
         * execution of any method declared to throw RemoteException
         * in our library.
         */
        pointcut throwsRemoteException(): within(com.company.lib..*)
           && execution(* *(..) throws RemoteException+);
    
        /**
         * This around advice converts RemoteException to
         * RuntimeRemoteException at all join points picked out
         * by throwsRemoteException().
         * That means *no* RemoteException will be thrown from
         * this join point, and thus that none will be converted
         * by the AspectJ runtime to SoftException.
         */
        Object around(): throwsRemoteException() {
            try {
                return proceed();
            } catch (RemoteException re) {
                re.printStackTrace(System.err);
                throw new RuntimeRemoteException(re);
            }
        }
    }
        

    Error when not using Thread factory

    back to top

    |   Wes Isberg  |  common declares/Declares.java:13  |

        /** signal error if Thread constructor called outside our Thread factory */
        declare error : call(Thread+.new(..)) && within(com.company..*)
            && !withincode(Thread com.company.lib.Factory.makeThread(..)) :
            "constructing threads prohibited - use Factory.makeThread(..)";
        

    Using declare to enforce type constraints

    back to top

    |   Wes Isberg  |  common declares/Declares.java:28  |

    protected interface SoughtException {}
    // XXX ajc broken here?
    /**
     * Require that any SoughtException implementation be
     * a subclass of Throwable.  This picks out the mistake
     * of declaring SoughtException a parent of something
     * that is not an exception at all.
     */
    declare error : staticinitialization(SoughtException+)
        && ! staticinitialization(SoughtException)
        && ! staticinitialization(Throwable+) :
        "all SoughtException must be subclasses of Throwable";
        

    j2ee

    back to top

    j2ee-myeclipseide

    back to top

    Using MyEclipseIDE to develop AspectJ programs for J2EE

    back to top

    |   Wes Isberg  |  trails myeclipseide.html:5  |

    MyEclipseIde (https://www.myeclipseide.com) aims to make it easy to develop J2EE applications using Eclipse. AJDT (https://eclipse.org/ajdt) is an Eclipse plug-in that supports AspectJ.

    j2ee-servlets

    back to top

    Using AspectJ in servlets

    back to top

    |   Wes Isberg  |  trails j2ee.html:11  |

    AspectJ programs work if run in the same namespace and with aspectjrt.jar. Servlet runners and J2EE web containers should run AspectJ programs fine if the classes and required libraries are deployed as usual. As with any shared library, if more than one application is using AspectJ, then the aspectjrt.jar should be deployed where it will be loaded by a common classloader. The same is true of any shared aspects.

    j2ee-tomcat4

    back to top

    Running AspectJ JSP's in Tomcat 4.x

    back to top

    |   Wes Isberg  |  trails j2ee.html:44  |

    Tomcat 4.x uses the Jasper engine based on Ant to compile JSP's. To set up ajc as the compiler, do the following before starting Tomcat:

    1. Put aspectjtools.jar in ${CATALINA_HOME}/common/lib so that it can be loaded by Jasper.
    2. Update Jasper servlet parameters in ${CATALINA_HOME}/conf/web.xml to tell Ant to use ajc by setting the compiler property to the AspectJ compiler adapter:
          <servlet>
              <servlet-name>jsp</servlet-name>
              <servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class>
              ...
              <init-param>
                  <param-name>compiler</param-name>
                  <param-value>org.aspectj.tools.ant.taskdefs.Ajc11CompilerAdapter</param-value>
              </init-param>
      	
    3. The classpath is dynamically generated from the webapp deployment, so aspectjrt.jar should be in {webapp}/WEB-INF/lib or some shared or common directory supported by the server.
    4. Alternatively, you can precompile JSP's using this Ant script. That involves manually updating the web.xml file with the Jasper-generated servlet mappings.

    Precompile JSP's for Tomcat 4.x using AspectJ

    back to top

    |   Wes Isberg  |  scripts precompile-jsp.build.xml:2  |

    <project name="Precompile Tomcat JSPs" default="all" basedir=".">
    
        <target name="all" depends="jspc,compile"/>
    
        <target name="info">
            <echo>
    This Ant script precompiles deployed .jsp files in Tomcat
    using AspectJ 1.1.
    
    Usage:
    
      {ant} -f {this-script} \
        -Dtomcat.home=/home/tomcat \
        -Dwebapp.name=launchWeb \
        -DASPECTJ_HOME=/dev/tools/aspectj-1.1.0
    
    This defines the web application deployment $${webapp.dir} as
    
       $${tomcat.home}/webapps/$${webapp.name}
    
    , generates the Java source files to
    
       $${webapp.dir}/WEB-INF/src
    
    , uses iajc (AspectJ) to compile them to
    
       $${webapp.dir}/WEB-INF/classes,
    
    , and creates the mappings
    
       $${webapp.dir}/WEB-INF/generated_web.xml,
    
    which must be manually inserted into
    
       $${webapp.dir}/WEB-INF/generated_web.xml,
    
    at which point the web application can be reloaded.
    
    This assumes that aspectjrt.jar is already deployed in
    any of places on the Tomcat application classpath
    (the application, shared, or common classpath).
    If ASPECTJ_HOME is not defined, it assumes that
    aspectjtools.jar is in
    
       ${CATALINA_HOME}/common/lib
    
    </echo>
        </target>
    
      <target name="init">
        <!-- declare these two on command-line -->
        <property name="webapp.name"
            value="launchWeb"/>
    
        <property name="tomcat.home"
            location="i:/home/tomcat"/>
    
        <property name="webapp.path"
            location="${tomcat.home}/webapps/${webapp.name}"/>
        <property name="webapp.src.dir"
            location="${webapp.path}/WEB-INF/src"/>
    
        <property name="ASPECTJ_HOME"
            location="${tomcat.home}/common"/>
    
      </target>
    
      <target name="jspc" depends="init">
    
        <taskdef classname="org.apache.jasper.JspC" name="jasper2" >
          <classpath id="jspc.classpath">
            <pathelement location="${java.home}/../lib/tools.jar"/>
            <fileset dir="${tomcat.home}/server/lib">
              <include name="*.jar"/>
            </fileset>
            <fileset dir="${tomcat.home}/common/lib">
              <include name="*.jar"/>
            </fileset>
          </classpath>
        </taskdef>
    
        <mkdir dir="${webapp.src.dir}"/>
        <jasper2
             validateXml="true"
             uriroot="${webapp.path}"
             webXmlFragment="${webapp.path}/WEB-INF/generated_web.xml"
             outputDir="${webapp.src.dir}" />
    
      </target>
    
      <target name="compile" depends="init">
    
        <mkdir dir="${webapp.path}/WEB-INF/classes"/>
        <mkdir dir="${webapp.path}/WEB-INF/lib"/>
    
          <path id="iajc.classpath">
            <fileset dir="${ASPECTJ_HOME}/lib">
              <include name="aspectjtools.jar"/>
            </fileset>
          </path>
        <taskdef
          resource="org/aspectj/tools/ant/taskdefs/aspectjTaskdefs.properties">
          <classpath refid="iajc.classpath"/>
        </taskdef>
    
        <!-- forking compile so it runs in Eclipse -->
        <iajc destdir="${webapp.path}/WEB-INF/classes"
               sourceroots="${webapp.src.dir}"
               debug="on"
               verbose="true"
               fork="true"
               forkclasspathRef="iajc.classpath"
               failonerror="true"
               >
          <classpath>
            <pathelement location="${webapp.path}/WEB-INF/classes"/>
            <fileset dir="${webapp.path}/WEB-INF/lib">
              <include name="*.jar"/>
            </fileset>
            <pathelement location="${tomcat.home}/common/classes"/>
            <fileset dir="${tomcat.home}/common/lib">
              <include name="*.jar"/>
            </fileset>
            <pathelement location="${tomcat.home}/shared/classes"/>
            <fileset dir="${tomcat.home}/shared/lib">
              <include name="*.jar"/>
            </fileset>
          </classpath>
        </iajc>
    
      </target>
    
    </project>
    
        

    Running AspectJ servlets in Tomcat 4.x

    back to top

    |   Wes Isberg  |  trails j2ee.html:24  |

    In Tomcat, you can deploy application servlets in WAR's or in exploded web directories and share code across applications.

    1. Use ajc to compile the servlets, and deploy the classes as usual into {WebRoot}/WEB-INF/classes.
    2. If your web applications or aspects do not interact, deploy aspectjrt.jar into {WebRoot}/WEB-INF/lib.
    3. If your web applications or aspects might interact, deploy them to ${CATALINA_BASE}/shared/lib.

    language

    back to top

    Pick out latest and original recursive call

    back to top

    |   Erik Hilsdale  |  common language/ControlFlow.java:27  |

        /** call to factorial, with argument */
        pointcut f(int i) : call(int Fact.factorial(int)) && args(i);
    
        /** print most-recent recursive call */
        before(int i, final int j) : f(i) && cflowbelow(f(j)) {
            System.err.println(i + "-" + j);
        }
    
        /** print initial/topmost recursive call */
        before(int i, final int j) : f(i)
            && cflowbelow(cflow(f(j)) && !cflowbelow(f(int))) {
            System.err.println(i + "@" + j);
        }
        

    Implementing double-dispatch

    back to top

    |   Wes Isberg  |  common language/DoubleDispatch.java:30  |

    
    /**
     * By hypothesis, there is a working class with
     * methods taking a supertype and subtypes.
     * The goal of double-dispatch is to execute the
     * subtype method rather than the supertype
     * method selected when the compile-time
     * reference is of the super's type.
     */
    class Worker {
        void run(SuperType t) {}
        void run(SubTypeOne t) {}
        void run(SubTypeTwo t) {}
    }
    
    class SuperType {}
    class SubTypeOne extends SuperType {}
    class SubTypeTwo extends SuperType {}
    
    /** Implement double-dispatch for Worker.run(..) */
    aspect DoubleDispatchWorker {
    
        /**
         * Replace a call to the Worker.run(SuperType)
         * by delegating to a target method.
         * Each target subtype in this method dispatches back
         * to the subtype-specific Worker.run(SubType..) method,
         * to implement double-dispatch.
         */
        void around (Worker worker, SuperType targ):
               !withincode(void SuperType.doWorkerRun(Worker)) &&
                target (worker) && call (void run(SuperType)) &&
                args (targ) {
            targ.doWorkerRun(worker);
        }
    
        void SuperType.doWorkerRun(Worker worker) {
            worker.run(this);
        }
    
        // these could be in separate aspects
        void SubTypeOne.doWorkerRun(Worker worker) {
            worker.run(this);
        }
        void SubTypeTwo.doWorkerRun(Worker worker) {
            worker.run(this);
        }
    }
    
        

    Check input and result for a field set.

    back to top

    |   Erik Hilsdale, Wes Isberg  |  common language/Context.java:42  |

        /**
         * Check input and result for a field set.
         */
        void around(int input, C targ) : set(int C.i)
                && args(input) && target(targ) {
            String m = "setting C.i="  + targ.i  + " to " + input;
            System.out.println(m);
            proceed(input, targ);
            if (targ.i != input) {
                throw new Error("expected " + input);
            }
        }
        

    Log exception being handled

    back to top

    |   Erik Hilsdale, Wes Isberg  |  common language/Context.java:57  |

        /**
         * Log exception being handled
         */
        before (C.MoreError e) : handler(C.MoreError)
                && args(e) && within(C) {
            System.out.println("handling " + e);
        }
        

    Understanding object creation join points

    back to top

    |   Erik Hilsdale, Wes Isberg  |  common language/Initialization.java:36  |

    /*
     * To work with an object right when it is constructed,
     * understand the differences between the join points for
     * constructor call, constructor execution, and initialization.
     */
     // ------- examples of constructors and the ways they invoke each other.
    class Thing {
        String message;
        Thing() {
            this("none");
        }
        Thing(String message) {
            this.message = message;
        }
    }
    
    class AnotherThing extends Thing {
        AnotherThing() {
            super(); // this does not append to message as the one below does.
        }
    
        AnotherThing(String message) {
            super(message + " from-AnotherThing");
        }
    }
    
    aspect InitializationSample {
        // -------- constructor-call picks out the calls
        /**
         * After any call to any constructor, fix up the thing.
         * When creating an object, there is only one call to
         * construct it, so use call(..) avoid duplicate advice.
         * There is no target for the call, but the object
         * constructed is returned from the call.
         * In AspectJ 1.1, this only picks  out callers in the input
         * classes or source files, and it does not pick out
         * invocations via super(..)
         * or this(..).
         */
        after() returning (Thing thing):
                call(Thing.new(..)) {
            thing.message += " after-any-constructor-call";
        }
    
        // -------- constructor-execution picks out each body
        /**
         * After executing the String constructor, fix up the thing.
         * The object being-constructed is available as either
         * this or target.
         * This works even if the constructor was invoked using
         * super() or this() or by code
         * outside the control of the AspectJ compiler.
         * However, if you advise multiple constructors, you'll advise
         * a single instance being constructed multiple times
         * if the constructors call each other.
         * In AspectJ 1.1, this only affects constructors in the input
         * classes or source files.
         */
        after(Thing thing) returning : target(thing) &&
                execution(Thing.new(String)) {
            thing.message += " after-String-constructor-execution";
        }
    
        /**
         * DANGER -- BAD!!  Before executing the String constructor,
         * this uses the target object, which is not constructed.
         */
        before (Thing thing): this(thing) && execution(Thing.new(String)) {
            // DANGER!! thing not constructed yet.
            //thing.message += " before-String-constructor-execution";
        }
    
        // -------- initialization picks out any construction, once
        /**
         * This advises all Thing constructors in one join point,
         * even if they call each other with this(), etc.
         * The object being-constructed is available as either
         * this or target.
         * In AspectJ 1.1, this only affects types input to the compiler.
         */
        after(Thing thing) returning: this(thing)
                && initialization(Thing.new(..)) {
            thing.message += " after-initialization";
        }
    }
        

    library

    back to top

    Defining library pointcuts in a class

    back to top

    |   Wes Isberg  |  common libraries/PointcutLibraryTest.java:18  |

    /** private default implementation of library */
    class PrivatePointcutLibrary {
        pointcut adviceCflow() : cflow(adviceexecution());
        pointcut publicCalls() : call(public * *(..))
            && !adviceCflow()
            ;
    }
    
    /** public interface for library */
    class PointcutLibrary extends PrivatePointcutLibrary {
    }
    
    // ---- different clients of the library
    
    /** client by external reference to library */
    aspect ExternalClientOfLibrary {
      before() : PointcutLibrary.publicCalls() {
          System.out.println("XCL: "
              + thisJoinPointStaticPart);
      }
    }
    
    /** use library by inheriting scope in aspect */
    aspect AEL extends PointcutLibrary {
        before() : publicCalls() {
            System.out.println("AEL: "
                + thisJoinPointStaticPart);
        }
    }
    
    /** use library by inheriting scope in class */
    class CEL extends PointcutLibrary {
        static aspect A {
            before() : publicCalls() {
                System.out.println("CEL: "
                    + thisJoinPointStaticPart);
            }
        }
    }
    
    /** more clients by inheritance */
    aspect CELSubAspect extends CEL {
        before() : publicCalls() {
            System.out.println("CSA: "
                + thisJoinPointStaticPart);
        }
    }
    
    // ---- redefining library pointcuts
    
    //-- affect all clients of PointcutLibrary
    // test: XCL advises Test()
    class VendorPointcutLibrary extends PrivatePointcutLibrary {
        /** add calls to public constructors */
        pointcut publicCalls() : PrivatePointcutLibrary.publicCalls()
            || (call(public new(..)) && !adviceCflow());
        static aspect A {
            declare parents:
                PointcutLibrary extends VendorPointcutLibrary;
        }
    }
    
    //-- only affect CEL, subtypes, & references thereto
    // test: CSA does not advise call(* println(String))
    // test: CSA advises call(* prun())
    class CPlus extends PointcutLibrary {
        /** add calls to private methods, remove calls to java..* */
        pointcut publicCalls() : (PointcutLibrary.publicCalls()
            || (call(private * *(..)) && !adviceCflow()))
            && (!(call(* java..*.*(..)) || call(java..*.new(..))));
        static aspect A {
            declare parents: CEL extends CPlus;
        }
    }
        

    Standard pointcut idioms

    back to top

    |   Wes Isberg  |  common org/aspectj/langlib/Pointcuts.java:13  |

    package org.aspectj.langlib;
    
    import java.io.*;
    import java.util.*;
    
    /**
     * Library of pointcut idioms to use in combination with
     * other pointcuts.
     *
     * @author Wes Isberg
     */
    public final class Pointcuts {
    
        // ------- not staticly-determinable
        public pointcut adviceCflow() : cflow(adviceexecution());
    
        public pointcut notInAdviceCflow() : !adviceCflow();
    
        public pointcut cflowMainExecution() :
            cflow(mainExecution());
    
        // ------- staticly-determinable
    
        public pointcut mainExecution() :
            execution(public static void main(String[]));
    
        /** staticly-determinable to never match any join point */
        public pointcut never();
           // if(false) && execution(ThreadDeath *(ThreadDeath, ThreadDeath));
    
        public pointcut afterAdviceSupported() : !handler(*);
    
        public pointcut aroundAdviceSupported() : !handler(*)
            && !initialization(new(..)) && !preinitialization(new(..));
    
        public pointcut anyMethodExecution() :
            execution(* *(..));
    
        public pointcut anyPublicMethodExecution() :
            execution(public * *(..));
    
        public pointcut anyNonPrivateMethodExecution() :
            execution(!private * *(..));
    
        public pointcut anyConstructorExecution() :
            execution(new(..));
    
        public pointcut anyPublicConstructorExecution() :
            execution(public new(..));
    
        public pointcut anyNonPrivateConstructorExecution() :
            execution(!private new(..));
    
        public pointcut anyPublicFieldGet() :
            get(public * *);
    
        public pointcut anyNonPrivateFieldGet() :
            get(!private * *);
    
        public pointcut anyPublicFieldSet() :
            set(public * *);
    
        public pointcut anyNonPrivateFieldSet() :
            set(!private * *); // also !transient?
    
        public pointcut withinSetter() :  // require !static?
            withincode(void set*(*)); // use any return type? multiple parms?
    
        public pointcut withinGetter() :
            withincode(!void get*()); // permit parms? require !static?
    
        public pointcut anyNonPublicFieldSetOutsideConstructorOrSetter() :
            set(!public * *) && !withincode(new(..))
            && !withinSetter();
    
        public pointcut anyRunnableImplementation() :
            staticinitialization(Runnable+);
    
        public pointcut anyGetSystemErrOut() :
            get(PrintStream System.err) || get(PrintStream System.out);
    
        public pointcut anySetSystemErrOut() :
            call(void System.setOut(..)) || call(void System.setErr(..));
    
        public pointcut withinAnyJavaCode() :
            within(java..*) || within(javax..*);
    
        public pointcut notWithinJavaCode() :
            !withinAnyJavaCode();
    
        public pointcut toStringExecution() :
            execution(String toString()) && !within(String);
    
        /** call or execution of any Thread constructor, including subclasses */
        public pointcut anyThreadConstruction() :
            call(Thread+.new(..)) || execution(Thread+.new(..));
    
        /**
         * Any calls to java.io classes
         * (but not methods declared only on their subclasses).
         */
        public pointcut anyJavaIOCalls() :
            call(* java.io..*.*(..)) || call(java.io..*.new(..));
    
        /**
         * Any calls to java.awt or javax.swing methods or constructors
         * (but not methods declared only on their subclasses).
         */
        public pointcut anyJavaAWTOrSwingCalls() :
            call(* java.awt..*.*(..)) || call(java.awt..*.new(..))
            || call(* javax.swing..*.*(..)) || call(javax.swing..*.new(..));
    
        public pointcut cloneImplementationsInNonCloneable() :
            execution(Object !Cloneable+.clone());
    
        public pointcut runImplementationsInNonRunnable() :
            execution(void !Runnable+.run());
    
        /** any calls to java.lang.reflect or Class.get* (except getName()) */
        public pointcut anySystemReflectiveCalls() :
            call(* java.lang.reflect..*.*(..))
            || (!call(* Class.getName())
                && call(* Class.get*(..)));
    
        /** standard class-loading calls by Class and ClassLoader
         * Note that `Foo.class` in bytecode is `Class.forName("Foo")`,
         * so 'Foo.class' will also be picked out by this pointcut.
         */
        public pointcut anySystemClassLoadingCalls() :
            call(Class Class.forName(..))
            || call(Class ClassLoader.loadClass(..));
    
        public pointcut anySystemProcessSpawningCalls() :
            call(Process Runtime.exec(..))
            || call(Class ClassLoader.loadClass(..));
    
        /** Write methods on Collection
         * Warning: Does not pick out iterator(), even though
         * an Iterator can remove elements.
         */
        public pointcut anyCollectionWriteCalls() :
            call(boolean Collection+.add(Object))
            || call(boolean Collection+.addAll(Collection))
            || call(void Collection+.clear())
            || call(boolean Collection+.remove(Object))
            || call(boolean Collection+.removeAll(Collection))
            || call(boolean Collection+.retainAll(Collection));
    
        public pointcut mostThrowableReadCalls() :
            call(* Throwable+.get*(..))
            || call(* Throwable+.print*(..))
            || call(String Throwable+.toString(..));
    
        public pointcut exceptionWrappingCalls() :
            (args(Throwable+,..) || args(.., Throwable+))
            && (set(Throwable+ Throwable+.*)
                || (call(* Throwable+.*(..))
                    || call(Throwable+.new(..))));
    
        public pointcut anyCodeThrowingException() :
            execution(* *(..) throws Exception+)
                || execution(new(..) throws Exception+);
    
        private Pointcuts() {} // XXX avoid this? else pointcuts match it...
    }
        

    pubs

    back to top

    pubs-all

    back to top

    List of AspectJ publications

    back to top

    |   AspectJ community  |  trails links.html:17  |

    For a list of publications about AspectJ that might contain sample code, see the publications link off the AspectJ web site at https://eclipse.org/aspectj

    pubs-books

    back to top

    AspectJ in Action

    back to top

    |   Ramnivas Laddad  |  trails links.html:28  |

    AspectJ in Action by Ramnivas Laddad has sample code including four patterns: the worker object creation pattern, the exception introduction pattern, the participant pattern, and the wormhole pattern. Find the code at https://www.manning.com/laddad/.

    pubs-papers

    back to top

    Aspect-Oriented Design Pattern Implementations

    back to top

    |   Jan Hanneman  |  trails links.html:45  |

    In the OOPSLA 2002 paper Design Pattern Implementation in Java and AspectJ Jan Hannemann and Gregor Kiczales discuss the implementation in AspectJ of 23 of the traditional "gang of four" design patterns (from the book Design Patterns: elements of reusable object-oriented software by Gamma, Helm, Johnson, and Vlissides). Their paper and the code for those patterns are available from https://www.cs.ubc.ca/~jan/AODPs/ . The code is also available from the AspectJ web site as ubc-design-patterns.zip. The latest version is checked into the CVS repository along with other sample code at org.aspectj/modules/docs/sandbox/ubc-design-patterns. For instructions on downloading code from the CVS repository, see the FAQ entry "buildingsource".

    pubs-projects

    back to top

    ATrack bug tracker

    back to top

    |   Ron Bodkin and others  |  trails links.html:69  |

    The aTrack project aims to create an open source bug tracking application that demonstrates use of Aspect-Oriented Programming (AOP) with AspectJ. It uses AOP pragmatically to provide systematic support for technical, middleware, and business concerns.

    scripts

    back to top

    Shell script to use ajc to weave jars and then run

    back to top

    |   Wes Isberg  |  scripts snippets.sh:6  |

    ASPECTJ_HOME="${ASPECTJ_HOME:-c:/aspectj-1.1.0}"
    ajc="$ASPECTJ_HOME/bin/ajc"
    
    # make system.jar by weaving aspects.jar into lib.jar and app.jar
    $ajc -classpath "$ASPECTJ_HOME/lib/aspectjrt.jar" \
         -aspectpath aspects.jar" \
         -injars "app.jar;lib.jar" \
         -outjar system.jar
    
    # XXX copy any required resources from META-INF directories
    
    # run it
    java -classpath "aspects.jar;system.jar" com.company.app.Main
    
        

    testing

    back to top

    testing-inoculated

    back to top

    Advise calls to the proxy object only

    back to top

    |   Wes Isberg  |  inoculated/src StubReplace.java:37  |

        pointcut stubWrite() : printerStreamTestCalls() && target(StubStream);
    
        pointcut printerStreamTestCalls() : call(* PrinterStream.write());
    
        before() : stubWrite() {
            System.err.println("picking out stubWrite" );
        }
        

    Log failures

    back to top

    |   Wes Isberg  |  inoculated/src MainFailure.java:28  |

        /** log after failure, but do not affect exception */
        after(String[] args) throwing (Throwable t) : main(args) {
            logFailureCase(args, t, thisJoinPoint);
        }
        

    Inject IOException on test driver command

    back to top

    |   Wes Isberg  |  inoculated/src Injection.java:24  |

        /** the test starts when the driver starts executing */
        pointcut testEntryPoint(TestDriver driver) :
            target(driver) && execution(* TestDriver.startTest());
    
        /**
         * The fault may be injected at the execution of any
         * (non-static) PrinterStream  method that throws an IOException
         */
        pointcut testCheckPoint(PrinterStream stream) : target(stream)
            && execution(public * PrinterStream+.*(..) throws IOException);
    
        /**
         * After the method returns normally, query the
         * test driver to see if we should instead throw
         * an exception ("inject" the fault).
         */
        after (TestDriver driver, PrinterStream stream) returning
                throws IOException :
                cflowbelow(testEntryPoint(driver))
                && testCheckPoint(stream) {
            IOException e = driver.createException(stream);
            if (null != e) {
                System.out.println("InjectingIOException - injecting " + e);
                throw e;
            }
        }
        /* Comment on the after advice IOException declaration:
    
           "throws IOException" is a declaration of the advice,
           not the pointcut.
    
           Since the advice might throw the injected fault, it
           must declare that it throws IOException. When advice declares
           exceptions thrown, the compiler will emit an error if any
           join point is not also declared to throw an IOException.
    
           In this case, the testCheckPoint pointcut only picks out
           methods that throw IOException, so the compile will not
           signal any errors.
        */
        

    Constructor execution

    back to top

    |   Wes Isberg  |  inoculated/src RuntimeWrites.java:47  |

        /** execution of any constructor for PrinterStream */
        pointcut init() : execution(PrinterStream+.new(..));
        

    Using around for integration testing

    back to top

    |   Wes Isberg  |  inoculated/src BufferTest.java:23  |

        /**
         * When PrinterBuffer.capacity(int) is called,
         * test it with repeatedly with a set of input
         * (validating the result) and then continue with
         * the original call.
         *
         * This assumes that the capacity method causes no
         * relevant state changes in the buffer.
         */
        int around(int original, PrinterBuffer buffer) :
            call(int PrinterBuffer.capacity(int)) && args(original) && target(buffer) {
            int[] input = new int[] { 0, 1, 10, 1000, -1, 4096 };
            for (int i = 0; i < input.length; i++) {
                int result = proceed(input[i], buffer); // invoke test
                validateResult(buffer, input[i], result);
            }
            return proceed(original, buffer);           // continue with original processing
        }
        

    Prohibit field writes by other instances

    back to top

    |   Wes Isberg  |  inoculated/src RuntimeWrites.java:66  |

        /**
         * Handle any situation where fields are written
         * by another object.
         */
        before(Object caller, PrinterStream targ) : this(caller)
            && target(targ) && fieldWrites() {
            if (caller != targ) {
                String err = "variation 1: caller (" + caller
                    + ") setting fields in targ (" + targ + ")";
                handle(err, thisJoinPointStaticPart);
            }
        }
        

    Prohibit writes by other instances and static methods

    back to top

    |   Wes Isberg  |  inoculated/src RuntimeWrites.java:91  |

        /**
         * Handle any situation where fields are written
         * other than by the same object.
         */
        before(PrinterStream targ) : target(targ) && fieldWrites() {
            Object caller = thisJoinPoint.getThis();
            if (targ != caller) {
                String err = "variation 2: caller (" + caller
                    + ") setting fields in targ (" + targ + ")";
                handle(err, thisJoinPointStaticPart);
            }
        }
        

    Prohibit writes by subclasses

    back to top

    |   Wes Isberg  |  inoculated/src RuntimeWrites.java:82  |

        /** any write to a non-static field in PrinterStream or any subclasses */
        //pointcut fieldWrites() : set(!static * PrinterStream+.*);
    
        /** execution of any constructor for PrinterStream or any subclasses */
        //pointcut init() : execution(PrinterStream+.new(..));
        

    Prohibit field writes after construction

    back to top

    |   Wes Isberg  |  inoculated/src RuntimeWrites.java:52  |

        /** any write to a non-static field in PrinterStream itself */
        pointcut fieldWrites() : set(!static * PrinterStream.*);
    
        /**
         * Handle any situation where fields are written
         * outside of the control flow of initialization
         */
        before() : fieldWrites() && !cflow(init()) {
            handle("field set outside of init", thisJoinPointStaticPart);
        }
        

    Replace object with proxy on constructiono

    back to top

    |   Wes Isberg  |  inoculated/src StubReplace.java:25  |

        /**
         * Replace all PrintStream with our StubStream
         * by replacing the call to any constructor of
         * PrinterStream or any subclasses.
         */
        PrinterStream around () : within(PrintJob)
            && call (PrinterStream+.new(..)) && !call (StubStream+.new(..)) {
            return new StubStream(thisJoinPoint.getArgs());
        }
        

    Round-trip integration testing

    back to top

    |   Wes Isberg  |  inoculated/src com/xerox/printing/RoundTrip.java:28  |

        /**
         * After returning a PrinterStream from any call in our
         * packages, verify it by doing a round-trip between
         * PrinterStream and BufferedPrinterStream.
         * This uses a round-trip as a way to verify the
         * integrity of PrinterStream, but one could also use
         * a self-test (built-in or otherwise) coded specifically
         * for validating the object (without changing state).
         */
        after () returning (PrinterStream stream) :
            call (PrinterStream+ com.xerox.printing..*(..))
            && !call (PrinterStream PrinterStream.make(BufferedPrinterStream)) {
            BufferedPrinterStream bufferStream = new BufferedPrinterStream(stream);
            PrinterStream newStream = PrinterStream.make(bufferStream);
            if (!stream.equals(newStream)) {
                throw new Error("round-trip failed for " + stream);
            } else {
                System.err.println("round-trip passed for " + stream);
            }
        }
        

    Throw Error when factory returns null

    back to top

    |   Wes Isberg  |  inoculated/src RunTime.java:37  |

        /** Throw Error if a factory method for creating a Point returns null */
        after () returning (Point p) :
            call(Point+ SubPoint+.create(..)) {
            if (null == p) {
                String err = "Null Point constructed when this ("
                    + thisJoinPoint.getThis()
                    + ") called target ("
                    + thisJoinPoint.getTarget()
                    + ") at join point ("
                    + thisJoinPoint.getSignature()
                    + ") from source location ("
                    + thisJoinPoint.getSourceLocation()
                    + ") with args ("
                    + Arrays.asList(thisJoinPoint.getArgs())
                    + ")";
                throw new Error(err);
            }
        }
        

    tracing

    back to top

    Record time to execute public methods

    back to top

    |   Wes Isberg  |  common tracing/Logging.java:10  |

        /** record time to execute my public methods */
        Object around() : execution(public * com.company..*.* (..)) {
            long start = System.currentTimeMillis();
            try {
                return proceed();
            } finally {
                long end = System.currentTimeMillis();
                recordTime(start, end,
                    thisJoinPointStaticPart.getSignature());
            }
        }
        // implement recordTime...
        

    Trace join points executed to log

    back to top

    |   Jim Hugunin, Wes Isberg  |  common tracing/TraceJoinPoints.java:2  |

    /* TraceJoinPoints.java */
    
    package tracing;
    
    import org.aspectj.lang.*;
    import org.aspectj.lang.reflect.*;
    import java.io.*;
    
    /**
     * Print join points being executed in context to a log.xml file.
     * To use this, define the abstract pointcuts in a subaspect.
     * @author Jim Hugunin, Wes Isberg
     */
    public abstract aspect TraceJoinPoints
        extends TraceJoinPointsBase {
    
        // abstract protected pointcut entry();
    
        PrintStream out;
        int logs = 0;
        int depth = 0;
        boolean terminal = false;
    
        /**
         * Emit a message in the log, e.g.,
         * <pre>TraceJoinPoints tjp = TraceJoinPoints.aspectOf();
         * if (null != tjp) tjp.message("Hello, World!");</pre>
         */
        public void message(String s) {
            out.println("" + prepareMessage(s) + "");
        }
    
        protected void startLog() {
            makeLogStream();
        }
    
        protected void completeLog() {
            closeLogStream();
        }
    
        protected void logEnter(JoinPoint.StaticPart jp) {
            if (terminal) out.println(">");
            indent(depth);
            out.print("<" + jp.getKind());
            writeSig(jp);
            writePos(jp);
    
            depth += 1;
            terminal = true;
        }
    
        protected void logExit(JoinPoint.StaticPart jp) {
            depth -= 1;
            if (terminal) {
                getOut().println("/>");
            } else {
                indent(depth);
                getOut().println("");
    }
    terminal = false;
    }
    
    protected PrintStream getOut() {
    if (null == out) {
    String m = "not in the control flow of entry()";
    throw new IllegalStateException(m);
    }
    return out;
    }
    
    protected void makeLogStream() {
    try {
    String name = "log" + logs++ + ".xml";
    out = new PrintStream(new FileOutputStream(name));
    } catch (IOException ioe) {
    out = System.err;
    }
    }
    
    protected void closeLogStream() {
    PrintStream out = this.out;
    if (null != out) {
    out.close();
    // this.out = null;
    }
    }
    
    /** @return input String formatted for XML */
    protected String prepareMessage(String s) { // XXX unimplemented
    return s;
    }
    
    void message(String sink, String s) {
    if (null == sink) {
    message(s);
    } else {
    getOut().println("
    " + prepareMessage(s) + "
    
    ");
    }
    }
    
    void writeSig(JoinPoint.StaticPart jp) {
    PrintStream out = getOut();
    out.print(" sig=");
    out.print(quoteXml(jp.getSignature().toShortString()));
    }
    
    void writePos(JoinPoint.StaticPart jp) {
    SourceLocation loc = jp.getSourceLocation();
    if (loc == null) return;
    PrintStream out = getOut();
    
    out.print(" pos=");
    out.print(quoteXml(loc.getFileName() +
    ":" + loc.getLine() +
    ":" + loc.getColumn()));
    }
    
    protected String quoteXml(String s) { // XXX weak
    return "\"" + s.replace('<', '_').replace('>', '_') + "\"";
    }
    
    protected void indent(int i) {
    PrintStream out = getOut();
    while (i-- > 0) out.print(" ");
    }
    }
    

    Trace join points executed

    back to top

    |   Jim Hugunin, Wes Isberg  |  common tracing/TraceJoinPointsBase.java:2  |

    /* TraceJoinPointsBase.java */
    
    package tracing;
    
    import org.aspectj.lang.JoinPoint;
    
    /**
     * Trace join points being executed in context.
     * To use this, define the abstract members in a subaspect.
     * Warning: this does not trace join points that do not
     * support after advice.
     * @author Jim Hugunin, Wes Isberg
     */
    abstract aspect TraceJoinPointsBase {
        // this line is for AspectJ 1.1
        // for Aspectj 1.0, use "TraceJoinPointsBase dominates * {"
        declare precedence : TraceJoinPointsBase, *;
    
        abstract protected pointcut entry();
    
        protected pointcut exit(): call(* java..*.*(..));
    
        final pointcut start(): entry() && !cflowbelow(entry());
    
        final pointcut trace(): cflow(entry())
            && !cflowbelow(exit()) && !within(TraceJoinPointsBase+);
    
        private pointcut supportsAfterAdvice() : !handler(*)
            && !preinitialization(new(..));
    
        before(): start() { startLog(); }
    
        before(): trace() && supportsAfterAdvice(){
            logEnter(thisJoinPointStaticPart);
        }
    
        after(): trace() && supportsAfterAdvice() {
            logExit(thisJoinPointStaticPart);
        }
    
        after(): start() { completeLog(); }
    
        abstract protected void logEnter(JoinPoint.StaticPart jp);
        abstract protected void logExit(JoinPoint.StaticPart jp);
        abstract protected void startLog();
        abstract protected void completeLog();
    }
    
        

    Trace to log join points executed by main method

    back to top

    |   Jim Hugunin, Wes Isberg  |  common tracing/TraceMyJoinPoints.java:3  |

    /* TraceMyJoinPoints.java */
    
    package tracing;
    
    import com.company.app.Main;
    
    /**
     * Trace all join points in company application.
     * @author Jim Hugunin, Wes Isberg
     */
    aspect TraceMyJoinPoints extends TraceJoinPoints {
        protected pointcut entry() : execution(void Main.runMain(String[]));
    }
        

    trails

    back to top

    trails-debugging

    back to top

    Debugging AspectJ 1.0 Programs

    back to top

    |   Wes Isberg  |  trails debugging.html:10  |

    The AspectJ 1.0 compiler produces .class files that have the normal source file attribute and line information as well as the information specified by JSR-045 required to debug .class files composed of multiple source files. This permits the compiler to inline advice code into the .class files of the target classes; the advice code in the target class can have source attributes that point back to the aspect file.

    Support for JSR-45 varies. At the time of this writing, Sun's VM supports it, but not some others, which means that the Sun VM must be used as the runtime VM. Because the VM does all the work of associating the source line with the code being debugged, debuggers should be able to operate normally with AspectJ 1.0 source files even if they weren't written with that in mind, if they use the correct API's to the debugger. Unfortunately, some debuggers take shortcuts based on the default case of one file per class in order to avoid having the VM calculate the file suffix or package prefix. These debuggers do not support JSR-45 and thus AspectJ.

    Debugging AspectJ 1.1 Programs

    back to top

    |   Wes Isberg  |  trails debugging.html:43  |

    The AspectJ 1.1 compiler usually implements advice as call-backs to the aspect, which means that most AspectJ programs do not require JSR-45 support in order to be debugged. However, it does do a limited amount of advice inlining; to avoid this, use the -XnoInline flag.

    Because inlined advice can be more efficient, we plan to support JSR-45 as soon as feasible. This will require upgrading the BCEL library we use to pass around the correct source attributes.

    Sometimes debuggers correctly get the source line information, but fail when they attempt to parse AspectJ source files expected to contain Java code. For these it might be possible to step into AspectJ code, but not to set breakpoints, or to work around the parse errors by using .aj rather than .java as a file extension.

    Author Index

  • AspectJ community
  • Erik Hilsdale
  • Erik Hilsdale, Wes Isberg
  • Jan Hanneman
  • Jim Hugunin, Wes Isberg
  • Ramnivas Laddad
  • Ricardo Giacomin, Wes Isberg
  • Ron Bodkin and others
  • Wes Isberg