Several months ago, I made a simple application just to run every night to send emails to my users, if certain criteria in database are found. I used Spring 3.1.0 and Quartz 1.8.5 to accomplish this; so far, so good.
The app is deployed in a weblogic server.
Last week, I was asked to modify the app to allow it to be manually commanded to run through an URL.
I added a servlet to it and it worked fine: everytime someone invokes the URL, the app runs.
But one collateral effect appeared - each servlet invocation also provokes the reload of spring context and another instance of Quartz is created. So, if said servlet is called n times, my app will send n+1 repeated emails when Quartz fires my job, instead of only 1.
I would like to understand why this is happening and how to fix this. Could you help me, please?
The following are the relevant files to my question.
Log file excerpt, comprising the app's starting up:
28/11/2012 16:37:48 org.springframework.web.context.ContextLoader initWebApplicationContext
INFO: Root WebApplicationContext: initialization started
28/11/2012 16:37:48 org.springframework.context.support.AbstractApplicationContext prepareRefresh
INFO: Refreshing Root WebApplicationContext: startup date [Wed Nov 28 16:37:48 BRST 2012]; root of context hierarchy
28/11/2012 16:37:48 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from URL [file:/C:/bea/user_projects/workspaces/app/target/app-1.0.0/WEB-INF/classes/app-context.xml]
28/11/2012 16:37:48 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [app-data-context.xml]
28/11/2012 16:37:48 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [app-scheduler.xml]
28/11/2012 16:37:48 org.springframework.core.io.support.PropertiesLoaderSupport loadProperties
INFO: Loading properties file from class path resource [app.properties]
28/11/2012 16:37:48 org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
INFO: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@599bcd: defining beans [org.springframework.beans.factory.config.PropertyPlaceholderConfigurer#0,appDS,job,cronTrigger,org.springframework.scheduling.quartz.SchedulerFactoryBean#0,appDAO,appService,mailerService,appTask, (...)]; root of factory hierarchy
28/11/2012 16:37:50 org.springframework.context.support.DefaultLifecycleProcessor$LifecycleGroup start
INFO: Starting beans in phase 2147483647
**28/11/2012 16:37:50 org.springframework.scheduling.quartz.SchedulerFactoryBean startScheduler
INFO: Starting Quartz Scheduler now**
28/11/2012 16:37:50 org.springframework.web.context.ContextLoader initWebApplicationContext
INFO: Root WebApplicationContext: initialization completed in 2109 ms
Log file excerpt that occurs everytime the servlet is called:
<28/11/2012 16h37min50s BRST> <Notice> <WebLogicServer> <BEA-000360> <Server started in RUNNING mode>
28/11/2012 16:39:06 org.springframework.context.support.AbstractApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@11f7458: startup date [Wed Nov 28 16:39:06 BRST 2012]; root of context hierarchy
28/11/2012 16:39:06 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [app-context.xml]
28/11/2012 16:39:06 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [app-data-context.xml]
28/11/2012 16:39:06 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [app-scheduler.xml]
28/11/2012 16:39:06 org.springframework.core.io.support.PropertiesLoaderSupport loadProperties
INFO: Loading properties file from class path resource [app.properties]
28/11/2012 16:39:06 org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
INFO: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@10ca001: defining beans [(...)]; root of factory hierarchy
28/11/2012 16:39:06 org.springframework.context.support.DefaultLifecycleProcessor$LifecycleGroup start
INFO: Starting beans in phase 2147483647
**28/11/2012 16:39:06 org.springframework.scheduling.quartz.SchedulerFactoryBean startScheduler
INFO: Starting Quartz Scheduler now**
web.xml:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
version="2.5">
<description>generic web.xml</description>
<!-- ====================================== -->
<!-- SPRING -->
<!-- ====================================== -->
<!-- Loading Application Bean's. -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:app-context.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<display-name>appServlet</display-name>
<servlet-name>appServlet</servlet-name>
<servlet-class>br.com.app.publicInterface.appServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>appServlet</servlet-name>
<url-pattern>/appServlet</url-pattern>
</servlet-mapping>
</web-app>
app-context.xml excerpt:
<?xml version="1.0" encoding="UTF-8"?>
<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-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />
<property name="locations">
<list>
<value>classpath:app.properties</value>
</list>
</property>
</bean>
<import resource="classpath:app-data-context.xml"/>
<import resource="classpath:app-scheduler.xml"/>
<!-- DAOs -->
<!-- Services -->
<!-- tasks -->
</beans>
app-scheduler.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">
<bean name="job" class="org.springframework.scheduling.quartz.JobDetailBean">
<property name="jobClass" value="br.com.app.job.QuartzJob" />
<property name="jobDataAsMap">
<map>
<entry key="gerenciadorTasks">
<ref bean="gerenciadorTasks" />
</entry>
</map>
</property>
</bean>
<bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean">
<property name="jobDetail" ref="job" />
<property name="cronExpression" value="${quartz.cronExpression}" />
</bean>
<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="quartzProperties">
<props>
<prop key="org.quartz.scheduler.skipUpdateCheck">true</prop>
<prop key="org.quartz.jobStore.isClustered">true</prop>
<prop key="org.quartz.scheduler.instanceName">NotificadorQuartzScheduler</prop>
<prop key="org.quartz.scheduler.instanceId">AUTO</prop>
</props>
</property>
<property name="triggers">
<list>
<ref bean="cronTrigger" />
</list>
</property>
</bean>
</beans>
AppServlet source code:
public class AppServlet extends HttpServlet {
private static final long serialVersionUID = 7213474106234238692L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
PrintWriter out = resp.getWriter();
out.println("execução iniciada em: " + new Date());
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:app-context.xml");
GerenciadorTasks gt = (GerenciadorTasks) context.getBean(SpringNameBeans.GERENCIADOR_TASKS);
gt.init();
out.println("execução terminada em: " + new Date());
}
}
Thank you very much for your time.
Solution
--- Following @Boris information, I altered my servlet a little. My context was already loaded, I just needed to access it:
public class AppServlet extends HttpServlet {
private static final long serialVersionUID = 7213474106234238692L;
ApplicationContext applicationContext = null;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
PrintWriter out = resp.getWriter();
out.println("execução iniciada em: " + new Date());
if (applicationContext == null){
applicationContext = WebApplicationContextUtils.getWebApplicationContext(this.getServletContext());
}
GerenciadorTasks gt = (GerenciadorTasks) applicationContext.getBean(SpringNameBeans.GERENCIADOR_TASKS);
gt.init();
out.println("execução terminada em: " + new Date());
}
}