Save This Page
Home » mojarra-1.2_09-b02-FCS-source » com.sun.faces.application » [javadoc | source]
    1   /* 
    2    * $Id: ViewHandlerImpl.java,v 1.109.4.5 2008/06/11 18:03:03 rlubke Exp $
    3    */
    4   
    5   
    6   /*
    7    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
    8    *
    9    * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
   10    *
   11    * The contents of this file are subject to the terms of either the GNU
   12    * General Public License Version 2 only ("GPL") or the Common Development
   13    * and Distribution License("CDDL") (collectively, the "License").  You
   14    * may not use this file except in compliance with the License. You can obtain
   15    * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
   16    * or glassfish/bootstrap/legal/LICENSE.txt.  See the License for the specific
   17    * language governing permissions and limitations under the License.
   18    *
   19    * When distributing the software, include this License Header Notice in each
   20    * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
   21    * Sun designates this particular file as subject to the "Classpath" exception
   22    * as provided by Sun in the GPL Version 2 section of the License file that
   23    * accompanied this code.  If applicable, add the following below the License
   24    * Header, with the fields enclosed by brackets [] replaced by your own
   25    * identifying information: "Portions Copyrighted [year]
   26    * [name of copyright owner]"
   27    *
   28    * Contributor(s):
   29    *
   30    * If you wish your version of this file to be governed by only the CDDL or
   31    * only the GPL Version 2, indicate your decision by adding "[Contributor]
   32    * elects to include this software in this distribution under the [CDDL or GPL
   33    * Version 2] license."  If you don't indicate a single choice of license, a
   34    * recipient has the option to distribute your version of this file under
   35    * either the CDDL, the GPL Version 2 or to extend the choice of license to
   36    * its licensees as provided above.  However, if you add GPL Version 2 code
   37    * and therefore, elected the GPL Version 2 license, then the option applies
   38    * only if the new code is made subject to such option by the copyright
   39    * holder.
   40    */
   41   
   42   
   43   // ViewHandlerImpl.java
   44   
   45   package com.sun.faces.application;
   46   
   47   import com.sun.faces.RIConstants;
   48   import com.sun.faces.config.WebConfiguration;
   49   import com.sun.faces.config.WebConfiguration.WebContextInitParameter;
   50   import com.sun.faces.io.FastStringWriter;
   51   import com.sun.faces.util.DebugUtil;
   52   import com.sun.faces.util.MessageUtils;
   53   import com.sun.faces.util.Util;
   54   import com.sun.faces.util.FacesLogger;
   55   import com.sun.faces.util.RequestStateManager;
   56   
   57   import javax.faces.FacesException;
   58   import javax.faces.FactoryFinder;
   59   import javax.faces.application.StateManager;
   60   import javax.faces.application.ViewHandler;
   61   import javax.faces.component.UIViewRoot;
   62   import javax.faces.context.ExternalContext;
   63   import javax.faces.context.FacesContext;
   64   import javax.faces.context.ResponseWriter;
   65   import javax.faces.render.RenderKit;
   66   import javax.faces.render.RenderKitFactory;
   67   import javax.faces.render.ResponseStateManager;
   68   import javax.servlet.ServletRequest;
   69   import javax.servlet.ServletResponse;
   70   import javax.servlet.http.HttpServletResponse;
   71   import javax.servlet.jsp.jstl.core.Config;
   72   
   73   
   74   import java.io.IOException;
   75   import java.io.Writer;
   76   import java.util.Iterator;
   77   import java.util.Locale;
   78   import java.util.Map;
   79   import java.util.logging.Level;
   80   import java.util.logging.Logger;
   81   
   82   /**
   83    * <B>ViewHandlerImpl</B> is the default implementation class for ViewHandler.
   84    *
   85    * @version $Id: ViewHandlerImpl.java,v 1.109.4.5 2008/06/11 18:03:03 rlubke Exp $
   86    * @see javax.faces.application.ViewHandler
   87    */
   88   public class ViewHandlerImpl extends ViewHandler {
   89   
   90       // Log instance for this class
   91       private static final Logger logger = FacesLogger.APPLICATION.getLogger();
   92   
   93       private ApplicationAssociate associate;
   94   
   95       /**
   96        * <p>Store the value of <code>DEFAULT_SUFFIX_PARAM_NAME</code>
   97        * or, if that isn't defined, the value of <code>DEFAULT_SUFFIX</code>
   98        */
   99       private String contextDefaultSuffix;
  100       private int bufSize = -1;
  101   
  102       public ViewHandlerImpl() {
  103           if (logger.isLoggable(Level.FINE)) {
  104               logger.log(Level.FINE,"Created ViewHandler instance ");
  105           }
  106       }
  107       
  108   
  109       /**
  110        * Do not call the default implementation of {@link ViewHandler#initView(javax.faces.context.FacesContext)}
  111        * if the {@link javax.faces.context.ExternalContext#getRequestCharacterEncoding()} returns a
  112        * <code>non-null</code> result.
  113        *
  114        * @see ViewHandler#initView(javax.faces.context.FacesContext)
  115        */
  116       @Override
  117       public void initView(FacesContext context) throws FacesException {
  118   
  119           if (context.getExternalContext().getRequestCharacterEncoding() == null) {
  120               super.initView(context);
  121           }
  122           
  123       }
  124   
  125   
  126       public void renderView(FacesContext context,
  127               UIViewRoot viewToRender) throws IOException,
  128               FacesException {
  129   
  130           // suppress rendering if "rendered" property on the component is
  131           // false
  132           if (!viewToRender.isRendered()) {
  133               return;
  134           }
  135   
  136           ExternalContext extContext = context.getExternalContext();
  137           ServletRequest request = (ServletRequest) extContext.getRequest();
  138           ServletResponse response = (ServletResponse) extContext.getResponse();
  139   
  140           try {
  141               if (executePageToBuildView(context, viewToRender)) {
  142                   response.flushBuffer();
  143                   ApplicationAssociate associate = getAssociate(context);
  144                   if (associate != null) {
  145                       associate.responseRendered();
  146                   }
  147                   return;
  148               }
  149           } catch (IOException e) {
  150               throw new FacesException(e);
  151           }
  152   
  153           if (logger.isLoggable(Level.FINE)) {
  154               logger.log(Level.FINE, "Completed building view for : \n" +
  155                       viewToRender.getViewId());
  156           }
  157           if (logger.isLoggable(Level.FINEST)) {
  158               logger.log(Level.FINEST, "+=+=+=+=+=+= Printout for " + viewToRender.getViewId() + " about to render.");
  159               DebugUtil.printTree(viewToRender, logger, Level.FINEST);
  160           }
  161   
  162           // set up the ResponseWriter
  163   
  164           RenderKitFactory renderFactory = (RenderKitFactory)
  165           FactoryFinder.getFactory(FactoryFinder.RENDER_KIT_FACTORY);
  166           RenderKit renderKit =
  167                   renderFactory.getRenderKit(context, viewToRender.getRenderKitId());
  168   
  169           ResponseWriter oldWriter = context.getResponseWriter();
  170   
  171           if (bufSize == -1) {
  172               WebConfiguration webConfig =
  173                     WebConfiguration
  174                           .getInstance(context.getExternalContext());
  175               try {
  176                   bufSize = Integer
  177                         .parseInt(webConfig.getOptionValue(
  178                               WebContextInitParameter.ResponseBufferSize));
  179               } catch (NumberFormatException nfe) {
  180                   bufSize = Integer
  181                         .parseInt(WebContextInitParameter.ResponseBufferSize.getDefaultValue());
  182               }
  183           }
  184   
  185   
  186           WriteBehindStateWriter stateWriter =
  187                 new WriteBehindStateWriter(response.getWriter(),
  188                                            context,
  189                                            bufSize);
  190           ResponseWriter newWriter;
  191           if (null != oldWriter) {
  192               newWriter = oldWriter.cloneWithWriter(stateWriter);
  193           } else {
  194               newWriter = renderKit.createResponseWriter(stateWriter,
  195                                                          null,
  196                                                          request.getCharacterEncoding());
  197           }
  198           context.setResponseWriter(newWriter);
  199   
  200           newWriter.startDocument();
  201   
  202           doRenderView(context, viewToRender);
  203   
  204           newWriter.endDocument();
  205   
  206           // replace markers in the body content and write it to response.
  207   
  208           // flush directly to the response
  209           if (stateWriter.stateWritten()) {
  210               stateWriter.flushToWriter();
  211           }
  212   
  213           // clear the ThreadLocal reference.
  214           stateWriter.release();
  215   
  216           if (null != oldWriter) {
  217               context.setResponseWriter(oldWriter);
  218           }
  219   
  220           // write any AFTER_VIEW_CONTENT to the response
  221           // side effect: AFTER_VIEW_CONTENT removed
  222           ViewHandlerResponseWrapper wrapper = (ViewHandlerResponseWrapper)
  223                 RequestStateManager.remove(context, RequestStateManager.AFTER_VIEW_CONTENT);
  224           if (null != wrapper) {
  225               wrapper.flushToWriter(response.getWriter(),
  226                       response.getCharacterEncoding());
  227           }
  228   
  229           response.flushBuffer();
  230   
  231       }
  232   
  233       /**
  234        * <p>This is a separate method to account for handling the content
  235        * after the view tag.</p>
  236        *
  237        * <p>Create a new ResponseWriter around this response's Writer.
  238        * Set it into the FacesContext, saving the old one aside.</p>
  239        *
  240        * <p>call encodeBegin(), encodeChildren(), encodeEnd() on the
  241        * argument <code>UIViewRoot</code>.</p>
  242        *
  243        * <p>Restore the old ResponseWriter into the FacesContext.</p>
  244        *
  245        * <p>Write out the after view content to the response's writer.</p>
  246        *
  247        * <p>Flush the response buffer, and remove the after view content
  248        * from the request scope.</p>
  249        *
  250        * @param context the <code>FacesContext</code> for the current request
  251        * @param viewToRender the view to render
  252        * @throws IOException if an error occurs rendering the view to the client
  253        * @throws FacesException if some error occurs within the framework
  254        *  processing
  255        */
  256   
  257       private void doRenderView(FacesContext context,
  258                                 UIViewRoot viewToRender)
  259       throws IOException, FacesException {
  260   
  261           ApplicationAssociate associate = getAssociate(context);
  262   
  263           if (null != associate) {
  264               associate.responseRendered();
  265           }
  266   
  267           if (logger.isLoggable(Level.FINE)) {
  268               logger.log(Level.FINE, "About to render view " + viewToRender.getViewId());
  269           }
  270   
  271           viewToRender.encodeAll(context);
  272       }
  273   
  274   
  275       public UIViewRoot restoreView(FacesContext context, String viewId) {
  276           if (context == null) {
  277               String message = MessageUtils.getExceptionMessageString
  278                   (MessageUtils.NULL_PARAMETERS_ERROR_MESSAGE_ID, "context");
  279               throw new NullPointerException(message);
  280           }
  281   
  282           ExternalContext extContext = context.getExternalContext();
  283   
  284           String mapping = Util.getFacesMapping(context);
  285           UIViewRoot viewRoot = null;
  286   
  287           if (mapping != null) {
  288               if (!Util.isPrefixMapped(mapping)) {
  289                   viewId = convertViewId(context, viewId);
  290               } else {
  291                   viewId = normalizeRequestURI(viewId, mapping);
  292               }
  293           }
  294   
  295           // maping could be null if a non-faces request triggered
  296           // this response.
  297           if (extContext.getRequestPathInfo() == null && mapping != null &&
  298               Util.isPrefixMapped(mapping)) {
  299               // this was probably an initial request
  300               // send them off to the root of the web application
  301               try {
  302                   context.responseComplete();
  303                   if (logger.isLoggable(Level.FINE)) {
  304                       logger.log(Level.FINE, "Response Complete for" + viewId);
  305                   }
  306                   extContext.redirect(extContext.getRequestContextPath());
  307               } catch (IOException ioe) {
  308                   throw new FacesException(ioe);
  309               }
  310           } else {
  311               // this is necessary to allow decorated impls.
  312               ViewHandler outerViewHandler =
  313                       context.getApplication().getViewHandler();
  314               String renderKitId =
  315                       outerViewHandler.calculateRenderKitId(context);
  316               viewRoot = Util.getStateManager(context).restoreView(context,
  317                                                                    viewId,
  318                                                                    renderKitId);
  319           }
  320   
  321           return viewRoot;
  322       }
  323   
  324   
  325       public UIViewRoot createView(FacesContext context, String viewId) {
  326           if (context == null) {
  327               String message = MessageUtils.getExceptionMessageString
  328                       (MessageUtils.NULL_PARAMETERS_ERROR_MESSAGE_ID, "context");
  329               throw new NullPointerException(message);
  330           }
  331   
  332           UIViewRoot result = (UIViewRoot)
  333                   context.getApplication().createComponent(UIViewRoot.COMPONENT_TYPE);
  334   
  335           if (viewId != null) {
  336               String mapping = Util.getFacesMapping(context);
  337   
  338               if (mapping != null) {
  339                   if (!Util.isPrefixMapped(mapping)) {
  340                      viewId = convertViewId(context, viewId);
  341                   } else {
  342                       viewId = normalizeRequestURI(viewId, mapping);
  343                       if (viewId.equals(mapping)) {
  344                           // The request was to the FacesServlet only - no
  345                           // path info
  346                           // on some containers this causes a recursion in the
  347                           // RequestDispatcher and the request appears to hang.
  348                           // If this is detected, return status 404
  349                           send404Error(context);
  350                       }
  351                   }
  352               }
  353   
  354               result.setViewId(viewId);
  355           }
  356   
  357           Locale locale = null;
  358           String renderKitId = null;
  359   
  360           // use the locale from the previous view if is was one which will be
  361           // the case if this is called from NavigationHandler. There wouldn't be
  362           // one for the initial case.
  363           if (context.getViewRoot() != null) {
  364               locale = context.getViewRoot().getLocale();
  365               renderKitId = context.getViewRoot().getRenderKitId();
  366           }
  367   
  368           if (logger.isLoggable(Level.FINE)) {
  369               logger.log(Level.FINE, "Created new view for " + viewId);
  370           }
  371           // PENDING(): not sure if we should set the RenderKitId here.
  372           // The UIViewRoot ctor sets the renderKitId to the default
  373           // one.
  374           // if there was no locale from the previous view, calculate the locale
  375           // for this view.
  376           if (locale == null) {
  377               locale =
  378                   context.getApplication().getViewHandler().calculateLocale(
  379                       context);
  380               if (logger.isLoggable(Level.FINE)) {
  381                   logger.fine("Locale for this view as determined by calculateLocale "
  382                               + locale.toString());
  383               }
  384           } else {
  385               if (logger.isLoggable(Level.FINE)) {
  386                   logger.fine("Using locale from previous view "
  387                               + locale.toString());
  388               }
  389           }
  390   
  391           if (renderKitId == null) {
  392               renderKitId =
  393                   context.getApplication().getViewHandler().calculateRenderKitId(
  394                       context);
  395              if (logger.isLoggable(Level.FINE)) {
  396                  logger.fine(
  397                  "RenderKitId for this view as determined by calculateRenderKitId "
  398                  + renderKitId);
  399               }
  400           } else {
  401               if (logger.isLoggable(Level.FINE)) {
  402                   logger.fine("Using renderKitId from previous view "
  403                               + renderKitId);
  404               }
  405           }
  406   
  407           result.setLocale(locale);
  408           result.setRenderKitId(renderKitId);
  409   
  410           return result;
  411       }
  412   
  413       /**
  414        * Execute the target view.  If the HTTP status code range is
  415        * not 2xx, then return true to indicate the response should be
  416        * immediately flushed by the caller so that conditions such as 404
  417        * are properly handled.
  418        * @param context the <code>FacesContext</code> for the current request
  419        * @param viewToExecute the view to build
  420        * @return <code>true</code> if the response should be immediately flushed
  421        *  to the client, otherwise <code>false</code>
  422        * @throws IOException if an error occurs executing the page
  423        */
  424       private boolean executePageToBuildView(FacesContext context,
  425                                           UIViewRoot viewToExecute)
  426       throws IOException {
  427   
  428           if (null == context) {
  429               String message = MessageUtils.getExceptionMessageString
  430                       (MessageUtils.NULL_PARAMETERS_ERROR_MESSAGE_ID, "context");
  431               throw new NullPointerException(message);
  432           }
  433           if (null == viewToExecute) {
  434               String message = MessageUtils.getExceptionMessageString
  435                       (MessageUtils.NULL_PARAMETERS_ERROR_MESSAGE_ID, "viewToExecute");
  436               throw new NullPointerException(message);
  437           }
  438   
  439           ExternalContext extContext = context.getExternalContext();
  440   
  441           if ("/*".equals(RequestStateManager.get(context, RequestStateManager.INVOCATION_PATH))) {
  442               throw new FacesException(MessageUtils.getExceptionMessageString(
  443                     MessageUtils.FACES_SERVLET_MAPPING_INCORRECT_ID));
  444           }
  445   
  446           String requestURI = viewToExecute.getViewId();
  447   
  448           if (logger.isLoggable(Level.FINE)) {
  449               logger.fine("About to execute view " + requestURI);
  450           }
  451   
  452           // update the JSTL locale attribute in request scope so that JSTL
  453           // picks up the locale from viewRoot. This attribute must be updated
  454           // before the JSTL setBundle tag is called because that is when the
  455           // new LocalizationContext object is created based on the locale.
  456           if (extContext.getRequest() instanceof ServletRequest) {
  457               Config.set((ServletRequest)
  458               extContext.getRequest(),
  459                          Config.FMT_LOCALE, context.getViewRoot().getLocale());
  460           }
  461           if (logger.isLoggable(Level.FINE)) {
  462               logger.fine("Before dispacthMessage to viewId " + requestURI);
  463           }
  464   
  465           // save the original response
  466           Object originalResponse = extContext.getResponse();
  467   
  468           // replace the response with our wrapper
  469           ViewHandlerResponseWrapper wrapped = getWrapper(extContext);
  470           extContext.setResponse(wrapped);
  471   
  472           // build the view by executing the page
  473           extContext.dispatch(requestURI);
  474   
  475           if (logger.isLoggable(Level.FINE)) {
  476               logger.fine("After dispacthMessage to viewId " + requestURI);
  477           }
  478   
  479           // replace the original response
  480           extContext.setResponse(originalResponse);
  481   
  482           // Follow the JSTL 1.2 spec, section 7.4,
  483           // on handling status codes on a forward
  484           if (wrapped.getStatus() < 200 || wrapped.getStatus() > 299) {
  485               // flush the contents of the wrapper to the response
  486               // this is necessary as the user may be using a custom
  487               // error page - this content should be propagated
  488               wrapped.flushContentToWrappedResponse();
  489               return true;
  490           }
  491   
  492           // Put the AFTER_VIEW_CONTENT into request scope
  493           // temporarily
  494           RequestStateManager.set(context,
  495                                   RequestStateManager.AFTER_VIEW_CONTENT,
  496                                   wrapped);
  497   
  498           return false;
  499   
  500       }
  501   
  502   
  503       public Locale calculateLocale(FacesContext context) {
  504   
  505           if (context == null) {
  506               String message = MessageUtils.getExceptionMessageString
  507                   (MessageUtils.NULL_PARAMETERS_ERROR_MESSAGE_ID, "context");
  508               throw new NullPointerException(message);
  509           }
  510   
  511           Locale result = null;
  512           // determine the locales that are acceptable to the client based on the
  513           // Accept-Language header and the find the best match among the
  514           // supported locales specified by the client.
  515           Iterator<Locale> locales = context.getExternalContext().getRequestLocales();
  516           while (locales.hasNext()) {
  517               Locale perf = locales.next();
  518               result = findMatch(context, perf);
  519               if (result != null) {
  520                   break;
  521               }
  522           }
  523           // no match is found.
  524           if (result == null) {
  525               if (context.getApplication().getDefaultLocale() == null) {
  526                   result = Locale.getDefault();
  527               } else {
  528                   result = context.getApplication().getDefaultLocale();
  529               }
  530           }
  531           return result;
  532       }
  533   
  534   
  535       public String calculateRenderKitId(FacesContext context) {
  536   
  537           if (context == null) {
  538               String message = MessageUtils.getExceptionMessageString
  539                   (MessageUtils.NULL_PARAMETERS_ERROR_MESSAGE_ID, "context");
  540               throw new NullPointerException(message);
  541           }
  542   
  543           Map<String,String> requestParamMap = context.getExternalContext()
  544               .getRequestParameterMap();
  545           String result = requestParamMap.get(
  546               ResponseStateManager.RENDER_KIT_ID_PARAM);
  547   
  548           if (result == null) {
  549               if (null ==
  550                   (result = context.getApplication().getDefaultRenderKitId())) {
  551                   result = RenderKitFactory.HTML_BASIC_RENDER_KIT;
  552               }
  553           }
  554           return result;
  555       }
  556   
  557   
  558       /**
  559        * Attempts to find a matching locale based on <code>pref</code> and
  560        * list of supported locales, using the matching algorithm
  561        * as described in JSTL 8.3.2.
  562        * @param context the <code>FacesContext</code> for the current request
  563        * @param pref the preferred locale
  564        * @return the Locale based on pref and the matching alogritm specified
  565        *  in JSTL 8.3.2
  566        */
  567       protected Locale findMatch(FacesContext context, Locale pref) {
  568           Locale result = null;
  569           Iterator<Locale> it = context.getApplication().getSupportedLocales();
  570           while (it.hasNext()) {
  571               Locale supportedLocale = it.next();
  572   
  573               if (pref.equals(supportedLocale)) {
  574                   // exact match
  575                   result = supportedLocale;
  576                   break;
  577               } else {
  578                   // Make sure the preferred locale doesn't have country
  579                   // set, when doing a language match, For ex., if the
  580                   // preferred locale is "en-US", if one of supported
  581                   // locales is "en-UK", even though its language matches
  582                   // that of the preferred locale, we must ignore it.
  583                   if (pref.getLanguage().equals(supportedLocale.getLanguage()) &&
  584                        supportedLocale.getCountry().length() == 0) {
  585                       result = supportedLocale;
  586                   }
  587               }
  588           }
  589           // if it's not in the supported locales,
  590           if (null == result) {
  591               Locale defaultLocale = context.getApplication().getDefaultLocale();
  592               if (defaultLocale != null) {
  593                   if ( pref.equals(defaultLocale)) {
  594                       // exact match
  595                       result = defaultLocale;
  596                   } else {
  597                       // Make sure the preferred locale doesn't have country
  598                       // set, when doing a language match, For ex., if the
  599                       // preferred locale is "en-US", if one of supported
  600                       // locales is "en-UK", even though its language matches
  601                       // that of the preferred locale, we must ignore it.
  602                       if (pref.getLanguage().equals(defaultLocale.getLanguage()) &&
  603                            defaultLocale.getCountry().length() == 0) {
  604                           result = defaultLocale;
  605                       }
  606                   }
  607               }
  608           }
  609   
  610           return result;
  611       }
  612   
  613   
  614       public void writeState(FacesContext context) throws IOException {
  615           if (context == null) {
  616              String message = MessageUtils.getExceptionMessageString
  617                   (MessageUtils.NULL_PARAMETERS_ERROR_MESSAGE_ID, "context");
  618               throw new NullPointerException(message);
  619           }
  620   
  621           if (logger.isLoggable(Level.FINE)) {
  622               logger.fine("Begin writing marker for viewId " +
  623                           context.getViewRoot().getViewId());
  624           }
  625   
  626           WriteBehindStateWriter writer = WriteBehindStateWriter.getCurrentInstance();
  627           if (writer != null) {
  628               writer.writingState();
  629           }
  630           context.getResponseWriter().write(RIConstants.SAVESTATE_FIELD_MARKER);
  631           if (logger.isLoggable(Level.FINE)) {
  632               logger.fine("End writing marker for viewId " +
  633                           context.getViewRoot().getViewId());
  634           }
  635   
  636       }
  637   
  638   
  639       public String getActionURL(FacesContext context, String viewId) {
  640   
  641           if (context == null) {
  642               String message = MessageUtils.getExceptionMessageString
  643                   (MessageUtils.NULL_PARAMETERS_ERROR_MESSAGE_ID, "context");
  644               throw new NullPointerException(message);
  645           }
  646           if (viewId == null) {
  647               String message = MessageUtils.getExceptionMessageString
  648                   (MessageUtils.NULL_PARAMETERS_ERROR_MESSAGE_ID, "viewId");
  649               throw new NullPointerException(message);
  650           }
  651   
  652           if (viewId.charAt(0) != '/') {
  653               String message =
  654                     MessageUtils.getExceptionMessageString(
  655                           MessageUtils.ILLEGAL_VIEW_ID_ID,
  656                           viewId);
  657               if (logger.isLoggable(Level.SEVERE)) {
  658                   logger.log(Level.SEVERE, "jsf.illegal_view_id_error", viewId);
  659               }
  660           throw new IllegalArgumentException(message);
  661           }
  662   
  663           // Acquire the context path, which we will prefix on all results
  664           ExternalContext extContext = context.getExternalContext();
  665           String contextPath = extContext.getRequestContextPath();
  666   
  667           // Acquire the mapping used to execute this request (if any)
  668           String mapping = Util.getFacesMapping(context);
  669   
  670           // If no mapping can be identified, just return a server-relative path
  671           if (mapping == null) {
  672               return (contextPath + viewId);
  673           }
  674   
  675           // Deal with prefix mapping
  676           if (Util.isPrefixMapped(mapping)) {
  677               if (mapping.equals("/*")) {
  678                   return (contextPath + viewId);
  679               } else {
  680                   return (contextPath + mapping + viewId);
  681               }
  682           }
  683   
  684           // Deal with extension mapping
  685           int period = viewId.lastIndexOf('.');
  686           if (period < 0) {
  687               return (contextPath + viewId + mapping);
  688           } else if (!viewId.endsWith(mapping)) {
  689               return (contextPath + viewId.substring(0, period) + mapping);
  690           } else {
  691               return (contextPath + viewId);
  692           }
  693   
  694       }
  695   
  696   
  697       public String getResourceURL(FacesContext context, String path) {
  698           ExternalContext extContext = context.getExternalContext();
  699           if (path.startsWith("/")) {
  700               return (extContext.getRequestContextPath() + path);
  701           } else {
  702               return path;
  703           }
  704   
  705       }
  706   
  707   
  708       /**
  709        * <p>if the specified mapping is a prefix mapping, and the provided
  710        * request URI (usually the value from <code>ExternalContext.getRequestServletPath()</code>)
  711        * starts with <code>mapping + '/'</code>, prune the mapping from the
  712        * URI and return it, otherwise, return the original URI.
  713        * @param uri the servlet request path
  714        * @param mapping the FacesServlet mapping used for this request
  715        * @return the URI without additional FacesServlet mappings
  716        * @since 1.2
  717        */
  718       private String normalizeRequestURI(String uri, String mapping) {
  719   
  720           if (mapping == null || !Util.isPrefixMapped(mapping)) {
  721               return uri;
  722           } else {
  723               int length = mapping.length() + 1;
  724               StringBuilder builder = new StringBuilder(length);
  725               builder.append(mapping).append('/');
  726               String mappingMod = builder.toString();
  727               boolean logged = false;
  728               while (uri.startsWith(mappingMod)) {
  729                   if (!logged && logger.isLoggable(Level.WARNING)) {
  730                       logged = true;
  731                       logger.log(Level.WARNING,
  732                                  "jsf.viewhandler.requestpath.recursion",
  733                                  new Object[] {uri, mapping});
  734                   }
  735                   uri = uri.substring(length - 1);
  736               }
  737               return uri;
  738           }
  739       }
  740   
  741       private void send404Error(FacesContext context) {
  742           HttpServletResponse response = (HttpServletResponse)
  743                context.getExternalContext().getResponse();
  744           try {
  745               context.responseComplete();
  746               response.sendError(HttpServletResponse.SC_NOT_FOUND);
  747           } catch (IOException ioe) {
  748               throw new FacesException(ioe);
  749           }
  750       }
  751   
  752   
  753       /**
  754        * <p>Adjust the viewID per the requirements of {@link #renderView}.</p>
  755        *
  756        * @param context current {@link FacesContext}
  757        * @param viewId  incoming view ID
  758        * @return the view ID with an altered suffix mapping (if necessary)
  759        */
  760       private String convertViewId(FacesContext context, String viewId) {
  761   
  762           if (contextDefaultSuffix == null) {
  763               contextDefaultSuffix =
  764                     WebConfiguration
  765                           .getInstance(context.getExternalContext())
  766                           .getOptionValue(WebContextInitParameter.JspDefaultSuffix);
  767               if (contextDefaultSuffix == null) {
  768                   contextDefaultSuffix = ViewHandler.DEFAULT_SUFFIX;
  769               }
  770               if (logger.isLoggable(Level.FINE)) {
  771                   logger.fine("contextDefaultSuffix "
  772                               + contextDefaultSuffix);
  773               }
  774           }
  775   
  776           String convertedViewId = viewId;
  777           // if the viewId doesn't already use the above suffix,
  778           // replace or append.
  779           if (!convertedViewId.endsWith(contextDefaultSuffix)) {
  780               StringBuilder buffer = new StringBuilder(convertedViewId);
  781               int extIdx = convertedViewId.lastIndexOf('.');
  782               if (extIdx != -1) {
  783                   buffer.replace(extIdx, convertedViewId.length(),
  784                                  contextDefaultSuffix);
  785               } else {
  786                   // no extension in the provided viewId, append the suffix
  787                   buffer.append(contextDefaultSuffix);
  788               }
  789               convertedViewId = buffer.toString();
  790               if (logger.isLoggable(Level.FINE)) {
  791                   logger.fine( "viewId after appending the context suffix " +
  792                                convertedViewId);
  793               }
  794   
  795           }
  796           return convertedViewId;
  797       }
  798   
  799   
  800       private ApplicationAssociate getAssociate(FacesContext context) {
  801           if (associate == null) {
  802               associate = ApplicationAssociate.getInstance(context.getExternalContext());
  803           }
  804           return associate;
  805       }
  806   
  807   
  808       private static ViewHandlerResponseWrapper getWrapper(ExternalContext extContext) {
  809           Object response = extContext.getResponse();
  810           if (response instanceof HttpServletResponse) {
  811               return new ViewHandlerResponseWrapper((HttpServletResponse) response);
  812           }
  813           throw new IllegalArgumentException();
  814   
  815       }
  816   
  817       // ----------------------------------------------------------- Inner Classes
  818   
  819       /**
  820        * Thanks to the Facelets folks for some of the concepts incorporated
  821        * into this class.
  822        */
  823       private static final class WriteBehindStateWriter extends Writer {
  824           // length of the state marker
  825           private static final int STATE_MARKER_LEN =
  826                 RIConstants.SAVESTATE_FIELD_MARKER.length();
  827   
  828           private static final ThreadLocal<WriteBehindStateWriter> CUR_WRITER =
  829                new ThreadLocal<WriteBehindStateWriter>();
  830           private Writer out;
  831           private Writer orig;
  832           private FastStringWriter fWriter;
  833           private boolean stateWritten;
  834           private int bufSize;
  835           private char[] buf;
  836           private FacesContext context;
  837   
  838   
  839           // -------------------------------------------------------- Constructors
  840   
  841   
  842           public WriteBehindStateWriter(Writer out, FacesContext context, int bufSize) {
  843               this.out = out;
  844               this.orig = out;
  845               this.context = context;
  846               this.bufSize = bufSize;
  847               this.buf = new char[bufSize];
  848               CUR_WRITER.set(this);
  849           }
  850   
  851   
  852           // ------------------------------------------------- Methods from Writer
  853   
  854   
  855   
  856           public void write(int c) throws IOException {
  857               out.write(c);
  858           }
  859   
  860   
  861           public void write(char cbuf[]) throws IOException {
  862               out.write(cbuf);
  863           }
  864   
  865   
  866           public void write(String str) throws IOException {
  867               out.write(str);
  868           }
  869   
  870   
  871           public void write(String str, int off, int len) throws IOException {
  872               out.write(str, off, len);
  873           }
  874   
  875   
  876           public void write(char cbuf[], int off, int len) throws IOException {
  877               out.write(cbuf, off, len);
  878           }
  879   
  880   
  881           public void flush() throws IOException {
  882               // no-op
  883           }
  884   
  885   
  886           public void close() throws IOException {
  887              // no-op
  888           }
  889   
  890   
  891           // ------------------------------------------------------ Public Methods
  892   
  893   
  894           public static WriteBehindStateWriter getCurrentInstance() {
  895               return CUR_WRITER.get();
  896           }
  897   
  898   
  899           public void release() {
  900               CUR_WRITER.remove();
  901           }
  902   
  903   
  904           public void writingState() {
  905               if (!stateWritten) {
  906                   this.stateWritten = true;
  907                   out = fWriter = new FastStringWriter(1024);
  908               }
  909           }
  910   
  911           public boolean stateWritten() {
  912               return stateWritten;
  913           }
  914   
  915           /**
  916            * <p> Write directly from our FastStringWriter to the provided
  917            * writer.</p>
  918            * @throws IOException if an error occurs
  919            */
  920           public void flushToWriter() throws IOException {
  921               // Save the state to a new instance of StringWriter to
  922               // avoid multiple serialization steps if the view contains
  923               // multiple forms.
  924               StateManager stateManager = Util.getStateManager(context);
  925               ResponseWriter origWriter = context.getResponseWriter();
  926               FastStringWriter state =
  927                     new FastStringWriter((stateManager.isSavingStateInClient(
  928                           context)) ? bufSize : 128);
  929               context.setResponseWriter(origWriter.cloneWithWriter(state));
  930               stateManager.writeState(context, stateManager.saveView(context));
  931               context.setResponseWriter(origWriter);
  932               StringBuilder builder = fWriter.getBuffer();
  933               // begin writing...
  934               int totalLen = builder.length();
  935               StringBuilder stateBuilder = state.getBuffer();
  936               int stateLen = stateBuilder.length();
  937               int pos = 0;
  938               int tildeIdx = getNextDelimiterIndex(builder, pos);
  939               while (pos < totalLen) {
  940                   if (tildeIdx != -1) {
  941                       if (tildeIdx > pos && (tildeIdx - pos) > bufSize) {
  942                           // there's enough content before the first ~
  943                           // to fill the entire buffer
  944                           builder.getChars(pos, (pos + bufSize), buf, 0);
  945                           orig.write(buf);
  946                           pos += bufSize;
  947                       } else {
  948                           // write all content up to the first '~'
  949                           builder.getChars(pos, tildeIdx, buf, 0);
  950                           int len = (tildeIdx - pos);
  951                           orig.write(buf, 0, len);
  952                           // now check to see if the state saving string is
  953                           // at the begining of pos, if so, write our
  954                           // state out.
  955                           if (builder.indexOf(
  956                                 RIConstants.SAVESTATE_FIELD_MARKER,
  957                                 pos) == tildeIdx) {
  958                               // buf is effectively zero'd out at this point
  959                               int statePos = 0;
  960                               while (statePos < stateLen) {
  961                                   if ((stateLen - statePos) > bufSize) {
  962                                       // enough state to fill the buffer
  963                                       stateBuilder.getChars(statePos,
  964                                                             (statePos + bufSize),
  965                                                             buf,
  966                                                             0);
  967                                       orig.write(buf);
  968                                       statePos += bufSize;
  969                                   } else {
  970                                       int slen = (stateLen - statePos);
  971                                       stateBuilder.getChars(statePos,
  972                                                             stateLen,
  973                                                             buf,
  974                                                             0);
  975                                       orig.write(buf, 0, slen);
  976                                       statePos += slen;
  977                                   }
  978   
  979                               }
  980                                // push us past the last '~' at the end of the marker
  981                               pos += (len + STATE_MARKER_LEN);
  982                               tildeIdx = getNextDelimiterIndex(builder, pos);
  983                           } else {
  984                               pos = tildeIdx;
  985                               tildeIdx = getNextDelimiterIndex(builder,
  986                                                                tildeIdx + 1);
  987   
  988                           }
  989                       }
  990                   } else {
  991                       // we've written all of the state field markers.
  992                       // finish writing content
  993                       if (totalLen - pos > bufSize) {
  994                           // there's enough content to fill the buffer
  995                           builder.getChars(pos, (pos + bufSize), buf, 0);
  996                           orig.write(buf);
  997                           pos += bufSize;
  998                       } else {
  999                           // we're near the end of the response
 1000                           builder.getChars(pos, totalLen, buf, 0);
 1001                           int len = (totalLen - pos);
 1002                           orig.write(buf, 0, len);
 1003                           pos += (len + 1);
 1004                       }
 1005                   }
 1006               }
 1007   
 1008               // all state has been written.  Have 'out' point to the
 1009               // response so that all subsequent writes will make it to the
 1010               // browser.
 1011               out = orig;
 1012           }
 1013   
 1014           private static int getNextDelimiterIndex(StringBuilder builder,
 1015                                                    int offset) {
 1016               return builder.indexOf(RIConstants.SAVESTATE_FIELD_DELIMITER,
 1017                                      offset);
 1018           }
 1019   
 1020       }
 1021   
 1022   
 1023   }

Save This Page
Home » mojarra-1.2_09-b02-FCS-source » com.sun.faces.application » [javadoc | source]