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