Seasar DI Container with AOP

An Overview of S2AOP

The main idea of AOP


Advice(MethodInterceptor)

Advice is a block of codes which will be inserted into another program. This could be called Interceptor.

Joinpoint(MethodInvocation)

Joinpoint is a point which Advice will be associated with a targeted class. Advice is allowed to get information about a method name and arguments from Joinpoint.

Pointcut

Pointcut is used to define the point where Jopinpoint is set in.

Aspect

Aspect associates Advice with Pointcut.

InterType

InterType modifies class internal structures, including adding fields, methodds and interfaces.

AOP advantates

  • AOP offers you a better maintenance by separating a "Core Concern" and a "Crosscutting Concern".
  • Your source code will be simple by detaching a systematic feature from a business logic. The detached systematic feature becomes a "Core Concern" in a "Croscutting Concern". As a result, your source code will be responsible only for your purpose.
  • You don't need complicated procedures such as an automatic transaction management or a remote method invocation made by EJB anymore. You can do it by POJO(Plain Old Java Object: POJO is an object indepented from a specific Java API.)

S2AOP advantages

  • A configuration becomes simplified.
  • Required interface implementations will be reduced to only one.
  • An aspect applied to the component can be cleared.
  • S2AOP enables you to start AOP programmings immediately since common implementations of object patterns for Aspect are provided. (You are allowd to implement your own interfaces and abstract classe.)

What you have to care about S2AOP

  • You can't adopt an aspect to a final class.
  • You can't adopt an aspect to a final method, a static method and a non-public method.
  • If you don't set a pointcut attribute, some methods will not accept an aspect. In order to make every method to accept an aspect, you must set ".*" to a pointcut attribute.

Brief Notes of S2AOP

The file you need

You need a configuration file (known as "dicon file") for S2Container to use S2AOP. However, you can place a configuration file in any directory, a configuration file is commonly created in the same directory(a.k.a. folder) as the "Crosscutting Concern" or the targeted component resides.

Setup file Descriptions

Aspect Tag (mandatory for AOP)

An aspect tag is used to associate an aspect with a component. Write the Interceptor configuration in the body by using the OGNL expression or in the nested tag accompanied by the component tag.
* Please refer below for OGNL OGNL Language Guide
What will happen if you associate multiple aspects with one component ? Aspects will be invoked according to the order which they are defined in a configuration file. If you want to know more, please read Original Interceptor for futher information

Pay attention

The component specified by the aspect tag will be get through the container while its initialization processes are going on. This indicates that a new instance doesn't create everytime when an Interceptor method is invoked under the condition that an instance attribute of a component is assigned as a prototype.

pointcut attribute(optional)

You can specify multiple method names delimited by commmas. If you don't set any pointcut, all implemented methods in a component will be invoked. You can use the regular expression(JDK1.4 regex) to specify method names.

Configuration Example

Suppose you want to set the aspect in both getTime() method and hasCode() method of java.util.Date class by means of a pointcut attribute, then the configuration will be as follows.

<component class="java.util.Date">
    <aspect pointcut="getTime,hashCode">
        <component class="org.seasar.framework.aop.interceptors.TraceInterceptor"/>
    </aspect>
</component>
The example shown below describes how to use regular expression to set an aspect.
<component class="java.util.Date">
    <aspect pointcut=".*">
        <component class="org.seasar.framework.aop.interceptors.TraceInterceptor"/>
    </aspect>
</component>

interType Tag

Specifies interType which is added into components. Either OGNL expressions in the body or the child component tag is used for the value passed as an interType.
* Please refer below for OGNL Language Guide

Configuration Example
<component class="java.util.Date">
    <interType>
        <component class="org.seasar.framework.aop.intertype.PropertyInterType"/>
    </interType>
</component>

Available S2AOP Interceptors

Following Interceptors are provided by S2AOP. Besides, you can define your own Interceptor easily.

(1) TraceInterceptor

Class Name

org.seasar.framework.aop.interceptors.TraceInterceptor

Description

This Interceptor handles a trace as a "Crosscutting Concern". Dicon file shown below is an example that TraceInterceptor is applied to getTime() method of java.uitl.Date class.

<component class="java.util.Date">
    <aspect pointcut="getTime">
        <component class="org.seasar.framework.aop.interceptors.TraceInterceptor"/>
    </aspect>
</component>

Please read TraceInterceptor for further information.

(2) ThrowsInterceptor

Class Name

org.seasar.framework.aop.interceptors.ThrowsInterceptor

Description

This Interceptor handles an exception as a "Crosscutting Concern". Implement the method "Object handleThrowable(Throwable, MethodInvocation)" in your class inherited from ThrowsInterceptor before you use this. You can assign a subclass of java.lang.Throwable to the first parameter of this method. If you define the method "Object handleThrowable(IOException, MethodInvocation)", the exception will be caught by the object which type is IOException or the subclass of IOException when the excpetion raised in the component associated with ThrowsInterceptor. You are allowed to define multiple handleThrowable() methods. Please read ThrowsInterceptor for further information.

(3) ToStringInterceptor

Class Name

org.seasar.framework.aop.interceptors.ToStringInterceptor

Description

ToStringInterceptor treats the string processing by the toString() method as "Crosscutting Concern".

<component class="java.util.Date">
    <aspect pointcut="toString">
        <component class="org.seasar.framework.aop.interceptors.ToStringInterceptor"/>
    </aspect>
</component>

Please refer to the ToStringInterceptor sample in details.

(4) MockInterceptor

Class Name

org.seasar.framework.aop.interceptors.MockInterceptor

Description

This Interceptor let a test made by Mock easier. Please read Testing for further information.

