Shutting down after task completion - spring

I have spring application written in the following way -
#SpringBootApplication
#Import({
ApplicationLoader.class
})
public class MyApplication {
#Bean
public Clock clock() {
return Clock.systemDefaultZone();
}
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
The ApplicationLoader class looks like below
public class ApplicationLoader {
private final MyService myService;
public ApplicationLoader(MyService myService) {
// create
this.myService = myService;
}
#PostConstruct
void startJob() {
// perform some long running task
myService.runLongRunningTask();
}
}
Once ApplicationLoader finishes its job, Spring Application continues to run while I want it to stop.
Is there a way I can introduce a stop to this entire application lifecycle which runs after the application is done performing the long running task? I don't mind a rewrite of ApplicationLoader or MyApplication if there is a better way. My aim is to instantiate MyService and run myService.runLongRunningTask() based on certain runtime conditions.

Related

Springboot Scheduler runs 2 times and ends

I trying to start Scheduled job, but instead printing xxx every 5 seconds infinitely, what happens is 'xxx' printed twice - on the same second (doesn't wait 5 secs) , then prints 'EXIT' .
Why does it happen?
#EnableScheduling
#SpringBootApplication
public class App implements CommandLineRunner{
#Autowired
private Job job;
public static void main(final String... args) {
try (final ConfigurableApplicationContext context = new SpringApplicationBuilder(App.class)
.web(WebApplicationType.NONE).run(args)) {
}
System.out.println("EXIT");
}
#Override
public void run(String... args) throws Exception {
job.run();
}
}
#Component
public class Job {
#Scheduled(fixedRateString = "5000")
public void run(){
System.out.println("xxx");
}
}
in console I only see:
z.r.AsyncReporter$BoundedAsyncReporter : Timed out waiting for in-flight spans to send.
o.s.s.c.ThreadPoolTaskScheduler : Shutting down ExecutorService 'taskScheduler'
You would need to add the #EnableScheduling annotation for enabling scheduled tasks capabilities. Additionally, you will need to change how you're using SpringApplicationBuilder as its scope (and lifetime) is currently limited by the try-with-resources statement. The following should work:
#EnableScheduling
#SpringBootApplication
public class App implements CommandLineRunner {
public static void main(final String... args) {
new SpringApplicationBuilder(App.class)
.web(WebApplicationType.NONE)
.build()
.run(args);
}
[...]
The fixedRate immediately fires. Therefore if you want to wait for the initial run you should add initialDelay as follows;
#Scheduled(fixedRate = 5000, initialDelay = 5000)
And when the applicaiton finishes the schedule cannot stop termination...
And of course #EnableScheduling is required...

How to schedule a cron job in spring boot without using #Scheduled() annotation

In spring boot, can I schedule a spring job by not using #Scheduled annotation to a method?
I am working with spring job in the spring boot. I want to schedule a job by using cron expression, but without using #Scheduled(cron = " ") annotation to the method.
I know that I can schedule a job inside this method as below.
#Scheduled (cron = "0 10 10 10 * ?")
public void execute() {
/ * some job code * /
}
But I want it to be dynamic so that I can take a cron expression as input from the user and schedule it.
I came up with a working example since I found your question interesting and have been interested in this problem before. It's based entirely on the source code so I have no idea if it comes close to following best practice. Nonetheless, you may be able to tune it to your needs. FYI, you don't necessarily need to create a new ScheduledTaskRegistrar object - I figured that since your objective is a dynamic scheduler, you wouldn't be interested in defining your tasks purely in the overwritten method.
#SpringBootApplication
public class TaskScheduler implements SchedulingConfigurer, CommandLineRunner {
public static void main(String[] args){SpringApplication.run(TaskScheduler.class, args);}
List<CronTask> cronTasks;
#Override
public void run(String... args) throws Exception {
CronTask task = this.createCronTask(new Runnable() {
#Override
public void run() {
System.out.println(LocalDateTime.now());
}
}, "1/10 * * * * *");
ScheduledTaskRegistrar taskRegistrar = new ScheduledTaskRegistrar();
taskRegistrar.addCronTask(task);
configureTasks(taskRegistrar);
Thread.sleep(51);
taskRegistrar.destroy();
taskRegistrar = null;
ScheduledTaskRegistrar taskRegistrar2 = new ScheduledTaskRegistrar();
taskRegistrar2.addCronTask(task);
configureTasks(taskRegistrar2);
}
#Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
// "Calls scheduleTasks() at bean construction time" - docs
taskRegistrar.afterPropertiesSet();
}
public CronTask createCronTask(Runnable action, String expression) {
return new CronTask(action, new CronTrigger(expression));
}
}
I have experience using cron jobs in Azure and other places. Programming in Java, I have typically used #Scheduled with fixed times just for the sake of simplicity. Hope this is useful to you though.
Here is my working example, If somebody wants to use TaskScheduler without using #Scheduled Annotation
#Configuration Class
#Configuration
public class SchedulerConfig implements SchedulingConfigurer {
final Logger LOGGER = LogManager.getLogger(SchedulerConfig.class);
#Override
public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) {
LOGGER.debug("Creating Async Task Scheduler");
scheduledTaskRegistrar.setTaskScheduler(taskScheduler());
}
// This is mandatory otherwise it will to be able to find bean of
// taskScheduler. Without this it was giving runtime error says, can not find
// taskScheduler bean.
#Bean
public TaskScheduler taskScheduler() {
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
scheduler.setPoolSize(20); // Better to read it from property file.
scheduler.setThreadNamePrefix("ThreadScheduler-");
scheduler.initialize();
return scheduler;
}
}
Scheduler Class which is called from Application class.
#Component
public class MyTaskScheduler {
private TaskScheduler taskScheduler;
// Here we are auto-wiring taskScheduler, that's why need to create
// taskScheduler bean in configuration class
#Autowired
public void setScheduler(TaskScheduler scheduler) {
this.taskScheduler = scheduler;
}
public void schedule() {
taskScheduler.scheduleWithFixedDelay(new Runnable(){
#Override
public void run() {
System.out.println("I am running after every 1 second");
}
}, 1000);
}
}
If in any chance #Configuration annotation is not working so put #EnableConfigurationProperties at main running class.
Make sure that you put #EnableScheduling at Application class, so main runnable class will look like
#SpringBootApplication
#EnableScheduling
#EnableConfigurationProperties
public class MainApplication implements CommandLineRunner {
#Autowired
MyTaskScheduler myTaskScheduler;
public static void main(String[] args) {
final Logger logger = LogManager.getLogger(MainApplication.class);
SpringApplication.run(MainApplication.class, args);
logger.info("Application started");
}
#Override
public void run(String... args) throws Exception {
myTaskScheduler.schedule();
}
}
This answer is similar to the two previous ones, but is more compact because it leverages the scheduling registrar already provided in the application context:
#Configuration
#EnableScheduling
public class Schedule implements SchedulingConfigurer {
private final transient WorkflowTriggerService workflowTriggerService;
public Schedule(final WorkflowTriggerService workflowTriggerService) {
this.workflowTriggerService = workflowTriggerService;
}
#Override
public void configureTasks(final ScheduledTaskRegistrar taskRegistrar) {
for (final WorkflowTrigger trigger : workflowTriggerService.getWorkflowTriggersWithSchedules()) {
taskRegistrar.addCronTask(new WorkflowTask(trigger), trigger.getSchedule());
}
}
}
Each WorkflowTrigger returned by the service has its own cron schedule, and therefore allows dynamic registration of scheduled tasks that are not known at compilation time.

Spring batch with multiple main and scheduler

I am working on a Spring Batch project that has two Main classes with #SpringBootApplication in it. I also have to use the #Scheduler and associate it only with one Main class. But the issue here is no matter which class I run, the scheduler is getting executed. Snippets below,
MainApp1.java
#SpringBootApplication
#EnableScheduling
public class MainApp1{
public static void main(String[] args) {
SpringApplication.run(MainApp1.class, args);
}
}
MainApp2.java
#SpringBootApplication
public class MainApp2{
public static void main(String[] args) {
SpringApplication.run(MainApp2.class, args);
}
}
Scheduler.java
#Configuration
public class TmsBatchSenderScheduler {
#Scheduled(fixedDelay = 5000)
public void myScheduler() {
}
}
I think what's happening here is the scheduler is getting created for both mains because of the #Configuration annotation. Is there a way to achieve this? I want the scheduler to run only when the MainApp1 is run.
Thanks in advance :)

