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)
Related
I need to get some configuration and connect to external resources/objects/systems somewhere and store it in application scope.
I can see two ways to setup my application:
Overriding the init() in the existing servlets and required code there and keeping all constructed objects inside that same servlet.
Having some kind of an initialisation servlet and using its init() to do the work. Then storing created objects in ServletContext to share it with my other servlets.
Which out of above is better approach? Is there any better way to share objects between servlets? Calling them directly from one another or so...?
None of both is the better approach. Servlets are intended to listen on HTTP events (HTTP requests), not on deployment events (startup/shutdown).
CDI/EJB unavailable? Use ServletContextListener
#WebListener
public class Config implements ServletContextListener {
public void contextInitialized(ServletContextEvent event) {
// Do stuff during webapp's startup.
}
public void contextDestroyed(ServletContextEvent event) {
// Do stuff during webapp's shutdown.
}
}
If you're not on Servlet 3.0 yet and can't upgrade (it would be about time because Servlet 3.0 was introduced more than a decade ago), and thus can't use #WebListener annotation, then you need to manually register it in /WEB-INF/web.xml like below:
<listener>
<listener-class>com.example.Config</listener-class>
</listener>
To store and obtain objects in the application scope (so that all servlets can access them), use ServletContext#setAttribute() and #getAttribute().
Here's an example which lets the listener store itself in the application scope:
public void contextInitialized(ServletContextEvent event) {
event.getServletContext().setAttribute("config", this);
// ...
}
and then obtain it in a servlet:
protected void doGet(HttpServletRequest request, HttpServletResponse response) {
Config config = (Config) getServletContext().getAttribute("config");
// ...
}
It's also available in JSP EL by ${config}. So you could make it a simple bean as well.
CDI available? Use #Observes on ApplicationScoped.class
import jakarta.enterprise.context.ApplicationScoped; // And thus NOT e.g. jakarta.faces.bean.ApplicationScoped
#ApplicationScoped
public class Config {
public void init(#Observes #Initialized(ApplicationScoped.class) ServletContext context) {
// Do stuff during webapp's startup.
}
public void destroy(#Observes #Destroyed(ApplicationScoped.class) ServletContext context) {
// Do stuff during webapp's shutdown.
}
}
This is available in a servlet via #Inject. Make it if necessary also #Named so it's available via #{config} in EL as well.
Noted should be that this is new since CDI 1.1. If you're still on CDI 1.0 and can't upgrade, then pick another approach.
In case you're curious how to install CDI on a non-JEE server such as Tomcat, head to: How to install and use CDI on Tomcat?
EJB available? Consider #Startup#Singleton
#Startup
#Singleton
public class Config {
#PostConstruct
public void init() {
// Do stuff during webapp's startup.
}
#PreDestroy
public void destroy() {
// Do stuff during webapp's shutdown.
}
}
This is available in a servlet via #EJB. The difference with other approaches is that it's by default transactional and in case of #Singleton also read/write locked. So if you would ever need to inject a random EJB (e.g. #Stateless) into a #WebListener or an #ApplicationScoped then you could basically as good merge both into a single #Startup #Singleton.
See also:
How to run a background task in a servlet based web application?
ServletContainerInitializer vs ServletContextListener
How do I force an application-scoped bean to instantiate at application startup?
Servlet 3.0-enabled containers allows us to skip the web.xml servlet configuration and automatically scan your code for resources and providers once you extend javax.ws.rs.core.Application, annotate it with #ApplicationPath and do not override the getClasses() method. (hope I got all of that right :\)
At the moment I am using the Jersey implementation and securing resource methods using #RolesAllowed annotations. For this I need to register the org.glassfish.jersey.server.filter.RolesAllowedDynamicFeature Provider class, however, the only ways I'm aware of to do this is either to:
Register the class in the getClasses() method of my Application class (which, I think, will cause the Servlet 3.0 container NOT to auto-scan)
Continue to use the web.xml Jersey servlet setup with
<init-param>
<param-name>jersey.config.server.provider.classnames</param-name>
<param-value>org.glassfish.jersey.server.filter.RolesAllowedDynamicFeature</param-value>
</init-param>
Now the context behind this question is that I might have to switch to using RESTeasy and if I use option 1 it adds a Jersey dependency in the code and the code is no longer generic.
How do I write my code to use security annotations while maintaining generic JAX-RS code that could be deployed to another Servlet 3.0 JAX-RS implementation?
One option is to use a javax.ws.rs.core.Feature (a JAX-RS standard class). You can register any components there, and then annotate the class with #Provider, and it will be picked up like any other #Provider or #Path annotated class
#Provider
public class MyFeature implements Feature {
#Overrride
public boolean configure(FeatureContext context) {
context.register(RolesAllowedDynamicFeature.class);
}
}
Do note that since you are using the Jersey feature, your app is no longer implementation independent, so you might as well use Jersey all the way. For one, Jersey does not recommend scanning the class-path, which is the affect of doing what you are doing. Instead Jersey has a mechanism that allows you to recursively scan a package (and its sub-packages). So you could instead do
#ApplicationPath("..")
public class AppConfig extends ResourceConfig {
public AppConfig() {
packages("the.packages.to.scan");
register(RolesAllowedDynamicFeature.class);
}
}
Note that ResourceConfig is a sub-class of Application
See Also:
When to Use JAX-RS Class-path Scanning Mechanism
Sevlet Based Deployment - Servlet 3.x Container
Note:
If you wanted to stick to the classpath scanning mechanism, and wanted to keep the project independent of any Jersey dependencies, you could also override Map<String, Object> getProperties() in the Application class. In the returned Map, you could add the property that you would otherwis have added in the web.xml
#Override
public Map<String, Object> getProperties() {
Map<String, Object> props = new HashMap<>();
props.put("jersey.config.server.provider.classnames",
"org.glassfish.jersey.server.filter.RolesAllowedDynamicFeature");
return props;
}
But even though the source code is implementation independent, the application is still dependent on the Jersey roles feature. If you decided you wanted to port, you would still need a replacement for the feature.
If you wanted to stay completely independent, you could implement the feature yourself. It's not all that complicated. You can check out the source code for RolesAllowedDynamicFeature. If you decide to try and implement the same, just annotate your implementation class with #Provider, and it should get picked up.
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 :)
I have a filter where I am dinamically mapping servlet classes:
#Override
public void init( FilterConfig filterConfig ) throws ServletException {
servletContext = filterConfig.getServletContext();
File directory = getConventionDirectory();
FileSystemInspector fileInspector = new FileSystemInspector();
Set<ActionInfoData> actions = fileInspector.getActions( directory );
for ( ActionInfoData action : actions ) {
servletContext
.addServlet( action.getServletName(), action.getClassName() )
.addMapping( action.getServletMapping() );
}
}
Then when I access a given mapping the EJB is not injected.
#EJB
private I18nManager i18nManager;
#Override
protected void doGet( HttpServletRequest request, HttpServletResponse response )
throws ServletException, IOException {
I18nManager i18n = i18nManager; //null
}
If I manually create a mapping in the web.xml the given EJB is working in that servlet.
It makes me wonder if the fact I am registering the servlets at runtime the container does not consider those servlets as managed.
If that is the case what is the proper way to inject the EJBs into my servlets without changing the way they are dinamically registered via filter?
Is via JNDI the only way to inject my EJBs?
EDIT 1:
I have tried to implement a ServletContextListener class as suggested by "Will" using the following code in the web.xml:
<listener>
<listener-class>com.megafone.web.filter.convention.InitServlet</listener-class>
</listener>
And the relevant part of the implementation:
...
#Override
public void contextInitialized( ServletContextEvent sce ) {
ServletContext servletContext = sce.getServletContext();
FileSystemInspector fileInspector = new FileSystemInspector();
Set<ActionInfoData> actions = fileInspector.getActions( getConventionDirectory() );
for ( ActionInfoData action : actions ) {
servletContext
.addServlet( action.getServletName(), action.getClassName() )
.addMapping( action.getServletMapping() );
}
}
...
Unfortunately it does not make the container inject the EJBs and the null pointer remains. I am currently making a custom type safe JNDI lookup to the service. Obviously this is far more expensive than using the proper injection (correct me if I am wrong, have done no experiments regarding performance yet).
Using:
Java EE 6
JBoss AS 7.1
The problem seems related to this reported bug which is as yet unresolved. Resource resolution works just fine for Managed Beans as defined by the JSF specification, but not for CDI Managed Beans. Simply annotating your dynamic servlet classes with #javax.faces.bean.ManagedBean should fix the problem (yes, its quite an ugly solution):
#ManagedBean
public class DynServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
#EJB
private LoginService loginService;
public DynServlet() {
super();
}
#Override
protected void doGet(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
response.getOutputStream().println(
"Request made to: " + getClass().getSimpleName());
response.getOutputStream().println("Login Service: " + loginService);
return;
}
}
#WebListener
public class DynamicServletLoadListener implements ServletContextListener {
public DynamicServletLoadListener() {
super();
}
#Override
public void contextDestroyed(final ServletContextEvent contextEvent) {
return;
}
#Override
public void contextInitialized(final ServletContextEvent contextEvent) {
contextEvent.getServletContext().addServlet("dynservlet", DynServlet.class)
.addMapping("/services/dynservlet");
}
}
Tested with JEE6 (ofc) and both JBoss 7.1.1 and 7.2.0 (EAP 6.1.0 Alpha).
Edit:
The problem with dynamic mapped servlets is actually pretty deep in the base JBoss architecture. They use JBossWeb (a forked version of Tomcat) as the servlet implementation, and in the guts of its context management code it makes a determination wether to instantiate new components via injection or regular new. Afaik as of date, your servlets would need to be annotated in some fashion in order for them to be processed via injection: I had mentioned #ManagedBean in my original answer but it looks like annotating with #WebServlet works as well.
Servlet 3.0 Spec, Sect. 4.4.3.5
Resource injection [e.g. #EJB] on all components (Servlets, Filters and Listeners) added
programmatically or created programmatically, other than the ones added via the
methods that takes an instance, will only be supported when the component is a
Managed Bean. For details about what is a Managed Bean please refer to the
Managed Bean specification defined as part of Java EE 6 and JSR 299.
Managed Bean Declaration
A Java EE 6 managed bean is annotated #javax.annotation.ManagedBean and has a no-arg constructor. A JSR 299 (CDI) managed bean merely needs a no-arg constructor or a constructor annotated #javax.inject.Inject.
Answer
To enable resource injection you need to:
place #ManagedBean annotation on dynamically added servlet
OR
enable CDI & include an empty beans.xml
Edit
Even though you're creating the servlet dynamically, it's important that the container does the creation. Don't think creation within ServletContext will support injection. Servlet doc very vague here.
With CDI try:
servletContext.addServlet("your servlet name", #Inject YourServletClass servlet)
First, in my test, this worked fine using a version of Glassfish V3.
But, second, you may well be running afoul of this clause of the Servlet 3.0 spec.
The following methods are added to ServletContext since Servlet 3.0 to
enable programmatic definition of servlets, filters and the url
pattern that they map to. These methods can only be called during the
initialization of the application either from the contexInitialized
method of a ServletContextListener implementation or from the
onStartup method of a ServletContainerInitializer implementation.
Notably, these methods can NOT be called from a Filter.init() method. I originally tried this in a Servlet.init() method, and the init method failed because the context was already initialized.
So, my experiment did not duplicate your test exactly -- I did not use a Filter.init() method for this, rather I put the code in a ServletContextListener. And when I did that, my #EJB annotation was honored.
Edit:
As un-helpful as it sounds, I would suggest this is a bug in JBoss. When I initially tried your original code with the injection from the Filter, Glassfish threw an exception, since you're not allowed to do the injection save where I mentioned earlier. Now perhaps that's an "added feature" of JBoss, but apparently the #EJB injection processing simply isn't working. According to spec, this should work as advertised.
Having become very comfortable with dependency injection as a style, I've found myself writing an HTTP servlet something like this:
public class FooServlet extends HttpServlet {
private Dependency dependency;
public void setDependency(Dependency dependency) {
this.dependency = dependency;
}
#Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
...
Result r = dependency.doSomething(...);
...
}
This is beautifully easy to unit test, and I'm keen to keep the model -- but now that I come to deploy it in Tomcat, I come to realised that I don't know where I can invoke setDependency() from.
Some vague possibilities that come to mind:
Get servlet parameters in init(). These are just String, so I'd have to do some Reflection to create. This wouldn't be true DI; just configured dependency creation.
Something with JNDI
Somehow, from the Java program in which Tomcat is embedded, get Tomcat to give me a reference to the Servlet object, allowing me to call its setter.
Use Spring. If I use Spring, I'd be looking for ways to keep it lightweight. This app is not complex enough to warrant Spring MVC.
It's actually just two lines of code to make #Autowire work in a servlet:
ApplicationContext appContext = WebApplicationContextUtils.getRequiredWebApplicationContext( getServletContext() );
appContext.getAutowireCapableBeanFactory().autowireBean( this );
There is a drawback, though: Servlets are singletons and they aren't created by Spring, so you can't inject prototype beans. So this will work as long as all injected beans are singletons. When you add a prototype bean later, the code will start to fail with odd errors.
For this reason, I usually create handler beans (see HttpRequestHandler) and create them in doPost() instead of autowiring the servlet itself.
I'd use Spring, as it provides a large ecosystem of features and functionalities that could be used by your project to enhance it. But only if you'd really use them. There's no point in loading such a huge framework only to use one little feature in one single place.
That said, you should also take care as the doPost() method will be called by different threads, while your dependency object is a member variable. This would make your code thread-unsafe, as the same dependency instance could be simultaneously used by different threads if it were a singleton.
With thanks to Aaron and Filipe, I found what seems to be the most lightweight use of Spring.
Configure a Dispatching servlet in WEB-INF/web.xml:
<servlet>
<servlet-name>
myDispatcher
</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/myDispatcher-context.xml</param-value>
</init-param>
</servlet>
In WEB-INF/spring/myDispatcher-context.xml, configure the application context explicitly, so that BeanNameUrlHandlerMapping maps requests to your handling class, and your dependency is injected.
<bean id="/*" class="org.me.MyHandler">
<property name="dependency" ref="dependency"/>
</bean>
<bean id="dependency" class="org.me.myDependency"/>
Write MyHandler as an implementation of HttpRequestHandler - it's very much analogous to a Servlet.
This gives you dependency injection. It avoids classpath scanning, and does not load dozens of classes -- but if you want to adopt the more advanced Spring MVC features later, the opportunity is there.