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