@EJB injection

Jersey has a simple injection provider SPI for injecting instances for annotated fields, method/constructor parameters or setter methods. So it is possible to plug-in your own injection functionality.

Out of the box there is currently no EE-based functionality supported in Jersey but using the injection provider SPI it is very easy to have basic support for @EJB in one small class.

If you have the following resource:

@Path("foo")
public class FooResource {
    @EJB private FooSessionRemote fooSessionRemoteBean;
    
    @GET
    @ProduceMime("text/plain")
    public String get() throws Exception {
        return fooSessionRemoteBean.fooMethod();
    }
}

then the field fooSessionRemoteBean should get injected with an instance of the remote EJB interface.

The following provider class supports this behaviour:

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

    public Scope getScope() {
        return Scope.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(HttpContext c) {
                    return o;
                }                    
            };            
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
}

The EJB reference is looked up using JNDI then an Injectable instance is returned with that reference. This is efficient for per-request resources as the JNDI look up is only performed once. Since the above class is annotated with @Provider it will get picked up and treated like any other provider.

Obviously this could do with some polish to fully implement @EJB injection functionality but i think this example is instructive how one could support other annotations.

Comments:

Thanks for the write-up. I implemented this in a project, and it was an elegant solution.

Posted by Dennis on July 01, 2008 at 10:13 AM CEST #

I need to thank you for this post as well. After spending countless hours of trying to inject @EJB into a REST resource using the Jersey 0.7 included in Netbeans 6.1, this post led me to find version Jersey 0.8 which supports your implementation. You may want to slip that hint in somewhere btw ;o)

I'm working on a blog now with my experiences and expect to have it up very soon. Hopefully that will help others avoid the pain I've been through. I'll be back when its on-line. In the meantime, keep up the good work, and once again... thanks!

Posted by Chris Hansen on July 06, 2008 at 12:20 PM CEST #

Hi Chris,

Thanks for your patience and persistence. The best way to share the pain or ask questions is to send email to users@jersey.dev.java.net.

Jersey currently does not support full EE integration. JAX-RS will be synced with EE6.

Posted by Paul Sandoz on July 21, 2008 at 04:56 AM CEST #

It would be good have the JNDI lookup flexible, and not only for Glassfish. In JBoss, for instance, there is this name composition:
<earName>/<className>Bean/local e.g. foo/CustomerBean/local or foo/CustomerBean/remote (for remote interfaces)

Thus your provider was easy to adapt by changing the argument value for the JNDI lookup method.

Posted by Juergen Zimmermann on July 27, 2008 at 03:31 AM CEST #

Hi Juergen,

Thanks for the feedback. I understand that the @EJB interface supports a number of parameters that are not implemented in the example. Did you implement some or all of those to support what you required? if so would you be interested in contributed back such a component to Jersey as it appears that a number of people have found this example useful?

Posted by Paul Sandoz on July 28, 2008 at 03:39 AM CEST #

I would like to use dependency injection in my current application but most of my resources are created by resource methods and I have to initialize them manually that leads to the propagating dependency antipattern.

It would be interesting to know the reasoning behind the following sentence from the spec: Objects returned by sub-resource locators are expected to be initialized by their creator and field and bean properties are not modified by the implementation runtime.

Posted by Taras Puchko on July 29, 2008 at 12:42 PM CEST #

Hi Taras,

In Jersey you can return the Class of a resource class and that will get instantiated with dependency injection by the runtime.

Alternatively if you need to get an instance of a resource class with dependency injection and then modify the the state of the resource class then you can use the Jersey specific class:

com.sun.jersey.api.core.ResourceContext

Inject the above into your root resource class and use the getResource method in sub-locator methods.

Injection is not performed on a instance returned by the sub-resource locator because the runtime does not know what the life-cycle and use of fields on the instance.

Paul.

Posted by Paul Sandoz on July 30, 2008 at 03:09 AM CEST #

Hi Paul,

I pass parameters into subresources and I'd like to use setter or field enjection instead of a service locator,
so an option to register a custom interseptor for processing subresources would suit me much better:

public class MyResourceInterceptor implements ResourceInterceptor {

private Injector injector;

public ResourceInterceptor(@Context ServletContext context) {
injector = (Injector) context.getAttribute(Injector.class.getName());
}

public Object intercept(Object resource) {
injector.injectMembers(resource);
return resource;
}
}

I've got deeply nested resources, e.g. RootResource returns UsersResource that returns UserResource:

public class UsersResource {

@Path("{id}")
public UserResource getUserResource(@PathParam("id") Long id) {
return new UserResource(id);
}
}

public class UserResource {

@Inject
private DataService dataService;
private Long id;

public UserResource(Long id) {
this.id = id;
}

@GET
public String get() {
return dataService.getUserName(id);
}
}

Currently I have to put the injection code into the base class for all my resources.

Thank you,
Taras.

Posted by Taras Puchko on July 30, 2008 at 07:57 AM CEST #

How about having an InjectableContext so that one could do:

@Context InjectableContext ic;

@Path("{id}")
public UserResource getUserResource(@PathParam("id") Long id) {
return ic.inject(new UserResource(id));
}

IMHO is clearer than an interceptor approach.

A declarative approach is to annotation the sub-resource locator saying "perform DI" on the returned instance, for example the following would be equivalent to the above:

@PerformInject // need better name
@Path("{id}")
public UserResource getUserResource(@PathParam("id") Long id) {
return new UserResource(id);
}

It should be easier to support both of the above. And we could have a configuration value to perform DI by default on all sub-resource locators without such an annotation.

Would that work for you?

