Spring Quatrz Dynamic Annotation Based Java Config - spring

Sorry i had to ask it over here as i searched a lot tried many things but failed to achieve the result.
So what i am trying to do is i have a service which give me the list of the Jobs along with the interval at which they need to run so , what i want to do is loop over the list of the jobs and schedule them using Spring Quartz and i want to do them using the java config and not XML based .
This application will be a web application which will be running on a server, a maven project.
I found result and the are mainly using the Custom Annotation . Is there any sample or example which i can try or use.?
The problem is we are very new to all this and none of us have a idea how to proceed with this so any help is very appreciated.
Thanks in advance,
Vishesh

Have your #Confugation class implement SchedulingConfigurer. This allows you to schedule tasks programmatically. For example
#Configuration
#EnableScheduling
public class TaskConfiguration implements SchedulingConfigurer{
#Autowired
private TaskDao taskDao; // implement TaskDao to read tasks from DB
#Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar){
List<Task> tasks = taskDao.findAllTasks();
for(Task task : tasks){
Runnable taskJob = createTaskJob(task); // create task by reflection
taskRegistrar.addCronTask(taskJob , task.getCronExpression());
}
}
}
public class Task implements Serializable{
private String cronExpression;
private String jobClass;
//getters and setters
}

#ekem chitsiga workaround will also do. Following is another alternate to run your cron jobs based on the cron epressions you set.
#Configuration
#EnableScheduling
public class Scheduler {
private static final Logger logger = LoggerFactory
.getLogger(Scheduler.class);
#Autowired
private JobRepository jobRepository;
/*
* Cron expression ="0 0/1 * 1/1 * ?" for every minute
*/
#Scheduled(cron = "0 0/1 * 1/1 * ?")
public void sendNotification() {
logger.info("Initializing Cron job scheduler....");
//Do you scheduler specific work here
}
}

Related

Test Kafka consumer using EmbddedKafka fail when launching a batch of tests

I'm testing my Kafka Consumer in Spring Boot. My consumer are similar to the following
#Slf4j
#Component
#RequiredArgsConstructor
public class KafkaPaymentConsumer {
private final PaymentInterface paymentInterface;
#KafkaListener(topics = "#{'${kafka.topic.payment}'}",
groupId = "#{'${kafka.group-id}'}")
public void consumePaymentEvents(PaymentEvent paymentEvent) {
paymentInterface.handleReceiptPaymentReceivedEvent(paymentEvent);
}
}
And My test cases are similar to the following
#SpringBootTest
#EmbeddedKafka(brokerProperties = {"listeners=PLAINTEXT://localhost:9092"},
partitions = 1,
controlledShutdown = true)
class KafkaPaymentConsumerTest {
#Autowired
KafkaTemplate<String, PaymentEvent> kafkaTemplate;
#Autowired
private ObjectMapper objectMapper;
#Value("${kafka.topic.payment}")
private String paymentTopic;
#SpyBean
private KafkaPaymentConsumer kafkaPaymentConsumer;
#SpyBean
private PaymentInterface paymentInterface;
#Captor
ArgumentCaptor<PaymentEvent> paymentEventCaptor;
private static File PAYMENT_EVENT_JSON = Paths.get("src", "test", "resources", "files",
"Payment.json").toFile();
#Test
#SneakyThrows
#DirtiesContext
void consumePaymentEvents() {
PaymentEvent event = objectMapper.readValue(PAYMENT_EVENT_JSON,
PaymentEvent.class);
kafkaTemplate.send(paymentTopic, "1", event);
verify(kafkaPaymentConsumer, timeout(10000).times(1)).consumePaymentEvents(
paymentEventCaptor.capture());
PaymentEvent argument = paymentEventCaptor.getValue();
verify(paymentInterface, timeout(10000).times(1)).handleReceiptPaymentReceivedEvent(any());
}
}
the test works well, BUT when running a batch of tests at once, some tests fail ! ( only when I run many tests at the same time !! ) it seems that there is an issue in the context with #EmbeddedKafka
I got like theses log errors
Actually, there were zero interactions with this mock.
or a Timeout when trying to poll records from the broker
Any explanation or suggestion please
Since you don’t use a #DirtiesContext on your test class to close an application context in the end, it is not a surprise that other tests for the same topic can steal data from you. See if you can clean up contexts as I explained, or consider to use different topics in different tests. I’d prefer the dirties context since it guarantees that no any extra resources in the memory to cause race conditions and surprises .

