Source code: com/sun/facelets/FaceletViewHandler.java
1 /**
2 * Licensed under the Common Development and Distribution License,
3 * you may not use this file except in compliance with the License.
4 * You may obtain a copy of the License at
5 *
6 * http://www.sun.com/cddl/
7 *
8 * Unless required by applicable law or agreed to in writing, software
9 * distributed under the License is distributed on an "AS IS" BASIS,
10 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
11 * implied. See the License for the specific language governing
12 * permissions and limitations under the License.
13 */
14
15 package com.sun.facelets;
16
17 import java.io.FileNotFoundException;
18 import java.io.IOException;
19 import java.io.Writer;
20 import java.net.MalformedURLException;
21 import java.net.URL;
22 import java.util.ArrayList;
23 import java.util.Arrays;
24 import java.util.Iterator;
25 import java.util.List;
26 import java.util.Locale;
27 import java.util.Map;
28 import java.util.logging.Level;
29 import java.util.logging.Logger;
30
31 import javax.el.ELException;
32 import javax.faces.FacesException;
33 import javax.faces.application.StateManager;
34 import javax.faces.application.ViewHandler;
35 import javax.faces.component.UIComponent;
36 import javax.faces.component.UIViewRoot;
37 import javax.faces.context.ExternalContext;
38 import javax.faces.context.FacesContext;
39 import javax.faces.context.ResponseWriter;
40 import javax.faces.render.RenderKit;
41 import javax.portlet.PortletResponse;
42 import javax.servlet.ServletRequest;
43 import javax.servlet.ServletResponse;
44 import javax.servlet.http.HttpServletRequest;
45 import javax.servlet.http.HttpServletResponse;
46
47 import com.sun.facelets.compiler.Compiler;
48 import com.sun.facelets.compiler.SAXCompiler;
49 import com.sun.facelets.compiler.TagLibraryConfig;
50 import com.sun.facelets.impl.DefaultFaceletFactory;
51 import com.sun.facelets.impl.DefaultResourceResolver;
52 import com.sun.facelets.impl.ResourceResolver;
53 import com.sun.facelets.tag.TagDecorator;
54 import com.sun.facelets.tag.TagLibrary;
55 import com.sun.facelets.tag.jsf.ComponentSupport;
56 import com.sun.facelets.tag.ui.UIDebug;
57 import com.sun.facelets.util.DevTools;
58 import com.sun.facelets.util.FacesAPI;
59 import com.sun.facelets.util.FastWriter;
60 import com.sun.facelets.util.Resource;
61
62 /**
63 * ViewHandler implementation for Facelets
64 *
65 * @author Jacob Hookom
66 * @version $Id: FaceletViewHandler.java,v 1.49.2.6 2006/03/20 07:22:00 jhook
67 * Exp $
68 */
69 public class FaceletViewHandler extends ViewHandler {
70
71 protected final static Logger log = Logger
72 .getLogger("facelets.viewhandler");
73
74 public final static long DEFAULT_REFRESH_PERIOD = 2;
75
76 public final static String PARAM_REFRESH_PERIO = "facelets.REFRESH_PERIOD";
77
78 public final static String PARAM_SKIP_COMMENTS = "facelets.SKIP_COMMENTS";
79
80 /**
81 * Context initialization parameter for defining what viewIds should be
82 * handled by Facelets, and what should not. When left unset, all URLs will
83 * be handled by Facelets. When set, it must be a semicolon separated list
84 * of either extension mappings or prefix mappings. For example:
85 *
86 * <pre>
87 *
88 *
89 *
90 * <context-param>
91 * <param-name>facelets.VIEW_MAPPINGS</param-name>
92 * <param-value>/demos/*; *.xhtml</param-value>
93 * </context-param>
94 *
95 *
96 *
97 * </pre>
98 *
99 * would use Facelets for processing all viewIds in the "/demos" directory
100 * or that end in .xhtml, and use the standard JSP engine for all other
101 * viewIds.
102 * <p>
103 * <strong>NOTE</strong>: when using this parameter, you need to use
104 * prefix-mapping for the <code>FacesServlet</code> (that is,
105 * <code>/faces/*</code>, not <code>*.jsf</code>).
106 * </p>
107 */
108 public final static String PARAM_VIEW_MAPPINGS = "facelets.VIEW_MAPPINGS";
109
110 public final static String PARAM_LIBRARIES = "facelets.LIBRARIES";
111
112 public final static String PARAM_DECORATORS = "facelets.DECORATORS";
113
114 public final static String PARAM_DEVELOPMENT = "facelets.DEVELOPMENT";
115
116 public final static String PARAM_RESOURCE_RESOLVER = "facelets.RESOURCE_RESOLVER";
117
118 public final static String PARAM_BUILD_BEFORE_RESTORE = "facelets.BUILD_BEFORE_RESTORE";
119
120 public final static String PARAM_BUFFER_SIZE = "facelets.BUFFER_SIZE";
121
122 private final static String STATE_KEY = "~facelets.VIEW_STATE~";
123
124 private final static int STATE_KEY_LEN = STATE_KEY.length();
125
126 private final static Object STATE_NULL = new Object();
127
128 private final ViewHandler parent;
129
130 private boolean developmentMode = false;
131
132 private boolean buildBeforeRestore = false;
133
134 private int bufferSize;
135
136 private String defaultSuffix;
137
138 private FaceletFactory faceletFactory;
139
140 // Array of viewId extensions that should be handled by Facelets
141 private String[] extensionsArray;
142
143 // Array of viewId prefixes that should be handled by Facelets
144 private String[] prefixesArray;
145
146 /**
147 *
148 */
149 public FaceletViewHandler(ViewHandler parent) {
150 this.parent = parent;
151 }
152
153 /**
154 * Initialize the ViewHandler during its first request.
155 */
156 protected void initialize(FacesContext context) {
157 synchronized (this) {
158 if (this.faceletFactory == null) {
159 log.fine("Initializing");
160 Compiler c = this.createCompiler();
161 this.initializeCompiler(c);
162 this.faceletFactory = this.createFaceletFactory(c);
163
164 this.initializeMappings(context);
165 this.initializeMode(context);
166 this.initializeBuffer(context);
167
168 log.fine("Initialization Successful");
169 }
170 }
171 }
172
173 private void initializeMode(FacesContext context) {
174 ExternalContext external = context.getExternalContext();
175 String param = external.getInitParameter(PARAM_DEVELOPMENT);
176 this.developmentMode = "true".equals(param);
177
178 String restoreMode = external
179 .getInitParameter(PARAM_BUILD_BEFORE_RESTORE);
180 this.buildBeforeRestore = "true".equals(restoreMode);
181 }
182
183 private void initializeBuffer(FacesContext context) {
184 ExternalContext external = context.getExternalContext();
185 String param = external.getInitParameter(PARAM_BUFFER_SIZE);
186 this.bufferSize = (param != null && !"".equals(param)) ? Integer
187 .parseInt(param) : -1;
188 }
189
190 /**
191 * Initialize mappings, during the first request.
192 */
193 private void initializeMappings(FacesContext context) {
194 ExternalContext external = context.getExternalContext();
195 String viewMappings = external.getInitParameter(PARAM_VIEW_MAPPINGS);
196 if ((viewMappings != null) && (viewMappings.length() > 0)) {
197 String[] mappingsArray = viewMappings.split(";");
198
199 List extensionsList = new ArrayList(mappingsArray.length);
200 List prefixesList = new ArrayList(mappingsArray.length);
201
202 for (int i = 0; i < mappingsArray.length; i++) {
203 String mapping = mappingsArray[i].trim();
204 int mappingLength = mapping.length();
205 if (mappingLength <= 1) {
206 continue;
207 }
208
209 if (mapping.charAt(0) == '*') {
210 extensionsList.add(mapping.substring(1));
211 } else if (mapping.charAt(mappingLength - 1) == '*') {
212 prefixesList.add(mapping.substring(0, mappingLength - 1));
213 }
214 }
215
216 extensionsArray = new String[extensionsList.size()];
217 extensionsList.toArray(extensionsArray);
218
219 prefixesArray = new String[prefixesList.size()];
220 prefixesList.toArray(prefixesArray);
221 }
222 }
223
224 protected FaceletFactory createFaceletFactory(Compiler c) {
225
226 // refresh period
227 long refreshPeriod = DEFAULT_REFRESH_PERIOD;
228 FacesContext ctx = FacesContext.getCurrentInstance();
229 String userPeriod = ctx.getExternalContext().getInitParameter(
230 PARAM_REFRESH_PERIO);
231 if (userPeriod != null && userPeriod.length() > 0) {
232 refreshPeriod = Long.parseLong(userPeriod);
233 }
234
235 // resource resolver
236 ResourceResolver resolver = new DefaultResourceResolver();
237 String resolverName = ctx.getExternalContext().getInitParameter(
238 PARAM_RESOURCE_RESOLVER);
239 if (resolverName != null && resolverName.length() > 0) {
240 try {
241 resolver = (ResourceResolver) Class.forName(resolverName, true,
242 Thread.currentThread().getContextClassLoader())
243 .newInstance();
244 } catch (Exception e) {
245 throw new FacesException("Error Initializing ResourceResolver["
246 + resolverName + "]", e);
247 }
248 }
249
250 // Resource.getResourceUrl(ctx,"/")
251 return new DefaultFaceletFactory(c, resolver, refreshPeriod);
252 }
253
254 protected Compiler createCompiler() {
255 return new SAXCompiler();
256 }
257
258 protected void initializeCompiler(Compiler c) {
259 FacesContext ctx = FacesContext.getCurrentInstance();
260 ExternalContext ext = ctx.getExternalContext();
261
262 // load libraries
263 String libParam = ext.getInitParameter(PARAM_LIBRARIES);
264 if (libParam != null) {
265 libParam = libParam.trim();
266 String[] libs = libParam.split(";");
267 URL src;
268 TagLibrary libObj;
269 for (int i = 0; i < libs.length; i++) {
270 try {
271 src = ext.getResource(libs[i].trim());
272 if (src == null) {
273 throw new FileNotFoundException(libs[i]);
274 }
275 libObj = TagLibraryConfig.create(src);
276 c.addTagLibrary(libObj);
277 log.fine("Successfully Loaded Library: " + libs[i]);
278 } catch (IOException e) {
279 log.log(Level.SEVERE, "Error Loading Library: " + libs[i],
280 e);
281 }
282 }
283 }
284
285 // load decorators
286 String decParam = ext.getInitParameter(PARAM_DECORATORS);
287 if (decParam != null) {
288 decParam = decParam.trim();
289 String[] decs = decParam.split(";");
290 TagDecorator decObj;
291 for (int i = 0; i < decs.length; i++) {
292 try {
293 decObj = (TagDecorator) Class.forName(decs[i])
294 .newInstance();
295 c.addTagDecorator(decObj);
296 log.fine("Successfully Loaded Decorator: " + decs[i]);
297 } catch (Exception e) {
298 log.log(Level.SEVERE,
299 "Error Loading Decorator: " + decs[i], e);
300 }
301 }
302 }
303
304 // skip params?
305 String skipParam = ext.getInitParameter(PARAM_SKIP_COMMENTS);
306 if (skipParam != null && "true".equals(skipParam)) {
307 c.setTrimmingComments(true);
308 }
309 }
310
311 public UIViewRoot restoreView(FacesContext context, String viewId) {
312 if (UIDebug.debugRequest(context)) {
313 return new UIViewRoot();
314 }
315
316 if (!this.buildBeforeRestore || !handledByFacelets(viewId)) {
317 return this.parent.restoreView(context, viewId);
318 }
319
320 if (this.faceletFactory == null) {
321 this.initialize(context);
322 }
323
324 // In JSF 1.2, restoreView() will only be called on postback.
325 // But in JSF 1.1, it will be called for an initial request too,
326 // in which case we must return null in order to fall through
327 // to createView()
328 if (FacesAPI.getVersion() < 12) {
329 if (!isPostback11(context)) {
330 return null;
331 }
332 }
333
334 ViewHandler outerViewHandler = context.getApplication()
335 .getViewHandler();
336 String renderKitId = outerViewHandler.calculateRenderKitId(context);
337
338 UIViewRoot viewRoot = createView(context, viewId);
339 context.setViewRoot(viewRoot);
340 try {
341 this.buildView(context, viewRoot);
342 } catch (IOException ioe) {
343 log.log(Level.SEVERE, "Error Building View", ioe);
344 }
345 context.getApplication().getStateManager().restoreView(context, viewId,
346 renderKitId);
347 return viewRoot;
348 }
349
350 /*
351 * (non-Javadoc)
352 *
353 * @see javax.faces.application.ViewHandlerWrapper#getWrapped()
354 */
355 protected ViewHandler getWrapped() {
356 return this.parent;
357 }
358
359 protected ResponseWriter createResponseWriter(FacesContext context)
360 throws IOException, FacesException {
361 ExternalContext extContext = context.getExternalContext();
362 RenderKit renderKit = context.getRenderKit();
363 // Avoid a cryptic NullPointerException when the renderkit ID
364 // is incorrectly set
365 if (renderKit == null) {
366 String id = context.getViewRoot().getRenderKitId();
367 throw new IllegalStateException(
368 "No render kit was available for id \"" + id + "\"");
369 }
370
371 ServletResponse response = (ServletResponse) extContext.getResponse();
372
373 // set the buffer for content
374 if (this.bufferSize != -1) {
375 response.setBufferSize(this.bufferSize);
376 }
377
378 // get our content type
379 String contentType = null;
380
381 // get the encoding
382 String encoding = null;
383
384 // Create a dummy ResponseWriter with a bogus writer,
385 // so we can figure out what content type the ReponseWriter
386 // is really going to ask for
387 ResponseWriter writer = renderKit.createResponseWriter(
388 NullWriter.Instance, contentType, encoding);
389
390 contentType = getResponseContentType(context, writer.getContentType());
391 encoding = getResponseEncoding(context, writer.getCharacterEncoding());
392
393 // apply them to the response
394 response.setContentType(contentType + "; charset=" + encoding);
395
396 // removed 2005.8.23 to comply with J2EE 1.3
397 // response.setCharacterEncoding(encoding);
398
399 // Now, clone with the real writer
400 writer = writer.cloneWithWriter(response.getWriter());
401
402 return writer;
403 }
404
405 /**
406 * Generate the encoding
407 *
408 * @param context
409 * @param orig
410 * @return
411 */
412 protected String getResponseEncoding(FacesContext context, String orig) {
413 String encoding = orig;
414
415 // see if we need to override the encoding
416 Map m = context.getExternalContext().getRequestMap();
417 Map sm = context.getExternalContext().getSessionMap();
418
419 // 1. check the request attribute
420 if (m.containsKey("facelets.Encoding")) {
421 encoding = (String) m.get("facelets.Encoding");
422 if (log.isLoggable(Level.FINEST)) {
423 log.finest("Facelet specified alternate encoding '" + encoding
424 + "'");
425 }
426 sm.put(CHARACTER_ENCODING_KEY, encoding);
427 }
428
429 // 2. get it from request
430 Object request = context.getExternalContext().getRequest();
431 if (encoding == null && request instanceof ServletRequest) {
432 encoding = ((ServletRequest) request).getCharacterEncoding();
433 }
434
435 // 3. get it from the session
436 if (encoding == null) {
437 encoding = (String) sm.get(CHARACTER_ENCODING_KEY);
438 if (log.isLoggable(Level.FINEST)) {
439 log.finest("Session specified alternate encoding '" + encoding
440 + "'");
441 }
442 }
443
444 // 4. default it
445 if (encoding == null) {
446 encoding = "UTF-8";
447 if (log.isLoggable(Level.FINEST)) {
448 log
449 .finest("ResponseWriter created had a null CharacterEncoding, defaulting to UTF-8");
450 }
451 }
452
453 return encoding;
454 }
455
456 /**
457 * Generate the content type
458 *
459 * @param context
460 * @param orig
461 * @return
462 */
463 protected String getResponseContentType(FacesContext context, String orig) {
464 String contentType = orig;
465
466 // see if we need to override the contentType
467 Map m = context.getExternalContext().getRequestMap();
468 if (m.containsKey("facelets.ContentType")) {
469 contentType = (String) m.get("facelets.ContentType");
470 if (log.isLoggable(Level.FINEST)) {
471 log.finest("Facelet specified alternate contentType '"
472 + contentType + "'");
473 }
474 }
475
476 // safety check
477 if (contentType == null) {
478 contentType = "text/html";
479 if (log.isLoggable(Level.FINEST)) {
480 log
481 .finest("ResponseWriter created had a null ContentType, defaulting to text/html");
482 }
483 }
484
485 return contentType;
486 }
487
488 protected void buildView(FacesContext context, UIViewRoot viewToRender)
489 throws IOException, FacesException {
490 // setup our viewId
491 String renderedViewId = this.getRenderedViewId(context, viewToRender
492 .getViewId());
493 viewToRender.setViewId(renderedViewId);
494
495 if (log.isLoggable(Level.FINE)) {
496 log.fine("Building View: " + renderedViewId);
497 }
498
499 // grab our FaceletFactory and create a Facelet
500 Facelet f = null;
501 FaceletFactory.setInstance(this.faceletFactory);
502 try {
503 f = this.faceletFactory.getFacelet(viewToRender.getViewId());
504 } finally {
505 FaceletFactory.setInstance(null);
506 }
507
508 // populate UIViewRoot
509 long time = System.currentTimeMillis();
510 f.apply(context, viewToRender);
511 time = System.currentTimeMillis() - time;
512 if (log.isLoggable(Level.FINE)) {
513 log.fine("Took " + time + "ms to build view: "
514 + viewToRender.getViewId());
515 }
516 }
517
518 public void renderView(FacesContext context, UIViewRoot viewToRender)
519 throws IOException, FacesException {
520
521 // lazy initialize so we have a FacesContext to use
522 if (this.faceletFactory == null) {
523 this.initialize(context);
524 }
525
526 // exit if the view is not to be rendered
527 if (!viewToRender.isRendered()) {
528 return;
529 }
530
531 // if facelets is not supposed to handle this request
532 if (!handledByFacelets(viewToRender.getViewId())) {
533 this.parent.renderView(context, viewToRender);
534 return;
535 }
536
537 // log request
538 if (log.isLoggable(Level.FINE)) {
539 log.fine("Rendering View: " + viewToRender.getViewId());
540 }
541
542 StateWriter stateWriter = null;
543 try {
544 // build view - but not if we're in "buildBeforeRestore"
545 // land and we've already got a populated view. Note
546 // that this optimizations breaks if there's a "c:if" in
547 // the page that toggles as a result of request processing -
548 // should that be handled? Or
549 // is this optimization simply so minor that it should just
550 // be trimmed altogether?
551 if (!this.buildBeforeRestore
552 || viewToRender.getChildren().isEmpty()) {
553 this.buildView(context, viewToRender);
554 }
555
556 // setup writer and assign it to the context
557 ResponseWriter origWriter = this.createResponseWriter(context);
558 // QUESTION: should we use bufferSize? Or, since the
559 // StateWriter usually only needs a small bit at the end,
560 // should we always use a much smaller size?
561 stateWriter = new StateWriter(origWriter,
562 this.bufferSize != -1 ? this.bufferSize : 1024);
563
564 ResponseWriter writer = origWriter.cloneWithWriter(stateWriter);
565 context.setResponseWriter(writer);
566
567 // force creation of session if saving state there
568 StateManager stateMgr = context.getApplication().getStateManager();
569 if (!stateMgr.isSavingStateInClient(context)) {
570 context.getExternalContext().getSession(true);
571 }
572
573 long time = System.currentTimeMillis();
574
575 // render the view to the response
576 writer.startDocument();
577 if (FacesAPI.getVersion() >= 12) {
578 viewToRender.encodeAll(context);
579 } else {
580 ComponentSupport.encodeRecursive(context, viewToRender);
581 }
582 writer.endDocument();
583
584 // finish writing
585 writer.close();
586
587 // remove transients for older versions
588 if (FacesAPI.getVersion() < 12) {
589 ComponentSupport.removeTransient(viewToRender);
590 }
591
592 boolean writtenState = stateWriter.isStateWritten();
593 // flush to origWriter
594 if (writtenState) {
595 String content = stateWriter.getAndResetBuffer();
596 int end = content.indexOf(STATE_KEY);
597 // See if we can find any trace of the saved state.
598 // If so, we need to perform token replacement
599 if (end >= 0) {
600 // save state
601 Object stateObj = stateMgr.saveSerializedView(context);
602 String stateStr;
603 if (stateObj == null) {
604 stateStr = null;
605 } else {
606 stateMgr.writeState(context,
607 (StateManager.SerializedView) stateObj);
608 stateStr = stateWriter.getAndResetBuffer();
609 }
610
611 int start = 0;
612
613 while (end != -1) {
614 origWriter.write(content, start, end - start);
615 if (stateStr != null) {
616 origWriter.write(stateStr);
617 }
618 start = end + STATE_KEY_LEN;
619 end = content.indexOf(STATE_KEY, start);
620 }
621 origWriter.write(content, start, content.length() - start);
622 // No trace of any saved state, so we just need to flush
623 // the buffer
624 } else {
625 origWriter.write(content);
626 // But for JSF 1.1 (actually, just 1.1_01 RI), if we didn't
627 // detect any saved state, force a call to
628 // saveSerializedView() in case we're using the old
629 // pure-server-side state saving
630 if ((FacesAPI.getVersion() < 12)
631 && !stateMgr.isSavingStateInClient(context)) {
632 stateMgr.saveSerializedView(context);
633 }
634 }
635 }
636
637 time = System.currentTimeMillis() - time;
638 if (log.isLoggable(Level.FINE)) {
639 log.fine("Took " + time + "ms to render view: "
640 + viewToRender.getViewId());
641 }
642
643 } catch (FileNotFoundException fnfe) {
644 this.handleFaceletNotFound(context, viewToRender.getViewId());
645 } catch (Exception e) {
646 this.handleRenderException(context, e);
647 } finally {
648 if (stateWriter != null)
649 stateWriter.release();
650 }
651 }
652
653 protected void handleRenderException(FacesContext context, Exception e)
654 throws IOException, ELException, FacesException {
655 Object resp = context.getExternalContext().getResponse();
656
657 // always log
658 if (log.isLoggable(Level.SEVERE)) {
659 UIViewRoot root = context.getViewRoot();
660 StringBuffer sb = new StringBuffer(64);
661 sb.append("Error Rendering View");
662 if (root != null) {
663 sb.append('[');
664 sb.append(root.getViewId());
665 sb.append(']');
666 }
667 log.log(Level.SEVERE, sb.toString(), e);
668 }
669
670 // handle dev response
671 if (this.developmentMode && !context.getResponseComplete()
672 && resp instanceof HttpServletResponse) {
673 HttpServletResponse httpResp = (HttpServletResponse) resp;
674 httpResp.reset();
675 httpResp.setContentType("text/html; charset=UTF-8");
676 Writer w = httpResp.getWriter();
677 DevTools.debugHtml(w, context, e);
678 w.flush();
679 context.responseComplete();
680 } else if (e instanceof RuntimeException) {
681 throw (RuntimeException) e;
682 } else if (e instanceof IOException) {
683 throw (IOException) e;
684 } else {
685 throw new FacesException(e.getMessage(), e);
686 }
687 }
688
689 protected void handleFaceletNotFound(FacesContext context, String viewId)
690 throws FacesException, IOException {
691 String actualId = this.getActionURL(context, viewId);
692 Object respObj = context.getExternalContext().getResponse();
693 if (respObj instanceof HttpServletResponse) {
694 HttpServletResponse respHttp = (HttpServletResponse) respObj;
695 respHttp.sendError(HttpServletResponse.SC_NOT_FOUND, actualId);
696 context.responseComplete();
697 }
698 }
699
700 /**
701 * Determine if Facelets needs to handle this request.
702 */
703 private boolean handledByFacelets(String viewId) {
704 // If there's no extensions array or prefixes array, then
705 // just make Facelets handle everything
706 if ((extensionsArray == null) && (prefixesArray == null)) {
707 return true;
708 }
709
710 if (extensionsArray != null) {
711 for (int i = 0; i < extensionsArray.length; i++) {
712 String extension = extensionsArray[i];
713 if (viewId.endsWith(extension)) {
714 return true;
715 }
716 }
717 }
718
719 if (prefixesArray != null) {
720 for (int i = 0; i < prefixesArray.length; i++) {
721 String prefix = prefixesArray[i];
722 if (viewId.startsWith(prefix)) {
723 return true;
724 }
725 }
726 }
727
728 return false;
729 }
730
731 public String getDefaultSuffix(FacesContext context) throws FacesException {
732 if (this.defaultSuffix == null) {
733 ExternalContext extCtx = context.getExternalContext();
734 String viewSuffix = extCtx
735 .getInitParameter(ViewHandler.DEFAULT_SUFFIX_PARAM_NAME);
736 this.defaultSuffix = (viewSuffix != null) ? viewSuffix
737 : ViewHandler.DEFAULT_SUFFIX;
738 }
739 return this.defaultSuffix;
740 }
741
742 protected String getRenderedViewId(FacesContext context, String actionId) {
743 ExternalContext extCtx = context.getExternalContext();
744 String viewId = actionId;
745 if (extCtx.getRequestPathInfo() == null) {
746 String facesSuffix = actionId.substring(actionId.lastIndexOf('.'));
747 String viewSuffix = this.getDefaultSuffix(context);
748 viewId = actionId.replaceFirst(facesSuffix, viewSuffix);
749 }
750 if (log.isLoggable(Level.FINE)) {
751 log.fine("ActionId -> ViewId: " + actionId + " -> " + viewId);
752 }
753 return viewId;
754 }
755
756 public void writeState(FacesContext context) throws IOException {
757 if (handledByFacelets(context.getViewRoot().getViewId())) {
758 // Tell the StateWriter that we're about to write state
759 StateWriter.getCurrentInstance().writingState();
760 // Write the STATE_KEY out. Unfortunately, this will
761 // be wasteful for pure server-side state managers where nothing
762 // is actually written into the output, but this cannot
763 // programatically be discovered
764 context.getResponseWriter().write(STATE_KEY);
765 } else {
766 this.parent.writeState(context);
767 }
768 }
769
770 public Locale calculateLocale(FacesContext context) {
771 return this.parent.calculateLocale(context);
772 }
773
774 public String calculateRenderKitId(FacesContext context) {
775 return this.parent.calculateRenderKitId(context);
776 }
777
778 public UIViewRoot createView(FacesContext context, String viewId) {
779 if (UIDebug.debugRequest(context)) {
780 return new UIViewRoot();
781 }
782 return this.parent.createView(context, viewId);
783 }
784
785 public String getActionURL(FacesContext context, String viewId) {
786 return this.parent.getActionURL(context, viewId);
787 }
788
789 public String getResourceURL(FacesContext context, String path) {
790 return this.parent.getResourceURL(context, path);
791 }
792
793 /**
794 * Try to guess if this is a postback request. In JSF 1.2, this method is
795 * not needed, since ResponseStateManager can identify postbacks. We use a
796 * simple heuristic: for HttpServletRequests, "POST" and "PUT" are
797 * postbacks. For anything that isn't an HttpServletRequest, just guess that
798 * if there's a request parameter, it's probably a postback.
799 */
800 static private boolean isPostback11(FacesContext context) {
801 Object reqObject = context.getExternalContext().getRequest();
802 if (reqObject instanceof HttpServletRequest) {
803 HttpServletRequest request = (HttpServletRequest) reqObject;
804
805 String method = request.getMethod();
806
807 // Is this a POST or PUT request?
808 if ("POST".equals(method) || "PUT".equals(method)) {
809 return true;
810 }
811
812 return false;
813 } else {
814 Map paramMap = context.getExternalContext()
815 .getRequestParameterMap();
816 return !paramMap.isEmpty();
817 }
818 }
819
820 protected static class NullWriter extends Writer {
821
822 static final NullWriter Instance = new NullWriter();
823
824 public void write(char[] buffer) {
825 }
826
827 public void write(char[] buffer, int off, int len) {
828 }
829
830 public void write(String str) {
831 }
832
833 public void write(int c) {
834 }
835
836 public void write(String str, int off, int len) {
837 }
838
839 public void close() {
840 }
841
842 public void flush() {
843 }
844 }
845 }