Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

hooking into @EJB or @PersistenceContext injection in JAX-RS unit tests

I'm enjoying learning JAX-RS and Jersey, but I've hit a roadblock trying to test a simple resource that needs a DAO injected, something like this:

@Stateless
@Path("simple")
public class SimpleResource {

    @PersistenceContext
    private EntityManager em;

// @GET, etc...

}

(I'll be moving to a more abstracted DAO pattern, but the problem is the same, i.e., how do I inject the @EJB DAO?)

In my unit tests I'm using a embedded Jetty server that configures Jersey in its web.xml, and I'd like to hook into the resource's lifecycle so that I can inject a mock EntityManager, but I've not found a clean answer after a lot of searching. Can you help? Some possible directions I've come across:

1) Use JNDI context lookup in my code to get the DAO bean, and register the mock object in the tests.

Instead of @EJB or @PersistenceContext, use something like this in the resource's constructor:

  theDAO = (DAOImpl) new InitialContext().lookup("java:global/EJB/DAOImpl");

However, that means my test environment needs to support JNDI, and doing so in Jetty will probably involve some pain. Plus, it doesn't use the clean annotation approach.

2) Use method injection.

Inject into the method so that I can set the DAO post-instantiation, e.g.,

@PersistenceContext(name = "persistence/pu00")
public void setPersistenceUnit00(final EntityManager em) {
    em00 = em;
}

OR

private MyEjbInterface myEjb;
@EJB(mappedName="ejb/MyEjb")
public void setMyEjb(MyEjb myEjb) {
    this.myEjb = myEjb;
}

However, to do this I need the Jersey-instantiated instance, e.g., SimpleResource. How do I get that?

3) Use reflection.

A kind of DIY injection, something like:

public static void setPrivateField(Class<? extends Object> instanceFieldClass, Object instance, String fieldName, Object fieldValue) {
    Field setId = instanceFieldClass.getDeclaredField(fieldName);
    setId.setAccessible(true);
    setId.set(instance, fieldValue);
}

Again, I need the Jersey-instantiated instance.

4) Use an Injection Provider.

I'm still sketchy on how this works, but it looks like Jersey provides a means of defining customized injectable annotations, e.g.,

    @Provider
    public class EJBProvider implements InjectableProvider<EJB, Type> {

    public ComponentScope getScope() {
    return ComponentScope.Singleton;
    }

    public Injectable getInjectable(ComponentContext cc, EJB ejb, Type t) {
    if (!(t instanceof Class)) {
        return null;
    }
    try {
        Class c = (Class) t;
        Context ic = new InitialContext();
        final Object o = ic.lookup(c.getName());
        return new Injectable<Object>() {
        public Object getValue() {
            return o;
        }
        };
    } catch (Exception e) {
        e.printStackTrace();
        return null;
    }
    }
}

A variation using a helper class:

Server server = new Server(8080);
Context root = new Context(server,"/",Context.SESSIONS);

ResourceConfig rc = new PackagesResourceConfig("edu.mit.senseable.livesingapore.platform.restws.representations");
rc.getSingletons().add(new SingletonTypeInjectableProvider<javax.ws.rs.core.Context, Myobj>(Myobj.class, new Myobj(12,13)){});

root.addServlet(new ServletHolder(new ServletContainer(rc)), "/");
server.start();

With this use:

@Path("/helloworld")
public class HelloWorldResource {
    @Context Myobj myClass;
    ....
}

Is this viable for @EJB or @PersistenceContext?

5) Extend javax.ws.rs.core.Application.

Sketchy on this, but:

@javax.ws.rs.ApplicationPath("application")
public class InjectionApplication extends javax.ws.rs.core.Application {

  private Set<Object> singletons = new HashSet<Object>();
  private Set<Class<?>> classes = new HashSet<Class<?>>();

  public InjectionApplication() {
    // no instance is created, just class is listed
    classes.add(BookResource.class);
  }

  @Override
  public Set<Class<?>> getClasses() {
    return classes;
  }

  @Override
  public Set<Object> getSingletons() {
    return singletons;
  }
}

6) Extend ServletContainer.

An older style of using InjectableProvider? Looks more complex:

public class ServletAdapter extends ServletContainer {

@Override
protected void configure(ServletConfig servletConfig, ResourceConfig rc, WebApplication wa) {
    super.configure(servletConfig, rc, wa);

    rc.getSingletons().add(new InjectableProvider<Resource, Type>() {

        public ComponentScope getScope() {
            return ComponentScope.Singleton;
        }

        public Injectable<Object> getInjectable(ComponentContext ic, Resource r, Type c) {
            final Holder value = new Holder();

                Context ctx = new InitialContext();
                try {
                    value.value = ctx.lookup(r.name());
                } catch (NamingException ex) {

                    value.value = ctx.lookup("java:comp/env/" + r.name());
                }

            return new Injectable<Object>() {
                public Object getValue() {
                    return value.value;
                }
            };
        }
    });
}
}  

7) Use an embedded EJB container.

E.g., http://openejb.apache.org. This is pretty heavy, and I expect it's going to be messy to get working. (In fact, what started me down the "Jetty + Jersey" route was a bug in GlassFish Embedded around security logins. I also looked at other Java EE 6 application containers like JBoss AS, but each had problems in embedded mode, with limited user community support.)

8) Use a third-party IoC library like Spring or Guice.

Spring is apparently commonly used for solving these kinds of problems (injecting mocks when unit testing), but I wanted to avoid having to learn another big set of APIs - pure Java EE has been enough of a challenge! But I'm game if it's the best solution. I haven't yet looked carefully into Spring or Guice.

Have you used any of these successfully? Any other solutions you like? I'm really looking forward to your advice on this. Thanks in advance -- matt

like image 753
Matthew Cornell Avatar asked Jul 19 '12 13:07

Matthew Cornell


1 Answers

Since you are using Netbeans, give this a try:

Using the Embedded EJB Container to Test Enterprise Applications

The tutorial uses an embedded Glassfish container and injects an EJB that encapsulates the EntityManager (similar to what you described in your first option).

like image 183
Andre Avatar answered Nov 11 '22 06:11

Andre