Using Dependency Injection in Struts2 for stateless EJBs part 2

This is the second, and last part of the Using Dependency Injection in Struts2 for stateless EJBs series of posts. In this post I will present a utility class that can be used to make the creation of Guice bindings for EJB3s easier.

Introducing EjbBinder

Our goal is to make an easier API, specifically for creating bindings of EJB3s, and make the specification of the JNDI name optional, when possible. The below code is a new version of the EjbModule class that uses a new class, called EjbBinder, that we are going to present in this post.

public class EjbModule implements Module {

  public void configure(Binder binder) {
    // Bind Context to the default InitialContext.
    binder.bind(Context.class).to(InitialContext.class);

    EjbBinder ejbs = new EjbBinder(binder, new GlassfishEjbJndiNameStrategy());
    ejbs.bindRemote(CurrencyManagerRemote.class);
    ejbs.bind(DatesManagerRemote.class, "com.tzavellas.dates.ejb.DatesManagerBean");
  }
}

EjbBinder contains two methods named bind and bindRemote. The bind method receives as arguments the expected class and JNDI name and simply uses Binder and JndiItegration to create a binding. The bindRemote method takes as an argument the remote interface of a sesion EJB3 and creates a binding using a JNDI name retrieved from the EjbJndiNameStrategy, that is specified in the EjbBinder constructor (BTW the CurrencyManagerRemote EJB interface in the above code does not relate with the application that we developed in the previous post and it is here to illustrate the bindRemote method).

The definition of the EjbJndiNameStrategyinterface:

public interface EjbJndiNameStrategy {

  String interfaceToJndiName(Class<?> beanInterface);
}

The implementation of EjbBinder:

import com.google.inject.Binder;
import com.google.inject.binder.ScopedBindingBuilder;
import static com.google.inject.jndi.JndiIntegration.fromJndi;

public class EjbBinder {

  private Binder binder;
  private EjbJndiNameStrategy jndiNameStrategy;

  public EjbBinder(Binder binder, EjbJndiNameStrategy nameStrategy) {
    this.binder = binder;
    this.jndiNameStrategy = nameStrategy;
  }

  public <T> ScopedBindingBuilder bindRemote(Class<T> beanInterface) {
    return bind(beanInterface,
                jndiNameStrategy.interfaceToJndiName(beanInterface));
  }

  public <T> ScopedBindingBuilder bind(Class<T> beanInterface, String jndiName) {
    return binder.bind(beanInterface)
                 .toProvider(fromJndi(beanInterface, jndiName));
  }
}

Application Server Specific Naming Strategies

When you create a new EJB and you do not specify a JNDI name, the application server assigns a default name for you. So, if you have an implementation of EjbJndiNameStrategy that uses the naming rules of your application server you could avoid the need to specify a JNDI name when creating the EJB and when creating the binding of the EJB to Guice.

Below we have two implementations of the EjbJndiNameStrategy that can infer the JNDI name of an EJB’s remote client interface using the remote interface’s class for the Glassfish and JBoss application servers.

For the Glassfish application server the default global JNDI name of an EJB with a remote interface is the fully qualified name of the remote interface.

public class GlassfishEjbJndiNameStrategy implements EjbJndiNameStrategy {

  public String interfaceToJndiName(Class beanInterface) {
    return beanInterface.getName();
  }
}

For the JBoss application server the default JNDI name for the remote interface of an EJB is earName/beanName/remote.

//WARNING: Not tested!
public class JbossEjbJndiNameStrategy implements EjbJndiNameStrategy {

  private String earName = "";

  public JbossEjbJndiNameStrategy() { }

  public JbossEjbJndiNameStrategy(String earName) {
    if (earName != null && "".equals(earName.trim()))
      this.earName = earName + "/";
  }

  public String interfaceToJndiName(Class<?> beanInterface) {
    return earName + interfaceToBeanName(beanInterface) + "/remote";
  }

  protected String interfaceToBeanName(Class<?> beanInterface) {
    String name = beanInterface.getSimpleName();
    if (name.endsWith("Remote"))
      name = name.replace("Remote", "");
    return name + "Bean";
  }
}