We're using GWTP RPC Dispatch with Guice in a web/enterprise application. We want our business logic in a separated module of our EAR using EJBs. Since RPC Dispatch is in action here there's just a single servlet which is kind of capsuled. So there is no direct way to get our EJBs injected, at least not without loosing container management of the EJBs.
Is there another way to integrate EJBs in our application? Or am I missing something here?
So after some research there seem to be two ways for me to use EJB injection in Guice/RPC Dispatch applications:
EJB injection works in GuiceServletContextListener
or whatever the name of your child class is. There you can e.g. inject a bean via @EJB
annotation and then pass the reference to a module in the getInjector()
method:
import javax.ejb.EJB;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.servlet.GuiceServletContextListener;
public class GuiceServletConfig extends GuiceServletContextListener {
@EJB
private SampleBeanLocal sampleBean;
@Override
protected Injector getInjector() {
return Guice.createInjector(new ServerModule(), new BeanModule(sampleBean));
}
}
The other way is to create a custom annotation which you then use to inject beans where it isn't possible. The implementation has been described here.
Due to some flaws in the code example i will also post my solution here:
@Retention(RetentionPolicy.RUNTIME)
@Target({ TYPE, METHOD, FIELD})
public @interface GuiceEJB {
/**
* Name of the bean.
*
* @return Name of the bean.
*/
String name();
/**
* Local EJB as default type.
*
* @return bean type.
*/
String type() default "local";
}
import java.lang.reflect.Field;
import com.google.inject.TypeLiteral;
import com.google.inject.spi.TypeEncounter;
import com.google.inject.spi.TypeListener;
/**
* Custom Guice {@link TypeListener} for the {@link GuiceEJB} annotation.
*/
public class GuiceEJBTypeListener implements TypeListener {
@Override
public <I> void hear(TypeLiteral<I> typeLiteral, TypeEncounter<I> encounter) {
Class< ? > clazz = typeLiteral.getRawType();
while (clazz != null) {
// iterate through the fields of the class
for (Field field : clazz.getDeclaredFields()) {
if (field.isAnnotationPresent(GuiceEJB.class)) {
encounter.register(new GuiceEJBMemberInjector<I>(field));
}
}
// repeat for super class
clazz = clazz.getSuperclass();
}
}
}
import java.lang.reflect.Field;
import javax.naming.Context;
import javax.naming.InitialContext;
import com.google.inject.MembersInjector;
/**
* Custom member injector for {@link GuiceEJB} annotations.
*/
public class GuiceEJBMemberInjector<T> implements MembersInjector<T> {
/**
* Field that needs injection.
*/
private Field field;
/**
* Bean type.
*/
private String type;
/**
* Bean name.
*/
private String name;
/**
* @param field
*/
public GuiceEJBMemberInjector(Field field) {
this.field = field;
GuiceEJB annotation = field.getAnnotation(GuiceEJB.class);
this.type = annotation.type();
this.name = annotation.name();
this.field.setAccessible(true);
}
@Override
public void injectMembers(T t) {
try {
if ("local".compareToIgnoreCase(this.type) == 0) {
this.field.set(t, findLocalEJB());
}
} catch (Exception e) {
throw new GuiceEJBInjectException(e);
}
}
/**
* Lookup an local EJB for the given field.
*
* @return
*/
private Object findLocalEJB() {
Context initialContext = null;
Object localRef = null;
try {
// look up the bean by name
localRef = InitialContext.doLookup(this.name);
} catch (Exception e) {
throw new GuiceEJBInjectException(e);
} finally {
try {
if (initialContext != null)
initialContext.close();
} catch (Exception e) {
}
}
return localRef;
}
}