001/** 002 * 003 * Copyright (c) 2014, the Railo Company Ltd. All rights reserved. 004 * 005 * This library is free software; you can redistribute it and/or 006 * modify it under the terms of the GNU Lesser General Public 007 * License as published by the Free Software Foundation; either 008 * version 2.1 of the License, or (at your option) any later version. 009 * 010 * This library is distributed in the hope that it will be useful, 011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 013 * Lesser General Public License for more details. 014 * 015 * You should have received a copy of the GNU Lesser General Public 016 * License along with this library. If not, see <http://www.gnu.org/licenses/>. 017 * 018 **/ 019package lucee.runtime.schedule; 020 021import java.util.Calendar; 022import java.util.TimeZone; 023 024import lucee.commons.date.DateTimeUtil; 025import lucee.commons.date.JREDateTimeUtil; 026import lucee.commons.io.log.Log; 027import lucee.commons.lang.ExceptionUtil; 028import lucee.runtime.config.Config; 029import lucee.runtime.config.ConfigImpl; 030import lucee.runtime.engine.CFMLEngineImpl; 031import lucee.runtime.engine.ThreadLocalPageContext; 032import lucee.runtime.type.dt.DateTimeImpl; 033 034public class ScheduledTaskThread extends Thread { 035 036 037 private static final long DAY=24*3600000; 038 //private Calendar calendar; 039 040 private long startDate; 041 private long startTime; 042 private long endDate; 043 private long endTime; 044 private int intervall; 045 private int amount; 046 047 private DateTimeUtil util; 048 049 private int cIntervall; 050 051 private Config config; 052 private ScheduleTask task; 053 private String charset; 054 private final CFMLEngineImpl engine; 055 private TimeZone timeZone; 056 private SchedulerImpl scheduler; 057 058 059 060 061 public ScheduledTaskThread(CFMLEngineImpl engine,SchedulerImpl scheduler, Config config, ScheduleTask task, String charset) { 062 util = DateTimeUtil.getInstance(); 063 this.engine=engine; 064 this.scheduler=scheduler; 065 this.config=config; 066 this.task=task; 067 this.charset=charset; 068 timeZone=ThreadLocalPageContext.getTimeZone(config); 069 070 this.startDate=util.getMilliSecondsAdMidnight(timeZone,task.getStartDate().getTime()); 071 this.startTime=util.getMilliSecondsInDay(timeZone, task.getStartTime().getTime()); 072 this.endDate=task.getEndDate()==null?Long.MAX_VALUE:util.getMilliSecondsAdMidnight(timeZone,task.getEndDate().getTime()); 073 this.endTime=task.getEndTime()==null?DAY:util.getMilliSecondsInDay(timeZone, task.getEndTime().getTime()); 074 075 076 this.intervall=task.getInterval(); 077 if(intervall>=10){ 078 amount=intervall; 079 intervall=ScheduleTaskImpl.INTERVAL_EVEREY; 080 } 081 else amount=1; 082 083 cIntervall = toCalndarIntervall(intervall); 084 } 085 086 087 public void run(){ 088 try{ 089 _run(); 090 } 091 finally{ 092 task.setValid(false); 093 try { 094 scheduler.removeIfNoLonerValid(task); 095 } catch (Throwable t) { 096 ExceptionUtil.rethrowIfNecessary(t); 097 t.printStackTrace(); 098 } 099 } 100 101 } 102 public void _run(){ 103 104 105 // check values 106 if(startDate>endDate) { 107 log(Log.LEVEL_ERROR,"This task can not be executed because the task definition is invalid; enddate is before startdate"); 108 return; 109 } 110 if(intervall==ScheduleTaskImpl.INTERVAL_EVEREY && startTime>endTime) { 111 log(Log.LEVEL_ERROR,"This task can not be executed because the task definition is invalid; endtime is before starttime"); 112 return; 113 } 114 115 116 long today = System.currentTimeMillis(); 117 long execution ; 118 boolean isOnce=intervall==ScheduleTask.INTERVAL_ONCE; 119 if(isOnce){ 120 if(startDate+startTime<today) return; 121 execution=startDate+startTime; 122 } 123 else execution = calculateNextExecution(today,false); 124 //long sleep=execution-today; 125 126 log(Log.LEVEL_INFO,"first execution runs at "+new DateTimeImpl(execution,false).castToString(timeZone)); 127 128 while(true){ 129 sleepEL(execution,today); 130 131 if(!engine.isRunning()) break; 132 133 today=System.currentTimeMillis(); 134 long todayTime=util.getMilliSecondsInDay(null,today); 135 long todayDate=today-todayTime; 136 137 if(!task.isValid()) break; 138 if(!task.isPaused()){ 139 if(endDate<todayDate && endTime<todayTime) { 140 break; 141 } 142 execute(); 143 } 144 if(isOnce)break; 145 today=System.currentTimeMillis(); 146 execution=calculateNextExecution(today,true); 147 148 if (!task.isPaused()) 149 log(Log.LEVEL_INFO,"next execution runs at "+new DateTimeImpl(execution,false).castToString(timeZone)+":"+today+":"+execution); 150 //sleep=execution-today; 151 } 152 } 153 154 155 156 157 private void log(int level, String msg) { 158 String logName="schedule task:"+task.getTask(); 159 ((ConfigImpl)config).getLog("scheduler").log(level,logName, msg); 160 } 161 162 163 private void sleepEL(long when, long now) { 164 long millis = when-now; 165 166 try { 167 while(true){ 168 sleep(millis); 169 millis=when-System.currentTimeMillis(); 170 if(millis<=0)break; 171 millis=10; 172 } 173 } 174 catch (InterruptedException e) { 175 e.printStackTrace(); 176 } 177 178 } 179 180 private void execute() { 181 if(config!=null)new ExecutionThread(config,task,charset).start(); 182 } 183 184 private long calculateNextExecution(long now, boolean notNow) { 185 long nowTime=util.getMilliSecondsInDay(timeZone,now); 186 long nowDate=now-nowTime; 187 188 189 // when second or date intervall switch to current date 190 if(startDate<nowDate && (cIntervall==Calendar.SECOND || cIntervall==Calendar.DATE)) 191 startDate=nowDate; 192 193 // init calendar 194 Calendar calendar = JREDateTimeUtil.getThreadCalendar(timeZone); 195 calendar.setTimeInMillis(startDate+startTime); 196 197 long time; 198 while(true) { 199 time=getMilliSecondsInDay(calendar); 200 if(now<=calendar.getTimeInMillis() && time>=startTime) { 201 // this is used because when cames back sometme to early 202 if(notNow && (calendar.getTimeInMillis()-now)<1000); 203 else if(intervall==ScheduleTaskImpl.INTERVAL_EVEREY && time>endTime) 204 now=nowDate+DAY; 205 else 206 break; 207 } 208 calendar.add(cIntervall, amount); 209 } 210 return calendar.getTimeInMillis(); 211 } 212 213 private static int toCalndarIntervall(int intervall) { 214 switch(intervall){ 215 case ScheduleTask.INTERVAL_DAY:return Calendar.DATE; 216 case ScheduleTask.INTERVAL_MONTH:return Calendar.MONTH; 217 case ScheduleTask.INTERVAL_WEEK:return Calendar.WEEK_OF_YEAR; 218 case ScheduleTask.INTERVAL_ONCE:return -1; 219 220 } 221 return Calendar.SECOND; 222 } 223 224 public static long getMilliSecondsInDay(Calendar c) { 225 return (c.get(Calendar.HOUR_OF_DAY)*3600000)+ 226 (c.get(Calendar.MINUTE)*60000)+ 227 (c.get(Calendar.SECOND)*1000)+ 228 (c.get(Calendar.MILLISECOND)); 229 230 } 231 232 233 public Config getConfig() { 234 return config; 235 } 236 237 238 public ScheduleTask getTask() { 239 return task; 240 } 241}