(5) DelegateInterceptor

Class Name

org.seasar.framework.aop.interceptors.DelegateInterceptor

Description

This Interceptor delegates a method invocation to another component. A delegation is performed when a component is assigned to a target property of DelegateInterceptor. If you want to specify a method name explicitly to support different method names, use "DelegateInterceptor#addMethodNameMap(String methodName, String targetMethodName)". Suppose you want to let the method bar() be delegated to foo.bar2(), then your code might be like this: "DelegateInterceptor#setTarget(foo), DelegateInterceptor#addMethodNameMap("bar", "bar2")". Please read DelegateInterceptor for further information.

Pay attention

A component assigned to a target property will be get through the container while its initialization processes are going on. As a result, the same instance will be used even if the instance attribute of the component is a property. If you want to get a new instance for every method invocation, use PrototypeDelegateInterceptor described below.

(6) PrototypeDelegateInterceptor

Class Name

org.seasar.framework.aop.interceptors.PrototypeDelegateInterceptor

Description

This Interceptor delegates a method invocation to another component. A new component will be get from a container in every method invocation. The delegation is performed when the targeted component name is assigned to the targetName property of PrototypeDelegateInterceptor. If you want to set a method name explicitly to support different method names, use "PrototypeDelegateInterceptor#setTarget(foo), PrototypeDelegateInterceptor#addMethodNameMap("bar", "bar2")". Please read PrototypeDelegateInterceptor for further information.

(7) SyncInterceptor

Class Name

org.seasar.framework.aop.interceptors.SyncInterceptor

Description

This Interceptor synchronizes a multiple method invocations by an aspect. You don't need to modify your code when you want to sychronize method invocations. Please read SyncInterceptor for further information.

(8) InterceptorChain

Class Name

org.seasar.framework.aop.interceptors.InterceptorChain

Description

This Interceptor enables you to combine multiple Interceptors and to reuse them easily. If you want to adopt a combination to more than one component, give the name of InterceptorChain into each component as follows.

<component name="interceptor1" .../>
<component name="interceptor2" .../>
<component name="interceptor3" .../>
<component name="chain" class="org.seasar.framework.aop.interceptors.InterceptorChain">
<initMethod name="add"><arg>interceptor1</arg></initMethod>
<initMethod name="add"><arg>interceptor2</arg></initMethod>
<initMethod name="add"><arg>interceptor3</arg></initMethod>
</component> <component ...> <aspect>chain</aspect> </component> <component ...> <aspect>chain</aspect> </component>

(9) InterceptorLifecycleAdapter

Class Name

org.seasar.framework.aop.interceptors.InterceptorLifecycleAdapter

Description

Interceptor instances are built into classes only when initializeing container. This is why new interceptors are not built into classes even if instance property is prototype. In this case, applies InterceptorLifecycleAdapter to MethodInterceptor#invoke().

<component name="myInterceptor" instance="prototype" .../>
    <aspect pointcut="invoke">
        <component class="org.seasar.framework.aop.interceptors.InterceptorLifecycleAdapter">
    </aspect>
</component>
<component ...>
    <aspect>myInterceptor</aspect>
</component>

How to define your own Interceptor

Description

To define your own Interceptor, you need to implement an interface or an abstract class shown below.

org.aopalliance.intercept.MethodInterceptor
org.seasar.framework.aop.interceptors.AbstractInterceptor

The invoke() method shown below is the only one that you have to implement in both cases.

public Object invoke(MethodInvocation invocation) throws Throwable

AbstractInterceptor is an abstract class and implements MethodInterceptor. AbstracInterceptor has two methods, createProxy() and getTargetClass() shown below. createProxy() method is used to get a Proxy object. getTargetClass() method is used to get a target class which an aspect is associated. When Interceptor requires a name of a class that an aspect is associated with, for example, logging Interceptor, you can get a required class name easily from AbstractInterceptor.

public Object createProxy(Class proxyClass)
protected Class getTargetClass(MethodInvocation invocation)

You can get a target object, method and arguments from MethodInvocation object given as a parameter of getTargetClass() method. getThis() method has a feature to get a class name from a bytecode. proceed() method invokes a concrete method and gets the result through this invocation. Following is an example how to use proceed() method.

Warning

Interceptor instances are built into classes only when initializeing container. This is why new interceptors are not built into classes even if instance property is prototype.
Applies InterceptorLifecycleAdapter to such interceptor.

Example

public class ABS implements MethodInterceptor {
    public Object invoke(MethodInvocation invocation) throws Throwable {
        System.out.println("Before");
        Object ret = invocation/proceed();
        System.out.println("After");
        return ret;
    }
}

This example consists of two parts, before and after the proceed() method. The first part is ensured to be executed before proceed() method and the second part is ensured to be done after proceed() method. Suppose multiple aspects associated with only one component, then aspects will be invoked as follows:

  1. An execution goes down along the order of aspect elements appeared in dicon file. At this stage, codes reside in former part of proceed() method of MethodInterceptor are executed.
  2. After the last execution has done, methods in a component are invoked next.
  3. An execution goes up along the reverse order of aspect elements appered in dicon file. At this state, codes reside in latter part of proceed() method of MethodInterceptor are executed.

Please read How to define your own Interceptor for further information.

Available S2AOP InterTypes

Following Intertypes are provided by S2AOP. Besides, you can define your own InterType easily.

(1) PropertyTraceInterType

Class Name

org.seasar.framework.aop.intertype.PropertyInterType

Description

PropertyInterType creates the setter and getter method for the field.

Warning

PropertyInterType doesn't create the getter and setter method for the private field. We need to define field scope as either protected or package.

