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