0001: /*
0002: * $Id: RequestProcessor.java 471754 2006-11-06 14:55:09Z husted $
0003: *
0004: * Licensed to the Apache Software Foundation (ASF) under one
0005: * or more contributor license agreements. See the NOTICE file
0006: * distributed with this work for additional information
0007: * regarding copyright ownership. The ASF licenses this file
0008: * to you under the Apache License, Version 2.0 (the
0009: * "License"); you may not use this file except in compliance
0010: * with the License. You may obtain a copy of the License at
0011: *
0012: * http://www.apache.org/licenses/LICENSE-2.0
0013: *
0014: * Unless required by applicable law or agreed to in writing,
0015: * software distributed under the License is distributed on an
0016: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
0017: * KIND, either express or implied. See the License for the
0018: * specific language governing permissions and limitations
0019: * under the License.
0020: */
0021: package org.apache.struts.action;
0022:
0023: import org.apache.commons.logging.Log;
0024: import org.apache.commons.logging.LogFactory;
0025: import org.apache.struts.Globals;
0026: import org.apache.struts.config.ActionConfig;
0027: import org.apache.struts.config.ExceptionConfig;
0028: import org.apache.struts.config.ForwardConfig;
0029: import org.apache.struts.config.ModuleConfig;
0030: import org.apache.struts.upload.MultipartRequestWrapper;
0031: import org.apache.struts.util.MessageResources;
0032: import org.apache.struts.util.RequestUtils;
0033:
0034: import javax.servlet.RequestDispatcher;
0035: import javax.servlet.ServletContext;
0036: import javax.servlet.ServletException;
0037: import javax.servlet.http.HttpServletRequest;
0038: import javax.servlet.http.HttpServletResponse;
0039: import javax.servlet.http.HttpSession;
0040:
0041: import java.io.IOException;
0042:
0043: import java.util.HashMap;
0044: import java.util.Iterator;
0045: import java.util.Locale;
0046:
0047: /**
0048: * <p><strong>RequestProcessor</strong> contains the processing logic that the
0049: * {@link ActionServlet} performs as it receives each servlet request from the
0050: * container. You can customize the request processing behavior by subclassing
0051: * this class and overriding the method(s) whose behavior you are interested
0052: * in changing.</p>
0053: *
0054: * @version $Rev: 471754 $ $Date: 2006-11-06 08:55:09 -0600 (Mon, 06 Nov 2006) $
0055: * @since Struts 1.1
0056: */
0057: public class RequestProcessor {
0058: // ----------------------------------------------------- Manifest Constants
0059:
0060: /**
0061: * <p>The request attribute under which the path information is stored for
0062: * processing during a <code>RequestDispatcher.include</code> call.</p>
0063: */
0064: public static final String INCLUDE_PATH_INFO = "javax.servlet.include.path_info";
0065:
0066: /**
0067: * <p>The request attribute under which the servlet path information is
0068: * stored for processing during a <code>RequestDispatcher.include</code>
0069: * call.</p>
0070: */
0071: public static final String INCLUDE_SERVLET_PATH = "javax.servlet.include.servlet_path";
0072:
0073: /**
0074: * <p>Commons Logging instance.</p>
0075: */
0076: protected static Log log = LogFactory
0077: .getLog(RequestProcessor.class);
0078:
0079: // ----------------------------------------------------- Instance Variables
0080:
0081: /**
0082: * <p>The set of <code>Action</code> instances that have been created and
0083: * initialized, keyed by the fully qualified Java class name of the
0084: * <code>Action</code> class.</p>
0085: */
0086: protected HashMap actions = new HashMap();
0087:
0088: /**
0089: * <p>The <code>ModuleConfiguration</code> with which we are
0090: * associated.</p>
0091: */
0092: protected ModuleConfig moduleConfig = null;
0093:
0094: /**
0095: * <p>The servlet with which we are associated.</p>
0096: */
0097: protected ActionServlet servlet = null;
0098:
0099: // --------------------------------------------------------- Public Methods
0100:
0101: /**
0102: * <p>Clean up in preparation for a shutdown of this application.</p>
0103: */
0104: public void destroy() {
0105: synchronized (this .actions) {
0106: Iterator actions = this .actions.values().iterator();
0107:
0108: while (actions.hasNext()) {
0109: Action action = (Action) actions.next();
0110:
0111: action.setServlet(null);
0112: }
0113:
0114: this .actions.clear();
0115: }
0116:
0117: this .servlet = null;
0118: }
0119:
0120: /**
0121: * <p>Initialize this request processor instance.</p>
0122: *
0123: * @param servlet The ActionServlet we are associated with
0124: * @param moduleConfig The ModuleConfig we are associated with.
0125: * @throws ServletException If an error occor during initialization
0126: */
0127: public void init(ActionServlet servlet, ModuleConfig moduleConfig)
0128: throws ServletException {
0129: synchronized (actions) {
0130: actions.clear();
0131: }
0132:
0133: this .servlet = servlet;
0134: this .moduleConfig = moduleConfig;
0135: }
0136:
0137: /**
0138: * <p>Process an <code>HttpServletRequest</code> and create the
0139: * corresponding <code>HttpServletResponse</code> or dispatch to another
0140: * resource.</p>
0141: *
0142: * @param request The servlet request we are processing
0143: * @param response The servlet response we are creating
0144: * @throws IOException if an input/output error occurs
0145: * @throws ServletException if a processing exception occurs
0146: */
0147: public void process(HttpServletRequest request,
0148: HttpServletResponse response) throws IOException,
0149: ServletException {
0150: // Wrap multipart requests with a special wrapper
0151: request = processMultipart(request);
0152:
0153: // Identify the path component we will use to select a mapping
0154: String path = processPath(request, response);
0155:
0156: if (path == null) {
0157: return;
0158: }
0159:
0160: if (log.isDebugEnabled()) {
0161: log.debug("Processing a '" + request.getMethod()
0162: + "' for path '" + path + "'");
0163: }
0164:
0165: // Select a Locale for the current user if requested
0166: processLocale(request, response);
0167:
0168: // Set the content type and no-caching headers if requested
0169: processContent(request, response);
0170: processNoCache(request, response);
0171:
0172: // General purpose preprocessing hook
0173: if (!processPreprocess(request, response)) {
0174: return;
0175: }
0176:
0177: this .processCachedMessages(request, response);
0178:
0179: // Identify the mapping for this request
0180: ActionMapping mapping = processMapping(request, response, path);
0181:
0182: if (mapping == null) {
0183: return;
0184: }
0185:
0186: // Check for any role required to perform this action
0187: if (!processRoles(request, response, mapping)) {
0188: return;
0189: }
0190:
0191: // Process any ActionForm bean related to this request
0192: ActionForm form = processActionForm(request, response, mapping);
0193:
0194: processPopulate(request, response, form, mapping);
0195:
0196: // Validate any fields of the ActionForm bean, if applicable
0197: try {
0198: if (!processValidate(request, response, form, mapping)) {
0199: return;
0200: }
0201: } catch (InvalidCancelException e) {
0202: ActionForward forward = processException(request, response,
0203: e, form, mapping);
0204: processForwardConfig(request, response, forward);
0205: return;
0206: } catch (IOException e) {
0207: throw e;
0208: } catch (ServletException e) {
0209: throw e;
0210: }
0211:
0212: // Process a forward or include specified by this mapping
0213: if (!processForward(request, response, mapping)) {
0214: return;
0215: }
0216:
0217: if (!processInclude(request, response, mapping)) {
0218: return;
0219: }
0220:
0221: // Create or acquire the Action instance to process this request
0222: Action action = processActionCreate(request, response, mapping);
0223:
0224: if (action == null) {
0225: return;
0226: }
0227:
0228: // Call the Action instance itself
0229: ActionForward forward = processActionPerform(request, response,
0230: action, form, mapping);
0231:
0232: // Process the returned ActionForward instance
0233: processForwardConfig(request, response, forward);
0234: }
0235:
0236: // ----------------------------------------------------- Processing Methods
0237:
0238: /**
0239: * <p>Return an <code>Action</code> instance that will be used to process
0240: * the current request, creating a new one if necessary.</p>
0241: *
0242: * @param request The servlet request we are processing
0243: * @param response The servlet response we are creating
0244: * @param mapping The mapping we are using
0245: * @return An <code>Action</code> instance that will be used to process
0246: * the current request.
0247: * @throws IOException if an input/output error occurs
0248: */
0249: protected Action processActionCreate(HttpServletRequest request,
0250: HttpServletResponse response, ActionMapping mapping)
0251: throws IOException {
0252: // Acquire the Action instance we will be using (if there is one)
0253: String className = mapping.getType();
0254:
0255: if (log.isDebugEnabled()) {
0256: log.debug(" Looking for Action instance for class "
0257: + className);
0258: }
0259:
0260: // If there were a mapping property indicating whether
0261: // an Action were a singleton or not ([true]),
0262: // could we just instantiate and return a new instance here?
0263: Action instance;
0264:
0265: synchronized (actions) {
0266: // Return any existing Action instance of this class
0267: instance = (Action) actions.get(className);
0268:
0269: if (instance != null) {
0270: if (log.isTraceEnabled()) {
0271: log.trace(" Returning existing Action instance");
0272: }
0273:
0274: return (instance);
0275: }
0276:
0277: // Create and return a new Action instance
0278: if (log.isTraceEnabled()) {
0279: log.trace(" Creating new Action instance");
0280: }
0281:
0282: try {
0283: instance = (Action) RequestUtils
0284: .applicationInstance(className);
0285:
0286: // Maybe we should propagate this exception
0287: // instead of returning null.
0288: } catch (Exception e) {
0289: log.error(getInternal().getMessage("actionCreate",
0290: mapping.getPath()), e);
0291:
0292: response.sendError(
0293: HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
0294: getInternal().getMessage("actionCreate",
0295: mapping.getPath()));
0296:
0297: return (null);
0298: }
0299:
0300: actions.put(className, instance);
0301: }
0302:
0303: if (instance.getServlet() == null) {
0304: instance.setServlet(this .servlet);
0305: }
0306:
0307: return (instance);
0308: }
0309:
0310: /**
0311: * <p>Retrieve and return the <code>ActionForm</code> associated with this
0312: * mapping, creating and retaining one if necessary. If there is no
0313: * <code>ActionForm</code> associated with this mapping, return
0314: * <code>null</code>.</p>
0315: *
0316: * @param request The servlet request we are processing
0317: * @param response The servlet response we are creating
0318: * @param mapping The mapping we are using
0319: * @return The <code>ActionForm</code> associated with this mapping.
0320: */
0321: protected ActionForm processActionForm(HttpServletRequest request,
0322: HttpServletResponse response, ActionMapping mapping) {
0323: // Create (if necessary) a form bean to use
0324: ActionForm instance = RequestUtils.createActionForm(request,
0325: mapping, moduleConfig, servlet);
0326:
0327: if (instance == null) {
0328: return (null);
0329: }
0330:
0331: // Store the new instance in the appropriate scope
0332: if (log.isDebugEnabled()) {
0333: log.debug(" Storing ActionForm bean instance in scope '"
0334: + mapping.getScope() + "' under attribute key '"
0335: + mapping.getAttribute() + "'");
0336: }
0337:
0338: if ("request".equals(mapping.getScope())) {
0339: request.setAttribute(mapping.getAttribute(), instance);
0340: } else {
0341: HttpSession session = request.getSession();
0342:
0343: session.setAttribute(mapping.getAttribute(), instance);
0344: }
0345:
0346: return (instance);
0347: }
0348:
0349: /**
0350: * <p>Forward or redirect to the specified destination, by the specified
0351: * mechanism. This method uses a <code>ForwardConfig</code> object
0352: * instead an <code>ActionForward</code>.</p>
0353: *
0354: * @param request The servlet request we are processing
0355: * @param response The servlet response we are creating
0356: * @param forward The ForwardConfig controlling where we go next
0357: * @throws IOException if an input/output error occurs
0358: * @throws ServletException if a servlet exception occurs
0359: */
0360: protected void processForwardConfig(HttpServletRequest request,
0361: HttpServletResponse response, ForwardConfig forward)
0362: throws IOException, ServletException {
0363: if (forward == null) {
0364: return;
0365: }
0366:
0367: if (log.isDebugEnabled()) {
0368: log.debug("processForwardConfig(" + forward + ")");
0369: }
0370:
0371: String forwardPath = forward.getPath();
0372: String uri;
0373:
0374: // If the forward can be unaliased into an action, then use the path of the action
0375: String actionIdPath = RequestUtils.actionIdURL(forward,
0376: request, servlet);
0377: if (actionIdPath != null) {
0378: forwardPath = actionIdPath;
0379: ForwardConfig actionIdForward = new ForwardConfig(forward);
0380: actionIdForward.setPath(actionIdPath);
0381: forward = actionIdForward;
0382: }
0383:
0384: // paths not starting with / should be passed through without any
0385: // processing (ie. they're absolute)
0386: if (forwardPath.startsWith("/")) {
0387: // get module relative uri
0388: uri = RequestUtils.forwardURL(request, forward, null);
0389: } else {
0390: uri = forwardPath;
0391: }
0392:
0393: if (forward.getRedirect()) {
0394: // only prepend context path for relative uri
0395: if (uri.startsWith("/")) {
0396: uri = request.getContextPath() + uri;
0397: }
0398:
0399: response.sendRedirect(response.encodeRedirectURL(uri));
0400: } else {
0401: doForward(uri, request, response);
0402: }
0403: }
0404:
0405: // :FIXME: if Action.execute throws Exception, and Action.process has been
0406: // removed, should the process* methods still throw IOException,
0407: // ServletException?
0408:
0409: /**
0410: * <P>Ask the specified <code>Action</code> instance to handle this
0411: * request. Return the <code>ActionForward</code> instance (if any)
0412: * returned by the called <code>Action</code> for further processing.
0413: * </P>
0414: *
0415: * @param request The servlet request we are processing
0416: * @param response The servlet response we are creating
0417: * @param action The Action instance to be used
0418: * @param form The ActionForm instance to pass to this Action
0419: * @param mapping The ActionMapping instance to pass to this Action
0420: * @return The <code>ActionForward</code> instance (if any) returned by
0421: * the called <code>Action</code>.
0422: * @throws IOException if an input/output error occurs
0423: * @throws ServletException if a servlet exception occurs
0424: */
0425: protected ActionForward processActionPerform(
0426: HttpServletRequest request, HttpServletResponse response,
0427: Action action, ActionForm form, ActionMapping mapping)
0428: throws IOException, ServletException {
0429: try {
0430: return (action.execute(mapping, form, request, response));
0431: } catch (Exception e) {
0432: return (processException(request, response, e, form,
0433: mapping));
0434: }
0435: }
0436:
0437: /**
0438: * <p>Removes any <code>ActionMessages</code> object stored in the session
0439: * under <code>Globals.MESSAGE_KEY</code> and <code>Globals.ERROR_KEY</code>
0440: * if the messages' <code>isAccessed</code> method returns true. This
0441: * allows messages to be stored in the session, display one time, and be
0442: * released here.</p>
0443: *
0444: * @param request The servlet request we are processing.
0445: * @param response The servlet response we are creating.
0446: * @since Struts 1.2
0447: */
0448: protected void processCachedMessages(HttpServletRequest request,
0449: HttpServletResponse response) {
0450: HttpSession session = request.getSession(false);
0451:
0452: if (session == null) {
0453: return;
0454: }
0455:
0456: // Remove messages as needed
0457: ActionMessages messages = (ActionMessages) session
0458: .getAttribute(Globals.MESSAGE_KEY);
0459:
0460: if (messages != null) {
0461: if (messages.isAccessed()) {
0462: session.removeAttribute(Globals.MESSAGE_KEY);
0463: }
0464: }
0465:
0466: // Remove error messages as needed
0467: messages = (ActionMessages) session
0468: .getAttribute(Globals.ERROR_KEY);
0469:
0470: if (messages != null) {
0471: if (messages.isAccessed()) {
0472: session.removeAttribute(Globals.ERROR_KEY);
0473: }
0474: }
0475: }
0476:
0477: /**
0478: * <p>Set the default content type (with optional character encoding) for
0479: * all responses if requested. <strong>NOTE</strong> - This header will
0480: * be overridden automatically if a <code>RequestDispatcher.forward</code>
0481: * call is ultimately invoked.</p>
0482: *
0483: * @param request The servlet request we are processing
0484: * @param response The servlet response we are creating
0485: */
0486: protected void processContent(HttpServletRequest request,
0487: HttpServletResponse response) {
0488: String contentType = moduleConfig.getControllerConfig()
0489: .getContentType();
0490:
0491: if (contentType != null) {
0492: response.setContentType(contentType);
0493: }
0494: }
0495:
0496: /**
0497: * <p>Ask our exception handler to handle the exception. Return the
0498: * <code>ActionForward</code> instance (if any) returned by the called
0499: * <code>ExceptionHandler</code>.</p>
0500: *
0501: * @param request The servlet request we are processing
0502: * @param response The servlet response we are processing
0503: * @param exception The exception being handled
0504: * @param form The ActionForm we are processing
0505: * @param mapping The ActionMapping we are using
0506: * @return The <code>ActionForward</code> instance (if any) returned by
0507: * the called <code>ExceptionHandler</code>.
0508: * @throws IOException if an input/output error occurs
0509: * @throws ServletException if a servlet exception occurs
0510: */
0511: protected ActionForward processException(
0512: HttpServletRequest request, HttpServletResponse response,
0513: Exception exception, ActionForm form, ActionMapping mapping)
0514: throws IOException, ServletException {
0515: // Is there a defined handler for this exception?
0516: ExceptionConfig config = mapping.findException(exception
0517: .getClass());
0518:
0519: if (config == null) {
0520: log.warn(getInternal().getMessage("unhandledException",
0521: exception.getClass()));
0522:
0523: if (exception instanceof IOException) {
0524: throw (IOException) exception;
0525: } else if (exception instanceof ServletException) {
0526: throw (ServletException) exception;
0527: } else {
0528: throw new ServletException(exception);
0529: }
0530: }
0531:
0532: // Use the configured exception handling
0533: try {
0534: ExceptionHandler handler = (ExceptionHandler) RequestUtils
0535: .applicationInstance(config.getHandler());
0536:
0537: return (handler.execute(exception, config, mapping, form,
0538: request, response));
0539: } catch (Exception e) {
0540: throw new ServletException(e);
0541: }
0542: }
0543:
0544: /**
0545: * <p>Process a forward requested by this mapping (if any). Return
0546: * <code>true</code> if standard processing should continue, or
0547: * <code>false</code> if we have already handled this request.</p>
0548: *
0549: * @param request The servlet request we are processing
0550: * @param response The servlet response we are creating
0551: * @param mapping The ActionMapping we are using
0552: * @return <code>true</code> to continue normal processing;
0553: * <code>false</code> if a response has been created.
0554: * @throws IOException if an input/output error occurs
0555: * @throws ServletException if a servlet exception occurs
0556: */
0557: protected boolean processForward(HttpServletRequest request,
0558: HttpServletResponse response, ActionMapping mapping)
0559: throws IOException, ServletException {
0560: // Are we going to processing this request?
0561: String forward = mapping.getForward();
0562:
0563: if (forward == null) {
0564: return (true);
0565: }
0566:
0567: // If the forward can be unaliased into an action, then use the path of the action
0568: String actionIdPath = RequestUtils.actionIdURL(forward,
0569: this .moduleConfig, this .servlet);
0570: if (actionIdPath != null) {
0571: forward = actionIdPath;
0572: }
0573:
0574: internalModuleRelativeForward(forward, request, response);
0575:
0576: return (false);
0577: }
0578:
0579: /**
0580: * <p>Process an include requested by this mapping (if any). Return
0581: * <code>true</code> if standard processing should continue, or
0582: * <code>false</code> if we have already handled this request.</p>
0583: *
0584: * @param request The servlet request we are processing
0585: * @param response The servlet response we are creating
0586: * @param mapping The ActionMapping we are using
0587: * @return <code>true</code> to continue normal processing;
0588: * <code>false</code> if a response has been created.
0589: * @throws IOException if an input/output error occurs
0590: * @throws ServletException if thrown by invoked methods
0591: */
0592: protected boolean processInclude(HttpServletRequest request,
0593: HttpServletResponse response, ActionMapping mapping)
0594: throws IOException, ServletException {
0595: // Are we going to processing this request?
0596: String include = mapping.getInclude();
0597:
0598: if (include == null) {
0599: return (true);
0600: }
0601:
0602: // If the forward can be unaliased into an action, then use the path of the action
0603: String actionIdPath = RequestUtils.actionIdURL(include,
0604: this .moduleConfig, this .servlet);
0605: if (actionIdPath != null) {
0606: include = actionIdPath;
0607: }
0608:
0609: internalModuleRelativeInclude(include, request, response);
0610:
0611: return (false);
0612: }
0613:
0614: /**
0615: * <p>Automatically select a <code>Locale</code> for the current user, if
0616: * requested. <strong>NOTE</strong> - configuring Locale selection will
0617: * trigger the creation of a new <code>HttpSession</code> if
0618: * necessary.</p>
0619: *
0620: * @param request The servlet request we are processing
0621: * @param response The servlet response we are creating
0622: */
0623: protected void processLocale(HttpServletRequest request,
0624: HttpServletResponse response) {
0625: // Are we configured to select the Locale automatically?
0626: if (!moduleConfig.getControllerConfig().getLocale()) {
0627: return;
0628: }
0629:
0630: // Has a Locale already been selected?
0631: HttpSession session = request.getSession();
0632:
0633: if (session.getAttribute(Globals.LOCALE_KEY) != null) {
0634: return;
0635: }
0636:
0637: // Use the Locale returned by the servlet container (if any)
0638: Locale locale = request.getLocale();
0639:
0640: if (locale != null) {
0641: if (log.isDebugEnabled()) {
0642: log.debug(" Setting user locale '" + locale + "'");
0643: }
0644:
0645: session.setAttribute(Globals.LOCALE_KEY, locale);
0646: }
0647: }
0648:
0649: /**
0650: * <p>Select the mapping used to process the selection path for this
0651: * request. If no mapping can be identified, create an error response and
0652: * return <code>null</code>.</p>
0653: *
0654: * @param request The servlet request we are processing
0655: * @param response The servlet response we are creating
0656: * @param path The portion of the request URI for selecting a mapping
0657: * @return The mapping used to process the selection path for this
0658: * request.
0659: * @throws IOException if an input/output error occurs
0660: */
0661: protected ActionMapping processMapping(HttpServletRequest request,
0662: HttpServletResponse response, String path)
0663: throws IOException {
0664: // Is there a mapping for this path?
0665: ActionMapping mapping = (ActionMapping) moduleConfig
0666: .findActionConfig(path);
0667:
0668: // If a mapping is found, put it in the request and return it
0669: if (mapping != null) {
0670: request.setAttribute(Globals.MAPPING_KEY, mapping);
0671:
0672: return (mapping);
0673: }
0674:
0675: // Locate the mapping for unknown paths (if any)
0676: ActionConfig[] configs = moduleConfig.findActionConfigs();
0677:
0678: for (int i = 0; i < configs.length; i++) {
0679: if (configs[i].getUnknown()) {
0680: mapping = (ActionMapping) configs[i];
0681: request.setAttribute(Globals.MAPPING_KEY, mapping);
0682:
0683: return (mapping);
0684: }
0685: }
0686:
0687: // No mapping can be found to process this request
0688: String msg = getInternal().getMessage("processInvalid");
0689:
0690: log.error(msg + " " + path);
0691: response.sendError(HttpServletResponse.SC_NOT_FOUND, msg);
0692:
0693: return null;
0694: }
0695:
0696: /**
0697: * <p>If this is a multipart request, wrap it with a special wrapper.
0698: * Otherwise, return the request unchanged.</p>
0699: *
0700: * @param request The HttpServletRequest we are processing
0701: * @return A wrapped request, if the request is multipart; otherwise the
0702: * original request.
0703: */
0704: protected HttpServletRequest processMultipart(
0705: HttpServletRequest request) {
0706: if (!"POST".equalsIgnoreCase(request.getMethod())) {
0707: return (request);
0708: }
0709:
0710: String contentType = request.getContentType();
0711:
0712: if ((contentType != null)
0713: && contentType.startsWith("multipart/form-data")) {
0714: return (new MultipartRequestWrapper(request));
0715: } else {
0716: return (request);
0717: }
0718: }
0719:
0720: /**
0721: * <p>Set the no-cache headers for all responses, if requested.
0722: * <strong>NOTE</strong> - This header will be overridden automatically if
0723: * a <code>RequestDispatcher.forward</code> call is ultimately
0724: * invoked.</p>
0725: *
0726: * @param request The servlet request we are processing
0727: * @param response The servlet response we are creating
0728: */
0729: protected void processNoCache(HttpServletRequest request,
0730: HttpServletResponse response) {
0731: if (moduleConfig.getControllerConfig().getNocache()) {
0732: response.setHeader("Pragma", "No-cache");
0733: response.setHeader("Cache-Control",
0734: "no-cache,no-store,max-age=0");
0735: response.setDateHeader("Expires", 1);
0736: }
0737: }
0738:
0739: /**
0740: * <p>Identify and return the path component (from the request URI) that
0741: * we will use to select an <code>ActionMapping</code> with which to
0742: * dispatch. If no such path can be identified, create an error response
0743: * and return <code>null</code>.</p>
0744: *
0745: * @param request The servlet request we are processing
0746: * @param response The servlet response we are creating
0747: * @return The path that will be used to select an action mapping.
0748: * @throws IOException if an input/output error occurs
0749: */
0750: protected String processPath(HttpServletRequest request,
0751: HttpServletResponse response) throws IOException {
0752: String path;
0753:
0754: // For prefix matching, match on the path info (if any)
0755: path = (String) request.getAttribute(INCLUDE_PATH_INFO);
0756:
0757: if (path == null) {
0758: path = request.getPathInfo();
0759: }
0760:
0761: if ((path != null) && (path.length() > 0)) {
0762: return (path);
0763: }
0764:
0765: // For extension matching, strip the module prefix and extension
0766: path = (String) request.getAttribute(INCLUDE_SERVLET_PATH);
0767:
0768: if (path == null) {
0769: path = request.getServletPath();
0770: }
0771:
0772: String prefix = moduleConfig.getPrefix();
0773:
0774: if (!path.startsWith(prefix)) {
0775: String msg = getInternal().getMessage("processPath");
0776:
0777: log.error(msg + " " + request.getRequestURI());
0778: response.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
0779:
0780: return null;
0781: }
0782:
0783: path = path.substring(prefix.length());
0784:
0785: int slash = path.lastIndexOf("/");
0786: int period = path.lastIndexOf(".");
0787:
0788: if ((period >= 0) && (period > slash)) {
0789: path = path.substring(0, period);
0790: }
0791:
0792: return (path);
0793: }
0794:
0795: /**
0796: * <p>Populate the properties of the specified <code>ActionForm</code>
0797: * instance from the request parameters included with this request. In
0798: * addition, request attribute <code>Globals.CANCEL_KEY</code> will be set
0799: * if the request was submitted with a button created by
0800: * <code>CancelTag</code>.</p>
0801: *
0802: * @param request The servlet request we are processing
0803: * @param response The servlet response we are creating
0804: * @param form The ActionForm instance we are populating
0805: * @param mapping The ActionMapping we are using
0806: * @throws ServletException if thrown by RequestUtils.populate()
0807: */
0808: protected void processPopulate(HttpServletRequest request,
0809: HttpServletResponse response, ActionForm form,
0810: ActionMapping mapping) throws ServletException {
0811: if (form == null) {
0812: return;
0813: }
0814:
0815: // Populate the bean properties of this ActionForm instance
0816: if (log.isDebugEnabled()) {
0817: log.debug(" Populating bean properties from this request");
0818: }
0819:
0820: form.setServlet(this .servlet);
0821: form.reset(mapping, request);
0822:
0823: if (mapping.getMultipartClass() != null) {
0824: request.setAttribute(Globals.MULTIPART_KEY, mapping
0825: .getMultipartClass());
0826: }
0827:
0828: RequestUtils.populate(form, mapping.getPrefix(), mapping
0829: .getSuffix(), request);
0830:
0831: // Set the cancellation request attribute if appropriate
0832: if ((request.getParameter(Globals.CANCEL_PROPERTY) != null)
0833: || (request.getParameter(Globals.CANCEL_PROPERTY_X) != null)) {
0834: request.setAttribute(Globals.CANCEL_KEY, Boolean.TRUE);
0835: }
0836: }
0837:
0838: /**
0839: * <p>General-purpose preprocessing hook that can be overridden as
0840: * required by subclasses. Return <code>true</code> if you want standard
0841: * processing to continue, or <code>false</code> if the response has
0842: * already been completed. The default implementation does nothing.</p>
0843: *
0844: * @param request The servlet request we are processing
0845: * @param response The servlet response we are creating
0846: * @return <code>true</code> to continue normal processing;
0847: * <code>false</code> if a response has been created.
0848: */
0849: protected boolean processPreprocess(HttpServletRequest request,
0850: HttpServletResponse response) {
0851: return (true);
0852: }
0853:
0854: /**
0855: * <p>If this action is protected by security roles, make sure that the
0856: * current user possesses at least one of them. Return <code>true</code>
0857: * to continue normal processing, or <code>false</code> if an appropriate
0858: * response has been created and processing should terminate.</p>
0859: *
0860: * @param request The servlet request we are processing
0861: * @param response The servlet response we are creating
0862: * @param mapping The mapping we are using
0863: * @return <code>true</code> to continue normal processing;
0864: * <code>false</code> if a response has been created.
0865: * @throws IOException if an input/output error occurs
0866: * @throws ServletException if a servlet exception occurs
0867: */
0868: protected boolean processRoles(HttpServletRequest request,
0869: HttpServletResponse response, ActionMapping mapping)
0870: throws IOException, ServletException {
0871: // Is this action protected by role requirements?
0872: String[] roles = mapping.getRoleNames();
0873:
0874: if ((roles == null) || (roles.length < 1)) {
0875: return (true);
0876: }
0877:
0878: // Check the current user against the list of required roles
0879: for (int i = 0; i < roles.length; i++) {
0880: if (request.isUserInRole(roles[i])) {
0881: if (log.isDebugEnabled()) {
0882: log.debug(" User '" + request.getRemoteUser()
0883: + "' has role '" + roles[i]
0884: + "', granting access");
0885: }
0886:
0887: return (true);
0888: }
0889: }
0890:
0891: // The current user is not authorized for this action
0892: if (log.isDebugEnabled()) {
0893: log
0894: .debug(" User '"
0895: + request.getRemoteUser()
0896: + "' does not have any required role, denying access");
0897: }
0898:
0899: response.sendError(HttpServletResponse.SC_FORBIDDEN,
0900: getInternal().getMessage("notAuthorized",
0901: mapping.getPath()));
0902:
0903: return (false);
0904: }
0905:
0906: /**
0907: * <p>If this request was not cancelled, and the request's {@link
0908: * ActionMapping} has not disabled validation, call the
0909: * <code>validate</code> method of the specified {@link ActionForm}, and
0910: * forward to the input path if there were any errors. Return
0911: * <code>true</code> if we should continue processing, or
0912: * <code>false</code> if we have already forwarded control back to the
0913: * input form.</p>
0914: *
0915: * @param request The servlet request we are processing
0916: * @param response The servlet response we are creating
0917: * @param form The ActionForm instance we are populating
0918: * @param mapping The ActionMapping we are using
0919: * @return <code>true</code> to continue normal processing;
0920: * <code>false</code> if a response has been created.
0921: * @throws IOException if an input/output error occurs
0922: * @throws ServletException if a servlet exception occurs
0923: * @throws InvalidCancelException if a cancellation is attempted
0924: * without the proper action configuration.
0925: */
0926: protected boolean processValidate(HttpServletRequest request,
0927: HttpServletResponse response, ActionForm form,
0928: ActionMapping mapping) throws IOException,
0929: ServletException, InvalidCancelException {
0930: if (form == null) {
0931: return (true);
0932: }
0933:
0934: // Has validation been turned off for this mapping?
0935: if (!mapping.getValidate()) {
0936: return (true);
0937: }
0938:
0939: // Was this request cancelled? If it has been, the mapping also
0940: // needs to state whether the cancellation is permissable; otherwise
0941: // the cancellation is considered to be a symptom of a programmer
0942: // error or a spoof.
0943: if (request.getAttribute(Globals.CANCEL_KEY) != null) {
0944: if (mapping.getCancellable()) {
0945: if (log.isDebugEnabled()) {
0946: log
0947: .debug(" Cancelled transaction, skipping validation");
0948: }
0949: return (true);
0950: } else {
0951: request.removeAttribute(Globals.CANCEL_KEY);
0952: throw new InvalidCancelException();
0953: }
0954: }
0955:
0956: // Call the form bean's validation method
0957: if (log.isDebugEnabled()) {
0958: log.debug(" Validating input form properties");
0959: }
0960:
0961: ActionMessages errors = form.validate(mapping, request);
0962:
0963: if ((errors == null) || errors.isEmpty()) {
0964: if (log.isTraceEnabled()) {
0965: log.trace(" No errors detected, accepting input");
0966: }
0967:
0968: return (true);
0969: }
0970:
0971: // Special handling for multipart request
0972: if (form.getMultipartRequestHandler() != null) {
0973: if (log.isTraceEnabled()) {
0974: log.trace(" Rolling back multipart request");
0975: }
0976:
0977: form.getMultipartRequestHandler().rollback();
0978: }
0979:
0980: // Was an input path (or forward) specified for this mapping?
0981: String input = mapping.getInput();
0982:
0983: if (input == null) {
0984: if (log.isTraceEnabled()) {
0985: log
0986: .trace(" Validation failed but no input form available");
0987: }
0988:
0989: response.sendError(
0990: HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
0991: getInternal().getMessage("noInput",
0992: mapping.getPath()));
0993:
0994: return (false);
0995: }
0996:
0997: // Save our error messages and return to the input form if possible
0998: if (log.isDebugEnabled()) {
0999: log.debug(" Validation failed, returning to '" + input
1000: + "'");
1001: }
1002:
1003: request.setAttribute(Globals.ERROR_KEY, errors);
1004:
1005: if (moduleConfig.getControllerConfig().getInputForward()) {
1006: ForwardConfig forward = mapping.findForward(input);
1007:
1008: processForwardConfig(request, response, forward);
1009: } else {
1010: internalModuleRelativeForward(input, request, response);
1011: }
1012:
1013: return (false);
1014: }
1015:
1016: /**
1017: * <p>Do a module relative forward to specified URI using request
1018: * dispatcher. URI is relative to the current module. The real URI is
1019: * compute by prefixing the module name.</p> <p>This method is used
1020: * internally and is not part of the public API. It is advised to not use
1021: * it in subclasses. </p>
1022: *
1023: * @param uri Module-relative URI to forward to
1024: * @param request Current page request
1025: * @param response Current page response
1026: * @throws IOException if an input/output error occurs
1027: * @throws ServletException if a servlet exception occurs
1028: * @since Struts 1.1
1029: */
1030: protected void internalModuleRelativeForward(String uri,
1031: HttpServletRequest request, HttpServletResponse response)
1032: throws IOException, ServletException {
1033: // Construct a request dispatcher for the specified path
1034: uri = moduleConfig.getPrefix() + uri;
1035:
1036: // Delegate the processing of this request
1037: // :FIXME: - exception handling?
1038: if (log.isDebugEnabled()) {
1039: log.debug(" Delegating via forward to '" + uri + "'");
1040: }
1041:
1042: doForward(uri, request, response);
1043: }
1044:
1045: /**
1046: * <p>Do a module relative include to specified URI using request
1047: * dispatcher. URI is relative to the current module. The real URI is
1048: * compute by prefixing the module name.</p> <p>This method is used
1049: * internally and is not part of the public API. It is advised to not use
1050: * it in subclasses.</p>
1051: *
1052: * @param uri Module-relative URI to include
1053: * @param request Current page request
1054: * @param response Current page response
1055: * @throws IOException if an input/output error occurs
1056: * @throws ServletException if a servlet exception occurs
1057: * @since Struts 1.1
1058: */
1059: protected void internalModuleRelativeInclude(String uri,
1060: HttpServletRequest request, HttpServletResponse response)
1061: throws IOException, ServletException {
1062: // Construct a request dispatcher for the specified path
1063: uri = moduleConfig.getPrefix() + uri;
1064:
1065: // Delegate the processing of this request
1066: // FIXME - exception handling?
1067: if (log.isDebugEnabled()) {
1068: log.debug(" Delegating via include to '" + uri + "'");
1069: }
1070:
1071: doInclude(uri, request, response);
1072: }
1073:
1074: /**
1075: * <p>Do a forward to specified URI using a <code>RequestDispatcher</code>.
1076: * This method is used by all internal method needing to do a
1077: * forward.</p>
1078: *
1079: * @param uri Context-relative URI to forward to
1080: * @param request Current page request
1081: * @param response Current page response
1082: * @throws IOException if an input/output error occurs
1083: * @throws ServletException if a servlet exception occurs
1084: * @since Struts 1.1
1085: */
1086: protected void doForward(String uri, HttpServletRequest request,
1087: HttpServletResponse response) throws IOException,
1088: ServletException {
1089: RequestDispatcher rd = getServletContext()
1090: .getRequestDispatcher(uri);
1091:
1092: if (rd == null) {
1093: response.sendError(
1094: HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
1095: getInternal().getMessage("requestDispatcher", uri));
1096:
1097: return;
1098: }
1099:
1100: rd.forward(request, response);
1101: }
1102:
1103: /**
1104: * <p>Do an include of specified URI using a <code>RequestDispatcher</code>.
1105: * This method is used by all internal method needing to do an
1106: * include.</p>
1107: *
1108: * @param uri Context-relative URI to include
1109: * @param request Current page request
1110: * @param response Current page response
1111: * @throws IOException if an input/output error occurs
1112: * @throws ServletException if a servlet exception occurs
1113: * @since Struts 1.1
1114: */
1115: protected void doInclude(String uri, HttpServletRequest request,
1116: HttpServletResponse response) throws IOException,
1117: ServletException {
1118: RequestDispatcher rd = getServletContext()
1119: .getRequestDispatcher(uri);
1120:
1121: if (rd == null) {
1122: response.sendError(
1123: HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
1124: getInternal().getMessage("requestDispatcher", uri));
1125:
1126: return;
1127: }
1128:
1129: rd.include(request, response);
1130: }
1131:
1132: // -------------------------------------------------------- Support Methods
1133:
1134: /**
1135: * <p>Return the <code>MessageResources</code> instance containing our
1136: * internal message strings.</p>
1137: *
1138: * @return The <code>MessageResources</code> instance containing our
1139: * internal message strings.
1140: */
1141: protected MessageResources getInternal() {
1142: return (servlet.getInternal());
1143: }
1144:
1145: /**
1146: * <p>Return the <code>ServletContext</code> for the web application in
1147: * which we are running.</p>
1148: *
1149: * @return The <code>ServletContext</code> for the web application.
1150: */
1151: protected ServletContext getServletContext() {
1152: return (servlet.getServletContext());
1153: }
1154: }
|