package railo.runtime.schedule;
import java.util.Calendar;
import java.util.TimeZone;
import railo.commons.date.DateTimeUtil;
import railo.commons.date.JREDateTimeUtil;
import railo.commons.io.log.Log;
import railo.commons.io.log.LogAndSource;
import railo.commons.io.log.LogUtil;
import railo.commons.lang.SystemOut;
import railo.runtime.config.Config;
import railo.runtime.engine.CFMLEngineImpl;
import railo.runtime.engine.ThreadLocalPageContext;
import railo.runtime.type.dt.DateTimeImpl;
public class ScheduledTaskThread extends Thread {
private static final long DAY=24*3600000;
//private Calendar calendar;
private long startDate;
private long startTime;
private long endDate;
private long endTime;
private int intervall;
private int amount;
private DateTimeUtil util;
private int cIntervall;
private Config config;
private LogAndSource log;
private ScheduleTask task;
private String charset;
private final CFMLEngineImpl engine;
private TimeZone timeZone;
private SchedulerImpl scheduler;
public ScheduledTaskThread(CFMLEngineImpl engine,SchedulerImpl scheduler, Config config, LogAndSource log, ScheduleTask task, String charset) {
util = DateTimeUtil.getInstance();
this.engine=engine;
this.scheduler=scheduler;
this.config=config;
this.log=log;
this.task=task;
this.charset=charset;
timeZone=ThreadLocalPageContext.getTimeZone(config);
this.startDate=util.getMilliSecondsAdMidnight(timeZone,task.getStartDate().getTime());
this.startTime=util.getMilliSecondsInDay(timeZone, task.getStartTime().getTime());
this.endDate=task.getEndDate()==null?Long.MAX_VALUE:util.getMilliSecondsAdMidnight(timeZone,task.getEndDate().getTime());
this.endTime=task.getEndTime()==null?DAY:util.getMilliSecondsInDay(timeZone, task.getEndTime().getTime());
this.intervall=task.getInterval();
if(intervall>=10){
amount=intervall;
intervall=ScheduleTaskImpl.INTERVAL_EVEREY;
}
else amount=1;
cIntervall = toCalndarIntervall(intervall);
}
public void run(){
try{
_run();
}
finally{
task.setValid(false);
try {
scheduler.removeIfNoLonerValid(task);
} catch (Throwable t) {
t.printStackTrace();
}
}
}
public void _run(){
// check values
if(startDate>endDate) {
log(Log.LEVEL_ERROR,"This task can not be executed because the task definition is invalid; enddate is before startdate");
return;
}
if(intervall==ScheduleTaskImpl.INTERVAL_EVEREY && startTime>endTime) {
log(Log.LEVEL_ERROR,"This task can not be executed because the task definition is invalid; endtime is before starttime");
return;
}
long today = System.currentTimeMillis();
long execution ;
boolean isOnce=intervall==ScheduleTask.INTERVAL_ONCE;
if(isOnce){
if(startDate+startTime<today) return;
execution=startDate+startTime;
}
else execution = calculateNextExecution(today,false);
//long sleep=execution-today;
log(Log.LEVEL_INFO,"first execution runs at "+new DateTimeImpl(execution,false).castToString(timeZone));
while(true){
sleepEL(execution,today);
if(!engine.isRunning()) break;
today=System.currentTimeMillis();
long todayTime=util.getMilliSecondsInDay(null,today);
long todayDate=today-todayTime;
if(!task.isValid()) break;
if(!task.isPaused()){
if(endDate<todayDate && endTime<todayTime) {
break;
}
execute();
}
if(isOnce)break;
today=System.currentTimeMillis();
execution=calculateNextExecution(today,true);
log(Log.LEVEL_INFO,"next execution runs at "+new DateTimeImpl(execution,false).castToString(timeZone)+":"+today+":"+execution);
//sleep=execution-today;
}
}
private void log(int level, String msg) {
String logName="schedule task:"+task.getTask();
if(log!=null) log.log(level,logName, msg);
else SystemOut.print(LogUtil.toStringType(level, "INFO").toUpperCase()+":"+msg);
}
private void sleepEL(long when, long now) {
long millis = when-now;
try {
while(true){
sleep(millis);
millis=when-System.currentTimeMillis();
if(millis<=0)break;
millis=10;
}
}
catch (InterruptedException e) {
e.printStackTrace();
}
}
private void execute() {
if(config!=null)new ExecutionThread(config,log,task,charset).start();
}
private long calculateNextExecution(long now, boolean notNow) {
long nowTime=util.getMilliSecondsInDay(timeZone,now);
long nowDate=now-nowTime;
// when second or date intervall switch to current date
if(startDate<nowDate && (cIntervall==Calendar.SECOND || cIntervall==Calendar.DATE))
startDate=nowDate;
// init calendar
Calendar calendar = JREDateTimeUtil.getThreadCalendar(timeZone);
calendar.setTimeInMillis(startDate+startTime);
long time;
while(true) {
time=getMilliSecondsInDay(calendar);
if(now<=calendar.getTimeInMillis() && time>=startTime) {
// this is used because when cames back sometme to early
if(notNow && (calendar.getTimeInMillis()-now)<1000);
else if(intervall==ScheduleTaskImpl.INTERVAL_EVEREY && time>endTime)
now=nowDate+DAY;
else
break;
}
calendar.add(cIntervall, amount);
}
return calendar.getTimeInMillis();
}
private static int toCalndarIntervall(int intervall) {
switch(intervall){
case ScheduleTask.INTERVAL_DAY:return Calendar.DATE;
case ScheduleTask.INTERVAL_MONTH:return Calendar.MONTH;
case ScheduleTask.INTERVAL_WEEK:return Calendar.WEEK_OF_YEAR;
case ScheduleTask.INTERVAL_ONCE:return -1;
}
return Calendar.SECOND;
}
public static long getMilliSecondsInDay(Calendar c) {
return (c.get(Calendar.HOUR_OF_DAY)*3600000)+
(c.get(Calendar.MINUTE)*60000)+
(c.get(Calendar.SECOND)*1000)+
(c.get(Calendar.MILLISECOND));
}
public Config getConfig() {
return config;
}
public ScheduleTask getTask() {
return task;
}
}