Spring Boot insert sample data into database upon startup

What is the right way for creating test data upon server startup and inserting them into the database (I'm using a JPA/JDBC backed Postgres instance).
Preferably in form of creating Entities and having them persisted through a Repository interface rather than writing plain SQL code. Something like RoR's Rake db:seed helper.
If the framework exposes a hook for doing stuff when all the beans have been injected and the database is ready, that could also work.
You can catch ApplicationReadyEvent then insert demo data, for example:
#Component
public class DemoData {
#Autowired
private final EntityRepository repo;
#EventListener
public void appReady(ApplicationReadyEvent event) {
repo.save(new Entity(...));
}
}
Or you can implement CommandLineRunner or ApplicationRunner, to load demo data when an application is fully started:
#Component
public class DemoData implements CommandLineRunner {
#Autowired
private final EntityRepository repo;
#Override
public void run(String...args) throws Exception {
repo.save(new Entity(...));
}
}
#Component
public class DemoData implements ApplicationRunner {
#Autowired
private final EntityRepository repo;
#Override
public void run(ApplicationArguments args) throws Exception {
repo.save(new Entity(...));
}
}
Or even implement them like a Bean right in your Application (or other 'config') class:
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Bean
public CommandLineRunner demoData(EntityRepository repo) {
return args -> {
repo.save(new Entity(...));
}
}
}
From Spring documentation: http://docs.spring.io/spring-boot/docs/1.5.4.RELEASE/reference/htmlsingle/#howto-database-initialization
Initialize a database using Hibernate
A file named import.sql in the root of the classpath will be executed on startup if Hibernate creates the schema from scratch (that is if the ddl-auto property is set to create or create-drop). This can be useful for demos and for testing if you are careful, but probably not something you want to be on the classpath in production. It is a Hibernate feature (nothing to do with Spring).
You can do like this
#SpringBootApplication
public class H2Application {
public static void main(String[] args) {
SpringApplication.run(H2Application.class, args);
}
#Bean
CommandLineRunner init (StudentRepo studentRepo){
return args -> {
List<String> names = Arrays.asList("udara", "sampath");
names.forEach(name -> studentRepo.save(new Student(name)));
};
}
}

