Паттерн: Шаблонный метод (Template Method)
Исходник: RequestProcessor.java, язык: java [code #528, hits: 7943]
автор: this [добавлен: 08.10.2007]
  1. /*
  2. * $Id: RequestProcessor.java,v 1.1 2007/03/14 21:03:58 me Exp $
  3. *
  4. * Copyright 2000-2006 The Apache Software Foundation.
  5. *
  6. * Licensed under the Apache License, Version 2.0 (the "License");
  7. * you may not use this file except in compliance with the License.
  8. * You may obtain a copy of the License at
  9. *
  10. * http://www.apache.org/licenses/LICENSE-2.0
  11. *
  12. * Unless required by applicable law or agreed to in writing, software
  13. * distributed under the License is distributed on an "AS IS" BASIS,
  14. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15. * See the License for the specific language governing permissions and
  16. * limitations under the License.
  17. */
  18. package org.apache.struts.action;
  19.  
  20. import org.apache.commons.logging.Log;
  21. import org.apache.commons.logging.LogFactory;
  22. import org.apache.struts.Globals;
  23. import org.apache.struts.config.ActionConfig;
  24. import org.apache.struts.config.ExceptionConfig;
  25. import org.apache.struts.config.ForwardConfig;
  26. import org.apache.struts.config.ModuleConfig;
  27. import org.apache.struts.upload.MultipartRequestWrapper;
  28. import org.apache.struts.util.MessageResources;
  29. import org.apache.struts.util.RequestUtils;
  30.  
  31. import javax.servlet.RequestDispatcher;
  32. import javax.servlet.ServletContext;
  33. import javax.servlet.ServletException;
  34. import javax.servlet.http.HttpServletRequest;
  35. import javax.servlet.http.HttpServletResponse;
  36. import javax.servlet.http.HttpSession;
  37.  
  38. import java.io.IOException;
  39.  
  40. import java.util.HashMap;
  41. import java.util.Iterator;
  42. import java.util.Locale;
  43.  
  44. /**
  45. * <p><strong>RequestProcessor</strong> contains the processing logic that the
  46. * {@link ActionServlet} performs as it receives each servlet request from the
  47. * container. You can customize the request processing behavior by subclassing
  48. * this class and overriding the method(s) whose behavior you are interested
  49. * in changing.</p>
  50. *
  51. * @version $Rev: 421119 $ $Date: 2007/03/14 21:03:58 $
  52. * @since Struts 1.1
  53. */
  54. public class RequestProcessor {
  55. // ----------------------------------------------------- Manifest Constants
  56.  
  57. /**
  58. * <p>The request attribute under which the path information is stored for
  59. * processing during a <code>RequestDispatcher.include</code> call.</p>
  60. */
  61. public static final String INCLUDE_PATH_INFO =
  62. "javax.servlet.include.path_info";
  63.  
  64. /**
  65. * <p>The request attribute under which the servlet path information is
  66. * stored for processing during a <code>RequestDispatcher.include</code>
  67. * call.</p>
  68. */
  69. public static final String INCLUDE_SERVLET_PATH =
  70. "javax.servlet.include.servlet_path";
  71.  
  72. /**
  73. * <p>Commons Logging instance.</p>
  74. */
  75. protected static Log log = LogFactory.getLog(RequestProcessor.class);
  76.  
  77. // ----------------------------------------------------- Instance Variables
  78.  
  79. /**
  80. * <p>The set of <code>Action</code> instances that have been created and
  81. * initialized, keyed by the fully qualified Java class name of the
  82. * <code>Action</code> class.</p>
  83. */
  84. protected HashMap actions = new HashMap();
  85.  
  86. /**
  87. * <p>The <code>ModuleConfiguration</code> with which we are
  88. * associated.</p>
  89. */
  90. protected ModuleConfig moduleConfig = null;
  91.  
  92. /**
  93. * <p>The servlet with which we are associated.</p>
  94. */
  95. protected ActionServlet servlet = null;
  96.  
  97. // --------------------------------------------------------- Public Methods
  98.  
  99. /**
  100. * <p>Clean up in preparation for a shutdown of this application.</p>
  101. */
  102. public void destroy() {
  103. synchronized (this.actions) {
  104. Iterator actions = this.actions.values().iterator();
  105.  
  106. while (actions.hasNext()) {
  107. Action action = (Action) actions.next();
  108.  
  109. action.setServlet(null);
  110. }
  111.  
  112. this.actions.clear();
  113. }
  114.  
  115. this.servlet = null;
  116. }
  117.  
  118. /**
  119. * <p>Initialize this request processor instance.</p>
  120. *
  121. * @param servlet The ActionServlet we are associated with
  122. * @param moduleConfig The ModuleConfig we are associated with.
  123. * @throws ServletException If an error occor during initialization
  124. */
  125. public void init(ActionServlet servlet, ModuleConfig moduleConfig)
  126. throws ServletException {
  127. synchronized (actions) {
  128. actions.clear();
  129. }
  130.  
  131. this.servlet = servlet;
  132. this.moduleConfig = moduleConfig;
  133. }
  134.  
  135. /**
  136. * <p>Process an <code>HttpServletRequest</code> and create the
  137. * corresponding <code>HttpServletResponse</code> or dispatch to another
  138. * resource.</p>
  139. *
  140. * @param request The servlet request we are processing
  141. * @param response The servlet response we are creating
  142. * @throws IOException if an input/output error occurs
  143. * @throws ServletException if a processing exception occurs
  144. */
  145. public void process(HttpServletRequest request, HttpServletResponse response)
  146. throws IOException, ServletException {
  147. // Wrap multipart requests with a special wrapper
  148. request = processMultipart(request);
  149.  
  150. // Identify the path component we will use to select a mapping
  151. String path = processPath(request, response);
  152.  
  153. if (path == null) {
  154. return;
  155. }
  156.  
  157. if (log.isDebugEnabled()) {
  158. log.debug("Processing a '" + request.getMethod() + "' for path '"
  159. + path + "'");
  160. }
  161.  
  162. // Select a Locale for the current user if requested
  163. processLocale(request, response);
  164.  
  165. // Set the content type and no-caching headers if requested
  166. processContent(request, response);
  167. processNoCache(request, response);
  168.  
  169. // General purpose preprocessing hook
  170. if (!processPreprocess(request, response)) {
  171. return;
  172. }
  173.  
  174. this.processCachedMessages(request, response);
  175.  
  176. // Identify the mapping for this request
  177. ActionMapping mapping = processMapping(request, response, path);
  178.  
  179. if (mapping == null) {
  180. return;
  181. }
  182.  
  183. // Check for any role required to perform this action
  184. if (!processRoles(request, response, mapping)) {
  185. return;
  186. }
  187.  
  188. // Process any ActionForm bean related to this request
  189. ActionForm form = processActionForm(request, response, mapping);
  190.  
  191. processPopulate(request, response, form, mapping);
  192.  
  193. // Validate any fields of the ActionForm bean, if applicable
  194. try {
  195. if (!processValidate(request, response, form, mapping)) {
  196. return;
  197. }
  198. } catch (InvalidCancelException e) {
  199. ActionForward forward = processException(request, response, e, form, mapping);
  200. processForwardConfig(request, response, forward);
  201. return;
  202. } catch (IOException e) {
  203. throw e;
  204. } catch (ServletException e) {
  205. throw e;
  206. }
  207.  
  208. // Process a forward or include specified by this mapping
  209. if (!processForward(request, response, mapping)) {
  210. return;
  211. }
  212.  
  213. if (!processInclude(request, response, mapping)) {
  214. return;
  215. }
  216.  
  217. // Create or acquire the Action instance to process this request
  218. Action action = processActionCreate(request, response, mapping);
  219.  
  220. if (action == null) {
  221. return;
  222. }
  223.  
  224. // Call the Action instance itself
  225. ActionForward forward =
  226. processActionPerform(request, response, action, form, mapping);
  227.  
  228. // Process the returned ActionForward instance
  229. processForwardConfig(request, response, forward);
  230. }
  231.  
  232. // ----------------------------------------------------- Processing Methods
  233.  
  234. /**
  235. * <p>Return an <code>Action</code> instance that will be used to process
  236. * the current request, creating a new one if necessary.</p>
  237. *
  238. * @param request The servlet request we are processing
  239. * @param response The servlet response we are creating
  240. * @param mapping The mapping we are using
  241. * @return An <code>Action</code> instance that will be used to process
  242. * the current request.
  243. * @throws IOException if an input/output error occurs
  244. */
  245. protected Action processActionCreate(HttpServletRequest request,
  246. HttpServletResponse response, ActionMapping mapping)
  247. throws IOException {
  248. // Acquire the Action instance we will be using (if there is one)
  249. String className = mapping.getType();
  250.  
  251. if (log.isDebugEnabled()) {
  252. log.debug(" Looking for Action instance for class " + className);
  253. }
  254.  
  255. // If there were a mapping property indicating whether
  256. // an Action were a singleton or not ([true]),
  257. // could we just instantiate and return a new instance here?
  258. Action instance;
  259.  
  260. synchronized (actions) {
  261. // Return any existing Action instance of this class
  262. instance = (Action) actions.get(className);
  263.  
  264. if (instance != null) {
  265. if (log.isTraceEnabled()) {
  266. log.trace(" Returning existing Action instance");
  267. }
  268.  
  269. return (instance);
  270. }
  271.  
  272. // Create and return a new Action instance
  273. if (log.isTraceEnabled()) {
  274. log.trace(" Creating new Action instance");
  275. }
  276.  
  277. try {
  278. instance = (Action) RequestUtils.applicationInstance(className);
  279.  
  280. // Maybe we should propagate this exception
  281. // instead of returning null.
  282. } catch (Exception e) {
  283. log.error(getInternal().getMessage("actionCreate",
  284. mapping.getPath()), e);
  285.  
  286. response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
  287. getInternal().getMessage("actionCreate", mapping.getPath()));
  288.  
  289. return (null);
  290. }
  291.  
  292. actions.put(className, instance);
  293. }
  294.  
  295. if (instance.getServlet() == null) {
  296. instance.setServlet(this.servlet);
  297. }
  298.  
  299. return (instance);
  300. }
  301.  
  302. /**
  303. * <p>Retrieve and return the <code>ActionForm</code> associated with this
  304. * mapping, creating and retaining one if necessary. If there is no
  305. * <code>ActionForm</code> associated with this mapping, return
  306. * <code>null</code>.</p>
  307. *
  308. * @param request The servlet request we are processing
  309. * @param response The servlet response we are creating
  310. * @param mapping The mapping we are using
  311. * @return The <code>ActionForm</code> associated with this mapping.
  312. */
  313. protected ActionForm processActionForm(HttpServletRequest request,
  314. HttpServletResponse response, ActionMapping mapping) {
  315. // Create (if necessary) a form bean to use
  316. ActionForm instance =
  317. RequestUtils.createActionForm(request, mapping, moduleConfig,
  318. servlet);
  319.  
  320. if (instance == null) {
  321. return (null);
  322. }
  323.  
  324. // Store the new instance in the appropriate scope
  325. if (log.isDebugEnabled()) {
  326. log.debug(" Storing ActionForm bean instance in scope '"
  327. + mapping.getScope() + "' under attribute key '"
  328. + mapping.getAttribute() + "'");
  329. }
  330.  
  331. if ("request".equals(mapping.getScope())) {
  332. request.setAttribute(mapping.getAttribute(), instance);
  333. } else {
  334. HttpSession session = request.getSession();
  335.  
  336. session.setAttribute(mapping.getAttribute(), instance);
  337. }
  338.  
  339. return (instance);
  340. }
  341.  
  342. /**
  343. * <p>Forward or redirect to the specified destination, by the specified
  344. * mechanism. This method uses a <code>ForwardConfig</code> object
  345. * instead an <code>ActionForward</code>.</p>
  346. *
  347. * @param request The servlet request we are processing
  348. * @param response The servlet response we are creating
  349. * @param forward The ForwardConfig controlling where we go next
  350. * @throws IOException if an input/output error occurs
  351. * @throws ServletException if a servlet exception occurs
  352. */
  353. protected void processForwardConfig(HttpServletRequest request,
  354. HttpServletResponse response, ForwardConfig forward)
  355. throws IOException, ServletException {
  356. if (forward == null) {
  357. return;
  358. }
  359.  
  360. if (log.isDebugEnabled()) {
  361. log.debug("processForwardConfig(" + forward + ")");
  362. }
  363.  
  364. String forwardPath = forward.getPath();
  365. String uri;
  366.  
  367. // paths not starting with / should be passed through without any
  368. // processing (ie. they're absolute)
  369. if (forwardPath.startsWith("/")) {
  370. // get module relative uri
  371. uri = RequestUtils.forwardURL(request, forward, null);
  372. } else {
  373. uri = forwardPath;
  374. }
  375.  
  376. if (forward.getRedirect()) {
  377. // only prepend context path for relative uri
  378. if (uri.startsWith("/")) {
  379. uri = request.getContextPath() + uri;
  380. }
  381.  
  382. response.sendRedirect(response.encodeRedirectURL(uri));
  383. } else {
  384. doForward(uri, request, response);
  385. }
  386. }
  387.  
  388. // :FIXME: if Action.execute throws Exception, and Action.process has been
  389. // removed, should the process* methods still throw IOException,
  390. // ServletException?
  391.  
  392. /**
  393. * <P>Ask the specified <code>Action</code> instance to handle this
  394. * request. Return the <code>ActionForward</code> instance (if any)
  395. * returned by the called <code>Action</code> for further processing.
  396. * </P>
  397. *
  398. * @param request The servlet request we are processing
  399. * @param response The servlet response we are creating
  400. * @param action The Action instance to be used
  401. * @param form The ActionForm instance to pass to this Action
  402. * @param mapping The ActionMapping instance to pass to this Action
  403. * @return The <code>ActionForward</code> instance (if any) returned by
  404. * the called <code>Action</code>.
  405. * @throws IOException if an input/output error occurs
  406. * @throws ServletException if a servlet exception occurs
  407. */
  408. protected ActionForward processActionPerform(HttpServletRequest request,
  409. HttpServletResponse response, Action action, ActionForm form,
  410. ActionMapping mapping)
  411. throws IOException, ServletException {
  412. try {
  413. return (action.execute(mapping, form, request, response));
  414. } catch (Exception e) {
  415. return (processException(request, response, e, form, mapping));
  416. }
  417. }
  418.  
  419. /**
  420. * <p>Removes any <code>ActionMessages</code> object stored in the session
  421. * under <code>Globals.MESSAGE_KEY</code> and <code>Globals.ERROR_KEY</code>
  422. * if the messages' <code>isAccessed</code> method returns true. This
  423. * allows messages to be stored in the session, display one time, and be
  424. * released here.</p>
  425. *
  426. * @param request The servlet request we are processing.
  427. * @param response The servlet response we are creating.
  428. * @since Struts 1.2
  429. */
  430. protected void processCachedMessages(HttpServletRequest request,
  431. HttpServletResponse response) {
  432. HttpSession session = request.getSession(false);
  433.  
  434. if (session == null) {
  435. return;
  436. }
  437.  
  438. // Remove messages as needed
  439. ActionMessages messages =
  440. (ActionMessages) session.getAttribute(Globals.MESSAGE_KEY);
  441.  
  442. if (messages != null) {
  443. if (messages.isAccessed()) {
  444. session.removeAttribute(Globals.MESSAGE_KEY);
  445. }
  446. }
  447.  
  448. // Remove error messages as needed
  449. messages = (ActionMessages) session.getAttribute(Globals.ERROR_KEY);
  450.  
  451. if (messages != null) {
  452. if (messages.isAccessed()) {
  453. session.removeAttribute(Globals.ERROR_KEY);
  454. }
  455. }
  456. }
  457.  
  458. /**
  459. * <p>Set the default content type (with optional character encoding) for
  460. * all responses if requested. <strong>NOTE</strong> - This header will
  461. * be overridden automatically if a <code>RequestDispatcher.forward</code>
  462. * call is ultimately invoked.</p>
  463. *
  464. * @param request The servlet request we are processing
  465. * @param response The servlet response we are creating
  466. */
  467. protected void processContent(HttpServletRequest request,
  468. HttpServletResponse response) {
  469. String contentType =
  470. moduleConfig.getControllerConfig().getContentType();
  471.  
  472. if (contentType != null) {
  473. response.setContentType(contentType);
  474. }
  475. }
  476.  
  477. /**
  478. * <p>Ask our exception handler to handle the exception. Return the
  479. * <code>ActionForward</code> instance (if any) returned by the called
  480. * <code>ExceptionHandler</code>.</p>
  481. *
  482. * @param request The servlet request we are processing
  483. * @param response The servlet response we are processing
  484. * @param exception The exception being handled
  485. * @param form The ActionForm we are processing
  486. * @param mapping The ActionMapping we are using
  487. * @return The <code>ActionForward</code> instance (if any) returned by
  488. * the called <code>ExceptionHandler</code>.
  489. * @throws IOException if an input/output error occurs
  490. * @throws ServletException if a servlet exception occurs
  491. */
  492. protected ActionForward processException(HttpServletRequest request,
  493. HttpServletResponse response, Exception exception, ActionForm form,
  494. ActionMapping mapping)
  495. throws IOException, ServletException {
  496. // Is there a defined handler for this exception?
  497. ExceptionConfig config = mapping.findException(exception.getClass());
  498.  
  499. if (config == null) {
  500. log.warn(getInternal().getMessage("unhandledException",
  501. exception.getClass()));
  502.  
  503. if (exception instanceof IOException) {
  504. throw (IOException) exception;
  505. } else if (exception instanceof ServletException) {
  506. throw (ServletException) exception;
  507. } else {
  508. throw new ServletException(exception);
  509. }
  510. }
  511.  
  512. // Use the configured exception handling
  513. try {
  514. ExceptionHandler handler =
  515. (ExceptionHandler) RequestUtils.applicationInstance(config
  516. .getHandler());
  517.  
  518. return (handler.execute(exception, config, mapping, form, request,
  519. response));
  520. } catch (Exception e) {
  521. throw new ServletException(e);
  522. }
  523. }
  524.  
  525. /**
  526. * <p>Process a forward requested by this mapping (if any). Return
  527. * <code>true</code> if standard processing should continue, or
  528. * <code>false</code> if we have already handled this request.</p>
  529. *
  530. * @param request The servlet request we are processing
  531. * @param response The servlet response we are creating
  532. * @param mapping The ActionMapping we are using
  533. * @return <code>true</code> to continue normal processing;
  534. * <code>false</code> if a response has been created.
  535. * @throws IOException if an input/output error occurs
  536. * @throws ServletException if a servlet exception occurs
  537. */
  538. protected boolean processForward(HttpServletRequest request,
  539. HttpServletResponse response, ActionMapping mapping)
  540. throws IOException, ServletException {
  541. // Are we going to processing this request?
  542. String forward = mapping.getForward();
  543.  
  544. if (forward == null) {
  545. return (true);
  546. }
  547.  
  548. internalModuleRelativeForward(forward, request, response);
  549.  
  550. return (false);
  551. }
  552.  
  553. /**
  554. * <p>Process an include requested by this mapping (if any). Return
  555. * <code>true</code> if standard processing should continue, or
  556. * <code>false</code> if we have already handled this request.</p>
  557. *
  558. * @param request The servlet request we are processing
  559. * @param response The servlet response we are creating
  560. * @param mapping The ActionMapping we are using
  561. * @return <code>true</code> to continue normal processing;
  562. * <code>false</code> if a response has been created.
  563. * @throws IOException if an input/output error occurs
  564. * @throws ServletException if thrown by invoked methods
  565. */
  566. protected boolean processInclude(HttpServletRequest request,
  567. HttpServletResponse response, ActionMapping mapping)
  568. throws IOException, ServletException {
  569. // Are we going to processing this request?
  570. String include = mapping.getInclude();
  571.  
  572. if (include == null) {
  573. return (true);
  574. }
  575.  
  576. internalModuleRelativeInclude(include, request, response);
  577.  
  578. return (false);
  579. }
  580.  
  581. /**
  582. * <p>Automatically select a <code>Locale</code> for the current user, if
  583. * requested. <strong>NOTE</strong> - configuring Locale selection will
  584. * trigger the creation of a new <code>HttpSession</code> if
  585. * necessary.</p>
  586. *
  587. * @param request The servlet request we are processing
  588. * @param response The servlet response we are creating
  589. */
  590. protected void processLocale(HttpServletRequest request,
  591. HttpServletResponse response) {
  592. // Are we configured to select the Locale automatically?
  593. if (!moduleConfig.getControllerConfig().getLocale()) {
  594. return;
  595. }
  596.  
  597. // Has a Locale already been selected?
  598. HttpSession session = request.getSession();
  599.  
  600. if (session.getAttribute(Globals.LOCALE_KEY) != null) {
  601. return;
  602. }
  603.  
  604. // Use the Locale returned by the servlet container (if any)
  605. Locale locale = request.getLocale();
  606.  
  607. if (locale != null) {
  608. if (log.isDebugEnabled()) {
  609. log.debug(" Setting user locale '" + locale + "'");
  610. }
  611.  
  612. session.setAttribute(Globals.LOCALE_KEY, locale);
  613. }
  614. }
  615.  
  616. /**
  617. * <p>Select the mapping used to process the selection path for this
  618. * request. If no mapping can be identified, create an error response and
  619. * return <code>null</code>.</p>
  620. *
  621. * @param request The servlet request we are processing
  622. * @param response The servlet response we are creating
  623. * @param path The portion of the request URI for selecting a mapping
  624. * @return The mapping used to process the selection path for this
  625. * request.
  626. * @throws IOException if an input/output error occurs
  627. */
  628. protected ActionMapping processMapping(HttpServletRequest request,
  629. HttpServletResponse response, String path)
  630. throws IOException {
  631. // Is there a mapping for this path?
  632. ActionMapping mapping =
  633. (ActionMapping) moduleConfig.findActionConfig(path);
  634.  
  635. // If a mapping is found, put it in the request and return it
  636. if (mapping != null) {
  637. request.setAttribute(Globals.MAPPING_KEY, mapping);
  638.  
  639. return (mapping);
  640. }
  641.  
  642. // Locate the mapping for unknown paths (if any)
  643. ActionConfig[] configs = moduleConfig.findActionConfigs();
  644.  
  645. for (int i = 0; i < configs.length; i++) {
  646. if (configs[i].getUnknown()) {
  647. mapping = (ActionMapping) configs[i];
  648. request.setAttribute(Globals.MAPPING_KEY, mapping);
  649.  
  650. return (mapping);
  651. }
  652. }
  653.  
  654. // No mapping can be found to process this request
  655. String msg = getInternal().getMessage("processInvalid");
  656.  
  657. log.error(msg + " " + path);
  658. response.sendError(HttpServletResponse.SC_NOT_FOUND, msg);
  659.  
  660. return null;
  661. }
  662.  
  663. /**
  664. * <p>If this is a multipart request, wrap it with a special wrapper.
  665. * Otherwise, return the request unchanged.</p>
  666. *
  667. * @param request The HttpServletRequest we are processing
  668. * @return A wrapped request, if the request is multipart; otherwise the
  669. * original request.
  670. */
  671. protected HttpServletRequest processMultipart(HttpServletRequest request) {
  672. if (!"POST".equalsIgnoreCase(request.getMethod())) {
  673. return (request);
  674. }
  675.  
  676. String contentType = request.getContentType();
  677.  
  678. if ((contentType != null)
  679. && contentType.startsWith("multipart/form-data")) {
  680. return (new MultipartRequestWrapper(request));
  681. } else {
  682. return (request);
  683. }
  684. }
  685.  
  686. /**
  687. * <p>Set the no-cache headers for all responses, if requested.
  688. * <strong>NOTE</strong> - This header will be overridden automatically if
  689. * a <code>RequestDispatcher.forward</code> call is ultimately
  690. * invoked.</p>
  691. *
  692. * @param request The servlet request we are processing
  693. * @param response The servlet response we are creating
  694. */
  695. protected void processNoCache(HttpServletRequest request,
  696. HttpServletResponse response) {
  697. if (moduleConfig.getControllerConfig().getNocache()) {
  698. response.setHeader("Pragma", "No-cache");
  699. response.setHeader("Cache-Control", "no-cache,no-store,max-age=0");
  700. response.setDateHeader("Expires", 1);
  701. }
  702. }
  703.  
  704. /**
  705. * <p>Identify and return the path component (from the request URI) that
  706. * we will use to select an <code>ActionMapping</code> with which to
  707. * dispatch. If no such path can be identified, create an error response
  708. * and return <code>null</code>.</p>
  709. *
  710. * @param request The servlet request we are processing
  711. * @param response The servlet response we are creating
  712. * @return The path that will be used to select an action mapping.
  713. * @throws IOException if an input/output error occurs
  714. */
  715. protected String processPath(HttpServletRequest request,
  716. HttpServletResponse response)
  717. throws IOException {
  718. String path;
  719.  
  720. // For prefix matching, match on the path info (if any)
  721. path = (String) request.getAttribute(INCLUDE_PATH_INFO);
  722.  
  723. if (path == null) {
  724. path = request.getPathInfo();
  725. }
  726.  
  727. if ((path != null) && (path.length() > 0)) {
  728. return (path);
  729. }
  730.  
  731. // For extension matching, strip the module prefix and extension
  732. path = (String) request.getAttribute(INCLUDE_SERVLET_PATH);
  733.  
  734. if (path == null) {
  735. path = request.getServletPath();
  736. }
  737.  
  738. String prefix = moduleConfig.getPrefix();
  739.  
  740. if (!path.startsWith(prefix)) {
  741. String msg = getInternal().getMessage("processPath");
  742.  
  743. log.error(msg + " " + request.getRequestURI());
  744. response.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
  745.  
  746. return null;
  747. }
  748.  
  749. path = path.substring(prefix.length());
  750.  
  751. int slash = path.lastIndexOf("/");
  752. int period = path.lastIndexOf(".");
  753.  
  754. if ((period >= 0) && (period > slash)) {
  755. path = path.substring(0, period);
  756. }
  757.  
  758. return (path);
  759. }
  760.  
  761. /**
  762. * <p>Populate the properties of the specified <code>ActionForm</code>
  763. * instance from the request parameters included with this request. In
  764. * addition, request attribute <code>Globals.CANCEL_KEY</code> will be set
  765. * if the request was submitted with a button created by
  766. * <code>CancelTag</code>.</p>
  767. *
  768. * @param request The servlet request we are processing
  769. * @param response The servlet response we are creating
  770. * @param form The ActionForm instance we are populating
  771. * @param mapping The ActionMapping we are using
  772. * @throws ServletException if thrown by RequestUtils.populate()
  773. */
  774. protected void processPopulate(HttpServletRequest request,
  775. HttpServletResponse response, ActionForm form, ActionMapping mapping)
  776. throws ServletException {
  777. if (form == null) {
  778. return;
  779. }
  780.  
  781. // Populate the bean properties of this ActionForm instance
  782. if (log.isDebugEnabled()) {
  783. log.debug(" Populating bean properties from this request");
  784. }
  785.  
  786. form.setServlet(this.servlet);
  787. form.reset(mapping, request);
  788.  
  789. if (mapping.getMultipartClass() != null) {
  790. request.setAttribute(Globals.MULTIPART_KEY,
  791. mapping.getMultipartClass());
  792. }
  793.  
  794. RequestUtils.populate(form, mapping.getPrefix(), mapping.getSuffix(),
  795. request);
  796.  
  797. // Set the cancellation request attribute if appropriate
  798. if ((request.getParameter(Globals.CANCEL_PROPERTY) != null)
  799. || (request.getParameter(Globals.CANCEL_PROPERTY_X) != null)) {
  800. request.setAttribute(Globals.CANCEL_KEY, Boolean.TRUE);
  801. }
  802. }
  803.  
  804. /**
  805. * <p>General-purpose preprocessing hook that can be overridden as
  806. * required by subclasses. Return <code>true</code> if you want standard
  807. * processing to continue, or <code>false</code> if the response has
  808. * already been completed. The default implementation does nothing.</p>
  809. *
  810. * @param request The servlet request we are processing
  811. * @param response The servlet response we are creating
  812. * @return <code>true</code> to continue normal processing;
  813. * <code>false</code> if a response has been created.
  814. */
  815. protected boolean processPreprocess(HttpServletRequest request,
  816. HttpServletResponse response) {
  817. return (true);
  818. }
  819.  
  820. /**
  821. * <p>If this action is protected by security roles, make sure that the
  822. * current user possesses at least one of them. Return <code>true</code>
  823. * to continue normal processing, or <code>false</code> if an appropriate
  824. * response has been created and processing should terminate.</p>
  825. *
  826. * @param request The servlet request we are processing
  827. * @param response The servlet response we are creating
  828. * @param mapping The mapping we are using
  829. * @return <code>true</code> to continue normal processing;
  830. * <code>false</code> if a response has been created.
  831. * @throws IOException if an input/output error occurs
  832. * @throws ServletException if a servlet exception occurs
  833. */
  834. protected boolean processRoles(HttpServletRequest request,
  835. HttpServletResponse response, ActionMapping mapping)
  836. throws IOException, ServletException {
  837. // Is this action protected by role requirements?
  838. String[] roles = mapping.getRoleNames();
  839.  
  840. if ((roles == null) || (roles.length < 1)) {
  841. return (true);
  842. }
  843.  
  844. // Check the current user against the list of required roles
  845. for (int i = 0; i < roles.length; i++) {
  846. if (request.isUserInRole(roles[i])) {
  847. if (log.isDebugEnabled()) {
  848. log.debug(" User '" + request.getRemoteUser()
  849. + "' has role '" + roles[i] + "', granting access");
  850. }
  851.  
  852. return (true);
  853. }
  854. }
  855.  
  856. // The current user is not authorized for this action
  857. if (log.isDebugEnabled()) {
  858. log.debug(" User '" + request.getRemoteUser()
  859. + "' does not have any required role, denying access");
  860. }
  861.  
  862. response.sendError(HttpServletResponse.SC_FORBIDDEN,
  863. getInternal().getMessage("notAuthorized", mapping.getPath()));
  864.  
  865. return (false);
  866. }
  867.  
  868. /**
  869. * <p>If this request was not cancelled, and the request's {@link
  870. * ActionMapping} has not disabled validation, call the
  871. * <code>validate</code> method of the specified {@link ActionForm}, and
  872. * forward to the input path if there were any errors. Return
  873. * <code>true</code> if we should continue processing, or
  874. * <code>false</code> if we have already forwarded control back to the
  875. * input form.</p>
  876. *
  877. * @param request The servlet request we are processing
  878. * @param response The servlet response we are creating
  879. * @param form The ActionForm instance we are populating
  880. * @param mapping The ActionMapping we are using
  881. * @return <code>true</code> to continue normal processing;
  882. * <code>false</code> if a response has been created.
  883. * @throws IOException if an input/output error occurs
  884. * @throws ServletException if a servlet exception occurs
  885. * @throws InvalidCancelException if a cancellation is attempted
  886. * without the proper action configuration.
  887. */
  888. protected boolean processValidate(HttpServletRequest request,
  889. HttpServletResponse response, ActionForm form, ActionMapping mapping)
  890. throws IOException, ServletException, InvalidCancelException {
  891. if (form == null) {
  892. return (true);
  893. }
  894.  
  895. // Has validation been turned off for this mapping?
  896. if (!mapping.getValidate()) {
  897. return (true);
  898. }
  899.  
  900. // Was this request cancelled? If it has been, the mapping also
  901. // needs to state whether the cancellation is permissable; otherwise
  902. // the cancellation is considered to be a symptom of a programmer
  903. // error or a spoof.
  904. if (request.getAttribute(Globals.CANCEL_KEY) != null) {
  905. if (mapping.getCancellable()) {
  906. if (log.isDebugEnabled()) {
  907. log.debug(" Cancelled transaction, skipping validation");
  908. }
  909. return (true);
  910. } else {
  911. request.removeAttribute(Globals.CANCEL_KEY);
  912. throw new InvalidCancelException();
  913. }
  914. }
  915.  
  916. // Call the form bean's validation method
  917. if (log.isDebugEnabled()) {
  918. log.debug(" Validating input form properties");
  919. }
  920.  
  921. ActionMessages errors = form.validate(mapping, request);
  922.  
  923. if ((errors == null) || errors.isEmpty()) {
  924. if (log.isTraceEnabled()) {
  925. log.trace(" No errors detected, accepting input");
  926. }
  927.  
  928. return (true);
  929. }
  930.  
  931. // Special handling for multipart request
  932. if (form.getMultipartRequestHandler() != null) {
  933. if (log.isTraceEnabled()) {
  934. log.trace(" Rolling back multipart request");
  935. }
  936.  
  937. form.getMultipartRequestHandler().rollback();
  938. }
  939.  
  940. // Was an input path (or forward) specified for this mapping?
  941. String input = mapping.getInput();
  942.  
  943. if (input == null) {
  944. if (log.isTraceEnabled()) {
  945. log.trace(" Validation failed but no input form available");
  946. }
  947.  
  948. response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
  949. getInternal().getMessage("noInput", mapping.getPath()));
  950.  
  951. return (false);
  952. }
  953.  
  954. // Save our error messages and return to the input form if possible
  955. if (log.isDebugEnabled()) {
  956. log.debug(" Validation failed, returning to '" + input + "'");
  957. }
  958.  
  959. request.setAttribute(Globals.ERROR_KEY, errors);
  960.  
  961. if (moduleConfig.getControllerConfig().getInputForward()) {
  962. ForwardConfig forward = mapping.findForward(input);
  963.  
  964. processForwardConfig(request, response, forward);
  965. } else {
  966. internalModuleRelativeForward(input, request, response);
  967. }
  968.  
  969. return (false);
  970. }
  971.  
  972. /**
  973. * <p>Do a module relative forward to specified URI using request
  974. * dispatcher. URI is relative to the current module. The real URI is
  975. * compute by prefixing the module name.</p> <p>This method is used
  976. * internally and is not part of the public API. It is advised to not use
  977. * it in subclasses. </p>
  978. *
  979. * @param uri Module-relative URI to forward to
  980. * @param request Current page request
  981. * @param response Current page response
  982. * @throws IOException if an input/output error occurs
  983. * @throws ServletException if a servlet exception occurs
  984. * @since Struts 1.1
  985. */
  986. protected void internalModuleRelativeForward(String uri,
  987. HttpServletRequest request, HttpServletResponse response)
  988. throws IOException, ServletException {
  989. // Construct a request dispatcher for the specified path
  990. uri = moduleConfig.getPrefix() + uri;
  991.  
  992. // Delegate the processing of this request
  993. // :FIXME: - exception handling?
  994. if (log.isDebugEnabled()) {
  995. log.debug(" Delegating via forward to '" + uri + "'");
  996. }
  997.  
  998. doForward(uri, request, response);
  999. }
  1000.  
  1001. /**
  1002. * <p>Do a module relative include to specified URI using request
  1003. * dispatcher. URI is relative to the current module. The real URI is
  1004. * compute by prefixing the module name.</p> <p>This method is used
  1005. * internally and is not part of the public API. It is advised to not use
  1006. * it in subclasses.</p>
  1007. *
  1008. * @param uri Module-relative URI to include
  1009. * @param request Current page request
  1010. * @param response Current page response
  1011. * @throws IOException if an input/output error occurs
  1012. * @throws ServletException if a servlet exception occurs
  1013. * @since Struts 1.1
  1014. */
  1015. protected void internalModuleRelativeInclude(String uri,
  1016. HttpServletRequest request, HttpServletResponse response)
  1017. throws IOException, ServletException {
  1018. // Construct a request dispatcher for the specified path
  1019. uri = moduleConfig.getPrefix() + uri;
  1020.  
  1021. // Delegate the processing of this request
  1022. // FIXME - exception handling?
  1023. if (log.isDebugEnabled()) {
  1024. log.debug(" Delegating via include to '" + uri + "'");
  1025. }
  1026.  
  1027. doInclude(uri, request, response);
  1028. }
  1029.  
  1030. /**
  1031. * <p>Do a forward to specified URI using a <code>RequestDispatcher</code>.
  1032. * This method is used by all internal method needing to do a
  1033. * forward.</p>
  1034. *
  1035. * @param uri Context-relative URI to forward to
  1036. * @param request Current page request
  1037. * @param response Current page response
  1038. * @throws IOException if an input/output error occurs
  1039. * @throws ServletException if a servlet exception occurs
  1040. * @since Struts 1.1
  1041. */
  1042. protected void doForward(String uri, HttpServletRequest request,
  1043. HttpServletResponse response)
  1044. throws IOException, ServletException {
  1045. RequestDispatcher rd = getServletContext().getRequestDispatcher(uri);
  1046.  
  1047. if (rd == null) {
  1048. response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
  1049. getInternal().getMessage("requestDispatcher", uri));
  1050.  
  1051. return;
  1052. }
  1053.  
  1054. rd.forward(request, response);
  1055. }
  1056.  
  1057. /**
  1058. * <p>Do an include of specified URI using a <code>RequestDispatcher</code>.
  1059. * This method is used by all internal method needing to do an
  1060. * include.</p>
  1061. *
  1062. * @param uri Context-relative URI to include
  1063. * @param request Current page request
  1064. * @param response Current page response
  1065. * @throws IOException if an input/output error occurs
  1066. * @throws ServletException if a servlet exception occurs
  1067. * @since Struts 1.1
  1068. */
  1069. protected void doInclude(String uri, HttpServletRequest request,
  1070. HttpServletResponse response)
  1071. throws IOException, ServletException {
  1072. RequestDispatcher rd = getServletContext().getRequestDispatcher(uri);
  1073.  
  1074. if (rd == null) {
  1075. response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
  1076. getInternal().getMessage("requestDispatcher", uri));
  1077.  
  1078. return;
  1079. }
  1080.  
  1081. rd.include(request, response);
  1082. }
  1083.  
  1084. // -------------------------------------------------------- Support Methods
  1085.  
  1086. /**
  1087. * <p>Return the <code>MessageResources</code> instance containing our
  1088. * internal message strings.</p>
  1089. *
  1090. * @return The <code>MessageResources</code> instance containing our
  1091. * internal message strings.
  1092. */
  1093. protected MessageResources getInternal() {
  1094. return (servlet.getInternal());
  1095. }
  1096.  
  1097. /**
  1098. * <p>Return the <code>ServletContext</code> for the web application in
  1099. * which we are running.</p>
  1100. *
  1101. * @return The <code>ServletContext</code> for the web application.
  1102. */
  1103. protected ServletContext getServletContext() {
  1104. return (servlet.getServletContext());
  1105. }
  1106. }
Сущность ConcreteClass

Ключевой класс обработки http-запроса struts-фреймворка(1-ой версии).
Сам алгоритм обработки состоит из операций-зацепок, представленных в классе версиями по-умалчанию.
Тестировалось на: java 1.5.0_04

+добавить реализацию