Programming with JAC

Programming guidelines, restrictions

The programming philosophy of JAC is to let the functional (core-business) program components free from any reference towards all the technical or business extensions that it needs to be effective. Thus, JAC functional components do not have to implement special interface or extend special classes. All this is done seamlessly by the JAC container. In J2EE world, this kind of containers begin to appear with POJOs (Plain Old Java Object) support. However, AOP (the core technology of JAC) allows better separated concern and naturally fully fonctional objects.

Yt can be troubleshooting for the programmer when s/he makes its first JAC program. The idea is to concentrate on the core functionalities and to make a program as if you were a very beginner in Java. For instance, even if you know that you will need to display a given component or that a given component will need to be persistent, just program a regular Java class and do not try to import any packages that deal with persistence or GUI, all these will be done in a second step and will not require any reengineering.

This magic stuff is possible because of the aspect-oriented framework implemented by JAC. This framework builds a set of classes and objects that allows the programmer to define new aspects or use existing ones in a very simple fashion. Once configured, the aspects will be able to deal with your components the way you expect and introduce new concerns on them (e.g. persistence, transaction, distribution, fault-tolerance, ...).

However, since existing aspects are generic and may be applied to several programs, they have made some assumptions on how your program works and expect the programmer to follow some rules that can be seen as restrictions. Some programming restrictions example are given below (note that most of these restrictions will be overcome in further versions).

Configuring the existing aspects

In most cases, a regular programmer will find the aspects s/he needs for its application provided by the JAC distribution (well, this will be the case in a close future, we hope). As a consequence, programming an aspect-oriented application is just a matter of choosing the right aspects and configure them to behave properly for the final program.

Each existing aspect within the distribution corresponds to an aspect component. Each aspect component is documented so that any final programmer is able to use it just by taking a look at its API (in the worst cases, some code reading may be necessary). Here is a list of the most useful aspect components provided by JAC (with a direct link towards their API documentation).

NOTE: a useful piece of documentation is also the package that corresponds to a given aspect component. In general, we explain here how it works and make a short summary of its configuration methods (for instance, see persistence, or GUI).

There are two manners to customize an aspect for your own application needs.

The simple way

You write a configuration file for your aspect and use it to call all the parametrisation methods that you need. These configuration files are usually in the same directory than the application *.java files and are *.acc files (for aspect-component configuration).

Here is a sample configuration file for the persistence aspect of the photos sample (see persistence.acc in the sample directory).

configureStorage "org.objectweb.jac.aspects.persistence.FSStorage" { "data/photos" };
makePersistent "org.objectweb.jac.samples.photos.*" ALL;
registerStatics org.objectweb.jac.samples.photos.PhotoRepository photorepository0;
registerStatics org.objectweb.jac.samples.photos.Users users0;

Note that all the called methods (configureStorage, makePersistent, and registerStatics) are part of the API of the PersistenceAC aspect component.

Once you have written this file, the advantage is that JAC will be able to parse it dynamically and instantiate the corresponding aspect component. This is a very flexible mechanism since the aspect can be unwoven and woven again with a different configuration while the application is running. The following technique is more static.

The complex way

However, in some cases, the aspect component will not provide the configuration API that you need (our aspect components are more-or-less finalized). In these cases, the programmer needs to add some configuration methods to the aspect component s/he needs to use or to create a new aspect component that extends the existing one so that the configuration is automatically done at the aspect component's construction time (in this case, you MUST declare your new aspect within the org.objectweb.jac.prop file).

Important note: in the case you decide to add extra configuration methods to our existing components and if you think they can be useful for others, then it would be great if you could send us your code so that we include them into the next releases. Thanks.

Programming a JAC application's launcher/descriptor

Once all the aspect configurations are made, you must declare to the system that your program uses them. This is done by programming the application's launcher/descriptor.

The program should be launched by a static main method (as usual). This launching method is declared in a JAC application descriptor (*.jac files) that also registers the program within the applications repository, construct the supported aspects configurations.

