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.tag; 020 021import java.net.URL; 022 023import lucee.commons.io.res.Resource; 024import lucee.commons.io.res.util.ResourceUtil; 025import lucee.commons.lang.StringUtil; 026import lucee.commons.net.HTTPUtil; 027import lucee.commons.security.Credentials; 028import lucee.commons.security.CredentialsImpl; 029import lucee.runtime.exp.ApplicationException; 030import lucee.runtime.exp.DatabaseException; 031import lucee.runtime.exp.PageException; 032import lucee.runtime.ext.tag.TagImpl; 033import lucee.runtime.net.proxy.ProxyData; 034import lucee.runtime.net.proxy.ProxyDataImpl; 035import lucee.runtime.op.Caster; 036import lucee.runtime.op.date.DateCaster; 037import lucee.runtime.schedule.ScheduleTask; 038import lucee.runtime.schedule.ScheduleTaskImpl; 039import lucee.runtime.schedule.Scheduler; 040import lucee.runtime.schedule.SchedulerImpl; 041import lucee.runtime.type.QueryImpl; 042import lucee.runtime.type.dt.Date; 043import lucee.runtime.type.dt.DateImpl; 044import lucee.runtime.type.dt.Time; 045import lucee.runtime.type.util.KeyConstants; 046/** 047* Provides a programmatic interface to the Lucee scheduling engine. You can run a specified 048* page at scheduled intervals with the option to write out static HTML pages. This lets you offer users 049* access to pages that publish data, such as reports, without forcing users to wait while a database transaction 050* is performed in order to populate the data on the page. 051**/ 052public final class Schedule extends TagImpl { 053 054 private static final short ACTION_RUN=1; 055 private static final short ACTION_UPDATE=2; 056 private static final short ACTION_DELETE=3; 057 private static final short ACTION_LIST=4; 058 private static final short ACTION_PAUSE=5; 059 private static final short ACTION_RESUME=6; 060 061 062 /** Password if URL is protected. */ 063 private String password=""; 064 065 /** Required when action ='update'. The date when scheduling of the task should start. */ 066 private Date startdate; 067 068 /** Specifies whether to resolve links in the result page to absolute references. */ 069 private boolean resolveurl; 070 071 /** */ 072 private short action; 073 074 /** Host name or IP address of a proxy server. */ 075 private String proxyserver; 076 077 /** The date when the scheduled task ends. */ 078 private Date enddate; 079 080 /** Required with publish ='Yes' A valid filename for the published file. */ 081 private String strFile; 082 083 /** Required when creating tasks with action = 'update'. Enter a value in seconds. The time when 084 ** scheduling of the task starts. */ 085 private Time starttime; 086 087 /** The port number on the proxy server from which the task is being requested. Default is 80. When 088 ** used with resolveURL, the URLs of retrieved documents that specify a port number are automatically 089 ** resolved to preserve links in the retrieved document. */ 090 private int proxyport=80; 091 092 /** The port number on the server from which the task is being scheduled. Default is 80. When used 093 ** with resolveURL, the URLs of retrieved documents that specify a port number are automatically resolved 094 ** to preserve links in the retrieved document. */ 095 private int port=-1; 096 097 /** The time when the scheduled task ends. Enter a value in seconds. */ 098 private Time endtime; 099 100 101 /** Required when creating tasks with action = 'update'. Interval at which task should be scheduled. 102 ** Can be set in seconds or as Once, Daily, Weekly, and Monthly. The default interval is one hour. The 103 ** minimum interval is one minute. */ 104 private String interval; 105 106 /** Specifies whether the result should be saved to a file. */ 107 private boolean publish; 108 109 /** Customizes the requestTimeOut for the task operation. Can be used to extend the default timeout 110 ** for operations that require more time to execute. */ 111 private long requesttimeout=-1; 112 113 /** Username if URL is protected. */ 114 private String username; 115 116 /** Required when action = 'update'. The URL to be executed. */ 117 private String url; 118 119 /** Required with publish ='Yes' The path location for the published file. */ 120 private String strPath; 121 122 /** The name of the task to delete, update, or run. */ 123 private String task; 124 private Scheduler scheduler; 125 126 private String proxyuser; 127 private String proxypassword=""; 128 129 private String returnvariable="cfschedule"; 130 private boolean hidden; 131 private boolean readonly; 132 private String serverPassword=null; 133 private boolean paused; 134 private boolean autoDelete; 135 136 public void setAutodelete(boolean autoDelete) { 137 this.autoDelete=autoDelete; 138 } 139 140 /** 141 * @param readonly the readonly to set 142 */ 143 public void setReadonly(boolean readonly) { 144 this.readonly = readonly; 145 } 146 147 /** 148 * @param hidden the hidden to set 149 */ 150 public void setHidden(boolean hidden) { 151 this.hidden = hidden; 152 } 153 154 /** 155 * @param returnvariable The returnvariable to set. 156 */ 157 public void setReturnvariable(String returnvariable) { 158 this.returnvariable = returnvariable; 159 } 160 161 /** 162 * @param proxypassword The proxypassword to set. 163 */ 164 public void setProxypassword(String proxypassword) { 165 this.proxypassword = proxypassword; 166 } 167 /** 168 * @param proxyuser The proxyuser to set. 169 */ 170 public void setProxyuser(String proxyuser) { 171 this.proxyuser = proxyuser; 172 } 173 174 public void setPaused(boolean paused) { 175 this.paused = paused; 176 } 177 178 /** set the value password 179 * Password if URL is protected. 180 * @param password value to set 181 **/ 182 public void setPassword(String password) { 183 this.password=password; 184 } 185 186 /** set the value startdate 187 * Required when action ='update'. The date when scheduling of the task should start. 188 * @param objStartDate value to set 189 * @throws PageException 190 **/ 191 public void setStartdate(Object objStartDate) throws PageException { 192 if(StringUtil.isEmpty(objStartDate)) return; 193 this.startdate=new DateImpl(DateCaster.toDateAdvanced(objStartDate,pageContext.getTimeZone())); 194 } 195 196 /** set the value resolveurl 197 * Specifies whether to resolve links in the result page to absolute references. 198 * @param resolveurl value to set 199 **/ 200 public void setResolveurl(boolean resolveurl) { 201 this.resolveurl=resolveurl; 202 } 203 204 public void setServerpassword(String serverPassword) { 205 this.serverPassword=serverPassword; 206 } 207 208 /** set the value action 209 * 210 * @param action value to set 211 * @throws ApplicationException 212 **/ 213 public void setAction(String action) throws ApplicationException { 214 if(StringUtil.isEmpty(action)) return; 215 action=action.toLowerCase().trim(); 216 if(action.equals("run"))this.action=ACTION_RUN; 217 else if(action.equals("delete"))this.action=ACTION_DELETE; 218 else if(action.equals("update"))this.action=ACTION_UPDATE; 219 else if(action.equals("list"))this.action=ACTION_LIST; 220 else if(action.equals("lists"))this.action=ACTION_LIST; 221 else if(action.equals("pause"))this.action=ACTION_PAUSE; 222 else if(action.equals("resume"))this.action=ACTION_RESUME; 223 else throw new ApplicationException("attribute action with value ["+action+"] of tag schedule is invalid","valid attributes are [delete,run,update,list,resume,pause]"); 224 } 225 226 /** set the value proxyserver 227 * Host name or IP address of a proxy server. 228 * @param proxyserver value to set 229 **/ 230 public void setProxyserver(String proxyserver) { 231 this.proxyserver=proxyserver; 232 } 233 234 /** set the value enddate 235 * The date when the scheduled task ends. 236 * @param enddate value to set 237 * @throws PageException 238 **/ 239 public void setEnddate(Object enddate) throws PageException { 240 if(StringUtil.isEmpty(enddate)) return; 241 this.enddate=new DateImpl(DateCaster.toDateAdvanced(enddate,pageContext.getTimeZone())); 242 } 243 244 /** set the value file 245 * Required with publish ='Yes' A valid filename for the published file. 246 * @param file value to set 247 **/ 248 public void setFile(String file) { 249 this.strFile=file; 250 } 251 252 /** set the value starttime 253 * Required when creating tasks with action = 'update'. Enter a value in seconds. The time when 254 * scheduling of the task starts. 255 * @param starttime value to set 256 * @throws PageException 257 **/ 258 public void setStarttime(Object starttime) throws PageException { 259 if(StringUtil.isEmpty(starttime)) return; 260 this.starttime=DateCaster.toTime(pageContext.getTimeZone(),starttime); 261 } 262 263 /** set the value proxyport 264 * The port number on the proxy server from which the task is being requested. Default is 80. When 265 * used with resolveURL, the URLs of retrieved documents that specify a port number are automatically 266 * resolved to preserve links in the retrieved document. 267 * @param proxyport value to set 268 * @throws PageException 269 **/ 270 public void setProxyport(Object oProxyport) throws PageException { 271 if(StringUtil.isEmpty(oProxyport)) return; 272 this.proxyport=Caster.toIntValue(oProxyport); 273 } 274 275 /** set the value port 276 * The port number on the server from which the task is being scheduled. Default is 80. When used 277 * with resolveURL, the URLs of retrieved documents that specify a port number are automatically resolved 278 * to preserve links in the retrieved document. 279 * @param port value to set 280 * @throws PageException 281 **/ 282 public void setPort(Object oPort) throws PageException { 283 if(StringUtil.isEmpty(oPort)) return; 284 this.port=Caster.toIntValue(oPort); 285 } 286 287 /** set the value endtime 288 * The time when the scheduled task ends. Enter a value in seconds. 289 * @param endtime value to set 290 * @throws PageException 291 **/ 292 public void setEndtime(Object endtime) throws PageException { 293 if(StringUtil.isEmpty(endtime)) return; 294 this.endtime=DateCaster.toTime(pageContext.getTimeZone(),endtime); 295 } 296 297 /** set the value operation 298 * The type of operation the scheduler performs when executing this task. 299 * @param operation value to set 300 * @throws ApplicationException 301 **/ 302 public void setOperation(String operation) throws ApplicationException { 303 if(StringUtil.isEmpty(operation)) return; 304 operation=operation.toLowerCase().trim(); 305 if(!operation.equals("httprequest")) 306 throw new ApplicationException("attribute operation must have the value [HTTPRequest]"); 307 } 308 309 /** set the value interval 310 * Required when creating tasks with action = 'update'. Interval at which task should be scheduled. 311 * Can be set in seconds or as Once, Daily, Weekly, and Monthly. The default interval is one hour. The 312 * minimum interval is one minute. 313 * @param interval value to set 314 **/ 315 public void setInterval(String interval) { 316 if(StringUtil.isEmpty(interval)) return; 317 interval=interval.trim().toLowerCase(); 318 if(interval.equals("week")) this.interval="weekly"; 319 else if(interval.equals("day")) this.interval="daily"; 320 else if(interval.equals("month")) this.interval="monthly"; 321 else if(interval.equals("year")) this.interval="yearly"; 322 this.interval=interval; 323 } 324 325 /** set the value publish 326 * Specifies whether the result should be saved to a file. 327 * @param publish value to set 328 **/ 329 public void setPublish(boolean publish) { 330 this.publish=publish; 331 } 332 333 /** set the value requesttimeout 334 * Customizes the requestTimeOut for the task operation. Can be used to extend the default timeout 335 * for operations that require more time to execute. 336 * @param requesttimeout value to set 337 **/ 338 public void setRequesttimeout(Object oRequesttimeout) throws PageException { 339 if(StringUtil.isEmpty(oRequesttimeout)) return; 340 this.requesttimeout=Caster.toLongValue(oRequesttimeout)*1000L; 341 } 342 343 /** set the value username 344 * Username if URL is protected. 345 * @param username value to set 346 **/ 347 public void setUsername(String username) { 348 this.username=username; 349 } 350 351 /** set the value url 352 * Required when action = 'update'. The URL to be executed. 353 * @param url value to set 354 **/ 355 public void setUrl(String url) { 356 this.url=url; 357 } 358 359 /** set the value path 360 * Required with publish ='Yes' The path location for the published file. 361 * @param path value to set 362 **/ 363 public void setPath(String path) { 364 this.strPath=path; 365 } 366 367 /** set the value task 368 * The name of the task to delete, update, or run. 369 * @param task value to set 370 **/ 371 public void setTask(String task) { 372 this.task=task; 373 } 374 375 376 @Override 377 public int doStartTag() throws PageException { 378 scheduler=pageContext.getConfig().getScheduler(); 379 380 if(action!=ACTION_LIST && task==null) { 381 throw new ApplicationException("attribute task is required for tag schedule when action is not list"); 382 } 383 384 385 switch(action) { 386 case ACTION_DELETE: doDelete(); break; 387 case ACTION_RUN: doRun(); break; 388 case ACTION_UPDATE: doUpdate(); break; 389 case ACTION_LIST: doList(); break; 390 case ACTION_PAUSE: doPause(true); break; 391 case ACTION_RESUME: doPause(false); break; 392 } 393 return SKIP_BODY; 394 } 395 396 /** 397 * @throws PageException 398 */ 399 private void doUpdate() throws PageException { 400 String message="missing attribute for tag schedule with action update"; 401 String detail="required attributes are [startDate, startTime, URL, interval, operation]"; 402 403 Resource file=null; 404 //if(publish) { 405 if(!StringUtil.isEmpty(strFile) && !StringUtil.isEmpty(strPath)) { 406 file=ResourceUtil.toResourceNotExisting(pageContext, strPath); 407 file=file.getRealResource(strFile); 408 } 409 else if(!StringUtil.isEmpty(strFile)) { 410 file=ResourceUtil.toResourceNotExisting(pageContext, strFile); 411 } 412 else if(!StringUtil.isEmpty(strPath)) { 413 file=ResourceUtil.toResourceNotExisting(pageContext, strPath); 414 } 415 if(file!=null)pageContext.getConfig().getSecurityManager().checkFileLocation(pageContext.getConfig(),file,serverPassword); 416 417 // missing attributes 418 if(startdate==null || starttime==null || url==null || interval==null) 419 throw new ApplicationException(message,detail); 420 421 // timeout 422 if(requesttimeout<0)requesttimeout=pageContext.getRequestTimeout(); 423 424 // username/password 425 Credentials cr=null; 426 if(username!=null) cr=CredentialsImpl.toCredentials(username,password); 427 428 try { 429 430 ScheduleTask st=new ScheduleTaskImpl( 431 task, 432 file, 433 startdate, 434 starttime, 435 enddate, 436 endtime, 437 url, 438 port, 439 interval, 440 requesttimeout, 441 cr, 442 ProxyDataImpl.getInstance(proxyserver,proxyport,proxyuser,proxypassword), 443 resolveurl, 444 publish, 445 hidden, 446 readonly, 447 paused, 448 autoDelete); 449 scheduler.addScheduleTask(st,true); 450 } catch (Exception e) { 451 throw Caster.toPageException(e); 452 } 453 454 455 456 457 458 // 459 } 460 461 /** 462 * @throws PageException 463 */ 464 private void doRun() throws PageException { 465 try { 466 scheduler.runScheduleTask(task,true); 467 } catch (Exception e) { 468 throw Caster.toPageException(e); 469 } 470 } 471 472 /** 473 * @throws PageException 474 */ 475 private void doDelete() throws PageException { 476 try { 477 scheduler.removeScheduleTask(task,false); 478 } catch (Exception e) { 479 throw Caster.toPageException(e); 480 } 481 } 482 483 /** 484 * @throws PageException 485 * 486 */ 487 private void doList() throws PageException { 488 //if(tr ue) throw new PageRuntimeException("qqq"); 489 ScheduleTask[] tasks = scheduler.getAllScheduleTasks(); 490 final String v="VARCHAR"; 491 String[] cols = new String[]{"task","path","file","startdate","starttime","enddate","endtime", 492 "url","port","interval","timeout","username","password","proxyserver", 493 "proxyport","proxyuser","proxypassword","resolveurl","publish","valid","paused","autoDelete"}; 494 String[] types = new String[]{v,v,v,"DATE","OTHER","DATE","OTHER", 495 v,v,v,v,v,v,v, 496 v,v,v,v,"BOOLEAN",v,"BOOLEAN","BOOLEAN"}; 497 lucee.runtime.type.Query query=new QueryImpl(cols,types,tasks.length,"query" 498 ); 499 try { 500 for(int i=0;i<tasks.length;i++) { 501 int row=i+1; 502 ScheduleTask task=tasks[i]; 503 query.setAt(KeyConstants._task,row,task.getTask()); 504 if(task.getResource()!=null) { 505 query.setAt(KeyConstants._path,row,task.getResource().getParent()); 506 query.setAt(KeyConstants._file,row,task.getResource().getName()); 507 } 508 query.setAt("publish",row,Caster.toBoolean(task.isPublish())); 509 query.setAt("startdate",row,task.getStartDate()); 510 query.setAt("starttime",row,task.getStartTime()); 511 query.setAt("enddate",row,task.getEndDate()); 512 query.setAt("endtime",row,task.getEndTime()); 513 query.setAt(KeyConstants._url,row,printUrl(task.getUrl())); 514 query.setAt(KeyConstants._port,row,Caster.toString(HTTPUtil.getPort(task.getUrl()))); 515 query.setAt("interval",row,task.getStringInterval()); 516 query.setAt("timeout",row,Caster.toString(task.getTimeout()/1000)); 517 query.setAt("valid",row,Caster.toString(task.isValid())); 518 if(task.hasCredentials()) { 519 query.setAt("username",row,task.getCredentials().getUsername()); 520 query.setAt("password",row,task.getCredentials().getPassword()); 521 } 522 ProxyData pd = task.getProxyData(); 523 if(ProxyDataImpl.isValid(pd)){ 524 query.setAt("proxyserver",row,pd.getServer()); 525 if(pd.getPort()>0)query.setAt("proxyport",row,Caster.toString(pd.getPort())); 526 if(ProxyDataImpl.hasCredentials(pd)) { 527 query.setAt("proxyuser",row,pd.getUsername()); 528 query.setAt("proxypassword",row,pd.getPassword()); 529 } 530 } 531 query.setAt("resolveurl",row,Caster.toString(task.isResolveURL())); 532 533 query.setAt("paused",row,Caster.toBoolean(task.isPaused())); 534 query.setAt("autoDelete",row,Caster.toBoolean(((ScheduleTaskImpl)task).isAutoDelete())); 535 536 537 538 } 539 pageContext.setVariable(returnvariable,query); 540 } 541 catch (DatabaseException e) {} 542 543 544 } 545 546 private void doPause(boolean pause) throws PageException { 547 try { 548 ((SchedulerImpl)scheduler).pauseScheduleTask(task,pause,true); 549 } catch (Exception e) { 550 throw Caster.toPageException(e); 551 } 552 553 } 554 555 private String printUrl(URL url) { 556 557 String qs=url.getQuery(); 558 if(qs==null) qs=""; 559 else if(qs.length()>0)qs="?"+qs; 560 561 String str=url.getProtocol()+"://"+url.getHost()+url.getPath()+qs; 562 return str; 563 } 564 @Override 565 public int doEndTag() { 566 return EVAL_PAGE; 567 } 568 569 @Override 570 public void release() { 571 super.release(); 572 readonly=false; 573 strPath=null; 574 strFile=null; 575 starttime=null; 576 startdate=null; 577 endtime=null; 578 enddate=null; 579 url=null; 580 port=-1; 581 interval=null; 582 requesttimeout=-1; 583 username=null; 584 password=""; 585 proxyserver=null; 586 proxyport=80; 587 proxyuser=null; 588 proxypassword=""; 589 resolveurl=false; 590 publish=false; 591 returnvariable="cfschedule"; 592 task=null; 593 hidden=false; 594 serverPassword=null; 595 paused=false; 596 autoDelete=false; 597 } 598 599}