[ 
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.

Reply via email to