spring scheduling a job with fixed delay and initial delay - spring

I am trying o schedule a method call. I want to schedule this method call as soon as server starts and then after every 30 seconds.
Below code:
#Configuration
#EnableScheduling
#EnableTransactionManagement
public class Schedular implements SchedulingConfigurer {
#Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
taskRegistrar.setScheduler(poolScheduler());
taskRegistrar.addTriggerTask(new Runnable() {
#Override
public void run() {
testScheduling();
}
}, new Trigger() {
#Override
public Date nextExecutionTime(TriggerContext triggerContext) {
Calendar nextExecutionTime = Calendar.getInstance();
nextExecutionTime.add(Calendar.SECOND, <some value from database>);
return nextExecutionTime.getTime();
}
});
}
#Bean
public TaskScheduler poolScheduler() {
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
scheduler.setThreadNamePrefix("poolScheduler");
scheduler.setPoolSize(10);
return scheduler;
}
public void testScheduling(){
System.out.println("Scheduling Testing");
}
}
The code below schedule the method code after 30 seconds after the server started BUT NOT just after server started. I know I need to do some other config to schedule the method call just after server start and then after every 30 seconds (or whatever time I want to).
I am using spring boot. Could anyone please suggest.
Also, is it possible to get both initial and fixeddelay/fixedrate value from database. I want to set the initial value as well from database
Thanks in advance.

let me know if this worked for you
#PostConstruct
#Scheduled(fixedDelay=30000)
public void testScheduling(){
System.out.println("Scheduling Testing");
}
Use the #PostConstuct annotation to start the method after the application starts.

You can use like below. I had used Spring Boot version v2.2.7
#Scheduled(fixedRateString = "${echo.interval(milliseconds)}", initialDelayString = "${echo.initialDelay(milliseconds)}")
The properties should be mentioned in "application.properties" file for the Spring Boot to detect and inject the values of the fixed rate and initial delay into the Scheduler.

Related

JobRunr Spring Boot: how to get notified if a recurring job - including retries - has failed

I'm using jobrunr 5.1.4 in my spring boot application. I have a simple service declaring a recurring job which allows for some retries. A single failing job run is not that relevant for me. Instead, I'm interested in getting notified after all jobs, i.e. the initial job including all the retries, have failed.
I thought JobRunr's JobServerFilter would be a good idea. But the onProcessed() method never gets triggered in case of an exception only in case of a successful job run. And the ApplyStateFilter gets triggered on every state change. Far too often for my requirement. Leaving me clueless, if a change to a FAILED state was the last in a series of jobs belonging together (initial job + allowed retried jobs).
A simple example would look like this:
#Service
public class JobScheduler {
#Job(name = "My Recurring Job", retries = 2, jobFilters = ExceptionFilter.class)
#Recurring(id = "my-recurring-job", cron = "*/10 * * * *")
public void recurringJob() {
throw new RuntimeException("foo");
}
}
A basic implementation of my JobFilter looks like this:
#Component
public class ExceptionFilter implements JobServerFilter, ApplyStateFilter {
#Override
public void onProcessing(Job job) {
log.info("onProcessing: {}", job.getJobName());
log.info(job.getJobState().getName().name());
}
#Override
public void onProcessed(Job job) {
log.info("onProcessed: {}", job.getJobName());
log.info(job.getJobState().getName().name());
}
#Override
public void onStateApplied(Job job, JobState jobState1, JobState jobState2) {
log.info("onStateApplied: {}", job.getJobName());
log.info("jobState1: {}", jobState1.getName().name());
log.info("jobState2: {}", jobState2.getName().name());
}
}
Is this use case even possible with JobRunr? Or does anyone have an idea how to solve this issue in a different way?
Thank you very much in advance for you support.
I think you're on the right track with onStateApplied from ApplyStateFilter.
You can use the following approach:
#Override
public void onStateApplied(Job job, JobState oldState, JobState newState) {
if (isFailed(newState) && maxAmountOfRetriesReached(job)) {
// your logic here
}
}
OnProcessed is not triggered as your job was not processed (due to the failure).

