1
2
3
4
5
6
7
8 /
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68 public class CronSchedule
69 implements Schedule
70 {
71
72
73 /*** index of minute field. */
74 private static final int MINUTE = 0;
75
76 /*** index of hour field. */
77 private static final int HOUR_OF_DAY = 1;
78
79 /*** index of day field. */
80 private static final int DAY_OF_MONTH = 2;
81
82 /*** index of month field. */
83 private static final int MONTH = 3;
84
85 /*** index of weekday field. */
86 private static final int DAY_OF_WEEK = 4;
87
88 /*** The schedule type. */
89 public static final String TYPE = "cron";
90
91
92
93 /*** The textual configuration form. */
94 private String config;
95
96 /*** The parserd schedule data. */
97 private int[][] schedule;
98
99 /*** I18n component */
100 private I18n i18n;
101
102 /***
103 * Constructor.
104 *
105 * @param i18n the i18n component.
106 */
107 public CronSchedule(I18n i18n)
108 {
109 this.i18n = i18n;
110 }
111
112 /***
113 * {@inheritDoc}
114 */
115 public void init(AbstractScheduler scheduler, String config)
116 throws InvalidScheduleException
117 {
118 setConfig(config);
119 }
120
121 /***
122 * {@inheritDoc}
123 */
124 public String getType()
125 {
126 return TYPE;
127 }
128
129 /***
130 * {@inheritDoc}
131 */
132 public String getConfig()
133 {
134 return config;
135 }
136
137 /***
138 * {@inheritDoc}
139 */
140 public void setConfig(String config)
141 throws InvalidScheduleException
142 {
143
144 StringReader sr = new StringReader("");
145 CronParser parser = new CronParser(sr);
146 try
147 {
148 this.schedule = parser.parse(config);
149 this.config = config;
150 }
151 catch(ParseException e)
152 {
153 throw new InvalidScheduleException(localizeParseException(e), e);
154 }
155 catch(ValueOutOfRangeException e)
156 {
157 throw new InvalidScheduleException(localizeValueOutOrRangeException(e), e);
158 }
159
160 }
161
162 /***
163 * {@inheritDoc}
164 */
165 public boolean atStartup()
166 {
167 return (schedule.length == 0);
168 }
169
170 /***
171 * {@inheritDoc}
172 */
173 public Date getNextRunTime(Date currentTime, Date lastRunTime)
174 {
175 if(schedule.length == 0)
176 {
177 return null;
178 }
179 else
180 {
181 GregorianCalendar nextRun = new GregorianCalendar();
182 nextRun.setTime(currentTime);
183
184 nextRun.set(Calendar.MILLISECOND, 0);
185 nextRun.set(Calendar.SECOND, 0);
186 nextRun.add(Calendar.MINUTE, 1);
187
188 computeDay(nextRun);
189 computeHour(nextRun);
190 computeMinute(nextRun);
191
192 return nextRun.getTime();
193 }
194 }
195
196
197
198 private void computeMonth(Calendar nextRunDOM)
199 {
200 int i;
201 if(schedule[MONTH].length != 0)
202 {
203 loop: for(i=0; i<schedule[MONTH].length; i++)
204 {
205 if(schedule[MONTH][i] >= nextRunDOM.get(Calendar.MONTH))
206 {
207 break loop;
208 }
209 }
210 if(i == schedule[MONTH].length)
211 {
212 nextRunDOM.add(Calendar.YEAR, 1);
213 nextRunDOM.set(Calendar.MONTH, schedule[MONTH][0]);
214 nextRunDOM.set(Calendar.DAY_OF_MONTH, 0);
215 nextRunDOM.set(Calendar.HOUR_OF_DAY, 0);
216 nextRunDOM.set(Calendar.MINUTE, 0);
217 }
218 else
219 {
220 if(schedule[MONTH][i] > nextRunDOM.get(Calendar.MONTH))
221 {
222 nextRunDOM.set(Calendar.MONTH, schedule[MONTH][i]);
223 nextRunDOM.set(Calendar.DAY_OF_MONTH, 0);
224 nextRunDOM.set(Calendar.HOUR_OF_DAY, 0);
225 nextRunDOM.set(Calendar.MINUTE, 0);
226 }
227 }
228 }
229 }
230
231 private void computeDay(Calendar nextRun)
232 {
233 int i;
234 GregorianCalendar nextRunDOM = new GregorianCalendar();
235 nextRunDOM.setTime(nextRun.getTime());
236
237 computeMonth(nextRunDOM);
238
239
240 if(schedule[DAY_OF_MONTH].length != 0)
241 {
242 loop: for(i=0; i<schedule[DAY_OF_MONTH].length; i++)
243 {
244 if(schedule[DAY_OF_MONTH][i] >= nextRunDOM.get(Calendar.DAY_OF_MONTH))
245 {
246 break loop;
247 }
248 }
249 if(i == schedule[DAY_OF_MONTH].length)
250 {
251 nextRunDOM.add(Calendar.MONTH, 1);
252 computeMonth(nextRunDOM);
253
254 nextRunDOM.set(Calendar.DAY_OF_MONTH, schedule[DAY_OF_MONTH][0]);
255 nextRunDOM.set(Calendar.HOUR_OF_DAY, 0);
256 nextRunDOM.set(Calendar.MINUTE, 0);
257 }
258 else
259 {
260 if(schedule[DAY_OF_MONTH][i] > nextRunDOM.get(Calendar.DAY_OF_MONTH))
261 {
262 nextRunDOM.set(Calendar.DAY_OF_MONTH, schedule[DAY_OF_MONTH][i]);
263
264 nextRunDOM.set(Calendar.HOUR_OF_DAY, 0);
265 nextRunDOM.set(Calendar.MINUTE, 0);
266 }
267 }
268 }
269
270 GregorianCalendar nextRunDOW = new GregorianCalendar();
271 nextRunDOW.setTime(nextRun.getTime());
272
273 if(schedule[DAY_OF_WEEK].length != 0)
274 {
275 loop: for(i=0; i<schedule[DAY_OF_WEEK].length; i++)
276 {
277 if(schedule[DAY_OF_WEEK][i] >= nextRunDOW.get(Calendar.DAY_OF_WEEK))
278 {
279 break loop;
280 }
281 }
282 if(i == schedule[DAY_OF_WEEK].length)
283 {
284 nextRunDOW.add(Calendar.WEEK_OF_YEAR, 1);
285 nextRunDOW.set(Calendar.DAY_OF_WEEK, schedule[DAY_OF_WEEK][0]);
286 nextRunDOW.set(Calendar.HOUR_OF_DAY, 0);
287 nextRunDOW.set(Calendar.MINUTE, 0);
288 }
289 else
290 {
291 if(schedule[DAY_OF_WEEK][i] > nextRunDOW.get(Calendar.DAY_OF_WEEK))
292 {
293 if(nextRunDOW.get(Calendar.DAY_OF_WEEK) == Calendar.SUNDAY &&
294 nextRunDOW.getFirstDayOfWeek() != Calendar.SUNDAY)
295 {
296 nextRunDOW.add(Calendar.WEEK_OF_YEAR, 1);
297 }
298 nextRunDOW.set(Calendar.DAY_OF_WEEK, schedule[DAY_OF_WEEK][i]);
299 nextRunDOW.set(Calendar.HOUR_OF_DAY, 0);
300 nextRunDOW.set(Calendar.MINUTE, 0);
301 }
302 }
303 }
304
305 if(schedule[DAY_OF_WEEK].length == 0)
306 {
307 nextRun.setTime(nextRunDOM.getTime());
308 }
309 else
310 {
311 if(schedule[DAY_OF_MONTH].length == 0 && schedule[MONTH].length == 0)
312 {
313 nextRun.setTime(nextRunDOW.getTime());
314 }
315 else
316 {
317 if(nextRunDOM.getTime().compareTo(nextRunDOW.getTime()) < 0)
318 {
319 nextRun.setTime(nextRunDOM.getTime());
320 }
321 else
322 {
323 nextRun.setTime(nextRunDOW.getTime());
324 }
325 }
326 }
327 }
328
329 private void computeHour(Calendar nextRun)
330 {
331 int i;
332 if(schedule[HOUR_OF_DAY].length != 0)
333 {
334 loop: for(i=0; i<schedule[HOUR_OF_DAY].length; i++)
335 {
336 if(schedule[HOUR_OF_DAY][i] >= nextRun.get(Calendar.HOUR_OF_DAY))
337 {
338 break loop;
339 }
340 }
341 if(i == schedule[HOUR_OF_DAY].length)
342 {
343 nextRun.add(Calendar.DAY_OF_MONTH, 1);
344 computeDay(nextRun);
345 nextRun.set(Calendar.HOUR_OF_DAY, schedule[HOUR_OF_DAY][0]);
346 nextRun.set(Calendar.MINUTE, 0);
347 }
348 else
349 {
350 if(schedule[HOUR_OF_DAY][i] > nextRun.get(Calendar.HOUR_OF_DAY))
351 {
352 nextRun.set(Calendar.MINUTE, 0);
353 nextRun.set(Calendar.HOUR_OF_DAY, schedule[HOUR_OF_DAY][i]);
354 }
355 }
356 }
357 }
358
359 private void computeMinute(Calendar nextRun)
360 {
361 int i;
362 if(schedule[MINUTE].length != 0)
363 {
364 loop: for(i=0; i<schedule[MINUTE].length; i++)
365 {
366 if(schedule[MINUTE][i] >= nextRun.get(Calendar.MINUTE))
367 {
368 break loop;
369 }
370 }
371 if(i == schedule[MINUTE].length)
372 {
373 nextRun.add(Calendar.HOUR_OF_DAY, 1);
374 computeHour(nextRun);
375 nextRun.set(Calendar.MINUTE, schedule[MINUTE][0]);
376 }
377 else
378 {
379 if(schedule[MINUTE][i] > nextRun.get(Calendar.MINUTE))
380 {
381 nextRun.set(Calendar.MINUTE, schedule[MINUTE][i]);
382 }
383 }
384 }
385 }
386
387 /***
388 * The end of line string for this machine.
389 */
390 protected String eol = System.getProperty("line.separator", "\n");
391
392 /***
393 * Create a localized message out of ParseException object.
394 *
395 * @param ex the exception.
396 */
397 private String localizeParseException(ParseException ex)
398 {
399 String expected = "";
400 int maxSize = 0;
401 for (int i = 0; i < ex.expectedTokenSequences.length; i++)
402 {
403 if (maxSize < ex.expectedTokenSequences[i].length)
404 {
405 maxSize = ex.expectedTokenSequences[i].length;
406 }
407 for (int j = 0; j < ex.expectedTokenSequences[i].length; j++)
408 {
409 expected += ex.tokenImage[ex.expectedTokenSequences[i][j]] + " ";
410 }
411 if (ex.expectedTokenSequences[i][ex.expectedTokenSequences[i].length - 1] != 0)
412 {
413 expected += "...";
414 }
415 expected += eol + " ";
416 }
417
418 String encountered = "";
419 Token tok = ex.currentToken.next;
420 for (int i = 0; i < maxSize; i++)
421 {
422 if (i != 0)
423 {
424 encountered += " ";
425 }
426 if (tok.kind == 0)
427 {
428 encountered += ex.tokenImage[0];
429 break;
430 }
431 encountered += addEscapes(tok.image);
432 tok = tok.next;
433 }
434
435 String[] strings = new String[] {
436 encountered,
437 Integer.toString(ex.currentToken.next.beginColumn),
438 expected
439 };
440
441 String pattern;
442 if(ex.expectedTokenSequences.length == 1)
443 {
444 pattern = "ledge.scheduler.cron.parseOne";
445 }
446 else
447 {
448 pattern = "ledge.scheduler.cron.parseMany";
449 }
450 return i18n.get(i18n.getDefaultLocale(), pattern, strings);
451 }
452
453 /***
454 * Create a localized message out of ParseException object.
455 *
456 * @param ex the exception.
457 */
458 private String localizeValueOutOrRangeException(ValueOutOfRangeException ex)
459 {
460 String[] strings = new String[] {
461 Integer.toString(ex.token.beginColumn),
462 Integer.toString(ex.value),
463 Integer.toString(ex.min),
464 Integer.toString(ex.max)
465 };
466 String pattern = "ledge.scheduler.cron.outOfRange";
467 return i18n.get(i18n.getDefaultLocale(), pattern, strings);
468 }
469
470 /***
471 * Used to convert raw characters to their escaped version
472 * when these raw version cannot be used as part of an ASCII
473 * string literal.
474 *
475 * <p>This method was copied from JavaCC generated code.</p>
476 *
477 * @author JavaCC team
478 * @param str input string.
479 * @return string with escaped sepcial characters.
480 */
481 protected String addEscapes(String str)
482 {
483 StringBuilder retval = new StringBuilder();
484 char ch;
485 for (int i = 0; i < str.length(); i++)
486 {
487 switch (str.charAt(i))
488 {
489 case 0 :
490 continue;
491 case '\b':
492 retval.append("//b");
493 continue;
494 case '\t':
495 retval.append("//t");
496 continue;
497 case '\n':
498 retval.append("//n");
499 continue;
500 case '\f':
501 retval.append("//f");
502 continue;
503 case '\r':
504 retval.append("//r");
505 continue;
506 case '\"':
507 retval.append("//\"");
508 continue;
509 case '\'':
510 retval.append("//\'");
511 continue;
512 case '//':
513 retval.append("////");
514 continue;
515 default:
516 if ((ch = str.charAt(i)) < 0x20 || ch > 0x7e)
517 {
518 String s = "0000" + Integer.toString(ch, 16);
519 retval.append("//u" + s.substring(s.length() - 4, s.length()));
520 }
521 else
522 {
523 retval.append(ch);
524 }
525 continue;
526 }
527 }
528 return retval.toString();
529 }
530 }