<component class="Hoge">
    <interType>
        <component class="org.seasar.framework.aop.interType.PropertyInterType"/>
    </interType>
</component>

We can specify the Property annotation for the fields where we want to create the getter and setter method.
Tiger annotation is as follows.

import org.seasar.framework.container.annotation.tiger.Property;
import org.seasar.framework.container.annotation.tiger.PropertyType;

public class Hoge {
    @Property
    int foo;

    @Property(PropertyType.READ)
    long bar;

    @Property(PropertyType.WRITE)
    String baz;
}

Backport175 annotation is as follows.

public class Hoge {
    /** @org.seasar.framework.container.annotation.backport175.Property */
    int foo;

    /** @org.seasar.framework.container.annotation.backport175.Property("read") */
    long bar;

    /** @org.seasar.framework.container.annotation.backport175.Property("write") */
    String baz;
}

In the above sample, We create the getter and setter method in foo field, and create the getter method in bar field, and create the setter method in baz field.

We can sepecify the Property annotation for a class. The annotation for a class is adapted for the fields which we don't specify the annotation.
Tiger annotation is as follows.

import org.seasar.framework.container.annotation.tiger.Property;
import org.seasar.framework.container.annotation.tiger.PropertyType;

@Property
public class Hoge {
    int foo;

    long bar;

    @Property(PropertyType.NONE)
    String baz;
}

Backport175 annotation is as follows.

/** @org.seasar.framework.container.annotation.backport175.Property */
public class Hoge {
    int foo;

    long bar;

    /** @org.seasar.framework.container.annotation.backport175.Property("none") */
    String baz;
}

In the above sample, we create the getter and setter method in the foo and bar field, and don't create any method in the baz field.

(2) InterTypeChain

Class Name

org.seasar.framework.aop.intertye.InterTypeChain

Description

InterType enables you to combine multiple InterTypes and to reuse them easily.

<component name="interType1" .../>
<component name="interType2" .../>
<component name="interType3" .../>
<component name="chain" class="org.seasar.framework.aop.interttype.InterTypeChain">
<initMethod name="add"><arg>interType1</arg></initMethod>
<initMethod name="add"><arg>interType2</arg></initMethod>
<initMethod name="add"><arg>interType3</arg></initMethod>
</component> <component ...> <interType>chain</interType> </component> <component ...> <interType>chain</interType> </component>

How to define your own InterType

Description

To define your own InterType, you need to implement an interface or an abstract class shown below.

org.seasar.framework.aop.InterType
org.seasar.framework.aop.intertype.AbstractInterType

The class implementing InterType need to implement the folloing method.

public void introduce(Class targetClass, CtClass enhancedClass)

InterType is applied to targetClass and is built into enhancedClass. Please refer to Javassist, Tutorial, JavaDoc for details.

AbstractInterType is an abstract class implementing InterType. The sub-class of InterType need to implement folloing methods.

public void introduce() trhows CannotCompileException, NotFoundException

AbstractInterType acquires objects using folloing methods.

protected Class getTargetClass()
protected CtClass getEnhancedClass()
protected ClassPool getClassPool()

AbstractInterType supplies following utility methods for adding fields, methods, and interfaces.

protected void addField(Class type, String name)
protected void addField(Class type, String name, String init)
protected void addStaticField(Class type, String name)
protected void addStaticField(Class type, String name, String init)
protected void addMethod(String name, String src)
protected void addMethod(Class returnType, String name, String src)
protected void addMethod(String name, Class[] paramTypes, String src)
protected void addMethod(Class returnType, String name, Class[] paramTypes, String src)
protected void addStaticMethod(String name, String src)
protected void addStaticMethod(Class returnType, String name, String src)
protected void addStaticMethod(String name, Class[] paramTypes, String src)
protected void addStaticMethod(Class returnType, String name, Class[] paramTypes, String src)

AOP without "dicon file"

This section describes how to put aspects into your program without dicon file. Four solutions shown below are provided.

  • Assign one or more method names to arguments of org.seasar.framework.aop.impl.PointcutImpl constructor. Suppose a class implements some interfaces, all methods in an interface can be associated with an aspect. For example, when you emply java.util.HashMap and write your code like new PointcutImpl(HashMap.class), all methods in java.util.Map interface can be associated with an aspect automatically.
  • Set Interceptor to the first argument and Pointcut to the second argument of org.seasar.framework.aop.impl.AspectImpl constructor. You need to create this Pointcut from PointcutImpl.
  • Set the target class and an array of Aspect to arguments of org.seasar.framework.aop.proxy.AopProxy constructor. You need to create this Aspect from AspectImple.
  • You can get an object which is associated with Aspect from a method, org.seasar.framework.aop.proxy.AopProxy#create()

This is an example how to insert an aspect without dicon file. This example adopts TraceInterceptor to java.uitl.Date, and let getTime() method be a target method.

Pointcut pointcut = new PointcutImpl(new String[]{"getTime"});
Aspect aspect = new AspectImpl(new TraceInterceptor(), pointcut);
AopProxy aopProxy = new AopProxy(Date.class, new Aspect[]{aspect});
Date proxy = (Date) aopProxy.create();
proxy.getTime();

Example

You have to finish a setup before running these samples.

TraceInterceptor

First example uses TraceInterceptor. This shows you how to output a trace when getTime() and hasCode() methods in both java.util.Array and java.util.Date classes are invoked. Write two files explained below.

  • Dicon file to define a component(Trace.icon)
  • Java class to check whether the configuration is defined correcltly or not(AopTraceClient.java)

