1 4 package org.oddjob.quartz; 5 6 import java.io.Serializable ; 7 import java.util.Date ; 8 import java.util.HashMap ; 9 import java.util.Map ; 10 import java.util.TimeZone ; 11 12 import org.oddjob.OddjobException; 13 import org.oddjob.Resetable; 14 import org.oddjob.Stateful; 15 import org.oddjob.arooa.registry.ComponentRegistry; 16 import org.oddjob.framework.BasePrimary; 17 import org.oddjob.images.IconHelper; 18 import org.oddjob.schedules.Interval; 19 import org.oddjob.schedules.Schedule; 20 import org.oddjob.schedules.ScheduleCalculator; 21 import org.oddjob.schedules.ScheduleListener; 22 import org.oddjob.state.AbstractJobStateListener; 23 import org.oddjob.state.JobState; 24 import org.oddjob.state.JobStateEvent; 25 import org.oddjob.util.Clock; 26 import org.oddjob.util.DefaultClock; 27 import org.oddjob.util.OddjobConfigException; 28 import org.oddjob.util.OddjobConstantException; 29 import org.quartz.JobDataMap; 30 import org.quartz.JobDetail; 31 import org.quartz.Scheduler; 32 import org.quartz.SchedulerException; 33 import org.quartz.SimpleTrigger; 34 35 62 public class OddjobSchedule extends BasePrimary 63 implements Serializable , Stateful, QuartzSchedule, ScheduleListener { 64 private static final long serialVersionUID = 20051121; 65 66 67 private static final String GROUP_NAME = Scheduler.DEFAULT_GROUP; 68 69 72 private JobToken jobToken; 73 74 private transient ComponentRegistry componentRegistry; 75 76 79 private Schedule normalSchedule; 80 81 86 private Schedule retrySchedule; 87 88 93 private TimeZone timeZone; 94 95 101 private transient Date nextDue; 102 103 113 private Date scheduleDate; 114 115 123 private Interval lastComplete; 124 125 126 private transient boolean retry; 127 128 129 private transient Execute execute; 130 131 132 private String id; 133 134 private Map contextData = new HashMap (); 135 136 141 private transient Clock clock; 142 143 149 public void setId(String id) { 150 if (this.id != null) { 151 throw new OddjobConfigException("Id can't be changed."); 152 } 153 this.id = id; 154 } 155 156 161 private String triggerName() { 162 return id + "-Trigger"; 163 } 164 165 public void setComponentRegistry(ComponentRegistry componentRegistry) { 166 this.componentRegistry = componentRegistry; 167 } 168 169 174 public void setJob(Runnable job) { 175 if (job == null) { 176 throw new OddjobConfigException("No job to schedule specified!"); 177 } 178 if (jobToken != null) { 179 throw new OddjobConstantException("[" + this 180 + "] job can't be changed."); 181 } 182 jobToken = JobToken.create(componentRegistry, job); 183 } 184 185 190 public void setSchedule(Schedule schedule) { 191 this.normalSchedule = schedule; 192 } 193 194 199 public void setRetry(Schedule retry) { 200 this.retrySchedule = retry; 201 } 202 203 204 private transient Scheduler scheduler; 205 206 208 private transient ScheduleCalculator scheduleCalculator; 209 210 213 public void scheduleWith(Scheduler scheduler) throws SchedulerException { 214 if (jobToken == null) { 215 throw new OddjobConfigException("A job is required."); 216 } 217 218 this.execute = new Execute(); 219 this.scheduler = scheduler; 220 221 scheduleCalculator = new ScheduleCalculator(getClock(), 223 normalSchedule, retrySchedule, timeZone); 224 scheduleCalculator.addScheduleListener(this); 225 226 scheduleCalculator.initialise(lastComplete, contextData); 227 } 228 229 232 public void initialised(Date scheduleDate) { 233 this.scheduleDate = scheduleDate; 234 this.retry = false; 235 setNextDue(scheduleDate); 236 setJobStateReady(); 237 } 238 239 242 public void complete(Date scheduleDate, Interval lastComplete) { 243 this.scheduleDate = scheduleDate; 244 this.lastComplete = lastComplete; 245 this.retry = false; 246 setNextDue(scheduleDate); 247 JobStateEvent jse = new JobStateEvent(this, JobState.COMPLETE, 248 lastComplete.getFromDate(), null); 249 stateHandler.fireEvent(jse); 250 iconHelper.changeIcon(IconHelper.COMPLETE); 251 } 252 253 256 public void retry(Date scheduleDate, Date retryDate) { 257 this.scheduleDate = scheduleDate; 258 this.retry = true; 259 setNextDue(retryDate); 260 setJobStateNotComplete(); 261 } 262 263 266 public void failed(Date scheduleDate) { 267 this.scheduleDate = scheduleDate; 268 this.retry = false; 269 setNextDue(scheduleDate); 270 setJobStateException(new OddjobException("Schedule failed.")); 271 } 272 273 278 public Clock getClock() { 279 if (clock == null) { 280 clock = new DefaultClock(); 281 } 282 return clock; 283 } 284 285 290 public void setClock(Clock clock) { 291 this.clock = clock; 292 } 293 294 299 public Date getNextDue() { 300 return nextDue; 301 } 302 303 308 public void setNextDue(Date nextDue) { 309 310 logger().debug("Setting next due to : " + nextDue); 311 Date oldNextDue = this.nextDue; 312 this.nextDue = nextDue; 313 changes.firePropertyChange("nextDue", oldNextDue, nextDue); 314 315 if (nextDue == null) { 316 logger().debug("Schedule finished."); 317 return; 318 } 319 320 SimpleTrigger trigger = new SimpleTrigger(triggerName(), 321 GROUP_NAME, 322 nextDue, 323 null, 324 0, 325 0L); 326 327 try { 328 JobDetail jobDetail = scheduler.getJobDetail(toString(), Scheduler.DEFAULT_GROUP); 329 if (jobDetail == null) { 331 332 OddjobData ojd = new OddjobData(); 333 ojd.setJob(execute); 334 335 JobDataMap jobDataMap = new JobDataMap(); 336 337 jobDataMap.put(OddjobData.ODDJOB_DATA, ojd); 338 339 jobDetail = new JobDetail(toString(), 340 GROUP_NAME, 341 RunnableQuartzJob.class); 342 jobDetail.setJobDataMap(jobDataMap); 343 scheduler.scheduleJob(jobDetail, trigger); 344 } 345 else { 346 trigger.setJobName(jobDetail.getName()); 347 trigger.setJobGroup(jobDetail.getGroup()); 348 scheduler.rescheduleJob(trigger.getName(), jobDetail.getGroup(), trigger); 349 } 350 } catch (SchedulerException e) { 351 logger().debug("Failed to schedule (" + e.getMessage() + ")"); 352 } 353 } 354 355 359 public void unscheduleFrom(Scheduler scheduler) throws SchedulerException { 360 scheduler.unscheduleJob(triggerName(), GROUP_NAME); 361 } 362 363 368 public Interval getLastComplete() { 369 return lastComplete; 370 } 371 372 382 public void setReSchedule(Date reSchedule) { 383 logger().debug("Rescheduling [" + reSchedule + "]"); 384 Interval old = this.lastComplete; 385 this.lastComplete = new Interval(reSchedule, reSchedule); 386 387 scheduleCalculator.removeScheduleListener(this); 389 390 scheduleCalculator = new ScheduleCalculator(getClock(), 392 normalSchedule, retrySchedule, timeZone); 393 scheduleCalculator.addScheduleListener(this); 394 395 scheduleCalculator.initialise(this.lastComplete, contextData); 396 397 changes.firePropertyChange("lastComplete", old, reSchedule); 398 } 399 400 405 public Date getScheduleDate() { 406 return scheduleDate; 407 } 408 409 414 public void setScheduleDate(Date scheduleDate) { 415 this.scheduleDate = scheduleDate; 416 } 417 418 423 public String getTimeZone() { 424 if (timeZone == null) { 425 return null; 426 } 427 return timeZone.getID(); 428 } 429 430 435 public void setTimeZone(String timeZoneId) { 436 if (timeZoneId == null) { 437 this.timeZone = null; 438 } else { 439 this.timeZone = TimeZone.getTimeZone(timeZoneId); 440 } 441 } 442 443 445 class Execute implements Runnable { 446 synchronized public void run() { 447 logger().debug("Executing at [" + new Date ()+ "]"); 448 Runnable job = (Runnable ) JobToken.retrieve(componentRegistry, jobToken); 449 450 if (job == null) { 451 setJobStateException( 452 new NullPointerException ("Failed to find job for [" + jobToken + "]")); 453 } 454 455 if (job instanceof Resetable) { 456 if (retry) { 457 ((Resetable) job).softReset(); 458 } 459 else { 460 ((Resetable) job).hardReset(); 462 } 463 } 464 ScheduleStateListener ssl = new ScheduleStateListener(); 465 ((Stateful) job).addJobStateListener(ssl); 466 467 try { 468 job.run(); 469 } 470 catch (Throwable t) { 471 ssl.jobStateException((Stateful) job, new Date (), t); 472 } 473 } 474 } 475 476 class ScheduleStateListener extends AbstractJobStateListener { 477 478 protected void jobStateComplete(Stateful source, Date time) { 480 source.removeJobStateListener(this); 481 logger().debug("Job [" + source + "] Complete"); 482 scheduleCalculator.calculateComplete(); 483 } 484 485 protected void jobStateNotComplete(Stateful source, Date time) { 488 source.removeJobStateListener(this); 489 logger().debug("Job [" + source + "] Not Complete"); 490 scheduleCalculator.calculateRetry(); 491 } 492 493 495 protected void jobStateException(Stateful source, Date time, Throwable t) { 496 source.removeJobStateListener(this); 497 logger().debug("Job [" + source + "] Exception"); 498 scheduleCalculator.calculateRetry(); 499 } 500 501 protected void jobStateReady(Stateful source, Date time) { 503 setJobStateReady(); 504 } 505 506 protected void jobStateExecuting(Stateful source, Date time) { 508 iconHelper.changeIcon(IconHelper.EXECUTING); 509 stateHandler.requestJobStateExecuting(); 510 } 511 512 } 513 514 public String toString() { 515 if (getName() == null) { 516 return "OddjobSchedule [" + jobToken + "]"; 517 } 518 return getName(); 519 } 520 521 public void onDestroy() { 522 scheduleCalculator.removeScheduleListener(this); 523 scheduleCalculator = null; 524 } 525 } 526 | Popular Tags |