Circular Dependencies of Session Beans

This post is about circular dependencies between session beans (ejb 3.0), which is ‘not possible’ without manual loading. The manual variant might be the solution of choice for you. Therefore, I will sketch it out later in this post. The first part of this post post is about a trial to achieve this with @EJB annotation only … which failed! But perhaps it will stop some of you to try it out (for nuts) and it’s a great bridge to a solution by a snatch enabling to have circular injection of ‘your own’ beans. I will show you the latter in my next post.

The idea is, to use interceptors and an own @Inject annotation, in order to inject Session Beans into another bean before a method is invoked on the latter and to clear the fields afterwards. In my way of thinking, no bean depends on interceptors, so there are no circles as I define all of dependencies in the interceptor. Unfortunately, dependencies of declared interceptors are evaluated with the bean and ‘count’ as theirs. This is mentioned in the documentation of ejb interceptors:

Just like a bean class, an interceptor can be the target of Dependency injection. The format for how this works is the same, and the injection works off the same ENC [Enterprise Naming Context] as the bean to which the interceptor is bound.

The next code snippet shows one of the two beans having a circular dependency:

@Stateless
@Local(Circular.class)
@Interceptors({Circular2Interceptor.class})
public class CircularBean implements Circular {
  private final static Logger log = Logger.getLogger(CircularBean.class.getName());
  @Inject
  public Circular2 circle2;
  public void foo() {
    log.severe("CircularBean#foo(): " + this);
    this.circle2.bar("this.circle#bar(String) called from " + this);
  }
  public void bar(final String msg) {
    log.severe("CircularBean#bar(): " + this + ", msg: " + msg);
  }
}

In the following the second bean is presented:

@Stateless
@Local(Circular2.class)
@Interceptors({CircularInterceptor.class})
public class Circular2Bean implements Circular2 {
  private final static Logger log = Logger.getLogger(Circular2Bean.class.getName());
  @Inject
  public Circular circle;
  public void foo() {
    log.severe("Circular2Bean#foo(): " + this);
    this.circle.bar("this.circle#bar(String) called from " + this);
  }
  public void bar(final String msg) {
    log.severe("Circular2Bean#bar(): " + this + ", msg: " + msg);
  }
}

Both have in common that they have a declaration for an interceptor. The interceptor iterates through all public fields with an @Inject annotation and will inject the corresponding object, if it has been injected via @EJB annotation into the interceptor. You can find the code in the project package.

You will get the following error message on the console right after starting the jboss, because of the circular dependency:

DEPLOYMENTS MISSING DEPENDENCIES:
  Deployment "jboss.j2ee:jar=circular-dependencies-0.0.1-SNAPSHOT.jar,name=Circular2Bean,service=EJB3" is missing the following dependencies:
    Dependency "<UNKNOWN jboss.j2ee:jar=circular-dependencies-0.0.1-SNAPSHOT.jar,name=Circular2Bean,service=EJB3>" (should be in state "Described", but is actually in state "** UNRESOLVED Demands 'Class:de.jn.sandbox.ejb.circular.Circular' **")
  Deployment "jboss.j2ee:jar=circular-dependencies-0.0.1-SNAPSHOT.jar,name=Circular2Bean,service=EJB3_endpoint" is missing the following dependencies:
    Dependency "jboss.j2ee:jar=circular-dependencies-0.0.1-SNAPSHOT.jar,name=Circular2Bean,service=EJB3" (should be in state "Configured", but is actually in state "PreInstall")
  Deployment "jboss.j2ee:jar=circular-dependencies-0.0.1-SNAPSHOT.jar,name=CircularBean,service=EJB3" is missing the following dependencies:
    Dependency "<UNKNOWN jboss.j2ee:jar=circular-dependencies-0.0.1-SNAPSHOT.jar,name=CircularBean,service=EJB3>" (should be in state "Described", but is actually in state "** UNRESOLVED Demands 'Class:de.jn.sandbox.ejb.circular.Circular2' **")
  Deployment "jboss.j2ee:jar=circular-dependencies-0.0.1-SNAPSHOT.jar,name=CircularBean,service=EJB3_endpoint" is missing the following dependencies:
    Dependency "jboss.j2ee:jar=circular-dependencies-0.0.1-SNAPSHOT.jar,name=CircularBean,service=EJB3" (should be in state "Configured", but is actually in state "PreInstall")
  Deployment "jboss.j2ee:jar=circular-dependencies-0.0.1-SNAPSHOT.jar,name=CircularMBean,service=EJB3" is missing the following dependencies:
    Dependency "<UNKNOWN jboss.j2ee:jar=circular-dependencies-0.0.1-SNAPSHOT.jar,name=CircularMBean,service=EJB3>" (should be in state "Described", but is actually in state "** UNRESOLVED Demands 'jndi:CircularBean/local-de.jn.sandbox.ejb.circular.Circular' **")
    Dependency "<UNKNOWN jboss.j2ee:jar=circular-dependencies-0.0.1-SNAPSHOT.jar,name=CircularMBean,service=EJB3>" (should be in state "Described", but is actually in state "** UNRESOLVED Demands 'jndi:Circular2Bean/local-de.jn.sandbox.ejb.circular.Circular2' **")

DEPLOYMENTS IN ERROR:
  Deployment "<UNKNOWN jboss.j2ee:jar=circular-dependencies-0.0.1-SNAPSHOT.jar,name=CircularMBean,service=EJB3>" is in error due to the following reason(s): ** UNRESOLVED Demands 'jndi:CircularBean/local-de.jn.sandbox.ejb.circular.Circular' **, ** UNRESOLVED Demands 'jndi:Circular2Bean/local-de.jn.sandbox.ejb.circular.Circular2' **
  Deployment "<UNKNOWN jboss.j2ee:jar=circular-dependencies-0.0.1-SNAPSHOT.jar,name=CircularBean,service=EJB3>" is in error due to the following reason(s): ** UNRESOLVED Demands 'Class:de.jn.sandbox.ejb.circular.Circular2' **
  Deployment "<UNKNOWN jboss.j2ee:jar=circular-dependencies-0.0.1-SNAPSHOT.jar,name=Circular2Bean,service=EJB3>" is in error due to the following reason(s): ** UNRESOLVED Demands 'Class:de.jn.sandbox.ejb.circular.Circular' **

As you can see, the dependencies belong to the respective bean. The interceptors aren’t even mentioned in the error messages.

You may download the source code of circular-dependencies-0.0.2-SNAPSHOT.

Of course, You may use manual lookups via InitialContext. JBoss, e.g., has nice annotations for setting @LocalBinding and @RemoteBinding names for a bean, which can be added as a value to the respective new @Inject annotation. The interceptor loads the dependent beans by looking up the name and inject them as mentioned before in a method annotated with @PostConstruct and clears them before the interceptor is destroyed. But this is a unfortunate solution (in my opinion). Luckily, you may switch to Java EE 6 and CDI.

We need an mbean in order to invoke our code (if you like to try out the ‘manual lookup’ variant):

@Service(objectName="circular-dependencies:service=Circular")
@Management(CircularMBeanService.class)
public class CircularMBean implements CircularMBeanService {
  @EJB
  private Circular circle;
  @EJB
  private Circular2 circle2;
  public void executeFoo() {
    this.circle.foo();
    this.circle2.foo();
  }
}

The mbean method may be executed via the jmx-console (http://localhost:8080/jmx-console server and port scheme may differ):
how to invoke the foo method of the mbean

Leave a Reply

Your email address will not be published. Required fields are marked *