How to write dicon file

  • Define a component element for TraceInterceptor. In this case, name attribute is "traceInterceptor".
  • Define a component element for java.util.ArrayList class. Add an aspect element as a nested child element of component and set the name "traceInterceptor" in its body.
  • Define a component element for java.util.Date class. Likewise, add an aspect element and set "traceInterceptor" to its body. This time, set both getTime() and hashCode() methods to a pointcut attribute of an aspect element. In addition, define an arg element to pass a value of zero to java.util.Date constructor.

Trace.dicon

<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE components PUBLIC "-//SEASAR//DTD S2Container//EN"
"http://www.seasar.org/dtd/components.dtd">
<components>
    <component name="traceInterceptor"
               class="org.seasar.framework.aop.interceptors.TraceInterceptor"/>
    <component class="java.util.ArrayList>
        <aspect>traceInterceptor</aspect>
    </component>
    <component class="java.util.Date">
        <arg>0</arg>
        <aspect pointcut="getTime, hashCode">
            traceInterceptor
        </aspect>
    </component>
</components>

How to write a cline Java program

  • Create a S2Container object by the method, org.seasar.framework.container.S2ContainerFactory#create(). Set a path to dicon file(Trace.dicon) to an argument of create() method.
  • Get an object by the method, org.seasar.framework.container.S2Container#getComponent(). Set a component name to an argument of getComponent() method. A component name must be the name defined in dicon file. In this case, assign List.class and java.util.Date to the arguments of each getComponent() method.
  • Inovke one of methods ArrayList component has. This example choose size() method. Check that the trace captured by an aspect while the method is executing.
  • Equally, invoke getTime() and hasCode() methods of Date component and check the trace.

AopTraceClient.java

package examples.aop.traceinterceptor;

import java.util.Date;
import java.util.List;

import org.seasar.framework.container.S2Container;
import org.seasar.framework.container.factory.S2ContainerFactory;

public class AopTraceClient {
    private static String PATH = "examples/aop/traceinterceptor/Trace.dicon";

    public static void main(String[] args) {
        S2Container container = S2ContainerFactory.create(PATH);
        ArrayList list = (List) container.getComponent(ArrayList.class);
        list.size();
        Date date = (Date) container.getComponent(Date.class);
        date.getTime();
        date.hashCode();
    }
}

Output

You see an output shown below. This means that the traces are outputed both before and after the method invocation. As for Date component, you know java.util.Date#getTime() method is traced between BEGIN and END of hasCode() method, too. The reason is that getTime() method is used in hashCode() method.

BEGIN java.util.ArrayList#size()
END java.util.ArrayList#size() : 0
BEGIN java.util.Date#getTime()
END java.util.Date#getTime() : 0
BEGIN java.util.Date#hashCode()
BEGIN java.util.Date#getTime()
END java.util.Date#getTime() : 0
END java.util.Date#hashCode() : 0

This example is included in the directory, seasar2/src/examples/aop/traceinterceptor.


ThrowsInterceptor

(1)The second example uses ThrowsInterceptor. This shows you how to go processing through to the end even if an exception occurs. Write four files explained below.

  • A class that raises RuntimeException(Checker.java)
  • Interceptor inherited from ThrowsInterceptor(HandleThrowableInterceptor.java)
  • Dicon file to define a component(Checker.dicon)
  • Java class to check whether the configuration is made correctly or not(AopCheckerClient.java)

How to define a class that throws exception

  • This class outputs a string given as an argument when a string is not null.
  • This class throws NullPointerException when null is set to an argument of check() method.

Checker.java

package examples.aop.throwsinterceptor;

public class Checker {
    public void check(String str) {
        if (str != null) {
            System.out.println(str);
        } else {
            throw new NullPointerException("null");
        }
    }
}

How to define Interceptor inherited from ThrowsInterceptor

  • Define a subclass of ThrowsInterceptor.
  • Implement handleThrowable(Throwable, MethodInvocation) method.

HandleThrowableInterceptor.java

package examples.aop.throwsinterceptor;

import org.aopalliance.intercept.MethodInvocation;
import org.seasar.framework.aop.interceptors.ThrowsInterceptor;

public class HandleThrowableInterceptor extends ThrowsInterceptor {
    public void handleThrowable(Throwable t, MethodInvocation invocation)
        throws Throwable {
    }
}

How to write dicon file

  • Write a component element for Interceptor you defined before and set handleThrowableInterceptor to the name attribute
  • Associate HandleThrowableInterceptor class with check() method in Checker class so that an aspect can work.

Checker.dicon

<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE components PUBLIC "-//SEASAR//DTD S2Container//EN"
"http://www.seasar.org/dtd/components.dtd">
<components>
    <component name="handleThrowableInterceptor"
               class="examples.aop.throwsinterceptor.HandleThrowableInterceptor"/>
    <component class="examples.aop.throwsinterceptor.Checker">
        <aspect pointcut="check">
            handleThrowableInterceptor
        </aspect>
    </component>
</components>

How to write a client Java class

  • Create a S2Container object by the method, org.seasar.framework.container.S2ContainerFactory#create(). Set a path to dicon file(Checker.dicon) to an argument of create() method.
  • Get an object by the method, org.seasar.framework.container.S2Container#getComponent(). Set a component name to an argument of getComponent() method. A component name must be the name defined in dicon file. In this case, assign Foo.class.
  • Pass the string "foo" to Checker#check() method as an argument
  • Raise an exception by passing null to an argument of Checker#check() method.
  • Pass the second string "hoge" to Checker#check() method as an argument.

AopCheckerClient.java

package examples.aop.throwsinterceptor;

import org.seasar.framework.container.S2Container;
import org.seasar.framework.container.factory.S2ContainerFactory;

