001    package railo.runtime.schedule;
002    
003    import java.io.IOException;
004    import java.util.ArrayList;
005    
006    import org.w3c.dom.Attr;
007    import org.w3c.dom.Document;
008    import org.w3c.dom.Element;
009    import org.w3c.dom.NamedNodeMap;
010    import org.w3c.dom.Node;
011    import org.w3c.dom.NodeList;
012    import org.xml.sax.SAXException;
013    
014    import railo.commons.io.log.LogAndSource;
015    import railo.commons.io.res.Resource;
016    import railo.loader.engine.CFMLEngine;
017    import railo.runtime.config.Config;
018    import railo.runtime.engine.CFMLEngineImpl;
019    import railo.runtime.exp.PageException;
020    import railo.runtime.op.Caster;
021    
022    /**
023     * scheduler class to execute the scheduled tasks
024     */
025    public final class SchedulerImpl implements Scheduler {
026    
027        private ScheduleTaskImpl[] tasks;
028        private Resource schedulerFile;
029        private Document doc;
030        private LogAndSource log;
031        private StorageUtil su=new StorageUtil();
032            private String charset;
033            private Config config;
034            //private String md5;
035    
036            private CFMLEngineImpl engine;
037            
038        /**
039         * constructor of the sheduler
040         * @param config 
041         * @param schedulerDir schedule file
042         * @param log
043         * @throws IOException
044         * @throws SAXException
045         * @throws PageException
046         */
047        public SchedulerImpl(CFMLEngine engine,Config config, Resource schedulerDir, LogAndSource log, String charset) throws SAXException, IOException, PageException {
048            this.engine=(CFMLEngineImpl) engine;
049            this.charset=charset;
050            this.config=config;
051            
052            initFile(schedulerDir,log);
053            doc=su.loadDocument(schedulerFile);
054            tasks=readInAllTasks();
055            init();
056        }
057        
058    
059        /**
060         * creates a empty Scheduler, used for event gateway context
061         * @param engine
062         * @param config
063         * @param log
064         * @throws SAXException
065         * @throws IOException
066         * @throws PageException
067         */
068        public SchedulerImpl(CFMLEngine engine,String xml,Config config, LogAndSource log) {
069            this.engine=(CFMLEngineImpl) engine;
070            this.config=config;
071            try {
072                            doc=su.loadDocument(xml);
073                    } catch (Exception e) {}
074            tasks=new ScheduleTaskImpl[0];
075            init();
076        }
077        
078        
079        
080        
081            private void initFile(Resource schedulerDir, LogAndSource log) throws IOException {
082                    this.schedulerFile=schedulerDir.getRealResource("scheduler.xml");
083                    if(!schedulerFile.exists()) su.loadFile(schedulerFile,"/resource/schedule/default.xml");
084                    this.log=log;  
085            }
086            
087        /**
088         * initialize all tasks
089         */
090        private void init() {
091            for(int i=0;i<tasks.length;i++) {
092                init(tasks[i]);
093            }
094        }
095    
096            private void init(ScheduleTask task) {
097                    new ScheduledTaskThread(engine,this,config,log,task,charset).start();
098            }
099    
100            /**
101             * read in all schedule tasks
102             * @return all schedule tasks
103             * @throws PageException
104         */
105        private ScheduleTaskImpl[] readInAllTasks() throws PageException {
106            Element root = doc.getDocumentElement();
107            NodeList children = root.getChildNodes();
108            ArrayList<ScheduleTaskImpl> list=new ArrayList<ScheduleTaskImpl>();
109            
110            int len=children.getLength();
111            for(int i=0;i<len;i++) {
112                Node n=children.item(i);
113                if(n instanceof Element && n.getNodeName().equals("task")) {
114                    list.add(readInTask((Element)n));
115                } 
116            }
117            return (ScheduleTaskImpl[]) list.toArray(new ScheduleTaskImpl[list.size()]);
118        }
119        
120        
121    
122        /**
123         * read in a single task element
124         * @param el
125         * @return matching task to Element
126         * @throws PageException
127         */
128        private ScheduleTaskImpl readInTask(Element el) throws PageException {
129            long timeout=su.toLong(el,"timeout");
130            if(timeout>0 && timeout<1000)timeout*=1000;
131            if(timeout<0)timeout=600000;
132            try {
133                ScheduleTaskImpl  st = new ScheduleTaskImpl(
134                        su.toString(el,"name").trim(),
135                        su.toResource(config,el,"file"),
136                        su.toDate(config,el,"startDate"),
137                        su.toTime(config,el,"startTime"),
138                        su.toDate(config,el,"endDate"),
139                        su.toTime(config,el,"endTime"),
140                        su.toString(el,"url"),
141                        su.toInt(el,"port",-1),
142                        su.toString(el,"interval"),
143                        timeout,
144                        su.toCredentials(el,"username","password"),
145                        su.toString(el,"proxyHost"),
146                        su.toInt(el,"proxyPort",80),
147                        su.toCredentials(el,"proxyUser","proxyPassword"),
148                        su.toBoolean(el,"resolveUrl"),
149                        su.toBoolean(el,"publish"),
150                        su.toBoolean(el,"hidden",false),
151                        su.toBoolean(el,"readonly",false),
152                        su.toBoolean(el,"paused",false),
153                        su.toBoolean(el,"autoDelete",false));
154                return st;
155            } catch (Exception e) {e.printStackTrace();
156                throw Caster.toPageException(e);
157            }
158        }
159        
160    
161            private void addTask(ScheduleTaskImpl task) {   
162                    for(int i=0;i<tasks.length;i++){
163                            if(!tasks[i].getTask().equals(task.getTask())) continue;
164                            if(!tasks[i].md5().equals(task.md5())) {
165                                    tasks[i].setValid(false);
166                                    tasks[i]=task;
167                                    init(task);
168                            }
169                            return;
170                    }
171                    
172                    ScheduleTaskImpl[] tmp = new ScheduleTaskImpl[tasks.length+1];
173                    for(int i=0;i<tasks.length;i++){
174                            tmp[i]=tasks[i];
175                    }
176                    tmp[tasks.length]=task;
177                    tasks=tmp;
178                    init(task);
179            }
180        
181        /**
182         * sets all attributes in XML Element from Schedule Task
183         * @param el
184         * @param task
185         */
186        private void setAttributes(Element el,ScheduleTask task) {
187            if(el==null) return;
188            NamedNodeMap atts = el.getAttributes();
189            
190            for(int i=atts.getLength()-1;i>=0;i--) {
191                Attr att=(Attr) atts.item(i);
192                el.removeAttribute(att.getName());
193            }
194            
195            su.setString(el,"name",task.getTask());
196            su.setFile(el,"file",task.getResource());
197            su.setDateTime(el,"startDate",task.getStartDate());
198            su.setDateTime(el,"startTime",task.getStartTime());
199            su.setDateTime(el,"endDate",task.getEndDate());
200            su.setDateTime(el,"endTime",task.getEndTime());
201            su.setString(el,"url",task.getUrl().toExternalForm());
202            su.setInt(el,"port",task.getUrl().getPort());
203            su.setString(el,"interval",task.getIntervalAsString());
204            su.setInt(el,"timeout",(int)task.getTimeout());
205            su.setCredentials(el,"username","password",task.getCredentials());
206            su.setString(el,"proxyHost",task.getProxyHost());
207            su.setInt(el,"proxyPort",task.getProxyPort());
208            su.setCredentials(el,"proxyUser","proxyPassword",task.getProxyCredentials());
209            su.setBoolean(el,"resolveUrl",task.isResolveURL());  
210            su.setBoolean(el,"publish",task.isPublish());   
211            su.setBoolean(el,"hidden",((ScheduleTaskImpl)task).isHidden());  
212            su.setBoolean(el,"readonly",((ScheduleTaskImpl)task).isReadonly());  
213            su.setBoolean(el,"autoDelete",((ScheduleTaskImpl)task).isAutoDelete());  
214        }
215        
216        /**
217         * translate a schedule task object to a XML Element
218         * @param task schedule task to translate
219         * @return XML Element
220         */
221        private Element toElement(ScheduleTask task) {
222            Element el = doc.createElement("task");
223            setAttributes(el,task);   
224            return el;
225        }
226    
227            /**
228         * @see railo.runtime.schedule.Scheduler#getScheduleTask(java.lang.String)
229         */
230            public ScheduleTask getScheduleTask(String name) throws ScheduleException {
231                for(int i=0;i<tasks.length;i++) {
232                    if(tasks[i].getTask().equalsIgnoreCase(name)) return tasks[i];
233                }
234                throw new ScheduleException("schedule task with name "+name+" doesn't exist");
235            }
236            
237            /**
238         * @see railo.runtime.schedule.Scheduler#getScheduleTask(java.lang.String, railo.runtime.schedule.ScheduleTask)
239         */
240            public ScheduleTask getScheduleTask(String name, ScheduleTask defaultValue) {
241                for(int i=0;i<tasks.length;i++) {
242                    if(tasks[i]!=null && tasks[i].getTask().equalsIgnoreCase(name)) return tasks[i];
243                }
244                return defaultValue;
245            }
246    
247            /**
248         * @see railo.runtime.schedule.Scheduler#getAllScheduleTasks()
249         */
250            public ScheduleTask[] getAllScheduleTasks() {
251                    ArrayList<ScheduleTask> list=new ArrayList<ScheduleTask>();
252                    for(int i=0;i<tasks.length;i++) {
253                    if(!tasks[i].isHidden()) list.add(tasks[i]);
254                }
255                return (ScheduleTask[]) list.toArray(new ScheduleTask[list.size()]);
256            }
257            
258            /**
259         * @see railo.runtime.schedule.Scheduler#addScheduleTask(railo.runtime.schedule.ScheduleTask, boolean)
260         */
261            public void addScheduleTask(ScheduleTask task, boolean allowOverwrite) throws ScheduleException, IOException {
262                //ScheduleTask exTask = getScheduleTask(task.getTask(),null);
263                NodeList list = doc.getDocumentElement().getChildNodes();
264                Element el=su.getElement(list,"name", task.getTask());
265                
266                if(!allowOverwrite && el!=null)
267                        throw new ScheduleException("there is already a schedule task with name "+task.getTask());
268                
269                addTask((ScheduleTaskImpl)task);
270                
271                // Element update
272                if(el!=null) {
273                        setAttributes(el,task);
274                }
275                // Element insert
276                else {
277                        doc.getDocumentElement().appendChild(toElement(task));
278                }
279                
280                su.store(doc,schedulerFile);
281            }
282            
283    
284        // FUTURE add to interface
285            public void pauseScheduleTask(String name, boolean pause, boolean throwWhenNotExist) throws ScheduleException, IOException {
286    
287                for(int i=0;i<tasks.length;i++) {
288                    if(tasks[i].getTask().equalsIgnoreCase(name)) {
289                            tasks[i].setPaused(pause);
290                        
291                    }
292                }
293                
294                NodeList list = doc.getDocumentElement().getChildNodes();
295                Element el=su.getElement(list,"name", name);
296                if(el!=null) {
297                    el.setAttribute("paused", Caster.toString(pause));
298                    //el.getParentNode().removeChild(el);
299                }
300                else if(throwWhenNotExist) throw new ScheduleException("can't "+(pause?"pause":"resume")+" schedule task ["+name+"], task doesn't exist");
301                
302                //init();
303                su.store(doc,schedulerFile);
304            }
305    
306            /**
307         * @see railo.runtime.schedule.Scheduler#removeScheduleTask(java.lang.String, boolean)
308         */
309            public synchronized void removeScheduleTask(String name, boolean throwWhenNotExist) throws IOException, ScheduleException {
310                
311                int pos=-1;
312                for(int i=0;i<tasks.length;i++) {
313                    if(tasks[i].getTask().equalsIgnoreCase(name)) {
314                            tasks[i].setValid(false);
315                        pos=i;
316                    }
317                }
318                if(pos!=-1) {
319                        ScheduleTaskImpl[] newTasks=new ScheduleTaskImpl[tasks.length-1];
320                        int count=0;
321                        for(int i=0;i<tasks.length;i++) {
322                            if(i!=pos)newTasks[count++]=tasks[i];
323                            
324                        }
325                        tasks=newTasks;
326                }
327                
328                
329                NodeList list = doc.getDocumentElement().getChildNodes();
330                Element el=su.getElement(list,"name", name);
331                if(el!=null) {
332                    el.getParentNode().removeChild(el);
333                }
334                else if(throwWhenNotExist) throw new ScheduleException("can't delete schedule task ["+name+"], task doesn't exist");
335                
336                //init();
337                su.store(doc,schedulerFile);
338            }
339            
340            public synchronized void removeIfNoLonerValid(ScheduleTask task) throws IOException {
341                    ScheduleTaskImpl sti=(ScheduleTaskImpl) task;
342                    if(sti.isValid() || !sti.isAutoDelete()) return;
343                    
344                    
345                    try {
346                            removeScheduleTask(task.getTask(), false);
347                    } catch (ScheduleException e) {}
348            }
349            
350    
351            /**
352         * @see railo.runtime.schedule.Scheduler#runScheduleTask(java.lang.String, boolean)
353         */
354            public synchronized void runScheduleTask(String name, boolean throwWhenNotExist) throws IOException, ScheduleException {
355                ScheduleTask task = getScheduleTask(name);
356                
357                if(task!=null) {
358                    execute(task);
359                }
360                else if(throwWhenNotExist) throw new ScheduleException("can't run schedule task ["+name+"], task doesn't exist");
361                
362                
363                su.store(doc,schedulerFile);
364            }
365        
366        /**
367         * @param task 
368         * @see railo.runtime.schedule.Scheduler#execute()
369         */
370        public void execute(ScheduleTask task) {
371            new ExecutionThread(config,log,task,charset).start();
372        } 
373        
374    
375        /**
376         * @see railo.runtime.schedule.Scheduler#getNextExecutionTime()
377         */
378        public long getNextExecutionTime() {
379            // no longer called and used
380            return -1;
381        }
382    
383        /**
384         * @see railo.runtime.schedule.Scheduler#getLogger()
385         */
386        public LogAndSource getLogger() {
387            return log;
388        }
389    
390        /*
391         * FUTURE remove
392         * */
393            public void execute() {
394                    
395            }
396    }