Search code examples
scheduled-tasksmicronaut

How to force Micronaut scheduled jobs wait for application startup to complete?


I implemented some scheduled jobs in my micronaut application with the @Scheduled - annotation like described here: https://guides.micronaut.io/latest/micronaut-scheduled-gradle-java.html
This works just fine except that all jobs get executed before the application startup is completed. That is a problem because my startup process contains executing of db update jobs and stuff that needs to be done before everything else.
So how do I force all scheduled jobs to wait until the application start process is finished?
With "finished" I mean the moment

[main] INFO io.micronaut.runtime.Micronaut - Startup completed in 9127ms. Server Running: http://localhost:8080

appears in my console.

I know I could schedule all my jobs manually, but that would mean to implement an event listener to all my classes that contains scheduled tasks. I don't want to rely on other devs to remember this and it sounds like too much work for such a simple requirement.


Solution

  • The simplest way to achieve it is to use Micronaut 4 expression language and an event listener.

    Given the following job we are using the expression language value #{!this.paused} to pause the job unless the boolean flag paused is set to false.

    @Singleton
    public class SomeJob {
    
        private boolean paused = true;
    
        @Scheduled(fixedRate = "5m", condition = "#{!this.paused}" )
        public void doSomething() {
            System.out.println("The job runs...");
        }
    
        public boolean isPaused() {
            return paused;
        }
    
        @EventListener
        public void onStartup(StartupEvent event) {
            this.paused = false;
        }
    }
    

    Note that the expression language is a Micronaut 4 feature and not available in previous versions of Micronaut.

    If you have multiple jobs you could implement something like this.

    public sealed interface Pausable {
        void pause();
        void unpause();
    
        @Singleton
        final class SomeJobA implements Pausable {
    
            private boolean paused = true;
    
            @Scheduled(fixedRate = "5m", condition = "#{!this.paused}" )
            public void doSomething() {
                System.out.println("The job runs...");
            }
    
            @Override
            public void pause() { this.paused = true; }
    
            @Override
            public void unpause() { this.paused = false; }
        }
    
        @Singleton
        final class SomeJobB implements Pausable {
    
            private boolean paused = true;
    
            @Scheduled(fixedRate = "5m", condition = "#{!this.paused}" )
            public void doSomething() {
                System.out.println("The job runs...");
            }
    
            @Override
            public void pause() { this.paused = true; }
    
            @Override
            public void unpause() { this.paused = false; }
        }
    }
    

    Then you implement the event listener in your Application.java.

    @Singleton
    public class Application {
    
      private static ApplicationContext ctx;
    
      public static void main(String[] args) {
        ctx = Micronaut.run(Application.class, args);
      }
    
      @EventListener
      public void onStartup(StartupEvent event) {
        ctx.getBeansOfType(Pausable.class).forEach(Pausable::unpause);
      }
    }