001/**
002 * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com).
003 * <p>
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 * <p>
008 * http://www.apache.org/licenses/LICENSE-2.0
009 * <p>
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package io.jboot.components.schedule;
017
018import com.jfinal.aop.Aop;
019import com.jfinal.kit.Prop;
020import com.jfinal.kit.StrKit;
021import com.jfinal.plugin.IPlugin;
022import com.jfinal.plugin.cron4j.ITask;
023import it.sauronsoftware.cron4j.ProcessTask;
024import it.sauronsoftware.cron4j.Scheduler;
025import it.sauronsoftware.cron4j.Task;
026
027import java.util.ArrayList;
028import java.util.List;
029
030
031public class JbootCron4jPlugin implements IPlugin {
032
033    private List<TaskInfo> taskInfoList = new ArrayList<>();
034    public static final String defaultConfigName = "cron4j";
035
036    public JbootCron4jPlugin() {
037
038    }
039
040    public JbootCron4jPlugin(String configFile) {
041        this(new Prop(configFile), defaultConfigName);
042    }
043
044    public JbootCron4jPlugin(Prop configProp) {
045        this(configProp, defaultConfigName);
046    }
047
048    public JbootCron4jPlugin(String configFile, String configName) {
049        this(new Prop(configFile), configName);
050    }
051
052    public JbootCron4jPlugin(Prop configProp, String configName) {
053        try {
054            addTask(configProp, configName);
055        } catch (RuntimeException e) {
056            throw e;
057        } catch (Exception e) {
058            throw new RuntimeException(e);
059        }
060    }
061
062    /*
063     * 考虑添加对 ProcessTask 的配置支持,目前不支持 ProcessTask 对象的构造方法的参数配置
064     * 对于 ProcessTask 型的任务调度,建议对 ProcessTask 的创建使用 java 代码
065     */
066    private void addTask(Prop configProp, String configName) throws Exception {
067        String configNameValue = configProp.get(configName);
068        if (StrKit.isBlank(configNameValue)) {
069            throw new IllegalArgumentException("The value of configName: " + configName + " can not be blank.");
070        }
071        String[] taskNameArray = configNameValue.trim().split(",");
072        for (String taskName : taskNameArray) {
073            if (StrKit.isBlank(taskName)) {
074                throw new IllegalArgumentException("taskName can not be blank.");
075            }
076            taskName = taskName.trim();
077
078            String taskCron = configProp.get(taskName + ".cron");
079            if (StrKit.isBlank(taskCron)) {
080                throw new IllegalArgumentException(taskName + ".cron" + " not found.");
081            }
082            taskCron = taskCron.trim();
083
084            String taskClass = configProp.get(taskName + ".class");
085            if (StrKit.isBlank(taskClass)) {
086                throw new IllegalArgumentException(taskName + ".class" + " not found.");
087            }
088            taskClass = taskClass.trim();
089
090            Object taskObj = Class.forName(taskClass).newInstance();
091            if (!(taskObj instanceof Runnable) && !(taskObj instanceof Task)) {
092                throw new IllegalArgumentException("Task 必须是 Runnable、ITask、ProcessTask 或者 Task 类型");
093            }
094
095            boolean taskDaemon = configProp.getBoolean(taskName + ".daemon", false);
096            boolean taskEnable = configProp.getBoolean(taskName + ".enable", true);
097            taskInfoList.add(new JbootCron4jPlugin.TaskInfo(taskCron, taskObj, taskDaemon, taskEnable));
098        }
099    }
100
101    public JbootCron4jPlugin addTask(String cron, Runnable task, boolean daemon, boolean enable) {
102        taskInfoList.add(new JbootCron4jPlugin.TaskInfo(cron, task, daemon, enable));
103        return this;
104    }
105
106    public JbootCron4jPlugin addTask(String cron, Runnable task, boolean daemon) {
107        return addTask(cron, task, daemon, true);
108    }
109
110    public JbootCron4jPlugin addTask(String cron, Runnable task) {
111        return addTask(cron, task, false, true);
112    }
113
114    public JbootCron4jPlugin addTask(String cron, ProcessTask processTask, boolean daemon, boolean enable) {
115        taskInfoList.add(new JbootCron4jPlugin.TaskInfo(cron, processTask, daemon, enable));
116        return this;
117    }
118
119    public JbootCron4jPlugin addTask(String cron, ProcessTask processTask, boolean daemon) {
120        return addTask(cron, processTask, daemon, true);
121    }
122
123    public JbootCron4jPlugin addTask(String cron, ProcessTask processTask) {
124        return addTask(cron, processTask, false, true);
125    }
126
127    public JbootCron4jPlugin addTask(String cron, Task task, boolean daemon, boolean enable) {
128        taskInfoList.add(new JbootCron4jPlugin.TaskInfo(cron, task, daemon, enable));
129        return this;
130    }
131
132    public JbootCron4jPlugin addTask(String cron, Task task, boolean daemon) {
133        return addTask(cron, task, daemon, true);
134    }
135
136    public JbootCron4jPlugin addTask(String cron, Task task) {
137        return addTask(cron, task, false, true);
138    }
139
140    @Override
141    public boolean start() {
142        for (JbootCron4jPlugin.TaskInfo taskInfo : taskInfoList) {
143            taskInfo.schedule();
144        }
145        for (JbootCron4jPlugin.TaskInfo taskInfo : taskInfoList) {
146            taskInfo.start();
147        }
148        return true;
149    }
150
151    @Override
152    public boolean stop() {
153        taskInfoList.forEach(TaskInfo::stop);
154        taskInfoList.clear();
155        return true;
156    }
157
158    private static class TaskInfo {
159        Scheduler scheduler;
160
161        String cron;
162        Object task;
163        boolean daemon;
164        boolean enable;
165
166        TaskInfo(String cron, Object task, boolean daemon, boolean enable) {
167            if (StrKit.isBlank(cron)) {
168                throw new IllegalArgumentException("cron 不能为空.");
169            }
170            if (task == null) {
171                throw new IllegalArgumentException("task 不能为 null.");
172            }
173
174            Aop.inject(task);
175
176            this.cron = cron.trim();
177            this.task = task;
178            this.daemon = daemon;
179            this.enable = enable;
180        }
181
182        void schedule() {
183            if (enable) {
184                scheduler = new Scheduler();
185                if (task instanceof Runnable) {
186                    scheduler.schedule(cron, (Runnable) task);
187                } else if (task instanceof Task) {
188                    scheduler.schedule(cron, (Task) task);
189                } else {
190                    scheduler = null;
191                    throw new IllegalStateException("Task 必须是 Runnable、ITask、ProcessTask 或者 Task 类型");
192                }
193                scheduler.setDaemon(daemon);
194            }
195        }
196
197        void start() {
198            if (enable) {
199                scheduler.start();
200            }
201        }
202
203        void stop() {
204            if (enable) {
205                if (task instanceof ITask) {   // 如果任务实现了 ITask 接口,则回调 ITask.stop() 方法
206                    ((ITask) task).stop();
207                }
208                scheduler.stop();
209            }
210        }
211    }
212}