Add Guice dependency Injection to Servlets configured in web.xml - java

I'm using Guice in a project for dependency injection into Servlets. We have a ServletModule that defines the serve().with() configuration. This all works fine.
I now need to be able to include a webapp with servlets defined in the web.xml. All of the documentation says add GuiceFilter to web.xml and then use the programattic config in the ServletModule, but I'm wondering if it's possible to get Guice to inject dependencies into servlets configured in web.xml?
I want to be able to define servlets in web.xml eg:
<servlet>
<servlet-name>test</servlet-name>
<servlet-class>TestServlet</servlet-class>
<servlet>
When the servlet is created the container just called the no-arg constructor. Can this behaviour be changed so that Guice creates the servlet and injects at creation time?

injector.injectMembers(obj) explicitly sets #Inject fields:
#Inject Foo foo;
TestServlet()
{
// wherever your injector instance is defined
...getInjector().injectMembers(this);
}
The docs recommend getInjector().getMembersInjector().injectMembers()
though I haven't used this.

You probably need to use Guice as a service locator in TestServlet.
TestServlet(Foo foo){ ... } // please inject foo!
TestServlet()
{
this( MyGuiceServletConfig.injector.getInstance(Foo.class) );
}
--
public class MyGuiceServletConfig extends GuiceServletContextListener {
public static final Injector = Guice.createInjector(new MyServletModule());
#Override
protected Injector getInjector() {
return injector;
}
}
(they say DI frameworks are not intrusive :)

Related

Dependency Injection using CDI with Jersey with/without abstract binding

