35

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?

Elrond_EGLDer
  • 47,430
  • 25
  • 189
  • 180
KS1
  • 919
  • 5
  • 18
  • 32

23 Answers23

27

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.

Community
  • 1
  • 1
nicholas.hauschild
  • 40,613
  • 9
  • 117
  • 115
  • 4
    Ok, so exactly in what class should the scheduled method then be declared? If I have it in a class annotated with @Service, I get this behaviour/ – demaniak Oct 05 '14 at 17:22
  • In my case I wanted to also manually invoke the scheduled function with @WebServlet – ropo Mar 01 '17 at 09:30
5

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"
Roman C
  • 47,329
  • 33
  • 60
  • 147
user2408678
  • 79
  • 1
  • 2
3

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.

Girish Kumar
  • 402
  • 5
  • 5
  • 55
    Is that like deleting system32 to make your computer run faster? – Matthew Jan 16 '14 at 11:24
  • Hello Girish, I have removed this code from web.xml and my spring @Scheduled started working but mine is Spring MVC application. When I try to run the index.jsp on it. It shows me error like: No WebApplicationContext found: no ContextLoaderListener registered? What is the solution for this? – Chintan Patel May 26 '15 at 06:39
3

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.

Chethan
  • 381
  • 1
  • 6
  • 13
2

I encountered similar problem. It could be because of below reasons.

  1. A bug in spring versions https://jira.spring.io/browse/SPR-10830

  2. Context being loaded twice.

  3. 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.

jchandrra
  • 21
  • 1
1

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.

Community
  • 1
  • 1
jlars62
  • 6,366
  • 7
  • 34
  • 56
1

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
}
ciberkids
  • 27
  • 1
0

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 :)

Titi Wangsa bin Damhore
  • 6,673
  • 4
  • 28
  • 33
0

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.

Khalil Kitar
  • 331
  • 4
  • 8
0

According to this post and Spring Jira this is a bug in Spring Framework Scheduler component.

Prashant
  • 682
  • 2
  • 9
  • 26
0

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.

Bran
  • 136
  • 8
0
<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 ).

0

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?");
    }

}
Bahadir Tasdemir
  • 8,208
  • 3
  • 44
  • 55
0

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.

Daryn
  • 1,323
  • 1
  • 10
  • 18
0

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

Chocolim
  • 54
  • 1
  • 2
0

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.

Nasir
  • 46
  • 4
0

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.

0

Hi,

It could be because of any of the 2 reasons:

  1. App Context getting loaded twice, or
  2. You have multiple app instances running (for scaling purposes).

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...
 }                    
 }
Kush Sharma
  • 1
  • 1
  • 3
0

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:

  • Remove the @Component Annotation (on the Class which contains the @Scheduled method)
  • Manually add the Class which contains the @Scheduled Method into the ApplicationContext

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);
    }

}
goblingift
  • 371
  • 3
  • 18
-1

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
Lucas Araújo
  • 270
  • 1
  • 3
  • 15
-1

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
jalpa
  • 192
  • 1
  • 1
  • 13
-1

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

oleeksandr
  • 11
  • 1
-1

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">