Injecting dependencies into a plain old Tomcat HTTP servlet - java

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.

Related

Make ServletContextListener spring aware

I am plugging in Spring to existing Java EE web Application. I have following lines in my web.xml:
<listener>
<listener-class>com.MyContextListener</listener-class>
</listener>
And Following MyContextListener class?
public class MyContextListener implements ServletContextListener {
public void contextInitialized(ServletContextEvent event) {
//...
}
}
What should I do to make MyContextListener be managed by Spring?
Edited:
My assumption is: Spring should create all servlets and all web app infrastructure so everything happened in contextInitialized method of MyContextListener should be somehow handled by Spring. How can I achieve, by implementing some interface I suppose. Correct me if I am wrong. Thanks!
Well,
We had a similar scenario of configuring an exiting Jersey web services app to use Spring for dependency injection. Our Jersey webapp had extended ContextLoaderListener as follow
public class XServletContextListener extends ContextLoaderListener {
...
#Override
public void contextInitialized(ServletContextEvent arg0) {
super.contextInitialized(arg0);
....
}
#Override
public void contextDestroyed(ServletContextEvent arg0) {
super.contextDestroyed(arg0);
....
}
}
where ContextLoaderListener is
import org.springframework.web.context.ContextLoaderListener;
We included the jersey-spring bridge with all spring dependencies including applicationContext.xml as follow
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd">
<context:component-scan base-package="com.xxx.*" />
....
....
</beans>
And obviously needed to make sure that XServletContextListener is included in the web.xml as follow
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>com.xxx.**.XServletContextListener</listener-class>
</listener>
Followed by servlet and its init-param values and servlet mapping. You can obviously adopt annotation config in place of xml confib in which case you would need to use WebListener annotation.
We use a variety of annotations such as
#Component for objects
#Service for services
#Repository for DAOs
#Controller for controllers/resources
#ContextConfiguration for tests
Everything is loaded and autowired by Spring framework.
What should I do to make MyContextListener be managed by Spring?
It depends on which configuration way you are using. Anyway, you should tell directly Spring to use the class you have declared. That could be done by the following way:
#WebListener
public class MyContextListener implements ServletContextListener { ... }
A class marked with this annotation (the Servlet 3.0 specification, 8.1.4) must implement one of these interfaces
HttpSessionAttributeListener
HttpSessionListener
ServletContextAttributeListener
ServletContextListener (+)
ServletRequestAttributeListener
ServletRequestListener
HttpSessionIdListener
that it actually does.
Personally, I prefer a meta-annotation based approach which makes my configuration shorter and more concise.
Spring should create all servlets and all web app infrastructure so everything happened in contextInitialized method of MyContextListener should be somehow handled by Spring.
Yes, Spring will do it for you if you provide some information which could help it to register / configure / create / manage an instance.
The information may be either meta-information (a template that tells how to create an instance, like BeanDefinitions) or a completed instance itself (usually, it gets passed programmatically that, in turn, leads to writing a huge amount of code).
How can I achieve, by implementing some interface I suppose.
You are implementing an interface to make your listener a listener (a class that describes specific methods which will be called at some points of time). Spring, itself, is responsible for guaranteeing such calls at those points of time, placing an object in the existing web infrastructure before.
Either annotate the class with #WebListener or the method with #Bean
Annotate where you create a new instance of MyContextListener with #Bean if using Java Configs with Spring Boot.

Implementing generic JAX-RS web service using javax.ws.rs.core.Application

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.

Failing to #AutoWire a member in a #WebServlet

