Sunday, November 21, 2010

Injecting into EJB3 with Google Guice

As I have already written in a previous post, Dependency Injection in EJB3 is quite limited.
The fact you can only inject other EJB's or something you can annotate with @Resource (JMS Queue, JCA and so on) is not enough in the case I want to externalize some of the logic of the EJB in a collaborator and unit testing the isolated EJB by mocking the collaborator.
Also properties files are quite tricky to use with EJB3, it would not be nice to have to access the file directly from the EJB in each EJB!
Since what I needed was just injecting something I thought it was a nice opportunity to give a try to Google Guice and I must say I am really satisfied with the final result :)

Since EJB are instantiated by the application server the only way to inject something is through an interceptor.
Therefore we will have a singleton EJB holding the Guice injector and an interceptor setting the dependencies on the EJB before the method invocation.

Let's first define the EJB interface

import com.google.inject.Injector;

@Local
public interface GuiceInjectorHolder {

 public Injector getInjector();
}

Next let's implement the EJB

@Singleton
public class GuiceInjectorHolderBean  implements GuiceInjectorHolder{

 private final Injector injector = Guice.createInjector(new PropertyModule());
 
 @Override
 public Injector getInjector(){
  
  return injector;
 }
 
 /**
  * Convenient Guice module for loading configuration values from property files
  * 
  * @author diego
  *
  */
 private static class PropertyModule extends AbstractModule{

  @Override
  protected void configure() {
  
   Properties prop = loadProperties();
   Names.bindProperties(binder(), prop);
  }

  private Properties loadProperties() {
   
   Properties result = new Properties();
   try {
    result.load(getClass().getResourceAsStream("/application.properties"));
   } catch (IOException e) {
    throw new RuntimeException(e);
   }
   return result;
  }
  
 }
}
As you can see an extension of the AbstractModule is provided to the Guice constructor. What it mainly does is simply loading properties from application.properties, which has to be found in the classpath. With few lines of code you can now make your application fully configurable :)

It is time now to implement the Interceptor:

public class GuiceInterceptor {

 @EJB
 private GuiceInjectorHolder injectorHolder;
 
 @AroundInvoke
 public Object injectByGuice(InvocationContext context) throws Exception{
  
  injectorHolder.getInjector().injectMembers(context.getTarget());
  return context.proceed();
 }
}

And finally our EJB

@Stateless
@Interceptors(value = { GuiceInterceptor.class })
public class InjectedEJB implement SomeLocalInterface{

    @Inject
    private Collaborator collaborator;

    @Inject
    private String configurableValue;

    public void doSomething() {
       //something before
       collaborator.doSomething(configurableValue);
       //something after
    } 

}

Now you can mock your collaborator and unit test the EJB.
Next time I will talk about mocking an EntityManager.

2 comments:

  1. Your solution worked for me. Really awesome. Thanks a lot

    ReplyDelete