1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package net.sf.appstatus.core.batch;
18
19 import java.util.Date;
20 import java.util.List;
21 import java.util.Vector;
22
23 import org.slf4j.Logger;
24 import org.slf4j.LoggerFactory;
25
26
27
28
29
30
31
32
33 public abstract class AbstractBatchProgressMonitor implements IBatchProgressMonitorExt {
34
35
36
37
38 private static Date maxDate = new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 24 * 365);
39
40
41
42
43 private final IBatch batch;
44
45 protected List<AbstractBatchProgressMonitor> currentChildren;
46
47
48
49
50 protected Object currentItem;
51 protected boolean done = false;
52
53 protected Long endTime = null;
54
55 protected final String executionId;
56 protected long itemCount = 0;
57 private String lastMessage;
58
59 private long lastUpdate = -1;
60 private long lastWriteTimestamp;
61
62 private Logger logger = LoggerFactory.getLogger("batch");
63
64 private String name;
65
66 protected AbstractBatchProgressMonitor parent;
67
68 private int parentWork;
69 protected final List<String> rejectedItems = new Vector<String>();
70 protected long startTime;
71
72
73
74
75 private boolean success;
76
77 protected final List<String> successItems = new Vector<String>();
78
79 private String taskDescription;
80
81 private String taskGroup;
82
83
84
85
86
87
88
89 protected String taskName = null;
90
91 protected int totalWork = UNKNOW;
92
93 protected int worked = 0;
94
95
96
97
98 private long writingDelay = 1000;
99
100
101
102
103
104
105
106 public AbstractBatchProgressMonitor(String executionId, IBatch batch) {
107 this.executionId = executionId;
108 this.batch = batch;
109 this.currentChildren = new Vector<AbstractBatchProgressMonitor>();
110 this.batch.setProgressMonitor(this);
111 startTime = System.currentTimeMillis();
112 getLogger().info("[{}] {}: Init progress monitoring (id: {})",
113 new Object[] { this.batch.getGroup(), batch.getName(), batch.getUuid() });
114 touch();
115 }
116
117
118
119
120
121
122
123
124
125
126
127 protected AbstractBatchProgressMonitor(String executionId, IBatchProgressMonitor parent, int parentWork,
128 IBatch batch) {
129 this.executionId = executionId;
130 this.parent = (AbstractBatchProgressMonitor) parent;
131 this.parentWork = parentWork;
132 this.currentChildren = new Vector<AbstractBatchProgressMonitor>();
133 this.batch = batch;
134 startTime = System.currentTimeMillis();
135 touch();
136
137 }
138
139
140
141
142 public void beginTask(String name, String description, int totalWork) {
143
144 if (this.taskName != null) {
145 throw new IllegalStateException(
146 "beginTask can only be called once (" + name + ", " + description + ", " + totalWork + ")");
147 }
148 this.totalWork = totalWork;
149 this.taskName = name;
150 this.taskDescription = description;
151 getLogger().info("[{}] {}: Begin {} ({}), steps : {}", new Object[] { getBatch().getGroup(),
152 getBatch().getName(), name, description, String.valueOf(totalWork) });
153 touch();
154
155 }
156
157
158
159
160 public IBatchProgressMonitor createSubTask(int work) {
161 AbstractBatchProgressMonitor newSubTask = (AbstractBatchProgressMonitor) newInstance(work);
162 newSubTask.setLogger(logger);
163 currentChildren.add(newSubTask);
164 touch();
165 return newSubTask;
166 }
167
168
169
170
171 public void done() {
172 if (parent != null) {
173 endTask(true);
174 getLogger().info("[{}] {}: End {}, {} ms", new Object[] { getBatch().getGroup(), getBatch().getName(), name,
175 System.currentTimeMillis() - startTime });
176 } else {
177 endBatch(true);
178 getLogger().info("[{}] {}: End batch, {} ms", new Object[] { getBatch().getGroup(), getBatch().getName(),
179 System.currentTimeMillis() - startTime });
180
181 }
182
183 touch();
184
185 }
186
187 protected void endBatch(boolean success) {
188 getMainMonitor().success = success;
189 getMainMonitor().done = true;
190 getMainMonitor().endTime = System.currentTimeMillis();
191 getMainMonitor().currentItem = null;
192
193 if (getMainMonitor().totalWork != UNKNOW && getMainMonitor().worked < getMainMonitor().totalWork) {
194 getMainMonitor().worked = getMainMonitor().totalWork;
195 }
196 getMainMonitor().currentChildren.clear();
197
198 onBatchEnd();
199 }
200
201 protected void endTask(boolean success) {
202
203 done = true;
204 currentItem = null;
205 this.endTime = System.currentTimeMillis();
206 this.success = success;
207
208 if (parent != null) {
209 parent.worked(parentWork);
210 parent.currentChildren.remove(this);
211 }
212
213 }
214
215 public void fail(String reason) {
216 fail(reason, null);
217
218 }
219
220 public void fail(String reason, Throwable t) {
221 message("Failed: " + reason + " " + (t != null ? t.getMessage() : ""));
222
223
224 endTask(false);
225 endBatch(false);
226
227 getLogger().error("[{}] {}: FAILED ({}) , duration: {} ms", this.batch.getGroup(), batch.getName(), reason,
228 String.valueOf(endTime - startTime), t);
229 touch();
230 }
231
232 public IBatch getBatch() {
233 return batch;
234 }
235
236 public Object getCurrentItem() {
237 return currentItem;
238 }
239
240
241
242
243
244
245
246 protected Date getEndDate() {
247
248
249 if (!done) {
250
251
252 if (totalWork == UNKNOW) {
253 return null;
254 }
255
256 long currentTime = System.currentTimeMillis();
257 long elapsed = currentTime - startTime;
258
259 if (getProgress() != 0.0d) {
260 long estEndTime = currentTime + (long) (totalWork * elapsed / getProgress());
261
262 Date endDate = new Date(estEndTime);
263
264 if (endDate.before(maxDate)) {
265 return endDate;
266 }
267 }
268 }
269
270
271 if (this.endTime != null) {
272 return new Date(this.endTime);
273 }
274
275
276 return null;
277 }
278
279 public long getItemCount() {
280 return itemCount;
281 }
282
283 public String getLastMessage() {
284 return lastMessage;
285 }
286
287 public Date getLastUpdate() {
288 return new Date(getMainMonitor().lastUpdate);
289 }
290
291 protected Logger getLogger() {
292 return getMainMonitor().logger;
293 }
294
295
296
297
298
299
300 protected AbstractBatchProgressMonitor getMainMonitor() {
301 AbstractBatchProgressMonitor main = this;
302
303 while (main.parent != null) {
304 main = main.parent;
305 }
306
307 return main;
308 }
309
310 protected int getParentWork() {
311 return parentWork;
312 }
313
314
315
316
317
318
319 public float getProgress() {
320
321 if (totalWork == UNKNOW) {
322 return UNKNOW;
323 }
324
325 float result = worked;
326
327 for (AbstractBatchProgressMonitor child : currentChildren) {
328
329 if (child.getTotalWork() != UNKNOW && !child.isDone()) {
330 float childProgress = child.getParentWork() * child.getProgress() / child.getTotalWork();
331 result = result + childProgress;
332 }
333
334 }
335
336 return result;
337 }
338
339
340
341
342
343
344 public List<String> getRejectedItems() {
345 return rejectedItems;
346 }
347
348 protected Date getStartDate() {
349 return new Date(startTime);
350 }
351
352 public String getTaskDescription() {
353 return taskDescription;
354 }
355
356 public String getTaskGroup() {
357 return taskGroup;
358 };
359
360 public String getTaskName() {
361 return taskName;
362 }
363
364
365
366
367 public int getTotalWork() {
368 return this.totalWork;
369 }
370
371 public long getWritingDelay() {
372 return writingDelay;
373 }
374
375 public boolean isCancelRequested() {
376 return false;
377 }
378
379 public boolean isDone() {
380 return done;
381 }
382
383
384
385
386
387
388
389
390
391 private boolean isLoggable(long lastWriteTimestamp) {
392 if (System.currentTimeMillis() - lastWriteTimestamp > writingDelay) {
393 return true;
394 }
395 return false;
396 }
397
398 public boolean isSuccess() {
399 return success;
400 }
401
402
403
404
405 public void message(String message) {
406 String actionName = name;
407 if (name == null) {
408 actionName = batch.getName();
409 }
410
411 getLogger().info("[{}] {}: {} ## {}", getBatch().getGroup(), getBatch().getName(), actionName, message);
412 lastMessage = message;
413 getMainMonitor().lastMessage = message;
414 touch();
415 }
416
417 protected abstract IBatchProgressMonitor newInstance(int work);
418
419 protected void onBatchEnd() {
420
421 }
422
423
424
425
426 public void reject(String itemId, String reason) {
427 reject(itemId, reason, null);
428 }
429
430
431
432
433 public void reject(String itemId, String reason, Throwable e) {
434 getMainMonitor().rejectedItems.add(itemId);
435 getLogger().warn("[{}] {}: {} rejected {} ({})", getBatch().getGroup(), getBatch().getName(), name, itemId,
436 reason, e);
437
438 touch();
439 }
440
441
442
443
444 public void reject(String[] itemIds, String reason) {
445 reject(itemIds, reason, null);
446 }
447
448
449
450
451 public void reject(String[] itemIds, String reason, Throwable e) {
452 if (itemIds != null) {
453 for (String id : itemIds) {
454 reject(id, reason, e);
455 }
456 }
457 }
458
459
460
461
462 public void setCurrentItem(Object item) {
463 if (currentItem != item) {
464 getMainMonitor().itemCount++;
465 }
466
467 currentItem = item;
468
469 if (isLoggable(lastWriteTimestamp)) {
470 lastWriteTimestamp = System.currentTimeMillis();
471 String actionName = name;
472 if (actionName == null) {
473 actionName = batch.getName();
474 }
475 getLogger().info("[{}] {}: {} working on {} (#{})",
476 new Object[] { getBatch().getGroup(), getBatch().getName(), actionName, item, itemCount });
477 }
478 touch();
479
480 }
481
482 public void setLogger(Logger loggerParam) {
483 getMainMonitor().logger = loggerParam;
484 }
485
486
487
488
489 public void setTotalWork(int totalWork) {
490 if (totalWork < 0) {
491 throw new IllegalArgumentException("totalWork cannot be negative : " + totalWork);
492 }
493
494 this.totalWork = totalWork;
495 }
496
497
498
499
500
501
502
503 public void setWritingDelay(long writingDelay) {
504 this.writingDelay = writingDelay;
505 }
506
507
508
509
510 protected void touch() {
511 getMainMonitor().lastUpdate = System.currentTimeMillis();
512 }
513
514
515
516
517 public void worked(int work) {
518
519 if (work < 0) {
520 throw new IllegalArgumentException("work cannot be negative : " + work);
521 }
522 worked = worked + work;
523
524
525 if (totalWork != UNKNOW) {
526 if (worked > totalWork) {
527 worked = totalWork;
528 }
529 }
530
531
532 if (isLoggable(lastWriteTimestamp)) {
533 float cp = getMainMonitor().getProgress() * 100f / getMainMonitor().getTotalWork();
534
535 lastWriteTimestamp = System.currentTimeMillis();
536 getLogger().info("[{}] {}: progress {}%", new Object[] { getBatch().getGroup(), getBatch().getName(), cp });
537
538 if (cp > 100) {
539 throw new RuntimeException();
540 }
541 }
542
543
544 touch();
545 }
546
547 }