All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.sun.faces.application.ViewHandlerImpl Maven / Gradle / Ivy

Go to download

This is the master POM file for Oracle's Implementation of the JSF 2.2 Specification.

There is a newer version: 2.2.20
Show newest version
/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved.
 *
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common Development
 * and Distribution License("CDDL") (collectively, the "License").  You
 * may not use this file except in compliance with the License.  You can
 * obtain a copy of the License at
 * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
 * or packager/legal/LICENSE.txt.  See the License for the specific
 * language governing permissions and limitations under the License.
 *
 * When distributing the software, include this License Header Notice in each
 * file and include the License file at packager/legal/LICENSE.txt.
 *
 * GPL Classpath Exception:
 * Oracle designates this particular file as subject to the "Classpath"
 * exception as provided by Oracle in the GPL Version 2 section of the License
 * file that accompanied this code.
 *
 * Modifications:
 * If applicable, add the following below the License Header, with the fields
 * enclosed by brackets [] replaced by your own identifying information:
 * "Portions Copyright [year] [name of copyright owner]"
 *
 * Contributor(s):
 * If you wish your version of this file to be governed by only the CDDL or
 * only the GPL Version 2, indicate your decision by adding "[Contributor]
 * elects to include this software in this distribution under the [CDDL or GPL
 * Version 2] license."  If you don't indicate a single choice of license, a
 * recipient has the option to distribute your version of this file under
 * either the CDDL, the GPL Version 2 or to extend the choice of license to
 * its licensees as provided above.  However, if you add GPL Version 2 code
 * and therefore, elected the GPL Version 2 license, then the option applies
 * only if the new code is made subject to such option by the copyright
 * holder.
 */

// ViewHandlerImpl.java

package com.sun.faces.application;

import com.sun.faces.RIConstants;
import com.sun.faces.config.WebConfiguration;
import com.sun.faces.config.WebConfiguration.WebContextInitParameter;
import com.sun.faces.io.FastStringWriter;
import com.sun.faces.util.MessageUtils;
import com.sun.faces.util.Util;
import com.sun.faces.util.FacesLogger;
import com.sun.faces.util.RequestStateManager;

import javax.faces.FacesException;
import javax.faces.FactoryFinder;
import javax.faces.application.StateManager;
import javax.faces.application.ViewHandler;
import javax.faces.component.UIViewRoot;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import javax.faces.render.RenderKit;
import javax.faces.render.RenderKitFactory;
import javax.faces.render.ResponseStateManager;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.jsp.jstl.core.Config;


import java.io.IOException;
import java.io.Writer;
import java.util.Iterator;
import java.util.Locale;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.net.MalformedURLException;

