1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 package org.fosstrak.epcis.repository.query;
22
23 import static java.util.Calendar.DAY_OF_MONTH;
24 import static java.util.Calendar.DAY_OF_WEEK;
25 import static java.util.Calendar.HOUR_OF_DAY;
26 import static java.util.Calendar.MINUTE;
27 import static java.util.Calendar.MONTH;
28 import static java.util.Calendar.SECOND;
29 import static java.util.Calendar.YEAR;
30
31 import java.io.Serializable;
32 import java.util.GregorianCalendar;
33 import java.util.NoSuchElementException;
34 import java.util.TreeSet;
35
36 import org.fosstrak.epcis.model.ImplementationException;
37 import org.fosstrak.epcis.model.QuerySchedule;
38 import org.fosstrak.epcis.model.SubscriptionControlsException;
39 import org.fosstrak.epcis.soap.ImplementationExceptionResponse;
40 import org.fosstrak.epcis.soap.SubscriptionControlsExceptionResponse;
41 import org.apache.commons.logging.Log;
42 import org.apache.commons.logging.LogFactory;
43
44
45
46
47
48
49
50
51
52 public class Schedule implements Serializable {
53
54 private static final Log LOG = LogFactory.getLog(Schedule.class);
55
56
57
58
59 private static final long serialVersionUID = -2930237937444822557L;
60
61
62
63
64 private TreeSet<Integer> seconds = new TreeSet<Integer>();
65 private TreeSet<Integer> minutes = new TreeSet<Integer>();
66 private TreeSet<Integer> hours = new TreeSet<Integer>();
67 private TreeSet<Integer> daysOfMonth = new TreeSet<Integer>();
68
69 private TreeSet<Integer> months = new TreeSet<Integer>();
70 private TreeSet<Integer> daysOfWeek = new TreeSet<Integer>();
71
72
73
74
75 Schedule() {
76 }
77
78
79
80
81
82
83
84
85
86
87
88 public Schedule(final QuerySchedule schedule) throws SubscriptionControlsExceptionResponse {
89
90
91 if ("1".equals(schedule.getMinute()) && schedule.getSecond() == null && schedule.getHour() == null
92 && schedule.getDayOfMonth() == null && schedule.getMonth() == null && schedule.getDayOfWeek() == null) {
93 throw new SubscriptionControlsExceptionResponse("Invalid query schedule: schedule is set to every second");
94 }
95
96
97 if (schedule.getSecond() == null) {
98 schedule.setSecond("");
99 }
100 if (schedule.getMinute() == null) {
101 schedule.setMinute("");
102 }
103 if (schedule.getHour() == null) {
104 schedule.setHour("");
105 }
106 if (schedule.getDayOfMonth() == null) {
107 schedule.setDayOfMonth("");
108 }
109 if (schedule.getMonth() == null) {
110 schedule.setMonth("");
111 }
112 if (schedule.getDayOfWeek() == null) {
113 schedule.setDayOfWeek("");
114 }
115
116
117 String[] second = schedule.getSecond().split(",");
118 String[] minute = schedule.getMinute().split(",");
119 String[] hour = schedule.getHour().split(",");
120 String[] dayOfMonth = schedule.getDayOfMonth().split(",");
121 String[] month = schedule.getMonth().split(",");
122 String[] dayOfWeek = schedule.getDayOfWeek().split(",");
123
124
125 handleValues(second, "second", 0, 59);
126 handleValues(minute, "minute", 0, 59);
127 handleValues(hour, "hour", 0, 23);
128 handleValues(dayOfMonth, "dayOfMonth", 1, 31);
129
130
131 handleValues(month, "month", 1, 12);
132 handleValues(dayOfWeek, "dayOfWeek", 1, 7);
133
134
135 if (!months.isEmpty()
136 && (months.first() == months.last() && months.first().intValue() == 1 && (daysOfMonth.first().intValue() == 30 || daysOfMonth.first().intValue() == 31))) {
137 throw new SubscriptionControlsExceptionResponse(
138 "Invalid query schedule: impossible month/dayOfMonth combination, e.g. February 30.");
139 }
140 if (!months.isEmpty()
141 && daysOfMonth.first().intValue() == 31
142 && !months.contains(Integer.valueOf(0))
143
144 && !months.contains(Integer.valueOf(2)) && !months.contains(Integer.valueOf(4))
145 && !months.contains(Integer.valueOf(6)) && !months.contains(Integer.valueOf(7))
146 && !months.contains(Integer.valueOf(9)) && !months.contains(Integer.valueOf(11))) {
147 throw new SubscriptionControlsExceptionResponse(
148 "Invalid query schedule: impossible month/dayOfMonth combination, e.g. April 31.");
149 }
150 }
151
152
153
154
155
156
157
158
159 public GregorianCalendar nextScheduledTime() throws ImplementationExceptionResponse {
160 GregorianCalendar cal = new GregorianCalendar();
161
162 cal.add(SECOND, 1);
163 return nextScheduledTime(cal);
164 }
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181 public GregorianCalendar nextScheduledTime(final GregorianCalendar time) throws ImplementationExceptionResponse {
182 GregorianCalendar nextSchedule = (GregorianCalendar) time.clone();
183
184 while (!monthMadeValid(nextSchedule)) {
185 nextSchedule.roll(YEAR, true);
186 setFieldsToMinimum(nextSchedule, MONTH);
187
188 }
189 return nextSchedule;
190 }
191
192
193
194
195
196
197
198
199
200
201
202 private boolean monthMadeValid(final GregorianCalendar nextSchedule) throws ImplementationExceptionResponse {
203
204
205
206 while (!months.isEmpty() && !months.contains(Integer.valueOf(nextSchedule.get(MONTH)))) {
207
208
209 if (!setFieldToNextValidRoll(nextSchedule, MONTH, DAY_OF_MONTH)) {
210 return false;
211 }
212 }
213
214
215 while (!dayMadeValid(nextSchedule)) {
216
217 if (!setFieldToNextValidRoll(nextSchedule, MONTH, DAY_OF_MONTH)) {
218 return false;
219 }
220
221 if (!setFieldsToMinimum(nextSchedule, DAY_OF_MONTH)) {
222 return false;
223 }
224 }
225 return true;
226 }
227
228
229
230
231
232
233
234
235
236
237
238 private boolean dayMadeValid(final GregorianCalendar nextSchedule) throws ImplementationExceptionResponse {
239 if (!daysOfMonth.contains(Integer.valueOf(nextSchedule.get(DAY_OF_MONTH))) && !daysOfMonth.isEmpty()) {
240 if (!setFieldToNextValidRoll(nextSchedule, DAY_OF_MONTH, HOUR_OF_DAY)) {
241 return false;
242 }
243 }
244
245
246 while (!daysOfWeek.contains(Integer.valueOf(nextSchedule.get(DAY_OF_WEEK))) && !daysOfWeek.isEmpty()) {
247 if (!setFieldToNextValidRoll(nextSchedule, DAY_OF_MONTH, HOUR_OF_DAY)) {
248 return false;
249 } else if (!daysOfWeek.contains(Integer.valueOf(nextSchedule.get(DAY_OF_WEEK)))) {
250 dayMadeValid(nextSchedule);
251 }
252 }
253
254
255
256 while (!hourMadeValid(nextSchedule)) {
257
258 if (!setFieldToNextValidRoll(nextSchedule, DAY_OF_MONTH, HOUR_OF_DAY)) {
259 return false;
260 }
261
262 if (!setFieldsToMinimum(nextSchedule, HOUR_OF_DAY)) {
263 return false;
264 }
265 }
266 return true;
267 }
268
269
270
271
272
273
274
275
276
277
278
279 private boolean hourMadeValid(final GregorianCalendar nextSchedule) throws ImplementationExceptionResponse {
280 if (!hours.contains(Integer.valueOf(nextSchedule.get(HOUR_OF_DAY))) && !hours.isEmpty()) {
281 if (!setFieldToNextValidRoll(nextSchedule, HOUR_OF_DAY, MINUTE)) {
282 return false;
283 }
284 }
285
286
287
288 while (!minuteMadeValid(nextSchedule)) {
289
290 if (!setFieldToNextValidRoll(nextSchedule, HOUR_OF_DAY, MINUTE)) {
291 return false;
292 }
293
294 if (!setFieldsToMinimum(nextSchedule, MINUTE)) {
295 return false;
296 }
297 }
298 return true;
299 }
300
301
302
303
304
305
306
307
308
309
310
311
312 private boolean minuteMadeValid(final GregorianCalendar nextSchedule) throws ImplementationExceptionResponse {
313 if (!minutes.contains(Integer.valueOf(nextSchedule.get(MINUTE))) && !minutes.isEmpty()) {
314
315 if (!setFieldToNextValidRoll(nextSchedule, MINUTE, SECOND)) {
316 return false;
317 }
318 }
319
320
321
322 while (!secondMadeValid(nextSchedule)) {
323
324
325 if (!setFieldToNextValidRoll(nextSchedule, MINUTE, SECOND)) {
326 return false;
327 }
328
329 if (!setFieldToMinimum(nextSchedule, SECOND)) {
330 return false;
331 }
332 }
333 return true;
334 }
335
336
337
338
339
340
341
342
343
344
345
346 private boolean secondMadeValid(final GregorianCalendar nextSchedule) throws ImplementationExceptionResponse {
347
348
349 if (!seconds.isEmpty() && !seconds.contains(Integer.valueOf(nextSchedule.get(SECOND)))) {
350
351
352 return setToNextScheduledValue(nextSchedule, SECOND);
353 }
354 return true;
355 }
356
357
358
359
360
361
362
363
364
365
366
367
368
369 private boolean setToNextScheduledValue(final GregorianCalendar cal, final int field)
370 throws ImplementationExceptionResponse {
371 int next;
372 TreeSet<Integer> vals = getValues(field);
373 if (vals.isEmpty()) {
374 next = cal.get(field) + 1;
375 } else {
376 try {
377
378 int incrValue = cal.get(field) + 1;
379 next = vals.tailSet(new Integer(incrValue)).first().intValue();
380 } catch (NoSuchElementException nse) {
381
382 return false;
383 }
384 }
385 if (next > cal.getActualMaximum(field) || next < cal.getActualMinimum(field)) {
386 return false;
387 }
388
389 cal.set(field, next);
390 return true;
391 }
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411 private boolean setFieldToNextValidRoll(final GregorianCalendar cal, final int field, final int smallerField)
412 throws ImplementationExceptionResponse {
413 setFieldsToMinimum(cal, smallerField);
414 return setToNextScheduledValue(cal, field);
415 }
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431 private boolean setFieldToMinimum(final GregorianCalendar cal, final int field)
432 throws ImplementationExceptionResponse {
433 int min;
434 TreeSet<Integer> values = getValues(field);
435 if (values.isEmpty()) {
436 min = cal.getActualMinimum(field);
437 } else {
438 min = Math.max(values.first().intValue(), cal.getActualMinimum(field));
439 if (min > cal.getActualMaximum(field)) {
440 min = cal.getActualMaximum(field);
441 if (!values.contains(Integer.valueOf(min)) || min < cal.getActualMinimum(field)
442 || min > cal.getActualMaximum(field)) {
443 return false;
444 }
445 }
446 }
447 cal.set(field, min);
448 return true;
449 }
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466 private boolean setFieldsToMinimum(final GregorianCalendar cal, final int largestField)
467 throws ImplementationExceptionResponse {
468 boolean result = true;
469 switch (largestField) {
470 case (MONTH):
471 result = setFieldToMinimum(cal, MONTH) && result;
472 case (DAY_OF_MONTH):
473 result = setFieldToMinimum(cal, DAY_OF_MONTH) && result;
474 case (HOUR_OF_DAY):
475 result = setFieldToMinimum(cal, HOUR_OF_DAY) && result;
476 case (MINUTE):
477 result = setFieldToMinimum(cal, MINUTE) && result;
478 case (SECOND):
479 result = setFieldToMinimum(cal, SECOND) && result;
480 break;
481 default:
482 String msg = "Invalid field: " + largestField;
483 ImplementationExceptionResponse iex = new ImplementationExceptionResponse(msg);
484 LOG.error(msg, iex);
485 throw iex;
486
487 }
488 return result;
489 }
490
491
492
493
494
495
496
497
498
499
500
501 private TreeSet<Integer> getValues(final int field) throws ImplementationExceptionResponse {
502 switch (field) {
503 case (DAY_OF_WEEK):
504 return daysOfWeek;
505 case (MONTH):
506 return months;
507 case (DAY_OF_MONTH):
508 return daysOfMonth;
509 case (HOUR_OF_DAY):
510 return hours;
511 case (MINUTE):
512 return minutes;
513 case (SECOND):
514 return seconds;
515 default:
516 String msg = "Invalid field: " + field;
517 ImplementationExceptionResponse iex = new ImplementationExceptionResponse(msg);
518 LOG.error(msg, iex);
519 throw iex;
520 }
521 }
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540 private void handleValues(final String[] values, final String type, final int min, final int max)
541 throws SubscriptionControlsExceptionResponse {
542
543 TreeSet<Integer> vals = new TreeSet<Integer>();
544 for (String v : values) {
545 try {
546 if (v.startsWith("[")) {
547
548 String[] range = v.substring(1, v.length() - 1).split("-");
549 int start = Integer.parseInt(range[0]);
550 int end = Integer.parseInt(range[1]);
551
552 if (start < min || end > max || start > end) {
553 throw new SubscriptionControlsExceptionResponse("The value for '" + type
554 + "' is out of range in the query schedule.");
555 }
556
557 for (int value = start; value <= end; value++) {
558 vals = addValue(value, type, vals);
559 }
560 } else if (!v.equals("")) {
561
562 int value = Integer.parseInt(v);
563
564 if (value < min || value > max) {
565 throw new SubscriptionControlsExceptionResponse("The value for '" + type
566 + "' is out of range in the query schedule.");
567 }
568
569 vals = addValue(value, type, vals);
570 }
571 } catch (Exception e) {
572 String msg = "The value '" + v + "' for parameter '" + type + "' is invalid in the query schedule.";
573 LOG.info("USER ERROR: " + msg + e.getMessage());
574 throw new SubscriptionControlsExceptionResponse(msg);
575 }
576 }
577
578 if (type.equals("second")) {
579 this.seconds = vals;
580 } else if (type.equals("minute")) {
581 this.minutes = vals;
582 } else if (type.equals("hour")) {
583 this.hours = vals;
584 } else if (type.equals("dayOfMonth")) {
585 this.daysOfMonth = vals;
586 } else if (type.equals("month")) {
587 this.months = vals;
588 } else if (type.equals("dayOfWeek")) {
589 this.daysOfWeek = vals;
590 }
591 }
592
593
594
595
596
597
598
599
600
601
602
603
604
605 private TreeSet<Integer> addValue(final int value, final String type, final TreeSet<Integer> vals) {
606 if (type.equals("dayOfWeek")) {
607 vals.add(new Integer((value % 7) + 1));
608 } else if (type.equals("month")) {
609 vals.add(new Integer(value - 1));
610 } else {
611 vals.add(new Integer(value));
612 }
613 return vals;
614 }
615
616
617
618
619 public TreeSet<Integer> getDaysOfMonth() {
620 return daysOfMonth;
621 }
622
623
624
625
626 public TreeSet<Integer> getDaysOfWeek() {
627 return daysOfWeek;
628 }
629
630
631
632
633 public TreeSet<Integer> getHours() {
634 return hours;
635 }
636
637
638
639
640 public TreeSet<Integer> getMinutes() {
641 return minutes;
642 }
643
644
645
646
647 public TreeSet<Integer> getMonths() {
648 return months;
649 }
650
651
652
653
654 public TreeSet<Integer> getSeconds() {
655 return seconds;
656 }
657 }