View Javadoc
1   /**
2    * Copyright 2005-2014 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.rice.krad.uif.lifecycle;
17  
18  import java.util.Deque;
19  import java.util.HashMap;
20  import java.util.LinkedHashSet;
21  import java.util.LinkedList;
22  import java.util.Map;
23  import java.util.Set;
24  
25  import org.apache.log4j.Logger;
26  import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
27  import org.kuali.rice.krad.uif.component.Component;
28  import org.kuali.rice.krad.uif.freemarker.LifecycleRenderingContext;
29  import org.kuali.rice.krad.uif.util.LifecycleElement;
30  import org.kuali.rice.krad.uif.util.ProcessLogger;
31  import org.kuali.rice.krad.uif.view.DefaultExpressionEvaluator;
32  import org.kuali.rice.krad.uif.view.ExpressionEvaluator;
33  import org.kuali.rice.krad.uif.view.ExpressionEvaluatorFactory;
34  import org.springframework.util.StringUtils;
35  
36  /**
37   * Single-threaded view lifecycle processor implementation.
38   * 
39   * @author Kuali Rice Team (rice.collab@kuali.org)
40   */
41  public class SynchronousViewLifecycleProcessor extends ViewLifecycleProcessorBase {
42      private static final Logger LOG = Logger.getLogger(SynchronousViewLifecycleProcessor.class);
43  
44      // pending lifecycle phases.
45      private final Deque<ViewLifecyclePhase> pendingPhases = new LinkedList<ViewLifecyclePhase>();
46  
47      // the phase currently active on this lifecycle.
48      private ViewLifecyclePhase activePhase;
49  
50      // the rendering context.
51      private LifecycleRenderingContext renderingContext;
52  
53      // the expression evaluator to use with this lifecycle.
54      private final ExpressionEvaluator expressionEvaluator;
55  
56      private static String getTracePath(ViewLifecyclePhase phase) {
57          Component parent = phase.getParent();
58          if (parent == null) {
59              return "";
60          } else {
61              return phase.getParent().getViewPath();
62          }
63      }
64      
65      private static final class TraceNode {
66          private final String path;
67          private StringBuilder buffer = new StringBuilder();
68          private Set<String> childPaths = new LinkedHashSet<String>();
69  
70          private TraceNode(ViewLifecyclePhase phase) {
71              path = getTracePath(phase);
72          }
73          
74          private void startTrace(ViewLifecyclePhase phase) {
75              try {
76                  LifecycleElement element = phase.getElement();
77                  
78                  String parentPath = phase.getParentPath();
79                  if (StringUtils.hasLength(parentPath)) {
80                      childPaths.add(phase.getParentPath());
81                  }
82                  
83                  buffer.append('\n');
84                  for (int i = 0; i < phase.getDepth(); i++) {
85                      buffer.append("  ");
86                  }
87                  buffer.append(phase.getViewPath());
88                  buffer.append(' ');
89                  buffer.append(phase.getEndViewStatus());
90                  buffer.append(' ');
91                  buffer.append(element.getViewStatus());
92                  buffer.append(' ');
93                  buffer.append(element.isRender());
94                  buffer.append(' ');
95                  buffer.append(element.getClass().getSimpleName());
96                  buffer.append(' ');
97                  buffer.append(element.getId());
98                  buffer.append(' ');
99              } catch (Throwable e) {
100                 LOG.warn("Error tracing lifecycle", e);
101             }
102         }
103         
104         private void finishTrace(long phaseStartTime, Throwable error) {
105             if (error == null) {
106                 buffer.append(" done ");
107             } else {
108                 buffer.append(" ERROR ");
109             }
110             buffer.append(ProcessLogger.intervalToString(System.currentTimeMillis() - phaseStartTime));
111         }
112     }
113 
114     private final Map<String, TraceNode> trace = ViewLifecycle.isTrace() ? new HashMap<String, TraceNode>() : null;
115     
116     private TraceNode getTraceNode(ViewLifecyclePhase phase) {
117         if (trace == null) {
118             return null;
119         }
120         
121         String tracePath = getTracePath(phase);
122         TraceNode traceNode = trace.get(tracePath);
123         
124         if (traceNode == null) {
125             traceNode = new TraceNode(phase);
126             trace.put(tracePath, traceNode);
127         }
128         
129         return traceNode;
130     }
131 
132     /**
133      * Creates a new synchronous processor for a lifecycle.
134      * 
135      * @param lifecycle The lifecycle to process.
136      */
137     public SynchronousViewLifecycleProcessor(ViewLifecycle lifecycle) {
138         super(lifecycle);
139 
140         // The null conditions noted here should not happen in full configured environments
141         // Conditional fallback support is in place primary for unit testing.
142         ExpressionEvaluatorFactory expressionEvaluatorFactory;
143         if (lifecycle.helper == null) {
144             LOG.warn("No helper is defined for the view lifecycle, using global expression evaluation factory");
145             expressionEvaluatorFactory = KRADServiceLocatorWeb.getExpressionEvaluatorFactory();
146         } else {
147             expressionEvaluatorFactory = lifecycle.helper.getExpressionEvaluatorFactory();
148         }
149 
150         if (expressionEvaluatorFactory == null) {
151             LOG.warn("No global expression evaluation factory is defined, using DefaultExpressionEvaluator");
152             expressionEvaluator = new DefaultExpressionEvaluator();
153         } else {
154             expressionEvaluator = expressionEvaluatorFactory.createExpressionEvaluator();
155         }
156     }
157 
158     /**
159      * {@inheritDoc}
160      */
161     @Override
162     public void offerPendingPhase(ViewLifecyclePhase pendingPhase) {
163         pendingPhases.offer(pendingPhase);
164     }
165 
166     /**
167      * {@inheritDoc}
168      */
169     @Override
170     public void pushPendingPhase(ViewLifecyclePhase phase) {
171         pendingPhases.push(phase);
172     }
173 
174     /**
175      * {@inheritDoc}
176      */
177     @Override
178     public void performPhase(ViewLifecyclePhase initialPhase) {
179         long startTime = System.currentTimeMillis();
180         TraceNode initialNode = getTraceNode(initialPhase);
181 
182         offerPendingPhase(initialPhase);
183         while (!pendingPhases.isEmpty()) {
184             ViewLifecyclePhase pendingPhase = pendingPhases.poll();
185             long phaseStartTime = System.currentTimeMillis();
186 
187             try {
188                 if (trace != null) {
189                     getTraceNode(pendingPhase).startTrace(pendingPhase);
190                 }
191 
192                 pendingPhase.run();
193 
194                 if (trace != null) {
195                     getTraceNode(pendingPhase).finishTrace(phaseStartTime, null);
196                 }
197 
198             } catch (Throwable e) {
199                 if (trace != null) {
200                     getTraceNode(pendingPhase).finishTrace(phaseStartTime, e);
201                 }
202 
203                 if (e instanceof RuntimeException) {
204                     throw (RuntimeException) e;
205                 } else if (e instanceof Error) {
206                     throw (Error) e;
207                 } else {
208                     throw new IllegalStateException(e);
209                 }
210             }
211         }
212 
213         if (trace != null) {
214             assert initialNode != null : initialPhase;
215             Deque<TraceNode> msgQueue = new LinkedList<TraceNode>();
216             StringBuilder msg = new StringBuilder();
217 
218             msgQueue.push(initialNode);
219             while (!msgQueue.isEmpty()) {
220                 TraceNode traceNode = msgQueue.pop();
221                 assert traceNode != null : msg + " " + trace.keySet();
222                 assert traceNode.buffer != null : traceNode.path;
223 
224                 msg.append(traceNode.buffer);
225 
226                 for (String childPath : traceNode.childPaths) {
227                     TraceNode child = trace.get(traceNode.path + (traceNode.path.equals("") ? "" : ".") + childPath);
228                     if (child != null) {
229                         msgQueue.push(child);
230                     }
231                 }
232             }
233 
234             LOG.info("Lifecycle phase processing completed in "
235                     + ProcessLogger.intervalToString(System.currentTimeMillis() - startTime) + msg);
236             trace.clear();
237         }
238     }
239 
240     /**
241      * {@inheritDoc}
242      */
243     @Override
244     public ViewLifecyclePhase getActivePhase() {
245         return activePhase;
246     }
247 
248     /**
249      * {@inheritDoc}
250      */
251     public LifecycleRenderingContext getRenderingContext() {
252         if (renderingContext == null && ViewLifecycle.isRenderInLifecycle()) {
253             ViewLifecycle lifecycle = getLifecycle();
254             this.renderingContext = new LifecycleRenderingContext(lifecycle.model, lifecycle.request);
255         }
256 
257         return this.renderingContext;
258     }
259 
260     /**
261      * {@inheritDoc}
262      */
263     @Override
264     public ExpressionEvaluator getExpressionEvaluator() {
265         return this.expressionEvaluator;
266     }
267 
268     /**
269      * {@inheritDoc}
270      */
271     @Override
272     void setActivePhase(ViewLifecyclePhase phase) {
273         if (activePhase != null && phase != null) {
274             throw new IllegalStateException("Another phase is already active on this lifecycle thread " + activePhase);
275         }
276 
277         activePhase = phase;
278     }
279 
280 }