/**
 * 

* This is the default implementation for JSF 1.2. *

*

* While this class isn't used by the 2.0 runtime, it's kept for binary * compatibility with those that extend from this class directly. *

* * @deprecated Refer to {@link com.sun.faces.application.view.MultiViewHandler} */ public class ViewHandlerImpl extends ViewHandler { // Log instance for this class private static final Logger logger = FacesLogger.APPLICATION.getLogger(); private ApplicationAssociate associate; private String[] configuredExtensions; private int bufSize = -1; public ViewHandlerImpl() { if (logger.isLoggable(Level.FINE)) { logger.log(Level.FINE,"Created ViewHandler instance "); } WebConfiguration config = WebConfiguration.getInstance(); String defaultSuffixConfig = config.getOptionValue(WebConfiguration.WebContextInitParameter.DefaultSuffix); Map appMap = FacesContext.getCurrentInstance().getExternalContext().getApplicationMap(); configuredExtensions = Util.split(appMap, defaultSuffixConfig, " "); } /** * Do not call the default implementation of {@link ViewHandler#initView(javax.faces.context.FacesContext)} * if the {@link javax.faces.context.ExternalContext#getRequestCharacterEncoding()} returns a * non-null result. * * @see ViewHandler#initView(javax.faces.context.FacesContext) */ @Override public void initView(FacesContext context) throws FacesException { if (context.getExternalContext().getRequestCharacterEncoding() == null) { super.initView(context); } } public void renderView(FacesContext context, UIViewRoot viewToRender) throws IOException, FacesException { // suppress rendering if "rendered" property on the component is // false if (!viewToRender.isRendered()) { return; } ExternalContext extContext = context.getExternalContext(); ServletRequest request = (ServletRequest) extContext.getRequest(); ServletResponse response = (ServletResponse) extContext.getResponse(); try { if (executePageToBuildView(context, viewToRender)) { response.flushBuffer(); ApplicationAssociate associate = getAssociate(context); if (associate != null) { associate.responseRendered(); } return; } } catch (IOException e) { throw new FacesException(e); } if (logger.isLoggable(Level.FINE)) { logger.log(Level.FINE, "Completed building view for : \n" + viewToRender.getViewId()); } // set up the ResponseWriter RenderKitFactory renderFactory = (RenderKitFactory) FactoryFinder.getFactory(FactoryFinder.RENDER_KIT_FACTORY); RenderKit renderKit = renderFactory.getRenderKit(context, viewToRender.getRenderKitId()); ResponseWriter oldWriter = context.getResponseWriter(); if (bufSize == -1) { WebConfiguration webConfig = WebConfiguration .getInstance(context.getExternalContext()); try { bufSize = Integer .parseInt(webConfig.getOptionValue( WebContextInitParameter.ResponseBufferSize)); } catch (NumberFormatException nfe) { bufSize = Integer .parseInt(WebContextInitParameter.ResponseBufferSize.getDefaultValue()); } } WriteBehindStateWriter stateWriter = new WriteBehindStateWriter(response.getWriter(), context, bufSize); ResponseWriter newWriter; if (null != oldWriter) { newWriter = oldWriter.cloneWithWriter(stateWriter); } else { newWriter = renderKit.createResponseWriter(stateWriter, null, request.getCharacterEncoding()); } context.setResponseWriter(newWriter); try { newWriter.startDocument(); doRenderView(context, viewToRender); newWriter.endDocument(); // replace markers in the body content and write it to response. // flush directly to the response if (stateWriter.stateWritten()) { stateWriter.flushToWriter(); } } finally { // clear the ThreadLocal reference. stateWriter.release(); if (null != oldWriter) { context.setResponseWriter(oldWriter); } } // write any AFTER_VIEW_CONTENT to the response // side effect: AFTER_VIEW_CONTENT removed Map stateMap = RequestStateManager.getStateMap(context); ViewHandlerResponseWrapper wrapper = (ViewHandlerResponseWrapper) stateMap.remove(RequestStateManager.AFTER_VIEW_CONTENT); if (null != wrapper) { wrapper.flushToWriter(response.getWriter(), response.getCharacterEncoding()); } response.flushBuffer(); } /** *

This is a separate method to account for handling the content * after the view tag.

* *

Create a new ResponseWriter around this response's Writer. * Set it into the FacesContext, saving the old one aside.

* *

call encodeBegin(), encodeChildren(), encodeEnd() on the * argument UIViewRoot.

* *

Restore the old ResponseWriter into the FacesContext.

* *

Write out the after view content to the response's writer.

* *

Flush the response buffer, and remove the after view content * from the request scope.

* * @param context the FacesContext for the current request * @param viewToRender the view to render * @throws IOException if an error occurs rendering the view to the client * @throws FacesException if some error occurs within the framework * processing */ private void doRenderView(FacesContext context, UIViewRoot viewToRender) throws IOException, FacesException { ApplicationAssociate associate = getAssociate(context); if (null != associate) { associate.responseRendered(); } if (logger.isLoggable(Level.FINE)) { logger.log(Level.FINE, "About to render view " + viewToRender.getViewId()); } viewToRender.encodeAll(context); } public UIViewRoot restoreView(FacesContext context, String viewId) { if (context == null) { String message = MessageUtils.getExceptionMessageString (MessageUtils.NULL_PARAMETERS_ERROR_MESSAGE_ID, "context"); throw new NullPointerException(message); } ExternalContext extContext = context.getExternalContext(); String mapping = Util.getFacesMapping(context); UIViewRoot viewRoot = null; if (mapping != null) { if (!Util.isPrefixMapped(mapping)) { viewId = convertViewId(context, viewId); } else { viewId = normalizeRequestURI(viewId, mapping); } } // maping could be null if a non-faces request triggered // this response. if (extContext.getRequestPathInfo() == null && mapping != null && Util.isPrefixMapped(mapping)) { // this was probably an initial request // send them off to the root of the web application try { context.responseComplete(); if (logger.isLoggable(Level.FINE)) { logger.log(Level.FINE, "Response Complete for" + viewId); } extContext.redirect(extContext.getRequestContextPath()); } catch (IOException ioe) { throw new FacesException(ioe); } } else { // this is necessary to allow decorated impls. ViewHandler outerViewHandler = context.getApplication().getViewHandler(); String renderKitId = outerViewHandler.calculateRenderKitId(context); viewRoot = Util.getStateManager(context).restoreView(context, viewId, renderKitId); } return viewRoot; } public UIViewRoot createView(FacesContext context, String viewId) { if (context == null) { String message = MessageUtils.getExceptionMessageString (MessageUtils.NULL_PARAMETERS_ERROR_MESSAGE_ID, "context"); throw new NullPointerException(message); } UIViewRoot result = (UIViewRoot) context.getApplication().createComponent(UIViewRoot.COMPONENT_TYPE); if (viewId != null) { String mapping = Util.getFacesMapping(context); if (mapping != null) { if (!Util.isPrefixMapped(mapping)) { viewId = convertViewId(context, viewId); } else { viewId = normalizeRequestURI(viewId, mapping); if (!Util.isPortletRequest(context) && viewId.equals(mapping)) { // The request was to the FacesServlet only - no // path info // on some containers this causes a recursion in the // RequestDispatcher and the request appears to hang. // If this is detected, return status 404 send404Error(context); } } } result.setViewId(viewId); } Locale locale = null; String renderKitId = null; // use the locale from the previous view if is was one which will be // the case if this is called from NavigationHandler. There wouldn't be // one for the initial case. if (context.getViewRoot() != null) { locale = context.getViewRoot().getLocale(); renderKitId = context.getViewRoot().getRenderKitId(); } if (logger.isLoggable(Level.FINE)) { logger.log(Level.FINE, "Created new view for " + viewId); } // PENDING(): not sure if we should set the RenderKitId here. // The UIViewRoot ctor sets the renderKitId to the default // one. // if there was no locale from the previous view, calculate the locale // for this view. if (locale == null) { locale = context.getApplication().getViewHandler().calculateLocale( context); if (logger.isLoggable(Level.FINE)) { logger.fine("Locale for this view as determined by calculateLocale " + locale.toString()); } } else { if (logger.isLoggable(Level.FINE)) { logger.fine("Using locale from previous view " + locale.toString()); } } if (renderKitId == null) { renderKitId = context.getApplication().getViewHandler().calculateRenderKitId( context); if (logger.isLoggable(Level.FINE)) { logger.fine( "RenderKitId for this view as determined by calculateRenderKitId " + renderKitId); } } else { if (logger.isLoggable(Level.FINE)) { logger.fine("Using renderKitId from previous view " + renderKitId); } } result.setLocale(locale); result.setRenderKitId(renderKitId); return result; } /** * Execute the target view. If the HTTP status code range is * not 2xx, then return true to indicate the response should be * immediately flushed by the caller so that conditions such as 404 * are properly handled. * @param context the FacesContext for the current request * @param viewToExecute the view to build * @return true if the response should be immediately flushed * to the client, otherwise false * @throws IOException if an error occurs executing the page */ private boolean executePageToBuildView(FacesContext context, UIViewRoot viewToExecute) throws IOException { if (null == context) { String message = MessageUtils.getExceptionMessageString (MessageUtils.NULL_PARAMETERS_ERROR_MESSAGE_ID, "context"); throw new NullPointerException(message); } if (null == viewToExecute) { String message = MessageUtils.getExceptionMessageString (MessageUtils.NULL_PARAMETERS_ERROR_MESSAGE_ID, "viewToExecute"); throw new NullPointerException(message); } ExternalContext extContext = context.getExternalContext(); Map stateMap = RequestStateManager.getStateMap(context); if ("/*".equals(stateMap.get(RequestStateManager.INVOCATION_PATH))) { throw new FacesException(MessageUtils.getExceptionMessageString( MessageUtils.FACES_SERVLET_MAPPING_INCORRECT_ID)); } String requestURI = viewToExecute.getViewId(); if (logger.isLoggable(Level.FINE)) { logger.fine("About to execute view " + requestURI); } // update the JSTL locale attribute in request scope so that JSTL // picks up the locale from viewRoot. This attribute must be updated // before the JSTL setBundle tag is called because that is when the // new LocalizationContext object is created based on the locale. if (extContext.getRequest() instanceof ServletRequest) { Config.set((ServletRequest) extContext.getRequest(), Config.FMT_LOCALE, context.getViewRoot().getLocale()); } if (logger.isLoggable(Level.FINE)) { logger.fine("Before dispacthMessage to viewId " + requestURI); } // save the original response Object originalResponse = extContext.getResponse(); // replace the response with our wrapper ViewHandlerResponseWrapper wrapped = getWrapper(extContext); extContext.setResponse(wrapped); // build the view by executing the page extContext.dispatch(requestURI); if (logger.isLoggable(Level.FINE)) { logger.fine("After dispacthMessage to viewId " + requestURI); } // replace the original response extContext.setResponse(originalResponse); // Follow the JSTL 1.2 spec, section 7.4, // on handling status codes on a forward if (wrapped.getStatus() < 200 || wrapped.getStatus() > 299) { // flush the contents of the wrapper to the response // this is necessary as the user may be using a custom // error page - this content should be propagated wrapped.flushContentToWrappedResponse(); return true; } // Put the AFTER_VIEW_CONTENT into request scope // temporarily stateMap.put(RequestStateManager.AFTER_VIEW_CONTENT, wrapped); return false; } public Locale calculateLocale(FacesContext context) { if (context == null) { String message = MessageUtils.getExceptionMessageString (MessageUtils.NULL_PARAMETERS_ERROR_MESSAGE_ID, "context"); throw new NullPointerException(message); } Locale result = null; // determine the locales that are acceptable to the client based on the // Accept-Language header and the find the best match among the // supported locales specified by the client. Iterator locales = context.getExternalContext().getRequestLocales(); while (locales.hasNext()) { Locale perf = locales.next(); result = findMatch(context, perf); if (result != null) { break; } } // no match is found. if (result == null) { if (context.getApplication().getDefaultLocale() == null) { result = Locale.getDefault(); } else { result = context.getApplication().getDefaultLocale(); } } return result; } public String calculateRenderKitId(FacesContext context) { if (context == null) { String message = MessageUtils.getExceptionMessageString (MessageUtils.NULL_PARAMETERS_ERROR_MESSAGE_ID, "context"); throw new NullPointerException(message); } Map requestParamMap = context.getExternalContext() .getRequestParameterMap(); String result = requestParamMap.get( ResponseStateManager.RENDER_KIT_ID_PARAM); if (result == null) { result = context.getApplication().getDefaultRenderKitId(); if (null == result) { result = RenderKitFactory.HTML_BASIC_RENDER_KIT; } } return result; } /** * Attempts to find a matching locale based on pref and * list of supported locales, using the matching algorithm * as described in JSTL 8.3.2. * @param context the FacesContext for the current request * @param pref the preferred locale * @return the Locale based on pref and the matching alogritm specified * in JSTL 8.3.2 */ protected Locale findMatch(FacesContext context, Locale pref) { Locale result = null; Iterator it = context.getApplication().getSupportedLocales(); while (it.hasNext()) { Locale supportedLocale = it.next(); if (pref.equals(supportedLocale)) { // exact match result = supportedLocale; break; } else { // Make sure the preferred locale doesn't have country // set, when doing a language match, For ex., if the // preferred locale is "en-US", if one of supported // locales is "en-UK", even though its language matches // that of the preferred locale, we must ignore it. if (pref.getLanguage().equals(supportedLocale.getLanguage()) && supportedLocale.getCountry().length() == 0) { result = supportedLocale; } } } // if it's not in the supported locales, if (null == result) { Locale defaultLocale = context.getApplication().getDefaultLocale(); if (defaultLocale != null) { if ( pref.equals(defaultLocale)) { // exact match result = defaultLocale; } else { // Make sure the preferred locale doesn't have country // set, when doing a language match, For ex., if the // preferred locale is "en-US", if one of supported // locales is "en-UK", even though its language matches // that of the preferred locale, we must ignore it. if (pref.getLanguage().equals(defaultLocale.getLanguage()) && defaultLocale.getCountry().length() == 0) { result = defaultLocale; } } } } return result; } public void writeState(FacesContext context) throws IOException { if (context == null) { String message = MessageUtils.getExceptionMessageString (MessageUtils.NULL_PARAMETERS_ERROR_MESSAGE_ID, "context"); throw new NullPointerException(message); } if (logger.isLoggable(Level.FINE)) { logger.fine("Begin writing marker for viewId " + context.getViewRoot().getViewId()); } WriteBehindStateWriter writer = WriteBehindStateWriter.getCurrentInstance(); if (writer != null) { writer.writingState(); } context.getResponseWriter().write(RIConstants.SAVESTATE_FIELD_MARKER); if (logger.isLoggable(Level.FINE)) { logger.fine("End writing marker for viewId " + context.getViewRoot().getViewId()); } } public String getActionURL(FacesContext context, String viewId) { if (context == null) { String message = MessageUtils.getExceptionMessageString (MessageUtils.NULL_PARAMETERS_ERROR_MESSAGE_ID, "context"); throw new NullPointerException(message); } if (viewId == null) { String message = MessageUtils.getExceptionMessageString (MessageUtils.NULL_PARAMETERS_ERROR_MESSAGE_ID, "viewId"); throw new NullPointerException(message); } if (0 == viewId.length() || viewId.charAt(0) != '/') { String message = MessageUtils.getExceptionMessageString( MessageUtils.ILLEGAL_VIEW_ID_ID, viewId); if (logger.isLoggable(Level.SEVERE)) { logger.log(Level.SEVERE, "jsf.illegal_view_id_error", viewId); } throw new IllegalArgumentException(message); } // Acquire the context path, which we will prefix on all results ExternalContext extContext = context.getExternalContext(); String contextPath = extContext.getRequestContextPath(); // Acquire the mapping used to execute this request (if any) String mapping = Util.getFacesMapping(context); // If no mapping can be identified, just return a server-relative path if (mapping == null) { return (contextPath + viewId); } // Deal with prefix mapping if (Util.isPrefixMapped(mapping)) { if (mapping.equals("/*")) { return (contextPath + viewId); } else { return (contextPath + mapping + viewId); } } // Deal with extension mapping for (String extension : configuredExtensions) { if (viewId.endsWith(extension)) { return (contextPath + viewId.substring(0, viewId.lastIndexOf('.')) + mapping); } } return (contextPath + viewId); } public String getResourceURL(FacesContext context, String path) { ExternalContext extContext = context.getExternalContext(); if (path.charAt(0) == '/' && !path.startsWith(extContext.getRequestContextPath())) { return (extContext.getRequestContextPath() + path); } else { return path; } } /** *

if the specified mapping is a prefix mapping, and the provided * request URI (usually the value from ExternalContext.getRequestServletPath()) * starts with mapping + '/', prune the mapping from the * URI and return it, otherwise, return the original URI. * @param uri the servlet request path * @param mapping the FacesServlet mapping used for this request * @return the URI without additional FacesServlet mappings * @since 1.2 */ private String normalizeRequestURI(String uri, String mapping) { if (mapping == null || !Util.isPrefixMapped(mapping)) { return uri; } else { int length = mapping.length() + 1; StringBuilder builder = new StringBuilder(length); builder.append(mapping).append('/'); String mappingMod = builder.toString(); boolean logged = false; while (uri.startsWith(mappingMod)) { if (!logged && logger.isLoggable(Level.WARNING)) { logged = true; logger.log(Level.WARNING, "jsf.viewhandler.requestpath.recursion", new Object[] {uri, mapping}); } uri = uri.substring(length - 1); } return uri; } } private void send404Error(FacesContext context) { HttpServletResponse response = (HttpServletResponse) context.getExternalContext().getResponse(); try { context.responseComplete(); response.sendError(HttpServletResponse.SC_NOT_FOUND); } catch (IOException ioe) { throw new FacesException(ioe); } } /** *

Adjust the viewID per the requirements of {@link #renderView}.

* * @param context current {@link FacesContext} * @param viewId incoming view ID * @return the view ID with an altered suffix mapping (if necessary) */ private String convertViewId(FacesContext context, String viewId) { // if the viewId doesn't already use the above suffix, // replace or append. StringBuilder buffer = new StringBuilder(viewId); for (String ext : configuredExtensions) { if (viewId.endsWith(ext)) { return viewId; } int extIdx = viewId.lastIndexOf('.'); if (extIdx != -1) { buffer.replace(extIdx, viewId.length(), ext); } else { // no extension in the provided viewId, append the suffix buffer.append(ext); } String convertedViewId = buffer.toString(); try { if (context.getExternalContext().getResource(convertedViewId) != null) { return convertedViewId; } else { // reset the buffer to check for the next extension buffer.setLength(0); buffer.append(viewId); } } catch (MalformedURLException e) { if (logger.isLoggable(Level.SEVERE)) { logger.log(Level.SEVERE, e.toString(), e); } } } // unable to find any resource match that the default ViewHandler // can deal with. Return the viewId as it was passed. There is // probably another ViewHandler in the stack that will handle this. return viewId; } private ApplicationAssociate getAssociate(FacesContext context) { if (associate == null) { associate = ApplicationAssociate.getInstance(context.getExternalContext()); } return associate; } private static ViewHandlerResponseWrapper getWrapper(ExternalContext extContext) { Object response = extContext.getResponse(); if (response instanceof HttpServletResponse) { return new ViewHandlerResponseWrapper((HttpServletResponse) response); } throw new IllegalArgumentException(); } // ----------------------------------------------------------- Inner Classes /** * Thanks to the Facelets folks for some of the concepts incorporated * into this class. */ private static final class WriteBehindStateWriter extends Writer { // length of the state marker private static final int STATE_MARKER_LEN = RIConstants.SAVESTATE_FIELD_MARKER.length(); private static final ThreadLocal CUR_WRITER = new ThreadLocal(); private Writer out; private Writer orig; private FastStringWriter fWriter; private boolean stateWritten; private int bufSize; private char[] buf; private FacesContext context; // -------------------------------------------------------- Constructors public WriteBehindStateWriter(Writer out, FacesContext context, int bufSize) { this.out = out; this.orig = out; this.context = context; this.bufSize = bufSize; this.buf = new char[bufSize]; CUR_WRITER.set(this); } // ------------------------------------------------- Methods from Writer public void write(int c) throws IOException { out.write(c); } public void write(char cbuf[]) throws IOException { out.write(cbuf); } public void write(String str) throws IOException { out.write(str); } public void write(String str, int off, int len) throws IOException { out.write(str, off, len); } public void write(char cbuf[], int off, int len) throws IOException { out.write(cbuf, off, len); } public void flush() throws IOException { // no-op } public void close() throws IOException { // no-op } // ------------------------------------------------------ Public Methods public static WriteBehindStateWriter getCurrentInstance() { return CUR_WRITER.get(); } public void release() { CUR_WRITER.remove(); } public void writingState() { if (!stateWritten) { this.stateWritten = true; out = fWriter = new FastStringWriter(1024); } } public boolean stateWritten() { return stateWritten; } /** *

Write directly from our FastStringWriter to the provided * writer.

* @throws IOException if an error occurs */ public void flushToWriter() throws IOException { // Save the state to a new instance of StringWriter to // avoid multiple serialization steps if the view contains // multiple forms. StateManager stateManager = Util.getStateManager(context); ResponseWriter origWriter = context.getResponseWriter(); FastStringWriter state = new FastStringWriter((stateManager.isSavingStateInClient( context)) ? bufSize : 128); context.setResponseWriter(origWriter.cloneWithWriter(state)); stateManager.writeState(context, stateManager.saveView(context)); context.setResponseWriter(origWriter); StringBuilder builder = fWriter.getBuffer(); // begin writing... int totalLen = builder.length(); StringBuilder stateBuilder = state.getBuffer(); int stateLen = stateBuilder.length(); int pos = 0; int tildeIdx = getNextDelimiterIndex(builder, pos); while (pos < totalLen) { if (tildeIdx != -1) { if (tildeIdx > pos && (tildeIdx - pos) > bufSize) { // there's enough content before the first ~ // to fill the entire buffer builder.getChars(pos, (pos + bufSize), buf, 0); orig.write(buf); pos += bufSize; } else { // write all content up to the first '~' builder.getChars(pos, tildeIdx, buf, 0); int len = (tildeIdx - pos); orig.write(buf, 0, len); // now check to see if the state saving string is // at the begining of pos, if so, write our // state out. if (builder.indexOf( RIConstants.SAVESTATE_FIELD_MARKER, pos) == tildeIdx) { // buf is effectively zero'd out at this point int statePos = 0; while (statePos < stateLen) { if ((stateLen - statePos) > bufSize) { // enough state to fill the buffer stateBuilder.getChars(statePos, (statePos + bufSize), buf, 0); orig.write(buf); statePos += bufSize; } else { int slen = (stateLen - statePos); stateBuilder.getChars(statePos, stateLen, buf, 0); orig.write(buf, 0, slen); statePos += slen; } } // push us past the last '~' at the end of the marker pos += (len + STATE_MARKER_LEN); tildeIdx = getNextDelimiterIndex(builder, pos); } else { pos = tildeIdx; tildeIdx = getNextDelimiterIndex(builder, tildeIdx + 1); } } } else { // we've written all of the state field markers. // finish writing content if (totalLen - pos > bufSize) { // there's enough content to fill the buffer builder.getChars(pos, (pos + bufSize), buf, 0); orig.write(buf); pos += bufSize; } else { // we're near the end of the response builder.getChars(pos, totalLen, buf, 0); int len = (totalLen - pos); orig.write(buf, 0, len); pos += (len + 1); } } } // all state has been written. Have 'out' point to the // response so that all subsequent writes will make it to the // browser. out = orig; } private static int getNextDelimiterIndex(StringBuilder builder, int offset) { return builder.indexOf(RIConstants.SAVESTATE_FIELD_DELIMITER, offset); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy