001 package railo.runtime.schedule; 002 003 import java.util.Calendar; 004 import java.util.TimeZone; 005 006 import railo.commons.date.DateTimeUtil; 007 import railo.commons.date.JREDateTimeUtil; 008 import railo.commons.io.log.Log; 009 import railo.commons.io.log.LogAndSource; 010 import railo.commons.io.log.LogUtil; 011 import railo.commons.lang.SystemOut; 012 import railo.runtime.config.Config; 013 import railo.runtime.engine.CFMLEngineImpl; 014 import railo.runtime.engine.ThreadLocalPageContext; 015 import railo.runtime.type.dt.DateTimeImpl; 016 017 public class ScheduledTaskThread extends Thread { 018 019 private static final long DAY=24*3600000; 020 private Calendar calendar; 021 022 private long startDate; 023 private long startTime; 024 private long endDate; 025 private long endTime; 026 private int intervall; 027 private int amount; 028 029 private DateTimeUtil util; 030 031 private int cIntervall; 032 033 private Config config; 034 private LogAndSource log; 035 private ScheduleTask task; 036 private String charset; 037 private final CFMLEngineImpl engine; 038 private TimeZone timeZone; 039 private SchedulerImpl scheduler; 040 041 042 043 044 public ScheduledTaskThread(CFMLEngineImpl engine,SchedulerImpl scheduler, Config config, LogAndSource log, ScheduleTask task, String charset) { 045 util = DateTimeUtil.getInstance(); 046 this.engine=engine; 047 this.scheduler=scheduler; 048 this.config=config; 049 this.log=log; 050 this.task=task; 051 this.charset=charset; 052 timeZone=ThreadLocalPageContext.getTimeZone(config); 053 054 this.startDate=util.getMilliSecondsAdMidnight(timeZone,task.getStartDate().getTime()); 055 this.startTime=util.getMilliSecondsInDay(timeZone, task.getStartTime().getTime()); 056 this.endDate=task.getEndDate()==null?Long.MAX_VALUE:util.getMilliSecondsAdMidnight(timeZone,task.getEndDate().getTime()); 057 this.endTime=task.getEndTime()==null?DAY:util.getMilliSecondsInDay(timeZone, task.getEndTime().getTime()); 058 059 060 this.intervall=task.getInterval(); 061 if(intervall>=10){ 062 amount=intervall; 063 intervall=ScheduleTaskImpl.INTERVAL_EVEREY; 064 } 065 else amount=1; 066 067 cIntervall = toCalndarIntervall(intervall); 068 } 069 070 071 public void run(){ 072 try{ 073 _run(); 074 } 075 finally{ 076 task.setValid(false); 077 try { 078 scheduler.removeIfNoLonerValid(task); 079 } catch (Throwable t) { 080 t.printStackTrace(); 081 } 082 } 083 084 } 085 public void _run(){ 086 087 088 // check values 089 if(startDate>endDate) { 090 log(Log.LEVEL_ERROR,"This task can not be executed because the task definition is invalid; enddate is before startdate"); 091 return; 092 } 093 if(intervall==ScheduleTaskImpl.INTERVAL_EVEREY && startTime>endTime) { 094 log(Log.LEVEL_ERROR,"This task can not be executed because the task definition is invalid; endtime is before starttime"); 095 return; 096 } 097 098 099 long today = System.currentTimeMillis(); 100 long execution ; 101 boolean isOnce=intervall==ScheduleTask.INTERVAL_ONCE; 102 if(isOnce){ 103 if(startDate+startTime<today) return; 104 execution=startDate+startTime; 105 } 106 else execution = calculateNextExecution(today,false); 107 //long sleep=execution-today; 108 109 log(Log.LEVEL_INFO,"first execution runs at "+new DateTimeImpl(execution,false).castToString(timeZone)); 110 111 112 while(true){ 113 sleepEL(execution,today); 114 115 if(!engine.isRunning()) break; 116 117 today=System.currentTimeMillis(); 118 long todayTime=util.getMilliSecondsInDay(null,today); 119 long todayDate=today-todayTime; 120 121 if(!task.isValid()) break; 122 if(!task.isPaused()){ 123 if(endDate<todayDate && endTime<todayTime) { 124 break; 125 } 126 execute(); 127 } 128 if(isOnce)break; 129 today=System.currentTimeMillis(); 130 execution=calculateNextExecution(today,true); 131 log(Log.LEVEL_INFO,"next execution runs at "+new DateTimeImpl(execution,false).castToString(timeZone)+":"+today+":"+execution); 132 //sleep=execution-today; 133 } 134 } 135 136 137 138 139 private void log(int level, String msg) { 140 String logName="schedule task:"+task.getTask(); 141 if(log!=null) log.log(level,logName, msg); 142 else SystemOut.print(LogUtil.toStringType(level, "INFO").toUpperCase()+":"+msg); 143 144 } 145 146 147 private void sleepEL(long when, long now) { 148 long millis = when-now; 149 150 try { 151 while(true){ 152 sleep(millis); 153 millis=when-System.currentTimeMillis(); 154 if(millis<=0)break; 155 millis=10; 156 } 157 } 158 catch (InterruptedException e) { 159 e.printStackTrace(); 160 } 161 162 } 163 164 private void execute() { 165 if(config!=null)new ExecutionThread(config,log,task,charset).start(); 166 } 167 168 private long calculateNextExecution(long now, boolean notNow) { 169 long nowTime=util.getMilliSecondsInDay(timeZone,now); 170 long nowDate=now-nowTime; 171 172 173 // when second or date intervall switch to current date 174 if(startDate<nowDate && (cIntervall==Calendar.SECOND || cIntervall==Calendar.DATE)) 175 startDate=nowDate; 176 177 // init calendar 178 if(calendar==null){ 179 calendar=JREDateTimeUtil.newInstance(timeZone); 180 calendar.setTimeInMillis(startDate+startTime); 181 } 182 long time; 183 while(true) { 184 time=getMilliSecondsInDay(calendar); 185 if(now<=calendar.getTimeInMillis() && time>=startTime) { 186 // this is used because when cames back sometme to early 187 if(notNow && (calendar.getTimeInMillis()-now)<1000); 188 else if(intervall==ScheduleTaskImpl.INTERVAL_EVEREY && time>endTime) 189 now=nowDate+DAY; 190 else 191 break; 192 } 193 calendar.add(cIntervall, amount); 194 } 195 return calendar.getTimeInMillis(); 196 } 197 198 private static int toCalndarIntervall(int intervall) { 199 switch(intervall){ 200 case ScheduleTask.INTERVAL_DAY:return Calendar.DATE; 201 case ScheduleTask.INTERVAL_MONTH:return Calendar.MONTH; 202 case ScheduleTask.INTERVAL_WEEK:return Calendar.WEEK_OF_YEAR; 203 case ScheduleTask.INTERVAL_ONCE:return -1; 204 205 } 206 return Calendar.SECOND; 207 } 208 209 public static long getMilliSecondsInDay(Calendar c) { 210 return (c.get(Calendar.HOUR_OF_DAY)*3600000)+ 211 (c.get(Calendar.MINUTE)*60000)+ 212 (c.get(Calendar.SECOND)*1000)+ 213 (c.get(Calendar.MILLISECOND)); 214 215 } 216 }