I need to start a daemon when I deploy a war. The daemon itself uses objects that should be injected with Spring. I did the following:
In web.xml
...
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/springapp-servlet.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<listener>
<listener-class>example.AppListener</listener-class>
</listener>
AppListener.java
public class AppListener implements ServletContextListener {
...
@Override
public void contextInitialized(final ServletContextEvent sce) {
log.info("======================= Begin context init =======================");
try {
// final ApplicationContext context = new ClassPathXmlApplicationContext("WEB-INF/springapp-servlet.xml");
final ApplicationContext context = new ClassPathXmlApplicationContext("src/main/webapp/WEB-INF/springapp-servlet.xml");
//final ApplicationContext context = new ClassPathXmlApplicationContext("//Users/.../WEB-INF/springapp-servlet.xml");
final SessionServiceDaemon sessionServiceDaemon = context.getBean(SessionServiceDaemon.class);
sessionServiceDaemon.start();
} catch (final Exception e) {
log.error("Was not able to start daemon",e);
}
}
SessionServiceDaemon.java
@Service
@Singleton
public class SessionServiceDaemon {
private final static Logger log = LoggerFactory.getLogger(SessionServiceDaemon.class);
private final SessionServiceHandler handler;
@Inject
public SessionServiceDaemon(final SessionServiceHandler handler) {
log.info("+++++++++++++++++++++++++++++++ SessionServiceDaemon injected");
this.handler = handler;
}
My springapp-servlet.xml simply has the packages required for the injection:
<?xml version="1.0" encoding="UTF-8"?>
<beans ...
<context:component-scan base-package="example" />
<mvc:annotation-driven />
</beans>
In the startup logs, I see as expected:
+++++++++++++++++++++++++++++++ SessionServiceDaemon injected
followed by
======================= Begin context init =======================
Problem is: I then get an exception that the file does not exist no matter which path I use to point to springapp-servlet.xml:
org.springframework.beans.factory.BeanDefinitionStoreException: IOException parsing XML document from class path resource [src/main/webapp/WEB-INF/springapp-servlet.xml]; nested exception is java.io.FileNotFoundException: class path resource [src/main/webapp/WEB-INF/springapp-servlet.xml] cannot be opened because it does not exist
I tried different relative paths and even the absolute path without success. I even edited the code above and added just above my attempt to load the context the following:
try {
log.info(org.apache.commons.io.FileUtils.readFileToString(new File("src/main/webapp/WEB-INF/springapp-servlet.xml")));
} catch (final Exception e) {
log.error("Unable to find file",e);
}
and that printed the content of springapp-servlet.xml just fine.
My 2 questions:
- How can I get a “class path resource [src/main/webapp/WEB-INF/springapp-servlet.xml] cannot be opened because it does not exist” when I am able to display the file content using the exact same path from the same method?
- Do I have the correct approach anyway for starting a Daemon that has dependencies that are injected?
PS: I use Tomcat.
You are starting two different spring application contexts. The first, the built-in ContextLoaderListener, is likely picking up your springapp-servlet.xml configuration from default locations. (You didn’t say if you are specifying a contextConfigLocation.)
In your custom listener, you then construct a new application context using ClassPathXmlApplicationContext with an explicit path. Of the three lines you’ve shown, only the one with “”WEB-INF/springapp-servlet.xml” looks like a possible candidate for classpath resolution, although it really depends on how you’ve configured and startup your Tomcat instance. (i.e. What is the classpath from Tomcat’s point-of-view?)
Regardless, there are better ways to get the Spring application context in to a servlet/listener. A direct approach is to use the ContextLoaderListener as you have done, but then in your custom servlet/listener, make use of Spring’s WebApplicationContextUtils.getWebApplicationContext.
Spring has direct support for servlets as well, including configuration via annotations, the HttpServletBean class, or even using FrameworkServlet directly.