For those who wish to _experiment_ with JSF 1.2 RI ( created using
version 1.2_04p2 from Sun) only (MyFaces is not supported in this
code), here is an updated TilesViewHandler class as I modified it to
work with Tiles 2.0.4 (the latest released version as of this date).
Clearly, the code is far from production usable and I make no claims
to it or its usability for any purpose, but it does potentially
provide a bridge, for me at least, to use JSF 1.2 in my development
effort while I wait for the production version from the experts.  :-)

I politely request that you do not ask for help with this code from
the Shale or Tiles developers if you use it, since it is not released
by either development group. In addition my interest in this code
extends to its ability to support my needs, so I currently do not
intend to support it  beyond any issues I run into in my development
efforts prior to release of an official Shale version.

                    -=> Gregg <=-

------ TilesViewHandler.java

package org.apache.shale.tiles;

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.DebugUtil;
import com.sun.faces.util.MessageUtils;
import com.sun.faces.util.Util;
import com.sun.faces.application.InterweavingResponse;
//import com.sun.faces.application.ApplicationAssociate;
import com.sun.faces.application.ViewHandlerResponseWrapper;

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.text.MessageFormat;

import java.util.Iterator;
import java.util.Locale;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;


import org.apache.tiles.context.BasicAttributeContext;
import org.apache.tiles.Definition;
import org.apache.tiles.definition.Definitions;
import org.apache.tiles.definition.DefinitionsFactory;
import org.apache.tiles.definition.UrlDefinitionsFactory;
import org.apache.tiles.definition.DefinitionsFactoryException;
import org.apache.tiles.access.TilesAccess;
import org.apache.tiles.TilesContainer;
import org.apache.tiles.impl.BasicTilesContainer;

import org.apache.tiles.context.ChainedTilesContextFactory;
import org.apache.tiles.context.TilesRequestContext;
import org.apache.tiles.context.TilesContextFactory;


public class TilesViewHandler extends ViewHandler {

    // -------------------------------------------------------------
Constructor


    /**
     * <p>Stores the reference to the default view handler for later
use.</p>
     *
     * @param defaultViewHandler The default view handler
     */
    public TilesViewHandler(ViewHandler defaultViewHandler) {
        this.defaultViewHandler = defaultViewHandler;
        if (logger.isLoggable(Level.FINE)) {
            logger.log(Level.FINE,"Created ViewHandler instance ");
        }
    }

    // -------------------------------------------------------- Static
Variables


    /**
     * <p><code>MessageFormat</code> used to perform parameter
substitution.</p>
     */
    private MessageFormat format = new MessageFormat("");

    /**
     * <p>Message resources for this class.</p>
     */
    private static ResourceBundle bundle =
        ResourceBundle.getBundle("org.apache.shale.tiles.Bundle",
                Locale.getDefault(),
                TilesViewHandler.class.getClassLoader());

    /**
     * <p>The default JSF view handler.</p>
     */
    private ViewHandler defaultViewHandler = null;


    // -----------------------------------------------------
ViewHandler Methods

    // Log instance for this class
    private static final Logger logger = Util.getLogger(Util.FACES_LOGGER
                                                  +
Util.APPLICATION_LOGGER);

    private static final String AFTER_VIEW_CONTENT =
RIConstants.FACES_PREFIX+
                                                     "AFTER_VIEW_CONTENT";

//    private ApplicationAssociate associate;

    /**
     * <p>Store the value of <code>DEFAULT_SUFFIX_PARAM_NAME</code>
     * or, if that isn't defined, the value of <code>DEFAULT_SUFFIX</code>
     */
    private String contextDefaultSuffix;
    private int bufSize = -1;

    public String calculateRenderKitId(FacesContext context) {

        if (context == null) {
            String message = MessageUtils.getExceptionMessageString
            (MessageUtils.NULL_PARAMETERS_ERROR_MESSAGE_ID, "context");
            throw new NullPointerException(message);
        }

        Map<String,String> requestParamMap = context.getExternalContext()
        .getRequestParameterMap();
        String result = requestParamMap.get(
                ResponseStateManager.RENDER_KIT_ID_PARAM);

        if (result == null) {
            if (null ==
                (result =
context.getApplication().getDefaultRenderKitId())) {
                result = RenderKitFactory.HTML_BASIC_RENDER_KIT;
            }
        }
        return result;
    }


