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