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 org.apache.commons.lang.StringUtils;
19 import org.apache.commons.logging.Log;
20 import org.apache.commons.logging.LogFactory;
21 import org.apache.log4j.Logger;
22 import org.kuali.rice.core.api.CoreApiServiceLocator;
23 import org.kuali.rice.core.api.config.property.Config;
24 import org.kuali.rice.core.api.config.property.ConfigContext;
25 import org.kuali.rice.krad.datadictionary.validator.ValidationController;
26 import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
27 import org.kuali.rice.krad.uif.UifConstants;
28 import org.kuali.rice.krad.uif.UifPropertyPaths;
29 import org.kuali.rice.krad.uif.component.Component;
30 import org.kuali.rice.krad.uif.container.Group;
31 import org.kuali.rice.krad.uif.freemarker.LifecycleRenderingContext;
32 import org.kuali.rice.krad.uif.service.ViewHelperService;
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.view.DefaultExpressionEvaluator;
36 import org.kuali.rice.krad.uif.view.ExpressionEvaluator;
37 import org.kuali.rice.krad.uif.view.ExpressionEvaluatorFactory;
38 import org.kuali.rice.krad.uif.view.View;
39 import org.kuali.rice.krad.util.KRADConstants;
40 import org.kuali.rice.krad.web.form.UifFormBase;
41
42 import javax.servlet.http.HttpServletRequest;
43 import java.io.Serializable;
44 import java.util.ArrayList;
45 import java.util.Collections;
46 import java.util.HashMap;
47 import java.util.HashSet;
48 import java.util.List;
49 import java.util.Map;
50 import java.util.Set;
51
52
53
54
55
56
57
58 public class ViewLifecycle implements Serializable {
59
60 private static Logger LOG = Logger.getLogger(ViewLifecycle.class);
61 private static final long serialVersionUID = -4767600614111642241L;
62
63 private static final ThreadLocal<ViewLifecycleProcessor> PROCESSOR = new ThreadLocal<ViewLifecycleProcessor>();
64
65 private static Boolean strict;
66 private static Boolean renderInLifecycle;
67 private static Boolean trace;
68
69 private final List<EventRegistration> eventRegistrations;
70 private final View view;
71
72 private final ComponentPostMetadata refreshComponentPostMetadata;
73
74 final ViewHelperService helper;
75
76 final Object model;
77
78 final HttpServletRequest request;
79 private ViewPostMetadata viewPostMetadata;
80
81 private Set<String> visitedIds;
82
83 private static String refreshComponentId;
84
85
86
87
88
89
90
91
92
93
94
95 private ViewLifecycle(View view, Object model, ComponentPostMetadata refreshComponentPostMetadata,
96 HttpServletRequest request) {
97 this.view = view;
98 this.model = model;
99 this.request = request;
100 this.refreshComponentPostMetadata = refreshComponentPostMetadata;
101 this.helper = view.getViewHelperService();
102 this.eventRegistrations = new ArrayList<EventRegistration>();
103 }
104
105
106
107
108
109
110
111
112
113 public static void encapsulateLifecycle(View view, Object model, HttpServletRequest request,
114 Runnable lifecycleProcess) {
115 encapsulateLifecycle(view, model, null, null, request, lifecycleProcess);
116 }
117
118
119
120
121
122
123 public static void encapsulateLifecycle(View view, Object model, ViewPostMetadata viewPostMetadata,
124 ComponentPostMetadata refreshComponentPostMetadata, HttpServletRequest request, Runnable lifecycleProcess) {
125 ViewLifecycleProcessor processor = PROCESSOR.get();
126 if (processor != null) {
127 throw new IllegalStateException("Another lifecycle is already active on this thread");
128 }
129
130 try {
131 ViewLifecycle viewLifecycle = new ViewLifecycle(view, model, refreshComponentPostMetadata, request);
132 processor = isAsynchronousLifecycle() ? new AsynchronousViewLifecycleProcessor(viewLifecycle) :
133 new SynchronousViewLifecycleProcessor(viewLifecycle);
134 PROCESSOR.set(processor);
135
136 if (viewPostMetadata != null) {
137 viewLifecycle.viewPostMetadata = viewPostMetadata;
138 }
139
140 lifecycleProcess.run();
141 } finally {
142 PROCESSOR.remove();
143 }
144 }
145
146
147
148
149
150
151
152
153
154
155 public static void preProcess(View view) {
156 encapsulateLifecycle(view, null, null, new ViewLifecyclePreProcessBuild());
157 }
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182 public static ViewPostMetadata buildView(View view, Object model, HttpServletRequest request,
183 final Map<String, String> parameters) {
184 ViewPostMetadata postMetadata = new ViewPostMetadata(view.getId());
185
186 ViewLifecycle.encapsulateLifecycle(view, model, postMetadata, null, request, new ViewLifecycleBuild(parameters,
187 null));
188
189
190 if (CoreApiServiceLocator.getKualiConfigurationService().getPropertyValueAsBoolean(
191 UifConstants.VALIDATE_VIEWS_ONBUILD)) {
192 ValidationController validator = new ValidationController(true, true, true, true, false);
193 Log tempLogger = LogFactory.getLog(ViewLifecycle.class);
194 validator.validate(view, tempLogger, false);
195 }
196
197 return postMetadata;
198 }
199
200
201
202
203
204
205
206
207
208
209
210 public static Component performComponentLifecycle(View view, Object model, HttpServletRequest request,
211 ViewPostMetadata viewPostMetadata, String componentId) {
212 if (viewPostMetadata == null) {
213 UifFormBase form = (UifFormBase) model;
214
215 throw new RuntimeException(
216 "View post metadata is null which cannot occur for refresh. Form id: " + form.getFormKey()
217 + ", requested form id: " + form.getRequestedFormKey());
218 }
219
220
221 refreshComponentId = componentId;
222
223 ComponentPostMetadata componentPostMetadata = viewPostMetadata.getComponentPostMetadata(componentId);
224 boolean componentPostMetadataInitialized = false;
225 if (componentPostMetadata == null) {
226 componentPostMetadata = viewPostMetadata.initializeComponentPostMetadata(componentId);
227 componentPostMetadataInitialized = true;
228 }
229
230 String rootPath = componentPostMetadata.getRootObjectPath();
231
232 if (rootPath != null && !rootPath.equals(componentPostMetadata.getPath())) {
233 ComponentPostMetadata rootComponentPostMetadata = viewPostMetadata.getComponentPostMetadataForPath(
234 rootPath);
235
236
237 if (rootComponentPostMetadata != null && rootComponentPostMetadata.isDetachedComponent()) {
238 setupStandaloneComponentForRefresh(view, rootComponentPostMetadata.getId(), rootComponentPostMetadata);
239 }
240 }
241
242
243 if (componentPostMetadataInitialized || componentPostMetadata.isDetachedComponent()) {
244 setupStandaloneComponentForRefresh(view, componentId, componentPostMetadata);
245 }
246
247 Map<String, List<String>> refreshPathMappings = componentPostMetadata.getRefreshPathMappings();
248
249 encapsulateLifecycle(view, model, viewPostMetadata, componentPostMetadata, request, new ViewLifecycleBuild(null,
250 refreshPathMappings));
251
252 return ObjectPropertyUtils.getPropertyValue(view, componentPostMetadata.getPath());
253 }
254
255
256
257
258
259
260
261
262
263
264 protected static void setupStandaloneComponentForRefresh(View view, String componentId,
265 ComponentPostMetadata componentPostMetadata) {
266 Component refreshComponent = (Component) KRADServiceLocatorWeb.getDataDictionaryService().getDictionaryBean(
267 componentId);
268
269 if ((refreshComponent == null) || !(refreshComponent instanceof Group)) {
270 throw new RuntimeException("Refresh component was null or not a group instance");
271 }
272
273 List<Group> dialogs = new ArrayList<Group>();
274 if ((view.getDialogs() != null) && !view.getDialogs().isEmpty()) {
275 dialogs.addAll(view.getDialogs());
276 }
277
278 refreshComponent.addDataAttribute(UifConstants.DataAttributes.DETACHED, "true");
279 dialogs.add((Group) refreshComponent);
280 view.setDialogs(dialogs);
281
282 String refreshPath = UifPropertyPaths.DIALOGS + "[" + (view.getDialogs().size() - 1) + "]";
283 componentPostMetadata.setPath(refreshPath);
284
285 List<String> refreshPaths = new ArrayList<String>();
286 refreshPaths.add(refreshPath);
287
288 Map<String, List<String>> refreshPathMappings = new HashMap<String, List<String>>();
289
290 refreshPathMappings.put(UifConstants.ViewPhases.INITIALIZE, refreshPaths);
291 refreshPathMappings.put(UifConstants.ViewPhases.APPLY_MODEL, refreshPaths);
292 refreshPathMappings.put(UifConstants.ViewPhases.FINALIZE, refreshPaths);
293
294 componentPostMetadata.setRefreshPathMappings(refreshPathMappings);
295
296 componentPostMetadata.setDetachedComponent(true);
297 }
298
299
300
301
302
303
304
305 public static boolean isRefreshComponent(String viewPhase, String viewPath) {
306 if (!ViewLifecycle.isRefreshLifecycle()) {
307 return false;
308 }
309
310 return StringUtils.equals(getRefreshComponentPhasePath(viewPhase), viewPath);
311 }
312
313
314
315
316
317
318
319 public static String getRefreshComponentPhasePath(String viewPhase) {
320 if (!ViewLifecycle.isRefreshLifecycle()) {
321 return null;
322 }
323
324 ComponentPostMetadata refreshComponentPostMetadata = ViewLifecycle.getRefreshComponentPostMetadata();
325 if (refreshComponentPostMetadata == null) {
326 return null;
327 }
328
329 String refreshPath = refreshComponentPostMetadata.getPath();
330 if (refreshComponentPostMetadata.getPhasePathMapping() != null) {
331 Map<String, String> phasePathMapping = refreshComponentPostMetadata.getPhasePathMapping();
332
333
334
335 if (phasePathMapping.containsKey(viewPhase)) {
336 refreshPath = phasePathMapping.get(viewPhase);
337 }
338 }
339
340 return refreshPath;
341 }
342
343
344
345
346
347
348
349
350
351
352 public void invokeEventListeners(LifecycleEvent event, View view, Object model, LifecycleElement eventElement) {
353 synchronized (eventRegistrations) {
354 for (EventRegistration registration : eventRegistrations) {
355 if (registration.getEvent().equals(event) && (registration.getEventComponent() == eventElement)) {
356 registration.getEventListener().processEvent(event, view, model, eventElement);
357 }
358 }
359 }
360 }
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378 public void registerLifecycleCompleteListener(Component eventComponent, LifecycleEventListener listenerComponent) {
379 EventRegistration eventRegistration = new EventRegistration(LifecycleEvent.LIFECYCLE_COMPLETE, eventComponent,
380 listenerComponent);
381
382 synchronized (eventRegistrations) {
383 eventRegistrations.add(eventRegistration);
384 }
385 }
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405 public static boolean isStrict() {
406 if (strict == null) {
407 strict = ConfigContext.getCurrentContextConfig().getBooleanProperty(
408 KRADConstants.ConfigParameters.KRAD_STRICT_LIFECYCLE, false);
409 }
410
411 return strict;
412 }
413
414
415
416
417
418
419
420
421
422
423
424
425 public static boolean isRenderInLifecycle() {
426 if (renderInLifecycle == null) {
427 renderInLifecycle = ConfigContext.getCurrentContextConfig().getBooleanProperty(
428 KRADConstants.ConfigParameters.KRAD_RENDER_IN_LIFECYCLE, false);
429 }
430
431 return renderInLifecycle;
432 }
433
434
435
436
437
438
439
440
441
442
443
444
445 public static boolean isAsynchronousLifecycle() {
446 Config config = ConfigContext.getCurrentContextConfig();
447 return config != null && config.getBooleanProperty(
448 KRADConstants.ConfigParameters.KRAD_VIEW_LIFECYCLE_ASYNCHRONOUS, false);
449 }
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465 public static boolean isTrace() {
466 if (trace == null) {
467 Config config = ConfigContext.getCurrentContextConfig();
468 if (config == null) {
469 return false;
470 }
471
472 trace = config.getBooleanProperty(KRADConstants.ConfigParameters.KRAD_VIEW_LIFECYCLE_TRACE, false);
473 }
474
475 return trace;
476 }
477
478
479
480
481
482
483
484
485
486
487
488
489 public static void reportIllegalState(String message) {
490 reportIllegalState(message, null);
491 }
492
493
494
495
496
497
498
499
500
501
502
503
504
505 public static void reportIllegalState(String message, Throwable cause) {
506 IllegalStateException illegalState = new IllegalStateException(message + "\nPhase: " + ViewLifecycle.getPhase(),
507 cause);
508
509 if (ViewLifecycle.isStrict()) {
510 throw illegalState;
511 } else {
512 if (LOG.isTraceEnabled()) {
513 LOG.trace(illegalState.getMessage(), illegalState);
514 }
515 }
516 }
517
518
519
520
521
522
523 public static ViewHelperService getHelper() {
524 ViewLifecycle active = getActiveLifecycle();
525
526 if (active.helper == null) {
527 throw new IllegalStateException("Context view helper is not available");
528 }
529
530 return active.helper;
531 }
532
533
534
535
536
537
538 public static View getView() {
539 ViewLifecycle active = getActiveLifecycle();
540
541 if (active.view == null) {
542 throw new IllegalStateException("Context view is not available");
543 }
544
545 return active.view;
546 }
547
548
549
550
551
552
553
554
555
556
557
558
559 public static ExpressionEvaluator getExpressionEvaluator() {
560 ViewLifecycleProcessor processor = PROCESSOR.get();
561
562 if (processor == null) {
563 ExpressionEvaluatorFactory expressionEvaluatorFactory =
564 KRADServiceLocatorWeb.getExpressionEvaluatorFactory();
565
566 if (expressionEvaluatorFactory == null) {
567 return new DefaultExpressionEvaluator();
568 } else {
569 return expressionEvaluatorFactory.createExpressionEvaluator();
570 }
571 }
572
573 return processor.getExpressionEvaluator();
574 }
575
576
577
578
579
580
581 public static Object getModel() {
582 ViewLifecycle active = getActiveLifecycle();
583
584 if (active.model == null) {
585 throw new IllegalStateException("Model is not available");
586 }
587
588 return active.model;
589 }
590
591
592
593
594
595
596 public static Set<String> getVisitedIds() {
597 ViewLifecycle active = getActiveLifecycle();
598
599 if (active.visitedIds == null) {
600 synchronized (active) {
601 if (active.visitedIds == null) {
602 active.visitedIds = Collections.synchronizedSet(new HashSet<String>());
603 }
604 }
605 }
606
607 return active.visitedIds;
608 }
609
610
611
612
613
614
615 public static ViewPostMetadata getViewPostMetadata() {
616 ViewLifecycle active = getActiveLifecycle();
617
618 if (active.model == null) {
619 throw new IllegalStateException("Post Metadata is not available");
620 }
621
622 return active.viewPostMetadata;
623 }
624
625
626
627
628
629
630
631
632 public static ComponentPostMetadata getRefreshComponentPostMetadata() {
633 ViewLifecycle active = getActiveLifecycle();
634
635 if (active == null) {
636 throw new IllegalStateException("No lifecycle is active");
637 }
638
639 return active.refreshComponentPostMetadata;
640 }
641
642
643
644
645
646
647 public static boolean isRefreshLifecycle() {
648 ViewLifecycle active = getActiveLifecycle();
649
650 if (active == null) {
651 throw new IllegalStateException("No lifecycle is active");
652 }
653
654 return (active.refreshComponentPostMetadata != null);
655 }
656
657
658
659
660
661
662 public static HttpServletRequest getRequest() {
663 ViewLifecycle active = getActiveLifecycle();
664
665 if (active.model == null) {
666 throw new IllegalStateException("Request is not available");
667 }
668
669 return active.request;
670 }
671
672
673
674
675
676
677 public static ViewLifecyclePhase getPhase() {
678 ViewLifecycleProcessor processor = PROCESSOR.get();
679 try {
680 return processor == null ? null : processor.getActivePhase();
681 } catch (IllegalStateException e) {
682 if (LOG.isDebugEnabled()) {
683 LOG.debug("No lifecycle phase is active on the current processor", e);
684 }
685 return null;
686 }
687 }
688
689
690
691
692
693
694 public static LifecycleRenderingContext getRenderingContext() {
695 ViewLifecycleProcessor processor = PROCESSOR.get();
696 return processor == null ? null : processor.getRenderingContext();
697 }
698
699
700
701
702
703
704 public static ViewLifecycleProcessor getProcessor() {
705 ViewLifecycleProcessor processor = PROCESSOR.get();
706
707 if (processor == null) {
708 throw new IllegalStateException("No view lifecycle is active on this thread");
709 }
710
711 return processor;
712 }
713
714
715
716
717
718
719
720
721
722
723
724
725
726 static void setProcessor(ViewLifecycleProcessor processor) {
727 ViewLifecycleProcessor active = PROCESSOR.get();
728
729 if (active != null && processor != null) {
730 throw new IllegalStateException("Another lifecycle processor is already active on this thread");
731 }
732
733 if (processor == null) {
734 PROCESSOR.remove();
735 } else {
736 PROCESSOR.set(processor);
737 }
738 }
739
740
741
742
743
744
745 public static ViewLifecycle getActiveLifecycle() {
746 return getProcessor().getLifecycle();
747 }
748
749
750
751
752
753
754 public static boolean isActive() {
755 return PROCESSOR.get() != null;
756 }
757
758
759
760
761 public static String getRefreshComponentId() {
762 return refreshComponentId;
763 }
764
765
766
767
768 public static void setRefreshComponentId(String id) {
769 refreshComponentId = id;
770 }
771
772
773
774
775 public static enum LifecycleEvent {
776
777
778 LIFECYCLE_COMPLETE
779 }
780
781
782
783
784 protected class EventRegistration implements Serializable {
785
786 private static final long serialVersionUID = -5077429381388641016L;
787
788
789 private LifecycleEvent event;
790
791
792 private Component eventComponent;
793
794
795 private LifecycleEventListener eventListener;
796
797
798
799
800
801
802
803
804 private EventRegistration(LifecycleEvent event, Component eventComponent,
805 LifecycleEventListener eventListener) {
806 this.event = event;
807 this.eventComponent = eventComponent;
808 this.eventListener = eventListener;
809 }
810
811
812
813
814
815
816
817
818 public LifecycleEvent getEvent() {
819 return event;
820 }
821
822
823
824
825
826
827 public Component getEventComponent() {
828 return eventComponent;
829 }
830
831
832
833
834
835
836 public LifecycleEventListener getEventListener() {
837 return eventListener;
838 }
839
840
841
842
843
844
845
846 public void setEvent(LifecycleEvent event) {
847 this.event = event;
848 }
849
850
851
852
853
854
855
856 public void setEventComponent(Component eventComponent) {
857 this.eventComponent = eventComponent;
858 }
859
860
861
862
863
864
865
866 public void setEventListener(LifecycleEventListener eventListener) {
867 this.eventListener = eventListener;
868 }
869 }
870 }