How do I get JobRunr to detect my scheduled background job in a Spring controller/service?

I have been looking into using JobRunr for starting background jobs on my Spring MVC application, as I really like the simplicity of it, and the ease of integrating it into an IoC container.
I am trying to create a simple test scheduled job that writes a line of text to my configured logger every minute, but I'm struggling to figure out how to get the JobRunr background job server to detect it and queue it up. I am not using Spring Boot so I am just using the generic jobrunr Maven artifact rather than the "Spring Boot Starter". My setup is as follows:
pom.xml
<dependency>
<groupId>org.jobrunr</groupId>
<artifactId>jobrunr</artifactId>
<version>2.0.0</version>
</dependency>
ApplicationConfig.java
#Bean
public JobMapper jobMapper() {
return new JobMapper(new JacksonJsonMapper());
}
#Bean
#DependsOn("jobMapper")
public StorageProvider storageProvider(JobMapper jobMapper) {
InMemoryStorageProvider storageProvider = new InMemoryStorageProvider();
storageProvider.setJobMapper(jobMapper);
return storageProvider;
}
#Bean
#DependsOn("storageProvider")
public JobScheduler jobScheduler(StorageProvider storageProvider, ApplicationContext applicationContext) {
return JobRunr.configure().useStorageProvider(storageProvider)
.useJobActivator(applicationContext::getBean)
.useDefaultBackgroundJobServer()
.useDashboard()
.useJmxExtensions()
.initialize();
}
BackgroundJobsController.java
#Controller
public class BackgroundJobsController {
private final Logger logger = LoggerFactory.getLogger(getClass());
private #Autowired JobScheduler jobScheduler;
#Job(name = "Test")
public void executeJob() {
BackgroundJob.scheduleRecurrently(Cron.minutely(), () -> logger.debug("It works!"));
jobScheduler.scheduleRecurrently(Cron.minutely(), () -> logger.debug("It works too!"));
}
}
As you can see, I have tried both methods of initiating the background job in the executeJob method. The issue is basically getting Jobrunr to detect the jobs - is it simply a case of somehow triggering the executeJob method upon startup of the application? If so, does anyone know the most simple way to do that? Previously I have used the Spring #Scheduled annotation to automatically run through methods in a Service/Controller class upon startup of the application, so I was hoping there was a straightforward way to get Jobrunr to pick up the scheduled tasks I am trying to create. Apologies if it is something stupid that I have overlooked. I've spent a good few hours trying different things and reading through the documentation!
Thanks in advance!
There are different ways for doing so:
This is one, annotating a method with #PostConstruct is indeed another.
#SpringBootApplication
#Import(JobRunrExampleConfiguration.class)
public class JobRunrApplication {
public static void main(String[] args) {
ConfigurableApplicationContext applicationContext = SpringApplication.run(JobRunrApplication.class, args);
JobScheduler jobScheduler = applicationContext.getBean(JobScheduler.class);
jobScheduler.<SampleJobService>scheduleRecurrently("recurring-sample-job", every5minutes(), x -> x.executeSampleJob("Hello from recurring job"));
}
}
You can see an example here: https://github.com/jobrunr/example-java-mag/blob/main/src/main/java/org/jobrunr/examples/JobRunrApplication.java
Have you tried annotating your executeJob Method with a #PostConstruct ? That way upon initialisation of your application, the jobs would be registered to the JobServer.
I believe the #Job annotation is meant fo the method of the job itself. (In your case the debug method).
There is now a new way to do so:
You can add #Recurring to any Spring Boot, Micronaut or Quarkus bean method. A Spring Boot example:
#Component
public class SomeService {
#Recurring(id="recurring-job-every-5-min" interval = "PT5M")
#Job(name="job name for the dashboard")
public void runEvery5Minutes() {
// business logic comes here
}
}
For more info, see the JobRunr documentation.

