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