First of all, this is not opinionated question and I have read most of related questions in SO. I am seeking advise if below implemented solution is the right approach/method.
I have read many tutorials on how to implement DI in a jersey-based webapp and most of them recommend that its a must to create a beans.xml in WEB-INF/* in order to enable CDI but, I wonder if using Jersey's AbstractBinder achieve the same result?
I have a jersey-webapp that has the following in web.xml
<servlet>
<servlet-name>Test Jersey</servlet-name>
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>javax.ws.rs.Application</param-name>
<param-value>com.test.config.AppConfig</param-value>
</init-param>
And com.test.config.AppConfig as follow
public class AppConfig extends ResourceConfig {
public AppConfig() {
AbstractBinder binder = new AbstractBinder() {
#Override
protected void configure() {
bind(Impl.class).to(Interface.class).in(Singleton.class);
}
};
register(binder);
register(MultiPartFeature.class);
packages("..."); //packages
}
}
and then I annotate the interfaces and the implementation gets injected
#Inject
private SomeInterface someInterface;
Above works just fine. Whatever that I want to be injected, I include it in the binder and then specify an injection point and it gets injected.
There is no beans.xml in WEB-INF/ directory and I wonder if using AbstractBinder inside AppConfig that extends ResourceConfig eliminate the need to declare beans.xml ?
Adding beans.xml would probably enable scanning of classes that would pave the way for DI when we annotate classes with #Component or #ManagedBean.
Regardless, I would be happy to hear your feedback/advise/suggestions/recommendations on whether to
Stick with existing solution (shown above) for DI in Jersey because .... ?
Switch to annotating classes (that needs to be injected) and use annotation-discovery of beans.xml because ... ?
Jersey uses HK2 by default, is it worth using a different DI
container or HK2 is good enough?
What is your view on Jersey's Spring DI in comparison with JavaEE 6
CDI only for DI purposes?
There are many tutorials stating that CDI is not supported by Tomcat? but worked above using AbstractBinder and I guess its because I programmatically bind? Any comments.
I do not have a clear answers and possibly there doesn't exist a correct one. Not least because Weld SE support was introduced in version 2.15 of Jersey and this certainly not without any reason. But I would like to give it a try:
The shown solution works fine for non-complex project structures but declaring every single binding might not be the best solution
You don't need to use beans.xml. Annotations and auto-binding works fine with some additional effort (see below)
I'm not sure about this, but would say Weld seems to be more advanced. And of course you could mix CDI with some effort.
(no answer here)
Here the example, which I think could be interesting:
Dependencies (Maven):
<dependency>
<groupId>org.glassfish.hk2</groupId>
<artifactId>hk2-metadata-generator</artifactId>
<version>2.5.0-b05</version> <!-- HK2 version int. used by Jersey 2.23.2 -->
</dependency>
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-servlet</artifactId>
</dependency>
An application event listener:
import org.glassfish.hk2.api.*;
import org.glassfish.jersey.server.*;
#Provider
public class ApplicationListener implements ApplicationEventListener {
#Inject ServiceLocator serviceLocator;
#Override
public void onEvent(ApplicationEvent event) {
switch (event.getType()) {
case INITIALIZATION_FINISHED:
onInitFinished();
break;
case DESTROY_FINISHED:
case INITIALIZATION_APP_FINISHED:
case INITIALIZATION_START:
case RELOAD_FINISHED:
default:
break;
}
}
#Override
public RequestEventListener onRequest(RequestEvent requestEvent) { return null; }
public void onInitFinished() {
populate(serviceLocator);
}
private void populate(ServiceLocator serviceLocator) {
DynamicConfigurationService dcs = serviceLocator.getService(DynamicConfigurationService.class);
Populator populator = dcs.getPopulator();
try {
populator.populate();
} catch (IOException | MultiException e) {
throw new MultiException(e);
}
}
}
A contract:
import org.jvnet.hk2.annotations.Contract;
#Contract
public interface ExampleService {
void executeSomething();
}
One or more services:
import javax.inject.Named;
import org.jvnet.hk2.annotations.Service;
#Service
#Named("bar")
public class BarService implements ExampleService {
#Override
public void executeSomething() { /* doBar */ }
}
Usage:
#Path("/")
public class TestResource {
// either ...
#Inject
#Named("bar")
private ExampleService bar;
// or ...
#Inject
private IterableProvider<ExampleService> services;
}
Just an option to get rid of beans.xml (which I've never used or have seen) or declaration within ResourceConfig, but it might find interested parties :)
Additionally, it seems like Jersey 3.0 is comming ^^
Have a nice day!

Jersey global ExceptionHandler doesn't work

i'm trying to create a global ExceptionHandler in this way:
#Provider
public class MyExceptionHandler implements ExceptionMapper<Exception>
{
#Override
public Response toResponse(Exception exception)
{
return Response.status(Status.BAD_REQUEST).entity("TEST").build();
}
}
in order to catch all the exception in the controller:
#Path("/policy")
#GET
public void getPolicy(#DefaultValue("no") #QueryParam("_id") String _id) throws Exception{
int a = Integer.parseInt("test"); // it generates NumberFormatException
}
the NumberFormatException isn't handled by MyExceptionHandler.
Where i'm wrong?
Thanks!
If you use jersey < 2.5 it could be a bug while component package scanning:
#see: https://java.net/jira/browse/JERSEY-2175
Otherwise, you properly missed to add the package of your MyExceptionHandler for component scans. The #Provider annotation means, that the implementation "should be discoverable by JAX-RS runtime during a provider scanning phase", but in fact it does'nt did it like we thought. Probably a bug, but i'm not sure.
Meaning: Afaig, you have to register package or mapper by yourself - otherwise it will not work for now!
Note: I did not tested this with servlet 3.x without Application subclass and basic web.xml #see 4.7.2.3.1. JAX-RS application without an Application subclass link
Maybe also interesting:
Disable MBW, MBR, ExceptionMapper automatic registration via META-INF/services
The jersey-metainf-services (Jersey extension module enabling automatic registration of JAX-RS providers (MBW/MBR/EM) via META-INF/services mechanism) was added #since 2.9.x
For the sake of completeness:
You can do the registration by using ResourceConfig.packages(String...packages):
import org.glassfish.jersey.server.ResourceConfig;
public class MyResourceConfig extends ResourceConfig {
public MyResourceConfig() {
packages(new String[] {
"path.to.foo",
"path.to.bar"
});
}
}
Alternative in web.xml servlet config
...for packages:
<init-param>
<param-name>jersey.config.server.provider.packages</param-name>
<param-value>path.to.foo,path.to.bar</param-value>
</init-param>
...for classes
<init-param>
<param-name>jersey.config.server.provider.classnames</param-name>
<param-value>
path.to.foo.MyExceptionHandler,
path.to.bar.FooBar
</param-value>
</init-param>
It's worth checking that the ExceptionMapper is registered as a singleton. Registering a component as a singleton depends on your framework. Here's a couple of examples:
Guice
In your injector instance --
Guice.createInjector(new JerseyServletModule() {
#Override
protected void configureServlets() {
/* ... this is where modules are installed and where
* component dependencies are binded
*/
bind(MyExceptionHandler.class).in(Scopes.SINGLETON);
/* configure filters etc, omitted for brevity */
}
}
Spring
In your Spring config
<bean id="exceptionMapper" class="my.package.MyExceptionHandler" scope="singleton" />
Or using component scanning, mark your ExceptionMapper as #Component --
Config:
<context:component-scan base-package="my.package"/>
Annotated class:
#Component
#Provider
public class MyExceptionHandler implements ExceptionMapper<Exception> {
}

Guice and Wicket: using SessionScoped injections

I have a working Wicket [v6] application with Guice [v3] - I have used dependency injection for repository operations right now and I want to expend it into using services that are session scoped (one per user's session). I have read through official documentation, various blog posts and questions here, but I am not sure if I am using the correct approach.
I have two questions:
1. Do I use the correct way?
2. Do I need anything special to run TestNG tests on classes that rely on SessionScoped injections?
My setup:
web.xml:
<filter>
<filter-name>guiceFilter</filter-name>
<filter-class>com.google.inject.servlet.GuiceFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>guiceFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<listener>
<listener-class>com.xxx.CustomServletConfig</listener-class>
MyApplication init:
#Override
protected void init()
{
super.init();
getResourceSettings().setResourcePollFrequency(null);
getMarkupSettings().setStripWicketTags(true);
getDebugSettings().setDevelopmentUtilitiesEnabled(true);
GuiceComponentInjector injector = new GuiceComponentInjector(this, new WebModule(), new GuiceModule());;
}
CustomServletConfig:
public class CustomServletConfig extends GuiceServletContextListener {
#Override
protected Injector getInjector() {
return Guice.createInjector(new GuiceModule(), new WebModule());
}
WebModule:
public static class WebModule extends ServletModule {
#Override
protected void configureServlets() {
bind(WebApplication.class).toProvider(WicketGuiceAppProvider.class).asEagerSingleton();
bind(IUserService.class).to(UserService.class).in(ServletScopes.SESSION);
Map<String, String> params = new HashMap<String, String>();
params.put(WicketFilter.FILTER_MAPPING_PARAM, "/*");
filter("/*").through(WicketGuiceFilter.class, params);
}
}
In an example page I have:
#Inject
IUserService userService
...
userService.doSomething
At userService.doSomething during unit test I am getting Guice OutOfScopeException, pointing to my bindings in ServletModule:
Error in custom provider, com.google.inject.OutOfScopeException?: Cannot access scoped object. Either we are not currently inside an HTTP Servlet request, or you may have forgotten to apply com.google.inject.servlet.GuiceFilter? as a servlet filter for this request.
Is my configuration ok and I need to run unit tests differently (I am simply launching my application with WicketTester), or is my design faulty?
This is very common fault.
All entities in ServletScopes or RequestScopes should be passed in as Providers.
So your code should be:
#Inject
Provider<IUserService> userServiceProvider
public IUserService getUserService() {
userServiceProvider.get();
}
Why so?! Everything fine as long as you use it in Stage.DEVELOPMENT and the parent class is not created eagerly. If you bind you parent class as asEagerSingleton or switch to Stage.PRODUCTION your classes start to be created eagerly at startup time. Otherwise they are created in lazy way only when they are accessed (very likely during first request).
And there your problem comes to scene. Your WebApplication is initialized eagerly at startup time. Then guice tries to inject all child dependencies and found IUserService which is field injection in SessionScope. The problem is you are currently not inside GuiceFilter and there is no request, so guice cannot determine current session or create new one. So these scopes cannot be reached. You are currently in your ContextListener and your application is instantiated eagerly. Everything could be fine if you would use just Singleton instead of asEagerSingleton because of lazy loading.
Anyway, passing Session and Request scoped objects as Providers is the best practice. You can learn more about Providers here and Scopes here (there is also nice table with eager vs. lazy loading comparsion)

Jersey ResourceFilter and spring

I am using Jersey 1.17 and Jersey-Spring 1.8.
I am trying to get a jersey ResourceFilter to install correctly. I want this filter to be application-wide.
I have defined the ResourceFilter implementation as a bean in spring (defined in XML) and annotated it with the #Provider annotation.
#Provider
public class ContainerResourceFilterTest implements ResourceFilter
{
public ContainerRequestFilter getRequestFilter()
{
return null; //TODO
}
public ContainerResponseFilter getResponseFilter()
{
return null; //TODO
}
}
But it doesn't get hit when I send a request in.
If I use the #ResourceFilters annotation on a particular resource then it works as expected, but I don't want to do that on every single class.
How do I register a filter that is application wide?
Answering my own questions.
Doing some further reading of the jersey source code I have found that the #Provider annotation doesn't do anything for ResourceFilterFactory or ContainerRequest/Response. These can only be registered in one of the following 2 ways
Using the META-INF/services
Using the init params of the servlet, for example in web.xml
<init-param>
<param-name>com.sun.jersey.spi.container.ResourceFilters</param-name>
<param-value>com.my.path.ResourceFilterFactory</param-value>
</init-param>
Not sure why the code doesn't inspect the registered spring beans for #Providers that implement the appropriate interfaces.
Worth noting that registering a class that implements ResourceFilter does not work for either of the above methods. You must do it for ResourceFilterFactory and have that return ResourceFilter implementations.
The good news is once you have made jersey aware of the classes that need to be registered it calls into spring to provide those classes so auto-wiring etc works as per usual.
Jersey doesn't scan the classpath for everything - ResourceFilters are one of the things it won't find automagickally. If you're using a ResourceConfig class, register your ResourceFilter class:
#ApplicationPath("/myapp")
public class MyApplication extends ResourceConfig {
public MyApplication() {
packages(getClass().getPackage().toString());
register(ContainerResourceFilterTest.class);
}
}
Jersey will then scan that class for the filter interface, create an instance (all filters are singleton scope according to their docs) and insert it into the Jersey filter chain.

Inject an EJB into JAX-RS (RESTful service)

I'm trying to inject a Stateless EJB into my JAX-RS webservice via annotations. Unfortunately the EJB is just null and I get a NullPointerException when I try to use it.
#Path("book")
public class BookResource {
#EJB
private BookEJB bookEJB;
public BookResource() {
}
#GET
#Produces("application/xml")
#Path("/{bookId}")
public Book getBookById(#PathParam("bookId") Integer id)
{
return bookEJB.findById(id);
}
}
What am I doing wrong?
Here is some information about my machine:
Glassfish 3.1
Netbeans 6.9 RC 2
Java EE 6
Can you guys show some working example?
I am not sure this is supposed to work. So either:
Option 1: Use the injection provider SPI
Implement a provider that will do the lookup and inject the EJB. See:
#EJB injection.
Example for com.sun.jersey:jersey-server:1.17 :
import com.sun.jersey.core.spi.component.ComponentContext;
import com.sun.jersey.core.spi.component.ComponentScope;
import com.sun.jersey.spi.inject.Injectable;
import com.sun.jersey.spi.inject.InjectableProvider;
import javax.ejb.EJB;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.ws.rs.ext.Provider;
import java.lang.reflect.Type;
/**
* JAX-RS EJB Injection 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;
}
}
}
Option 2: Make the BookResource an EJB
#Stateless
#Path("book")
public class BookResource {
#EJB
private BookEJB bookEJB;
//...
}
See:
How to Combine REST Services with EJB 3.1
EJB 3.1 And REST - The Lightweight Hybrid
Option 3: Use CDI
#Path("book")
#RequestScoped
public class BookResource {
#Inject
private BookEJB bookEJB;
//...
}
See:
Injecting an EJB from a jar into a jax-rs class in a war
This thread is rather old, nevertheless i fought the same problem just yesterday. Here is my solution:
Just make the BookResource a managed bean through #javax.annotation.ManagedBean at class level.
For this to work you need to enable CDI with a beans.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/beans_1_0.xsd">
</beans>
This file needs to be in WEB-INF if the BookResource is part of a war file. If the BookResource is packaged with the ejbs put it into META-INF.
If you want to use #EJB you're done. If you want to inject the EJB through #Inject than a beans.xml must be put into the ejbs jar file into META-INF as well.
What you're doing: You're just telling the container that the resource should be container managed. Therefor it supports injection as well as lifecycle events. So you have your business facade without promoting it to an EJB.
You don't need to extend javax.ws.rs.core.Application for this to work. BookResource is as a root resource automatically request scoped.
Tested with Glassfish 3.1.2 and a maven project.
Happy coding.
You shall be able to do injection in JAX-RS resource without making it EJB or CDI component. But you have to remember that your JAX-RS resource must not be singleton.
So, you setup your application with this code. This makes BookResource class per-request JAX-RS resource.
#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;
}
}
With this setup, you are letting JAX-RS to instantiate BookResource for you on per-request basis and also inject all the required dependencies. If you make BookResource class singleton JAX-RS resource, this is, you put in getSingletons
public Set<Object> getSingletons() {
singletons.add(new BookResource());
return singletons;
}
then, you created instance which is not managed by JAX-RS runtime and nobody in container cares to inject anything.
Unfortunately, my answer is too long for a comment, so here goes. :)
Zeck, I hope that you are aware of what exactly you are doing by promoting your bean to an EJB, as suggested by Pascal. Unfortunately, as easy as it is nowadays with Java EE to 'make a class an EJB', you should be aware of the implications of doing so. Each EJB creates overhead along with the additional functionality it provides: they are transaction aware, have their own contexts, they take part in the full EJB life cycle, etc.
What I think you should be doing for a clean and reusable approach is this: extract the access to your servers services (which hopefully are accessed through a SessionFacade :) into a BusinessDelegate. This delegate should be using some kind of JNDI lookup (probably a ServiceLocator - yes, they are still valid in Java EE!) to access your backend.
Okay, off the record: if you really, really, really need the injection because you do not want to write JNDI access manually, you could still make your delegate an EJB, although it ... well, it just feels wrong. :)
That way at least it will be easy to replace it later with something else if you do decide to switch to a JNDI lookup approach...
I was trying to do the exact same thing. I'm using EJB 3.1 and have a deployed my app as an EAR with separate EJB project. As Jav_Rock pointed out, I use context lookup.
#Path("book")
public class BookResource {
#EJB
BookEJB bookEJB;
public BookResource() {
try {
String lookupName = "java:global/my_app/my_ejb_module/BookEJB";
bookEJB = (BookEJB) InitialContext.doLookup(lookupName);
} catch (NamingException e) {
e.printStackTrace();
}
}
#GET
#Produces("application/xml")
#Path("/{bookId}")
public Book getBookById(#PathParam("bookId") Integer id) {
return bookEJB.findById(id);
}
}
See the link below for very useful JNDI look up tips
JNDI look up tips
Arjan is right. I created another class to initialize the EJB instead of creating a bean for RS
#Singleton
#LocalBean
public class Mediator {
#EJB
DatabaseInterface databaseFacade;
to avoid null pointer with:
#Path("stock")
public class StockResource {
#EJB
DatabaseInterface databaseFacade;
...
it actually works on GF
I have the same problem, and I solved it calling te EJB by a context lookup (the injection was impossible, I had the same error NullPointerException).

Categories