Scheduling a function in Spring boot

I am trying to schedule the following function to run every 10seconds. But it tells that it needs a void function. Is there any way to schedule the following function?
#Scheduled(fixedRate = 10000)
public FtpOutboundGateway getFiles() {
FtpOutboundGateway gateway = new FtpOutboundGateway(sf(), "mget", "payload");
gateway.setAutoCreateDirectory(true);
gateway.setLocalDirectory(new File("./downloads/"));
gateway.setFileExistsMode(FileExistsMode.REPLACE_IF_MODIFIED);
gateway.setFilter(new AcceptOnceFileListFilter<>());
gateway.setOutputChannelName("fileResults");
return gateway;
}
Since #Scheduled method is called internally by the threads in thread pool in a given interval or time, the annotation expect it to be void because it doesn't make sense to return values. if you want to track down the response the extract it to another method and call it from the #Scheduled method as well as from other services.
For this, we need to do the following things.
First put this Annotation #EnableScheduling at the class level.
then at the function which needs to be scheduled should be marked with #Scheduled.
The function that we are trying to schedule should not return anything.
#EnableScheduling
class <ClassName>{
//def
#Scheduled(fixedDelayString = "10000")
public void fxnName() {
//def
}
}

spring boot batch-- How can we read Jobperameters from ItemReader<>?

need to fetch the job parameters in ItemReades from jobLancher.run(job, parameters); in to ItemReader<>
Spring Boot------
i did like below.
i created listener where i can monitor after/before batch job running.
in listener i created static method which is return JobExecution entity where as we can find job related parameters like below.
public class MyListener extends Job***Listener**{
private static Jobexceution jobExecution1;
#Override
p v beforeJob(Jobexceution jobExecution){
this.jobExecution1=jobExecution;
}
p s JobExecution getJobExecution (){
return jobExecution1;
}
}

How to dynamically schedule a Spring Batch job with ThreadPoolTaskScheduler

