/*
* Copyright 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.
*
* $Header:$
*/
package org.apache.beehive.netui.pageflow;
import javax.servlet.Filter;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.FilterChain;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Set;
import java.util.Map;
import org.apache.struts.util.RequestUtils;
import org.apache.struts.util.MessageResources;
import org.apache.struts.action.ActionServlet;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.config.ModuleConfig;
import org.apache.struts.Globals;
import org.apache.beehive.netui.core.urls.URLRewriterService;
import org.apache.beehive.netui.util.logging.Logger;
import org.apache.beehive.netui.util.internal.FileUtils;
import org.apache.beehive.netui.util.internal.ServletUtils;
import org.apache.beehive.netui.pageflow.internal.DefaultURLRewriter;
import org.apache.beehive.netui.pageflow.internal.JavaControlUtils;
import org.apache.beehive.netui.pageflow.internal.InternalUtils;
import org.apache.beehive.netui.pageflow.internal.InternalConstants;
import org.apache.beehive.netui.pageflow.internal.AdapterManager;
import org.apache.beehive.netui.pageflow.internal.PageFlowRequestWrapper;
import org.apache.beehive.netui.pageflow.handler.Handlers;
import org.apache.beehive.netui.script.common.ImplicitObjectUtil;
/**
* Base class for Servlet Filters that run before Page Flow page requests.
*/
public abstract class PageFlowPageFilter implements Filter
{
private ServletContext _servletContext;
private ServletContainerAdapter _servletContainerAdapter;
private FlowControllerFactory _flowControllerFactory;
private static final Logger _log = Logger.getInstance( PageFlowPageFilter.class );
private static final String PREVENT_CACHE_ATTR = InternalConstants.ATTR_PREFIX + "preventCache";
protected PageFlowPageFilter()
{
}
PageFlowPageFilter( ServletContext servletContext )
{
_servletContext = servletContext;
_servletContainerAdapter = AdapterManager.getServletContainerAdapter( _servletContext );
_flowControllerFactory = FlowControllerFactory.get( servletContext );
}
public void init( FilterConfig filterConfig ) throws ServletException
{
_servletContext = filterConfig.getServletContext();
if ( ! PageFlowContextListener.isInit( _servletContext ) )
{
PageFlowContextListener.performInitializations( _servletContext );
}
_servletContainerAdapter = AdapterManager.getServletContainerAdapter( _servletContext );
_flowControllerFactory = FlowControllerFactory.get( _servletContext );
}
protected abstract Set getValidFileExtensions();
public void doFilter( ServletRequest request, ServletResponse response, FilterChain chain )
throws IOException, ServletException
{
if ( request instanceof HttpServletRequest && response instanceof HttpServletResponse )
{
HttpServletRequest httpRequest = ( HttpServletRequest ) request;
HttpServletResponse httpResponse = ( HttpServletResponse ) response;
//
// Don't do the filter if the request is in error.
//
Object errStatusCode = request.getAttribute( "javax.servlet.error.status_code" );
if ( errStatusCode != null )
{
if ( _log.isDebugEnabled() )
{
_log.debug( "Request has error status code " + errStatusCode + ". Skipping filter." );
}
continueChainNoWrapper( request, response, chain );
return;
}
String servletPath = InternalUtils.getDecodedServletPath( httpRequest );
String extension = FileUtils.getFileExtension( servletPath );
Set validFileExtensions = getValidFileExtensions();
if ( validFileExtensions != null && ! validFileExtensions.contains( extension ) )
{
if ( _log.isDebugEnabled() )
{
_log.debug( "Path " + servletPath +
" does not have an appropriate file extension. Skipping filter." );
}
continueChainNoWrapper( request, response, chain );
return;
}
if ( _log.isDebugEnabled() ) _log.debug( "Filtering request for path " + servletPath );
//
// If at an earlier stage in the request we determined that we should prevent caching,
// actually write the appropriate headers to the response now.
//
if ( request.getAttribute( PREVENT_CACHE_ATTR ) != null ) ServletUtils.preventCache( httpResponse );
//
// Initialize the ServletContext in the request. Often, we need access to the ServletContext when we only
// have a ServletRequest.
//
InternalUtils.setServletContext( httpRequest, _servletContext );
//
// Callback to the server adapter.
//
PageFlowEventReporter er = _servletContainerAdapter.getEventReporter();
_servletContainerAdapter.beginRequest( httpRequest, httpResponse );
RequestContext requestContext = new RequestContext( request, response );
er.beginPageRequest( requestContext );
long startTime = System.currentTimeMillis();
//
// Initialize the ControlBeanContext in the session.
//
JavaControlUtils.initializeControlContext( httpRequest, httpResponse, _servletContext );
//
// Register the default URLRewriter
//
URLRewriterService.registerURLRewriter( 0, request, new DefaultURLRewriter() );
PageFlowRequestWrapper rw = PageFlowRequestWrapper.unwrap( request );
boolean isForwardedRequest = rw != null && rw.isForwardedRequest();
try
{
ModuleConfig prevModuleConfig = RequestUtils.getRequestModuleConfig( httpRequest );
MessageResources prevMessageResources = ( MessageResources ) request.getAttribute( Globals.MESSAGES_KEY );
if ( rw == null || ! rw.isStayInCurrentModule() ) initializeModule( httpRequest, httpResponse );
try
{
//
// Initialize shared flows for the current request.
//
Map/*< String, SharedFlowController >*/ sharedFlows =
_flowControllerFactory.getSharedFlowsForRequest( requestContext );
ImplicitObjectUtil.loadSharedFlow( request, sharedFlows );
ImplicitObjectUtil.loadGlobalApp( request, PageFlowUtils.getGlobalApp( httpRequest ) );
//
// Make sure that the current PageFlowController is set up for this request.
//
PageFlowController curJpf;
if ( rw != null && rw.isStayInCurrentModule() )
{
rw.setStayInCurrentModule( false );
curJpf = PageFlowUtils.getCurrentPageFlow( httpRequest, _servletContext );
}
else
{
curJpf = _flowControllerFactory.getPageFlowForRequest( requestContext );
}
//
// If there is no pageflow for the current Struts module, than fall back to default
// Struts behavior, which is *not* to allow a page request to set the current module.
//
if ( curJpf == null )
{
InternalUtils.setCurrentModule( prevModuleConfig, request );
request.setAttribute( Globals.MESSAGES_KEY, prevMessageResources );
}
if ( _log.isDebugEnabled() )
{
_log.debug( "Current PageFlowController is: " + curJpf );
_log.debug( "Continuing with filter chain..." );
}
runPage( curJpf, httpRequest, httpResponse, chain );
}
catch ( ClassNotFoundException e )
{
throw new ServletException( e );
}
catch ( InstantiationException e )
{
throw new ServletException( e );
}
catch ( IllegalAccessException e )
{
throw new ServletException( e );
}
}
finally
{
//
// Clean up the ControlBeanContext in the session.
//
JavaControlUtils.uninitializeControlContext( httpRequest, httpResponse, _servletContext );
//
// Callback to the server adapter.
//
_servletContainerAdapter.endRequest( httpRequest, httpResponse );
long timeTaken = System.currentTimeMillis() - startTime;
er.endPageRequest( requestContext, timeTaken );
//
// If this is not a forwarded request, then commit any session-scoped changes that were stored in the
// request.
//
if ( ! isForwardedRequest )
{
Handlers.get( _servletContext ).getStorageHandler().applyChanges( requestContext );
}
}
}
else
{
continueChainNoWrapper( request, response, chain );
}
}
private void runPage( PageFlowController curJpf, HttpServletRequest request, HttpServletResponse response,
FilterChain chain )
throws IOException, ServletException
{
//
// Make sure that the pageflow's getRequest() and getResponse() will work while the page
// is being rendered, since methods on the pageflow may be called (through databinding
// or tags, or through direct reference).
//
if ( curJpf != null )
{
//
// We're going to bail out if there are too many concurrent requests for the same JPF.
// This prevents an attack that takes advantage of the fact that we synchronize requests
// to the same pageflow.
//
if ( curJpf.incrementRequestCount( request, response, _servletContext ) )
{
try
{
//
// Any databinding calls, indirect calls to getRequest(), etc. must be protected
// against conflicts from running action methods at the same time as rendering
// the page here. Synchronize on the JPF.
//
synchronized ( curJpf )
{
FlowController.PerRequestState newState =
new FlowController.PerRequestState( request, response, null );
FlowController.PerRequestState prevState = curJpf.setPerRequestState( newState );
ImplicitObjectUtil.loadImplicitObjects( request, response, _servletContext, curJpf );
//
// Tell the page flow that we're about to display a page so it can manage settings,
// such as previous page information, if needed in advance.
//
curJpf.beforePage();
try
{
chain.doFilter( request, response );
}
catch ( ServletException servletEx )
{
//
// If a ServletException escapes out of the page, let the current FlowController handle it.
//
if ( ! handleException( servletEx, curJpf, request, response ) ) throw servletEx;
}
catch ( IOException ioe )
{
//
// If an IOException escapes out of the page, let the current FlowController handle it.
//
if ( ! handleException( ioe, curJpf, request, response ) ) throw ioe;
}
catch ( Throwable th )
{
//
// If a Throwable escapes out of the page, let the current FlowController handle it.
//
if ( ! handleException( th, curJpf, request, response ) )
{
if ( th instanceof Error ) throw ( Error ) th;
throw new ServletException( th );
}
}
finally
{
curJpf.setPerRequestState( prevState );
}
}
}
finally
{
curJpf.decrementRequestCount( request );
}
}
}
else
{
ImplicitObjectUtil.loadImplicitObjects( request, response, _servletContext, null );
continueChainNoWrapper( request, response, chain );
}
}
private static void continueChainNoWrapper( ServletRequest request, ServletResponse response, FilterChain chain )
throws IOException, ServletException
{
//
// Remove our request wrapper -- the page doesn't need to see this.
//
if ( request instanceof PageFlowRequestWrapper )
{
request = ( ( PageFlowRequestWrapper ) request ).getHttpRequest();
}
chain.doFilter( request, response );
}
private boolean handleException( Throwable th, FlowController fc, HttpServletRequest request,
HttpServletResponse response )
{
try
{
ActionMapping mapping = InternalUtils.getCurrentActionMapping( request );
ActionForm form = InternalUtils.getCurrentActionForm( request );
ActionForward fwd = fc.handleException( th, mapping, form, request, response );
fc.getRequestProcessor().doActionForward( request, response, fwd );
return true;
}
catch ( Throwable t )
{
_log.error( "Exception while handling exception " + th.getClass().getName()
+ ". The original exception will be thrown.", th );
return false;
}
}
private void initializeModule( HttpServletRequest request, HttpServletResponse response )
throws IOException, ServletException
{
//
// Ensure that the right Struts module is selected, for use by the tags.
//
String curModulePath = PageFlowUtils.getModulePath( request );
ActionServlet as = InternalUtils.getActionServlet( _servletContext );
if ( as instanceof AutoRegisterActionServlet )
{
AutoRegisterActionServlet das = ( AutoRegisterActionServlet ) as;
das.ensureModuleRegistered( curModulePath, request );
}
ModuleConfig mc = InternalUtils.selectModule( curModulePath, request, _servletContext );
if ( mc == null )
{
//
// If we still haven't had success in selecting the module, see if we can dynamically register one.
//
if ( as instanceof AutoRegisterActionServlet )
{
AutoRegisterActionServlet das = ( AutoRegisterActionServlet ) as;
das.ensureModuleSelected( curModulePath, request, response );
}
}
}
/**
* Make sure that when this page is rendered, it will set headers in the response to prevent caching.
* Because these headers are lost on server forwards, we set a request attribute to cause the headers
* to be set right before the page is rendered.
*/
static void preventCache( HttpServletRequest request )
{
request.setAttribute( PREVENT_CACHE_ATTR, Boolean.TRUE );
}
public void destroy()
{
_servletContext = null;
}
}