Aspect Oriented Programming in opentaps

From Opentaps Wiki
Jump to navigationJump to search

opentaps uses aspect oriented programming (AOP) to interact with pre-existing ofbiz code and enhance its behavior. For example, we have inserted an aspect "advice" to the ofbiz entity engine's delegator so that the data is stored will be indexed for opentaps search. You can also use AOP yourself to alter the behavior of opentaps code to meet your needs.

With opentaps 1.0.5 and later, including opentaps 1.4, AspectWerkz is used to support aspect oriented programming. (opentaps 1.0.0 through 1.0.4 used AspectJ, and you can find more information about it in Understanding AspectJ

AspectWerkz allows you to define your AOP advice in Java using annotations. For example, in IndexForDelegatorAspects class, we define the following point cut for the ofbiz delegator:

 * @Expression execution(* org.ofbiz.entity.GenericDelegator.evalEcaRules(..)) && 
               args(event, currentOperation, value, eventMap, noEventMapFound, isError)
void pointcut(String event, String currentOperation, GenericEntity value, Map eventMap, boolean noEventMapFound, boolean isError) {}

This basically defines a point cut for the

   protected void evalEcaRules(String event, String currentOperation, GenericEntity value, 
                               Map eventMap, boolean noEventMapFound, boolean isError) throws GenericEntityException {

method of the delegator. Then, we define this method to be executed after the pointcut:

   * @After pointcut(event, currentOperation, value, eventMap, noEventMapFound, isError)
   public void createIndexForEca(String event, String currentOperation, GenericEntity value, 
                                 Map eventMap, boolean noEventMapFound, boolean isError) {

which means that createIndexForEca will be run after evalEcaRules is run.

These aspects are then configured in the file hot-deploy/opentaps-common/config/aop-ecas.xml:

   <system id="aspectwerkz">
       <aspect class="org.opentaps.aspect.secas.CommonServiceAspects"/>
       <aspect class="org.opentaps.aspect.secas.IndexForDelegatorAspects"/>

During build time, the main build.xml invokes build-aspects.xml:

   <subant inheritall="false" failonerror="true">
      <filelist dir="." files="hot-deploy/opentaps-common/build-aspects.xml"/>

This in turn modifies the target jar file. For example:

 <java classname="org.codehaus.aspectwerkz.compiler.AspectWerkzC" fork="true">
    <jvmarg value="-Daspectwerkz.definition.file=${aopConfig}"/>
    <jvmarg value="-Daspectwerkz.transform.filter=no"/>
    <arg value="-verify"/>
    <arg value="-verbose"/>
    <classpath refid="local.class.path" />
       <pathelement path="${build.dir}/classes/common"/>
    <arg value="${ofbiz.dir}/framework/entity/build/lib/ofbiz-entity.jar"/>

weaves the advice codes to ofbiz-entity.jar and add aop.xml into ofbiz-entity.jar in this line.

<jar jarfile="${ofbiz.dir}/framework/entity/build/lib/ofbiz-entity.jar" update="true" 
     basedir="${build.dir}/classes/" includes="META-INF/aop.xml" />

If you use a Java decompiler to reconstruct the source code from your jar file, you might see something like this:

protected void evalEcaRules(String paramString1, String paramString2, GenericEntity paramGenericEntity, 
                            Map paramMap, boolean paramBoolean1, boolean paramBoolean2)
   throws GenericEntityException
   GenericDelegator_1_20389545_1395125462___AW_JoinPoint.invoke(this, paramString1, paramString2, paramGenericEntity, 
                                                                paramMap, paramBoolean1, paramBoolean2, this);

Note that when you need to define aspect advice for methods, Public, Private, and Protected need to be capitalized and. They cannot be written as public, private, or protected. For example, for, the correct definition is:

    * Pointcut testRunContainerStart is pointcut to TestRunContainer.start.
    * @Expression execution(Public boolean org.ofbiz.testtools.TestRunContainer.start(..))
   Pointcut testRunContainerStart;

Other References