Update Cron expression runtime in SpringBoot #Scheduled

#Configuration
#EnableScheduling
public class CustomScheduler {
#Autowired
private IAppUpdateService appUpdateService;
#Scheduled(cron = "#{#appUpdateService.findCroExpById()}")
public void job1() {
System.out.println(new Date());
}
}
Above snippet dosent work properly as expected is there any way to achieve this or any other approach is available!!!
I want to achieve like to retrieve cron expression from db at runtime.
User may change cron timing through UI,thats why i want to fetch it through service.
You can create a bean to get the values from database and use the bean in the corn. Detailed example is in the link :
http://mbcoder.com/dynamic-task-scheduling-with-spring/

How to program the spring MVC cron without annotation

package com.test.cron;
#Service
public class CronJob {
protected static final Logger logger = Logger.getLogger(CronJob.class);
#Scheduled(cron="0 0 23 * * *")
public void demoServiceMethod()
{
logger.debug("Cron job started.");
}
}
Cron excution time will be often changed.
I have to exchange '#Scheduled' annotation to java code.
You can externalize this cron value into a properties file.
#Scheduled(cron="${schedularTime}")
and in your properties file ( example: application-dev.properties)
schedularTime=0 0/2 * * * ?
Old thread, but there is no real answer to the question.
Here is a way to schedule jobs without any annotations.
The example is in Kotlin, but works the same way in java.
#Component
class CliRunner(
private val scheduler: ThreadPoolTaskScheduler,
private val scheduledService: ScheduledService
) : CommandLineRunner {
override fun run(vararg args: String) {
scheduler.scheduleAtFixedRate(scheduledService::run, 10_000)
}
}
Inject ThreadPoolTaskScheduler and whatever you want to schedule, and call any of the scheduling methods on the scheduler.
My example schedules the run method to be executed every 10 seconds.
You could use a database for the configuration of schedule, Quartz had a capability to save its job meta data in a datasource.
Somebody had implemented something like this here.
https://github.com/davidkiss/spring-boot-quartz-demo

Set the cron expression value dynamically

I want to set the cron expression value dynamically using controller. This is my code snippet.
#Service
#EnableScheduling
#Configuration
#PropertySources({
#PropertySource("classpath:properties/cron.properties")
})
public class SchedulerServiceImpl implements SchedulerService{
private final static Logger log = LoggerFactory.getLogger(SchedulerServiceImpl.class);
#Value( "${cron.expression}")
private String cronValue;
#Scheduled(cron = "${cron.expression}")
public void getTweetsFromAPI(){
log.info("Trying to make request for get Tweets through TweetController.");
log.debug("----------------Executing cron.expression {}----------------",cronValue);
}
}
how can I set the cron.expression value dynamically after the deployment. If I used a controller and replace the properties file existing value, Does it work?
Logically your question is wrong. Cron expression is for scheduling a method for a specified time i.e. the method will be executed at a specified time say 12 noon everyday.
Now if you are dynamically changing the cron expression value that means you are controlling the execution of this method and not the scheduler which is wrong.
You can do something like below:-
#Value( "${cron.expression1}")
private String cron1;
#Value( "${cron.expression2}")
private String cron2;
#Scheduled(cron=cron1)
public void method1(){
//call to method
this.method();
}
#Scheduled(cron=cron2)
public void method2(){
//call to method
this.method();
}
private void method(){
//Your scheduling logic goes here
}
Here you are reusing the method() to be scheduled at two different times.

Resources