[
https://issues.apache.org/jira/browse/TOMAHAWK-922?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=12577436#action_12577436
]
Christian Küppers commented on TOMAHAWK-922:
--------------------------------------------
Hello, I migrated my web application a few days ago running in the same
problems.
I have adjusted the JspTilesTwoViewHandlerImpl for JSF1.2 and it works for me.
Maybe it should be refactured anymore.
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.tiles.access.TilesAccess;
import org.apache.tiles.context.BasicAttributeContext;
import org.apache.tiles.context.TilesRequestContext;
import org.apache.tiles.definition.NoSuchDefinitionException;
import org.apache.tiles.factory.TilesContainerFactory;
import org.apache.tiles.impl.BasicTilesContainer;
import org.apache.tiles.preparer.NoSuchPreparerException;
import org.apache.tiles.preparer.ViewPreparer;
import org.apache.tiles.servlet.context.ServletTilesApplicationContext;
import org.apache.tiles.servlet.context.ServletTilesRequestContext;
import org.apache.tiles.AttributeContext;
import org.apache.tiles.Definition;
import org.apache.tiles.TilesContainer;
import org.apache.tiles.TilesException;
import org.apache.myfaces.application.jsp.JspViewHandlerImpl;
import org.apache.myfaces.application.jsp.ViewResponseWrapper;
import org.apache.myfaces.shared_impl.config.MyfacesConfig;
import org.apache.myfaces.shared_impl.renderkit.html.util.JavascriptUtils;
import org.apache.myfaces.shared_tomahawk.webapp.webxml.ServletMapping;
import org.apache.myfaces.shared_tomahawk.webapp.webxml.WebXml;
import javax.faces.application.StateManager;
import javax.faces.application.ViewHandler;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import javax.faces.FacesException;
import javax.faces.FactoryFinder;
import javax.faces.component.UIViewRoot;
import javax.faces.render.RenderKit;
import javax.faces.render.RenderKitFactory;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import javax.servlet.ServletResponse;
import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;
import java.util.List;
import java.util.Locale;
import java.util.StringTokenizer;
/**
* @author Martin Marinschek
* @version $Revision: 472792 $ $Date: 2006-11-09 07:34:47 +0100 (Do, 09 Nov
2006) $
*/
public class JspTilesTwoViewHandlerImpl extends ViewHandler {
private ViewHandler _viewHandler;
private static final Log log =
LogFactory.getLog(JspTilesTwoViewHandlerImpl.class);
private static final String TILES_DEF_EXT = ".tiles";
private String tilesExtension = TILES_DEF_EXT;
private static final String AFTER_VIEW_TAG_CONTENT_PARAM =
JspViewHandlerImpl.class + ".AFTER_VIEW_TAG_CONTENT";
public JspTilesTwoViewHandlerImpl(ViewHandler viewHandler) {
_viewHandler = viewHandler;
}
private void initContainer(ExternalContext context) {
if(TilesAccess.getContainer(context.getContext())==null) {
try {
TilesContainerFactory factory =
TilesContainerFactory.getFactory(context.getContext());
TilesContainer container =
factory.createTilesContainer(context.getContext());
TilesAccess.setContainer(context.getContext(),container);
} catch (Exception e) {
throw new FacesException("Error reading tiles definitions : " +
e.getMessage(), e);
}
}
}
public void renderView(FacesContext facesContext, UIViewRoot viewToRender)
throws IOException, FacesException {
if (viewToRender == null) {
log.fatal("viewToRender must not be null");
throw new NullPointerException("viewToRender must not be null");
}
ExternalContext externalContext = facesContext.getExternalContext();
String viewId = deriveViewId(externalContext, viewToRender.getViewId());
if(viewId==null) {
//deriving view-id made clear we are not responsible for this
view-id - call the delegate
_viewHandler.renderView(facesContext, viewToRender);
return;
}
initContainer(externalContext);
String tilesId = deriveTileFromViewId(viewId);
TilesContainer container =
TilesAccess.getContainer(externalContext.getContext());
Object[] requestObjects = {externalContext.getRequest(),
externalContext.getResponse()};
if(container.isValidDefinition(tilesId, requestObjects)) {
//propagate our new view-id to wherever it makes sense
setViewId(viewToRender, viewId, facesContext);
renderTilesView(facesContext, requestObjects, container,
viewToRender, viewId, tilesId);
} else {
//we're not using tiles as no valid definition has been found
//just call the delegate view-handler to let it do its thing
_viewHandler.renderView(facesContext, viewToRender);
}
}
private String deriveTileFromViewId(String viewId) {
String tilesId = viewId;
int idx = tilesId.lastIndexOf('.');
if (idx > 0) {
tilesId = tilesId.substring(0, idx) + tilesExtension;
} else {
tilesId = tilesId + tilesExtension;
}
return tilesId;
}
private String deriveViewId(ExternalContext externalContext, String viewId)
{
ServletMapping servletMapping = getServletMapping(externalContext);
String defaultSuffix =
externalContext.getInitParameter(ViewHandler.DEFAULT_SUFFIX_PARAM_NAME);
String suffix = defaultSuffix != null ? defaultSuffix :
ViewHandler.DEFAULT_SUFFIX;
if (servletMapping.isExtensionMapping()) {
if (!viewId.endsWith(suffix)) {
int dot = viewId.lastIndexOf('.');
if (dot == -1) {
if (log.isTraceEnabled()) log.trace("Current viewId has no
extension, appending default suffix " + suffix);
return viewId + suffix;
} else {
if (log.isTraceEnabled()) log.trace("Replacing extension of
current viewId by suffix " + suffix);
return viewId.substring(0, dot) + suffix;
}
}
//extension-mapped page ends with proper suffix - all ok
return viewId;
} else if (!viewId.endsWith(suffix)) {
// path-mapping used, but suffix is no default-suffix
return null;
} else {
//path-mapping used, suffix is default-suffix - all ok
return viewId;
}
}
private void renderTilesView(FacesContext facesContext, Object[]
requestObjects, TilesContainer container, UIViewRoot viewToRender, String
viewId, String tilesId) throws IOException {
ExternalContext externalContext = facesContext.getExternalContext();
handleCharacterEncoding(viewId, externalContext, viewToRender);
container.startContext(requestObjects);
try {
//container.render(tilesId,requestObjects);
buildTilesViewLikeContainer(externalContext, container,
tilesId, requestObjects);
} catch (TilesException e) {
throw new FacesException(e);
}
finally {
container.endContext(requestObjects);
}
handleCharacterEncodingPostDispatch(externalContext);
// render the view in this method (since JSF 1.2)
RenderKitFactory renderFactory = (RenderKitFactory)
FactoryFinder.getFactory(FactoryFinder.RENDER_KIT_FACTORY);
RenderKit renderKit = renderFactory.getRenderKit(facesContext,
viewToRender.getRenderKitId());
ServletResponse response = (ServletResponse) requestObjects[1];
ResponseWriter responseWriter =
facesContext.getResponseWriter();
if (responseWriter == null) {
responseWriter =
renderKit.createResponseWriter(response.getWriter(), null,
((HttpServletRequest)
externalContext.getRequest()).getCharacterEncoding());
facesContext.setResponseWriter(responseWriter);
}
ResponseWriter oldResponseWriter = responseWriter;
StateMarkerAwareWriter stateAwareWriter = null;
StateManager stateManager =
facesContext.getApplication().getStateManager();
if (stateManager.isSavingStateInClient(facesContext)) {
stateAwareWriter = new StateMarkerAwareWriter();
// Create a new response-writer using as an underlying
writer the stateAwareWriter
// Effectively, all output will be buffered in the
stateAwareWriter so that later
// this writer can replace the state-markers with the
actual state.
responseWriter =
oldResponseWriter.cloneWithWriter(stateAwareWriter);
facesContext.setResponseWriter(responseWriter);
}
actuallyRenderView(facesContext, viewToRender);
//We're done with the document - now we can write all content
//to the response, properly replacing the state-markers on the way out
//by using the stateAwareWriter
if (stateManager.isSavingStateInClient(facesContext)) {
stateAwareWriter.flushToWriter(response.getWriter());
} else {
stateManager.saveView(facesContext);
}
// Final step - we output any content in the wrappedResponse
response from above to the response,
// removing the wrappedResponse response from the request, we don't
need it anymore
ViewResponseWrapper afterViewTagResponse = (ViewResponseWrapper)
externalContext.getRequestMap().get(AFTER_VIEW_TAG_CONTENT_PARAM);
externalContext.getRequestMap().remove(AFTER_VIEW_TAG_CONTENT_PARAM);
if (afterViewTagResponse != null) {
afterViewTagResponse.flushToWriter(response.getWriter());
}
response.flushBuffer();
}
private void handleCharacterEncodingPostDispatch(ExternalContext
externalContext) {
// handle character encoding as of section 2.5.2.2 of JSF 1.1
if (externalContext.getRequest() instanceof HttpServletRequest) {
HttpServletResponse response = (HttpServletResponse)
externalContext.getResponse();
HttpServletRequest request = (HttpServletRequest)
externalContext.getRequest();
HttpSession session = request.getSession(false);
if (session != null) {
session.setAttribute(ViewHandler.CHARACTER_ENCODING_KEY,
response.getCharacterEncoding());
}
}
}
private void handleCharacterEncoding(String viewId, ExternalContext
externalContext, UIViewRoot viewToRender) {
if (log.isTraceEnabled()) log.trace("Dispatching to " + viewId);
// handle character encoding as of section 2.5.2.2 of JSF 1.1
if (externalContext.getResponse() instanceof ServletResponse) {
ServletResponse response = (ServletResponse)
externalContext.getResponse();
response.setLocale(viewToRender.getLocale());
}
}
private void buildTilesViewLikeContainer(ExternalContext externalContext,
TilesContainer container, String definitionName, Object... requestItems) throws
TilesException {
if (log.isDebugEnabled()) {
log.debug("Render request recieved for definition '" +
definitionName + "'");
}
TilesRequestContext tilesRequest =
((BasicTilesContainer)container).getContextFactory().createRequestContext(container.getApplicationContext(),
requestItems);
Definition definition =
((BasicTilesContainer)container).getDefinitionsFactory().getDefinition(definitionName,
tilesRequest);
if (definition == null) {
if (log.isWarnEnabled()) {
String message = "Unable to find the definition '" +
definitionName + "'";
log.warn(message);
}
throw new NoSuchDefinitionException(definitionName);
}
if (!isPermitted(tilesRequest, definition.getRole())) {
log.info("Access to definition '" + definitionName + "' denied.
User not in role '" + definition.getRole());
return;
}
AttributeContext originalContext =
container.getAttributeContext(requestItems);
BasicAttributeContext subContext = new
BasicAttributeContext(originalContext);
subContext.addMissing(definition.getAttributes());
BasicAttributeContext.pushContext(subContext, tilesRequest);
try {
if (definition.getPreparer() != null) {
prepare(container, tilesRequest, definition.getPreparer(),
true);
}
String dispatchPath = definition.getTemplate();
if (log.isDebugEnabled()) {
log.debug("Dispatching to definition path '" +
definition.getTemplate() + " '");
}
if (!buildView(container, tilesRequest, externalContext,
dispatchPath)) {
//building the view was unsuccessful - an exception
occurred during rendering
//we need to jump out
return;
}
// tiles exception so that it doesn't need to be rethrown.
} catch (TilesException e) {
throw e;
} catch (Exception e) {
log.error("Error rendering tile", e);
throw new TilesException(e.getMessage(), e);
} finally {
BasicAttributeContext.popContext(tilesRequest);
}
}
/**
* Checks if the current user is in one of the comma-separated roles
* specified in the <code>role</code> parameter.
*
* @param request The request context.
* @param role The comma-separated list of roles.
* @return <code>true</code> if the current user is in one of those roles.
*/
private boolean isPermitted(TilesRequestContext request, String role) {
if (role == null) {
return true;
}
StringTokenizer st = new StringTokenizer(role, ",");
while (st.hasMoreTokens()) {
if (request.isUserInRole(st.nextToken())) {
return true;
}
}
return false;
}
/**
* Execute a preparer.
*
* @param context The request context.
* @param preparerName The name of the preparer.
* @param ignoreMissing If <code>true</code> if the preparer is not found,
* it ignores the problem.
* @throws TilesException If the preparer is not found (and
* <code>ignoreMissing</code> is not set) or if the preparer itself threw an
* exception.
*/
private void prepare(TilesContainer container, TilesRequestContext context,
String preparerName, boolean ignoreMissing) throws TilesException {
if (log.isDebugEnabled()) {
log.debug("Prepare request received for '" + preparerName);
}
ViewPreparer preparer =
((BasicTilesContainer)container).getPreparerFactory().getPreparer(preparerName,
context);
if (preparer == null && ignoreMissing) {
return;
}
if (preparer == null) {
throw new NoSuchPreparerException("Preparer '" + preparerName + "
not found");
}
AttributeContext attributeContext =
BasicAttributeContext.getContext(context);
preparer.execute(context, attributeContext);
}
/**Build the view-tree before rendering.
* This is done by dispatching to the underlying JSP-page, effectively
processing it, creating
* components out of any text in between JSF components (not rendering the
text to the output of course, this
* will happen later while rendering), attaching these components
* to the component tree, and buffering any content after the view-root.
*
* @param container The current TilesContainer
* @param tilesRequest The current TilesRequestContext - its response will
be replaced while the view-building happens (we want the text in the component
tree, not on the actual servlet output stream)
* @param externalContext The external context where the response will be
replaced while building
* @param viewId The view-id to dispatch to
* @return true if successfull, false if an error occurred during rendering
* @throws IOException
*/
private boolean buildView(TilesContainer container, TilesRequestContext
tilesRequest, ExternalContext externalContext, String viewId) throws
IOException {
HttpServletResponse response = (HttpServletResponse)
tilesRequest.getResponse();
ViewResponseWrapper wrappedResponse = new ViewResponseWrapper(response);
tilesRequest = new
ServletTilesRequestContext(((ServletTilesApplicationContext)container.getApplicationContext()).getServletContext(),
(HttpServletRequest)tilesRequest.getRequest(),
wrappedResponse);
tilesRequest.dispatch(viewId);
tilesRequest = new
ServletTilesRequestContext(((ServletTilesApplicationContext)container.getApplicationContext()).getServletContext(),
(HttpServletRequest)tilesRequest.getRequest(),
response);
boolean errorResponse = wrappedResponse.getStatus() < 200 ||
wrappedResponse.getStatus() > 299;
if (errorResponse) {
wrappedResponse.flushToWrappedResponse();
return false;
}
// store the wrapped response in the request, so it is thread-safe
externalContext.getRequestMap().put(AFTER_VIEW_TAG_CONTENT_PARAM,
wrappedResponse);
return true;
}
/**
* Render the view now - properly setting and resetting the response writer
*/
private void actuallyRenderView(FacesContext facesContext, UIViewRoot
viewToRender) throws IOException {
// Set the new ResponseWriter into the FacesContext, saving the old one
aside.
ResponseWriter responseWriter = facesContext.getResponseWriter();
//Now we actually render the document
// Call startDocument() on the ResponseWriter.
responseWriter.startDocument();
// Call encodeAll() on the UIViewRoot
viewToRender.encodeAll(facesContext);
// Call endDocument() on the ResponseWriter
responseWriter.endDocument();
responseWriter.flush();
}
private void setViewId(UIViewRoot viewToRender, String viewId, FacesContext
facesContext) {
viewToRender.setViewId(viewId);
if(facesContext.getViewRoot()!=null) {
facesContext.getViewRoot().setViewId(viewId);
}
}
private static ServletMapping getServletMapping(ExternalContext
externalContext) {
String servletPath = externalContext.getRequestServletPath();
String requestPathInfo = externalContext.getRequestPathInfo();
WebXml webxml = WebXml.getWebXml(externalContext);
List<?> mappings = webxml.getFacesServletMappings();
boolean isExtensionMapping = requestPathInfo == null;
for (int i = 0, size = mappings.size(); i < size; i++)
{
ServletMapping servletMapping = (ServletMapping) mappings.get(i);
if (servletMapping.isExtensionMapping() == isExtensionMapping)
{
String urlpattern = servletMapping.getUrlPattern();
if (isExtensionMapping)
{
String extension = urlpattern.substring(1,
urlpattern.length());
if (servletPath.endsWith(extension))
{
return servletMapping;
}
}
else
{
urlpattern = urlpattern.substring(0, urlpattern.length() -
2);
// servletPath starts with "/" except in the case where the
// request is matched with the "/*" pattern, in which case
// it is the empty string (see Servlet Sepc 2.3 SRV4.4)
if (servletPath.equals(urlpattern))
{
return servletMapping;
}
}
}
}
log.error("could not find pathMapping for servletPath = " + servletPath
+
" requestPathInfo = " + requestPathInfo);
throw new IllegalArgumentException("could not find pathMapping for
servletPath = " + servletPath +
" requestPathInfo = " + requestPathInfo);
}
public Locale calculateLocale(FacesContext context) {
return _viewHandler.calculateLocale(context);
}
public String calculateRenderKitId(FacesContext context) {
return _viewHandler.calculateRenderKitId(context);
}
public UIViewRoot createView(FacesContext context, String viewId) {
return _viewHandler.createView(context, viewId);
}
public String getActionURL(FacesContext context, String viewId) {
return _viewHandler.getActionURL(context, viewId);
}
public String getResourceURL(FacesContext context, String path) {
return _viewHandler.getResourceURL(context, path);
}
public UIViewRoot restoreView(FacesContext context, String viewId) {
return _viewHandler.restoreView(context, viewId);
}
public void writeState(FacesContext context) throws IOException {
_viewHandler.writeState(context);
}
/**
* Writes the response and replaces the state marker tags with the state
information for the current context
*/
private static class StateMarkerAwareWriter extends Writer
{
private StringBuilder buf;
public StateMarkerAwareWriter()
{
this.buf = new StringBuilder();
}
@Override
public void close() throws IOException
{
}
@Override
public void flush() throws IOException
{
}
@Override
public void write(char[] cbuf, int off, int len) throws
IOException
{
if ((off < 0) || (off > cbuf.length) || (len < 0) ||
((off + len) > cbuf.length) || ((off +
len) < 0)) {
throw new IndexOutOfBoundsException();
} else if (len == 0) {
return;
}
buf.append(cbuf, off, len);
}
public StringBuilder getStringBuilder()
{
return buf;
}
public void flushToWriter(Writer writer) throws IOException
{
FacesContext facesContext = FacesContext.getCurrentInstance();
StateManager stateManager =
facesContext.getApplication().getStateManager();
StringWriter stateWriter = new StringWriter();
ResponseWriter realWriter = facesContext.getResponseWriter();
facesContext.setResponseWriter(realWriter.cloneWithWriter(stateWriter));
Object serializedView = stateManager.saveView(facesContext);
stateManager.writeState(facesContext, serializedView);
facesContext.setResponseWriter(realWriter);
StringBuilder contentBuffer = getStringBuilder();
String state = stateWriter.getBuffer().toString();
ExternalContext extContext = facesContext.getExternalContext();
if (JavascriptUtils.isJavascriptAllowed(extContext) &&
MyfacesConfig.getCurrentInstance(extContext).isViewStateJavascript()) {
// If javascript viewstate is enabled no state markers were
written
write(contentBuffer, 0, contentBuffer.length(), writer);
writer.write(state);
} else {
// If javascript viewstate is disabled state markers must be
replaced
int lastFormMarkerPos = 0;
int formMarkerPos = 0;
// Find all state markers and write out actual state instead
while ((formMarkerPos =
contentBuffer.indexOf(JspViewHandlerImpl.FORM_STATE_MARKER, formMarkerPos)) >
-1)
{
// Write content before state marker
write(contentBuffer, lastFormMarkerPos, formMarkerPos,
writer);
// Write state and move position in buffer after marker
writer.write(state);
formMarkerPos +=
JspViewHandlerImpl.FORM_STATE_MARKER_LEN;
lastFormMarkerPos = formMarkerPos;
}
// Write content after last state marker
if (lastFormMarkerPos < contentBuffer.length()) {
write(contentBuffer, lastFormMarkerPos,
contentBuffer.length(), writer);
}
}
}
/**
* Writes the content of the specified StringBuffer from index
* <code>beginIndex</code> to index <code>endIndex - 1</code>.
*
* @param contentBuffer the <code>StringBuffer</code> to copy content
from
* @param begin the beginning index, inclusive.
* @param end the ending index, exclusive
* @param writer the <code>Writer</code> to write to
* @throws IOException if an error occurs writing to specified
<code>Writer</code>
*/
private void write(StringBuilder contentBuffer, int beginIndex, int
endIndex, Writer writer) throws IOException {
int index = beginIndex;
int bufferSize = 2048;
char[] bufToWrite = new char[bufferSize];
while (index < endIndex)
{
int maxSize = Math.min(bufferSize, endIndex - index);
contentBuffer.getChars(index, index + maxSize, bufToWrite, 0);
writer.write(bufToWrite, 0, maxSize);
index += bufferSize;
}
}
}
}
> JSF-1.2: JspTilesViewHandlerImpl
> --------------------------------
>
> Key: TOMAHAWK-922
> URL: https://issues.apache.org/jira/browse/TOMAHAWK-922
> Project: MyFaces Tomahawk
> Issue Type: Bug
> Components: Tiles
> Affects Versions: 1.1.5-SNAPSHOT
> Environment: MyFaces Tomahawk SVN HEAD + JSF-1.2_03 (Reference
> implementation) + JDK-1.5.0_11 + Tomcat-6.0.10
> Reporter: Jesper Pedersen
>
> The JspTilesViewHandlerImpl doesn't work under JSF-1.2 (RI) as it doesn't
> deliver any output.
> Steps:
> 1) Get myfaces-example-tiles-1.1.5-SNAPSHOT.war
> 2) Replace MyFaces-Core with JSF-1.2 (RI) in WEB-INF/lib
> 3) Deploy on Tomcat-6.0.10
> 4) Go to http://localhost:8080/myfaces-example-tiles-1.1.5-SNAPSHOT/
> Generated HTML:
> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
> <head>
> <meta http-equiv="Content-Type" content="text/html;CHARSET=iso-8859-1" />
> <title>Myfaces - Tiles</title>
> <link rel="stylesheet" type="text/css" href="css/tiles.css" />
> </head>
> </html>
> Basically I'm unable to use Tomahawk under JSF-1.2 currently, so any pointers
> on how to fix this issue or to provide more information would be great.
--
This message is automatically generated by JIRA.
-
You can reply to this email to add a comment to the issue online.