public class AopCheckerClient {
    private static String PATH = "examples/aop/throwsinterceptor/Checker.dicon";

    public static void main(String[] args) {
        S2Container container = S2ContainerFactory.create(PATH);
        Checker checker = (Checker) container.getComponent(Checker.class);
        checker.check("foo");
        checker.check(null);
        checker.check("hoge");
    }
}

Output

You see the string "hoge", so you know that the raised exception didn't stop processing.

foo
hoge

This example is included in seasar2/src/examples/aop/throwsinterceptor


(2) This ThrowsInterceptor example shows you how to replace the type of exeception with another Interceptor. Modify HandleThrowableInterceptor defined above.

How to make Interceptor to throw a new exception

Suppose org.seasar.framework.exception.SRuntimeException will be thrown when the first argument of handleThrowable(Throwable, MethodInvocation) method is NullPointerException, then you need to modify HandleThrowableInterceptor as follows.

package examples.aop.throwsinterceptor;

import org.aopalliance.intercept.MethodInvocation;
import org.seasar.framework.aop.interceptors.ThrowsInterceptor;
import org.seasar.framework.exception.SRuntimeException;

public class HandleThrowableInterceptor extends ThrowsInterceptor {
    public void handleThrowable(NullPointerException t, MethodInvocation invocation)
            throws Throwable {
        throw new SRuntimeException("ESSR0007", new Object[] { "argument" });
    }
}

You use other three files, Checker.java, Checker.dicon and AopCheckerClient.java, to run this example.

Output

You see new error messages shown below.

foo
Exception in thread "main" org.seasar.framework.exception.SRuntimeException:
  [ESSR0007]argument should not be null or empty
        at examples.aop.throwsinterceptor.HandleThrowableInterceptor.handleThrowable(
            HandleThrowableInterceptor.java:12)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(
            DelegatingMethodAccessorImpl.java:25)
        at java.lang.reflect.Method.invoke(Method.java:585)
        at org.seasar.framework.aop.interceptors.ThrowsInterceptor.invoke(
            ThrowsInterceptor.java:54)
        at examples.aop.throwsinterceptor.Checker$$EnhancedByS2AOP$$16672d6$$
            MethodInvocation$$check0.proceed(MethodInvocationClassGenerator.java)
        at examples.aop.throwsinterceptor.Checker$$EnhancedByS2AOP$$16672d6.check(
             Checker$$EnhancedByS2AOP$$16672d6.java)
        at examples.aop.throwsinterceptor.AopCheckerClient.main(AopCheckerClient.java:13)

This example is included in seasar2/src/examples/aop/throwsinterceptor

ToStringInterceptor

We can automatically output the fields information using ToStringInterceptor. The written files are as follows.

  • Entity classes which have fields(Entity.java, EntityImpl.java, EntityImplImpl.java, InnerEntity.java)
  • dicon file where we define components(ToString.dicon)
  • Main program to confirm the settering(AopToStringClient.java)

Creation entity classes

Entity.java

package examples.aop.tostringinterceptor;

import java.util.HashMap;
import java.util.Map;

public class Entity {
    private int intValue = 1;
    private static int staticIntValue = 12;
    private double doubleValue = 12.3;
    private String stringValue = "1234";
    public static final int staticFinalIntValue = 12345;
    private Map mapValue = new HashMap();
    private InnerEntity innerEntity;

    public Entity(InnerEntity innerEntity) {
        this.innerEntity = innerEntity;
    }
}

EntityImpl.java

package examples.aop.tostringinterceptor;

public class EntityImpl extends Entity {
    private int implValue = 1;

    public EntityImpl(InnerEntity innerEntity) {
        super(innerEntity);
    }
}

EntityImplImpl.java

package examples.aop.tostringinterceptor;

public class EntityImplImpl extends EntityImpl {
    private int implImplValue = 1;

    public EntityImplImpl(InnerEntity innerEntity) {
        super(innerEntity);
    }
}

InnerEntity.java

package examples.aop.tostringinterceptor;

public class InnerEntity {
    int innerInt = 0;
    long innerLong = 234;
    String str = "foo";
    char[] charArray = new char[]{'a', 'b', 'c'};
}

How to write dicon file

  • We define the ToStringInterceptor class as a component, and the component name is toStringInterceptor.
  • We define the EntityImplImpl class as a component. We set the innerEntity component to the constructor argument using the arg tag. We specify the toStringInterceptor component for the arg tag, and specify the toString method for the pointcut property.
  • We define the InnerEntity class as a component. We specify the toStringInterceptor component to the aspect tag, and specify the toString method for the pointcut property.

ToString.dicon

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE components PUBLIC "-//SEASAR//DTD S2Container//EN"
"http://www.seasar.org/dtd/components.dtd">
<components>
        <component name="toStringInterceptor" 
          class="org.seasar.framework.aop.interceptors.ToStringInterceptor"/>
        <component class="examples.aop.tostringinterceptor.EntityImplImpl">
                <arg>
                        innterEntity
                </arg>
                <aspect pointcut="toString">
                        toStringInterceptor
                </aspect>
        </component>
        <component name="innterEntity" 
          class="examples.aop.tostringinterceptor.InnerEntity">
                <aspect pointcut="toString">
                        toStringInterceptor
                </aspect>
        </component>
</components>

Creation the main program

  • We specify the dicon file path for the first argument of S2Container#create(), and create a container.
  • When we specify the class, such as EnitityImpl.class, for the first argument of S2Container#getComponent(), we can get the component.
  • We show the result using Sytem.out.println() in order to confirm it.

AopTraceClient.java

package examples.aop.tostringinterceptor;

import org.seasar.framework.container.S2Container;
import org.seasar.framework.container.factory.S2ContainerFactory;

