Programming with JAC

Programming guidelines

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 system.

Thus, it 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 so that they behave properly for the final program.

Each existing aspect within the distribution corresponds to an aspect component (see the JAC architecture overview for more details). Each aspect component is documented so that any final programmer must be 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 "jac.aspects.persistence.PostgresStorage" \
	         { "photo", "jac" "" };
configureClass "Photo" persistent;
configureClass "PhotoRepository" root;
configureClass "Person" root;
wrapCollection "PhotoRepository" photos;
registerStatic "photorepository0";

Note that all the called methods (configureStorage, wrapCollection, and registerStatic) 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 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 Run class that implements a main method. 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 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 jac.prop file in your JAC distribution root directory.

To run the application then use the jac command that must have been set into you path by scripts/init_env.

jac -W -G myAppPath/myApp.jac arg1, ...

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

JAC is developed under Linux but will work under all the other platforms that support Java 2. If you run into some installation problems, please, do not hesitate to ask for some help to Renaud Pawlak or Lionel Seinturier

Programming new aspects

Overview on the programming model

You must keep in mind that JAC is not a language but a framework (see the architecture overview). Thus, most of the programming is done by extending the classes or implementing the interfaces that are provided by the 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 jac.core.Wrapper (that implements the jac.core.CollaborationParticipant interface).

public class MyWrapper extends Wrapper {
   public Object myWrappingMethod () {...};
}

Within the runtime context of a wrapping method, 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();
// 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 "+method()+" with "+args()+" >>");
ret = proceed();
System.out.println("<< "+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();
} 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)arg(0)).intValue() >= 0 &&
    ((Integer)arg(0)).intValue() <= 100) {
  ret = proceed();
} 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)arg(0)).intValue() < 0) args()[0] = 0;
if (((Integer)arg(0)).intValue() > 100) args()[0] = 100;
return proceed();

Example 5:

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

Object ret = null;
ret = proceed();
Database d = new Database();
d.connect();
d.write( wrappee(), Utils.getObjectState( 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();
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();
if (((Integer)wrappee().getFieldValue("aField")).intValue() < 0
    || ((Integer)wrappee().getFieldValue("aField")).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++) {
  replicas[i].invoke(method(), args());
}
return proceed();

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 nextWrapper method so that the wrappee is never called.

return remoteRef.invoke ( method(), 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(wrappee(), args())) {
  ret = getCachedValue(wrappee(), args());
} else {
  ret = proceed();
  setCachedValue(wrappee(), args(), ret);
}
return ret;

Programming a role method

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

public ret_type myRoleMethod ( user_defined_args... );

The goal of the role method is to extend the wrappee functionalities. A role method can be invoked on a wrappee (interface core.Wrappee seamlessly supported by any wrappable object) as follows:

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

Within the runtime context of a role method, the programmer can access several informations about the current method call as defined in jac.core.CollaborationParticipant.

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 ( AnException e );

Within its runtime context, wrappee() is a reference to the object that is wrapped. The method() and 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 {
   int nRetry = 0;
   public Object handler( RetOutOfBoundsException e ) {

      Object ret = null;
      // retry to call the method once
      nRetry ++;
      if (nRetry < 1) {
         ret = wrappee().callMethod ( method(), args() );
      } else {
         System.out.println ("We retried once but there is still a " + e);
	 throw new InconsistencyError( wrappee(), method(), 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 jac.core.rtti package instead of the java.lang.reflect one.

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

Moreover, JAC allows the programmer to access the running collaboration. A running context is composed of the interactions stack (one per thread) and a set of 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 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" );
// "a" is equal to "aValue1" for all the following calls
attr ( "a" );
// for all execution flows that goes through this, "a" will be equal
// "aValue2" it will still equals "aValue1" for the
// rest of the program.
attrdef ( "a", "aValue2" );

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 but that 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:

  • you can pass a wrapper instance at the pointcut construction
  • you can let the pointcut deal with the wrapper contruction and then you just pass the wrapper class name; in this case, you must specify the "one2one" flag:

    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 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 Object verboseCall() {
            System.out.println(method()+" is called");
            Object ret = proceed();
            System.out.println(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();
        public void whenUsingNewInstance() {
           wrappee().wrapAll( wrapper, "verboseCall" );
        }
    }
    

    This is nice but very low-level since you have to overload the whenUsingNewInstance method and you have to know the Wrappee interface 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();
        public void whenUsingNewInstance() {
            String name = NameRepository.get().getName(wrappee());
            if( name.equals( "o1" ) ) {
                wrappee().wrap( 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);
        }
    }
    
    public class Tracing_2_AC extends AspectComponent {
        Tracing_2_AC() {
            pointcut("o1",".*","m1.*",TracingWrapper.class.getName(),
                     "verboseCall",false);
        }
    }
    

    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). The point is that you must learn regular expressions to use pointcuts.

    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 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  = "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, you use objects as components and you 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 your program instantiates objects regarding a more complex algorithm, you 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). So 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, either an 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 be a "god" in 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 aspect (see jac.core.rtti). In the future, some of the RTTI configurations will be automatically performed through bytecode analysis, which will make JAC easier to use.

    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
    // getter
    trace ".*0" "A || B" "GETTER(f) || SETTER(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 in this release (<= 5.1). Do not try it.

    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 jac.wrapper package (you can copy it and adapt it to the kind of trace you want to see):

    import jac.core.*;
    import java.util.*;
    public class VerboseWrapper {
       public Object printCallingInfos() {
          System.out.println(
             "<<< Calling '" + method() + " on '" + wrappee().toString() +
             "' with " + Arrays.asList(args()) + " >>>"
          );
          Object ret = proceed();
          System.out.println(
             "<<< Returning from '" + method() + " on '" + 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 jac.core.*;
    class TracingAC extends AspectComponent {
       VerboseWrapper wrapper = new VerboseWrapper();
       Vector verboseClasses = new Vector();
       public void addVerboseClass( String className ) {
          verboseClasses.add( className );
       }  
       public void whenUsingNewInstance() {
          if( isVerboseClass( wrappee().getClass() ) ) {
             wrappee().wrapAll( 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 jac.core.Pointcut). 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 jac.core.*;
    public class TracingAC extends AspectComponent {
       public void addTrace( String wrappeeExpr, 
                             String wrappeeClassExpr, 
                             String wrappeeMethodExpr ) {
          pointcut( wrappeeExpr, wrappeeClassExpr, wrappeeMethodExpr,
                    VerboseWrapper.class.getName(), "printCallingInfos", false );
       }
    

    The expressions given are pointcut expressions that are based on POSIX regular expressions (see gnu regexp) enhanced with some operators such as &&, ||, and !, and some keywords to denote methods sets such as MODIFIERS, ACCESSORS, GETTER(field), or SETTER(field).

    Note: for the programmers that are familiar with AspectJ, they can notice that the pointcut semantics are quite different from the pointcut semantics of AspectJ. In JAC, a pointcut is not only the definition of a set of base-program points, but also the definition of how they are modified by the aspect (the wrapper and the wrapping method are specified).

    Step 3: register the aspect component so that its name is "tracing" (to do this, edit the 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;
    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 7: 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;
    class Run {
       public static void main( String[] args ) {
          Hello hello = new Hello()();
          hello.printHello();
      }
    }
    
    // hello.jac file
    applicationName: hello
    launchingClass: hello.Run
    aspects: \
      tracing jac/samples/calcul/tracing.acc true
    

    Then run the program.

    jac hello.jac
    

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

    The -G option allows you to access the administration GUI and to weave/unweave the tracing aspect during the program execution.