How can I run a job configured using Spring-Batch right after application startup?
Currently I'm specifying an exact time using cron job, but that requires to change the cron every time I restart the application:
#JobRegistry, #Joblauncher and a Job.
I execute the job as follows:
#Scheduled(cron = "${my.cron}")
public void launch() {
launcher.run(job, params);
}
Checking aroud Spring code I have found SmartLifecycle
An extension of the Lifecycle interface for those objects that require
to be started upon ApplicationContext refresh and/or shutdown in a
particular order. The isAutoStartup() return value indicates whether
this object should be started at the time of a context refresh.
Try creating a custom bean implementing SmartLifecycle and setting autoStartup; when this custom bean start method is invoked launch your job.
A few options that I can think of on the places to put your startup logic:
.1. In a bean #PostConstruct annotated method, reference is here - http://docs.spring.io/spring/docs/current/spring-framework-reference/html/beans.html#beans-postconstruct-and-predestroy-annotations
.2. By implementing an ApplicationListener, specifically for either ContextStartedEvent or ContextRefreshedEvent. Reference here - http://docs.spring.io/spring/docs/current/spring-framework-reference/html/beans.html#context-functionality-events
Related
I need to run some methods only once, when application starts for the first time. Is there any basic way to do this in Spring java/kotlin?
UPD:
For the first time means that i have new app that should run some methods on startup, but when i restart this app, i don't want it to run this method again
I would suggest that you use the ApplicationReadyEvent. According to the documentation, the ApplicationReadyEvent is an:
Event published as late as conceivably possible to indicate that the application is ready to service requests.
So, you could implement your own ApplicationListener listening for the ApplicationReadyEvent and run your code only when the application is ready, for example:
#Component
#Order(0)
class CustomApplicationListener implements ApplicationListener<ApplicationReadyEvent> {
#Override
public void onApplicationEvent(ApplicationReadyEvent event) {
// run your code
}
}
You can annotate your method with #PostConstruct (make sure you've made spring bean).
At the beginning of that method you can check if file "dummy.txt" exists in project directory if not proceed with method. At the end create file "dummy.txt".
You can also add property while starting java process first time like this:
java -DfirstTime=true -jar myjar.jar
Make firstTime false by default
I am integrating Spring Batch into an existing Spring webapp and have created three simple jobs as a learning process. Then I created a set of ancestor classes for jobs/readers/writers that can be used to migrate our old batch jobs easily.
We intend to run the jobs asynchronously from within Tomcat as separate threads. I have created a UI to manage and start/stop them.
I have a class annotated with #Configuration and #EnableBatchProcessing which does the job of setting up all the global classes for batch mode; it also then scans the base package for all our jobs for specific ancestor class. Then it suffixes each class name for the corresponding *Factory class, does a getBean for that class and uses it to register each job:
Job job = jobFactory.createInstanceForRegistration();
jobRegistry.register(new ReferenceJobFactory(job));
The createInstanceForRegistration method uses applicationContext.getBean to get the instances of the job/read/writer classes to put the job and steps together and then finally:
((SimpleJob)job).setSteps(steps);
return job;
In the UI, the jobs are listed and I should be able to start them. But when I do:
batchAuditId = jobOperator.start(jobName, parameters);
Suddenly, a NPE is thrown from w/i AbstractJob/execute # line 298:
jobParametersValidator.validate(execution.getJobParameters());
because jobParametersValidator is null - despite the fact that it is initialized with a default in the class itself:
private JobParametersValidator jobParametersValidator =
new DefaultJobParametersValidator();
More to the point, the job class constructor has code that sets several items, including the override of the validator:
super(BATCH_NAME, BATCH_TYPE_CODE, BATCH_FUNCTION, BATCH_PROCESS_AREA);
setLogger(LoggerFactory.getLogger(CustomJob.class));
getLogger().debug("constructed: {}", this.toString());
setParametersRequired(Boolean.TRUE); // this job requires parameters
setRestartable(Boolean.TRUE);
setJobParametersValidator(new CustomJobParametersValidatorImpl());
During deployment, when Spring creates all the classes, I can step through the code and the override validator is created and set for that job.
Yet when jobOperator.start executes, the NPE is thrown.
I can find no reason for this. It is almost like jobOperator.start somehow creates a new instance of the job class in a way that doesn't use the existing instance or my custom constructor.
Can anyone explain what is going on?
I have a method which is scheduled to run after every 20 minutes. I simply used #Scheduled annotation in Spring boot , However I need a scheduler which takes delay time at runtime. E.g. If I want to be able to change the delay time/frequency of method execution at runtime without stopping the application i.e. change the frequency in DB and Code should adapt it.
#Scheduled(initialDelay=15*60*1000, fixedRate=20*60*1000)
public void MyMethod() {
// Code to repeat after every 20 minutes
}
}
The fixed rate in the code should be variable and taken at the runtime. Is it possible to achieve it ?
You can do it with refresh the spring context after change the fixed rate in the properties file, or using spring cloud config, but this will raise some issues - refresh() should destroy all beans currently living in the context (singletons etc) and recreate them, so any bootstrapping that might happen will happen again.
Here is an reference:
Is spring application context reloading via ConfigurableApplicationContext refresh() considered bad-practice
It is my understanding that when you use Spring Cloud's RefreshScope annotation, a Proxy to the data is injected, and the proxy is automatically updated if the backing information is changed. Unfortunately, I need to find a way to be alerted when that refresh occurs, so that my code can re-read the data from the refresh-scoped bean.
Simple example: A scheduled task whose schedule is stored in Cloud Config. Unless you wait until the next execution of the task (which could take a while) or regularly poll the configuration (which seems wasteful), there's no way to know if the configuration has changed.
EnvironmentChangeEvent is fired when there's a change in Environment. In terms of Spring Cloud Config it means it's triggered when /env actuator endpoint is called.
RefreshScopeRefreshedEvent is fired when refresh of #RefreshScope beans has been initiated, e.g. /refresh actuator endpoint is called.
That means that you need to register ApplicationListener<RefreshScopeRefreshedEvent> like that:
#Configuration
public class AppConfig {
#EventListener(RefreshScopeRefreshedEvent.class)
public void onRefresh(RefreshScopeRefreshedEvent event) {
// Your code goes here...
}
}
When the refresh occurs EnvironmentChangeEvent would be raised in your config client, as the documentation states:
The application will listen for an EnvironmentChangedEvent and react
to the change in a couple of standard ways (additional
ApplicationListeners can be added as #Beans by the user in the normal
way).
So, you can define your event listener for this event:
public class YourEventListener implements ApplicationListener<EnvironmentChangeEvent> {
#Override
public void onApplicationEvent(EnvironmentChangeEvent event) {
// do stuff
}
}
I think an approach can be to annotate with #RefreshScope all your bean that have properties externalized by the configuration and annotated within #Value ( "${your.prop.key}" ) annotation.
These properties are updated when they changed on configuration.
More specifically, after the refresh of properties and application context under scope RefreshScope, an event RefreshScopeRefreshedEvent is triggered. You can have a listener for this given the understanding that the properties has finished updates (you can be sure to capture updated values only).
I'm looking for a lib that allow me to do
define a worker that will be invoked once on a specific time in the future (not need the re-schedule / cron like featrure) i.e. a Timer
The worker should accept a context which withe some parameters / inputs
all should be persistent in the DB (or file) the worker
worker should be managed by spring -- spring should instantiate the worker so it can be injected with dependencies
be able to create timers dynamically via API and not just statically via spring XML beans
nice to have:
support a cluster i.e. have several nodes that can host a worker. each store jobn in the DB will cause invokaction of ONE work on one of the nods
I've examined several alternatives none meets the requirements:
Quartz
when using org.springframework.scheduling.quartz.JobDetailBean makes quartz create your worker instance (and not by spring) so you can't get dependecy ijection, (which will lead me to use Service Locator which I want to avoid)
while using org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean you can't get a context. your Worker expose one public method that accepts no arguments.In addition when using MethodInvokingJobDetailFactoryBean you can't use persistence (form the Javadoc)
Note: JobDetails created via this FactoryBean are not serializable and thus not suitable for persistent job stores. You need to implement your own Quartz Job as a thin wrapper for each case where you want a persistent job to delegate to a specific service method.
Spring's Timer and simple JDK Timers does not support the persistence / cluster feature
I know I can impl thing myself using a DB and Spring (or even JDK) Timers but I prefer to use an a 3r party lib for that.
Any suggestions?
If you want to create the job details to generate triggers/job-details at runtime and still be able to use Spring DI on your beans you can refer to this blog post, it shows how to use SpringBeanJobFactory in conjunction with ObjectFactoryCreatingFactoryBean to create Quartz triggering objects at runtime with Spring injected beans.
For those interested in an alternative to Quartz, have a look at db-scheduler (https://github.com/kagkarlsson/db-scheduler). A persistent task/execution-schedule is kept in a single database table. It is guaranteed to be executed only once by a scheduler in the cluster.
Yes, see code example below.
Currently limited to a single string identifier for no format restriction. The scheduler will likely be extended in the future with better support for job-details/parameters.
The execution-time and context is persistent in the database. Binding a task-name to a worker is done when the Scheduler starts. The worker may be instantiated by Spring as long as it implements the ExecutionHandler interface.
See 3).
Yes, see code example below.
Code example:
private static void springWorkerExample(DataSource dataSource, MySpringWorker mySpringWorker) {
// instantiate and start the scheduler somewhere in your application
final Scheduler scheduler = Scheduler
.create(dataSource)
.threads(2)
.build();
scheduler.start();
// define a task and a handler that named task, MySpringWorker implements the ExecutionHandler interface
final OneTimeTask oneTimeTask = ComposableTask.onetimeTask("my-onetime-task", mySpringWorker);
// schedule a future execution for the task with a custom id (currently the only form for context supported)
scheduler.scheduleForExecution(LocalDateTime.now().plusDays(1), oneTimeTask.instance("1001"));
}
public static class MySpringWorker implements ExecutionHandler {
public MySpringWorker() {
// could be instantiated by Spring
}
#Override
public void execute(TaskInstance taskInstance, ExecutionContext executionContext) {
// called when the execution-time is reached
System.out.println("Executed task with id="+taskInstance.getId());
}
}
Your requirements 3 and 4 do not really make sense to me: how can you have the whole package (worker + work) serialized and have it wake up magically and do its work? Shouldn't something in your running system do this at the proper time? Shouldn't this be the worker in the first place?
My approach would be this: create a Timer that Spring can instantiate and inject dependencies to. This Timer would then load its work / tasks from persistent storage, schedule them for execution and execute them. Your class can be a wrapper around java.util.Timer and not deal with the scheduling stuff at all. You must implement the clustering-related logic yourself, so that only one Timer / Worker gets to execute the work / task.