
org.apache.myfaces.tomahawk.application.jsp.JspTilesTwoViewHandlerImpl Maven / Gradle / Ivy
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.myfaces.tomahawk.application.jsp;
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.shared_tomahawk.config.MyfacesConfig;
import org.apache.myfaces.shared_tomahawk.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;
/**
* This ViewHandler should be used with myfaces core 1.2.4 or superior
* and it does not work with jsf ri 1.2
*
* @since 1.1.7
* @author Martin Marinschek
* @version $Revision: 691871 $ $Date: 2008-09-03 23:32:08 -0500 (Wed, 03 Sep 2008) $
*/
public class JspTilesTwoViewHandlerImpl
extends ViewHandler {
private ViewHandler _viewHandler;
public static final String FORM_STATE_MARKER = "";
public static final int FORM_STATE_MARKER_LEN = FORM_STATE_MARKER.length();
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 = JspTilesTwoViewHandlerImpl.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 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(),
facesContext.getExternalContext()
.getResponseCharacterEncoding());
}
response.flushBuffer();
}
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 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 role
parameter.
*
* @param request The request context.
* @param role The comma-separated list of roles.
* @return true
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 true
if the preparer is not found,
* it ignores the problem.
* @throws TilesException If the preparer is not found (and
* ignoreMissing
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(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 += 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
* beginIndex
to index endIndex - 1
.
*
* @param contentBuffer the StringBuffer
to copy content from
* @param begin the beginning index, inclusive.
* @param end the ending index, exclusive
* @param writer the Writer
to write to
* @throws IOException if an error occurs writing to specified Writer
*/
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;
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy