demand
Implement the adding, deleting, modifying and checking of periodic message push policies, and send emails according to the policy content.
The implementation process
In fact, from the beginning, we intended to use the Java built-in timer to implement the timer, but we ran into problems immediately, either using the schedule method or scheduleAtFixedRate method, once the set time has passed, both methods will execute first. The scheduleAtFixedRate method, in particular, calculates a difference between the set past time and the present time, and then executes it as many times as the number of time intervals that the difference contains. This is clearly incompatible with demand.
If the current time is 2021-02-19 10:52:00 then the following code will print 5+1 times.
public static void main(String[] args) throws ParseException {
Date date = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse("The 2021-02-19 10:00:00");
new Timer().scheduleAtFixedRate(new TimerTask() {
@Override
public void run(a) {
System.out.println("Ha ha");
}
},date,1000*60*10);
}
Copy the code
In addition, there is not a single timing policy in the requirement, and there is the increase, deletion, change and check of the scheduled task. If the timer continues to be used, thread pool may be introduced to solve the problem, which becomes complicated. And that’s where Quartz comes in.
Quartz
Quartz is an open source project, developed entirely in Java, that can be used to perform timed tasks, similar to java.util.timer. However, compared with Timer, Quartz adds many functions, but compared with Timer, I think the most important thing is that each scheduled task can be easily managed.
Below is my general understanding of Quartz.
You are advised to create the QuartzUtil class for easy management.
public class Quartz {
@Autowired
private Scheduler scheduler;
public void addJob(String jobName, String jobGroup, String triggerName, String triggerGroup, String strDate, String fre, PushStrategyPO pushStrategyPO) throws InterruptedException{
try {
DateFormat bf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date date=null;
try {
date = bf.parse(strDate);
} catch (ParseException e) {
e.printStackTrace();
}
if (null == date || StringUtils.isEmpty(fre)) {
return;
}
String cronExpress="";
if (fre.equals("d")){
cronExpress = getEveryDayCron(date);
}
if (fre.equals("w")){
cronExpress = getEveryWeekCron(date);
}
// Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
// Create a task: define the task details
JobDetail jobDetail=null;
if(pushStrategyPO.getType()==0){
jobDetail = JobBuilder.newJob(CustomerEmailJob.class).withIdentity(jobName, jobGroup).build();
}
if(pushStrategyPO.getType()==1){
jobDetail = JobBuilder.newJob(ContractEmailJob.class).withIdentity(jobName, jobGroup).build();
}
JobDataMap jobDataMap = jobDetail.getJobDataMap();
jobDataMap.put("fre",fre);
jobDataMap.put("pushStrategyPO",pushStrategyPO);
CronTrigger trigger = TriggerBuilder
.newTrigger()
.withIdentity(triggerName, triggerGroup) // Create an identifier
// Triggers a task every second
.withSchedule(CronScheduleBuilder.cronSchedule(cronExpress))
.build();
// Register tasks and triggers with the scheduler
scheduler.scheduleJob(jobDetail, trigger);
if (!scheduler.isShutdown()) {
scheduler.start();
}
} catch (SchedulerException e) {
// TODO Auto-generated catch blocke.printStackTrace(); }}private String formatDateByPattern(Date date,String dateFormat){
SimpleDateFormat sdf = new SimpleDateFormat(dateFormat);
String formatTimeStr = null;
if(date ! =null) {
formatTimeStr = sdf.format(date);
}
return formatTimeStr;
}
/***
* convert Date to cron ,eg. "0 07 10 15 1 ? 2016"
* @paramDate: indicates the time *@return* /
public String getEveryDayCron(Date date){
String dateFormat="ss mm HH * * ?";
return formatDateByPattern(date, dateFormat);
}
/** ** will be sent on Monday by default@param date
* @return* /
public String getEveryWeekCron(Date date){
String dateFormat="ss mm HH ? * 2";
return formatDateByPattern(date, dateFormat);
}
public void modifyJobTime(String jobName, String jobGroupName, String triggerName, String triggerGroupName, String cron) {
try {
TriggerKey triggerKey = TriggerKey.triggerKey(triggerName, triggerGroupName);
CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
if (trigger == null) {
return;
}
String oldTime = trigger.getCronExpression();
if(! oldTime.equalsIgnoreCase(cron)) {/ / triggers
TriggerBuilder<Trigger> triggerBuilder = TriggerBuilder.newTrigger();
// Trigger name, trigger group
triggerBuilder.withIdentity(triggerName, triggerGroupName);
triggerBuilder.startNow();
// Trigger time setting
triggerBuilder.withSchedule(CronScheduleBuilder.cronSchedule(cron));
// Create the Trigger object
trigger = (CronTrigger) triggerBuilder.build();
Method 1: Change the trigger time of a taskscheduler.rescheduleJob(triggerKey, trigger); }}catch (Exception e) {
throw newRuntimeException(e); }}/** * Function: Remove a task **@param jobName
* @param jobGroupName
* @param triggerName
* @param triggerGroupName
*/
public void removeJob(String jobName, String jobGroupName, String triggerName, String triggerGroupName) {
try {
TriggerKey triggerKey = TriggerKey.triggerKey(triggerName, triggerGroupName);
// Stop the trigger
scheduler.pauseTrigger(triggerKey);
// Remove the trigger
scheduler.unscheduleJob(triggerKey);
// Delete the task
scheduler.deleteJob(JobKey.jobKey(jobName, jobGroupName));
System.out.println("removeJob:"+JobKey.jobKey(jobName));
} catch (Exception e) {
throw newRuntimeException(e); }}/** ** Function: Start all scheduled tasks */
public void startJobs(a) {
try {
scheduler.start();
} catch (Exception e) {
throw newRuntimeException(e); }}/** * Disable all scheduled tasks */
public void shutdownJobs(a) {
try {
if (!scheduler.isShutdown()) {
scheduler.shutdown();
}
} catch (Exception e) {
throw newRuntimeException(e); }}public List<String> getSchedulerJobs(a) {
List<String> jobGroupNames=null;
try {
jobGroupNames = scheduler.getJobGroupNames();
} catch (SchedulerException e) {
e.printStackTrace();
}
return jobGroupNames;
}
public String createJobID(int n )
{
String val = "";
Random random = new Random();
for ( int i = 0; i < n; i++ )
{
String str = random.nextInt( 2 ) % 2= =0 ? "num" : "char";
if ( "char".equalsIgnoreCase( str ) )
{ // Generate letters
int nextInt = random.nextInt( 2 ) % 2= =0 ? 65 : 97;
// System.out.println(nextInt + "!!!!" ); 1,0,1,1,1,0,0
val += (char) ( nextInt + random.nextInt( 26)); }else if ( "num".equalsIgnoreCase( str ) )
{ // Generate numbers
val += String.valueOf( random.nextInt( 10)); }}returnval; }}Copy the code
Problems with SpringBoot integration
The job implementation class will not be injected into the Spring container even if @bean or @component is added. The job implementation class will not be injected into the Spring container.
Introduction of depend on
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
Copy the code
The Job implementation class reintegrates spring’s QuartzJobBean
public class sendEmailJob extends QuartzJobBean {
private static final Logger logger = LoggerFactory.getLogger(sendEmailJob.class);
@Autowired
private ProjectServiceImpl projectService;
@Autowired
private SendEmailUtil sendEmailUtil;
@Override
protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {... }Copy the code
When you need to pass a parameter to the Job implementation class
Because the Job implementation class overrides the method, it cannot add the passed parameters to the parameters. In this case, you need to pass the parameter through the task manager, and the scheduler passes the parameter through the JobExecutionContext during timed execution.
JobDataMap jobDataMap = jobDetail.getJobDataMap();
jobDataMap.put("fre",fre);
jobDataMap.put("pushStrategyPO",pushStrategyPO);
Copy the code
JobDataMap jobDataMap =jobExecutionContext.getJobDetail().getJobDataMap();
String fre = jobDataMap.getString("fre");
PushStrategyPO pushStrategyPO = (PushStrategyPO) jobDataMap.get("pushStrategyPO");
Copy the code
It needs to be reloaded into schedule when the service restarts
When the service is restarted, the database needs to be read and each scheduled task is reloaded into task management.
@Component
public class ApplicationRunnerImpl implements ApplicationRunner {
private static final Logger logger = LoggerFactory.getLogger(ModuleController.class);
@Autowired
private MessageService messageService;
@Autowired
private Quartz quartz;
@Override
public void run(ApplicationArguments args) throws Exception {
// Query the policy table, including project management and contract management
logger.info("Querying all pushStrategies for mysql...");
ArrayList<PushStrategyPO> pushStrategyPOS = messageService.queryAllJob();
logger.info("pushStrategies is"+pushStrategyPOS.toString());
logger.info("Begin to start all timed task...");
for (PushStrategyPO ps : pushStrategyPOS) {
// Start all scheduled tasks
String jobName=ps.getJobId();
String jobGroup=ps.getJobId()+"-Group";
String triggerName=ps.getJobId()+"-Trigger";
String triggerGroup=ps.getJobId()+"-TriggerGroup";
String frequency = ps.getFrequency();
String setDate = ps.getSetDate();
quartz.addJob(jobName,jobGroup,triggerName,triggerGroup,setDate,frequency,ps);
}
List<String> schedulerJobs = quartz.getSchedulerJobs();
logger.info("there are "+schedulerJobs.size()+"job in schedule");
for (String schedulerJob : schedulerJobs) {
logger.info("job is :"+schedulerJob); }}}Copy the code