I can't seem to get my servlet's fields to #AutoWire; they end up null. I have a pure annotation-configured webapp (no XML files). My servlet looks like this:
#WebServlet("/service")
public
class
SatDBHessianServlet
extends HttpServlet
{
#Autowired protected NewsItemDAO mNewsItemDAO;
}
Other #AutoWired things seem to work fine, both #Service objects and #Repository objects. But not this one, and I can't figure out why. I even tried adding its package to the ComponentScan(basePackages) list for my other classes.
Additional Info:
I added the following to my servlet’s init() method, and everything seemed to wire up properly, but I'm confused as to why Spring can't wire it up without that.
SpringBeanAutowiringSupport.processInjectionBasedOnServletContext(this, inConfig.getServletContext());
Servlet are web components that are not being created by Spring container, based on that lifecycle is not managed by the container and a lot of stuff that spring provides such as autowired or aspect can not run from them.
You need to indicate to the spring container that a component was created outside of IoC container and need to be part of it.
And as the API said SpringBeanAutowiringSupport is for:
Convenient base class for self-autowiring classes that gets
constructed within a Spring-based web application
This generic servlet base class has no dependency on the Spring ApplicationContext concept.
There is another way to indicate servlets being created by spring container using an interface
Spring MVC uses the DispatcherServlet for handling all requests in a Servlet environment.
The DispatcherServlet accordingly forwards the requests to the appropriate #Controller class (if any) based on the #RequestMapping on that class and/or it's methods.
What is happening in your Servlet is that it is not managed by Spring (but by the Servlet container) and there for no injection of dependencies is occurring.

How to resolve Spring #Value annotations in a web servlet?