Create Spring boot standalone app

I'm trying to figure out how to build a Spring Boot standalone app. Of course to have things autowired requires some initial context starting point. If I just try to Autowire a class to run a job it is null even if I make it static.
Is there a way to use Spring #Services in a standalone non-web app?
#SpringBootApplication
public class MyApplication {
#Autowired
private static JobRunnerService job;
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
job.send(); //job is null !
}
}
So first wired in a static JobRunnerService to the main running MyApplication the JobRunner(Service) Class has a non-static SshSessionService wired into it.
the SshSession(Service) finally just has a no-arg constructor.
#Service("jobRunnerService")
public final class JobRunner implements JobRunnerService{
#Autowired
private SshSessionService ssh;
#Autowired
public JobRunner(SshSessionService ssh){
this.ssh = ssh;
}
public void sendToAgent() { ....
}
#Service("sshSessionService")
public class SshSession implements SshSessionService {
public SshSession() {
}
}
It starts off being null at the JobRunnerService job reference.
Several different solutions comes to mind:
If you take a look at the SpringApplication.run() method you will notice that it returns a ApplicationContext. From that, you can fetch the JobRunnerService, e.g.
#SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
ApplicationContext ctx = SpringApplication.run(MyApplication.class, args);
JobRunnerService job = ctx.getBean(JobRunnerService.class);
job.send();
}
}
Another solution is to use #PostConstruct annotation for the send() method:
#Service("jobRunnerService")
public class JobRunner implements JobRunnerService {
#PostConstruct
public void send() { ... }
}
However in your case, I would implement the ApplicationRunner interface, either as a separate bean which autowires the JobRunnerService and then calls its send() method
#Component
public class SendRunner implements ApplicationRunner {
#Autowired
private JobRunnerService job;
#Override
public void run(ApplicationArguments args) {
job.send();
}
}
or let the JobRunner implement the ApplicationRunner interface directly:
#Service("jobRunnerService")
public class JobRunner implements JobRunnerService, ApplicationRunner {
#Override
public void send() { ... }
#Override
public void run(ApplicationArguments args) {
send();
}
}
You haven't provided the code for JobRunnerService but I am assuming it has a default constructor and that it is annotated by #Component for Spring to figure it out as a bean before you can actually autowire it. your job is null probably because it's not able to find an autowired bean for JobRunnerService and that's probably because you don't have an identifier for Spring to scan and create bean of type JobRunnerService
You can use #Servicesor #Component to the JobRunnerService class then add annotation #ComponentScan("package of JobRunnerService") below #SpringBootApplication, see this link:
How to scan multiple paths using the #ComponentScan annotation?
You need a few steps to get your standalone app working:
A class with main() method.
A #SpringBootApplication annotation to your main class.
And a call to the SpringApplication.run() method.
package com.example.myproject;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
#SpringBootApplication // same as #Configuration #EnableAutoConfiguration #ComponentScan
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
As noted, the #SpringBootApplication is a composite annotation which consist of #Configuration #EnableAutoConfiguration and #ComponentScan. In other words, it can be replaced by the three latter annotations. Alternatively, you can use the alias scanBasePackage or scanBasePackageClasses to customize which directories that should be used for component scanning.
The example is copied from the #SpringBootApplication paragraph in the Spring Boot reference docs (see link above). If you would like to quick start your project, complete with build scripts (Maven or Gradle), dependencies, etc, you can generate a project skeleton using the Spring Initializr
I'm trying to run as Thread/runnable now as mentioned in the Spring document 3. Task Execution and Scheduling..
import org.springframework.core.task.TaskExecutor;
public class TaskExecutorExample {
private class MessagePrinterTask implements Runnable {
private String message;
public MessagePrinterTask(String message) {
this.message = message;
}
public void run() {
System.out.println(message);
}
}
private TaskExecutor taskExecutor;
public TaskExecutorExample(TaskExecutor taskExecutor) {
this.taskExecutor = taskExecutor;
}
public void printMessages() {
for(int i = 0; i < 25; i++) {
taskExecutor.execute(new MessagePrinterTask("Message" + i));
}
}
}
So in my case I'm trying...
#Service("jobRunnerService")
#Component
public class JobRunner implements JobRunnerService, ApplicationRunner{
#Autowired
public TaskExecutor taskExecutor;
#Autowired
private SshSessionService ssh;
private class JobTask implements Runnable{
public void run(){
Boolean success = connectToAgent();
if(success){
log.debug("CONNECTED!!!");
}
}
}
/**
* Construct JobRunner with TaskExecutor
* #param taskExecutor
*/
#Autowired
public JobRunner(TaskExecutor taskExecutor, SshSessionService ssh) {
this.taskExecutor = taskExecutor;
this.ssh = ssh;
}
private Map<String, String> sessionParams;
private final Logger log = LoggerFactory.getLogger(this.getClass());
#Override
public void run(ApplicationArguments args) {
/**
* Starting point of application
*
*/
taskExecutor.execute(new JobTask());
}
just getting org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [org.springframework.core.task.TaskExecutor] found for dependency
How can i get the imported lib to be accepted as a TaskExecutor Bean ??

Resources