/*
* Copyright 2002,2004 The Apache Software Foundation.
*
* Licensed 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.struts.faces.application;
import java.io.IOException;
import javax.faces.FactoryFinder;
import javax.faces.application.ViewHandler;
import javax.faces.component.UICommand;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.context.FacesContextFactory;
import javax.faces.event.ActionEvent;
import javax.faces.lifecycle.Lifecycle;
import javax.faces.lifecycle.LifecycleFactory;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.struts.Globals;
import org.apache.struts.action.Action;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.config.FormBeanConfig;
import org.apache.struts.config.ForwardConfig;
import org.apache.struts.faces.Constants;
import org.apache.struts.faces.component.FormComponent;
import org.apache.struts.faces.util.HttpServletRequestWrapper;
import org.apache.struts.tiles.TilesRequestProcessor;
/**
* <p>Concrete implementation of <code>RequestProcessor</code> that
* implements the standard Struts request processing lifecycle on a
* request that was received as an <code>ActionEvent</code> by our
* associated <code>ActionListener</code>. It replaces the request processor
* instance normally configured by Struts+Tiles, so it must support non-Faces
* requests as well.</p>
*
* @version $Revision: 1.6 $ $Date: 2004/06/29 14:57:18 $
*/
public class FacesTilesRequestProcessor extends TilesRequestProcessor {
// ------------------------------------------------------ Instance Variables
/**
* <p>The log instance for this class.</p>
*/
protected static Log log =
LogFactory.getLog(FacesTilesRequestProcessor.class);
// ------------------------------------------------------- Protected Methods
/**
* <p>Set up a Faces Request if we are not already processing one. Next,
* create a new view if the specified <code>uri</code> is different from
* the current view identifier. Finally, cause the new view to be
* rendered, and call <code>FacesContext.responseComplete()</code> to
* indicate that this has already been done.</p>
*
* @param uri Context-relative path to forward to
* @param request Current page request
* @param response Current page response
*
* @exception IOException if an input/output error occurs
* @exception ServletException if a servlet error occurs
*/
protected void doForward(String uri,
HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException {
if (log.isDebugEnabled()) {
log.debug("doForward(" + uri + ")");
}
// Remove the current ActionEvent (if any)
request.removeAttribute(Constants.ACTION_EVENT_KEY);
// Process a Struts controller request normally
if (isStrutsRequest(uri)) {
if (response.isCommitted()) {
if (log.isTraceEnabled()) {
log.trace(" super.doInclude(" + uri + ")");
}
super.doInclude(uri, request, response);
} else {
if (log.isTraceEnabled()) {
log.trace(" super.doForward(" + uri + ")");
}
super.doForward(uri, request, response);
}
return;
}
// Create a FacesContext for this request if necessary
LifecycleFactory lf = (LifecycleFactory)
FactoryFinder.getFactory(FactoryFinder.LIFECYCLE_FACTORY);
Lifecycle lifecycle = // FIXME - alternative lifecycle ids
lf.getLifecycle(LifecycleFactory.DEFAULT_LIFECYCLE);
FacesContext context = FacesContext.getCurrentInstance();
if (context == null) {
if (log.isTraceEnabled()) {
log.trace(" Creating new FacesContext for '" + uri + "'");
}
FacesContextFactory fcf = (FacesContextFactory)
FactoryFinder.getFactory(FactoryFinder.FACES_CONTEXT_FACTORY);
HttpServletRequestWrapper wrapper = new HttpServletRequestWrapper(request, uri);
context = fcf.getFacesContext(servlet.getServletContext(), wrapper,
response, lifecycle);
}
// Create a new view root
ViewHandler vh = context.getApplication().getViewHandler();
if (log.isTraceEnabled()) {
log.trace(" Creating new view for '" + uri + "'");
}
context.setViewRoot(vh.createView(context, uri));
// Cause the view to be rendered
if (log.isTraceEnabled()) {
log.trace(" Rendering view for '" + uri + "'");
}
lifecycle.render(context);
if (log.isTraceEnabled()) {
log.trace(" Marking request complete for '" + uri + "'");
}
context.responseComplete();
}
// Override default processing to provide logging
protected Action processActionCreate(HttpServletRequest request,
HttpServletResponse response,
ActionMapping mapping)
throws IOException {
if (log.isTraceEnabled()) {
log.trace("Performing standard action create");
}
Action result = super.processActionCreate(request, response, mapping);
if (log.isDebugEnabled()) {
log.debug("Standard action create returned " +
result.getClass().getName() + " instance");
}
return (result);
}
// Override default processing to provide logging
protected ActionForm processActionForm(HttpServletRequest request,
HttpServletResponse response,
ActionMapping mapping) {
if (log.isTraceEnabled()) {
log.trace("Performing standard action form processing");
String attribute = mapping.getAttribute();
if (attribute != null) {
String name = mapping.getName();
FormBeanConfig fbc = moduleConfig.findFormBeanConfig(name);
if (fbc != null) {
if ("request".equals(mapping.getScope())) {
log.trace(" Bean in request scope = " +
request.getAttribute(attribute));
} else {
log.trace(" Bean in session scope = " +
request.getSession().getAttribute(attribute));
}
} else {
log.trace(" No FormBeanConfig for '" + name + "'");
}
} else {
log.trace(" No form bean for this action");
}
}
ActionForm result =
super.processActionForm(request, response, mapping);
if (log.isDebugEnabled()) {
log.debug("Standard action form returned " +
result);
}
return (result);
}
// Override default processing to provide logging
protected ActionForward processActionPerform(HttpServletRequest request,
HttpServletResponse response,
Action action,
ActionForm form,
ActionMapping mapping)
throws IOException, ServletException {
if (log.isTraceEnabled()) {
log.trace("Performing standard action perform");
}
ActionForward result =
super.processActionPerform(request, response, action,
form, mapping);
if (log.isDebugEnabled()) {
log.debug("Standard action perform returned " +
result.getPath() + " forward path");
}
return (result);
}
// Override default processing to provide logging
protected boolean processForward(HttpServletRequest request,
HttpServletResponse response,
ActionMapping mapping)
throws IOException, ServletException {
if (log.isTraceEnabled()) {
log.trace("Performing standard forward handling");
}
boolean result = super.processForward
(request, response, mapping);
if (log.isDebugEnabled()) {
log.debug("Standard forward handling returned " + result);
}
return (result);
}
// Override default processing to provide logging
protected void processForwardConfig(HttpServletRequest request,
HttpServletResponse response,
ForwardConfig forward)
throws IOException, ServletException {
if (log.isTraceEnabled()) {
log.trace("Performing standard forward config handling");
}
super.processForwardConfig(request, response, forward);
if (log.isDebugEnabled()) {
log.debug("Standard forward config handling completed");
}
}
// Override default processing to provide logging
protected boolean processInclude(HttpServletRequest request,
HttpServletResponse response,
ActionMapping mapping)
throws IOException, ServletException {
if (log.isTraceEnabled()) {
log.trace("Performing standard include handling");
}
boolean result = super.processInclude
(request, response, mapping);
if (log.isDebugEnabled()) {
log.debug("Standard include handling returned " + result);
}
return (result);
}
/**
* <p>Identify and return the path component (from the request URI for a
* non-Faces request, or from the form event for a Faces request)
* that we will use to select an ActionMapping to dispatch with.
* If no such path can be identified, create an error response and return
* <code>null</code>.</p>
*
* @param request The servlet request we are processing
* @param response The servlet response we are creating
*
* @exception IOException if an input/output error occurs
*/
protected String processPath(HttpServletRequest request,
HttpServletResponse response)
throws IOException {
// Are we processing a Faces request?
ActionEvent event = (ActionEvent)
request.getAttribute(Constants.ACTION_EVENT_KEY);
// Handle non-Faces requests in the usual way
if (event == null) {
if (log.isTraceEnabled()) {
log.trace("Performing standard processPath() processing");
}
return (super.processPath(request, response));
}
// Calculate the path from the form name
UIComponent component = event.getComponent();
if (log.isTraceEnabled()) {
log.trace("Locating form parent for command component " +
event.getComponent());
}
while (!(component instanceof FormComponent)) {
component = component.getParent();
if (component == null) {
log.warn("Command component was not nested in a Struts form!");
return (null);
}
}
if (log.isTraceEnabled()) {
log.trace("Returning selected path of " +
((FormComponent) component).getAction());
}
return (((FormComponent) component).getAction());
}
/**
* <p>Populate the properties of the specified <code>ActionForm</code>
* instance from the request parameters included with this request,
* <strong>IF</strong> this is a non-Faces request. For a Faces request,
* this will have already been done by the <em>Update Model Values</em>
* phase of the request processing lifecycle, so all we have to do is
* recognize whether the request was cancelled or not.</p>
*
* @param request The servlet request we are processing
* @param response The servlet response we are creating
* @param form The ActionForm instance we are populating
* @param mapping The ActionMapping we are using
*
* @exception ServletException if thrown by RequestUtils.populate()
*/
protected void processPopulate(HttpServletRequest request,
HttpServletResponse response,
ActionForm form,
ActionMapping mapping)
throws ServletException {
// Are we processing a Faces request?
ActionEvent event = (ActionEvent)
request.getAttribute(Constants.ACTION_EVENT_KEY);
// Handle non-Faces requests in the usual way
if (event == null) {
if (log.isTraceEnabled()) {
log.trace("Performing standard processPopulate() processing");
}
super.processPopulate(request, response, form, mapping);
return;
}
// Faces Requests require no processing for form bean population
// so we need only check for the cancellation command name
if (log.isTraceEnabled()) {
log.trace("Faces request, so no processPopulate() processing");
}
UIComponent source = event.getComponent();
if (source instanceof UICommand) {
UICommand command = (UICommand) source;
if ("cancel".equals(((UICommand) source).getId())) {
if (log.isTraceEnabled()) {
log.trace("Faces request with cancel button pressed");
}
request.setAttribute(Globals.CANCEL_KEY, Boolean.TRUE);
}
}
}
// Override default processing to provide logging
protected boolean processValidate(HttpServletRequest request,
HttpServletResponse response,
ActionForm form,
ActionMapping mapping)
throws IOException, ServletException {
if (log.isTraceEnabled()) {
log.trace("Performing standard validation");
}
boolean result = super.processValidate
(request, response, form, mapping);
if (log.isDebugEnabled()) {
log.debug("Standard validation processing returned " + result);
}
return (result);
}
// --------------------------------------------------------- Private Methods
/**
* <p>Return <code>true</code> if the specified context-relative URI
* specifies a request to be processed by the Struts controller servlet.</p>
*
* @param uri URI to be checked
*/
private boolean isStrutsRequest(String uri) {
int question = uri.indexOf("?");
if (question >= 0) {
uri = uri.substring(0, question);
}
String mapping = (String)
servlet.getServletContext().getAttribute(Globals.SERVLET_KEY);
if (mapping == null) {
return (false);
} else if (mapping.startsWith("*.")) {
return (uri.endsWith(mapping.substring(1)));
} else if (mapping.endsWith("/*")) {
return (uri.startsWith(mapping.substring(0, mapping.length() - 2)));
} else {
return (false);
}
}
}