[Actionframework-users] rewrite of ActionServlet.processRequest
Status: Inactive
Brought to you by:
ptoman
From: Mark D. A. <md...@di...> - 2002-10-08 00:40:07
|
I've rewritten ActionServlet.processRequest with the attached code. This was for a number of reasons: 1. The existing code was incomprehensible :). I think a criteria for version 1.0 is that there are no more labeled breaks in the source code except for "for" loops.... 2. There was no provision for a subclass hook method which is called in between the point of determining what the action is, and actually executing that action. This is the problem that started me down this road: the function handle() is called early, but at that point the action is not known. The invokeMethod() is called later with the action, but at that point it is too late to change the action. 3. The decision of using regex matching was coupled with whether the action name comes from a parameter or from pathinfo. In my new version, I have not capitalized on 2 or 3 yet; I instead attempted to preserve existing semantics. I'm not sure I have done so (see issue 1). -mda -------------------- // possible results of calling analyzeRequest() protected static final int REQ_NO_ACTION = 1; protected static final int REQ_UNASSIGNED_ACTION = 2; protected static final int REQ_REAL_ACTION = 3; protected static final int REQ_ANALYZE_EXCEPTION = 99; protected static class ActionRequestData { int request_type_ = 0; MultipartRequest multipartRequest_ = null; String actionName_ = null; String formName_ = null; HashMap actions_ = null; ParameterRetriever retriever_ = null; boolean isRegexAction_ = false; ActionException exception_ = null; Template template_ = null; Action action_ = null; public ActionRequestData() {} public ActionRequestData(HttpServletRequest request, Context context) { } } protected void handleUpload(HttpServletRequest request, ActionRequestData ard) throws FileUploadException { if (!allowUploads) throw new FileUploadException("File uploads are not allowed"); try { if (uploadDirectory == null) ard.multipartRequest_ = new ServletMultipartRequest(request, maxUploadFileSize); else ard.multipartRequest_ = new ServletMultipartRequest(request, uploadDirectory); // override HttpParameterRetriever ard.retriever_ = new MultipartParameterRetriever(runtime.log, ard.multipartRequest_, UPLOAD_DIR_PROPERTY, uploadDirectory); } catch (IllegalArgumentException e) { throw new FileUploadException("Boundary not found in file upload request", e); } catch (IOException e) { throw new FileUploadException("I/O error ocurred while reading file upload request or file too big", e); } } /** * determine what the action is, or produce a template or exception. */ protected ActionRequestData analyzeRequest(HttpServletRequest request, Context context) { ActionRequestData ard = new ActionRequestData(request, context); // basic setup of parameter retriever ard.retriever_ = new HttpParameterRetriever(runtime.log, request); // maybe handle file upload // TODO: for now the upload is handled here, which is not ideal because // semantically this function just analyzes what it proposes to do later. String contentType = request.getContentType(); if (contentType != null && contentType.startsWith("multipart/form-data")) { try {handleUpload(request, ard);} catch(FileUploadException e) {ard.exception_ = e; ard.request_type_ = REQ_ANALYZE_EXCEPTION; return ard;} Enumeration e = ard.multipartRequest_.getURLParameters("form"); if (e.hasMoreElements()) ard.formName_ = (String) e.nextElement(); ard.actions_ = (HashMap) runtime.forms.get(ard.formName_ == null? "": ard.formName_.trim()); e = ard.multipartRequest_.getURLParameters("action"); if (e.hasMoreElements()) ard.actionName_ = (String) e.nextElement(); } // not a file upload. else { ard.formName_ = request.getParameter("form"); ard.actions_ = (HashMap) runtime.forms.get(ard.formName_ == null? "": ard.formName_.trim()); ard.actionName_ = request.getParameter("action"); runtime.log.debug("action from request parameter: " + ard.actionName_); } // action is not set from request parameter, so try PathInfo. // NOTE: currently we always do regex matching if and only if action name comes from pathinfo. // NOTE: we allow a "form" request parameter to be used even if action came from PathInfo. boolean attempt_match_regexp = false; if (ard.actionName_ == null) { ard.actionName_ = request.getPathInfo(); runtime.log.debug("action from PathInfo: " + ard.actionName_); attempt_match_regexp = true; } // we always trim the action name regardless of where it came from. if (ard.actionName_ != null) ard.actionName_ = ard.actionName_.trim(); // we treat action names of null, "", and "/" all as equivalent (i.e. nothing found), // regardless of where the action name came from. if (ard.actionName_.length() == 0 || ard.actionName_.equals("/")) { runtime.log.debug("ignoring action name specified as '" + ard.actionName_ + "'"); ard.actionName_ = null; } // if action name is not specified, use the "new session" [sic: terrible name]. if (ard.actionName_ == null) { // new session or reload of the default page ard.template_ = newSession(context); if (ard.template_ == null) { ard.request_type_ = REQ_ANALYZE_EXCEPTION; ard.exception_ = new ActionException("No new-session template defined - " + "ActionServlet.newSession() method returns null"); return ard; } ard.request_type_ = REQ_NO_ACTION; return ard; } // else if action name is specified, look up the action, either via regexp // or assuming the exact name has been provided. if (attempt_match_regexp) { ard.action_ = (Action) runtime.regularActions.get(ard.actionName_); ard.isRegexAction_ = true; } else if (ard.actions_ != null) { ard.action_ = (Action) ard.actions_.get(ard.actionName_); } // if no action has been found, call unassignedAction if (ard.action_ == null) { ard.request_type_ = REQ_UNASSIGNED_ACTION; runtime.log.debug("action '" + ard.actionName_ + "' did not match as " + (attempt_match_regexp ? "regular" : "exact")); try {ard.template_ = unassignedAction(context, ard.formName_, ard.actionName_);} catch(ActionException e) { ard.template_ = onException(context, ard.formName_, ard.actionName_, e); ard.request_type_ = REQ_ANALYZE_EXCEPTION; } return ard; } // ok now we have an action // set up parameter retriever if (ard.action_.urlParams != null) { String s = request.getRequestURI().substring(((String) context.get("SERVLET")).length()); String param_string = s.substring(s.indexOf('/', 1)); // insert URLParameterRetriever between ContextParameterRetriever and HTTP/multipart retreiver ard.retriever_ = new ChainParameterRetriever( new URLParameterRetriever(runtime.log, ard.action_.urlParams, param_string), ard.retriever_); } // set InputVariableParameterRetriever as last retriever - if this action has any <input-variable>s if (ard.action_.inputVariables != null) ard.retriever_ = new InputVariableParameterRetriever(runtime.log, ard.action_.inputVariables, ard.retriever_); ard.request_type_ = REQ_REAL_ACTION; return ard; } /** * Called from {@link #handle(HttpServletRequest,HttpServletResponse,Context) handle()} method. */ private ActionReturnData processRequest(HttpServletRequest request, Context context) { ActionRequestData ard = analyzeRequest(request, context); Action action = ard.action_; String actionName = ard.actionName_; String formName = ard.formName_; ParameterRetriever retriever = ard.retriever_; Template error_template = null; ActionException e = null; try { if (ard.exception_ != null) e = ard.exception_; else { // TODO: ideally upload handling would be here, but right now, // it has already been done by analyzeRequest(). // if (ard.multipartRequest_ != null) handleUpload(request, ard); if (ard.template_ != null) { return evalOutputVars(ard.template_, ard.retriever_, context); } if (action == null) throw new Error("i should have template or action set"); // get component for action Object component = null; if (action.componentData.persistence != ComponentData.PERSISTENCE_STATIC) component = getComponent(action.componentData.componentName, true, null); try { return action.invoke(formName, actionName, context, ard.retriever_, component, false); } finally { // add component with "request" persistence to 'lastComponents' if (action.componentData.persistence == ComponentData.PERSISTENCE_REQUEST && component instanceof Destroyed) { runtime.putNewLastComponent(request.getSession(true).getId(), action.componentData.componentName, component); } } // unreachable } } catch (ConversionException ce) { runtime.log.error("[" + context.hashCode() + "] " + "in processRequest", ce); // ConverstionException is not an ActionException // this likely won't be used since we are getting an error_template. e = new ActionException("ConversionException", ce); try { error_template = conversionError(context, formName, actionName, ce); } catch(Exception ex) { e = new ActionException("conversionError", ex); } } catch (InstantiatorException ie) { runtime.log.error("[" + context.hashCode() + "] " + "in processRequest", ie); e = new ActionException("Instantiation error", ie); } catch (ActionException ae) { runtime.log.error("[" + context.hashCode() + "] " + "in processRequest", ae); e = ae; } catch (Exception ex) { runtime.log.error("[" + context.hashCode() + "] " + "in processRequest", ex); e = new ActionException("Unexpected exception", ex); } // at this point either e or error_template is not null. // note that with this logic, it is possible to get two calls to onException. // get an error template if we don't have one if (error_template == null) { if (e == null) throw new Error("shouldn't be here without an exception"); try {error_template = onException(context, formName, actionName, e);} catch (Exception ex) { runtime.log.error("[" + context.hashCode() + "] " + "Exception thrown in '" + getClass().getName() + ".onException()'", ex); return new ActionReturnData(error(context, ex.getMessage()), null, null); } } // get ActionReturnData for error template try { return evalOutputVars(error_template, retriever, context); } catch (ActionException ex) { runtime.log.error("[" + context.hashCode() + "] " + "Exception after invoking '" + getClass().getName() + ".onException()'", ex); return new ActionReturnData(error_template, null, null); } catch (ConversionException ex) { runtime.log.error("[" + context.hashCode() + "] " + "Conversion error while handling <invoke> elements of template '" + error_template.getName() + "'", ex); return new ActionReturnData(error_template, null, null); } } |