How to start a daemon using Spring injection from a webapp - java

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.

Related

Spring autowire a class on server startup

I have a spring application. I am autowiring classes and they are working fine.
For e.g
#Controller
public class SearchController {
#Autowired
private EnvironmentControl envControl;
#Autowired
private SearchControl searchControl;
...
But now i have on server startup class called ScheduleServlet which uses init method to schedule something...
public class SchedulerServlet extends HttpServlet {
#Override
public void init(ServletConfig config) throws ServletException {
super.init(config);
this.LOGGER.info("timer servlet is initialized ");
try {
InitialContext ic = new InitialContext();
TimerManager tm = (TimerManager) ic.lookup("java:comp/env/tm/TimerManager");
Timer timer = tm.schedule(new GlobalTemplateScheduler(), 0, 3600000);// one hour interval
System.out.println("Timer..... " + timer);
}
...
In this my GlobalTemplateScheduler class has timerExpired method which is scheduled to execute after every one hour interval.
public class GlobalTemplateScheduler implements TimerListener {
#Autowired
private TemplateControl templateControl;
#Override
public void timerExpired(Timer timer) {
try {
templateControl.updateMappings(names);
} catch (Exception e) {
this.LOGGER.error(e.getMessage());
e.printStackTrace();
}
...
So i have to autowire templateControl which i am getting null. This should happen on server startup.
Further inside updateMappings there's a datasource object which is also autowired as constructor-arg(This is working fine on browser request but need to do it on server startup).
Note: I cannot use the ApplicationListener interface.
Any suggestions would really help.
Thankyou.
On application startup beans initialization would not be completed, beans can be used after the application context refresh or after the intialization of the bean, it will make no sense to execute a logic which requires the bean on the startup unless you detect whether the bean is ready or not.
You can execute some logic using #PostConstruct in the bean which will be executed after the initialization of the bean so you can manipulate your logic in a way to do so after the intialization of the bean or you could detect and execute logic after the ContextRefreshedEvent by impelementing applicationListener and put your logic in onAppplicationEvent method.
One solution would be to use Spring's Container within your servlet. There are many implementations for this purpose, for instance the AnnotationConfigApplicationContext. Spring's documentation describes how to use the ClassPathXmlApplicationContext
Suppose GlobalTemplateScheduler is also a bean, then the key point is this:
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
GlobalTemplateScheduler sheduler = context.getBean("sheduler", GlobalTemplateScheduler.class);
sheduler.someMethod();
The content of the XML, which is used by the ClassPathXmlApplicationContext is small. But you need to enable component scan:
<context:component-scan base-package="foo.bar.baz" />
Another approach, I could suggest, is to use Spring's DispatcherServlet to wire all the beans together. It can use the same XML, it is just a matter of loading it. The benefit is that is you don't need to load the application context by yourself and launch a bean as an entry point
There are plenty of tutorials how to use this servlet.
If you dont't like to write XML, you could use the WebApplicationInitializer
As i said the beans which i was autowiring were working fine. I just needed those beans in my Scheduler Servlet.
Here's the solution which worked...
In my scheduler servlet i got the application context xml and used the beans which were required...
ApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(this.getServletContext());
GlobalTemplateControl globalTemplateControlObject = context.getBean("globalTemplateControl", GlobalTemplateControl.class);
Thanks #duffymo #Amer Qarabsa #Spindizzy for your suggestions :)

How to access context init parameters from a PreProcessInterceptor?

I am inside a RESTEasy PreProcessInterceptor and need to access a parameter I've configured in my WAR's web.xml (either as an init-param in the servlet definition of the RestEasy servlet - as described here - or as a context-param).
How do I achieve that?
Alternatively, is there some other place where I should be configuring parameters that I need to be made available to my interceptors at runtime?
What worked for me is the following. In the past I had my PreProcessorInterceptor defined in the WAR's web.xml as follows:
<context-param>
<param-name>resteasy.providers</param-name>
<param-value>my.package.MyPreProcessorInterceptor</param-value>
</context-param>
I now moved it away from the web.xml and placed it inside my Application as follows:
public class JaxRsApplication extends Application {
private Set<Object> singletons = new HashSet<>();
public JaxRsApplication(#Context ServletContext servletContext) {
Assert.assertNotNull(servletContext);
singletons.add( new MyPreProcessorInterceptor(servletContext) );
}
#Override
public Set<Object> getSingletons() {
return singletons;
}
}

Singleton spring bean is being created more than once

I have a singleton spring bean named gameContext, my spring bean definition;
<bean name="gameContext" scope="singleton"
class="tr.com.hevi.game.numblock.core.context.GameContext"/>
I also use this class for session listening, here is my web.xml
<listener>
<listener-class>
tr.com.hevi.game.numblock.core.context.GameContext
</listener-class>
</listener>
The problem is that gameContext is being created twice. one; at the very beginning before the spring context is being loaded, and the second; within the spring context.
I am sure that I do not component-scan more than once.
I understand the reason behind but don't know how to tackle the problem. One possible solution should be adding the listener within the spring context not web.xml, or there might be a proxy object solution.
In your problem, there are 2 objects for spring because you are configuring the listener twice
The first is in the web.xml (outside the spring context)
Within the spring context as a bean.
The easiest way to have only 1 instance is if you are using the Servlet 3.0 specification. Here the ServletContext has a addListener() method make use of the same. Do something like the below:
#Component
public class MyCustomListener implements javax.servlet.http.HttpSessionListener, ApplicationContextAware {
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
if (applicationContext instanceof WebApplicationContext) {
((WebApplicationContext) applicationContext).getServletContext().addListener(this);
} else {
//Either throw an exception or fail gracefully, up to you
throw new RuntimeException("Must be inside a web application context");
}
}
}
The above approach will cause you to create only 1 object of the listener, and have the same object registered as a Servlet listener and spring bean.

Spring: Accessing the correct WebApplicationContext with multiple dispatchers declared

I have two Spring contexts declared in my application - one for Spring-MVC requests, and another for Flex/BlazeDS messagebroker requests, mapped to different url-patterns:
<servlet-mapping>
<servlet-name>spring-mvc</servlet-name>
<url-pattern>/app/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>flex</servlet-name>
<url-pattern>/messagebroker/*</url-pattern>
</servlet-mapping>
There's a common context configuration declared, (/WEB-INF/applicationContext.xml) and then each of the two contexts have their own configurations declared in spring-mvc-servlet.xml and flex-servlet.xml respectively.
Inside flex-servlet.xml I have beans declared which are specific to the flex context. However, when a call comes in to http://localhost/messagebroker/* I'm getting errors that those beans aren't available.
The code in question is inside a custom Spring component, so directly references the WebApplicationContext in order to access the declared beans:
public ISerializer getSerializer(Object source,boolean useAggressiveSerialization)
{
ServletContext ctx = FlexContext.getServletContext();
WebApplicationContext springContext = WebApplicationContextUtils.getRequiredWebApplicationContext(ctx);
String serializerBeanName = springContext.getBeanNamesForType(ISerializer.class);
}
This approach works when I'm running with a single context. However it needs to also support where there are multiple contexts running.
Setting a breakpoint, I see that the value of springContext is the root context, with a single configLocation - /WEB-INF/applicationContext.xml
I'm asssuming that this is the problem - as the ISerializer that the above code requires is declared in flex-servlet.xml.
How do I modify the above code to support both scenarios? (Single context, and multiple contexts)?
EDIT:
The code shown above sits inside a ManageableComponentFactoryBean, which appears to operate as a custom bean factory. It seems that the ApplicationContextAware interface is not honoured on generated classes. Eg:
<bean id="dpHibernateRemotingAdapterComponentFactory"
class="org.springframework.flex.core.ManageableComponentFactoryBean">
<constructor-arg
value="org.dphibernate.adapters.RemotingAdapter" />
<property name="properties">
<value>
{"dpHibernate" :
{
"serializerFactory" : "org.dphibernate.serialization.SpringContextSerializerFactory"
}
}
</value>
</property>
</bean>
The code quoted above sits inside the org.dphibernate.serialization.SpringContextSerializerFactory. Making this SpringContextSerializerFactory implement ApplicationContextAware has no impact.
If flex is a DispatcherServlet, and for some reason you can't follow Tomás Narros's suggestion, you can obtain a context associated with the current DispatcherServlet using RequestContextUtils.getWebApplicationContext(request).
There is also a convenience method RequestContextUtils.getWebApplicationContext(request, ctx), which returns the root context if DispatcherServlet's one is not available.
Declare your custom componente as Spring Context aware:
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
public MyCustomBean implements ApplicationContextAware {
private ApplicationContext springContext;
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
springContext = applicationContext;
}
public ISerializer getSerializer(Object source,boolean useAggressiveSerialization)
{
String serializerBeanName = springContext.getBeanNamesForType(ISerializer.class);
}
}
At the bean initialization, Spring will access the setApplicationContext method of your bean, passing as an argument the context in wich it's being created. There, you can keep it an use it whenever you need.
Hrmmmm.....I have nearly that exact sort of declaration in my Spring/Flex app, using Spring/Flex integration and there is only one application context. Could that be the problem? You have beans declared in the Flex context file that aren't in the MVC context file, and they aren't really getting loaded?

Is there a way to access web.xml properties from a Java Bean?

Is there any way in the Servlet API to access properties specified in web.xml (such as initialization parameters) from within a Bean or Factory class that is not associated at all with the web container?
For example, I'm writing a Factory class, and I'd like to include some logic within the Factory to check a hierarchy of files and configuration locations to see which if any are available to determine which implementation class to instantiate - for example,
a properties file in the classpath,
a web.xml parameter,
a system property, or
some default logic if nothing else is available.
I'd like to be able to do this without injecting any reference to ServletConfig or anything similiar to my Factory - the code should be able to run ok outside of a Servlet Container.
This might sound a little bit uncommon, but I'd like for this component I'm working on to be able to be packaged with one of our webapps, and also be versatile enough to be packaged with some of our command-line tools without requiring a new properties file just for my component - so I was hoping to piggyback on top of other configuration files such as web.xml.
If I recall correctly, .NET has something like Request.GetCurrentRequest() to get a reference to the currently executing Request - but since this is a Java app I'm looking for something simliar that could be used to gain access to ServletConfig.
One way you could do this is:
public class FactoryInitialisingServletContextListener implements ServletContextListener {
public void contextDestroyed(ServletContextEvent event) {
}
public void contextInitialized(ServletContextEvent event) {
Properties properties = new Properties();
ServletContext servletContext = event.getServletContext();
Enumeration<?> keys = servletContext.getInitParameterNames();
while (keys.hasMoreElements()) {
String key = (String) keys.nextElement();
String value = servletContext.getInitParameter(key);
properties.setProperty(key, value);
}
Factory.setServletContextProperties(properties);
}
}
public class Factory {
static Properties _servletContextProperties = new Properties();
public static void setServletContextProperties(Properties servletContextProperties) {
_servletContextProperties = servletContextProperties;
}
}
And then have the following in your web.xml
<listener>
<listener-class>com.acme.FactoryInitialisingServletContextListener<listener-class>
</listener>
If your application is running in a web container, then the listener will be invoked by the container once the context has been created. In which case, the _servletContextProperties will be replaced with any context-params specified in the web.xml.
If your application is running outside a web container, then _servletContextProperties will be empty.
Have you considered using the Spring framework for this? That way, your beans don't get any extra cruft, and spring handles the configuration setup for you.
I think that you will have to add an associated bootstrap class which takes a reference to a ServletConfig (or ServletContext) and transcribes those values to the Factory class. At least this way you can package it separately.
#toolkit : Excellent, most humbled - This is something that I have been trying to do for a while

Categories

Resources