    /**
     * Attempts to find a matching locale based on <code>pref</code> and
     * list of supported locales, using the matching algorithm
     * as described in JSTL 8.3.2.
     * @param context the <code>FacesContext</code> 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<Locale> 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());
        }

    }


    /**
     * <p>This is a separate method to account for handling the content
     * after the view tag.</p>
     *
     * <p>Create a new ResponseWriter around this response's Writer.
     * Set it into the FacesContext, saving the old one aside.</p>
     *
     * <p>call encodeBegin(), encodeChildren(), encodeEnd() on the
     * argument <code>UIViewRoot</code>.</p>
     *
     * <p>Restore the old ResponseWriter into the FacesContext.</p>
     *
     * <p>Write out the after view content to the response's writer.</p>
     *
     * <p>Flush the response buffer, and remove the after view content
     * from the request scope.</p>
     *
     * @param context the <code>FacesContext</code> 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 {  

// responseRendered is package private in the Sun com.... package
hierarchy, so we don't
// have access to it. The method sets a flag to indicate to anyone who
queries the
// associated isResponseRendered method if the response has been
rendered or not.
// I'm not sure what effect this will have by not doing it, but it
seems unnecessary
// for our purposes, since any out-of-package inherited class wouldn't
have access to it
// anyway. gsl
//        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 (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;
    }

    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 (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
        int period = viewId.lastIndexOf('.');
        if (period < 0) {
            return (contextPath + viewId + mapping);
        } else if (!viewId.endsWith(mapping)) {
            return (contextPath + viewId.substring(0, period) + mapping);
        } else {
            return (contextPath + viewId);
        }

    }

    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<Locale> 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 getResourceURL(FacesContext context, String path) {
        ExternalContext extContext = context.getExternalContext();
        if (path.startsWith("/")) {
            return (extContext.getRequestContextPath() + path);
        } else {
            return path;
        }

    }

       /**
        * <p>Looks up a tile, given a name. If the tile does not
exist, and the
        * <code>name</code> begins with a slash ('/'), look for a tile
        * without the slash. If no tile is found, return
<code>null</code>.</p>
        *
        * @param name The tile to lookup
        */
       private Definition getTile(String name) {
          if (name == null)
             return null;

          ExternalContext externalContext =
FacesContext.getCurrentInstance()
                                                       
.getExternalContext();
          Object context = externalContext.getContext(); // servlet
context
          Definition tile = null;
  
          TilesContainer theContainer = TilesAccess.getContainer(context);
          DefinitionsFactory theFactory = ((BasicTilesContainer)
theContainer).getDefinitionsFactory();

          try {
              // Parse files.
              Definitions theDefinitions = null;
              if (theFactory instanceof UrlDefinitionsFactory) {
                  UrlDefinitionsFactory theUrlFactory =
(UrlDefinitionsFactory) theFactory;
                  if (theUrlFactory.refreshRequired()) {
                      theUrlFactory.refresh();
                  }
                  theDefinitions = theUrlFactory.readDefinitions();
              }
              else {
                  logger.log(Level.ALL, "Unsupported Tiles definitions
factory.", theFactory);
              }
              tile = theDefinitions.getDefinition(name);
          } catch (DefinitionsFactoryException dex) {
              logger.log(Level.ALL, "Invalid Tiles definitions factory
source or could not read definitions.", dex);
          }
          return tile;
       }

       /**
        * <p>Given a view ID, returns the name of the corresponding
tile. For
        * example, for a view ID of /tiles/example/main.jsp, the tile name
        * returned by this method would be /tiles/example/main.</p>
        *
        * @param viewId The view ID
        */
       private String getTileName(String viewId) {
          int suffixIndex = viewId.lastIndexOf('.');
          return suffixIndex != -1 ? viewId.substring(0, suffixIndex)
                                   : viewId;
       }

    public void renderView(FacesContext facesContext, UIViewRoot
viewToRender)
    throws IOException, FacesException {
        String viewId = viewToRender.getViewId();
        String tileName = getTileName(viewId);
        Definition tile = getTile(tileName);

        if (logger.isLoggable(Level.FINE)) {
            String message = null;
            try {
                message = bundle.getString("tiles.renderingView");
            } catch (MissingResourceException e) {
                message = "Rendering view {0}, looking for tile {1}";
            }
            synchronized(format) {
                format.applyPattern(message);
                message = format.format(new Object[] { viewId,
tileName });
            }
            logger.fine(message);
        }

        if (tile != null) {
            if (logger.isLoggable(Level.FINE)) {
                String message = null;
                try {
                    message = bundle.getString("tiles.dispatchingToTile");
                } catch (MissingResourceException e) {
                    message = "Dispatching to tile {0}";
                }
                synchronized(format) {
                    format.applyPattern(message);
                    message = format.format(new Object[] { tileName });
                }
                logger.fine(message);
            }
            dispatchToTile(facesContext, viewToRender, tile);
        }
        else {
            if (logger.isLoggable(Level.FINE)) {
                String message = null;
                try {
                    message =
bundle.getString("tiles.dispatchingToViewHandler");
                } catch (MissingResourceException e) {
                    message = "Dispatching {0} to the default view
handler";
                }
                synchronized(format) {
                    format.applyPattern(message);
                    message = format.format(new Object[] { viewId });
                }
                logger.fine(message);
            }
            defaultViewHandler.renderView(facesContext, viewToRender);
        }
    }

    private void dispatchToTile(FacesContext facesContext, UIViewRoot
viewToRender, Definition tile) throws java.io.IOException
    {
        ExternalContext externalContext =
facesContext.getExternalContext();

        Object request = externalContext.getRequest();
        Object context = externalContext.getContext();
        Object response = externalContext.getResponse();
        TilesContainer theContainer = TilesAccess.getContainer(context);
        TilesContextFactory theContextFactory = ((BasicTilesContainer)
theContainer).getContextFactory();
        TilesRequestContext tilesContext = null;
        if (theContextFactory instanceof ChainedTilesContextFactory) {
            ChainedTilesContextFactory theChainedContextFactory =
(ChainedTilesContextFactory) theContextFactory;
            tilesContext =
theChainedContextFactory.createRequestContext(TilesAccess.getApplicationContext(context),
request, response);
        }
        else {
            logger.log(Level.ALL, "Unsupported Tiles context
factory.", theContextFactory);
        }

        BasicAttributeContext tileContext = (BasicAttributeContext)
BasicAttributeContext.getContext(tilesContext);
        if (tileContext == null) {
            tileContext = new BasicAttributeContext(tile.getAttributes());
            BasicAttributeContext.pushContext(tileContext, tilesContext);
        }
        else
            tileContext.addMissing(tile.getAttributes());

        renderTile(facesContext, viewToRender, tile);
    }

    private void renderTile(FacesContext context, UIViewRoot
viewToRender, Definition tile) 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, tile)) {
                response.flushBuffer();
// responseRendered is package private in the Sun com.... package
hierarchy, so we don't
// have access to it. The method sets a flag to indicate to anyone who
queries the
// associated isResponseRendered method if the response has been
rendered or not.
// I'm not sure what effect this will have by not doing it, but it
seems unnecessary
// for our purposes, since any out-of-package inherited class wouldn't
have access to it
// anyway. gsl
//               
ApplicationAssociate.getInstance(extContext).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());
        }
        if (logger.isLoggable(Level.FINEST)) {
            logger.log(Level.FINEST, "+=+=+=+=+=+= Printout for " +
viewToRender.getViewId() + " about to render.");
            DebugUtil.printTree(viewToRender, logger, Level.FINEST);
        }