public class Run {
   public static void main( String[] args ) {
      // create the root object
      new MyRootClass();
   }
}
// the application descriptor (myApp.jac)
applicationName: myApp
launchingClass: myAppPath.Run
aspects: \
  persistence myPath/persistence.acc true

By default, all the objects are wrappable except wrappers, aspect components and exceptions. If you need an object to be regular (not wrappable), you need to declare it in the org.objectweb.jac.prop file in your JAC distribution root directory.

To run the application then use org.objectweb.jac.jar.

java -jar org.objectweb.jac.jar -C myClassPath myAppPath/myApp.jac arg1 ...

To know about the launching options, use the command jac -h or refer the JAC launching class org.objectweb.jac.core.Jac.

Programming new aspects

Overview on the programming model

You must keep in mind that JAC is not a language but a framework. Thus, most of the programming is done by extending the classes or implementing the interfaces that are provided by the org.objectweb.jac.core package.

The next sections present all the programming concepts that are needed to program new aspects from scratch and finally show a simple aspect example.

Programming features

Programming a wrapping method

A wrapping method is a regular method that is implemented within a subclass of org.objectweb.jac.core.Wrapper.

public class MyWrapper extends Wrapper {
   // a wrapper must call the Wrapper(AspectComponent) 
   // constructor
   public MyWrapper(AspectComponent ac) {
       super(ac);
   }
   // A wrapping method must always have this prototype
   public Object myWrappingMethod (Interaction interaction) {...};
}

