Skip to main content

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [List Home]
[jetty-users] hooking into @EJB or @PersistenceContext injection in JAX-RS unit tests

Hi Folks. 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



-- 
Matthew Cornell | matt@xxxxxxxxxxxxxxxxxx | 413-626-3621 | 34
Dickinson Street, Amherst MA 01002 | matthewcornell.org |
thinktrylearn.com


Back to the top