Search code examples
jakarta-eeejbgwtp

Using EJBs in a GWTP project with RPC Dispatch


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?


Solution

  • So after some research there seem to be two ways for me to use EJB injection in Guice/RPC Dispatch applications:

    1. Centralized injection

    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));
        }
    
    }
    

    2. Custom Annotations

    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:

    The annotation

    @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";
    }
    

    The TypeListener

    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();
            }
        }
    }
    

    The MembersInjector

    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;
        }
    
    }