Posted by Paul Sandoz on July 30, 2008 at 09:06 AM CEST #

The approach with InjectableContext reminds me the service locator pattern I'm trying to avoid.
But the second alternative is ideal especially in conjunction with some IoC container.

BTW, if I understand the specification correctly, ContextResolver can also be used to inject application-specific
objects but it does not work for me while InjectableProvider works perfectly.

Posted by Taras Puchko on July 30, 2008 at 11:18 AM CEST #

OK, i will plan to implement the three aspects i described previously for the 0.9 release.

W.r.t. ContextResolver it is not designed to be a general mechanism for injection purposes for use with annotations and life-cycle. It is a simple way to get access to some context for a particular class given some object (e.g. a JAXBContext for a JAXB object or a JAXB Marshaller for a JAXB object).

Posted by Paul Sandoz on July 31, 2008 at 04:43 AM CEST #

It would be great! I don't think I will use InjectableContext but @PerformInject or @PostInject or whatever is quite sufficient.

I understand that ContextResolver is limited in functionality but it's the only standard way of doing injection, so I hope it will work in Jersey too.

Thank you for the great framework,
Taras.

Posted by Taras Puchko on July 31, 2008 at 05:37 AM CEST #

Hi Paul,

I am using the Jersey Framework for some of my interfaces.

I am trying to use the @EJB annotation using the above method. When I look at the logs, it tells me that the provider class has been found. But when I invoke a method on the fooSessionRemoteBean, it throws a null pointer exception. I am using the Jersey 0.8. Is there anything extra that needs to be done apart from what is provided by you above? (I have placed the above code as is in my implementation).

Manjunath.

Posted by Manjunath on August 07, 2008 at 05:34 PM CEST #

Hi Manjunath,

It is hard to say without more information on your example. It sounds like the EJB cannot be found. Perhaps you should verify that the "getInjectable" is getting called. I suspect the call to "ic.lookup" is returning null.

I recommend we continue this conversation over email at:

users@jersey.dev.java.net

as it is easier to track and others can help and benefit from the responses.

Paul.

Posted by Paul Sandoz on August 08, 2008 at 03:54 AM CEST #

Hi Paul,
the EJBProvider approach is an interesting one. How would it work from within unit tests, i.e. unit tests that use an embedded glassfish or grizzly?

Thanks
Thomas

Posted by Thomas on November 25, 2008 at 10:30 PM CET #

Hi Tomas,

In general I think it would work under the same constraints as injecting an EJB for on a servlet class. Namely it requires an EE compliant container. Using Grizzly will not work in this respect but using embedded GF might. I recommend emailing:
mailto:users@glassfish.dev.java.net

and asking about the status of embedded GF and EJB support.

Alternatively, you might be able to mock up something by registering a instance via JNDI under the same JNDI name. But i am not sure if this is the same way injection is performed for Servlet as for this example.

Paul.

Posted by Paul Sandoz on November 26, 2008 at 02:10 AM CET #

I would like to thank you for this useful tip. I've noted that with the latest release (jersey 1.0.1) there was some changes in packages and class names so this is a small update of your class that handle this changes.
Best regards
domenico

import com.sun.jersey.spi.inject.Injectable;
import com.sun.jersey.spi.inject.InjectableProvider;
import com.sun.jersey.core.spi.component.ComponentContext;
import com.sun.jersey.core.spi.component.ComponentScope;
import java.lang.reflect.Type;
import javax.ejb.EJB;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.ws.rs.ext.Provider;

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

Posted by domenico giffone on December 23, 2008 at 05:23 AM CET #

I've implemented domenico's modified code and I'm having trouble getting the appropriate principal for an explicit connection to an EJB. I modified the code slightly:

Properties props = new Properties();
props.put(Context.SECURITY_PRINCIPAL, "weblogic");
props.put(Context.SECURITY_CREDENTIALS, "weblogic");

Context ic = new InitialContext(props);
for (Object o : ic.getEnvironment().keySet()) {
String s = String.format("ENV key=%s val=%s", o.toString(), ic.getEnvironment().get(o));
System.out.println(s);
}

String simpleName = c.getSimpleName();
System.out.println("Looking up: " + simpleName);
final Object o = ic.lookup(simpleName);

The System.out.println of the context's env is:

ENV key=java.naming.factory.initial val=weblogic.jndi.WLInitialContextFactory
ENV key=java.naming.factory.url.pkgs val=weblogic.jndi.factories:weblogic.corba.j2ee.naming.url:weblogic.jndi.factories:weblogic.corba.j2ee.naming.url
ENV key=java.naming.security.principal val=weblogic
ENV key=java.naming.security.credentials val=weblogic

but when I execute a remote method that checks the principal for appropriate permissions, it indicates that the principal is "<anonymous>" even though the context indicates otherwise. Does Jersey currently use the javax.ws.rs.core.SecurityContext java.security.Principal returned from the getUserPrincipal method?

Posted by mark rabick on January 28, 2009 at 08:35 AM CET #

Hi Mark,

When using Servlet, Jersey implements the SecurityContext.getUserPrincipal by deferring to HttpServletRequest.getUserPrincipal.

However, Jersey does nothing with the java.security.Principal. Just as if you wrote a simple servlet to do the same thing with InitialContext.

Perhaps there is some configuration missing in the web.xml?

Have you tried a very simple application using a basic servlet to see if you can reproduce it?

Paul.
P.S. it might be easier to discuss further on users@jersey.dev.java.net.

Posted by Paul Sandoz on January 28, 2009 at 09:04 AM CET #

Post a Comment:
  • HTML Syntax: NOT allowed
About

sandoz

Search

Archives
« April 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today