process management blog posts

jBPM 3 annotations

Blog: PlanetjBPM's Weblog

Everybody using seam probably knows about the fact that Seam provides access to process instances, tasks and a (kind of) process context via annotations. But the seam process context does not always play nice with the plain jbpm context.

A few months ago, I was working on a ‘new’ JSF/Seam based jBPM web console and needed the real JbpmContext.  Now everybody using jBPM knows about the basic try/finally block

public void myMethod() {
    // Lookup the pojo persistence context-builder that is configured above
    JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();
    try {
      // Do work on the jBPM context
    } finally {
      // Tear down the pojo persistence context.
      // This includes flush the SQL for inserting the process definition  
      // to the database.
      jbpmContext.close();
    }
  }
}

Since I had many classes and inside that many methods where I needed the jBPM context, I decided to take a quick look at creating a custom annotation. Well, it turned out to be very simple. The result is:

  @Jbpm3Method
  public void myMethod() {
    //Do work on the jBPM context
  }

Much cleaner isn’t it? Since I created this as a proof of concept and I am not an expert in this area, it is (most likely) not optimized and dependent on seam (tips are of course welcome to make it better and more generic). You also need a @Jbpm3 annotation on the class level and have to annotate a variable of type JbpmContext. But when you put this in a base class:

  @Jbpm3
  public abstract class Jbpm3AbstractBaseClass {

  @Jbpm3Context
  public JbpmContext jBPMContext;

  }

All you have to do is extend this base class.

For everybody who is interested, the code off all classes:

Jbpm3Interceptor.java
package org.jboss.jbpm.console.interceptor;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

import org.jboss.seam.annotations.Logger;
import org.jboss.seam.annotations.intercept.AroundInvoke;
import org.jboss.seam.annotations.intercept.Interceptor;

import org.jboss.seam.intercept.AbstractInterceptor;
import org.jboss.seam.intercept.InvocationContext;
import org.jboss.seam.log.Log;
import org.jbpm.JbpmConfiguration;
import org.jbpm.JbpmContext;

@Interceptor
public class Jbpm3Interceptor extends AbstractInterceptor implements Serializable {

  /**
  *
  */
  private static final long serialVersionUID = 9077871878937632746L;

  @Logger
  Log                       log;

  JbpmContext               jBPMContext      = null;

  @AroundInvoke
  public Object aroundInvoke(InvocationContext invocationContext) throws Exception {

    Method method = invocationContext.getMethod();

    if (method.isAnnotationPresent(Jbpm3Method.class)) {

      Field jBPM3Context = null;
      Object invocationTarget = invocationContext.getTarget();
      Field[] fields = invocationTarget.getClass().getFields();

      for (Field field : fields) {

        if (field.isAnnotationPresent(Jbpm3Context.class)) {

          jBPM3Context = field;
          JbpmContext jBPMContext = JbpmConfiguration.getInstance().createJbpmContext();
          field.set(invocationTarget, jBPMContext);

          Object returnVal = invocationContext.proceed();

          if (jBPMContext != null) jBPMContext.close();

          return returnVal;
        }
      }
      if (jBPM3Context == null) {
        log.warn("Class " + invocationContext.getClass().getName()
        + " has Jbpm3 annotation, but no field public field has @Jbpm3Context annotation");
      }
    }

    return invocationContext.proceed();

  }

  public boolean isInterceptorEnabled() {
    log.debug("isInterceptorEnabled() = " + true + " for " + getComponent().getName() );
    return getComponent().beanClassHasAnnotation(Jbpm3.class);
  }

}

Jbpm3.java

  package org.jboss.jbpm.console.interceptor;

  import java.lang.annotation.Documented;
  import java.lang.annotation.ElementType;
  import java.lang.annotation.Inherited;
  import java.lang.annotation.Retention;
  import java.lang.annotation.RetentionPolicy;
  import java.lang.annotation.Target;

  import org.jboss.seam.annotations.intercept.Interceptors;

  @Target({ElementType.TYPE})
  @Retention(RetentionPolicy.RUNTIME)
  @Interceptors(Jbpm3Interceptor.class)
  @Documented
  @Inherited
  public @interface Jbpm3 {}

Jbpm3Context.java

  package org.jboss.jbpm.console.interceptor;

  import java.lang.annotation.Documented;
  import java.lang.annotation.ElementType;
  import java.lang.annotation.Inherited;
  import java.lang.annotation.Retention;
  import java.lang.annotation.RetentionPolicy;
  import java.lang.annotation.Target;

  @Target({ElementType.FIELD})
  @Retention(RetentionPolicy.RUNTIME)
  @Documented
  @Inherited
  public @interface Jbpm3Context {}

JbpmMethod.java

  package org.jboss.jbpm.console.interceptor;

  import java.lang.annotation.Documented;
  import java.lang.annotation.ElementType;
  import java.lang.annotation.Inherited;
  import java.lang.annotation.Retention;
  import java.lang.annotation.RetentionPolicy;
  import java.lang.annotation.Target;

  import org.jboss.seam.annotations.intercept.Interceptors;

  /**
   * Opens a JbpmContext before the method is called and closes it afterwards.
   */
  @Target({ElementType.METHOD})
  @Retention(RetentionPolicy.RUNTIME)
  @Interceptors(Jbpm3Interceptor.class)
  @Documented
  @Inherited
  public @interface Jbpm3Method {}