001/* 002 * This library is part of OpenCms - 003 * the Open Source Content Management System 004 * 005 * Copyright (c) Alkacon Software GmbH & Co. KG (http://www.alkacon.com) 006 * 007 * This library is free software; you can redistribute it and/or 008 * modify it under the terms of the GNU Lesser General Public 009 * License as published by the Free Software Foundation; either 010 * version 2.1 of the License, or (at your option) any later version. 011 * 012 * This library is distributed in the hope that it will be useful, 013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 015 * Lesser General Public License for more details. 016 * 017 * For further information about Alkacon Software GmbH & Co. KG, please see the 018 * company website: http://www.alkacon.com 019 * 020 * For further information about OpenCms, please see the 021 * project website: http://www.opencms.org 022 * 023 * You should have received a copy of the GNU Lesser General Public 024 * License along with this library; if not, write to the Free Software 025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 026 */ 027 028package org.opencms.scheduler; 029 030import org.opencms.configuration.CmsParameterConfiguration; 031import org.opencms.configuration.I_CmsConfigurationParameterHandler; 032import org.opencms.i18n.CmsMessageContainer; 033import org.opencms.main.CmsContextInfo; 034import org.opencms.main.CmsIllegalArgumentException; 035import org.opencms.main.CmsLog; 036import org.opencms.main.CmsRuntimeException; 037import org.opencms.main.OpenCms; 038import org.opencms.util.CmsStringUtil; 039 040import java.io.Serializable; 041import java.util.Collections; 042import java.util.Date; 043import java.util.SortedMap; 044import java.util.TreeMap; 045 046import javax.validation.Valid; 047 048import org.apache.commons.logging.Log; 049 050import org.quartz.CronExpression; 051import org.quartz.Trigger; 052 053/** 054 * Describes a scheduled job for the OpenCms scheduler.<p> 055 * 056 * <p> 057 * The time the scheduled job is executed is defined with Unix 'cron-like' definitions. 058 * </p> 059 * 060 * <p> 061 * For those unfamiliar with "cron", this means being able to create a firing 062 * schedule such as: "At 8:00am every Monday through Friday" or "At 1:30am 063 * every last Friday of the month". 064 * </p> 065 * 066 * <p> 067 * A "Cron-Expression" is a string comprised of 6 or 7 fields separated by 068 * white space. The 6 mandatory and 1 optional fields are as follows: <br> 069 * 070 * <table cellspacing="8"> 071 * <tr> 072 * <th align="left">Field Name</th> 073 * <th align="left"> </th> 074 * <th align="left">Allowed Values</th> 075 * <th align="left"> </th> 076 * <th align="left">Allowed Special Characters</th> 077 * </tr> 078 * <tr> 079 * <td align="left"><code>Seconds</code></td> 080 * <td align="left"> </td> 081 * <td align="left"><code>0-59</code></td> 082 * <td align="left"> </td> 083 * <td align="left"><code>, - * /</code></td> 084 * </tr> 085 * <tr> 086 * <td align="left"><code>Minutes</code></td> 087 * <td align="left"> </td> 088 * <td align="left"><code>0-59</code></td> 089 * <td align="left"> </td> 090 * <td align="left"><code>, - * /</code></td> 091 * </tr> 092 * <tr> 093 * <td align="left"><code>Hours</code></td> 094 * <td align="left"> </td> 095 * <td align="left"><code>0-23</code></td> 096 * <td align="left"> </td> 097 * <td align="left"><code>, - * /</code></td> 098 * </tr> 099 * <tr> 100 * <td align="left"><code>Day-of-month</code></td> 101 * <td align="left"> </td> 102 * <td align="left"><code>1-31</code></td> 103 * <td align="left"> </td> 104 * <td align="left"><code>, - * ? / L C</code></td> 105 * </tr> 106 * <tr> 107 * <td align="left"><code>Month</code></td> 108 * <td align="left"> </td> 109 * <td align="left"><code>1-12 or JAN-DEC</code></td> 110 * <td align="left"> </td> 111 * <td align="left"><code>, - * /</code></td> 112 * </tr> 113 * <tr> 114 * <td align="left"><code>Day-of-Week</code></td> 115 * <td align="left"> </td> 116 * <td align="left"><code>1-7 or SUN-SAT</code></td> 117 * <td align="left"> </td> 118 * <td align="left"><code>, - * ? / L C #</code></td> 119 * </tr> 120 * <tr> 121 * <td align="left"><code>Year (Optional)</code></td> 122 * <td align="left"> </td> 123 * <td align="left"><code>empty, 1970-2099</code></td> 124 * <td align="left"> </td> 125 * <td align="left"><code>, - * /</code></td> 126 * </tr> 127 * </table> 128 * </p> 129 * 130 * <p> 131 * The '*' character is used to specify all values. For example, "*" in the 132 * minute field means "every minute". 133 * </p> 134 * 135 * <p> 136 * The '?' character is allowed for the day-of-month and day-of-week fields. It 137 * is used to specify 'no specific value'. This is useful when you need to 138 * specify something in one of the two fields, but not the other. See the 139 * examples below for clarification. 140 * </p> 141 * 142 * <p> 143 * The '-' character is used to specify ranges For example "10-12" in the hour 144 * field means "the hours 10, 11 and 12". 145 * </p> 146 * 147 * <p> 148 * The ',' character is used to specify additional values. For example 149 * "MON,WED,FRI" in the day-of-week field means "the days Monday, Wednesday, 150 * and Friday". 151 * </p> 152 * 153 * <p> 154 * The '/' character is used to specify increments. For example "0/15" in the 155 * seconds field means "the seconds 0, 15, 30, and 45". And "5/15" in the 156 * seconds field means "the seconds 5, 20, 35, and 50". You can also specify 157 * '/' after the '*' character - in this case '*' is equivalent to having '0' 158 * before the '/'. 159 * </p> 160 * 161 * <p> 162 * The 'L' character is allowed for the day-of-month and day-of-week fields. 163 * This character is short-hand for "last", but it has different meaning in 164 * each of the two fields. For example, the value "L" in the day-of-month field 165 * means "the last day of the month" - day 31 for January, day 28 for February 166 * on non-leap years. If used in the day-of-week field by itself, it simply 167 * means "7" or "SAT". But if used in the day-of-week field after another 168 * value, it means "the last xxx day of the month" - for example "6L" means 169 * "the last friday of the month". When using the 'L' option, it is important 170 * not to specify lists, or ranges of values, as you'll get confusing results. 171 * </p> 172 * 173 * <p> 174 * The 'W' character is allowed for the day-of-month field. This character 175 * is used to specify the weekday (Monday-Friday) nearest the given day. As an 176 * example, if you were to specify "15W" as the value for the day-of-month 177 * field, the meaning is: "the nearest weekday to the 15th of the month". So 178 * if the 15th is a Saturday, the trigger will fire on Friday the 14th. If the 179 * 15th is a Sunday, the trigger will fire on Monday the 16th. If the 15th is 180 * a Tuesday, then it will fire on Tuesday the 15th. However if you specify 181 * "1W" as the value for day-of-month, and the 1st is a Saturday, the trigger 182 * will fire on Monday the 3rd, as it will not 'jump' over the boundary of a 183 * month's days. The 'W' character can only be specified when the day-of-month 184 * is a single day, not a range or list of days. 185 * </p> 186 * 187 * <p> 188 * The 'L' and 'W' characters can also be combined for the day-of-month 189 * expression to yield 'LW', which translates to "last weekday of the month". 190 * </p> 191 * 192 * <p> 193 * The '#' character is allowed for the day-of-week field. This character is 194 * used to specify "the nth" day of the month. For example, the value of 195 * "6#3" in the day-of-week field means the third Friday of the month (day 6 = 196 * Friday and "#3" = the 3rd one in the month). Other examples: "2#1" = the 197 * first Monday of the month and "4#5" = the fifth Wednesday of the month. Note 198 * that if you specify "#5" and there is not 5 of the given day-of-week in the 199 * month, then no firing will occur that month. 200 * </p> 201 * 202 * <p> 203 * The 'C' character is allowed for the day-of-month and day-of-week fields. 204 * This character is short-hand for "calendar". This means values are 205 * calculated against the associated calendar, if any. If no calendar is 206 * associated, then it is equivalent to having an all-inclusive calendar. A 207 * value of "5C" in the day-of-month field means "the first day included by the 208 * calendar on or after the 5th". A value of "1C" in the day-of-week field 209 * means "the first day included by the calendar on or after sunday". 210 * </p> 211 * 212 * <p> 213 * The legal characters and the names of months and days of the week are not 214 * case sensitive. 215 * </p> 216 * 217 * <p> 218 * Here are some full examples: <br><table cellspacing="8"> 219 * <tr> 220 * <th align="left">Expression</th> 221 * <th align="left"> </th> 222 * <th align="left">Meaning</th> 223 * </tr> 224 * <tr> 225 * <td align="left"><code>"0 0 12 * * ?"</code></td> 226 * <td align="left"> </td> 227 * <td align="left"><code>Fire at 12pm (noon) every day</code></td> 228 * </tr> 229 * <tr> 230 * <td align="left"><code>"0 15 10 ? * *"</code></td> 231 * <td align="left"> </td> 232 * <td align="left"><code>Fire at 10:15am every day</code></td> 233 * </tr> 234 * <tr> 235 * <td align="left"><code>"0 15 10 * * ?"</code></td> 236 * <td align="left"> </td> 237 * <td align="left"><code>Fire at 10:15am every day</code></td> 238 * </tr> 239 * <tr> 240 * <td align="left"><code>"0 15 10 * * ? *"</code></td> 241 * <td align="left"> </td> 242 * <td align="left"><code>Fire at 10:15am every day</code></td> 243 * </tr> 244 * <tr> 245 * <td align="left"><code>"0 15 10 * * ? 2005"</code></td> 246 * <td align="left"> </td> 247 * <td align="left"><code>Fire at 10:15am every day during the year 2005</code> 248 * </td> 249 * </tr> 250 * <tr> 251 * <td align="left"><code>"0 * 14 * * ?"</code></td> 252 * <td align="left"> </td> 253 * <td align="left"><code>Fire every minute starting at 2pm and ending at 2:59pm, every day</code> 254 * </td> 255 * </tr> 256 * <tr> 257 * <td align="left"><code>"0 0/5 14 * * ?"</code></td> 258 * <td align="left"> </td> 259 * <td align="left"><code>Fire every 5 minutes starting at 2pm and ending at 2:55pm, every day</code> 260 * </td> 261 * </tr> 262 * <tr> 263 * <td align="left"><code>"0 0/5 14,18 * * ?"</code></td> 264 * <td align="left"> </td> 265 * <td align="left"><code>Fire every 5 minutes starting at 2pm and ending at 2:55pm, AND fire every 5 minutes starting at 6pm and ending at 6:55pm, every day</code> 266 * </td> 267 * </tr> 268 * <tr> 269 * <td align="left"><code>"0 0-5 14 * * ?"</code></td> 270 * <td align="left"> </td> 271 * <td align="left"><code>Fire every minute starting at 2pm and ending at 2:05pm, every day</code> 272 * </td> 273 * </tr> 274 * <tr> 275 * <td align="left"><code>"0 10,44 14 ? 3 WED"</code></td> 276 * <td align="left"> </td> 277 * <td align="left"><code>Fire at 2:10pm and at 2:44pm every Wednesday in the month of March.</code> 278 * </td> 279 * </tr> 280 * <tr> 281 * <td align="left"><code>"0 15 10 ? * MON-FRI"</code></td> 282 * <td align="left"> </td> 283 * <td align="left"><code>Fire at 10:15am every Monday, Tuesday, Wednesday, Thursday and Friday</code> 284 * </td> 285 * </tr> 286 * <tr> 287 * <td align="left"><code>"0 15 10 15 * ?"</code></td> 288 * <td align="left"> </td> 289 * <td align="left"><code>Fire at 10:15am on the 15th day of every month</code> 290 * </td> 291 * </tr> 292 * <tr> 293 * <td align="left"><code>"0 15 10 L * ?"</code></td> 294 * <td align="left"> </td> 295 * <td align="left"><code>Fire at 10:15am on the last day of every month</code> 296 * </td> 297 * </tr> 298 * <tr> 299 * <td align="left"><code>"0 15 10 ? * 6L"</code></td> 300 * <td align="left"> </td> 301 * <td align="left"><code>Fire at 10:15am on the last Friday of every month</code> 302 * </td> 303 * </tr> 304 * <tr> 305 * <td align="left"><code>"0 15 10 ? * 6L"</code></td> 306 * <td align="left"> </td> 307 * <td align="left"><code>Fire at 10:15am on the last Friday of every month</code> 308 * </td> 309 * </tr> 310 * <tr> 311 * <td align="left"><code>"0 15 10 ? * 6L 2002-2005"</code></td> 312 * <td align="left"> </td> 313 * <td align="left"><code>Fire at 10:15am on every last friday of every month during the years 2002, 2003, 2004 and 2005</code> 314 * </td> 315 * </tr> 316 * <tr> 317 * <td align="left"><code>"0 15 10 ? * 6#3"</code></td> 318 * <td align="left"> </td> 319 * <td align="left"><code>Fire at 10:15am on the third Friday of every month</code> 320 * </td> 321 * </tr> 322 * </table> 323 * </p> 324 * 325 * <p> 326 * Pay attention to the effects of '?' and '*' in the day-of-week and 327 * day-of-month fields! 328 * </p> 329 * 330 * <p> 331 * <b>NOTES:</b> 332 * <ul> 333 * <li>Support for the features described for the 'C' character is not 334 * complete.</li> 335 * <li>Support for specifying both a day-of-week and a day-of-month value is 336 * not complete (you'll need to use the '?' character in on of these fields). 337 * </li> 338 * <li>Be careful when setting fire times between mid-night and 1:00 AM - 339 * "daylight savings" can cause a skip or a repeat depending on whether the 340 * time moves back or jumps forward.</li> 341 * </ul> 342 * </p> 343 * 344 * 345 * @since 6.0.0 346 */ 347public class CmsScheduledJobInfo implements I_CmsConfigurationParameterHandler, Serializable { 348 349 /** The log object for this class. */ 350 private static final Log LOG = CmsLog.getLog(CmsScheduledJobInfo.class); 351 352 /** The serial version id. */ 353 private static final long serialVersionUID = 7621446065755519582L; 354 355 /** Indicates if this job is currently active in the scheduler or not. */ 356 private boolean m_active; 357 358 /** The name of the class to schedule. */ 359 private String m_className; 360 361 /** The context information for the user to execute the job with. */ 362 private CmsContextInfo m_context; 363 364 /** The cron expression for this scheduler job. */ 365 private String m_cronExpression; 366 367 /** Indicates if the configuration of this job is finalized (frozen). */ 368 private boolean m_frozen; 369 370 /** The id of this job. */ 371 private String m_id; 372 373 /** Instance object of the scheduled job (only required when instance is re-used). */ 374 private transient I_CmsScheduledJob m_jobInstance; 375 376 /** The name of the job (for information purposes). */ 377 private String m_jobName; 378 379 /** Stores the next execution time. */ 380 private Date m_nextFireTime; 381 382 /** The parameters used for this job entry. */ 383 private SortedMap<String, String> m_parameters; 384 385 /** Stores the last job execution time. */ 386 private Date m_previousFireTime; 387 388 /** Indicates if the job instance should be re-used if the job is run. */ 389 private boolean m_reuseInstance; 390 391 /** The (cron) trigger used for scheduling this job. */ 392 private Trigger m_trigger; 393 394 /** 395 * Default constructor.<p> 396 */ 397 public CmsScheduledJobInfo() { 398 399 m_reuseInstance = false; 400 m_frozen = false; 401 // parameters are stored in a tree map 402 m_parameters = new TreeMap<String, String>(); 403 // a job is active by default 404 m_active = true; 405 } 406 407 /** 408 * Constructor for creating a new job with all required parameters.<p> 409 * 410 * @param id the id of the job of <code>null</code> if a new id should be automatically generated 411 * @param jobName the display name of the job 412 * @param className the class name of the job, must be an instance of <code>{@link I_CmsScheduledJob}</code> 413 * @param context the OpenCms user context information to use when executing the job 414 * @param cronExpression the cron expression for scheduling the job 415 * @param reuseInstance indicates if the job class should be re-used 416 * @param active indicates if the job should be active in the scheduler 417 * @param parameters the job parameters 418 */ 419 public CmsScheduledJobInfo( 420 String id, 421 String jobName, 422 String className, 423 CmsContextInfo context, 424 String cronExpression, 425 boolean reuseInstance, 426 boolean active, 427 SortedMap<String, String> parameters) { 428 429 m_frozen = false; 430 setId(id); 431 if (CmsStringUtil.isNotEmpty(jobName)) { 432 // job name is optional, if not present class name will be used 433 setJobName(jobName); 434 } 435 setClassName(className); 436 setContextInfo(context); 437 setCronExpression(cronExpression); 438 setReuseInstance(reuseInstance); 439 setActive(active); 440 setParameters(parameters); 441 } 442 443 /** 444 * @see org.opencms.configuration.I_CmsConfigurationParameterHandler#addConfigurationParameter(java.lang.String, java.lang.String) 445 */ 446 public void addConfigurationParameter(String paramName, String paramValue) { 447 448 checkFrozen(); 449 // add the configured parameter 450 m_parameters.put(paramName, paramValue); 451 if (LOG.isDebugEnabled()) { 452 LOG.debug( 453 org.opencms.configuration.Messages.get().getBundle().key( 454 org.opencms.configuration.Messages.LOG_ADD_CONFIG_PARAM_3, 455 paramName, 456 paramValue, 457 this)); 458 } 459 } 460 461 /** 462 * Clears the id of the job.<p> 463 * 464 * This is useful if you want to create a copy of a job without keeping the job id. 465 * Use <code>{@link CmsScheduledJobInfo#clone()}</code> first to create the copy, 466 * and then clear the id of the clone.<p> 467 */ 468 public void clearId() { 469 470 setId(null); 471 } 472 473 /** 474 * Creates a clone of this scheduled job.<p> 475 * 476 * The clone will not be active in the scheduler until it is scheduled 477 * with <code>{@link CmsScheduleManager#scheduleJob(org.opencms.file.CmsObject, CmsScheduledJobInfo)}</code>. 478 * The job id returned by <code>{@link #getId()}</code> will be the same. 479 * The <code>{@link #isActive()}</code> flag will be set to false. 480 * The clones job instance class will be the same 481 * if the <code>{@link #isReuseInstance()}</code> flag is set.<p> 482 * 483 * @see java.lang.Object#clone() 484 */ 485 @Override 486 public CmsScheduledJobInfo clone() { 487 488 CmsScheduledJobInfo result = new CmsScheduledJobInfo(); 489 490 result.m_id = m_id; 491 result.m_active = false; 492 result.m_frozen = false; 493 result.m_className = m_className; 494 if (isReuseInstance()) { 495 result.m_jobInstance = m_jobInstance; 496 } 497 result.m_reuseInstance = m_reuseInstance; 498 result.m_context = (CmsContextInfo)m_context.clone(); 499 result.m_cronExpression = m_cronExpression; 500 result.m_jobName = m_jobName; 501 result.m_parameters = new TreeMap<String, String>(m_parameters); 502 result.m_trigger = null; 503 504 return result; 505 } 506 507 /** 508 * Returns the name of the class to schedule.<p> 509 * 510 * @return the name of the class to schedule 511 */ 512 public String getClassName() { 513 514 return m_className; 515 } 516 517 /** 518 * @see org.opencms.configuration.I_CmsConfigurationParameterHandler#getConfiguration() 519 */ 520 public CmsParameterConfiguration getConfiguration() { 521 522 // this configuration does not support parameters 523 if (LOG.isDebugEnabled()) { 524 LOG.debug( 525 org.opencms.configuration.Messages.get().getBundle().key( 526 org.opencms.configuration.Messages.LOG_GET_CONFIGURATION_1, 527 this)); 528 } 529 530 return new CmsParameterConfiguration(getParameters()); 531 } 532 533 /** 534 * Returns the context information for the user executing this job.<p> 535 * 536 * Please note: The context time returned by {@link org.opencms.file.CmsRequestContext#getRequestTime()} 537 * will be set to the time when this job was created. 538 * This can be relevant in case you want to perform VFS operations, because it will 539 * affect how resources are processed that have date released / date expired attributes set.<p> 540 * 541 * @return the context information for the user executing this job 542 */ 543 @Valid 544 public CmsContextInfo getContextInfo() { 545 546 return m_context; 547 } 548 549 /** 550 * Returns the cron expression for this job entry.<p> 551 * 552 * @return the cron expression for this job entry 553 */ 554 public String getCronExpression() { 555 556 return m_cronExpression; 557 } 558 559 /** 560 * Returns the next time at which this job will be executed, after the given time.<p> 561 * 562 * If this job will not be executed after the given time, <code>null</code> will be returned..<p> 563 * 564 * @param date the after which the next execution time should be calculated 565 * @return the next time at which this job will be executed, after the given time 566 */ 567 public Date getExecutionTimeAfter(Date date) { 568 569 if (!m_active || (m_trigger == null)) { 570 // if the job is not active, no time can be calculated 571 return null; 572 } 573 574 return m_trigger.getFireTimeAfter(date); 575 } 576 577 /** 578 * Returns the next time at which this job will be executed.<p> 579 * 580 * If the job will not execute again, <code>null</code> will be returned.<p> 581 * 582 * @return the next time at which this job will be executed 583 */ 584 public Date getExecutionTimeNext() { 585 586 if (!m_active || (m_trigger == null)) { 587 // if the job is not active, no time can be calculated 588 return null; 589 } 590 if (m_nextFireTime == null) { 591 // in case next time is not set, check if the trigger supplies a valid next fire time 592 Date next = m_trigger.getNextFireTime(); 593 if (System.currentTimeMillis() < next.getTime()) { 594 m_nextFireTime = next; 595 } 596 } 597 return m_nextFireTime; 598 } 599 600 /** 601 * Returns the previous time at which this job will be executed.<p> 602 * 603 * If this job has not yet been executed, <code>null</code> will be returned. 604 * 605 * @return the previous time at which this job will be executed 606 */ 607 public Date getExecutionTimePrevious() { 608 609 if (!m_active || (m_trigger == null)) { 610 // if the job is not active, no time can be calculated 611 return null; 612 } 613 614 return m_previousFireTime; 615 } 616 617 /** 618 * Returns the internal id of this job in the scheduler.<p> 619 * 620 * Can be used to remove this job from the scheduler with 621 * <code>{@link CmsScheduleManager#unscheduleJob(org.opencms.file.CmsObject, String)}</code>.<p> 622 * 623 * @return the internal id of this job in the scheduler 624 */ 625 public String getId() { 626 627 return m_id; 628 } 629 630 /** 631 * Returns an instance of the configured job class.<p> 632 * 633 * If any error occurs during class invocaion, the error 634 * is written to the OpenCms log and <code>null</code> is returned.<p> 635 * 636 * @return an instance of the configured job class, or null if an error occurred 637 */ 638 public synchronized I_CmsScheduledJob getJobInstance() { 639 640 if (m_jobInstance != null) { 641 642 if (LOG.isDebugEnabled()) { 643 LOG.debug( 644 Messages.get().getBundle().key( 645 Messages.LOG_REUSING_INSTANCE_1, 646 m_jobInstance.getClass().getName())); 647 } 648 649 // job instance already initialized 650 return m_jobInstance; 651 } 652 653 I_CmsScheduledJob job = null; 654 655 try { 656 // create an instance of the OpenCms job class 657 job = (I_CmsScheduledJob)Class.forName(getClassName()).newInstance(); 658 } catch (ClassNotFoundException e) { 659 LOG.error(Messages.get().getBundle().key(Messages.LOG_CLASS_NOT_FOUND_1, getClassName()), e); 660 } catch (IllegalAccessException e) { 661 LOG.error(Messages.get().getBundle().key(Messages.LOG_ILLEGAL_ACCESS_0), e); 662 } catch (InstantiationException e) { 663 LOG.error(Messages.get().getBundle().key(Messages.LOG_INSTANCE_GENERATION_0), e); 664 } catch (ClassCastException e) { 665 LOG.error(Messages.get().getBundle().key(Messages.LOG_BAD_INTERFACE_0), e); 666 } 667 668 if (m_reuseInstance) { 669 // job instance must be re-used 670 m_jobInstance = job; 671 } 672 673 if (LOG.isDebugEnabled()) { 674 LOG.debug(Messages.get().getBundle().key(Messages.LOG_JOB_CREATED_1, getClassName())); 675 } 676 677 // this should not flood the log files: if class name is wrong or jar files missing this will 678 // most likely persist until restart. 679 if (job == null) { 680 setActive(false); 681 } 682 return job; 683 } 684 685 /** 686 * Returns the job name.<p> 687 * 688 * @return the job name 689 */ 690 public String getJobName() { 691 692 return m_jobName; 693 } 694 695 /** 696 * Returns the parameters.<p> 697 * 698 * @return the parameters 699 */ 700 public SortedMap<String, String> getParameters() { 701 702 return m_parameters; 703 } 704 705 /** 706 * Finalizes (freezes) the configuration of this scheduler job entry.<p> 707 * 708 * After this job entry has been frozen, any attempt to change the 709 * configuration of this entry with one of the "set..." methods 710 * will lead to a <code>RuntimeException</code>.<p> 711 * 712 * @see org.opencms.configuration.I_CmsConfigurationParameterHandler#initConfiguration() 713 */ 714 public void initConfiguration() { 715 716 // simple default configuration does not need to be initialized 717 if (LOG.isDebugEnabled()) { 718 LOG.debug( 719 org.opencms.configuration.Messages.get().getBundle().key( 720 org.opencms.configuration.Messages.LOG_INIT_CONFIGURATION_1, 721 this)); 722 } 723 setFrozen(true); 724 } 725 726 /** 727 * Returns <code>true</code> if this job is currently active in the scheduler.<p> 728 * 729 * @return <code>true</code> if this job is currently active in the scheduler 730 */ 731 public boolean isActive() { 732 733 return m_active; 734 } 735 736 /** 737 * Returns true if the job instance class is reused for this job.<p> 738 * 739 * @return true if the job instance class is reused for this job 740 */ 741 public boolean isReuseInstance() { 742 743 return m_reuseInstance; 744 } 745 746 /** 747 * Sets the active state of this job.<p> 748 * 749 * @param active the active state to set 750 */ 751 public void setActive(boolean active) { 752 753 checkFrozen(); 754 m_active = active; 755 } 756 757 /** 758 * Sets the name of the class to schedule.<p> 759 * 760 * @param className the class name to set 761 */ 762 public void setClassName(String className) { 763 764 checkFrozen(); 765 if (className != null) { 766 // remove leading or trailing white space 767 className = className.trim(); 768 } 769 if (!CmsStringUtil.isValidJavaClassName(className)) { 770 CmsMessageContainer message = Messages.get().container(Messages.ERR_BAD_JOB_CLASS_NAME_1, className); 771 if (OpenCms.getRunLevel() > OpenCms.RUNLEVEL_2_INITIALIZING) { 772 throw new CmsIllegalArgumentException(message); 773 } else { 774 LOG.warn(message.key()); 775 } 776 } else { 777 Class<?> jobClass; 778 try { 779 jobClass = Class.forName(className); 780 if (!I_CmsScheduledJob.class.isAssignableFrom(jobClass)) { 781 CmsMessageContainer message = Messages.get().container( 782 Messages.ERR_JOB_CLASS_BAD_INTERFACE_2, 783 className, 784 I_CmsScheduledJob.class.getName()); 785 786 if (OpenCms.getRunLevel() > OpenCms.RUNLEVEL_2_INITIALIZING) { 787 throw new CmsIllegalArgumentException(message); 788 } else { 789 LOG.warn(message.key()); 790 } 791 792 } 793 } catch (ClassNotFoundException e) { 794 CmsMessageContainer message = Messages.get().container(Messages.ERR_JOB_CLASS_NOT_FOUND_1, className); 795 if (OpenCms.getRunLevel() > OpenCms.RUNLEVEL_2_INITIALIZING) { 796 throw new CmsIllegalArgumentException(message); 797 } else { 798 LOG.warn(message.key()); 799 } 800 801 } 802 } 803 m_className = className; 804 if (getJobName() == null) { 805 // initialize job name with class name as default 806 setJobName(className); 807 } 808 809 } 810 811 /** 812 * Sets the context information for the user executing the job.<p> 813 * 814 * This will also "freeze" the context information that is set.<p> 815 * 816 * @param contextInfo the context information for the user executing the job 817 * 818 * @see CmsContextInfo#freeze() 819 */ 820 public void setContextInfo(CmsContextInfo contextInfo) { 821 822 checkFrozen(); 823 if (contextInfo == null) { 824 throw new CmsIllegalArgumentException(Messages.get().container(Messages.ERR_BAD_CONTEXT_INFO_0)); 825 } 826 m_context = contextInfo; 827 } 828 829 /** 830 * Sets the cron expression for this job entry.<p> 831 * 832 * @param cronExpression the cron expression to set 833 */ 834 @SuppressWarnings("unused") 835 public void setCronExpression(String cronExpression) { 836 837 checkFrozen(); 838 839 try { 840 // check if the cron expression is valid 841 new CronExpression(cronExpression); 842 } catch (Exception e) { 843 throw new CmsIllegalArgumentException( 844 Messages.get().container(Messages.ERR_BAD_CRON_EXPRESSION_2, getJobName(), cronExpression)); 845 } 846 847 m_cronExpression = cronExpression; 848 } 849 850 /** 851 * Sets the job name.<p> 852 * 853 * @param jobName the job name to set 854 */ 855 public void setJobName(String jobName) { 856 857 checkFrozen(); 858 if (CmsStringUtil.isEmpty(jobName) || !jobName.trim().equals(jobName)) { 859 throw new CmsIllegalArgumentException(Messages.get().container(Messages.ERR_BAD_JOB_NAME_1, jobName)); 860 861 } 862 m_jobName = jobName; 863 } 864 865 /** 866 * Sets the job parameters.<p> 867 * 868 * @param parameters the parameters to set 869 */ 870 public void setParameters(SortedMap<String, String> parameters) { 871 872 checkFrozen(); 873 if (parameters == null) { 874 throw new CmsIllegalArgumentException(Messages.get().container(Messages.ERR_BAD_JOB_PARAMS_0)); 875 } 876 // make sure the parameters are a sorted map 877 m_parameters = new TreeMap<String, String>(parameters); 878 } 879 880 /** 881 * Controls if the job instance class is reused for this job, 882 * of if a new instance is generated every time the job is run.<p> 883 * 884 * @param reuseInstance must be true if the job instance class is to be reused 885 */ 886 public void setReuseInstance(boolean reuseInstance) { 887 888 checkFrozen(); 889 m_reuseInstance = reuseInstance; 890 } 891 892 /** 893 * Checks if this job info configuration is frozen.<p> 894 * 895 * @throws CmsRuntimeException in case the configuration is already frozen 896 */ 897 protected void checkFrozen() throws CmsRuntimeException { 898 899 if (m_frozen) { 900 throw new CmsRuntimeException(Messages.get().container(Messages.ERR_JOB_INFO_FROZEN_1, getJobName())); 901 } 902 } 903 904 /** 905 * Returns the Quartz trigger used for scheduling this job.<p> 906 * 907 * This is an internal operation that should only by performed by the 908 * <code>{@link CmsScheduleManager}</code>, never by using this API directly.<p> 909 * 910 * @return the Quartz trigger used for scheduling this job 911 */ 912 protected Trigger getTrigger() { 913 914 return m_trigger; 915 } 916 917 /** 918 * Sets the "frozen" state of this job.<p> 919 * 920 * This is an internal operation to be used only by the <code>{@link CmsScheduleManager}</code>.<p> 921 * 922 * @param frozen the "frozen" state to set 923 */ 924 protected synchronized void setFrozen(boolean frozen) { 925 926 if (frozen && !m_frozen) { 927 // "freeze" the job configuration 928 m_parameters = Collections.unmodifiableSortedMap(m_parameters); 929 m_context.freeze(); 930 m_frozen = true; 931 } else if (!frozen && m_frozen) { 932 // "unfreeze" the job configuration 933 m_parameters = new TreeMap<String, String>(m_parameters); 934 m_frozen = false; 935 } 936 } 937 938 /** 939 * Sets the is used for scheduling this job.<p> 940 * 941 * This is an internal operation that should only by performed by the 942 * <code>{@link CmsScheduleManager}</code>, never by using this API directly.<p> 943 * 944 * @param id the id to set 945 */ 946 protected void setId(String id) { 947 948 checkFrozen(); 949 m_id = id; 950 } 951 952 /** 953 * Sets the next execution time.<p> 954 * 955 * @param nextFire the next execution time 956 */ 957 protected void setNextFireTime(Date nextFire) { 958 959 m_nextFireTime = nextFire; 960 } 961 962 /** 963 * Sets the previous execution time.<p> 964 * 965 * @param fireTime the previous execution time 966 */ 967 protected void setPreviousFireTime(Date fireTime) { 968 969 m_previousFireTime = fireTime; 970 } 971 972 /** 973 * Sets the Quartz trigger used for scheduling this job.<p> 974 * 975 * This is an internal operation that should only by performed by the 976 * <code>{@link CmsScheduleManager}</code>, never by using this API directly.<p> 977 * 978 * @param trigger the Quartz trigger to set 979 */ 980 protected void setTrigger(Trigger trigger) { 981 982 checkFrozen(); 983 m_trigger = trigger; 984 } 985 986 /** 987 * Updates the request time in the internal context information of the user with the current system time.<p> 988 * 989 * This is required before executing the job, otherwise the context information request time would be the time 990 * the context object was initially created.<p> 991 */ 992 protected void updateContextRequestTime() { 993 994 CmsContextInfo context = (CmsContextInfo)m_context.clone(); 995 context.setRequestTime(System.currentTimeMillis()); 996 context.freeze(); 997 m_context = context; 998 } 999}