001: /*
002: * Copyright (c) 1998-2008 Caucho Technology -- all rights reserved
003: *
004: * This file is part of Resin(R) Open Source
005: *
006: * Each copy or derived work must preserve the copyright notice and this
007: * notice unmodified.
008: *
009: * Resin Open Source is free software; you can redistribute it and/or modify
010: * it under the terms of the GNU General Public License version 2
011: * as published by the Free Software Foundation.
012: *
013: * Resin Open Source is distributed in the hope that it will be useful,
014: * but WITHOUT ANY WARRANTY; without even the implied warranty of
015: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
016: * of NON-INFRINGEMENT. See the GNU General Public License for more
017: * details.
018: *
019: * You should have received a copy of the GNU General Public License
020: * along with Resin Open Source; if not, write to the
021: *
022: * Free Software Foundation, Inc.
023: * 59 Temple Place, Suite 330
024: * Boston, MA 02111-1307 USA
025: *
026: * @author Scott Ferguson
027: */
028:
029: package com.caucho.jsf.lifecycle;
030:
031: import com.caucho.util.*;
032:
033: import java.io.*;
034: import java.util.*;
035: import java.util.logging.*;
036: import java.lang.reflect.*;
037:
038: import javax.el.*;
039: import javax.faces.*;
040: import javax.faces.application.*;
041: import javax.faces.component.*;
042: import javax.faces.context.*;
043: import javax.faces.event.*;
044: import javax.faces.lifecycle.*;
045: import javax.faces.render.*;
046:
047: import javax.servlet.http.*;
048:
049: import com.caucho.config.*;
050: import com.caucho.jsf.application.*;
051: import com.caucho.util.*;
052:
053: /**
054: * The default lifecycle implementation
055: */
056: public class LifecycleImpl extends Lifecycle {
057: private static final L10N L = new L10N(LifecycleImpl.class);
058: private static final Logger log = Logger
059: .getLogger(LifecycleImpl.class.getName());
060:
061: private ArrayList<PhaseListener> _phaseList = new ArrayList<PhaseListener>();
062: private PhaseListener[] _phaseListeners = new PhaseListener[0];
063:
064: public LifecycleImpl() {
065: }
066:
067: public void addPhaseListener(PhaseListener listener) {
068: if (listener == null)
069: throw new NullPointerException();
070:
071: synchronized (_phaseList) {
072: _phaseList.add(listener);
073: _phaseListeners = new PhaseListener[_phaseList.size()];
074: _phaseList.toArray(_phaseListeners);
075: }
076: }
077:
078: public PhaseListener[] getPhaseListeners() {
079: return _phaseListeners;
080: }
081:
082: public void removePhaseListener(PhaseListener listener) {
083: if (listener == null)
084: throw new NullPointerException();
085:
086: synchronized (_phaseList) {
087: _phaseList.remove(listener);
088: _phaseListeners = new PhaseListener[_phaseList.size()];
089: _phaseList.toArray(_phaseListeners);
090: }
091: }
092:
093: public void execute(FacesContext context) throws FacesException {
094: boolean isFiner = log.isLoggable(Level.FINER);
095:
096: if (context.getResponseComplete()
097: || context.getRenderResponse())
098: return;
099:
100: beforePhase(context, PhaseId.RESTORE_VIEW);
101:
102: try {
103: if (isFiner)
104: log.finer("JSF[] before restore view");
105:
106: restoreView(context);
107: } finally {
108: afterPhase(context, PhaseId.RESTORE_VIEW);
109: }
110:
111: if (context.getResponseComplete()
112: || context.getRenderResponse())
113: return;
114:
115: UIViewRoot viewRoot = context.getViewRoot();
116:
117: beforePhase(context, PhaseId.APPLY_REQUEST_VALUES);
118:
119: try {
120: if (isFiner)
121: log.finer(context.getViewRoot()
122: + " before process decodes");
123:
124: viewRoot.processDecodes(context);
125: } finally {
126: afterPhase(context, PhaseId.APPLY_REQUEST_VALUES);
127: }
128:
129: //
130: // Process Validations (processValidators)
131: //
132:
133: if (context.getResponseComplete()
134: || context.getRenderResponse())
135: return;
136:
137: beforePhase(context, PhaseId.PROCESS_VALIDATIONS);
138:
139: try {
140: if (isFiner)
141: log.finer(context.getViewRoot()
142: + " before process validators");
143:
144: viewRoot.processValidators(context);
145: } finally {
146: afterPhase(context, PhaseId.PROCESS_VALIDATIONS);
147: }
148:
149: //
150: // Update Model Values (processUpdates)
151: //
152:
153: if (context.getResponseComplete()
154: || context.getRenderResponse())
155: return;
156:
157: beforePhase(context, PhaseId.UPDATE_MODEL_VALUES);
158:
159: try {
160: if (isFiner)
161: log.finer(context.getViewRoot()
162: + " before process updates");
163:
164: viewRoot.processUpdates(context);
165: } catch (RuntimeException e) {
166: if (sendError(context, "processUpdates", e))
167: return;
168: } finally {
169: afterPhase(context, PhaseId.UPDATE_MODEL_VALUES);
170: }
171:
172: //
173: // Invoke Application (processApplication)
174: //
175:
176: if (context.getResponseComplete()
177: || context.getRenderResponse())
178: return;
179:
180: beforePhase(context, PhaseId.INVOKE_APPLICATION);
181:
182: try {
183: if (isFiner)
184: log.finer(context.getViewRoot()
185: + " before process application");
186:
187: viewRoot.processApplication(context);
188: } finally {
189: afterPhase(context, PhaseId.INVOKE_APPLICATION);
190: }
191: }
192:
193: private void restoreView(FacesContext context)
194: throws FacesException {
195: Application app = context.getApplication();
196:
197: if (app instanceof ApplicationImpl)
198: ((ApplicationImpl) app).initRequest();
199:
200: ViewHandler view = app.getViewHandler();
201:
202: view.initView(context);
203:
204: UIViewRoot viewRoot = context.getViewRoot();
205:
206: if (viewRoot != null) {
207: ExternalContext extContext = context.getExternalContext();
208:
209: viewRoot.setLocale(extContext.getRequestLocale());
210:
211: doSetBindings(context.getELContext(), viewRoot);
212:
213: return;
214: }
215:
216: String viewId = calculateViewId(context);
217:
218: String renderKitId = view.calculateRenderKitId(context);
219:
220: RenderKitFactory renderKitFactory = (RenderKitFactory) FactoryFinder
221: .getFactory(FactoryFinder.RENDER_KIT_FACTORY);
222:
223: RenderKit renderKit = renderKitFactory.getRenderKit(context,
224: renderKitId);
225:
226: ResponseStateManager stateManager = renderKit
227: .getResponseStateManager();
228:
229: if (stateManager.isPostback(context)) {
230: viewRoot = view.restoreView(context, viewId);
231:
232: if (viewRoot != null) {
233: doSetBindings(context.getELContext(), viewRoot);
234: } else {
235: // XXX: backward compat issues with ViewHandler and StateManager
236:
237: // throw new ViewExpiredException(L.l("{0} is an expired view", viewId));
238:
239: context.renderResponse();
240:
241: viewRoot = view.createView(context, viewId);
242:
243: context.setViewRoot(viewRoot);
244: }
245:
246: context.setViewRoot(viewRoot);
247: } else {
248: context.renderResponse();
249:
250: viewRoot = view.createView(context, viewId);
251:
252: context.setViewRoot(viewRoot);
253: }
254: }
255:
256: private void doSetBindings(ELContext elContext,
257: UIComponent component) {
258: if (component == null)
259: return;
260:
261: ValueExpression binding = component
262: .getValueExpression("binding");
263:
264: if (binding != null)
265: binding.setValue(elContext, component);
266:
267: Iterator<UIComponent> iter = component.getFacetsAndChildren();
268: while (iter.hasNext())
269: doSetBindings(elContext, iter.next());
270: }
271:
272: private String calculateViewId(FacesContext context) {
273: return JspViewHandler.createViewId(context);
274: }
275:
276: public void render(FacesContext context) throws FacesException {
277: if (context.getResponseComplete())
278: return;
279:
280: Application app = context.getApplication();
281: ViewHandler view = app.getViewHandler();
282:
283: UIViewRoot viewRoot = context.getViewRoot();
284:
285: beforePhase(context, PhaseId.RENDER_RESPONSE);
286:
287: try {
288: if (log.isLoggable(Level.FINER))
289: log
290: .finer(context.getViewRoot()
291: + " before render view");
292:
293: view.renderView(context, context.getViewRoot());
294: } catch (java.io.IOException e) {
295: if (sendError(context, "renderView", e))
296: return;
297:
298: throw new FacesException(e);
299: } catch (RuntimeException e) {
300: if (sendError(context, "renderView", e))
301: return;
302:
303: throw e;
304: } finally {
305: afterPhase(context, PhaseId.RENDER_RESPONSE);
306:
307: logMessages(context);
308: }
309: }
310:
311: private void beforePhase(FacesContext context, PhaseId phase) {
312: for (int i = 0; i < _phaseListeners.length; i++) {
313: PhaseListener listener = _phaseListeners[i];
314: PhaseId id = listener.getPhaseId();
315:
316: if (id == phase || id == PhaseId.ANY_PHASE) {
317: PhaseEvent event = new PhaseEvent(context, phase, this );
318:
319: listener.beforePhase(event);
320: }
321: }
322: }
323:
324: private void afterPhase(FacesContext context, PhaseId phase) {
325: for (int i = _phaseListeners.length - 1; i >= 0; i--) {
326: PhaseListener listener = _phaseListeners[i];
327: PhaseId id = listener.getPhaseId();
328:
329: if (phase == id || id == PhaseId.ANY_PHASE) {
330: PhaseEvent event = new PhaseEvent(context, phase, this );
331:
332: listener.afterPhase(event);
333: }
334: }
335: }
336:
337: private void logMessages(FacesContext context) {
338: UIViewRoot root = context.getViewRoot();
339: String viewId = "";
340:
341: if (root != null)
342: viewId = root.getViewId();
343:
344: Iterator<FacesMessage> iter = context.getMessages();
345:
346: while (iter != null && iter.hasNext()) {
347: FacesMessage msg = iter.next();
348:
349: if (log.isLoggable(Level.FINE)) {
350: if (msg.getDetail() != null)
351: log.fine(viewId + " [ " + msg.getSeverity() + "] "
352: + msg.getSummary() + " " + msg.getDetail());
353: else
354: log.fine(viewId + " [ " + msg.getSeverity() + "] "
355: + msg.getSummary());
356: }
357: }
358: }
359:
360: private boolean sendError(FacesContext context, String lifecycle,
361: Exception e) {
362: for (Throwable cause = e; cause != null; cause = cause
363: .getCause()) {
364: if (cause instanceof DisplayableException) {
365: if (e instanceof RuntimeException)
366: throw (RuntimeException) e;
367: else
368: throw new FacesException(e);
369: }
370: }
371:
372: ExternalContext extContext = context.getExternalContext();
373: Object response = extContext.getResponse();
374:
375: if (!(response instanceof HttpServletResponse)) {
376: context.renderResponse();
377:
378: if (e instanceof RuntimeException)
379: throw (RuntimeException) e;
380: else
381: throw new RuntimeException(e);
382: }
383:
384: log.log(Level.WARNING, e.toString(), e);
385:
386: HttpServletResponse res = (HttpServletResponse) response;
387:
388: try {
389: context.renderResponse();
390: context.responseComplete();
391:
392: res.setStatus(500, "JSF Exception");
393: res.setContentType("text/html");
394:
395: PrintWriter out = res.getWriter();
396:
397: out.println("<body>");
398:
399: out.println("<h3>JSF exception detected in " + lifecycle
400: + " phase</h3>");
401:
402: String msg = e.getMessage();
403: out.println("<span style='color:red;font:bold'>"
404: + Html.escapeHtml(msg) + "</span><br/>");
405:
406: out.println("<h3>Context: " + context.getViewRoot()
407: + "</h3>");
408: out.println("<code><pre>");
409:
410: String errorId = null;
411:
412: if (e instanceof FacesException && msg.startsWith("id=")) {
413: int p = msg.indexOf(' ');
414: errorId = msg.substring(3, p);
415: }
416:
417: printComponentTree(out, errorId, context, context
418: .getViewRoot(), 0);
419:
420: out.println("</pre></code>");
421:
422: if (!Alarm.isTest()) {
423: out.println("<h3>Stack Trace</h3>");
424: out.println("<pre>");
425: if (e.getCause() != null)
426: e.getCause().printStackTrace(out);
427: else
428: e.printStackTrace(out);
429: out.println("</pre>");
430: }
431:
432: out.println("</body>");
433:
434: // clear, so we don't just loop
435: Application app = context.getApplication();
436:
437: ViewHandler view = app.getViewHandler();
438:
439: UIViewRoot viewRoot = context.getViewRoot();
440:
441: viewRoot = view.createView(context, viewRoot.getViewId());
442:
443: context.setViewRoot(viewRoot);
444:
445: view.writeState(context);
446:
447: return true;
448: } catch (IOException e1) {
449: throw new RuntimeException(e);
450: }
451: }
452:
453: private void printComponentTree(PrintWriter out, String errorId,
454: FacesContext context, UIComponent comp, int depth) {
455: for (int i = 0; i < depth; i++)
456: out.print(' ');
457:
458: boolean isError = false;
459: if (errorId != null
460: && errorId.equals(comp.getClientId(context))) {
461: isError = true;
462: out.print("<span style='color:red'>");
463: }
464:
465: out.print("<" + comp.getClass().getSimpleName());
466: if (comp.getId() != null)
467: out.print(" id=\"" + comp.getId() + "\"");
468:
469: for (Method method : comp.getClass().getMethods()) {
470: if (!method.getName().startsWith("get")
471: && !method.getName().startsWith("is"))
472: continue;
473: else if (method.getParameterTypes().length != 0)
474: continue;
475:
476: String name;
477:
478: if (method.getName().startsWith("get"))
479: name = method.getName().substring(3);
480: else if (method.getName().startsWith("is"))
481: name = method.getName().substring(2);
482: else
483: continue;
484:
485: // XXX: getURL
486: name = Character.toLowerCase(name.charAt(0))
487: + name.substring(1);
488:
489: ValueExpression expr = comp.getValueExpression(name);
490:
491: Class type = method.getReturnType();
492:
493: if (expr != null) {
494: out.print(" " + name + "=\""
495: + expr.getExpressionString() + "\"");
496: } else if (method.getDeclaringClass().equals(
497: UIComponent.class)
498: || method.getDeclaringClass().equals(
499: UIComponentBase.class)) {
500: } else if (name.equals("family")) {
501: } else if (String.class.equals(type)) {
502: try {
503: Object value = method.invoke(comp);
504:
505: if (value != null)
506: out.print(" " + name + "=\"" + value + "\"");
507: } catch (Exception e) {
508: }
509: }
510: }
511:
512: int facetCount = comp.getFacetCount();
513: int childCount = comp.getChildCount();
514:
515: if (facetCount == 0 && childCount == 0) {
516: out.print("/>");
517:
518: if (isError)
519: out.print("</span>");
520:
521: out.println();
522: return;
523: }
524: out.println(">");
525:
526: if (isError)
527: out.print("</span>");
528:
529: for (int i = 0; i < childCount; i++) {
530: printComponentTree(out, errorId, context, comp
531: .getChildren().get(i), depth + 1);
532: }
533:
534: for (int i = 0; i < depth; i++)
535: out.print(' ');
536:
537: if (isError)
538: out.print("<span style='color:red'>");
539:
540: out.println("</" + comp.getClass().getSimpleName() + ">");
541:
542: if (isError)
543: out.print("</span>");
544: }
545:
546: public String toString() {
547: return "DefaultLifecycleImpl[]";
548: }
549: }
|