View Javadoc

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