public class AopToStringClient {
    private static String PATH = "examples/aop/tostringinterceptor/ToString.dicon";

    public static void main(String[] args) {
        S2Container container = S2ContainerFactory.create(PATH);
        Entity entity = (Entity) container.getComponent(EntityImpl.class);
        System.out.println(entity);
    }
}

Output

examples.aop.tostringinterceptor.EntityImplImpl@1db4f6f[
  implImplValue=3,implValue=2,intValue=1,staticIntValue=12,doubleValue=12.3,
  stringValue="1234",staticFinalIntValue=12345,mapValue={},
  innerEntity=examples.aop.tostringinterceptor.InnerEntity@13c1b02[
    innerInt=0,innerLong=234,str="foo",charArray={'a','b','c'}]]

This example is included in seasar2/src/examples/aop/tostringinterceptor.


DelegateInterceptor

The third example uses DelegateInterceptor of S2AOP. This shows how to delegate a method invocation to another class's method invocation.
First, define an abstract method in the interface, IBase.java. Next, define one abatract class, Dummy.java and one concreate class, Substance.java to implement IBase interface. Then, you are ready to delegate a method invocation in an abstract class to a concrete class. Write five files explained below.

  • An interface(IBase.java)
  • An abstract class that implmenets IBase interface(Dummy.java)
  • A concreate class that implements IBase interface.(Substance.java)
  • A dicon file so that the method invocation can be delegated.(Delegate.dicon)
  • Java class to check whether the configuration is made correctly or not(AopDelegateClient.java).

An interface, IBase.java

  • Define an interface so that it has an abstract method.

IBase.java

package examples.aop.delegateinterceptor;

public interface IBase {
    public abstract void run();
}

An abstract class, Dummy.java

  • Implement the interface defined above.

Dummy.java

package examples.aop.delegateinterceptor;

public abstract class Dummy implements IBase {
}

A concrete class, Substance.java

  • Implement the interface defined above.
  • Implement the abstract method defined above.

Substance.java

package examples.aop.delegateinterceptor;

public class Substance implements IBase {
    public void run() {
        System.out.println("substance");
    }
}

Dicon file

  • Define a component element for the abstract class, Dummy
  • Define an aspect element to associate Dummy with DelegateInterceptor component. Within this component element, define initMethod element to specify target component so that DelegateInterceptor#setTarget() delegates a method invocation to a target component. As shown below, the target component, examples.aop.delegateinterceptor.Substance, is set by a method injection.

Delegate.dicon

<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE components PUBLIC "-//SEASAR//DTD S2Container//EN"
"http://www.seasar.org/dtd/components.dtd">
<components>
    <component class="sample.aop.delegateinterceptor.Dummy">
        <aspect>
            <component
              class="org.seasar.framework.aop.interceptors.DelegateInterceptor">
                <initMethod name="setTarget">
                    <arg>new sample.aop.delegateinterceptor.Substance()</arg>
                </initMethod>
            </component>
        </aspect>
    </component>
</components>

A client Java class

  • Create a S2Container object by the method, org.seasar.framework.container.S2ContainerFactory#create(). Set a path to dicon file(Delegate.dicon) to an argument of create() method.
  • Get an object by the method, org.seasar.framework.container.S2Container#getComponent(). Set a component name to an argument of getComponent() method. A component name must be the name defined in dicon file. In this case, assign IBase.class.
  • Invoke IBase#run() method in the component got from a container.
AopDelegateCilent.dicon
package examples.aop.delegateinterceptor;

import org.seasar.framework.container.S2Container;
import org.seasar.framework.container.factory.S2ContainerFactory;

public class AopDelegateClient {
    private static String PATH = "examples/aop/delegateinterceptor/Delegate.dicon";
    public static void main( String[] args ){
        S2Container container = S2ContainerFactory.create(PATH);
        IBase base = (IBase) container.getComponent(Dummy.class);
        base.run();
    }
}

Output

The output string "substance" will appear on a console, so you can check that Dummy#run() has been delegated to Substance#run()

substance

This example is included in seasar2/src/examples/aop/delegateinterceptor.


PrototypeDelegateInterceptor

This example uses PrototypeDelegateInterceptor and delegates from a singlton component to a prototype component.
To make this example, first, define an abstract method in an intarface, then define an abstract class(singleton) and a concrete class(prototype) so that these two classes implement the interface. Second, write dicon file so that the method invocation of the abstract class will be delegated to the method in the concrete class. Write four files described below.

  • An interface(IBase.java)
  • An abstract singleton class that implements the interface(Dummy.java)
  • A prototype class that implements the interface(Substance.java)
  • Dicon file to deinfe a delegation(Delegate.dicon)
  • Java class to check whether the configuration is made correctly or not(AopPrototypeDelegateClient.java)

An interface, IBase.java

  • Define a abstract method.

IBase.java

package examples.aop.delegateinterceptor;

public interface IBase {
    public abstract void run();
}

An abstract class that implements the interface, Dummy.java

  • Implement the interface defined above.

Dummy.java

package examples.aop.delegateinterceptor;

public abstract class Dummy implements IBase {
}

A concrete class that implements the interface, Substance.java

  • Implement the interface defined above.
  • Implement the abstract method defined in the interface.

Substance.java

package examples.aop.delegateinterceptor;

public class Substance implements IBase {
    public void run() {
        System.out.println(this);
    }
}

Dicon file

  • Define a component element for an abstract class, Dummy. This component is a singleton.
  • Define an aspect element for the component, PrototypeDelegateInterceptor. Within this component element, define a property element to specify the target name ("target") to the property, targetName.
  • In addition, define a component element for a target class, Substance. An instance attribute of this component is a prototype.

