1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.kuali.rice.krad.uif.lifecycle;
17
18 import java.util.ArrayList;
19 import java.util.LinkedHashSet;
20 import java.util.LinkedList;
21 import java.util.List;
22 import java.util.Map;
23 import java.util.Queue;
24 import java.util.Set;
25
26 import org.apache.commons.lang.StringUtils;
27 import org.kuali.rice.krad.uif.UifConstants;
28 import org.kuali.rice.krad.uif.component.Component;
29 import org.kuali.rice.krad.uif.lifecycle.ViewLifecycle.LifecycleEvent;
30 import org.kuali.rice.krad.uif.lifecycle.initialize.AssignIdsTask;
31 import org.kuali.rice.krad.uif.util.ComponentUtils;
32 import org.kuali.rice.krad.uif.util.CopyUtils;
33 import org.kuali.rice.krad.uif.util.LifecycleElement;
34 import org.kuali.rice.krad.uif.util.ObjectPropertyUtils;
35 import org.kuali.rice.krad.uif.util.ProcessLogger;
36 import org.slf4j.Logger;
37 import org.slf4j.LoggerFactory;
38
39
40
41
42
43
44 public abstract class ViewLifecyclePhaseBase implements ViewLifecyclePhase {
45 private final Logger LOG = LoggerFactory.getLogger(ViewLifecyclePhaseBase.class);
46
47 private LifecycleElement element;
48 private Object model;
49 private Component parent;
50 private String viewPath;
51 private String path;
52 private int depth;
53
54 private List<String> refreshPaths;
55
56 private ViewLifecyclePhaseBase predecessor;
57 private ViewLifecyclePhaseBase nextPhase;
58
59 private boolean processed;
60 private boolean completed;
61
62 private Set<String> pendingSuccessors = new LinkedHashSet<String>();
63
64 private ViewLifecycleTask<?> currentTask;
65
66
67
68
69 protected void recycle() {
70 trace("recycle");
71 element = null;
72 model = null;
73 path = null;
74 viewPath = null;
75 depth = 0;
76 predecessor = null;
77 nextPhase = null;
78 processed = false;
79 completed = false;
80 refreshPaths = null;
81 pendingSuccessors.clear();
82 }
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98 protected void prepare(LifecycleElement element, Object model, String path, List<String> refreshPaths,
99 Component parent, ViewLifecyclePhaseBase nextPhase) {
100 if (element.getViewStatus().equals(getEndViewStatus())) {
101 LOG.warn(
102 "Component is already in the expected end status " + getEndViewStatus() + " before this phase " +
103 element.getClass() + " " + element.getId());
104 }
105
106 this.model = model;
107 this.path = path;
108
109 String parentViewPath = parent == null ? null : parent.getViewPath();
110 if (StringUtils.isEmpty(parentViewPath)) {
111 this.viewPath = path;
112 } else {
113 this.viewPath = parentViewPath + "." + path;
114 }
115
116 this.element = CopyUtils.unwrap(element);
117 this.refreshPaths = refreshPaths;
118 this.parent = parent;
119 this.nextPhase = nextPhase;
120
121 trace("prepare");
122 }
123
124
125
126
127
128
129
130
131
132
133
134 @Override
135 public final void run() {
136 try {
137 ViewLifecycleProcessorBase processor = (ViewLifecycleProcessorBase) ViewLifecycle.getProcessor();
138
139 validateBeforeProcessing();
140
141 boolean skipLifecycle = shouldSkipLifecycle();
142
143 String ntracePrefix = null;
144 String ntraceSuffix = null;
145 try {
146 if (ViewLifecycle.isTrace() && ProcessLogger.isTraceActive()) {
147 ntracePrefix = "lc-" + getStartViewStatus() + "-" + getEndViewStatus() + ":";
148 ntraceSuffix = ":" + getElement().getClass().getSimpleName() + (getElement().isRender()?":render":":no-render");
149
150 ProcessLogger.ntrace(ntracePrefix, ntraceSuffix, 1000);
151 ProcessLogger.countBegin(ntracePrefix + ntraceSuffix);
152 }
153
154 String viewStatus = element.getViewStatus();
155 if (viewStatus != null &&
156 !viewStatus.equals(getStartViewStatus())) {
157 trace("dup " + getStartViewStatus() + " " + getEndViewStatus() + " " + viewStatus);
158 }
159
160 processor.setActivePhase(this);
161
162 trace("path-update " + element.getViewPath());
163
164 element.setViewPath(getViewPath());
165 element.getPhasePathMapping().put(getViewPhase(), getViewPath());
166
167 Queue<ViewLifecycleTask<?>> pendingTasks = new LinkedList<ViewLifecycleTask<?>>();
168 if (skipLifecycle) {
169 initializeSkipLifecyclePendingTasks(pendingTasks);
170 } else {
171 initializePendingTasks(pendingTasks);
172 }
173
174 while (!pendingTasks.isEmpty()) {
175 ViewLifecycleTask<?> task = pendingTasks.poll();
176
177 currentTask = task;
178 task.run();
179 currentTask = null;
180 }
181
182 element.setViewStatus(getEndViewStatus());
183 processed = true;
184
185 } finally {
186 processor.setActivePhase(null);
187
188 if (ViewLifecycle.isTrace() && ProcessLogger.isTraceActive()) {
189 ProcessLogger.countEnd(ntracePrefix + ntraceSuffix,
190 getElement().getClass() + " " + getElement().getId());
191 }
192 }
193
194 if (skipLifecycle) {
195 notifyCompleted();
196 } else {
197 assert pendingSuccessors.isEmpty() : pendingSuccessors;
198
199 Queue<ViewLifecyclePhase> successors = new LinkedList<ViewLifecyclePhase>();
200
201 initializeSuccessors(successors);
202 processSuccessors(successors);
203 }
204 } catch (Throwable t) {
205 trace("error");
206 LOG.warn("Error in lifecycle phase " + this, t);
207
208 if (t instanceof RuntimeException) {
209 throw (RuntimeException) t;
210 } else if (t instanceof Error) {
211 throw (Error) t;
212 } else {
213 throw new IllegalStateException("Unexpected error in lifecycle phase " + this, t);
214 }
215 }
216 }
217
218
219
220
221
222
223
224
225
226
227
228
229 protected boolean shouldSkipLifecycle() {
230
231 boolean isPreProcessPhase = getViewPhase().equals(UifConstants.ViewPhases.PRE_PROCESS);
232
233
234 boolean isRefreshComponent = ViewLifecycle.isRefreshComponent(getViewPhase(), getViewPath());
235
236
237 boolean includesRefreshComponent = false;
238 if (StringUtils.isNotBlank(ViewLifecycle.getRefreshComponentPhasePath(getViewPhase()))) {
239 includesRefreshComponent = ViewLifecycle.getRefreshComponentPhasePath(getViewPhase()).startsWith(getViewPath());
240 }
241
242 boolean skipLifecycle = false;
243 if (!(isPreProcessPhase || isRefreshComponent || includesRefreshComponent)) {
244
245 skipLifecycle = element.skipLifecycle();
246 }
247
248 return skipLifecycle;
249 }
250
251
252
253
254
255
256 protected void validateBeforeProcessing() {
257 if (processed) {
258 throw new IllegalStateException("Lifecycle phase has already been processed " + this);
259 }
260
261 if (predecessor != null && !predecessor.isProcessed()) {
262 throw new IllegalStateException("Predecessor phase has not completely processed " + this);
263 }
264
265 if (!ViewLifecycle.isActive()) {
266 throw new IllegalStateException("No view lifecyle is not active on the current thread");
267 }
268
269 if (LOG.isDebugEnabled()) {
270 trace("ready " + getStartViewStatus() + " -> " + getEndViewStatus());
271 }
272 }
273
274
275
276
277
278
279
280 protected void processSuccessors(Queue<ViewLifecyclePhase> successors) {
281 for (ViewLifecyclePhase successor : successors) {
282 if (!pendingSuccessors.add(successor.getParentPath())) {
283 ViewLifecycle.reportIllegalState("Already pending " + successor + "\n" + this);
284 }
285 }
286
287 trace("processed " + pendingSuccessors);
288
289 if (pendingSuccessors.isEmpty()) {
290 notifyCompleted();
291 } else {
292 for (ViewLifecyclePhase successor : successors) {
293 if (successor instanceof ViewLifecyclePhaseBase) {
294 ViewLifecyclePhaseBase successorBase = (ViewLifecyclePhaseBase) successor;
295 assert successorBase.predecessor == null : this + " " + successorBase;
296
297 successorBase.predecessor = this;
298 successorBase.depth = this.depth + 1;
299 successorBase.trace("succ-pend");
300 }
301
302 ViewLifecycle.getProcessor().offerPendingPhase(successor);
303 }
304 }
305 }
306
307
308
309
310
311
312 protected void initializeSkipLifecyclePendingTasks(Queue<ViewLifecycleTask<?>> tasks) {
313
314 }
315
316
317
318
319
320
321
322
323
324 protected abstract void initializePendingTasks(Queue<ViewLifecycleTask<?>> tasks);
325
326
327
328
329
330
331
332
333
334
335
336 protected void initializeSuccessors(Queue<ViewLifecyclePhase> successors) {
337 if (ViewLifecycle.isRefreshLifecycle() && (refreshPaths != null)) {
338 String currentPath = getViewPath();
339
340 boolean withinRefreshComponent = currentPath.startsWith(ViewLifecycle.getRefreshComponentPhasePath(getViewPhase()));
341 if (withinRefreshComponent) {
342 initializeAllLifecycleSuccessors(successors);
343 } else if (refreshPaths.contains(currentPath) || StringUtils.isBlank(currentPath)) {
344 initializeRefreshPathSuccessors(successors);
345 }
346
347 return;
348 }
349
350 initializeAllLifecycleSuccessors(successors);
351 }
352
353
354
355
356
357
358 protected void initializeRefreshPathSuccessors(Queue<ViewLifecyclePhase> successors) {
359 LifecycleElement element = getElement();
360
361 String nestedPathPrefix;
362 Component nestedParent;
363 if (element instanceof Component) {
364 nestedParent = (Component) element;
365 nestedPathPrefix = "";
366 } else {
367 nestedParent = getParent();
368 nestedPathPrefix = getParentPath() + ".";
369 }
370
371 List<String> nestedProperties = getNestedPropertiesForRefreshPath();
372
373 for (String nestedProperty : nestedProperties) {
374 String nestedPath = nestedPathPrefix + nestedProperty;
375
376 LifecycleElement nestedElement = ObjectPropertyUtils.getPropertyValue(element, nestedProperty);
377 if (nestedElement != null) {
378 ViewLifecyclePhase nestedPhase = initializeSuccessor(nestedElement, nestedPath, nestedParent);
379 successors.add(nestedPhase);
380 }
381 }
382 }
383
384
385
386
387
388
389
390 protected List<String> getNestedPropertiesForRefreshPath() {
391 List<String> nestedProperties = new ArrayList<String>();
392
393 String currentPath = getViewPath();
394 if (currentPath == null) {
395 currentPath = "";
396 }
397
398 if (StringUtils.isNotBlank(currentPath)) {
399 currentPath += ".";
400 }
401
402
403
404 for (String refreshPath : refreshPaths) {
405 if (!refreshPath.startsWith(currentPath)) {
406 continue;
407 }
408
409 String nestedProperty = StringUtils.substringAfter(refreshPath, currentPath);
410
411 if (StringUtils.isBlank(nestedProperty)) {
412 continue;
413 }
414
415 if (StringUtils.contains(nestedProperty, ".")) {
416 nestedProperty = StringUtils.substringBefore(nestedProperty, ".");
417 }
418
419 if (!nestedProperties.contains(nestedProperty)) {
420 nestedProperties.add(nestedProperty);
421 }
422 }
423
424 return nestedProperties;
425 }
426
427
428
429
430
431
432 protected void initializeAllLifecycleSuccessors(Queue<ViewLifecyclePhase> successors) {
433 LifecycleElement element = getElement();
434
435 String nestedPathPrefix;
436 Component nestedParent;
437 if (element instanceof Component) {
438 nestedParent = (Component) element;
439 nestedPathPrefix = "";
440 } else {
441 nestedParent = getParent();
442 nestedPathPrefix = getParentPath() + ".";
443 }
444
445 for (Map.Entry<String, LifecycleElement> nestedElementEntry : ViewLifecycleUtils.getElementsForLifecycle(
446 element, getViewPhase()).entrySet()) {
447 String nestedPath = nestedPathPrefix + nestedElementEntry.getKey();
448 LifecycleElement nestedElement = nestedElementEntry.getValue();
449
450 if (nestedElement != null && !getEndViewStatus().equals(nestedElement.getViewStatus())) {
451 ViewLifecyclePhase nestedPhase = initializeSuccessor(nestedElement, nestedPath, nestedParent);
452 successors.offer(nestedPhase);
453 }
454 }
455 }
456
457
458
459
460
461
462
463
464
465 protected abstract ViewLifecyclePhase initializeSuccessor(LifecycleElement nestedElement, String nestedPath,
466 Component nestedParent);
467
468
469
470
471
472
473
474 protected void verifyCompleted() {
475 }
476
477
478
479
480 protected final void notifyCompleted() {
481 trace("complete");
482
483 completed = true;
484
485 LifecycleEvent event = getEventToNotify();
486 if (event != null) {
487 ViewLifecycle.getActiveLifecycle().invokeEventListeners(event, ViewLifecycle.getView(),
488 ViewLifecycle.getModel(), element);
489 }
490
491 element.notifyCompleted(this);
492
493 if (nextPhase != null) {
494 assert nextPhase.predecessor == null : this + " " + nextPhase;
495
496
497
498 if (predecessor != null) {
499
500
501
502 nextPhase.predecessor = predecessor;
503 nextPhase.depth = predecessor.depth + 1;
504 } else {
505
506
507 nextPhase.predecessor = this;
508 nextPhase.depth = this.depth + 1;
509 synchronized (pendingSuccessors) {
510 pendingSuccessors.add(nextPhase.getParentPath());
511 }
512 }
513
514 ViewLifecycle.getProcessor().pushPendingPhase(nextPhase);
515 return;
516 }
517
518 synchronized (this) {
519 if (predecessor != null) {
520 synchronized (predecessor) {
521 predecessor.pendingSuccessors.remove(getParentPath());
522 if (predecessor.pendingSuccessors.isEmpty()) {
523 predecessor.notifyCompleted();
524 }
525 LifecyclePhaseFactory.recycle(this);
526 }
527 } else {
528 trace("notify");
529 notifyAll();
530 }
531 }
532 }
533
534
535
536
537 @Override
538 public final LifecycleElement getElement() {
539 return element;
540 }
541
542
543
544
545 @Override
546 public final Object getModel() {
547 return model;
548 }
549
550
551
552
553 @Override
554 public final Component getParent() {
555 return this.parent;
556 }
557
558
559
560
561 @Override
562 public String getParentPath() {
563 return this.path;
564 }
565
566
567
568
569 @Override
570 public List<String> getRefreshPaths() {
571 return refreshPaths;
572 }
573
574
575
576
577 @Override
578 public String getViewPath() {
579 return this.viewPath;
580 }
581
582
583
584
585 public void setViewPath(String viewPath) {
586 this.viewPath = viewPath;
587 }
588
589
590
591
592 @Override
593 public int getDepth() {
594 return this.depth;
595 }
596
597
598
599
600 @Override
601 public final boolean isProcessed() {
602 return processed;
603 }
604
605
606
607
608 @Override
609 public final boolean isComplete() {
610 return completed;
611 }
612
613
614
615
616 @Override
617 public ViewLifecyclePhase getPredecessor() {
618 return predecessor;
619 }
620
621
622
623
624 @Override
625 public ViewLifecycleTask<?> getCurrentTask() {
626 return this.currentTask;
627 }
628
629
630
631
632 @Override
633 public String toString() {
634 StringBuilder sb = new StringBuilder();
635 Queue<ViewLifecyclePhase> toPrint = new LinkedList<ViewLifecyclePhase>();
636 toPrint.offer(this);
637 while (!toPrint.isEmpty()) {
638 ViewLifecyclePhase tp = toPrint.poll();
639
640 if (tp.getElement() == null) {
641 sb.append("\n ");
642 sb.append(tp.getClass().getSimpleName());
643 sb.append(" (recycled)");
644 continue;
645 }
646
647 String indent;
648 if (tp == this) {
649 if (this.model != null) {
650 sb.append("Model: ");
651 sb.append(this.model.getClass().getSimpleName());
652 }
653 sb.append("\nProcessed? ");
654 sb.append(processed);
655 indent = "\n";
656 } else {
657 indent = "\n ";
658 }
659 sb.append(indent);
660
661 sb.append(tp.getClass().getSimpleName());
662 sb.append(" ");
663 sb.append(System.identityHashCode(tp));
664 sb.append(" ");
665 sb.append(tp.getViewPath());
666 sb.append(" ");
667 sb.append(tp.getElement().getClass().getSimpleName());
668 sb.append(" ");
669 sb.append(tp.getElement().getId());
670 sb.append(" ");
671 sb.append(pendingSuccessors);
672
673 if (tp == this) {
674 sb.append("\nPredecessor Phases:");
675 }
676
677 ViewLifecyclePhase tpredecessor = tp.getPredecessor();
678 if (tpredecessor != null) {
679 toPrint.add(tpredecessor);
680 }
681 }
682 return sb.toString();
683 }
684
685
686
687
688
689
690
691
692 protected void trace(String step) {
693 if (ViewLifecycle.isTrace() && LOG.isDebugEnabled()) {
694 String msg = System.identityHashCode(this) + " " + getClass() + " " + step + " " + path + " " +
695 (element == null ? "(recycled)" : element.getClass() + " " + element.getId());
696 LOG.debug(msg);
697 }
698 }
699
700 }