Within the runtime context of wrapping method which is reified by the passed interaction, the programmer can access several informations about the current method call (actually the current collaboration's interaction).

A wrapping method can wrap several base object methods. The arguments are seamlessly set to the values corresponding to a given base-level call.

Within a wrapping method, the wrapper can perform treatments and manipulate its arguments. A wrapper task is partitioned in two parts: the before part, that is called before the base method is called, and the after part, that is called after it. Between the before and after parts, the wrapper programmer must call the base object. Since many wrappers can wrap a base object, the call process to the base object is a special process that continues the wrapping chain if needed (and calls the base object if there is no wrappers left). To perform it, the wrapper programmer must use the proceed method provided by the Wrapper class. A wrapper must return an object that stands for the return value of the base function (it has to be a compatible type).

Thus, a typical body of a wrapping method is:

Object ret = null;
// before job
...
ret = proceed(interaction);
// after job
...
return ret;

To show how wrappers can be used, here are ten simple examples of very classical wrapping function bodies. Note that a wrapping method can access the wrapper state that is not represented in the following examples.

Example 1:

A verbose wrapping method (that prints a trace on the screen --- easily modifiable to print in a log file).

Object ret = null;
System.out.println("<< calling "+interaction.method+
                   " with "+interaction.args+" >>");
ret = proceed(interaction);
System.out.println("<< "+interaction.method+" returned "+ret+" >>");
return ret;

Example 2:

A wrapping method that checks the type of the client and raises an exception if the client is not allowed (note, this exception should be handled by an exception handler).

Object ret = null;
if ( attr("cient") instanceof AllowedType ) {
  ret = proceed(interaction);
} else {
  throw new NotAllowedTypeException();
}
return ret;

Example 3:

A wrapping method that performs some precondition test on the arguments (here, the first argument is an integer and must be bounded within 1-100).

Object ret = null;
if (((Integer)interaction.args[0]).intValue() >= 0 &&
    ((Integer)interaction.args[0]).intValue() <= 100) {
  ret = proceed(interaction);
} else {
  throw new ArgOutOfBoundsException();
}
return ret;

Example 4:

Almost the same wrapping method but, that corrects the argument value instead of throwing an exception.

if (((Integer)interaction.args[0]).intValue() < 0) 
  interaction.args[0] = 0;
if (((Integer)interaction.args[0]).intValue() > 100) 
  interaction.args[0] = 100;
return proceed(interaction);

Example 5:

A wrapper that stores the object state within a database (to make it persistent).

Object ret = null;
ret = proceed(interaction);
Database d = new Database();
d.connect();
d.write(interaction.wrappee, Utils.getObjectState(interaction.wrappee)); 
return ret;

Example 6:

A wrapping method that performs a postcondition test on the return value (in this case, the return value must be an integer bounded within 0 and 100).

Object ret = null;
ret = proceed(interaction);
if (((Integer)ret).intValue() < 0 || ((Integer)ret).intValue() > 100) {
  throw new RetOutOfBoundsException()
}
return ret;

Example 7:

A wrapping method that performs a test on the state of the wrappee after the base function has been called.

Object ret = null;
ret = proceed(interaction);
if (  ((Integer)ClassRepository.get()
   .getClass(interaction.wrappee)
   .getField("aField").get()).intValue() < 0
   || ((Integer)ClassRepository.get()
   .getClass(interaction.wrappee)
   .getField("aField").get()).intValue() > 100)
{
  throw new FieldOutOfBoundsException();
}
return ret;

Example 8:

A wrapping method that forwards the call to a set of replicas (e.g. to implement a fault-tolerance aspect).

Object ret = null;
// replicas is a RemoteRef[] field of the wrapper
for (int i = 0; i < replicas.length; i++) {
  interaction.invoke(replicas[i]);
}
return proceed(interaction);

Example 9:

A wrapping method that forwards the call to a remote object (acts like a proxy). Note that this wrapper does not call the proceed method so that the wrappee is never called.

return remoteRef.invoke(interaction.method,interaction.args);

Example 10:

A wrapping method that caches the results of the wrappee and that uses the cached values when the arguments and the wrappee state have already been used.

Object ret = null;
if (isCacheHit(interaction.wrappee, interaction.args)) {
  ret = getCachedValue(interaction.wrappee, interaction.args);
} else {
  ret = proceed(interaction);
  setCachedValue(interaction.wrappee, interaction.args, ret);
}
return ret;

Programming a role method

A role method is a regular public method of a wrapper.

public ret_type myRoleMethod(Wrappee wrappee, user_defined_args... );

The goal of the role method is to extend the wrappee functionalities. A role method can be invoked by using the Wrapping API (class org.objectweb.jac.core.Wrapping) as follows:

ret = Wrapping.invokeRoleMethod(o, "myRoleMethod", 
                                user_defined_args_values);

Within the runtime context of a role method the wrappee parameter is the object on which the role method is invoked.

We will not give any precise example of role methods since role methods are exactly regular methods except that they are implemented in wrappers. Using role method is a great alternative to inheritance since they can be dynamically added or removed on a per-object basis. As an example, if you want to implement a transactional aspect, you could add prepare, commit and rollback role methods to the set of objects that need to participate a transaction (even if they do not belong to the same class).

Programming an exception handling method

An exception handling method is a regular method (of a wrapper) that presents a prototype which is the following:

public Object myHandler(Interaction interaction, AnException e);

Within its runtime context, interaction.wrappee is a reference to the object that is wrapped. The interaction.method and interaction.args are the wrappee method where the exception occurs and the arguments with which it was called.

The exception handling method is automatically notified if one of the method called by the wrappee raises an exception that is of the type of the one declared in the exception handling method.

Exception handlers are really useful when programming aspects since some exceptions can be raised by the wrappers (so that they are not declared in the throws clause of the wrappee interface and are consequently not handled by the client). For instance, in the wrapping method examples 2, 3, 6, and 7, the wrapping methods throw some exceptions that will stop the program execution if they are not handled.

As an example, here is an exception handling method for the 7th sample. This exception handler should wrap the clients of the 7th wrapper wrappees. To make it not so trivial than just printing a error message, this handler retries once to call the originally called method when it receives an RetOutOfBoundsException. This can be an implementation basis for fault-detection and tolerance aspect for an application that needs it.

public class RetCheckingWrapper extends Wrapper {
   int nRetry = 0;
   public Object handler(Interaction interaction, 
                         RetOutOfBoundsException e) {

      Object ret = null;
      // retry to call the method once
      nRetry ++;
      if (nRetry < 1) {
         ret = interaction.invoke(interaction.wrappee);
      } else {
         System.out.println ("We retried once but there is still a " + e);
	 throw new InconsistencyError( 
            interaction.wrappee, 
            interaction.method,
            interaction.args);
      }
      return ret;
  }
}

Even if the caught exception is not raised by an aspect, exception handlers can be useful when you want to have a clean code that is not polluted by catching all the runtime exceptions that can be thrown by the environment. For instance, you can wrap all the object of the application by exception handlers that catch the file system full exceptions so that your application is secure and readable.

Reflective features and contextual attributes

To be aspect compliant, the reflective code must use the org.objectweb.jac.core.rtti package instead of the java.lang.reflect one.

The JAC system offers the ability to introspect and query objects by using the org.objectweb.jac.core.ObjectRepository class.

Moreover, JAC allows the programmer to access the running collaboration. A running context is composed of thread local attributes (that can be dynamically defined at runtime). The collaboration propagates with the method calling flow.

The attributes are part of the collaboration and can be set in any object that implements the org.objectweb.jac.core.CollaborationParticipant interface, i.e. the wrappers and the aspect components. When an attribute has been set, all the subsequently called methods will also have access to this attribute value. For instance:

// no attributes are defined...
...
// defines an attribute 'a'
attrdef("a", "aValue1");
try {
  ...
  // "a" is equal to "aValue1" for all the following calls
  // of the same thread
  attr("a"); // ==a
  ...
} finally {
  // resets it
  attrdef("a", null);
}

Pointcuts expressions and definitions

How to use pointcuts?

A pointcut is an object that belongs to an aspect component and that is able to be parametrized to automatically wrap or perform aspect-actions on a set of base-objects regarding four pointcut expressions.

A pointcut expression is an expression based on regular expressions and can include other keywords or operators as it will be explained later on. For a given method of any base object, the pointcut is activated if all the pointcut expressions match.

Thus, all the methods of all the objects in the system are checked once by the pointcut. A given method will be extended by the pointcut if:

When a method matches these four pointcut-expressions, then either the object(s)/method(s) are wrapped by a wrapper, either an action is performed at the construction of the matching object. There are two ways to tell which wrapper should be used by a pointcut:

The recommanded entry point to use poincuts are the factory methods of the aspect components that allow the programmer to create relevant pointcuts (see the pointcut methods in org.objectweb.jac.core.AspectComponent.

Here is a sample of use of the pointcut feature for a tracing aspect.

If you got a tracing wrapper such as:

public class TracingWrapper extends Wrapper {
    public TracingWrapper(AspectComponent ac) {
        super(ac);
    }
    public Object verboseCall(Interaction i) {
        System.out.println(i.method+" is called");
        Object ret = proceed();
        System.out.println(i.method+" is returning "+ret);
    }
}

Then, the handy way of programming a verbose aspect that traces all the calls within the system is:

public class Tracing_1_AC extends AspectComponent {
    TracingWrapper wrapper = new TracingWrapper(this);
    public void whenUsingNewInstance(Interaction i) {
        if(i.wrappee!=null) {
            Wrapping.wrapAll(i.wrappee, wrapper, "verboseCall");
        }
    }
}

This is nice but very low-level since you have to overload the whenUsingNewInstance method and you have to know the Wrapping API to wrap the object by hand. Moreover, if you want to make more subtil filters on which objects or methods to wrap, it becomes tedious. For instance, imagine you want to make verbose only one instance called "o1", and one method of this instance called "m1":

public class Tracing_2_AC extends AspectComponent {
    TracingWrapper wrapper = new TracingWrapper(this);
    public void whenUsingNewInstance(Interaction i) {
        String name = NameRepository.get().getName(i.wrappee);
        if(name!=null && name.equals( "o1" ) ) {
            Wrapping.wrap( i.wrappee, wrapper, "verboseCall", "m1" );
        }
    }
}

Pointcuts offer an interesting, powerful, and simple alternative to this technique (that can still be used if pointcuts do not work). Let us rewrite Tracing_1_AC and Tracing_2_AC:

public class Tracing_1_AC extends AspectComponent {
    Tracing_1_AC() {
        pointcut(".*",".*",".*",TracingWrapper.class.getName(),
                 "verboseCall",false,null);
    }
}
public class Tracing_2_AC extends AspectComponent {
    Tracing_2_AC() {
        pointcut("o1",".*","m1.*",TracingWrapper.class.getName(),
                 "verboseCall",false,null);
    }
}

Please note that all the concision of the pointcut notation comes from the regular expression syntax that is very powerful (we use GNU regexp for the actual implementation).

Object-Pointcut-Expression Paths

Let us recall that the naming aspect of JAC names the objects that are created in the program with the following convention:

newObject.getClass().getShortName().lowerCase() + 
newObject.getClass().getInstanceCount()

Thus, if newObject is of the org.objectweb.jac.samples.Calcul, then the first created calcul instance name is calcul0, the second is calcul1, and so on.

When you want to weave a given aspect to some objets, you can refer their names instead of their class. For instance, in a pointcut definition, writing:

object expression = ".*"
class expression  = "org.objectweb.jac.samples.Calcul"

Is equivalent to:

object expression = "calcul[0-9]*"
class expression =  ".*"

But the point here is to be able to treat objects differently even if they are instances of the same class. For instance, the following pointcut expressions activates the pointcut *ONLY* on the three first calcul instances (and not on the other instances).

object expression = "calcul[0-2]"
class expression =  ".*"

In simple cases, programs use objects as components and instantiate (or bind to), a set of well-known objects with simple and predictible creation order so that it is simple to know their names.

However, in more dynamic cases, when programs instantiate objects regarding a more complex algorithm, aspects may be in trouble to find out the name of a particular object and it might be difficult or impossible to write the right pointcut expression with a regular expression.

A first simple means to overcome this issue is to custom the naming of the created objects so that the naming conventions are relevant and you can refer to an object unambigously. This can be done by telling the naming aspect to force the name of the objects.

For instance, imagine a double loop that dynamically creates objets:

class MyContainer {
    Vector elements = new Vector();
    public addElement( MyElement element ) {
        elements.add( element );
    }
}
class MyElement {...}

[...]
for( int i=0; i<max1; i++ ) {
    MyContainer container = new MyContainer();
    for( int j=0; j<max2; j++ ) {
        container.addElement( new MyElement() );
    }
}

Now, imagine that you want to wrap each first element of the containers. You are in trouble to know its name since max1 and max2 are only known at runtime (and can take any value depending on the program). A way to solve the problem is to force the names of the elements:

[...]
for( int i=0; i<max1; i++ ) {
    MyContainer container = new MyContainer();
    for( int j=0; j<max2; j++ ) {
        NamingAC.forceName("element_"+i+"_"+j);
        container.addElement( new MyElement() );
    }
}

It then becomes easy to write an object pointcut-expression that matches only the first elements of the containers:

"element_[0-9]*_0"

However, this solution is not really satisfying for AOP since your base program now handles a naming issue. In some cases, this dependency can be avoided. JAC provides an original feature to do this called the Object-Paths (OPaths). With the OPath feature, you can access objects by traveling through the relations. This feature was widely inspired from XPaths.

An OPath is of the following form:

objectExpr/relationExpr/objectExpr/relationExpr/ ... /objectExpr

Each sub-expression of an OPath is separated from the next one using / is either an object regular expression, or a relation regular expression where:

objectExpr:
an expression that matches a set of objects in the path's context (the root of the path matches in all the objects of the system) - it can be an index (e.g. 0 matches the first object in the path's context)
relationExpr:
an expression that matches all the references and all the collections of the objects set matched by the parent path expression

For instance, if you want to match all the users that own an account within a bank called bank0, then you simply write:

bank0/accounts/.*/owners/.*

Finally, in our previous containers-elements sample, you can denote all the first elements of the containers so that configuring the names is not needed anymore:

mycontainer[0-9]*/elements/0

Method-Pointcut-Expressions Keywords

In method pointcut-expressions, you can use keywords to very easily denote sets of methods.

For instance, to match all the setters of a class, instead of having to write the following regular expression on the method pointcut-expression:

set.*([^,]+):void  // all the methods that start with "set"
                   // and take only one argument (no comma 
                   // in the parameter types)

You can simply write:

SETTERS

There are two reasons for using keywords in method pointcut-expressions. First it is simplier and you do not need to know regular expressions. Second, you do not need to rely on naming conventions anymore!! Indeed, you can ask, for instance, to retrieve all the methods that modify the object's state (even if the names do not follow any conventions). This is possible because of the RTTI metadata (see org.objectweb.jac.core.rtti). RTTI metadata are automatically set through BCEL bytecode analysis.

Here are the implemented keywords (some can take arguments):

Pointcut Operators

A pointcut expression can be composed of pointcut sub-expressions (expressions of the over-depicted forms) composed with the && (logical and), || (logical or), and the ! (NOT) operators. So, let's give some configuration example of the tracing aspect that uses pointcut expressions as an input for the "trace" configuration method:

// traces all the calls on all the objects of the system
trace ".*" ".*" ".*";
// traces all the calls on all the objects of the system (alt.)
trace "ALL" "ALL" "ALL";
// traces all the calls on all the methods of all the instances of
// classes A and B
trace ".*" "A || B" ".*";
// traces all the calls on all the methods of the first instances
// of classes A and B (their names ends with 0)
trace ".*0" "A || B" ".*";
// restricts the trace to the f field setters and the f field
// getters
trace ".*0" "A || B" "GETTERS(f) || SETTERS(f)";
// restricts the trace to all the methods minus the methods that
// modify the state of the instances
trace ".*0" "A || B" ".* && !MODIFIERS";
// traces all the instances of all classes except a0 and only the
// methods that read the state of the instances or the getName
// method
trace ".* && !a0" ".*" "ACCESSORS || getName():String";

Note: parenthesis are not yet available.

Hello world example

The hello world example of Aspect-Oriented Programming is often the trace example where the aspect prints on the screen the methods that are called in the base program. The following steps show how to program this simple aspect and how to configure and weave it to a simple application.

Step 1: programming or reusing the wrappers

This aspect is really simple and only needs one wrapper that can be found in the org.objectweb.jac.wrapper package (you can copy it and adapt it to the kind of trace you want to see):

import org.objectweb.jac.core.*;
import java.util.*;
public class VerboseWrapper extends Wrapper {
   public VerboseWrapper(AspectComponent ac) {
      super(ac);
   }
   public Object printCallingInfos(Interaction i) {
      System.out.println(
         "<<< Calling '"+i.method+" on '"+i.wrappee.toString()+
         "' with "+Arrays.asList(i.args)+" >>>"
      );
      Object ret = proceed(i);
      System.out.println(
         "<<< Returning from '"+i.method+" on '"+i.wrappee.toString()+
         "' with "+Arrays.asList(args)+" >>>"
      );
      return ret;
   }
}

Step 2: programming an aspect component

To weave the trace aspect, you must define an aspect component that wraps all the methods of all the base objects that need to be verbose. To specify the objects that need to be verbose, the aspect programmer should add some configuration capability of the aspect component. Here the configuration of the AC can be done with the addVerboseClass that allows the user to specify that all the instances of the classes that are added to the verboseClasses list are verbose.

Simple version

import org.objectweb.jac.core.*;
class TracingAC extends AspectComponent {
   VerboseWrapper wrapper = new VerboseWrapper(this);
   Vector verboseClasses = new Vector();
   public void addVerboseClass(String className) {
      verboseClasses.add(className);
   }  
   public void whenUsingNewInstance(Interaction i) {
      if(isVerboseClass(wrappee().getClass())) {
         Wrapping.wrapAll(i.wrappee, wrapper, "printCallingInfos");
      }
   }
   protected boolean isVerboseClass(String className) {
      Iterator it = verboseClasses.iterator();
      while(it.hasNext()) {
         if(it.next().equals(className)) return true;
      }
      return false;
   }
}

An alternative to the redefinition of the whenUsingNewInstance method is to use the pointcut feature provided by the aspect components (see org.objectweb.jac.core.MethodPointcut). A pointcut can be defined by any aspect component at construction-time or at configuration-time and allows the aspect programmer to easily denote a set of base objects to wrap and how they are wrapped. Pointcuts can be added with the AspectComponent.pointcut(...) methods.

The following code is extracted from the tracing aspect that is furnished with the JAC distribution. It uses the pointcut feature to define a configuration method that allows the user to define which method must be traced in a very concise and precise way.

Pointcut-based version

import org.objectweb.jac.core.*;
public class TracingAC extends AspectComponent {
   public void addTrace(String wrappeeExpr, 
                        String wrappeeClassExpr, 
                        String wrappeeMethodExpr) {
      pointcut(wrappeeExpr,wrappeeClassExpr,wrappeeMethodExpr,
               VerboseWrapper.class.getName(),"printCallingInfos",
               null,false);
   }

The expressions given are pointcut expressions.

Step 3: register the aspect component so that its name is "tracing" (to do this, edit the org.objectweb.jac.prop file of the root directory of the distribution).

Step 4: program the base application.

This aspect can be applied to an application you have already programmed. For instance, if the application is composed of one class.

package hello;
public class Hello {
   public void printHello() {
      System.out.println("Hello!");
   }
}

Step 5: configure the aspect(s)

The configuration of the aspect can be handled within the main method by calling the configuration methods of the aspect components (here addVerboseClass) but the programmer must then re-compile this class each time a configuration changes. Thus, the best way to configure an aspect is to specify a configuration file for each aspect. Each line of the configuration file represents a invocation on a configuration method of the aspect. Here is the configuration file for the tracing aspect of the hello application (tracing.acc).

// each line is:
// methodName parameters (where each parameter is double-quoted and
// arrays are of the form : { item1, item2, ... itemN } 

addVerboseClass hello.Hello;

If you use the pointcut-based version, then the configuration file can look like:

addTrace ".*" "myClass1" ".*(float,float).*";
addTrace "anObjectName" ".*" "set.*([^,]+):void";
addTrace "anObjectName" ".*" "MODIFIERS && !set.*([^,]+):void";

This configuration is more precise thanks to the pointcut expressions. The first line tells that all the methods within any instance of the class named "myClass" and that take 2 floats must be traced. The second line tells that all the methods that begin with the "set" prefix, that have one and only one argument and that return nothing must be traced (this expression matches all the setters of a single object named anObjectName by the naming aspect. The third line uses a keyword to denote a set of methods, here the full expression means "all the methods that modify the object's state excluding all the setters.

Then, you can specify this file name to configure the tracing aspect (declared in your application descriptor).

Step 6: program the launcher and the application descriptor file

Then you must write a launching class and an application descriptor that registers the application within the system and creates the supported aspects configurations (here the tracing aspect) -- see section Programming a JAC application's launcher/descriptor.

package hello;
public class Run {
   public static void main(String[] args) {
      Hello hello = new Hello();
      hello.printHello();
  }
}
// hello.jac file
applicationName: hello
launchingClass: hello.Run
aspects: \
  tracing hello/tracing.acc true

Then run the program.

java -jar org.objectweb.jac.jar -C %helloClassPath% hello.jac

You should see the trace printing around the hello method call.