0001: /*
0002: * Licensed to the Apache Software Foundation (ASF) under one or more
0003: * contributor license agreements. See the NOTICE file distributed with
0004: * this work for additional information regarding copyright ownership.
0005: * The ASF licenses this file to You under the Apache License, Version 2.0
0006: * (the "License"); you may not use this file except in compliance with
0007: * the License. You may obtain a copy of the License at
0008: *
0009: * http://www.apache.org/licenses/LICENSE-2.0
0010: *
0011: * Unless required by applicable law or agreed to in writing, software
0012: * distributed under the License is distributed on an "AS IS" BASIS,
0013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0014: * See the License for the specific language governing permissions and
0015: * limitations under the License.
0016: *
0017: * $Header:$
0018: */
0019: package org.apache.beehive.netui.pageflow;
0020:
0021: import org.apache.beehive.netui.core.urls.URLRewriterService;
0022: import org.apache.beehive.netui.core.urls.TemplatedURLFormatter;
0023: import org.apache.beehive.netui.core.urltemplates.URLTemplatesFactory;
0024: import org.apache.beehive.netui.pageflow.config.PageFlowActionForward;
0025: import org.apache.beehive.netui.pageflow.config.PageFlowActionMapping;
0026: import org.apache.beehive.netui.pageflow.handler.ActionForwardHandler;
0027: import org.apache.beehive.netui.pageflow.handler.FlowControllerHandlerContext;
0028: import org.apache.beehive.netui.pageflow.handler.ForwardRedirectHandler;
0029: import org.apache.beehive.netui.pageflow.handler.Handlers;
0030: import org.apache.beehive.netui.pageflow.handler.LoginHandler;
0031: import org.apache.beehive.netui.pageflow.handler.ReloadableClassHandler;
0032: import org.apache.beehive.netui.pageflow.interceptor.InterceptorException;
0033: import org.apache.beehive.netui.pageflow.interceptor.Interceptors;
0034: import org.apache.beehive.netui.pageflow.interceptor.Interceptor;
0035: import org.apache.beehive.netui.pageflow.interceptor.action.ActionInterceptorContext;
0036: import org.apache.beehive.netui.pageflow.interceptor.action.InterceptorForward;
0037: import org.apache.beehive.netui.pageflow.interceptor.action.internal.ActionInterceptors;
0038: import org.apache.beehive.netui.pageflow.interceptor.request.RequestInterceptorContext;
0039: import org.apache.beehive.netui.pageflow.internal.*;
0040: import org.apache.beehive.netui.pageflow.scoping.ScopedRequest;
0041: import org.apache.beehive.netui.pageflow.scoping.ScopedServletUtils;
0042: import org.apache.beehive.netui.script.common.ImplicitObjectUtil;
0043: import org.apache.beehive.netui.util.internal.concurrent.InternalConcurrentHashMap;
0044: import org.apache.beehive.netui.util.internal.DiscoveryUtils;
0045: import org.apache.beehive.netui.util.internal.FileUtils;
0046: import org.apache.beehive.netui.util.internal.InternalStringBuilder;
0047: import org.apache.beehive.netui.util.internal.ServletUtils;
0048: import org.apache.beehive.netui.util.config.ConfigUtil;
0049: import org.apache.beehive.netui.util.config.bean.PageFlowConfig;
0050: import org.apache.beehive.netui.util.config.bean.PreventCache;
0051: import org.apache.beehive.netui.util.logging.Logger;
0052: import org.apache.struts.Globals;
0053: import org.apache.struts.action.Action;
0054: import org.apache.struts.action.ActionForm;
0055: import org.apache.struts.action.ActionForward;
0056: import org.apache.struts.action.ActionMapping;
0057: import org.apache.struts.action.ActionServlet;
0058: import org.apache.struts.action.ActionErrors;
0059: import org.apache.struts.action.InvalidCancelException;
0060: import org.apache.struts.config.ActionConfig;
0061: import org.apache.struts.config.FormBeanConfig;
0062: import org.apache.struts.config.ForwardConfig;
0063: import org.apache.struts.config.ModuleConfig;
0064: import org.apache.struts.tiles.TilesRequestProcessor;
0065: import org.apache.struts.tiles.TilesUtil;
0066: import org.apache.struts.tiles.TilesUtilImpl;
0067: import org.apache.struts.tiles.TilesUtilStrutsImpl;
0068: import org.apache.struts.upload.MultipartRequestHandler;
0069: import org.apache.struts.upload.MultipartRequestWrapper;
0070: import org.apache.struts.util.RequestUtils;
0071: import org.apache.struts.util.TokenProcessor;
0072:
0073: import javax.servlet.FilterChain;
0074: import javax.servlet.Servlet;
0075: import javax.servlet.ServletConfig;
0076: import javax.servlet.ServletContext;
0077: import javax.servlet.ServletException;
0078: import javax.servlet.ServletRequest;
0079: import javax.servlet.ServletResponse;
0080: import javax.servlet.http.HttpServletRequest;
0081: import javax.servlet.http.HttpServletResponse;
0082: import javax.servlet.http.HttpSession;
0083: import java.io.IOException;
0084: import java.io.InputStream;
0085: import java.io.Serializable;
0086: import java.lang.reflect.Field;
0087: import java.lang.reflect.Modifier;
0088: import java.net.URI;
0089: import java.net.URISyntaxException;
0090: import java.util.*;
0091:
0092: /**
0093: * The Page Flow extension of the Struts RequestProcessor, which contains callbacks that are invoked
0094: * during processing of a request to the Struts action servlet. This class is registered as the
0095: * <strong>controller</strong> for all Struts modules derived from page flows.
0096: */
0097: public class PageFlowRequestProcessor extends TilesRequestProcessor
0098: implements Serializable, InternalConstants, PageFlowConstants {
0099: private static int requestNumber = 0;
0100:
0101: private static final Logger LOG = Logger
0102: .getInstance(PageFlowRequestProcessor.class);
0103: private static final String ACTION_OVERRIDE_PARAM_PREFIX = "actionOverride:";
0104: private static final int ACTION_OVERRIDE_PARAM_PREFIX_LEN = ACTION_OVERRIDE_PARAM_PREFIX
0105: .length();
0106: private static final String SCHEME_UNSECURE = "http";
0107: private static final String SCHEME_SECURE = "https";
0108: private static final String REDIRECT_REQUEST_ATTRS_PREFIX = InternalConstants.ATTR_PREFIX
0109: + "requestAttrs:";
0110: private static final String REDIRECT_REQUEST_ATTRS_PARAM = "forceRedirect";
0111: private static final String FLOW_CONTROLLER_ACTION_CLASSNAME = FlowControllerAction.class
0112: .getName();
0113:
0114: private Map/*< String, Class >*/_formBeanClasses = new HashMap/*< String, Class >*/();
0115: private Map/*< String, List< ActionMapping > >*/_overloadedActions = new HashMap/*< String, List< ActionMapping > >*/();
0116: private Handlers _handlers;
0117: private FlowControllerFactory _flowControllerFactory;
0118: private LegacySettings _legacySettings;
0119: private InternalConcurrentHashMap/*< String, Class >*/_pageServletClasses = new InternalConcurrentHashMap/*< String, Class >*/();
0120: private transient ServletContainerAdapter _servletContainerAdapter;
0121: private transient PageFlowPageFilter _pageServletFilter;
0122:
0123: protected Action processActionCreate(HttpServletRequest request,
0124: HttpServletResponse response, ActionMapping actionMapping)
0125: throws IOException {
0126: String className = actionMapping.getType();
0127:
0128: if (className != null
0129: && className.equals(FLOW_CONTROLLER_ACTION_CLASSNAME)) {
0130: FlowController fc = PageFlowRequestWrapper.get(request)
0131: .getCurrentFlowController();
0132: assert fc != null : "no FlowController for request "
0133: + request.getRequestURI();
0134: assert fc.getClass().getName().equals(
0135: actionMapping.getParameter()) : "current page flow type "
0136: + fc.getClass().getName()
0137: + " does not match type specified in "
0138: + FLOW_CONTROLLER_ACTION_CLASSNAME
0139: + ": "
0140: + actionMapping.getParameter();
0141:
0142: Action action = new FlowControllerAction(fc);
0143: action.setServlet(servlet);
0144: return action;
0145: }
0146:
0147: return super .processActionCreate(request, response,
0148: actionMapping);
0149: }
0150:
0151: /**
0152: * See if this action mapping is our custom config type, and if so, see if the action should use a member variable
0153: * in the page flow controller as its form bean (the <code>useFormBean</code> attribute on
0154: * <code>@Jpf.Action</code>). If so, return the appropriate Field in the controller class.
0155: */
0156: private Field getPageFlowScopedFormMember(ActionMapping mapping,
0157: HttpServletRequest request) {
0158: if (mapping instanceof PageFlowActionMapping) {
0159: PageFlowActionMapping pfam = (PageFlowActionMapping) mapping;
0160: String formMember = pfam.getFormMember();
0161: if (formMember == null)
0162: return null;
0163:
0164: Field field = null;
0165: FlowController fc = PageFlowRequestWrapper.get(request)
0166: .getCurrentFlowController();
0167: try {
0168: field = fc.getClass().getDeclaredField(formMember);
0169: } catch (NoSuchFieldException e) {
0170: // try finding a non-private field from the class hierarchy
0171: field = InternalUtils.lookupField(fc.getClass(),
0172: formMember);
0173: if (field == null
0174: || Modifier.isPrivate(field.getModifiers())) {
0175: LOG.error("Could not find page flow member "
0176: + formMember + " as the form bean.");
0177: return null;
0178: }
0179: }
0180:
0181: if (!Modifier.isPublic(field.getModifiers()))
0182: field.setAccessible(true);
0183: return field;
0184: }
0185:
0186: return null;
0187: }
0188:
0189: protected ActionForm processActionForm(HttpServletRequest request,
0190: HttpServletResponse response, ActionMapping mapping) {
0191: //
0192: // See if we're using a pageflow-scoped form (a member variable in the current pageflow).
0193: //
0194: Field formMemberField = getPageFlowScopedFormMember(mapping,
0195: request);
0196:
0197: //
0198: // First look to see whether the input form was overridden in the request.
0199: // This happens when a pageflow action forwards to another pageflow,
0200: // whose begin action expects a form. In this case, the form is already
0201: // constructed, and shouldn't be instantiated anew or populated from request
0202: // parameters.
0203: //
0204: ActionForm previousForm = InternalUtils.getForwardedFormBean(
0205: request, false);
0206:
0207: if (previousForm != null) {
0208: //
0209: // If there was a forwarded form, and if this action specifies a pageflow-scoped form member,
0210: // set the member with this form.
0211: //
0212: if (formMemberField != null) {
0213: try {
0214: FlowController fc = PageFlowRequestWrapper.get(
0215: request).getCurrentFlowController();
0216: assert fc != null : "no FlowController in request "
0217: + request.getRequestURI();
0218: formMemberField.set(fc, InternalUtils
0219: .unwrapFormBean(previousForm));
0220: } catch (IllegalAccessException e) {
0221: LOG.error("Could not access page flow member "
0222: + formMemberField.getName()
0223: + " as the form bean.", e);
0224: }
0225: }
0226:
0227: //
0228: // Return the forwarded form.
0229: //
0230: previousForm.setServlet(servlet);
0231: return previousForm;
0232: }
0233:
0234: //
0235: // First see if the previous action put a pageflow-scoped form in the request. If so, remove it;
0236: // we don't want a normal request-scoped action to use this pageflow-scoped form.
0237: //
0238: String pageFlowScopedFormName = PageFlowRequestWrapper.get(
0239: request).getPageFlowScopedFormName();
0240: if (pageFlowScopedFormName != null) {
0241: request.removeAttribute(pageFlowScopedFormName);
0242: PageFlowRequestWrapper.get(request)
0243: .setPageFlowScopedFormName(null);
0244: }
0245:
0246: //
0247: // If this action specifies a pageflow-scoped form member variable, use it.
0248: //
0249: if (formMemberField != null) {
0250: try {
0251: FlowController fc = PageFlowRequestWrapper.get(request)
0252: .getCurrentFlowController();
0253: ActionForm form = InternalUtils
0254: .wrapFormBean(formMemberField.get(fc));
0255:
0256: if (form == null) // the pageflow hasn't filled the value yet
0257: {
0258: form = InternalUtils.createActionForm(mapping,
0259: moduleConfig, servlet, getServletContext());
0260: form.reset(mapping, request);
0261: formMemberField.set(fc, InternalUtils
0262: .unwrapFormBean(form));
0263: }
0264:
0265: //
0266: // Store the form in the right place in the request, so Struts can see it.
0267: // But, mark a flag so we know to remove this on the next action request -- we don't
0268: // want this form used by another action unless that action uses the pageflow-scoped
0269: // form.
0270: //
0271: String formAttrName = mapping.getAttribute();
0272: request.setAttribute(formAttrName, form);
0273: PageFlowRequestWrapper.get(request)
0274: .setPageFlowScopedFormName(formAttrName);
0275: return form;
0276: } catch (IllegalAccessException e) {
0277: LOG.error("Could not access page flow member "
0278: + formMemberField.getName()
0279: + " as the form bean.", e);
0280: }
0281: }
0282:
0283: ActionForm bean = super .processActionForm(request, response,
0284: mapping);
0285: if (bean == null) {
0286: bean = InternalUtils.createActionForm(mapping,
0287: moduleConfig, servlet, getServletContext());
0288: }
0289:
0290: return bean;
0291: }
0292:
0293: protected void processPopulate(HttpServletRequest request,
0294: HttpServletResponse response, ActionForm form,
0295: ActionMapping mapping) throws ServletException {
0296: //
0297: // If a previous action forwarded us a form, use that -- don't populate it from request parameters.
0298: //
0299: ActionForm previousForm = InternalUtils.getForwardedFormBean(
0300: request, true);
0301:
0302: if (previousForm != null) {
0303: return;
0304: }
0305:
0306: if (LOG.isDebugEnabled()) {
0307: LOG.debug("Populating bean properties from this request");
0308: }
0309:
0310: // struts does this
0311: if (form != null) {
0312: form.setServlet(servlet);
0313: form.reset(mapping, request);
0314: }
0315:
0316: if (mapping.getMultipartClass() != null) {
0317: request.setAttribute(Globals.MULTIPART_KEY, mapping
0318: .getMultipartClass());
0319: }
0320:
0321: PageFlowRequestWrapper requestWrapper = PageFlowRequestWrapper
0322: .get(request);
0323: boolean alreadyCalledInRequest = requestWrapper
0324: .isProcessPopulateAlreadyCalled();
0325: if (!alreadyCalledInRequest)
0326: requestWrapper.setProcessPopulateAlreadyCalled(true);
0327:
0328: //
0329: // If this is a forwarded request and the form-bean is null, don't call to ProcessPopulate.
0330: // We don't want to expose errors due to parameters from the original request, which won't
0331: // apply to a forwarded action that doesn't take a form.
0332: //
0333: if (!alreadyCalledInRequest || form != null) {
0334: // If this request was forwarded by a button-override of the main form action, then ensure that there are
0335: // no databinding errors when the override action does not use a form bean.
0336: if (form == null && requestWrapper.isForwardedByButton()) {
0337: // This is currently TODO; I removed the use of NullActionForm.
0338: // See http://issues.apache.org/jira/browse/BEEHIVE-947 .
0339: }
0340:
0341: ProcessPopulate.populate(request, response, form,
0342: alreadyCalledInRequest);
0343: }
0344: }
0345:
0346: /**
0347: * The requested action can be overridden by a request parameter. In this case, we parse the action from
0348: * the request parameter and forward to a URI constructed from it.
0349: *
0350: * @param request the current HttpServletRequest
0351: * @param response the current HttpServletResponse
0352: * @return <code>true</code> if the action was overridden by a request parameter, in which case the request
0353: * was forwarded.
0354: * @throws IOException
0355: * @throws ServletException
0356: */
0357: protected boolean processActionOverride(HttpServletRequest request,
0358: HttpServletResponse response) throws IOException,
0359: ServletException {
0360: // Only make this check if this is an initial (non-forwarded) request.
0361: //
0362: // TODO: performance?
0363: //
0364: PageFlowRequestWrapper wrapper = PageFlowRequestWrapper
0365: .get(request);
0366: if (!wrapper.isForwardedByButton()
0367: && !wrapper.isForwardedRequest()) {
0368: //
0369: // First, since we need access to request parameters here, process a multipart request
0370: // if that's what we have. This puts the parameters (each in a MIME part) behind an
0371: // interface that makes them look like normal request parameters.
0372: //
0373: HttpServletRequest multipartAwareRequest = processMultipart(request);
0374:
0375: for (Enumeration e = multipartAwareRequest
0376: .getParameterNames(); e.hasMoreElements();) {
0377: String paramName = (String) e.nextElement();
0378:
0379: if (paramName.startsWith(ACTION_OVERRIDE_PARAM_PREFIX)) {
0380: String actionPath = paramName
0381: .substring(ACTION_OVERRIDE_PARAM_PREFIX_LEN);
0382: ServletContext servletContext = getServletContext();
0383:
0384: String qualifiedAction = InternalUtils
0385: .qualifyAction(servletContext, actionPath);
0386: actionPath = InternalUtils.createActionPath(
0387: request, qualifiedAction);
0388:
0389: if (LOG.isDebugEnabled()) {
0390: LOG
0391: .debug("A request parameter overrode the action. Forwarding to: "
0392: + actionPath);
0393: }
0394:
0395: wrapper.setForwardedByButton(true);
0396: doForward(actionPath, request, response);
0397: return true;
0398: }
0399: }
0400: }
0401:
0402: return false;
0403: }
0404:
0405: /**
0406: * Internal method used to process an action.
0407: *
0408: * @param request the current request
0409: * @param response the current response
0410: * @throws IOException
0411: * @throws ServletException
0412: */
0413: private void processInternal(HttpServletRequest request,
0414: HttpServletResponse response) throws IOException,
0415: ServletException {
0416: //
0417: // First wrap the request with an object that contains request-scoped values that our runtime uses. This
0418: // is faster than sticking everything into attributes on the request (then basically reading from a HashMap).
0419: //
0420: request = PageFlowRequestWrapper.wrapRequest(request);
0421:
0422: String uri = InternalUtils.getDecodedServletPath(request);
0423: ServletContext servletContext = getServletContext();
0424:
0425: // The ServletContainerAdapter reference may have been lost during serialization/deserialization.
0426: if (_servletContainerAdapter == null) {
0427: _servletContainerAdapter = AdapterManager
0428: .getServletContainerAdapter(servletContext);
0429: }
0430:
0431: //
0432: // Allow the container to do a security check on forwarded requests, if that feature is enabled.
0433: //
0434: if (LegacySettings.get(servletContext).shouldDoSecureForwards()
0435: && PageFlowRequestWrapper.get(request)
0436: .isForwardedRequest()) {
0437: //
0438: // In some situations (namely, in scoped requests under portal), the initial
0439: // security check may not have been done for the request URI. In this case, a redirect
0440: // to https may happen during checkSecurity().
0441: //
0442: if (_servletContainerAdapter.doSecurityRedirect(uri,
0443: request, response)) {
0444: if (LOG.isDebugEnabled())
0445: LOG
0446: .debug("checkSecurity() caused a redirect. Ending processing for this request "
0447: + '(' + uri + ')');
0448:
0449: return;
0450: }
0451: }
0452:
0453: //
0454: // If we've come in on a forced redirect due to security constraints, look for request attrs
0455: // that we put into the session.
0456: //
0457: String hash = request
0458: .getParameter(REDIRECT_REQUEST_ATTRS_PARAM);
0459: if (hash != null) {
0460: HttpSession session = request.getSession(false);
0461:
0462: if (session != null) {
0463: String carryoverAttrName = makeRedirectedRequestAttrsKey(
0464: uri, hash);
0465: Map attrs = (Map) session
0466: .getAttribute(carryoverAttrName);
0467: session.removeAttribute(carryoverAttrName);
0468:
0469: if (attrs != null) {
0470: for (Iterator i = attrs.entrySet().iterator(); i
0471: .hasNext();) {
0472: Map.Entry entry = (Map.Entry) i.next();
0473:
0474: String attrName = (String) entry.getKey();
0475: if (request.getAttribute(attrName) == null) {
0476: request.setAttribute(attrName, entry
0477: .getValue());
0478: }
0479: }
0480: }
0481: }
0482: }
0483:
0484: //
0485: // The requested action can be overridden by a request parameter. In this case, we parse the action from
0486: // the request parameter and forward to a URI constructed from it. If this happens, just return.
0487: //
0488: if (processActionOverride(request, response))
0489: return;
0490:
0491: //
0492: // Process any direct request for a page flow by forwarding to its "begin" action.
0493: //
0494: if (processPageFlowRequest(request, response, uri))
0495: return;
0496:
0497: //
0498: // Get the FlowController for this request (page flow or shared flow), and cache it in the request.
0499: //
0500: String flowControllerClassName = InternalUtils
0501: .getFlowControllerClassName(moduleConfig);
0502:
0503: if (flowControllerClassName == null
0504: && !(moduleConfig instanceof AutoRegisterActionServlet.MissingRootModuleControllerConfig)) {
0505: //
0506: // If this isn't a blank module initialized in place of a missing root PageFlowController, emit a warning
0507: // about the missing controllerClass property.
0508: //
0509: if (LOG.isWarnEnabled())
0510: LOG
0511: .warn("Struts module "
0512: + moduleConfig.getPrefix()
0513: + " is configured to use "
0514: + getClass().getName()
0515: + " as the request processor, but the <controller> element does not contain a <set-property>"
0516: + " for \"controllerClass\". Page Flow actions in this module may not be handled correctly.");
0517: }
0518:
0519: //
0520: // Look up or create the appropriate SharedFlowControllers if they don't exist.
0521: //
0522: if (LOG.isInfoEnabled())
0523: LOG
0524: .info("Attempting to instantiate SharedFlowControllers for request "
0525: + request.getRequestURI());
0526:
0527: FlowController currentFlowController = null;
0528:
0529: try {
0530: RequestContext requestContext = new RequestContext(request,
0531: response);
0532: Map/*< String, SharedFlowController >*/sharedFlows = _flowControllerFactory
0533: .getSharedFlowsForRequest(requestContext);
0534:
0535: /* todo: why are the shared flow / global app objects loaded for data binding here? */
0536: ImplicitObjectUtil.loadSharedFlow(request, sharedFlows);
0537: ImplicitObjectUtil.loadGlobalApp(request, PageFlowUtils
0538: .getGlobalApp(request));
0539:
0540: if (flowControllerClassName != null) {
0541: currentFlowController = getFlowController(
0542: requestContext, flowControllerClassName);
0543: PageFlowRequestWrapper
0544: .get(request)
0545: .setCurrentFlowController(currentFlowController);
0546: } else {
0547: PageFlowRequestWrapper.get(request)
0548: .setCurrentFlowController(null);
0549: }
0550: } catch (ClassNotFoundException e) {
0551: LOG.error("Could not find FlowController class "
0552: + flowControllerClassName, e);
0553: ServletUtils.throwServletException(e);
0554: } catch (InstantiationException e) {
0555: LOG.error("Could not instantiate FlowController of type "
0556: + flowControllerClassName, e);
0557: ServletUtils.throwServletException(e);
0558: } catch (IllegalAccessException e) {
0559: LOG.error("Could not instantiate FlowController of type "
0560: + flowControllerClassName, e);
0561: ServletUtils.throwServletException(e);
0562: }
0563:
0564: //
0565: // Get the page flow for this request.
0566: //
0567: PageFlowController jpf = PageFlowUtils.getCurrentPageFlow(
0568: request, getServletContext());
0569:
0570: //
0571: // Remove any current JavaServer Faces backing bean. We have "left" any JSF page and are now processing a
0572: // Page Flow action.
0573: //
0574: InternalUtils.removeCurrentFacesBackingBean(request,
0575: servletContext);
0576:
0577: //
0578: // Set up implicit objects used by the expression language in simple actions and in declarative validation.
0579: //
0580: ImplicitObjectUtil.loadImplicitObjects(request, response,
0581: servletContext, jpf);
0582:
0583: try {
0584: super .process(request, response);
0585: } catch (UnhandledException unhandledException) {
0586: // If we get here, then we've already tried to find an exception handler. Just throw.
0587: rethrowUnhandledException(unhandledException);
0588: } catch (ServletException servletEx) {
0589: // If a ServletException escapes out of any of the processing methods, let the current FlowController handle it.
0590: if (!handleException(servletEx, currentFlowController,
0591: request, response))
0592: throw servletEx;
0593: } catch (IOException ioe) {
0594: // If an IOException escapes out of any of the processing methods, let the current FlowController handle it.
0595: if (!handleException(ioe, currentFlowController, request,
0596: response))
0597: throw ioe;
0598: } catch (Throwable th) {
0599: // If a Throwable escapes out of any of the processing methods, let the current FlowController handle it.
0600: if (!handleException(th, currentFlowController, request,
0601: response)) {
0602: if (th instanceof Error)
0603: throw (Error) th;
0604: ServletUtils.throwServletException(th);
0605: }
0606: }
0607: }
0608:
0609: private FlowController getFlowController(
0610: RequestContext requestContext, String fcClassName)
0611: throws ClassNotFoundException, InstantiationException,
0612: IllegalAccessException {
0613: Class fcClass = _flowControllerFactory
0614: .getFlowControllerClass(fcClassName);
0615: HttpServletRequest request = requestContext.getHttpRequest();
0616: HttpServletResponse response = requestContext.getHttpResponse();
0617:
0618: if (PageFlowController.class.isAssignableFrom(fcClass)) {
0619: PageFlowController current = PageFlowUtils
0620: .getCurrentPageFlow(request, getServletContext());
0621:
0622: if (current != null && current.getClass().equals(fcClass)) {
0623: if (LOG.isDebugEnabled()) {
0624: LOG.debug("Using current page flow: " + current);
0625: }
0626:
0627: //
0628: // Reinitialize transient data that may have been lost on session failover.
0629: //
0630: current.reinitialize(request, response,
0631: getServletContext());
0632: return current;
0633: }
0634:
0635: return _flowControllerFactory.createPageFlow(
0636: new RequestContext(request, response), fcClass);
0637: } else {
0638: assert SharedFlowController.class.isAssignableFrom(fcClass) : fcClass
0639: .getName();
0640:
0641: SharedFlowController current = PageFlowUtils.getSharedFlow(
0642: fcClass.getName(), request);
0643:
0644: if (current != null) {
0645: current.reinitialize(request, response,
0646: getServletContext());
0647: return current;
0648: }
0649:
0650: return _flowControllerFactory.createSharedFlow(
0651: new RequestContext(request, response), fcClass);
0652: }
0653: }
0654:
0655: private boolean handleException(Throwable th, FlowController fc,
0656: HttpServletRequest request, HttpServletResponse response) {
0657: if (fc != null) {
0658: try {
0659: ActionMapping mapping = InternalUtils
0660: .getCurrentActionMapping(request);
0661: ActionForm form = InternalUtils
0662: .getCurrentActionForm(request);
0663: ActionForward fwd = fc.handleException(th, mapping,
0664: form, request, response);
0665: processForwardConfig(request, response, fwd);
0666: return true;
0667: } catch (UnhandledException unhandledException) {
0668: if (LOG.isInfoEnabled()) {
0669: LOG
0670: .info(
0671: "This exception was unhandled by any exception handler.",
0672: unhandledException);
0673: }
0674:
0675: return false;
0676: } catch (Throwable t) {
0677: LOG.error("Exception while handling exception "
0678: + th.getClass().getName()
0679: + ". The original exception will be thrown.",
0680: t);
0681: return false;
0682: }
0683: }
0684:
0685: return false;
0686: }
0687:
0688: /**
0689: * Process any direct request for a page flow by forwarding to its "begin" action.
0690: *
0691: * @param request the current HttpServletRequest
0692: * @param response the current HttpServletResponse
0693: * @param uri the decoded request URI
0694: * @return <code>true</code> if the request was for a page flow, in which case it was forwarded.
0695: * @throws IOException
0696: * @throws ServletException
0697: */
0698: protected boolean processPageFlowRequest(
0699: HttpServletRequest request, HttpServletResponse response,
0700: String uri) throws IOException, ServletException {
0701: //
0702: // Forward requests for *.jpf to the "begin" action within the appropriate Struts module.
0703: //
0704: if (FileUtils.osSensitiveEndsWith(uri,
0705: PageFlowConstants.PAGEFLOW_EXTENSION)) {
0706: //
0707: // Make sure the current module config matches the request URI. If not, this could be an
0708: // EAR where the struts-config.xml wasn't included because of a compilation error.
0709: //
0710: String modulePath = PageFlowUtils.getModulePath(request);
0711: if (!moduleConfig.getPrefix().equals(modulePath)) {
0712: if (LOG.isErrorEnabled()) {
0713: InternalStringBuilder msg = new InternalStringBuilder(
0714: "No module configuration registered for ");
0715: msg.append(uri).append(" (module path ").append(
0716: modulePath).append(").");
0717: LOG.error(msg.toString());
0718: }
0719:
0720: if (modulePath.length() == 0)
0721: modulePath = "/";
0722: InternalUtils.sendDevTimeError("PageFlow_NoModuleConf",
0723: null,
0724: HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
0725: request, response, getServletContext(),
0726: new Object[] { uri, modulePath });
0727: return true;
0728: }
0729:
0730: // Make sure that the requested pageflow matches the pageflow for the directory.
0731: ActionMapping beginMapping = getBeginMapping();
0732: if (beginMapping != null) {
0733: String desiredType = beginMapping.getParameter();
0734: desiredType = desiredType.substring(desiredType
0735: .lastIndexOf('.') + 1)
0736: + PAGEFLOW_EXTENSION;
0737: String requestedType = InternalUtils
0738: .getDecodedServletPath(request);
0739: requestedType = requestedType.substring(requestedType
0740: .lastIndexOf('/') + 1);
0741:
0742: if (!requestedType.equals(desiredType)) {
0743: if (LOG.isDebugEnabled()) {
0744: LOG
0745: .debug("Wrong .jpf requested for this directory: got "
0746: + requestedType
0747: + ", expected "
0748: + desiredType);
0749: }
0750:
0751: if (LOG.isErrorEnabled()) {
0752: InternalStringBuilder msg = new InternalStringBuilder(
0753: "Wrong .jpf requested for this directory: got ");
0754: msg.append(requestedType).append(", expected ")
0755: .append(desiredType).append('.');
0756: LOG.error(msg.toString());
0757: }
0758:
0759: InternalUtils
0760: .sendDevTimeError(
0761: "PageFlow_WrongPath",
0762: null,
0763: HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
0764: request, response,
0765: getServletContext(),
0766: new Object[] { requestedType,
0767: desiredType });
0768:
0769: return true;
0770: }
0771: }
0772:
0773: uri = PageFlowUtils.getBeginActionURI(uri);
0774:
0775: if (LOG.isDebugEnabled()) {
0776: LOG.debug("Got request for " + request.getRequestURI()
0777: + ", forwarding to " + uri);
0778: }
0779:
0780: doForward(uri, request, response);
0781: return true;
0782: }
0783:
0784: return false;
0785: }
0786:
0787: /**
0788: * A MultipartRequestWrapper that we cache in the outer request once we've handled the multipart request once.
0789: * It extends the base Struts MultipartRequestWrapper by being aware of ScopedRequests; for ScopedRequests, it
0790: * filters the parameter names accordingly.
0791: */
0792: private static class RehydratedMultipartRequestWrapper extends
0793: MultipartRequestWrapper {
0794: public RehydratedMultipartRequestWrapper(HttpServletRequest req) {
0795: super (req);
0796:
0797: MultipartRequestHandler handler = MultipartRequestUtils
0798: .getCachedMultipartHandler(req);
0799:
0800: if (handler != null) {
0801: ScopedRequest scopedRequest = ScopedServletUtils
0802: .unwrapRequest(req);
0803: Map textElements = handler.getTextElements();
0804: parameters = scopedRequest != null ? scopedRequest
0805: .filterParameterMap(textElements)
0806: : textElements;
0807: }
0808: }
0809: }
0810:
0811: public void process(HttpServletRequest request,
0812: HttpServletResponse response) throws IOException,
0813: ServletException {
0814: int localRequestCount = -1;
0815:
0816: if (LOG.isTraceEnabled()) {
0817: localRequestCount = ++requestNumber;
0818: LOG.trace("------------------------------- Start Request #"
0819: + localRequestCount
0820: + " -----------------------------------");
0821: }
0822:
0823: //
0824: // First reinitialize the reloadable class handler. This will bounce a classloader if necessary.
0825: //
0826: ServletContext servletContext = getServletContext();
0827: _handlers.getReloadableClassHandler().reloadClasses(
0828: new RequestContext(request, response));
0829:
0830: //
0831: // Get the chain of pre-request interceptors.
0832: //
0833: RequestInterceptorContext context = new RequestInterceptorContext(
0834: request, response, getServletContext());
0835: List/*< Interceptor >*/interceptors = context
0836: .getRequestInterceptors();
0837:
0838: //
0839: // Execute pre-request interceptors
0840: //
0841: try {
0842: Interceptors.doPreIntercept(context, interceptors);
0843:
0844: if (context.requestWasCancelled()) {
0845: if (LOG.isDebugEnabled()) {
0846: LOG.debug("Interceptor "
0847: + context.getOverridingInterceptor()
0848: + " cancelled the request.");
0849: }
0850:
0851: return;
0852: }
0853: } catch (InterceptorException e) {
0854: ServletUtils.throwServletException(e);
0855: }
0856:
0857: //
0858: // Initialize the ServletContext in the request. Often, we need access to the ServletContext when we only
0859: // have a ServletRequest.
0860: //
0861: InternalUtils.setServletContext(request, servletContext);
0862:
0863: //
0864: // Callback to the servlet container adapter.
0865: //
0866: PageFlowEventReporter er = _servletContainerAdapter
0867: .getEventReporter();
0868: _servletContainerAdapter.beginRequest(request, response);
0869: RequestContext requestContext = new RequestContext(request,
0870: response);
0871: er.beginActionRequest(requestContext);
0872: long startTime = System.currentTimeMillis();
0873:
0874: // Register the default URLRewriter
0875: URLRewriterService.registerURLRewriter(0, request,
0876: new DefaultURLRewriter());
0877:
0878: //
0879: // A ServletContext may have synchronization associated with getting attributes.
0880: // This could be a bottleneck under load for an app with pages that require lots
0881: // of URL rewriting. To improve performance add the template factory and formatter
0882: // to the request.
0883: //
0884: if (URLTemplatesFactory.getURLTemplatesFactory(request) == null) {
0885: URLTemplatesFactory.initServletRequest(request,
0886: URLTemplatesFactory
0887: .getURLTemplatesFactory(servletContext));
0888: }
0889: if (TemplatedURLFormatter.getTemplatedURLFormatter(request) == null) {
0890: TemplatedURLFormatter.initServletRequest(request,
0891: TemplatedURLFormatter
0892: .getTemplatedURLFormatter(servletContext));
0893: }
0894:
0895: PageFlowRequestWrapper rw = PageFlowRequestWrapper
0896: .unwrap(request);
0897: boolean isForwardedRequest = rw != null
0898: && rw.isForwardedRequest();
0899:
0900: try {
0901: processInternal(request, response);
0902: } finally {
0903: //
0904: // If this is not a forwarded request, then commit any session-scoped changes that were stored in the
0905: // request.
0906: //
0907: if (!isForwardedRequest) {
0908: Handlers.get(getServletContext()).getStorageHandler()
0909: .applyChanges(requestContext);
0910: }
0911:
0912: //
0913: // Callback to the server adapter.
0914: //
0915: _servletContainerAdapter.endRequest(request, response);
0916: long timeTaken = System.currentTimeMillis() - startTime;
0917: er.endActionRequest(requestContext, timeTaken);
0918: }
0919:
0920: //
0921: // Execute post-request interceptors
0922: //
0923: try {
0924: Interceptors.doPostIntercept(context, interceptors);
0925: } catch (InterceptorException e) {
0926: ServletUtils.throwServletException(e);
0927: }
0928:
0929: if (LOG.isTraceEnabled()) {
0930: LOG.trace("-------------------------------- End Request #"
0931: + localRequestCount
0932: + " ------------------------------------");
0933: }
0934: }
0935:
0936: /**
0937: * If this is a multipart request, wrap it with a special wrapper. Otherwise, return the request unchanged.
0938: *
0939: * @param request The HttpServletRequest we are processing
0940: */
0941: protected HttpServletRequest processMultipart(
0942: HttpServletRequest request) {
0943: if (!"POST".equalsIgnoreCase(request.getMethod()))
0944: return request;
0945:
0946: String contentType = request.getContentType();
0947: if (contentType != null
0948: && contentType.startsWith("multipart/form-data")) {
0949: PageFlowRequestWrapper pageFlowRequestWrapper = PageFlowRequestWrapper
0950: .get(request);
0951:
0952: //
0953: // We may have already gotten a multipart wrapper during process(). If so, use that.
0954: //
0955: MultipartRequestWrapper cachedWrapper = pageFlowRequestWrapper
0956: .getMultipartRequestWrapper();
0957:
0958: if (cachedWrapper != null
0959: && cachedWrapper.getRequest() == request)
0960: return cachedWrapper;
0961:
0962: try {
0963: //
0964: // First, pre-handle the multipart request. This parses the stream and caches a single
0965: // MultipartRequestHandler in the outer request, so we can create new wrappers around it at will.
0966: //
0967: MultipartRequestUtils
0968: .preHandleMultipartRequest(request);
0969: } catch (ServletException e) {
0970: LOG.error("Could not parse multipart request.", e
0971: .getRootCause());
0972: return request;
0973: }
0974:
0975: MultipartRequestWrapper ret = new RehydratedMultipartRequestWrapper(
0976: request);
0977: pageFlowRequestWrapper.setMultipartRequestWrapper(ret);
0978: return ret;
0979: } else {
0980: return request;
0981: }
0982:
0983: }
0984:
0985: protected ActionMapping getBeginMapping() {
0986: return (ActionMapping) moduleConfig
0987: .findActionConfig(BEGIN_ACTION_PATH);
0988: }
0989:
0990: private static String makeRedirectedRequestAttrsKey(
0991: String webappRelativeURI, String hash) {
0992: return REDIRECT_REQUEST_ATTRS_PREFIX + hash + webappRelativeURI;
0993: }
0994:
0995: private static void rethrowUnhandledException(UnhandledException ex)
0996: throws ServletException {
0997: Throwable rootCause = ex.getRootCause();
0998:
0999: //
1000: // We shouldn't (and don't need to) wrap Errors or RuntimeExceptions.
1001: //
1002: if (rootCause instanceof Error) {
1003: throw (Error) rootCause;
1004: } else if (rootCause instanceof RuntimeException) {
1005: throw (RuntimeException) rootCause;
1006: }
1007:
1008: throw ex;
1009: }
1010:
1011: public ActionForward processException(HttpServletRequest request,
1012: HttpServletResponse response, Exception ex,
1013: ActionForm form, ActionMapping mapping) throws IOException,
1014: ServletException {
1015: //
1016: // Note: we should only get here if FlowController.handleException itself throws an exception, or if the user
1017: // has merged in Struts code that delegates to an action/exception-handler outside of the pageflow.
1018: //
1019: // If this is an UnhandledException thrown from FlowController.handleException, don't try to re-handle it here.
1020: //
1021:
1022: if (ex instanceof UnhandledException) {
1023: rethrowUnhandledException((UnhandledException) ex);
1024: assert false; // rethrowUnhandledException always throws something.
1025: return null;
1026: } else {
1027: return super .processException(request, response, ex, form,
1028: mapping);
1029: }
1030: }
1031:
1032: /**
1033: * Used by {@link PageFlowRequestProcessor#processMapping}. Its main job is to return
1034: * {@link ExceptionHandledAction} as the type.
1035: */
1036: protected static class ExceptionHandledActionMapping extends
1037: ActionMapping {
1038: private ActionForward _fwd;
1039:
1040: public ExceptionHandledActionMapping(String actionPath,
1041: ActionForward fwd) {
1042: setPath(actionPath);
1043: _fwd = fwd;
1044: }
1045:
1046: public String getType() {
1047: return ExceptionHandledAction.class.getName();
1048: }
1049:
1050: public ActionForward getActionForward() {
1051: return _fwd;
1052: }
1053:
1054: public boolean getValidate() {
1055: return false;
1056: }
1057: }
1058:
1059: /**
1060: * Used by {@link PageFlowRequestProcessor#processMapping}. This action simply returns the ActionForward stored in the
1061: * ExceptionHandledActionMapping that's passed in.
1062: */
1063: public static class ExceptionHandledAction extends Action {
1064: public ActionForward execute(ActionMapping mapping,
1065: ActionForm form, HttpServletRequest request,
1066: HttpServletResponse response) {
1067: assert mapping instanceof ExceptionHandledActionMapping : mapping
1068: .getClass().getName();
1069:
1070: return ((ExceptionHandledActionMapping) mapping)
1071: .getActionForward();
1072: }
1073: }
1074:
1075: private boolean isCorrectFormType(Class formBeanClass,
1076: ActionMapping mapping) {
1077: assert mapping.getName() != null : "cannot pass an ActionMapping that has no form bean";
1078: Class cachedFormBeanClass = (Class) _formBeanClasses
1079: .get(mapping.getName());
1080: return isCorrectFormType(formBeanClass, cachedFormBeanClass,
1081: mapping);
1082: }
1083:
1084: private boolean isCorrectFormType(Class formBeanClass,
1085: Class actionMappingFormBeanClass, ActionMapping mapping) {
1086: if (actionMappingFormBeanClass != null) {
1087: return actionMappingFormBeanClass
1088: .isAssignableFrom(formBeanClass);
1089: } else {
1090: //
1091: // The form bean class couldn't be loaded at init time -- just check against the class name.
1092: //
1093: FormBeanConfig mappingFormBean = moduleConfig
1094: .findFormBeanConfig(mapping.getName());
1095: String formClassName = formBeanClass.getName();
1096:
1097: if (mappingFormBean != null
1098: && mappingFormBean.getType().equals(formClassName))
1099: return true;
1100:
1101: if (mapping instanceof PageFlowActionMapping) {
1102: String desiredType = ((PageFlowActionMapping) mapping)
1103: .getFormClass();
1104: if (formClassName.equals(desiredType))
1105: return true;
1106: }
1107: }
1108:
1109: return false;
1110: }
1111:
1112: private ActionMapping checkTransaction(HttpServletRequest request,
1113: HttpServletResponse response, ActionMapping mapping,
1114: String actionPath) throws IOException {
1115: if (mapping instanceof PageFlowActionMapping
1116: && ((PageFlowActionMapping) mapping)
1117: .isPreventDoubleSubmit()) {
1118: if (!TokenProcessor.getInstance().isTokenValid(request,
1119: true)) {
1120: FlowController currentFC = PageFlowRequestWrapper.get(
1121: request).getCurrentFlowController();
1122: String actionName = InternalUtils
1123: .getActionName(mapping);
1124: DoubleSubmitException ex = new DoubleSubmitException(
1125: actionName, currentFC);
1126:
1127: if (currentFC != null) {
1128: try {
1129: ActionForward fwd = currentFC.handleException(
1130: ex, mapping, null, request, response);
1131: return new ExceptionHandledActionMapping(
1132: actionPath, fwd);
1133: } catch (ServletException servletException) {
1134: LOG.error("Exception occurred while handling "
1135: + ex.getClass().getName(),
1136: servletException);
1137: }
1138: }
1139:
1140: ex.sendResponseErrorCode(response);
1141: return null;
1142: }
1143: }
1144:
1145: return mapping;
1146: }
1147:
1148: public void init(ActionServlet actionServlet, ModuleConfig mc)
1149: throws ServletException {
1150: super .init(actionServlet, mc);
1151:
1152: ServletContext servletContext = getServletContext();
1153:
1154: //
1155: // Cache a reference to the ServletContainerAdapter, the Handlers, and the LegacySettings.
1156: //
1157: _servletContainerAdapter = AdapterManager
1158: .getServletContainerAdapter(servletContext);
1159: _legacySettings = LegacySettings.get(servletContext);
1160: _handlers = Handlers.get(servletContext);
1161: _flowControllerFactory = FlowControllerFactory
1162: .get(servletContext);
1163:
1164: // Initialize delegating action mappings and exception configs.
1165: InternalUtils.initDelegatingConfigs(mc, servletContext);
1166:
1167: // Cache a list of overloaded actions for each overloaded action path (actions are overloaded by form bean type).
1168: cacheOverloadedActionMappings();
1169:
1170: // Cache the form bean Classes by form bean name.
1171: cacheFormClasses();
1172:
1173: // Initialize the request interceptors and action interceptors.
1174: ActionInterceptorContext.init(servletContext);
1175: RequestInterceptorContext.init(servletContext);
1176:
1177: ensurePageServletFilter();
1178: assert _pageServletFilter != null;
1179: }
1180:
1181: private void cacheOverloadedActionMappings() {
1182: ActionConfig[] actionConfigs = moduleConfig.findActionConfigs();
1183:
1184: for (int i = 0; i < actionConfigs.length; i++) {
1185: ActionConfig actionConfig = actionConfigs[i];
1186:
1187: if (actionConfig instanceof PageFlowActionMapping) {
1188: PageFlowActionMapping mapping = (PageFlowActionMapping) actionConfig;
1189: String unqualifiedActionPath = ((PageFlowActionMapping) actionConfig)
1190: .getUnqualifiedActionPath();
1191:
1192: if (unqualifiedActionPath != null) {
1193: List/*< ActionMapping >*/overloaded = (List) _overloadedActions
1194: .get(unqualifiedActionPath);
1195:
1196: if (overloaded == null) {
1197: overloaded = new ArrayList/*< ActionMapping >*/();
1198: _overloadedActions.put(unqualifiedActionPath,
1199: overloaded);
1200: }
1201:
1202: overloaded.add(mapping);
1203: }
1204: }
1205: }
1206: }
1207:
1208: private void cacheFormClasses() {
1209: FormBeanConfig[] formBeans = moduleConfig.findFormBeanConfigs();
1210: ReloadableClassHandler rch = _handlers
1211: .getReloadableClassHandler();
1212:
1213: for (int i = 0; i < formBeans.length; i++) {
1214: FormBeanConfig formBeanConfig = formBeans[i];
1215: String formType = InternalUtils
1216: .getFormBeanType(formBeanConfig);
1217:
1218: try {
1219: Class formBeanClass = rch.loadClass(formType);
1220: _formBeanClasses.put(formBeanConfig.getName(),
1221: formBeanClass);
1222: } catch (ClassNotFoundException e) {
1223: LOG.error("Could not load class " + formType
1224: + " referenced from form bean config "
1225: + formBeanConfig.getName()
1226: + " in Struts module " + moduleConfig);
1227: }
1228: }
1229: }
1230:
1231: /**
1232: * Read component instance mapping configuration file.
1233: * This is where we read files properties.
1234: */
1235:
1236: protected void initDefinitionsMapping() throws ServletException {
1237: definitionsFactory = null;
1238: TilesUtilImpl tilesUtil = TilesUtil.getTilesUtil();
1239:
1240: if (tilesUtil instanceof TilesUtilStrutsImpl) {
1241: // Retrieve and set factory for this modules
1242: definitionsFactory = ((TilesUtilStrutsImpl) tilesUtil)
1243: .getDefinitionsFactory(getServletContext(),
1244: moduleConfig);
1245:
1246: if (definitionsFactory == null && log.isDebugEnabled()) {
1247: log.debug("Definition Factory not found for module: '"
1248: + moduleConfig.getPrefix());
1249: }
1250: }
1251: }
1252:
1253: public ActionMapping processMapping(HttpServletRequest request,
1254: HttpServletResponse response, String path)
1255: throws IOException {
1256: PageFlowRequestWrapper rw = PageFlowRequestWrapper.get(request);
1257: FlowController fc = rw.getCurrentFlowController();
1258: Object forwardedForm = InternalUtils
1259: .unwrapFormBean(InternalUtils.getForwardedFormBean(
1260: request, false));
1261:
1262: //
1263: // First, see if this is a request for a shared flow action. The shared flow's name (as declared by the
1264: // current page flow) will precede the dot.
1265: //
1266: if (fc != null
1267: && !processSharedFlowMapping(request, response, path,
1268: fc))
1269: return null;
1270:
1271: //
1272: // Look for a form-specific action path. This is used when there are two actions with the same
1273: // name, but different forms (in nesting).
1274: //
1275: Class forwardedFormClass = null;
1276:
1277: if (forwardedForm != null) {
1278: forwardedFormClass = forwardedForm.getClass();
1279: List/*< ActionMapping >*/possibleMatches = (List) _overloadedActions
1280: .get(path);
1281: ActionMapping bestMatch = null;
1282:
1283: //
1284: // Troll through the overloaded actions for the given path. Look for the one whose form bean class is
1285: // exactly the class of the forwarded form; failing that, look for one that's assignable from the class
1286: // of the forwarded form.
1287: //
1288: for (int i = 0; possibleMatches != null
1289: && i < possibleMatches.size(); ++i) {
1290: ActionMapping possibleMatch = (ActionMapping) possibleMatches
1291: .get(i);
1292: assert possibleMatch instanceof PageFlowActionMapping : possibleMatch
1293: .getClass();
1294: Class cachedFormBeanClass = (Class) _formBeanClasses
1295: .get(possibleMatch.getName());
1296:
1297: if (forwardedFormClass.equals(cachedFormBeanClass)) {
1298: bestMatch = possibleMatch;
1299: break;
1300: }
1301: if (bestMatch == null
1302: && isCorrectFormType(forwardedFormClass,
1303: possibleMatch)) {
1304: bestMatch = possibleMatch;
1305: }
1306: }
1307:
1308: if (bestMatch != null) {
1309: request.setAttribute(Globals.MAPPING_KEY, bestMatch);
1310:
1311: if (LOG.isDebugEnabled()) {
1312: LOG.debug("Found form-specific action mapping "
1313: + bestMatch.getPath() + " for " + path
1314: + ", form " + forwardedFormClass.getName());
1315: }
1316:
1317: return checkTransaction(request, response, bestMatch,
1318: path);
1319: }
1320: }
1321:
1322: //
1323: // Look for a directly-defined mapping for this path.
1324: //
1325: ActionMapping mapping = (ActionMapping) moduleConfig
1326: .findActionConfig(path);
1327:
1328: if (mapping != null) {
1329: boolean wrongForm = false;
1330:
1331: //
1332: // We're going to bail out if there is a forwarded form and this mapping requires a different form type.
1333: //
1334: if (forwardedForm != null) {
1335: boolean mappingHasNoFormBean = mapping.getName() == null;
1336: wrongForm = mappingHasNoFormBean
1337: || !isCorrectFormType(forwardedFormClass,
1338: mapping);
1339: }
1340:
1341: if (!wrongForm) {
1342: request.setAttribute(Globals.MAPPING_KEY, mapping);
1343: return checkTransaction(request, response, mapping,
1344: path);
1345: }
1346: }
1347:
1348: //
1349: // Look for a mapping for "unknown" paths
1350: //
1351: ActionMapping unknown = getUnknownActionFromConfig(moduleConfig);
1352: if (unknown != null) {
1353: request.setAttribute(Globals.MAPPING_KEY, unknown);
1354: return checkTransaction(request, response, unknown, path);
1355: }
1356:
1357: // If we haven't already tried this action on a shared flow or on Global.app, see if it's in the Global.app
1358: // module. If it is, forward to it. (Global.app is a deprecated fallback for unhandled actions.)
1359: String errorServletPath = rw.getOriginalServletPath();
1360: ModuleConfig globalApp = null;
1361:
1362: if (errorServletPath == null
1363: && (globalApp = InternalUtils.ensureModuleConfig(
1364: GLOBALAPP_MODULE_CONTEXT_PATH,
1365: getServletContext())) != null
1366: && (globalApp.findActionConfig(path) != null || getUnknownActionFromConfig(globalApp) != null)) {
1367:
1368: if (LOG.isDebugEnabled()) {
1369: LOG.debug("Trying Global.app for unhandled action "
1370: + path);
1371: }
1372:
1373: errorServletPath = InternalUtils
1374: .getDecodedServletPath(request);
1375: rw.setOriginalServletPath(errorServletPath);
1376: String globalAppURI = GLOBALAPP_MODULE_CONTEXT_PATH + path
1377: + ACTION_EXTENSION;
1378: try {
1379: doForward(globalAppURI, request, response);
1380: } catch (ServletException e) {
1381: LOG.error("Could not forward to Global.app path "
1382: + globalAppURI);
1383: }
1384: return null;
1385: } else {
1386: // If we are currently returning from nesting, then it is possible that the given action may actually be
1387: // found in a page flow that's farther down the nesting stack. This can happen when the browser back button
1388: // is used; see http://issues.apache.org/jira/browse/BEEHIVE-1024 .
1389: //
1390: // In this case, we simply forward to the action in the page flow in which it is found. The runtime will
1391: // take care of popping page flows until the right instance is made the current page flow.
1392: if (rw.isReturningFromNesting()) {
1393: ModuleConfig found = PageFlowStack.get(request,
1394: getServletContext()).findActionInStack(path);
1395:
1396: if (found != null) {
1397: String servletPath = found.getPrefix() + path
1398: + ACTION_EXTENSION;
1399:
1400: try {
1401: doForward(servletPath, request, response);
1402: } catch (ServletException e) {
1403: LOG.error("Could not forward to path "
1404: + servletPath);
1405: }
1406: return null;
1407: }
1408: }
1409:
1410: // If the error action path has a slash in it, then it's not local to the current page flow. Replace
1411: // it with the original servlet path.
1412: if (errorServletPath != null && path.indexOf('/') > 0)
1413: path = errorServletPath;
1414: return processUnresolvedAction(path, request, response,
1415: forwardedForm);
1416: }
1417: }
1418:
1419: private ActionMapping getUnknownActionFromConfig(ModuleConfig mc) {
1420: ActionConfig configs[] = mc.findActionConfigs();
1421: for (int i = 0; i < configs.length; i++) {
1422: if (configs[i].getUnknown()) {
1423: return (ActionMapping) configs[i];
1424: }
1425: }
1426: return null;
1427: }
1428:
1429: protected boolean processSharedFlowMapping(
1430: HttpServletRequest request, HttpServletResponse response,
1431: String actionPath, FlowController currentFlowController)
1432: throws IOException {
1433: if (currentFlowController.isPageFlow()) {
1434: int dot = actionPath.indexOf('.');
1435:
1436: if (dot != -1) {
1437: Map/*< String, SharedFlowController >*/sharedFlows = PageFlowUtils
1438: .getSharedFlows(request);
1439: if (sharedFlows == null)
1440: return true;
1441: if (dot == actionPath.length() - 1)
1442: return true; // empty action name
1443: assert actionPath.length() > 0
1444: && actionPath.charAt(0) == '/' : actionPath;
1445: String sharedFlowName = actionPath.substring(1, dot);
1446: SharedFlowController sf = (SharedFlowController) sharedFlows
1447: .get(sharedFlowName);
1448:
1449: if (sf != null) {
1450: if (LOG.isDebugEnabled()) {
1451: LOG.debug("Forwarding to shared flow "
1452: + sf.getDisplayName()
1453: + " to handle action \"" + actionPath
1454: + "\".");
1455: }
1456:
1457: //
1458: // Save the original request URI, so if the action fails on the shared flow, too, then we can
1459: // give an error message that includes *this* URI, not the shared flow URI.
1460: //
1461: PageFlowRequestWrapper
1462: .get(request)
1463: .setOriginalServletPath(
1464: InternalUtils
1465: .getDecodedServletPath(request));
1466:
1467: //
1468: // Construct a URI that is [shared flow module path] + [base action path] + [action-extension (.do)]
1469: //
1470: int lastSlash = actionPath.lastIndexOf('/');
1471: assert lastSlash != -1 : actionPath;
1472: InternalStringBuilder uri = new InternalStringBuilder(
1473: sf.getModulePath());
1474: uri.append('/');
1475: uri.append(actionPath.substring(dot + 1));
1476: uri.append(ACTION_EXTENSION);
1477:
1478: try {
1479: doForward(uri.toString(), request, response);
1480: return false;
1481: } catch (ServletException e) {
1482: LOG.error(
1483: "Could not forward to shared flow URI "
1484: + uri, e);
1485: }
1486: }
1487: }
1488: }
1489:
1490: return true;
1491: }
1492:
1493: protected ActionMapping processUnresolvedAction(String actionPath,
1494: HttpServletRequest request, HttpServletResponse response,
1495: Object returningForm) throws IOException {
1496: if (LOG.isInfoEnabled()) {
1497: InternalStringBuilder msg = new InternalStringBuilder(
1498: "Action \"").append(actionPath);
1499: LOG.info(msg.append("\" was also unhandled by Global.app.")
1500: .toString());
1501: }
1502:
1503: //
1504: // If there's a PageFlowController for this request, try and let it handle an
1505: // action-not-found exception. Otherwise, let Struts print out its "invalid path"
1506: // message.
1507: //
1508: FlowController fc = PageFlowUtils.getCurrentPageFlow(request,
1509: getServletContext());
1510:
1511: try {
1512: if (fc != null) {
1513: Exception ex = new ActionNotFoundException(actionPath,
1514: fc, returningForm);
1515: InternalUtils.setCurrentModule(fc.getModuleConfig(),
1516: request);
1517: ActionForward result = fc.handleException(ex, null,
1518: null, request, response);
1519: return new ExceptionHandledActionMapping(actionPath,
1520: result);
1521: }
1522: } catch (ServletException e) {
1523: // ignore this -- just let Struts do its thing.
1524:
1525: if (LOG.isDebugEnabled()) {
1526: LOG.debug(e);
1527: }
1528: }
1529:
1530: if (LOG.isDebugEnabled()) {
1531: LOG
1532: .debug("Couldn't handle an ActionNotFoundException -- delegating to Struts");
1533: }
1534:
1535: return super .processMapping(request, response, actionPath);
1536: }
1537:
1538: protected boolean processRoles(HttpServletRequest request,
1539: HttpServletResponse response, ActionMapping mapping)
1540: throws IOException, ServletException {
1541: //
1542: // If there are no required roles for this action, just return.
1543: //
1544: String roles[] = mapping.getRoleNames();
1545: if (roles == null || roles.length < 1) {
1546: return true;
1547: }
1548:
1549: // Check the current user against the list of required roles
1550: FlowController fc = PageFlowRequestWrapper.get(request)
1551: .getCurrentFlowController();
1552: FlowControllerHandlerContext context = new FlowControllerHandlerContext(
1553: request, response, fc);
1554:
1555: for (int i = 0; i < roles.length; i++) {
1556: if (_handlers.getLoginHandler().isUserInRole(context,
1557: roles[i])) {
1558: if (LOG.isDebugEnabled()) {
1559: LOG.debug(" User " + request.getRemoteUser()
1560: + " has role '" + roles[i]
1561: + "', granting access");
1562: }
1563:
1564: return true;
1565: }
1566: }
1567:
1568: // The current user is not authorized for this action
1569: if (LOG.isDebugEnabled()) {
1570: LOG
1571: .debug(" User '"
1572: + request.getRemoteUser()
1573: + "' does not have any required role, denying access");
1574: }
1575:
1576: //
1577: // Here, Struts sends an HTTP error. We try to let the current page flow handle a relevant exception.
1578: //
1579: LoginHandler loginHandler = _handlers.getLoginHandler();
1580: String actionName = InternalUtils.getActionName(mapping);
1581: FlowController currentFC = PageFlowRequestWrapper.get(request)
1582: .getCurrentFlowController();
1583: PageFlowException ex;
1584:
1585: if (loginHandler.getUserPrincipal(context) == null) {
1586: ex = currentFC.createNotLoggedInException(actionName,
1587: request);
1588: } else {
1589: ex = new UnfulfilledRolesException(mapping.getRoleNames(),
1590: mapping.getRoles(), actionName, currentFC);
1591: }
1592:
1593: if (currentFC != null) {
1594: ActionForward fwd = currentFC.handleException(ex, mapping,
1595: null, request, response);
1596: processForwardConfig(request, response, fwd);
1597: } else {
1598: ((ResponseErrorCodeSender) ex)
1599: .sendResponseErrorCode(response);
1600: }
1601:
1602: return false;
1603: }
1604:
1605: private static String addScopeParams(String url,
1606: HttpServletRequest request) {
1607: //
1608: // If the current request is scoped, add the right request parameter to the URL.
1609: //
1610: String scopeID = request
1611: .getParameter(ScopedServletUtils.SCOPE_ID_PARAM);
1612: if (scopeID != null) {
1613: return InternalUtils.addParam(url,
1614: ScopedServletUtils.SCOPE_ID_PARAM, scopeID);
1615: } else {
1616: return url;
1617: }
1618: }
1619:
1620: /**
1621: * This override of the base method ensures that absolute URIs don't get the context
1622: * path prepended, and handles forwards to special things like return-to="currentPage".
1623: */
1624: protected void processForwardConfig(HttpServletRequest request,
1625: HttpServletResponse response, ForwardConfig fwd)
1626: throws IOException, ServletException {
1627: ServletContext servletContext = getServletContext();
1628: ForwardRedirectHandler fwdRedirectHandler = _handlers
1629: .getForwardRedirectHandler();
1630: FlowController fc = PageFlowRequestWrapper.get(request)
1631: .getCurrentFlowController();
1632: FlowControllerHandlerContext context = new FlowControllerHandlerContext(
1633: request, response, fc);
1634:
1635: // Register this module as the one that's handling the action.
1636: if (fc != null) {
1637: InternalUtils.setForwardingModule(request, fc
1638: .getModulePath());
1639: }
1640:
1641: //
1642: // The following is similar to what's in super.processForwardConfig(), but it avoids putting
1643: // a slash in front of absolute URLs (e.g., ones that start with "http:").
1644: //
1645: if (fwd != null) {
1646: if (LOG.isDebugEnabled())
1647: LOG.debug("processForwardConfig(" + fwd + ')');
1648:
1649: //
1650: // Try to process a tiles definition. If the forward doesn't contain a
1651: // a tiles definition, continue on.
1652: //
1653: if (processTilesDefinition(fwd.getPath(), fwd
1654: .getContextRelative(), request, response)) {
1655: if (log.isDebugEnabled()) {
1656: log.debug(" '" + fwd.getPath()
1657: + "' - processed as definition");
1658: }
1659: return;
1660: }
1661:
1662: //
1663: // If this is a "special" page flow forward, create a Forward to handle it and pass
1664: // it to the current page flow. This should only happen when processValidate()
1665: // calls this method (or if a plain Struts action forwards to this forward) --
1666: // otherwise, the page flow should be using a Forward already.
1667: //
1668: if (fwd instanceof PageFlowActionForward) {
1669: ActionMapping mapping = (ActionMapping) request
1670: .getAttribute(Globals.MAPPING_KEY);
1671: assert mapping != null;
1672: ActionForm form = InternalUtils.getFormBean(mapping,
1673: request);
1674: Forward pfFwd = new Forward((ActionForward) fwd,
1675: servletContext);
1676: ActionForwardHandler handler = _handlers
1677: .getActionForwardHandler();
1678: fwd = handler.processForward(context, pfFwd, mapping,
1679: null, InternalUtils.getActionName(mapping),
1680: null, form);
1681: }
1682:
1683: String path = fwd.getPath();
1684: boolean startsWithSlash = path.length() > 0
1685: && path.charAt(0) == '/';
1686:
1687: //
1688: // If the URI is absolute (e.g., starts with "http:"), do a redirect to it no matter what.
1689: //
1690: if (FileUtils.isAbsoluteURI(path)) {
1691: fwdRedirectHandler.redirect(context, addScopeParams(
1692: path, request));
1693: } else if (fwd.getRedirect()) {
1694: String redirectURI;
1695:
1696: if (startsWithSlash && fwd instanceof Forward
1697: && ((Forward) fwd).isExplicitPath()) {
1698: redirectURI = path;
1699: } else if (fwd instanceof Forward
1700: && ((Forward) fwd).isExternalRedirect()) {
1701: assert startsWithSlash : path; // compiler should ensure path starts with '/'
1702: redirectURI = path;
1703: } else {
1704: redirectURI = request.getContextPath()
1705: + RequestUtils.forwardURL(request, fwd);
1706: }
1707:
1708: fwdRedirectHandler.redirect(context, addScopeParams(
1709: redirectURI, request));
1710: } else {
1711: String fwdURI;
1712:
1713: if (startsWithSlash && fwd instanceof Forward
1714: && ((Forward) fwd).isExplicitPath()) {
1715: fwdURI = path;
1716: } else {
1717: fwdURI = RequestUtils.forwardURL(request, fwd);
1718:
1719: //
1720: // First, see if the current module is a Shared Flow module. If so, unless this is a forward to
1721: // another action in the shared flow, we need to translate the local path so it makes sense (strip
1722: // off the shared flow module prefix "/-" and replace it with "/").
1723: //
1724: ModuleConfig mc = (ModuleConfig) request
1725: .getAttribute(Globals.MODULE_KEY);
1726:
1727: if (InternalUtils.isSharedFlowModule(mc)
1728: && !fwdURI.endsWith(ACTION_EXTENSION)
1729: && fwdURI
1730: .startsWith(SHARED_FLOW_MODULE_PREFIX)) {
1731: fwdURI = '/' + fwdURI
1732: .substring(SHARED_FLOW_MODULE_PREFIX_LEN);
1733: }
1734: }
1735:
1736: doForward(fwdURI, request, response);
1737: }
1738: }
1739: }
1740:
1741: protected boolean changeScheme(String webappRelativeURI,
1742: String scheme, int port,
1743: FlowControllerHandlerContext context)
1744: throws URISyntaxException, IOException, ServletException {
1745: if (port == -1) {
1746: if (LOG.isWarnEnabled()) {
1747: LOG
1748: .warn("Could not change the scheme to "
1749: + scheme
1750: + " because the relevant port was not provided "
1751: + "by the ServletContainerAdapter.");
1752: return false;
1753: }
1754: }
1755:
1756: //
1757: // First put all request attributes into the session, so they can be added to the
1758: // redirected request.
1759: //
1760: Map attrs = new HashMap();
1761: String queryString = null;
1762: ServletContext servletContext = getServletContext();
1763: HttpServletRequest request = ((RequestContext) context)
1764: .getHttpRequest();
1765:
1766: for (Enumeration e = request.getAttributeNames(); e
1767: .hasMoreElements();) {
1768: String name = (String) e.nextElement();
1769: attrs.put(name, request.getAttribute(name));
1770: }
1771:
1772: if (!attrs.isEmpty()) {
1773: String hash = Integer.toString(request.hashCode());
1774: String key = makeRedirectedRequestAttrsKey(
1775: webappRelativeURI, hash);
1776: request.getSession().setAttribute(key, attrs);
1777: queryString = URLRewriterService.getNamePrefix(
1778: servletContext, request,
1779: REDIRECT_REQUEST_ATTRS_PARAM)
1780: + REDIRECT_REQUEST_ATTRS_PARAM + '=' + hash;
1781: }
1782:
1783: //
1784: // Now do the redirect.
1785: //
1786: URI redirectURI = new URI(scheme, null,
1787: request.getServerName(), port, request.getContextPath()
1788: + webappRelativeURI, queryString, null);
1789:
1790: ForwardRedirectHandler fwdRedirectHandler = _handlers
1791: .getForwardRedirectHandler();
1792: fwdRedirectHandler.redirect(context, redirectURI.toString());
1793:
1794: if (LOG.isDebugEnabled()) {
1795: LOG.debug("Redirected to " + redirectURI);
1796: }
1797:
1798: return true;
1799: }
1800:
1801: /**
1802: * @deprecated Use {@link LegacySettings#shouldDoSecureForwards} instead.
1803: */
1804: protected boolean shouldDoSecureForwards() {
1805: return _legacySettings.shouldDoSecureForwards();
1806: }
1807:
1808: protected void doForward(String uri, HttpServletRequest request,
1809: HttpServletResponse response) throws IOException,
1810: ServletException {
1811: boolean securityRedirected = false;
1812: ServletContext servletContext = getServletContext();
1813: PageFlowRequestWrapper wrappedRequest = PageFlowRequestWrapper
1814: .get(request);
1815:
1816: //
1817: // As in the TilesRequestProcessor.doForward(), if the response has already been commited,
1818: // do an include instead.
1819: //
1820: if (!wrappedRequest.isScopedLookup() && response.isCommitted()) {
1821: doInclude(uri, request, response);
1822: return;
1823: }
1824:
1825: FlowController fc = wrappedRequest.getCurrentFlowController();
1826: FlowControllerHandlerContext context = new FlowControllerHandlerContext(
1827: request, response, fc);
1828:
1829: if (_legacySettings.shouldDoSecureForwards()) {
1830: SecurityProtocol sp = PageFlowUtils.getSecurityProtocol(
1831: uri, servletContext, request);
1832:
1833: if (!sp.equals(SecurityProtocol.UNSPECIFIED)) {
1834: try {
1835: if (request.isSecure()) {
1836: if (sp.equals(SecurityProtocol.UNSECURE)) {
1837: int listenPort = _servletContainerAdapter
1838: .getListenPort(request);
1839: securityRedirected = changeScheme(uri,
1840: SCHEME_UNSECURE, listenPort,
1841: context);
1842: }
1843: } else {
1844: if (sp.equals(SecurityProtocol.SECURE)) {
1845: int secureListenPort = _servletContainerAdapter
1846: .getSecureListenPort(request);
1847: securityRedirected = changeScheme(uri,
1848: SCHEME_SECURE, secureListenPort,
1849: context);
1850: }
1851: }
1852: } catch (URISyntaxException e) {
1853: LOG.error("Bad forward URI " + uri, e);
1854: }
1855: }
1856: }
1857:
1858: if (!securityRedirected) {
1859: if (!processPageForward(uri, request, response)) {
1860: ForwardRedirectHandler fwdRedirectHandler = _handlers
1861: .getForwardRedirectHandler();
1862: fwdRedirectHandler.forward(context, uri);
1863: }
1864: }
1865: }
1866:
1867: /**
1868: * An opportunity to process a page forward in a different way than performing a server forward. The default
1869: * implementation looks for a file on classpath called
1870: * META-INF/pageflow-page-servlets/<i>path-to-page</i>.properties (e.g.,
1871: * "/META-INF/pageflow-page-servlets/foo/bar/hello.jsp.properties"). This file contains mappings from
1872: * <i>platform-name</i> (the value returned by {@link ServletContainerAdapter#getPlatformName}) to the name of a Servlet
1873: * class that will process the page request. If the current platform name is not found, the value "default" is
1874: * tried. An example file might look like this:
1875: * <pre>
1876: * tomcat=org.apache.jsp.foo.bar.hello_jsp
1877: * default=my.servlets.foo.bar.hello
1878: * </pre>
1879: * @param pagePath the webapp-relative path to the page, e.g., "/foo/bar/hello.jsp"
1880: * @param request the current HttpServletRequest
1881: * @param response the current HttpServletResponse
1882: * @return <code>true</code> if the method handled the request, in which case it should not be forwarded.
1883: * @throws IOException
1884: * @throws ServletException
1885: */
1886: private boolean processPageForward(String pagePath,
1887: HttpServletRequest request, HttpServletResponse response)
1888: throws IOException, ServletException {
1889: Class pageServletClass = (Class) _pageServletClasses
1890: .get(pagePath);
1891:
1892: if (pageServletClass == null) {
1893: pageServletClass = Void.class;
1894: ClassLoader cl = DiscoveryUtils.getClassLoader();
1895: String path = "META-INF/pageflow-page-servlets" + pagePath
1896: + ".properties";
1897: InputStream in = cl.getResourceAsStream(path);
1898:
1899: if (in != null) {
1900: String className = null;
1901:
1902: try {
1903: Properties props = new Properties();
1904: props.load(in);
1905: className = props
1906: .getProperty(_servletContainerAdapter
1907: .getPlatformName());
1908: if (className == null)
1909: className = props.getProperty("default");
1910:
1911: if (className != null) {
1912: pageServletClass = cl.loadClass(className);
1913:
1914: if (Servlet.class
1915: .isAssignableFrom(pageServletClass)) {
1916: if (LOG.isInfoEnabled()) {
1917: LOG.info("Loaded page Servlet class "
1918: + className + " for path "
1919: + pagePath);
1920: }
1921: } else {
1922: pageServletClass = Void.class;
1923: LOG.error("Page Servlet class " + className
1924: + " for path " + pagePath
1925: + " does not extend "
1926: + Servlet.class.getName());
1927: }
1928: }
1929: } catch (IOException e) {
1930: LOG.error("Error while reading " + path, e);
1931: } catch (ClassNotFoundException e) {
1932: LOG.error("Error while loading page Servlet class "
1933: + className, e);
1934: }
1935: }
1936:
1937: _pageServletClasses.put(pagePath, pageServletClass);
1938: }
1939:
1940: if (pageServletClass.equals(Void.class)) {
1941: return false;
1942: }
1943:
1944: try {
1945: Servlet pageServlet = (Servlet) pageServletClass
1946: .newInstance();
1947: pageServlet.init(new PageServletConfig(pagePath));
1948:
1949: ensurePageServletFilter().doFilter(request, response,
1950: new PageServletFilterChain(pageServlet));
1951: return true;
1952: } catch (InstantiationException e) {
1953: LOG.error("Error while instantiating page Servlet of type "
1954: + pageServletClass.getName(), e);
1955: } catch (IllegalAccessException e) {
1956: LOG.error("Error while instantiating page Servlet of type "
1957: + pageServletClass.getName(), e);
1958: }
1959:
1960: return false;
1961: }
1962:
1963: // todo: why does the framework create a subclass of the page flow page filter here?
1964: private class PageServletFilter extends PageFlowPageFilter {
1965: public PageServletFilter() {
1966: super (getServletContext());
1967: }
1968:
1969: /**
1970: * Accept all file extensions.
1971: * @return the set of acceptable file extensions
1972: */
1973: protected Set getValidFileExtensions() {
1974: return null; // accept all
1975: }
1976: }
1977:
1978: /**
1979: * Used by {@link PageFlowRequestProcessor#processPageForward} to run a page Servlet.
1980: */
1981: private static class PageServletFilterChain implements FilterChain {
1982: private Servlet _pageServlet;
1983:
1984: public PageServletFilterChain(Servlet pageServlet) {
1985: _pageServlet = pageServlet;
1986: }
1987:
1988: public void doFilter(ServletRequest request,
1989: ServletResponse response) throws IOException,
1990: ServletException {
1991: _pageServlet.service(request, response);
1992: }
1993: }
1994:
1995: /**
1996: * Used by {@link PageFlowRequestProcessor#processPageForward} to initialize a page Servlet.
1997: */
1998: private class PageServletConfig implements ServletConfig {
1999: private String _pagePath;
2000:
2001: public PageServletConfig(String pagePath) {
2002: _pagePath = pagePath;
2003: }
2004:
2005: public String getServletName() {
2006: return _pagePath;
2007: }
2008:
2009: public ServletContext getServletContext() {
2010: return PageFlowRequestProcessor.this .getServletContext();
2011: }
2012:
2013: public String getInitParameter(String s) {
2014: return null;
2015: }
2016:
2017: public Enumeration getInitParameterNames() {
2018: return Collections.enumeration(Collections.EMPTY_LIST);
2019: }
2020: }
2021:
2022: /**
2023: * Set the no-cache headers. This overrides the base Struts behavior to prevent caching even for the pages.
2024: */
2025: protected void processNoCache(HttpServletRequest request,
2026: HttpServletResponse response) {
2027: //
2028: // Set the no-cache headers if:
2029: // 1) the module is configured for it, or
2030: // 2) netui-config.xml has an "always" value for <pageflow-config><prevent-cache>, or
2031: // 3) netui-config.xml has an "inDevMode" value for <pageflow-config><prevent-cache>, and we're not in
2032: // production mode.
2033: //
2034: boolean noCache = moduleConfig.getControllerConfig()
2035: .getNocache();
2036:
2037: if (!noCache) {
2038: PageFlowConfig pfConfig = ConfigUtil.getConfig()
2039: .getPageFlowConfig();
2040:
2041: if (pfConfig != null) {
2042: PreventCache preventCache = pfConfig.getPreventCache();
2043:
2044: if (preventCache != null) {
2045: switch (preventCache.getValue()) {
2046: case PreventCache.INT_ALWAYS:
2047: noCache = true;
2048: break;
2049: case PreventCache.INT_IN_DEV_MODE:
2050: noCache = !_servletContainerAdapter
2051: .isInProductionMode();
2052: break;
2053: }
2054: }
2055: }
2056: }
2057:
2058: if (noCache) {
2059: //
2060: // The call to PageFlowPageFilter.preventCache() will cause caching to be prevented
2061: // even when we end up forwarding to a page. Normally, no-cache headers are lost
2062: // when a server forward occurs.
2063: //
2064: ServletUtils.preventCache(response);
2065: PageFlowUtils.setPreventCache(request);
2066: }
2067: }
2068:
2069: private class ActionRunner implements
2070: ActionInterceptors.ActionExecutor {
2071: RequestInterceptorContext _ctxt;
2072: private Action _action;
2073: private ActionForm _formBean;
2074: private ActionMapping _actionMapping;
2075:
2076: public ActionRunner(RequestInterceptorContext context,
2077: Action action, ActionForm formBean,
2078: ActionMapping actionMapping) {
2079: _ctxt = context;
2080: _action = action;
2081: _formBean = formBean;
2082: _actionMapping = actionMapping;
2083: }
2084:
2085: public ActionForward execute() throws InterceptorException,
2086: ServletException, IOException {
2087: return PageFlowRequestProcessor.super .processActionPerform(
2088: _ctxt.getRequest(), _ctxt.getResponse(), _action,
2089: _formBean, _actionMapping);
2090: }
2091: }
2092:
2093: protected ActionForward processActionPerform(
2094: HttpServletRequest request, HttpServletResponse response,
2095: Action action, ActionForm form, ActionMapping mapping)
2096: throws IOException, ServletException {
2097: ServletContext servletContext = getServletContext();
2098: String actionName = InternalUtils.getActionName(mapping);
2099: ActionInterceptorContext context = null;
2100: List/*< Interceptor >*/interceptors = null;
2101:
2102: if (action instanceof FlowControllerAction) {
2103: FlowController fc = ((FlowControllerAction) action)
2104: .getFlowController();
2105:
2106: if (fc instanceof PageFlowController) {
2107: PageFlowController pfc = (PageFlowController) fc;
2108: context = new ActionInterceptorContext(request,
2109: response, servletContext, pfc, null, actionName);
2110: interceptors = context.getActionInterceptors();
2111: }
2112: }
2113:
2114: if (interceptors != null && interceptors.size() == 0)
2115: interceptors = null;
2116:
2117: try {
2118: //
2119: // Run any pre-action interceptors.
2120: //
2121: if (interceptors != null
2122: && !PageFlowRequestWrapper.get(request)
2123: .isReturningFromActionIntercept()) {
2124: Interceptors.doPreIntercept(context, interceptors);
2125:
2126: if (context.hasInterceptorForward()) {
2127: InterceptorForward fwd = context
2128: .getInterceptorForward();
2129:
2130: if (LOG.isDebugEnabled()) {
2131:
2132: Interceptor overridingInterceptor = context
2133: .getOverridingInterceptor();
2134: StringBuffer msg = new StringBuffer();
2135: msg.append("Action interceptor ");
2136: msg.append(overridingInterceptor.getClass()
2137: .getName());
2138: msg.append(" before action ");
2139: msg.append(actionName);
2140: msg.append(": forwarding to ");
2141: msg.append(fwd != null ? fwd.getPath()
2142: : "null [no forward]");
2143: LOG.debug(msg.toString());
2144: }
2145:
2146: return fwd;
2147: }
2148: } else {
2149: PageFlowRequestWrapper.get(request)
2150: .setReturningFromActionIntercept(false);
2151: }
2152:
2153: //
2154: // Execute the action.
2155: //
2156: RequestInterceptorContext requestContext = context != null ? context
2157: : new RequestInterceptorContext(request, response,
2158: getServletContext());
2159: ActionRunner actionExecutor = new ActionRunner(
2160: requestContext, action, form, mapping);
2161: ActionForward ret = ActionInterceptors.wrapAction(context,
2162: interceptors, actionExecutor);
2163:
2164: //
2165: // Run any post-action interceptors.
2166: //
2167: if (interceptors != null) {
2168: context.setOriginalForward(ret);
2169: Interceptors.doPostIntercept(context, interceptors);
2170:
2171: if (context.hasInterceptorForward()) {
2172: InterceptorForward fwd = context
2173: .getInterceptorForward();
2174:
2175: if (LOG.isDebugEnabled()) {
2176: LOG
2177: .debug("Action interceptor "
2178: + context
2179: .getOverridingInterceptor()
2180: .getClass().getName()
2181: + " after action " + actionName
2182: + ": forwarding to " + fwd != null ? fwd
2183: .getPath()
2184: : "null [no forward]");
2185: }
2186:
2187: return fwd;
2188: }
2189: }
2190:
2191: return ret;
2192: } catch (InterceptorException e) {
2193: ServletUtils.throwServletException(e);
2194: }
2195:
2196: // should not get here -- either a value is returned or an exception is thrown.
2197: assert false;
2198: return null;
2199: }
2200:
2201: void doActionForward(HttpServletRequest request,
2202: HttpServletResponse response, ActionForward forward)
2203: throws IOException, ServletException {
2204: request = PageFlowRequestWrapper.wrapRequest(request);
2205: processForwardConfig(request, response, forward);
2206: }
2207:
2208: protected boolean processValidate(HttpServletRequest request,
2209: HttpServletResponse response, ActionForm form,
2210: ActionMapping mapping) throws IOException,
2211: ServletException, InvalidCancelException {
2212:
2213: //
2214: // The raw Struts ActionForm doesn't have our logic for enabling declarative validation annotations.
2215: // If this is what we have, create a wrapper that extends FormData to process validation annotations,
2216: // but will also invoke the ActionForm's validate().
2217: //
2218: if (form != null && !(form instanceof BaseActionForm)) {
2219: ActionForm originalForm = form;
2220: form = new ActionFormValidationWrapper(originalForm);
2221: form.setServlet(servlet);
2222: form.setMultipartRequestHandler(originalForm
2223: .getMultipartRequestHandler());
2224: }
2225:
2226: return super .processValidate(request, response, form, mapping);
2227: }
2228:
2229: /**
2230: * Internal method used to ensure that the PageServletFilter is available for use by the request processor.
2231: * If serialization has occurred, this object is transient in this class and may need to be recreated.
2232: * @return the page servlet filter
2233: */
2234: private PageFlowPageFilter ensurePageServletFilter() {
2235: if (_pageServletFilter == null)
2236: _pageServletFilter = new PageServletFilter();
2237:
2238: return _pageServletFilter;
2239: }
2240:
2241: private static class ActionFormValidationWrapper extends
2242: BaseActionForm {
2243: private ActionForm _actionForm;
2244:
2245: public ActionFormValidationWrapper(ActionForm actionForm) {
2246: _actionForm = actionForm;
2247: }
2248:
2249: public ActionErrors validate(ActionMapping mapping,
2250: HttpServletRequest request) {
2251: return validateBean(_actionForm, mapping.getAttribute(),
2252: mapping, request);
2253: }
2254:
2255: protected ActionErrors getAdditionalActionErrors(
2256: ActionMapping mapping, HttpServletRequest request) {
2257: return _actionForm.validate(mapping, request);
2258: }
2259: }
2260: }
|