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    }