I have a Spring Batch application in which I want to schedule jobs calls.
The scheduling interval is not known at build so I can't just annotate my Job with #Scheduled.This led me to use a ThreadPoolTaskScheduler.
The thing is the method schedule takes a Runnable as a parameter. Is it possible to schedule jobs this way ?
I can call the job directly from the following service but I can't schedule it.
Here is my the background of my problem, I tried to make it simple :
#Service
public class ScheduledProcessor{
private final ThreadPoolTaskScheduler threadPoolTaskScheduler;
private Application application;
#Autowired
public ScheduledProcessor(ThreadPoolTaskScheduler threadPoolTaskScheduler, Application application){
this.threadPoolTaskScheduler = threadPoolTaskScheduler;
this.application = application;
scheduledTasks = new ArrayList();
Trigger trigger = new CronTrigger("0/6 * * * * *");
//Here I am trying to schedule my job.
//The following line is wrong because a Job can't be cast to a Runnable but I wanted to show the intended behaviour.
threadPoolTaskScheduler.schedule((Runnable) application.importUserjob, trigger);
System.out.println("Job launch !");
}
And here is the JobBuilderFactory :
#Bean
public Job importUserJob(JobBuilderFactory jobs, Step s1, Step s2) {
return jobs.get("importUserJob")
.incrementer(new RunIdIncrementer())
.flow(s1)
.end()
.build();
}
I understand (well, I'm even not sure about that) that I can't directly cast a Job to a Runnable but is it possible to convert it in any way ? Or can you give me some advice about what to use for being able to dynamically schedule spring batch jobs ?
In case that changes something, I also need to be able to restart / skip my steps, as I currently can with the threadPoolTaskScheduler.
Thank you in advance for any help or hint you could provide.
I finally got how to do it !
I created a class which implements Runnable (and for convenience, extends Thread, which avoid the need to implement all of Runnable classes).
#Component
public class MyRunnableJob extends Thread implements Runnable{
private Job job;
private JobParameters jobParameters;
private final JobOperator jobOperator;
#Autowired
public MyRunnableJob(JobOperator jobOperator) {
this.jobOperator = jobOperator;
}
public void setJob(Job job){
this.job=job;
}
#Override
public void run(){
try {
String dateParam = new Date().toString();
this.jobParameters = new JobParametersBuilder().addString("date", dateParam).toJobParameters();
System.out.println("jobName : "+job.getName()+" at "+dateParam);
jobOperator.start(job.getName(), jobParameters.toString());
} catch (NoSuchJobException | JobInstanceAlreadyExistsException | JobParametersInvalidException ex) {
Logger.getLogger(MyRunnableJob.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
In my ScheduledProcessor class, I set a Job to myRunnable class and then pass it as a parameter of the schedule method.
public class SchedulingProcessor {
//Autowired fields :
private final JobLauncher jobLauncher;
private final Job importUserJob;
private final ThreadPoolTaskScheduler threadPoolTaskScheduler;
private final MyRunnableJob myRunnableJob;
//Other fields :
private List<ScheduledFuture> scheduledTasks;
#Autowired
public SchedulingProcessor(JobLauncher jobLauncher, Job importUserJob, ThreadPoolTaskScheduler threadPoolTaskScheduler, MyRunnableJob myRunnableJob) throws Exception {
this.jobLauncher=jobLauncher;
this.importUserJob=importUserJob;
this.threadPoolTaskScheduler=threadPoolTaskScheduler;
this.myRunnableJob=myRunnableJob;
Trigger trigger = new CronTrigger("0/6 * * * * *");
myRunnableJob.setJob(this.importUserJob);
scheduledTasks = new ArrayList();
scheduledTasks.add(this.threadPoolTaskScheduler.schedule((Runnable) myRunnableJob, trigger));
}
}
The scheduledTasks list is just to keep a control over the tasks I just scheduled.
This trick enabled me to dynamically (thanks to ThreadPoolTaskScheduler) schedule Spring Batch Jobs encapsulated in a class implementing Runnable. I wish it can help someone in the same case as mine.
Heres another way to trigger them from your spring context.
Job emailJob = (Job) applicationContext.getBean("xyzJob");
JobLauncher launcher = (JobLauncher) applicationContext
.getBean("jobLauncher");
launcher.run(emailJob, new JobParameters());

Unit Testing Spring ApplicationEvents - Events are getting published but the listeners aren't firing?

I'm trying to unit test the custom events that I've created in Spring and am running into an interesting problem. If I create a StaticApplicationContext and manually register and wire the beans I can trigger events and see the program flow through the publisher (implements ApplicationEventPublisherAware) through to the listener (implements ApplicationListener<?>).
Yet when I try to create a JUnit test to create the context using the SpringJunit4ClassRunner and #ContextConfiguration everything works well except that the ApplicationEvents are not showing up in the listener (I have confirmed that they are getting published).
Is there some other way to create the context so that ApplicationEvents will work correctly? I haven't found much on the web about unit testing the Spring events framework.
The events will not fire because your test classes are not registered and resolved from the spring application context, which is the event publisher.
I've implemented a workaround for this where the event is handled in another class that is registered with Spring as a bean and resolved as part of the test. It isn't pretty, but after wasting the best part of a day trying to find a better solution I am happy with this for now.
My use case was firing an event when a message is received within a RabbitMQ consumer. It is made up of the following:
The wrapper class
Note the Init() function that is called from the test to pass in the callback function after resolving from the container within the test
public class TestEventListenerWrapper {
CountDownLatch countDownLatch;
TestEventWrapperCallbackFunction testEventWrapperCallbackFunction;
public TestEventListenerWrapper(){
}
public void Init(CountDownLatch countDownLatch, TestEventWrapperCallbackFunction testEventWrapperCallbackFunction){
this.countDownLatch = countDownLatch;
this.testEventWrapperCallbackFunction = testEventWrapperCallbackFunction;
}
#EventListener
public void onApplicationEvent(MyEventType1 event) {
testEventWrapperCallbackFunction.CallbackOnEventFired(event);
countDownLatch.countDown();
}
#EventListener
public void onApplicationEvent(MyEventType2 event) {
testEventWrapperCallbackFunction.CallbackOnEventFired(event);
countDownLatch.countDown();
}
#EventListener
public void onApplicationEvent(OnQueueMessageReceived event) {
testEventWrapperCallbackFunction.CallbackOnEventFired(event);
countDownLatch.countDown();
}
}
The callback interface
public interface TestEventWrapperCallbackFunction {
void CallbackOnEventFired(ApplicationEvent event);
}
A test configuration class to define the bean which is referenced in the unit test. Before this is useful, it will need to be resolved from the applicationContext and initialsed (see next step)
#Configuration
public class TestContextConfiguration {
#Lazy
#Bean(name="testEventListenerWrapper")
public TestEventListenerWrapper testEventListenerWrapper(){
return new TestEventListenerWrapper();
}
}
Finally, the unit test itself that resolves the bean from the applicationContext and calls the Init() function to pass assertion criteria (this assumes you have registered the bean as a singleton - the default for the Spring applicationContext). The callback function is defined here and also passed to Init().
#ContextConfiguration(classes= {TestContextConfiguration.class,
//..., - other config classes
//..., - other config classes
})
public class QueueListenerUnitTests
extends AbstractTestNGSpringContextTests {
private MessageProcessorManager mockedMessageProcessorManager;
private ChannelAwareMessageListener queueListener;
private OnQueueMessageReceived currentEvent;
#BeforeTest
public void Startup() throws Exception {
this.springTestContextPrepareTestInstance();
queueListener = new QueueListenerImpl(mockedMessageProcessorManager);
((QueueListenerImpl) queueListener).setApplicationEventPublisher(this.applicationContext);
currentEvent = null;
}
#Test
public void HandleMessageReceived_QueueMessageReceivedEventFires_WhenValidMessageIsReceived() throws Exception {
//Arrange
//Other arrange logic
Channel mockedRabbitmqChannel = CreateMockRabbitmqChannel();
CountDownLatch countDownLatch = new CountDownLatch(1);
TestEventWrapperCallbackFunction testEventWrapperCallbackFunction = (ev) -> CallbackOnEventFired(ev);
TestEventListenerWrapper testEventListenerWrapper = (TestEventListenerWrapper)applicationContext.getBean("testEventWrapperOnQueueMessageReceived");
testEventListenerWrapper.Init(countDownLatch, testEventWrapperCallbackFunction);
//Act
queueListener.onMessage(message, mockedRabbitmqChannel);
long awaitTimeoutInMs = 1000;
countDownLatch.await(awaitTimeoutInMs, TimeUnit.MILLISECONDS);
//Assert - assertion goes here
}
//The callback function that passes the event back here so it can be made available to the tests for assertion
private void CallbackOnEventFired(ApplicationEvent event){
currentEvent = (OnQueueMessageReceived)event;
}
}
EDIT 1: The sample code has been updated with CountDownLatch
EDIT 2: Assertions didn't fail tests so the above was updated with a different approach**
I just run my app as SpringBootTest, application events working fine:
#TestComponent
public class EventTestListener {
#EventListener
public void handle(MyCustomEvent event) {
// nothing to do, just spy the method...
}
}
#RunWith(SpringRunner.class)
#SpringBootTest
public class MyEventTest {
#SpyBean
private EventTestListener testEventListener;
#Test
public void testMyEventFires() {
// do something that fires the event..
verify(testEventListener).handle(any(MyCustomEvent.class));
}
}
use the #Captor / ArgumentCaptor to verify the content of your event.
You can create a context manually.
For example: I had needed to check if my ApplicationListener<ContextClosedEvent> closed Cassandra connections:
#Test
public void testSpringShutdownHookForCassandra(){
ConfigurableApplicationContext ctx = new AnnotationConfigApplicationContext(CassandraConfig.class);
CassandraConnectionManager connectionManager = ctx.getBean(CassandraConnectionManager.class);
Session session = connectionManager.openSession(testKeySpaceName);
Assert.assertFalse( session.isClosed() );
ctx.close();
Assert.assertTrue( session.isClosed() );
}

Resources