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    }