You are absolutely correct that the forum solution is Sun RI specific,
but I am (impatiently I admit) simply looking for a solution to develop
with JSF 1.2 now along with Shale and Tiles. MyFaces 1.2.0 currently has
EL problems (one of the MyFaces developers stated that these problems
should be fixed "real soon" in a patch release) that forced me over to
the Sun RI. I then ran into the problem mentioned below.

To get around these two difficulties, I have successfully updated the
code found in the forum link mentioned previously to display tiles using
the latest Sun JSF RI v1.2_04p2 code and at least the Tiles jar that
ships with Shale as of this date---tiles-core-2.0-r468346-SNAPSHOT. I
have not yet tried the latest Tiles 2.0.4 version with my modifications.

For those who wish to _experiment_ with JSF 1.2 here is the
TilesViewHandler class as I modified it. 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. :-)

-=> 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.commons.logging.Log;
//import org.apache.commons.logging.LogFactory;

import org.apache.tiles.ComponentContext;
import org.apache.tiles.ComponentDefinition;
import org.apache.tiles.DefinitionsFactoryException;
import org.apache.tiles.NoSuchDefinitionException;
import org.apache.tiles.TilesRequestContext;
import org.apache.tiles.TilesUtil;
import org.apache.tiles.context.BasicTilesContextFactory;

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>Log instance for this class.</p>
*/
// private static final Log log = LogFactory.getLog(
// TilesViewHandler.class.getName());
/**
* <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 ComponentDefinition getTile(String name) {
if (name == null)
return null;

ExternalContext externalContext = FacesContext.getCurrentInstance()
.getExternalContext();
Object request = externalContext.getRequest();
Object context = externalContext.getContext();
Object response = externalContext.getResponse();
ComponentDefinition tile = null;
try {
TilesRequestContext tilesContext =
new BasicTilesContextFactory().createRequestContext(context,
request, response);
tile = TilesUtil.getDefinition(name, tilesContext);
} catch (NoSuchDefinitionException nsex) {
logger.log(Level.ALL, "Couldn't find Tiles definition.", nsex);
} catch (DefinitionsFactoryException dex) {
logger.log(Level.ALL, "Tiles error", 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);
ComponentDefinition 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, ComponentDefinition tile) throws java.io.IOException
{
ExternalContext externalContext = facesContext.getExternalContext();

Object request = externalContext.getRequest();
Object context = externalContext.getContext();
// TilesContext tilesContext = TilesContextFactory.getInstance(context,
request);
Object response = externalContext.getResponse();
TilesRequestContext tilesContext =
new BasicTilesContextFactory().createRequestContext(context,
request, response);
ComponentContext tileContext = ComponentContext.getContext(tilesContext);
if (tileContext == null) {
tileContext = new ComponentContext(tile.getAttributes());
ComponentContext.setContext(tileContext, tilesContext);
}
else
tileContext.addMissing(tile.getAttributes());


renderTile(facesContext, viewToRender, tile);
// dispatch to the tile's layout
// externalContext.dispatch(tile.getPath());
}

private void renderTile(FacesContext context, UIViewRoot viewToRender,
ComponentDefinition 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();

/* ResponseWriter responseWriter;
if (null != oldWriter) {
responseWriter = oldWriter.cloneWithWriter(response.getWriter());
} else {
responseWriter = newWriter.cloneWithWriter(response.getWriter());
}
context.setResponseWriter(responseWriter);

strWriter.flushToWriter(responseWriter);

if (null != oldWriter) {
context.setResponseWriter(oldWriter);
}

// write any AFTER_VIEW_CONTENT to the response
writeAfterViewContent(extContext, response);
*/
}

private boolean executePageToBuildView(FacesContext context, UIViewRoot
viewToExecute, ComponentDefinition 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(viewToExecute.getViewId(), mapping);
*/
String requestURI =
updateRequestURI(tile.getPath(), 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 beupdated
// 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
// ViewHandlerResponseWrapper wrapped =
// new ViewHandlerResponseWrapper(
// (HttpServletResponse)extContext.getResponse());
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
// if (wrapped.isBytes()) {
// extContext.getRequestMap().put(AFTER_VIEW_CONTENT,
// wrapped.getBytes());
// } else if (wrapped.isChars()) {
// extContext.getRequestMap().put(AFTER_VIEW_CONTENT,
// wrapped.getChars());
// }
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 new ViewHandlerResponseWrapper((HttpServletResponse) response);
}
throw new IllegalArgumentException();

}