        // 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.getContextInitParameter(
                            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);

        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();
        }

        // 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
        InterweavingResponse wrapper = (InterweavingResponse)
              extContext.getRequestMap().remove(AFTER_VIEW_CONTENT);
        if (null != wrapper) {
            wrapper.flushToWriter(response.getWriter(),
                    response.getCharacterEncoding());
        }
       
        response.flushBuffer();
    }

    private boolean executePageToBuildView(FacesContext context,
UIViewRoot viewToExecute, Definition tile)
    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);
        }

        String mapping = Util.getFacesMapping(context);
        String requestURI = updateRequestURI(tile.getTemplate(), mapping);

        if (mapping.equals(requestURI)) {
            // 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
            HttpServletResponse response = (HttpServletResponse)
            context.getExternalContext().getResponse();
            response.sendError(HttpServletResponse.SC_NOT_FOUND);
            return true;
        }

        if (logger.isLoggable(Level.FINE)) {
            logger.fine("About to execute view " + requestURI);
        }

        ExternalContext extContext = context.getExternalContext();

//        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.
//        PENDING: this only works for servlet based requests
        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
        InterweavingResponse 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
        extContext.getRequestMap().put(AFTER_VIEW_CONTENT, wrapped);

        return false;

    }

    /**
     * <p>if the specified mapping is a prefix mapping, and the provided
     * request URI (usually the value from
<code>ExternalContext.getRequestServletPath()</code>)
     * starts with <code>mapping + '/'</code>, 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 updateRequestURI(String uri, String mapping) {
       
        if (!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;
        }
    }  

    /**
     * <p>if the specified mapping is a prefix mapping, and the provided
     * request URI (usually the value from
<code>ExternalContext.getRequestServletPath()</code>)
     * starts with <code>mapping + '/'</code>, 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);
        }
    }


    /**
     * <p>Adjust the viewID per the requirements of [EMAIL PROTECTED]
#renderView}.</p>
     *
     * @param context current [EMAIL PROTECTED] 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 (contextDefaultSuffix == null) {
            contextDefaultSuffix =
                  WebConfiguration
                        .getInstance(context.getExternalContext())
                       
.getContextInitParameter(WebContextInitParameter.JspDefaultSuffix);
            if (contextDefaultSuffix == null) {
                contextDefaultSuffix = ViewHandler.DEFAULT_SUFFIX;
            }
            if (logger.isLoggable(Level.FINE)) {
                logger.fine("contextDefaultSuffix "
                            + contextDefaultSuffix);
            }
        }
          
        String convertedViewId = viewId;
        // if the viewId doesn't already use the above suffix,
        // replace or append.
        if (!convertedViewId.endsWith(contextDefaultSuffix)) {
            StringBuilder buffer = new StringBuilder(convertedViewId);
            int extIdx = convertedViewId.lastIndexOf('.');
            if (extIdx != -1) {
                buffer.replace(extIdx, convertedViewId.length(),
                               contextDefaultSuffix);
            } else {
                // no extension in the provided viewId, append the suffix
                buffer.append(contextDefaultSuffix);
            }
            convertedViewId = buffer.toString();
            if (logger.isLoggable(Level.FINE)) {
                logger.fine( "viewId after appending the context
suffix " +
                             convertedViewId);
            }

        }
        return convertedViewId;
    }


//    private ApplicationAssociate getAssociate(FacesContext context) {
//        if (associate == null) {
//            associate =
ApplicationAssociate.getInstance(context.getExternalContext());
//        }
//        return associate;
//    }


    private static InterweavingResponse getWrapper(ExternalContext
extContext) {
        Object response = extContext.getResponse();
        if (response instanceof HttpServletResponse) {
            return (InterweavingResponse) 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<WriteBehindStateWriter>
CUR_WRITER =
             new ThreadLocal<WriteBehindStateWriter>();
        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.set(null);
        }


        public void writingState() {
            if (!stateWritten) {
                this.stateWritten = true;
                out = fWriter = new FastStringWriter(1024);
            }
        }

        public boolean stateWritten() {
            return stateWritten;
        }

        /**
         * <p> Write directly from our FastStringWriter to the provided
         * writer.</p>
         * @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) {
                        // theres 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);
                    }
                }
            }
        }

        private static int getNextDelimiterIndex(StringBuilder builder,
                                                 int offset) {
            return builder.indexOf(RIConstants.SAVESTATE_FIELD_DELIMITER,
                                   offset);
        }

    }
   
   
}

------ End TilesViewHandler.java

                          -=> Gregg <=-

Attachment: signature.asc
Description: OpenPGP digital signature

Reply via email to