PrototypeDelegate.dicon

<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE components PUBLIC "-//SEASAR//DTD S2Container//EN"
"http://www.seasar.org/dtd/components.dtd">
<components>
    <component class="sample.aop.delegateinterceptor.Dummy">
        <aspect>
            <component
              class="org.seasar.framework.aop.interceptors.PrototypeDelegateInterceptor">
                <property name="targetName">"target"</property>
            </component>
        </aspect>
    </component>

    <component name="target"
                  class="examples.aop.prototypedelegateinterceptor.Substance"
                  instance="prototype"/>
</components>

A client Java class

  • Create a S2Container object by the method, org.seasar.framework.container.S2ContainerFactory#create(). Set a path to dicon file(PrototypeDelegate.dicon) to an argument of create() method.
  • Get an object by the method, org.seasar.framework.container.S2Container#getComponent(). Set a component name to an argument of getComponent() method. A component name must be the name defined in dicon file. In this case, assign IBase.class.
  • Invoke IBase#run() method in the component got from a container several times.
AopPrototypeDelegateCilent.dicon
package examples.aop.prototypedelegateinterceptor;

import org.seasar.framework.container.S2Container;
import org.seasar.framework.container.factory.S2ContainerFactory;

public class AopDelegateClient {
    private static String PATH =
        "examples/aop/prototypedelegateinterceptor/PrototypeDelegate.dicon";
    public static void main( String[] args ){
        S2Container container = S2ContainerFactory.create(PATH);
        IBase base = (IBase) container.getComponent(Dummy.class);
        for (int i = 0; i < 5; ++i) {
            base.run();
        }
    }
}

Output

The output strings such as "examples.aop.prototypedelegateinterceptor.Substance@xxxxxx" will appear on a console, so you can check that Dummy#run() has been delegated to Substance#run(). Besides, you know that new Substance instances are created in every run() method invocation because output strings contain "@" followed by a hashcode value.

examples.aop.prototypedelegateinterceptor.Substance@ae533a
examples.aop.prototypedelegateinterceptor.Substance@da18ac
examples.aop.prototypedelegateinterceptor.Substance@8a0544
examples.aop.prototypedelegateinterceptor.Substance@401369
examples.aop.prototypedelegateinterceptor.Substance@e49dcd

This example is included in seasar2/src/examples/aop/delegateinterceptor


SyncInterceptor

This example uses SyncInterceptor to be able to synchronize accessing to one object from multiple threads.
For the comparison with SyncInterceptor and without it, we make a example without SyncInterceptor. First example creates multiple threads to access to a shared object. A shared object provides a method that adds one to the counter. Wrtie four files described below.

  • An interface that has a method to add one to the counter.(Count.java)
  • A conrete class that implements an interface(CountImpl.java)
  • Dicon file that defines components(SyncCalc.dicon)
  • Java class to check whether the configuration is defined correcltly or not(AopSyncClient.java)
An interface, Count.java
  • Define a method to add one to a counter
  • Define another method to get a counter value.

Count.java

package examples.aop.syncinterceptor;

public interface Count {

    public void add();

    public int get();
}

A concrete class, CountImple.java

  • Implement the interface defined above.
  • Declare counter variable and assign an initial value of zero.
  • Implement add() method so that it adds one to counter after sleeps for two seconds. The value before the addition is diplayed, too.
  • Implement get() method so that it returns the counter value.

CountImpl.java

package examples.aop.syncinterceptor;

public class CountImpl implements Count {
    private int _count = 0;

    public void add() {
        int a = _count;

        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        _count = a + 1;
        System.out.println(a);
    }

    public int get() {
        return _count;
    }
}

Dicon file

  • Define a component element for CountImple created above.

SyncCalc.dicon

<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE components PUBLIC "-//SEASAR//DTD S2Container//EN"
"http://www.seasar.org/dtd/components.dtd">
<components>
    <component class="examples.aop.syncinterceptor.CountImpl">
    </component>
</components>

A client Java class

  • Create a S2Container object by the method, org.seasar.framework.container.S2ContainerFactory#create(). Set a path to dicon file(SyncCalc.dicon) to an argument of create() method.
  • Get an object by the method, org.seasar.framework.container.S2Container#getComponent(). Set a component name to an argument of getComponent() method. A component name must be the name defined in dicon file. In this case, assign Count.class.
  • Create new Runnable instance. This instance is run by a thread and invokes add() method in Count class.
  • Create five Thread instances
  • Invoke each Thread#start() method so that five threads can work.
  • Invoke Thread#join() method to wait all of other threads finish thier works.
  • Output counter value when all created threads have done thier work.

AopSyncClient.java

package examples.aop.syncinterceptor;

import org.seasar.framework.container.S2Container;
import org.seasar.framework.container.factory.S2ContainerFactory;

public class AopSyncClient {
    private String PATH = "examples/aop/syncinterceptor/SyncCalc.dicon";
    private Count _count = null;

    public void init() {
        S2Container container = S2ContainerFactory.create(PATH);
        _count = (Count) container.getComponent(Count.class);
    }

    public void start() {
        System.out.println("count: " + _count.get());

        Runnable r = new Runnable() {
            public void run() {
                _count.add();
            }
        };
        Thread[] thres = new Thread[5];
        for (int i=0; i<5; i++) {
            thres[i] = new Thread(r);
            thres[i].start();
        }
        for (int i=0; i<5; i++) {
            try {
                thres[i].join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        System.out.println("count: " + _count.get());
    }

    public static void main(String[] args) {
        AopSyncClient asc = new AopSyncClient();
        asc.init();
        asc.start();
    }
}

Output

This output shows that created threads aren't synchronized.

count: 0
0
0
0
0
0
count: 1
Next, redefine dicon file to make created threads to synchronize by SyncInterceptor.
  • Associate

SyncCalc.dicon

<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE components PUBLIC "-//SEASAR//DTD S2Container//EN"
"http://www.seasar.org/dtd/components.dtd">
<components>
    <component class="examples.aop.syncinterceptor.CountImpl">
        <aspect>
            <component class="org.seasar.framework.aop.interceptors.SyncInterceptor"/>
        </aspect>
    </component>
</components>

Output

This output shows that all created threads are synchronized because counter value increases one by one.

count: 0
0
1
2
3
4
count: 5

This example is included in seasar2/src/examples/aop/syncinterceptor


How to define your own Interceptor

This section describes how you can make your own Interceptor. To help you to understand well, we adopt one example that get a target class name, method name and arguments. Besides, this example measures processing times needed in each method invocations. Also, we try to keep track a processing time that extremly consumes cpu power by our Interceptor. Write four files shown below.

  • An Interceptor that outputs a target class name, method name and arguments. Also, this outputs a method processing time measured by this Interceptor.(MeasurementInterceptor.java)
  • A class which will be associated with Interceptor. Write this class so that consumes much cpu power.(HeavyProcess.java)
  • Dicon file that defines components.(Measurement.dicon)
  • Java class to check whether the configuration is defined correcltly or not.(AopMeasurementClient.java)

How to define your own Intercepter

  • Implement org.seasar.framework.aop.interceptors.AbstractInterceptor class.
  • Implement invoke(MethodInvocation invocation) method.
  • Get FQCN(Fully Qualified Class Name) of a target class through getTargetClass(invocation).getName() method. If you get a name from MethodInvocation#getThis() method, you'll be get an embeded class name in bytecode.
  • Get the method name through invocation.getMethod().getName() method.
  • Get arguments through invocation.getArguments() method.
  • Get the current time before invocation.proceed() method. This means that you get the time just before the method invocation because invocation.proceed() invokes a target method.
  • Get the current time again after invocation.proceed() method. This has an effect to get the time right after the real method invocation. Then, output the processing time in a finally clause.

MeasurementInterceptor.java

package examples.aop.originalinterceptor;

import org.aopalliance.intercept.MethodInvocation;
import org.seasar.framework.aop.interceptors.AbstractInterceptor;

public class MeasurementInterceptor extends AbstractInterceptor{
    public Object invoke(MethodInvocation invocation) throws Throwable {
        long start = 0;
        long end = 0;
        StringBuffer buf = new StringBuffer(100);

        buf.append(getTargetClass(invocation).getName());
        buf.append("#");
        buf.append(invocation.getMethod().getName());
        buf.append("(");
        Object[] args = invocation.getArguments();
        if (args != null && args.length > 0) {
            for (int i = 0; i < args.length; ++i) {
                buf.append(args[i]);
                buf.append(", ");
            }
            buf.setLength(buf.length() - 2);
        }
        buf.append(")");
        try {
            start = System.currentTimeMillis();
            Object ret = invocation.proceed();
            end = System.currentTimeMillis();
            buf.append(" : ");
            return ret;
        } catch (Throwable t) {
            buf.append(" Throwable:");
            buf.append(t);
            throw t;
        } finally {
            System.out.println(buf.toString() + (end - start));
        }
    }
}

How to wrtie a class that will be associated with Interceptor

  • Make this class to sleep five seconds because we want to assume this class consumes much cpu power.

HeavyProcess.java

package examples.aop.originalinterceptor;

public class HeavyProcess {
    public void heavy(){
        try{
            Thread.sleep(5000);
        } catch (InterruptedException ex) {
            ex.printStackTrace();
        }
    }
}
Dicon file
  • Define a component element for MeasurementInterceptor. Assign measurement to name attribute.
  • Define a component element for HeavyProcess class and define an aspect element as a nested element to associated heavy() method with MeasurementInterceptor.

Measurement.dicon

<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE components PUBLIC "-//SEASAR//DTD S2Container//EN"
"http://www.seasar.org/dtd/components.dtd">
<components>
    <component name="measurement"
      class="examples.aop.originalinterceptor.MeasurementInterceptor"/>
    <component class="examples.aop.originalinterceptor.HeavyProcess">
        <aspect pointcut="heavy">
               measurement
        </aspect>
    </component>
</components>

A client Java class

  • Create a S2Container object by the method, org.seasar.framework.container.S2ContainerFactory#create(). Set a path to dicon file(Measurement.dicon) to an argument of create() method.
  • Get an object by the method, org.seasar.framework.container.S2Container#getComponent(). Set a component name to an argument of getComponent() method. A component name must be the name defined in dicon file. In this case, assign HeavyProcess.class.
  • Invoke HeavyProcess#heavy() method got from a container.

AopMeasurementClient.java

package examples.aop.originalinterceptor;

import org.seasar.framework.container.S2Container;
import org.seasar.framework.container.factory.S2ContainerFactory;

public class AopMeasurementClient {
    private static String PATH = "examples/aop/originalinterceptor/Measurement.dicon";

    public static void main(String[] args) {
        S2Container container = S2ContainerFactory.create(PATH);
        HeavyProcess heavyProcess = (HeavyProcess) container
                .getComponent(HeavyProcess.class);
        heavyProcess.heavy();
    }
}

Output

You see the message shown below, so you know this class keeps track a class name, a method name, arguments and processing time.

examples.aop.HeavyProcess#heavy() : 5004

This example is included in seasar2/src/examples/aop/originalinterceptor