/**
* <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 (!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>Returns true if the provided <code>url-mapping</code> is
* a prefix path mapping (starts with <code>/</code>).</p>
*
* @param mapping a <code>url-pattern</code>
* @return true if the mapping starts with <code>/</code>
private static boolean isPrefixMapped(String mapping) {
return (mapping.charAt(0) == '/');
}
*/

// ----------------------------------------------------------- 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


Gary VanMatre wrote:
> >Sorry about the empty post. Let's try again.
> >
> >There appears to be an unresolved bug from 02/Oct/06 reported against
> >this problem along with a workaround at:
> >https://issues.apache.org/struts/browse/SHALE-302
> >http://forum.java.sun.com/thread.jspa?threadID=770686&messageID=4408064
> <http://forum.java.sun.com/thread.jspa?threadID=770686&messageID=4408064>
> >
> > -=> Gregg <=-
>
> Gregg, thanks for providing more information. The issues that Shale
> has with regards to the proposed changes:
>
> * Shale doesn’t yet have a code branch specifically focusing on JSF
> 1.2. We have talked about doing this but have not made it a priority.
>
> * Shale tries to be RI neutral. The solution on the forum uses classes
> in the implementation of the Sun RI. This solution wouldn’t work with
> myfaces.
>
> Gary
>
>
> ------------------------------------------------------------------------
>
> Subject:
> Re: Any sucess with shale nightly 20070717 and RI JSF 1.2?
> From:
> Gregg Leichtman <[EMAIL PROTECTED]>
> Date:
> Wed, 25 Jul 2007 04:10:34 +0000
> To:
> user@shale.apache.org
>
> To:
> user@shale.apache.org
>
>
> Sorry about the empty post. Let's try again.
>
> There appears to be an unresolved bug from 02/Oct/06 reported against
> this problem along with a workaround at:
> https://issues.apache.org/struts/browse/SHALE-302
> <http://forum.java.sun.com/thread.jspa?threadID=770686&messageID=4408064>
>
>                                         -=> Gregg <=-
>
> Gregg Leichtman wrote:
>   
>> I have assembled a small sample project using:
>>     RI of JSF 1.2 v1.2_04_p2
>>     Shale nightly 20070717
>>     tiles-core-2.0-r468346-SNAPSHOT.jar from the Shale nightly distribution
>>     JSTL 1.1.2
>>     Tomcat 6.0.13
>>     Updated the faces-config.xml to use:
>>           <faces-config xmlns="http://java.sun.com/xml/ns/javaee";
>>               xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
>>               xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
>> http://java.sun.com/xml/ns/javaee/web-facesconfig_1_2.xsd";
>>               version="1.2">
>>     Updated the web.xml to use:
>>           <web-app version="2.5"
>>     xmlns="http://java.sun.com/xml/ns/j2ee";
>>     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
>>     xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
>>                         http://java.sun.com/xml/ns/j2ee/web-app_2_5.xsd";>
>>
>> I have the same sample project working successfully using:
>>     myfaces 1.1.4
>>     Shale nightly 20070409
>>     tiles-core-2.0-r468346-SNAPSHOT.jar from the Shale nightly distribution
>>     JSTL 1.1.2
>>     Tomcat 6.0.13
>>     faces-config.xml:
>>           <!DOCTYPE faces-config PUBLIC
>>   "-//Sun Microsystems, Inc.//DTD JavaServer Faces Config 1.1//EN"
>>   "http://java.sun.com/dtd/web-facesconfig_1_1.dtd";>
>>     web.xml:
>>           <web-app version="2.4"
>>     xmlns="http://java.sun.com/xml/ns/j2ee";
>>     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
>>     xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
>>                         http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd";>
>>
>> When I attempt to display the home page of the new project, I get a
>> blank page. I have checked the tomcat logs and there are no complaints.
>> The web page source from the page that is displayed consists only of:
>>
>> <html lang="en">
>>
>> </html>
>>
>> Using Live HTTP Headers inside FireFox yielded nothing of interest as
>> far as I can tell.
>>
>> Have I mixed incompatible components?
>>
>> If not does anyone have a suggestion short of debugging the JSF RI or
>> tiles source on how I can track down what is happening?
>>
>>                                              -=> Gregg <=-
>>
>>
>>
>>   
>>     
>
>   

Attachment: signature.asc
Description: OpenPGP digital signature

Reply via email to