Reputation: 1039
I have a simple test method here that is set to run every 5 seconds and it does, but looking at the System.out you can see it appears to be doing something odd.
@Scheduled(cron="*/5 * * * * ?")
public void testScheduledMethod() {
System.out.println(new Date()+" > Running testScheduledMethod...");
}
Output:
Wed Jan 09 16:49:15 GMT 2013 > Running testScheduledMethod...
Wed Jan 09 16:49:15 GMT 2013 > Running testScheduledMethod...
Wed Jan 09 16:49:20 GMT 2013 > Running testScheduledMethod...
Wed Jan 09 16:49:20 GMT 2013 > Running testScheduledMethod...
Wed Jan 09 16:49:25 GMT 2013 > Running testScheduledMethod...
Wed Jan 09 16:49:25 GMT 2013 > Running testScheduledMethod...
Wed Jan 09 16:49:30 GMT 2013 > Running testScheduledMethod...
Wed Jan 09 16:49:30 GMT 2013 > Running testScheduledMethod...
Why is it running TWICE (appear) each time?
Upvotes: 42
Views: 72177
Reputation: 475
I too faced the same issue. This is because my application is an enterprise framework (both war/ejb in the same .ear file). The theory is the WAR and EJB runs in their own container. In the end, the solution is to wire up the beans using Spring framework but only trigger the scheduler post servlet startup.
Upvotes: 0
Reputation: 2707
In my case, this issue occurred only at PRODUCTION environment.
It was NOT getting replicated at Local, Staging environments.
Later we realized that at Production we had multiple servers running.
Therefore, each server running its own cron, which actually caused multiple occurrences of cron events at same time.
Upvotes: 1
Reputation: 439
Had the same issue with my springboot-application, the Scheduler runs twice.
Reason was, as mentioned multiple times in earlier comments, that the bean is initialized multiple times.
My solution was:
In the code- this was the change i´ve made:
// @Component <------ Removed
public class FileTransferScheduler {
private final Logger logger = LoggerFactory.getLogger(FileTransferScheduler.class);
@Autowired
private ExtractorService extractorService;
@Scheduled(cron = "* /1 * * * *")
public void scheduleFileTransfer() {
logger.info("Triggered cronjob to transfer new uploads to dms...");
extractorService.extractTmcUploads();
}
}
...and then this change in the main method of the springboot-application:
@EnableScheduling
@SpringBootApplication
public class InsidersExtractorApplication extends SpringBootServletInitializer {
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
// The Scheduler Class needs to be added manually, because it dont have to be defined as Bean
return application.sources(InsidersExtractorApplication.class, FileTransferScheduler.class);
}
public static void main(String[] args) {
SpringApplication.run(InsidersExtractorApplication.class, args);
}
}
Upvotes: 1
Reputation: 59
Hi,
It could be because of any of the 2 reasons:
For 1), you may follow any of the above answers. I will speak about (2).
For (2), Generally, In cloud environments, you would have multiple instances (like 2,3,4....) running in cloud. Hence, your scheduler will be triggered by each app instance. I have faced this issue in cloud environment too. We run our app on Cloud Foundry and we have scaled to 2 instances of our app. Our scheduler started triggering twice (from each of the app instance i.e. app_instance_0 and app_instance_1).
In order to overcome, we could do any one of the below:
APPROACH I:
Use System.getenv("CF_INSTANCE_INDEX");
// this var is provided by default to the app by cloud foundry as env variable.
it gives us the app instance index which triggered the method call. Hence, we can then use simple condition to alwys trigger schduler when app_instance_0 runs it using:
@Scheduled(cron = * * * * * *)
public void scheduledTask() {
String appInstanceIndex = System.getenv("CF_INSTANCE_INDEX");
if (appInstanceIndex.equals("0")) {
.... //your scheduling logic here
.... //your scheduling logic here
}
else {
//dont do anything as its call from other app instances...
}
}
One main advantage here is that we dont make any DB calls. Ofcourse it wont work if you dont use CF. But do check if your PaaS provides any such variable.
APPROACH II: This is a genric approach. Implement a new table for scheduler. Add a unique constraint column. In your shceduler method, generate today's date and add it into Db. (this date is saved in table with column having unique constraint). Now, first time always this scheduler will run properly. When other app instances try to create todays date again and save into DB, it will lead to exception as DB has unique constraint and it cant add the date again. hence, for other app instances, the scheduler logic will fail and lead to exception.
@Scheduled(cron = * * * * * *)
public void scheduledTask() {
String date = LocalDate.now().toString();
try{
insertDateInDb();
// your scheduler logic here
}
catch(Exception e){
// Dont do anything...
}
}
Upvotes: 2
Reputation: 23
For me, after several unsuccessful attempts and configurations, resolution in a very simple:
Edit the conf/server.xml file:
<Host appBase="webapps" deployOnStartup="false" autoDeploy="false" name="localhost" unpackWARs="true">
Upvotes: -2
Reputation: 31
I had the same problem, I tried all the suggested methods and nothing worked for me. Finally I was able to find the solution by removing the extension to 'SpringBootServletInitializer' and the 'configure' method of my WebApplication class and with this I stopped duplicating my Scheduled Task.
Upvotes: 1
Reputation: 541
I was declaring my class as a "Service" and also using the @Scheduled annotation to declare it as a Scheduler. The scheduler invokes it normally, but as we declared the class as a service. It was initiated twice.
I removed the Service annotation. And declared that class as a bean in the context XML I have maintained. (custom context XML which is declared in web.xml). This fixed the issue for me. Hope it helps someone.
Upvotes: 8
Reputation: 11
Had the same problem.
In my case I moved my @Scheduled functions from @Service into new separate class with @Component annotation and it fixed my problem
Upvotes: -1
Reputation: 46
I had the same problem. I was using annotation based configuration as follows:
@Configuration
@EnableScheduling
public class SchedulerConfig {
@Scheduled(fixedDelay = 60 * 1000)
public void scheduledJob() {
// this method gets called twice...
}
}
I was also extending AbstractAnnotationConfigDispatcherServletInitializer for initializing the same.
public class SpringWebApplicationInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class<?>[] { MainConfig.class, SchedulerConfig.class };
}
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class<?>[] {SpringWebConfig.class};
}
@Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
@Override
protected Filter[] getServletFilters() {
final CharacterEncodingFilter encodingFilter = new CharacterEncodingFilter();
encodingFilter.setEncoding(CHARACTER_ENCODING);
encodingFilter.setForceEncoding(true);
return new Filter[] { encodingFilter };
}
}
Removing the SchedulerConfig.class
from getRootConfigClasses()
method did the trick for me.
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class<?>[] { MainConfig.class };
}
I hope this helps.
Upvotes: 0
Reputation: 204
In the application.properties, add the following property which tells the Spring Boot Application to not start the Batch Job on Application startup.
spring.batch.job.enabled=false
Upvotes: -1
Reputation: 56
I did have the same problem, looking in my code and after try all that is here, I did find that I had a SpringApplicationBuilder twice in 2 diferents class
Upvotes: 0
Reputation: 1609
I had the same problem. Spent hours trying to resolve.
Solution was the application was deploying twice on Tomcat.
When trying to clean Tomcat it gave an error.
Checking the server.xml Tomcat file I noticed the same was being deployed twice. There was also an unclosed "host" tag. Not sure which of these fixed it but relieved to have it working properly again.
Upvotes: 1
Reputation: 37
I had a similar problem, I resolved mine doing this:
package com.sample.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;
@Configuration
@EnableScheduling
public class JobExecutorConfig {
}
as configuration for spring boot. And I add this as jobclass:
package com.sample.jobs;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
@Component
public class Job {
private final Logger log = LoggerFactory.getLogger(this.getClass());
@Autowired
MyOtherClass moc;
@Scheduled(fixedRate = 60000) // every 60 seconds
public void doJob() {
log.debug("Job running !!!");
try {
moc.doSomething()
} catch (Exception e) {
log.error(e.getMessage());
}
finally {
log.debug("job Done !!!");
}
}
// examples of other CRON expressions
// * "0 0 * * * *" = the top of every hour of every day.
// * "*/10 * * * * *" = every ten seconds.
// * "0 0 8-10 * * *" = 8, 9 and 10 o'clock of every day.
// * "0 0/30 8-10 * * *" = 8:00, 8:30, 9:00, 9:30 and 10 o'clock every day.
// * "0 0 9-17 * * MON-FRI" = on the hour nine-to-five weekdays
// * "0 0 0 25 12 ?" = every Christmas Day at midnight
}
Upvotes: 2
Reputation: 567
I had the same problem today.
In my project i was using the scheduler with my spring boot application and in my ScheduledTaks class i was using the @Component annotation. But i made a mistake because the @Component represent a bean for my class and in my Application class i have create another bean for this class with the code:
public class Application extends SpringBootServletInitializer {
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(Application.class);
}
public static void main(String[] args) throws Exception {
SpringApplication.run(Application.class, args);
}
@Bean
public ScheduledTasks getScheduledTasks() {
return new ScheduledTasks();
}
}
I just remove this annotation and the scheduler works percectly.
Follow the example of my code ScheduledTasks class:
public class ScheduledTasks {
private static final SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");
@Scheduled(cron = "00 14 11 * * *")
public void reportCurrentTime() {
log.info("The date is: {} " + dateFormat.format(new Date()) );
}
}
And the result:
2016-10-20 11:13:41.298 INFO 6832 --- [ main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat started on port(s): 8080 (http)
2016-10-20 11:13:41.302 INFO 6832 --- [ main] br.com.Application : Started Application in 9.257 seconds (JVM running for 9.676)
2016-10-20 11:14:00.002 INFO 6832 --- [pool-2-thread-1] br.com.scheduler.ScheduledTasks : The date is: {} 11:14:00
Upvotes: -1
Reputation: 10813
I have faced the same situation and solved by this:
1) Scheduler Service
@Service
public class SchedulerService {
@Autowired
@Qualifier("WorkerClass")
private Worker worker;
@Scheduled(cron="0/5 * * * * ?", zone="Asia/Colombo")//zone is a sample
public void doSchedule() {
worker.work();
}
}
2) Worker Class
@Component("WorkerClass")
public class WorkerClass implements Worker {
@Override
public void work() {
doSomething();
}
protected void doSomething() {
system.out.pringln("What must I do?");
}
}
Upvotes: 0
Reputation: 11
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/spring/root-context.xml
/WEB-INF/spring/security/spring-security.xml
/WEB-INF/spring/mongo/mongo-config.xml
/WEB-INF/spring/appServlet/spring-context.xml
</param-value>
</context-param>
<servlet>
<servlet-name>appServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/appServlet/spring-context.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
That is my web.xml . So , you can see that "/WEB-INF/spring/appServlet/spring-context.xml" is loaded twice (once in context-param , once in servlet -> init-param ).
Upvotes: 0
Reputation: 7383
I had this same problem, and I eventually found out that the problem was occurring as a result of the beans being created in the root context
as well as the servlet context
.
So, to fix this, you need to separate the creation of the beans into the appropriate contexts.
This answer explains really well how to that and was what fixed my problem.
Upvotes: 2
Reputation: 146
You might want to check if you are scanning components for the same package in two different contexts if your app is WEB, e.g. applicationContext.xml and then again some-servlet.xml.
Upvotes: 0
Reputation: 31
I encountered similar problem. It could be because of below reasons.
A bug in spring versions https://jira.spring.io/browse/SPR-10830
Context being loaded twice.
The log4j.xml writing the logs twice. It happened in my case not sure about yours. If you have tried the other options, try this one also.
Upvotes: 3
Reputation: 702
According to this post and Spring Jira this is a bug in Spring Framework Scheduler component.
Upvotes: 0
Reputation: 341
Well in my case the job's bean had @Component annotation and i had this in my applicationContext.xml :
<task:annotation-driven/>
<bean id="getxxx" class="com.kitar.xxxx.jobs.RomeExample"></bean>
So the solution is to delete the bean definition (the second line) because :
<task:annotation-driven/>
: allow the detection of @Async and @Scheduled annotations on any Spring-managed object so no need to define the job's bean or it will be called twice.
Upvotes: 0
Reputation: 7199
i am using spring 4.0.3 and i have this problem. i solved it by renaming my beans.
to:
<task:annotation-driven executor="taskExecutor"
scheduler="taskScheduler" />
<task:executor id="taskExecutor" pool-size="25" />
<task:scheduler id="taskScheduler" pool-size="25" />
I noticed some INFO logging saying that no bean named taskScheduler found, creating a new instance. So I figured there were two instance of the taskScheduler.
Let me know if this works for you too :)
Upvotes: 0
Reputation: 450
it is happening because of context listener
Just remove
< listener >
< listener-class >org.springframework.web.context.ContextLoaderListener< /listener-class >
< /listener >
from web.xml it should work.
Upvotes: 6
Reputation: 119
I know the answer!!
Don't init your Scheduled twice
Loot at your web log :
WebApplicationContext
once and servlet once
so in your servlet.xml
don't do like this
import resource="classpath:applicationContext.xml"
Upvotes: 6
Reputation: 42834
If you look at the documentation, there is a note that explicitly calls out this phenomenon.
The note is under section 25.5.1 at this link, and reads:
Make sure that you are not initializing multiple instances of the same @Scheduled annotation class at runtime, unless you do want to schedule callbacks to each such instance. Related to this, make sure that you do not use @Configurable on bean classes which are annotated with @Scheduled and registered as regular Spring beans with the container: You would get double initialization otherwise, once through the container and once through the @Configurable aspect, with the consequence of each @Scheduled method being invoked twice.
I understand that this is merely suggestion at this point, but I do not think we have enough information to diagnose the issue further.
Upvotes: 30