这是indexloc提供的服务,不要输入任何密码
Skip to content
Merged
14 changes: 14 additions & 0 deletions spring-quartz/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

<!-- spring's support for quartz -->
<dependency>
<groupId>org.springframework</groupId>
Expand Down Expand Up @@ -64,6 +69,15 @@
<filtering>true</filtering>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<mainClass>org.baeldung.springquartz.SpringQuartzApp</mainClass>
</configuration>
</plugin>
</plugins>
</build>

<properties>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package org.baeldung.recovery;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.scheduling.annotation.EnableScheduling;

@ComponentScan
@EnableScheduling
@SpringBootApplication
public class SpringQuartzRecoveryApp {

public static void main(String[] args) {
SpringApplication app = new SpringApplication(SpringQuartzRecoveryApp.class);
app.setAdditionalProfiles("recovery");
app.run(args);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package org.baeldung.recovery.config;

import org.quartz.CronScheduleBuilder;
import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class QuartzConfig {

@Bean
public JobDetail sampleJobDetail() {
return JobBuilder.newJob(SampleJob.class)
.withIdentity("sampleJob", "group1")
.storeDurably()
.requestRecovery(true)
.build();
}

@Bean
public Trigger sampleTrigger(JobDetail sampleJobDetail) {
return TriggerBuilder.newTrigger()
.forJob(sampleJobDetail)
.withIdentity("sampleTrigger", "group1")
.withSchedule(CronScheduleBuilder.cronSchedule("0/30 * * * * ?")) // every 30s
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package org.baeldung.recovery.config;

import org.quartz.Job;
import org.quartz.JobExecutionContext;

public class SampleJob implements Job {
@Override
public void execute(JobExecutionContext context) {
System.out.println("Executing SampleJob at " + System.currentTimeMillis());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package org.baeldung.recovery.custom;

import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;

@Entity
public class ApplicationJob {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

private String name;
private boolean enabled;
private Boolean completed;

public Long getId() {
return id;
}

public void setId(Long id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public boolean isEnabled() {
return enabled;
}

public void setEnabled(boolean enabled) {
this.enabled = enabled;
}

public Boolean getCompleted() {
return completed;
}

public void setCompleted(Boolean completed) {
this.completed = completed;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package org.baeldung.recovery.custom;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface ApplicationJobRepository extends JpaRepository<ApplicationJob, Long> {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package org.baeldung.recovery.custom;

import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;

@Component
public class DataSeeder implements CommandLineRunner {

private final ApplicationJobRepository repository;

public DataSeeder(ApplicationJobRepository repository) {
this.repository = repository;
}

@Override
public void run(String... args) {
if (repository.count() == 0) {
ApplicationJob job = new ApplicationJob();
job.setName("simpleJob");
job.setEnabled(true);
job.setCompleted(false);

repository.save(job);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package org.baeldung.recovery.custom;

import org.baeldung.recovery.config.SampleJob;
import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SimpleScheduleBuilder;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Component;

@Component
public class JobInitializer implements ApplicationListener<ContextRefreshedEvent> {

@Autowired
private ApplicationJobRepository jobRepository;

@Autowired
private Scheduler scheduler;

@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
for (ApplicationJob job : jobRepository.findAll()) {
if (job.isEnabled() && (job.getCompleted() == null || !job.getCompleted())) {
JobDetail detail = JobBuilder.newJob(SampleJob.class)
.withIdentity(job.getName(), "appJobs")
.storeDurably()
.build();

Trigger trigger = TriggerBuilder.newTrigger()
.forJob(detail)
.withSchedule(SimpleScheduleBuilder.simpleSchedule()
.withIntervalInSeconds(30)
.repeatForever())
.build();

try {
scheduler.scheduleJob(detail, trigger);
} catch (SchedulerException e) {
throw new RuntimeException(e);
}
}
}
}
}
12 changes: 12 additions & 0 deletions spring-quartz/src/main/resources/application-recovery.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
spring.quartz.job-store-type=jdbc
# Always create the Quartz database on startup
spring.quartz.jdbc.initialize-schema=always

spring.datasource.jdbc-url=jdbc:h2:~/quartz-db;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=

spring.jpa.hibernate.ddl-auto=create

spring.h2.console.enabled=true
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package org.baeldung.recovery;

import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;

import org.junit.jupiter.api.Test;
import org.quartz.JobDetail;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.Trigger;
import org.quartz.TriggerKey;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.ApplicationContext;
import org.springframework.test.context.ActiveProfiles;

@ActiveProfiles("recovery")
@SpringBootTest(classes = SpringQuartzRecoveryApp.class)
class SpringQuartzRecoveryAppUnitTest {

@Autowired
private ApplicationContext applicationContext;

@Autowired
private Scheduler scheduler;

@Test
void givenSampleJob_whenSchedulerRestart_thenSampleJobIsReloaded() throws Exception {
// Given
JobKey jobKey = new JobKey("sampleJob", "group1");
TriggerKey triggerKey = new TriggerKey("sampleTrigger", "group1");

JobDetail jobDetail = scheduler.getJobDetail(jobKey);
assertNotNull(jobDetail, "SampleJob exists in running scheduler");

Trigger trigger = scheduler.getTrigger(triggerKey);
assertNotNull(trigger, "SampleTrigger exists in running scheduler");

// When
scheduler.standby();
Scheduler restartedScheduler = applicationContext.getBean(Scheduler.class);
restartedScheduler.start();

// Then
assertTrue(restartedScheduler.isStarted(), "Scheduler should be running after restart");

JobDetail reloadedJob = restartedScheduler.getJobDetail(jobKey);
assertNotNull(reloadedJob, "SampleJob should be reloaded from DB after restart");

Trigger reloadedTrigger = restartedScheduler.getTrigger(triggerKey);
assertNotNull(reloadedTrigger, "SampleTrigger should be reloaded from DB after restart");
}

}