I'm using spring boot and I have a service which stores/retrieves some data from DB. I want to use a quartz job which uses my service. I tried many suggestions from the internet to integrate quartz with spring boot, but it doesn't work. Can you help me pls?
Here is my code:
1) I added org.quartz-scheduler as dependency in pom.xml:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
</dependency>
</dependencies>
2) This is my application.properties configured to use mysql as DB for my service:
server.port=8281
spring.datasource.driverClassName=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/mydb
spring.datasource.username=myusername
spring.datasource.password=mypassword
3) This is my service:
@Service
public class ReportService {
JdbcTemplate jdbcTemplate;
public ReportService(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
public List getPendingReports() {
String sql = "SELECT * FROM report WHERE status = '" + ReportStatus.PENDING.name() +"'";
ReportMapper reportsMapper = new ReportMapper();
List reports = jdbcTemplate.query(sql, reportsMapper);
return reports;
}
}
4) The scheduler class:
import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SchedulerFactory;
import org.quartz.SimpleScheduleBuilder;
import org.quartz.SimpleTrigger;
import org.quartz.TriggerBuilder;
import org.quartz.impl.StdSchedulerFactory;
public class ReportScheduler {
public void scanAndUpdateReports() {
SchedulerFactory sf = new StdSchedulerFactory();
try {
Scheduler scheduler = sf.getScheduler();
scheduler.start();
JobDetail job = JobBuilder.newJob(ReportsJob.class)
.withIdentity("reportsJob")
.build();
SimpleTrigger trigger = (SimpleTrigger) TriggerBuilder.newTrigger()
.withIdentity("reportsTrigger")
.startNow()
.withSchedule(SimpleScheduleBuilder.simpleSchedule()
.withIntervalInSeconds(60).repeatForever())
.build();
scheduler.scheduleJob(job, trigger);
} catch (SchedulerException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
5) And my job class where I want to autowire the ReportService, but it is null:
public class ReportsJob implements Job {
@Autowired
ReportService reportService; //this doesn't work (it is null)
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
List reports = reportService.getPendingReports();
System.out.println("Reports:\n"+reports);
}
}
6) Right now I'm calling the scheduler from main method:
@SpringBootApplication
public class ReportAppBeApplication {
public static void main(String[] args) {
SpringApplication.run(ReportAppBeApplication.class, args);
ReportScheduler rs = new ReportScheduler();
rs.scanAndUpdateReports();
}
}
It is null because @Autowired only works in a @Component, @Repository or @Service annotated class.
Annotate your ReportJobclass and make sure the map where you store the class is included in the component scan on startup. You can include the map for scanning with:
@ComponentScan(basePackages={"folderOfReportJob"})
This annotation should be added to an @Configuration or @SpringBootApplication annotated class.
EDIT:
If you don't want to make your job a component you can also get the bean manually through the ApplicationContext class. It goes like this:
private static ApplicationContext ac;
ReportService reportService = ac.getBean(ReportService.class);