I have a servlet packaged in an ear file, and I cannot get it to resolve #Value annotated properties.
The app is in two parts: a servlet packaged in a war file, which is then included by different applications packaged in an ear file. The application supplies the implementation of a class (ResourceManager) defined by an interface in the servlet, and also the property file containing the value of the property for the #Value annotated field.
The war file contains:
web.xml:
<servlet>
<servlet-class>... extends MessageDispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath:/spring-ws.xml
classpath:/spring-app.xml
</param-value>
</init-param>
</servlet>
spring-ws.xml:
<context:annotation-config/>
<context:property-placeholder location="classpath:/spring-ws.properties"/>
The values in spring-ws.properties are referenced directly from spring-ws.xml, and all work fine.
The servlet class contains:
public class MyServlet extends MessageDispatcherServlet
#Autowired private ResourceManager resourceManager; // original annotation - got this to work
#Value("${app.name:}") private String appName; // added later - cannot get this to work
The app.name is optional, hence the trailing ":".
The ear file adds (packaged in a jar file in the ear lib directory):
spring-app.xml:
<context:property-placeholder location="classpath:/spring-app.properties"/>
<bean class="ResourceManagerImpl">
...
</bean>
spring-app.properties:
app.name=myApp
My first problem was with the #Autowired annotation: not sure I ever got to the bottom of that correctly, but I managed to get it to work by adding this code to the servlet:
#Override
protected void initFrameworkServlet() throws ServletException {
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
bpp.setBeanFactory(getWebApplicationContext().getAutowireCapableBeanFactory());
bpp.processInjection(this);
}
This may not be the best way to fix this, but it was the first I found and it worked, so I used it and moved on. Then I added a #Value annotated field, and I can't find a way to make this work. The javadocs for AutowiredAnnotationBeanPostProcessor say that it should process #Value annotations as well, but it doesn't appear to: the appName property is always blank (presumably from the default).
I do have a workaround, using beans:
#Autowired #Qualifier("appName") private String appName;
<bean id="appName" class="java.lang.String">
<constructor-arg value="myApp"/>
</bean>
But I'm sure there must be a better way of doing this. I've read this post Difference between applicationContext.xml and spring-servlet.xml in Spring Framework, but I'm not sure it applies: I don't think I even have an application context here, just a servlet context defined in the web.xml for the (single) servlet class.
I've also seen this: Spring 3.0.5 doesn't evaluate #Value annotation from properties, which looks like the same problem, but the solution there appeared to be to move the "<context:property-placeholder>" from the application context to the servlet context, whereas as I said before, I don't think I even have an application context here...
Finally, I've had a quick go at simplifying the problem, removing the separate app and packaging everything in a single war, but I don't think that is the problem (although could obviously be wrong...): spring can resolve the class path for the app.xml file so it must be able to resolve the class path for the app.properties file.
Any help much appreciated, but I guess at this point it is a bit of an academic interest, as I have a workable solution (using beans) and the people that pay the bills don't really care how it works as long as it works! I'd just like to know so next time I don't have to copy (what I think is) a flawed workaround.
Using spring-ws-core 2.1.4, which pulls in spring 3.2.4 via maven dependencies.
Thanks
Update - Solved
Turns out the key to fixing this was M Deinum's comment that "You also have a duplicate element...". I had two separate PropertyPlaceholderConfigurers in the same context, so the second was ignored and its properties never used. There was no problem with the context itself (which is a servlet context not a root context), or the annotation processing.
So the fix was simply to remove the property placeholder from the spring-app.xml, and combine both property files in the spring-ws.xml
<context:property-placeholder
location="classpath:/spring-ws.properties,
classpath:/spring-app.properties"/>
TL;DR's answer here Spring: namespace vs contextConfigLocation init parameters in web.xml is also an awesome explanation of how the different contexts are processed!
Cheers,
Your servlet isn't a spring managed bean it is managed by the Servlet container. Hence the reason why #Autowired doesn't work in the first place. Spring will only process beans it knows.
Both the ContextLoaderListener and your servlet (basically any servlet that extends FrameworkServlet) have there own instance of an ApplicationContext. The one from the ContextLoaderListener is used as a parent by the context constructed in the servlet. So you indeed have an ApplicationContext in your servlet.
Now for this #Value to resolve properly you need to have a PropertyPlaceHolderConfigurer to resolve the placeholders. As PropertyPlaceHolderConfigurer is a BeanFactoryPostProcessor it only operates on beans in the same context, so the one loaded/configured by the ContextLoaderListener doesn't do anything for beans related to the context from your servlet. (Hence you need to add <context:property-placeholder ... /> element to the servlet context).
You also have a duplicate element in general you want to avoid that kind of situation as it will lead to problems at same time, either one overriding the other or you will get exceptions that placeholders cannot be resolved by either one of the PropertyPlaceHolderConfigurer
Your init code is also overly complex and you don't need the AutowiredAnnotationBeanPostProcessor simply use the available methods.
getWebApplicationContext().getAutowireCapableBeanFactory().autowireBean(this);
Which also does more then what only the AutowiredAnnotationBeanPostProcessor does.
for #Autowired annotation, you just need to have a class with those annotations:
#Configuration
#PropertySource("classpath:myFile.properties")
#ComponentScan("com.company.package")
The #Configuration is used by Spring to tell it's a class for configuration (it replaces the xml files that you talked about, xml conf file is Spring 2, annotation is new with Spring 3 and 4)
The #ComponentScan manage the IOC (#Autowire)
The #PropertySource load the prop file
Then the class to initialized at startup with the prop file:
#Component("myFileProperties")
public class MyFileProperties{
#Value("${app.name}")
private String appName;
public String getAppName() {
return appName;
}
public void setAppName(String appName) {
this.appName= appName;
}
}
Take a look here if you want it can inspire you (a conf file in a web context that I developped): https://github.com/ebrigand/RSSLiker/blob/master/src/main/java/com/mrm/rss/init/WebAppConfig.java
There is a workaround for this.
You can create a static field in a java class to store the value in.
public class SystemPropertyHelper {
public static String propertyValue;
}
And create a java class annotated with #Service or #Component, which you can inject the property in, and pass this value to the static field.
#Component
public class SpringBeanClass {
#Value("${propertyName}")
private String propertyValue;
#PostConstruct
public void init() {
SystemPropertyHelper.propertyValue = propertyValue;
}
}
This way you can always access the property value through the static field SystemPropertyHelper.propertyValue

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)

Categories