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