public void doRequest()

in framework/webapp/src/main/java/org/apache/ofbiz/webapp/control/RequestHandler.java [358:1035]


    public void doRequest(HttpServletRequest request, HttpServletResponse response, String chain,
                          GenericValue userLogin, Delegator delegator) throws RequestHandlerException, RequestHandlerExceptionAllowExternalRequests {

        if (!HOSTHEADERSALLOWED.contains(request.getServerName())) {
            Debug.logError("Domain " + request.getServerName() + " not accepted to prevent host header injection."
                    + " You need to set host-headers-allowed property in security.properties file.", MODULE);
            throw new RequestHandlerException("Domain " + request.getServerName() + " not accepted to prevent host header injection."
                    + " You need to set host-headers-allowed property in security.properties file.");
        }

        final boolean throwRequestHandlerExceptionOnMissingLocalRequest = EntityUtilProperties.propertyValueEqualsIgnoreCase(
                "requestHandler", "throwRequestHandlerExceptionOnMissingLocalRequest", "Y", delegator);
        long startTime = System.currentTimeMillis();
        HttpSession session = request.getSession();

        // Parse controller config.
        try {
            ccfg = ConfigXMLReader.getControllerConfig(controllerConfigURL);
        } catch (WebAppConfigurationException e) {
            Debug.logError(e, "Exception thrown while parsing controller.xml file: ", MODULE);
            throw new RequestHandlerException(e);
        }

        // workaround if we are in the root webapp
        String cname = UtilHttp.getApplicationName(request);

        // Grab data from request object to process
        String defaultRequestUri = RequestHandler.getRequestUri(request.getPathInfo());

        String requestMissingErrorMessage = "Unknown request ["
                + defaultRequestUri
                + "]; this request does not exist or cannot be called directly.";

        String path = request.getPathInfo();
        String requestUri = getRequestUri(path);

        Collection<RequestMap> rmaps = resolveURI(ccfg, request);
        if (rmaps.isEmpty()) {
            if (throwRequestHandlerExceptionOnMissingLocalRequest) {
                if (path.contains("/checkLogin/") || path.contains("/sendconfirmationmail/") || path.contains("/getUiLabels")) {
                    // Nested requests related with checkLogin and sendconfirmationmail are OK.
                    // There is nothing to worry about, better remove these wrong errors messages.
                    return;
                } else if (path.contains("/images/") || path.contains("d.png")) {
                    if (Debug.warningOn()) {
                        Debug.logWarning("You should check if this request is really a problem or a false alarm: " + request.getRequestURL(), MODULE);
                    }
                    throw new RequestHandlerException(requestMissingErrorMessage);
                } else {
                    throw new RequestHandlerException(requestMissingErrorMessage);
                }
            } else {
                throw new RequestHandlerExceptionAllowExternalRequests();
            }
        }
        // The "overriddenView" attribute is set by resolveURI when necessary.
        String overrideViewUri = (String) request.getAttribute("overriddenView");

        String method = UtilHttp.getRequestMethod(request);
        RequestMap requestMap = resolveMethod(method, rmaps).orElseThrow(() -> {
            String msg = UtilProperties.getMessage("WebappUiLabels", "RequestMethodNotMatchConfig",
                    UtilMisc.toList(requestUri, method), UtilHttp.getLocale(request));
            return new MethodNotAllowedException(msg);
        });

        String eventReturn = null;
        if (requestMap.getMetrics() != null && requestMap.getMetrics().getThreshold() != 0.0 && requestMap.getMetrics().getTotalEvents() > 3
                && requestMap.getMetrics().getThreshold() < requestMap.getMetrics().getServiceRate()) {
            eventReturn = "threshold-exceeded";
        }
        ConfigXMLReader.RequestMap originalRequestMap = requestMap; // Save this so we can update the correct performance metrics.


        boolean interruptRequest = false;

        // Check for chained request.
        if (chain != null) {
            String chainRequestUri = RequestHandler.getRequestUri(chain);
            requestMap = ccfg.getRequestMapMap().get(chainRequestUri);
            if (requestMap == null) {
                throw new RequestHandlerException("Unknown chained request [" + chainRequestUri + "]; this request does not exist");
            }
            if (request.getAttribute("_POST_CHAIN_VIEW_") != null) {
                overrideViewUri = (String) request.getAttribute("_POST_CHAIN_VIEW_");
            } else {
                overrideViewUri = RequestHandler.getOverrideViewUri(chain);
            }
            if (overrideViewUri != null) {
                // put this in a request attribute early in case an event needs to access it
                // not using _POST_CHAIN_VIEW_ because it shouldn't be set unless the event execution is successful
                request.setAttribute("_CURRENT_CHAIN_VIEW_", overrideViewUri);
            }
            if (Debug.infoOn()) {
                Debug.logInfo("[RequestHandler]: Chain in place: requestUri=" + chainRequestUri + " overrideViewUri=" + overrideViewUri
                        + showSessionId(request), MODULE);
            }
        } else {
            // Check if X509 is required and we are not secure; throw exception
            if (!request.isSecure() && requestMap.isSecurityCert()) {
                throw new RequestHandlerException(requestMissingErrorMessage);
            }

            // Check to make sure we are allowed to access this request directly. (Also checks if this request is defined.)
            // If the request cannot be called, or is not defined, check and see if there is a default-request we can process
            if (!requestMap.isSecurityDirectRequest()) {
                if (ccfg.getDefaultRequest() == null || !ccfg.getRequestMapMap().get(ccfg.getDefaultRequest()).isSecurityDirectRequest()) {
                    // use the same message as if it was missing for security reasons, ie so can't tell if it is missing or direct request is not
                    // allowed
                    throw new RequestHandlerException(requestMissingErrorMessage);
                } else {
                    requestMap = ccfg.getRequestMapMap().get(ccfg.getDefaultRequest());
                }
            }
            // Check if we SHOULD be secure and are not.
            boolean forwardedHTTPS = "HTTPS".equalsIgnoreCase(request.getHeader("X-Forwarded-Proto"));
            if (!request.isSecure() && !forwardedHTTPS && requestMap.isSecurityHttps()) {
                // If the request method was POST then return an error to avoid problems with CSRF where the request
                // may have come from another machine/program and had the same session ID but was not encrypted as it
                // should have been (we used to let it pass to not lose data since it was too late to protect that data anyway)
                if ("POST".equalsIgnoreCase(request.getMethod())) {
                    // we can't redirect with the body parameters, and for better security from CSRF, just return an error message
                    Locale locale = UtilHttp.getLocale(request);
                    String errMsg = UtilProperties.getMessage("WebappUiLabels", "requestHandler.InsecureFormPostToSecureRequest", locale);
                    Debug.logError("Got an insecure (non HTTPS) form POST to a secure (HTTPS) request [" + requestMap.getUri() + "], returning error",
                            MODULE);

                    // see if HTTPS is enabled, if not then log a warning instead of throwing an exception
                    Boolean enableHttps = null;
                    String webSiteId = WebSiteWorker.getWebSiteId(request);
                    if (webSiteId != null) {
                        try {
                            GenericValue webSite = EntityQuery.use(delegator).from("WebSite").where("webSiteId", webSiteId).cache().queryOne();
                            if (webSite != null) enableHttps = webSite.getBoolean("enableHttps");
                        } catch (GenericEntityException e) {
                            Debug.logWarning(e, "Problems with WebSite entity; using global defaults", MODULE);
                        }
                    }
                    if (enableHttps == null) {
                        enableHttps = EntityUtilProperties.propertyValueEqualsIgnoreCase("url", "port.https.enabled", "Y", delegator);
                    }

                    if (Boolean.FALSE.equals(enableHttps)) {
                        Debug.logWarning("HTTPS is disabled for this site, so we can't tell if this was encrypted or not which means if a form was "
                                + "POSTed "
                                + "and it was not over HTTPS we don't know, but it would be vulnerable to an CSRF and other attacks: " + errMsg,
                                MODULE);
                    } else {
                        throw new RequestHandlerException(errMsg);
                    }
                } else {
                    StringBuilder urlBuf = new StringBuilder();
                    urlBuf.append(request.getPathInfo());
                    if (request.getQueryString() != null) {
                        urlBuf.append("?").append(request.getQueryString());
                    }
                    String newUrl = RequestHandler.makeUrl(request, response, urlBuf.toString());
                    if (newUrl.toUpperCase().startsWith("HTTPS")) {
                        // if we are supposed to be secure, redirect secure.
                        callRedirect(newUrl, response, request, ccfg.getStatusCode());
                        return;
                    }
                }
            }

            // Check for HTTPS client (x.509) security
            if (request.isSecure() && requestMap.isSecurityCert()) {
                if (!checkCertificates(request, certs -> SSLUtil.isClientTrusted(certs, null))) {
                    Debug.logWarning(requestMissingErrorMessage, MODULE);
                    throw new RequestHandlerException(requestMissingErrorMessage);
                }
            }

            // If its the first visit run the first visit events.
            if (this.trackVisit(request) && session.getAttribute("_FIRST_VISIT_EVENTS_") == null) {
                if (Debug.infoOn()) {
                    Debug.logInfo("This is the first request in this visit." + showSessionId(request), MODULE);
                }
                session.setAttribute("_FIRST_VISIT_EVENTS_", "complete");
                for (ConfigXMLReader.Event event : ccfg.getFirstVisitEventList().values()) {
                    try {
                        String returnString = this.runEvent(request, response, event, null, "firstvisit");
                        if (returnString == null || "none".equalsIgnoreCase(returnString)) {
                            interruptRequest = true;
                        } else if (!"success".equalsIgnoreCase(returnString)) {
                            throw new EventHandlerException("First-Visit event did not return 'success'.");
                        }
                    } catch (EventHandlerException e) {
                        Debug.logError(e, MODULE);
                    }
                }
            }

            // Invoke the pre-processor (but NOT in a chain)
            for (ConfigXMLReader.Event event : ccfg.getPreprocessorEventList().values()) {
                try {
                    String returnString = this.runEvent(request, response, event, null, "preprocessor");
                    if (returnString == null || "none".equalsIgnoreCase(returnString)) {
                        interruptRequest = true;
                    } else if (!"success".equalsIgnoreCase(returnString)) {
                        if (!returnString.contains(":_protect_:")) {
                            throw new EventHandlerException("Pre-Processor event [" + event.getInvoke() + "] did not return 'success'.");
                        } else { // protect the view normally rendered and redirect to error response view
                            returnString = returnString.replace(":_protect_:", "");
                            if (!returnString.isEmpty()) {
                                request.setAttribute("_ERROR_MESSAGE_", returnString);
                            }
                            eventReturn = null;
                            // check to see if there is a "protect" response, if so it's ok else show the default_error_response_view
                            if (!requestMap.getRequestResponseMap().containsKey("protect")) {
                                if (ccfg.getProtectView() != null) {
                                    overrideViewUri = ccfg.getProtectView();
                                } else {
                                    overrideViewUri = EntityUtilProperties.getPropertyValue("security", "default.error.response.view", delegator);
                                    overrideViewUri = overrideViewUri.replace("view:", "");
                                    if ("none:".equals(overrideViewUri)) {
                                        interruptRequest = true;
                                    }
                                }
                            }
                        }
                    }
                } catch (EventHandlerException e) {
                    Debug.logError(e, MODULE);
                }
            }
        }

        // Pre-Processor/First-Visit event(s) can interrupt the flow by returning null.
        // Warning: this could cause problems if more then one event attempts to return a response.
        if (interruptRequest) {
            if (Debug.infoOn()) {
                Debug.logInfo("[Pre-Processor Interrupted Request, not running: [" + requestMap.getUri() + "]. " + showSessionId(request), MODULE);
            }
            return;
        }

        if (Debug.verboseOn()) {
            Debug.logVerbose("[Processing Request]: " + requestMap.getUri() + showSessionId(request), MODULE);
        }
        request.setAttribute("thisRequestUri", requestMap.getUri()); // store the actual request URI

        // Store current requestMap map to be referred later when generating csrf token
        request.setAttribute("requestMapMap", getControllerConfig().getRequestMapMap());

        // Perform CSRF token check when request not on chain
        if (chain == null && originalRequestMap.isSecurityCsrfToken()) {
            CsrfUtil.checkToken(request, path);
        }

        // Perform security check.
        if (requestMap.isSecurityAuth()) {
            // Invoke the security handler
            // catch exceptions and throw RequestHandlerException if failed.
            if (Debug.verboseOn()) {
                Debug.logVerbose("[RequestHandler]: AuthRequired. Running security check. " + showSessionId(request), MODULE);
            }
            ConfigXMLReader.Event checkLoginEvent = ccfg.getRequestMapMap().get("checkLogin").getEvent();
            String checkLoginReturnString = null;

            try {
                checkLoginReturnString = this.runEvent(request, response, checkLoginEvent, null, "security-auth");
            } catch (EventHandlerException e) {
                throw new RequestHandlerException(e.getMessage(), e);
            }
            if (!"success".equalsIgnoreCase(checkLoginReturnString)) {
                // previous URL already saved by event, so just do as the return says...
                eventReturn = checkLoginReturnString;
                // if the request is an ajax request we don't want to return the default login check
                if (!"XMLHttpRequest".equals(request.getHeader("X-Requested-With"))) {
                    requestMap = ccfg.getRequestMapMap().get("checkLogin");
                } else {
                    requestMap = ccfg.getRequestMapMap().get("ajaxCheckLogin");
                }
            }
        } else if (requestUri != null) {
            String[] loginUris = EntityUtilProperties.getPropertyValue("security", "login.uris", delegator).split(",");
            boolean removePreviousRequest = true;
            for (int i = 0; i < loginUris.length; i++) {
                if (requestUri.equals(loginUris[i])) {
                    removePreviousRequest = false;
                }
            }
            if (removePreviousRequest) {
                // Remove previous request attribute on navigation to non-authenticated request
                request.getSession().removeAttribute("_PREVIOUS_REQUEST_");
            }
        }

        if (request.getAttribute("targetRequestUri") == null) {
            if (request.getSession().getAttribute("_PREVIOUS_REQUEST_") != null) {
                request.setAttribute("targetRequestUri", request.getSession().getAttribute("_PREVIOUS_REQUEST_"));
            } else {
                request.setAttribute("targetRequestUri", "/" + defaultRequestUri);
            }
        }

        // after security check but before running the event, see if a post-login redirect has completed and we have data from the pre-login
        // request form to use now
        // we know this is the case if the _PREVIOUS_PARAM_MAP_ attribute is there, but the _PREVIOUS_REQUEST_ attribute has already been removed
        if (request.getSession().getAttribute("_PREVIOUS_PARAM_MAP_FORM_") != null
                && request.getSession().getAttribute("_PREVIOUS_REQUEST_") == null) {
            Map<String, Object> previousParamMap = UtilGenerics.checkMap(request.getSession().getAttribute("_PREVIOUS_PARAM_MAP_FORM_"),
                    String.class, Object.class);
            previousParamMap.forEach(request::setAttribute);

            // to avoid this data being included again, now remove the _PREVIOUS_PARAM_MAP_ attribute
            request.getSession().removeAttribute("_PREVIOUS_PARAM_MAP_FORM_");
        }

        // now we can start looking for the next request response to use
        ConfigXMLReader.RequestResponse nextRequestResponse = null;

        // Invoke the defined event (unless login failed)
        if (eventReturn == null && requestMap.getEvent() != null) {
            if (requestMap.getEvent().getType() != null && requestMap.getEvent().getPath() != null && requestMap.getEvent().getInvoke() != null) {
                try {
                    long eventStartTime = System.currentTimeMillis();

                    // run the request event
                    eventReturn = this.runEvent(request, response, requestMap.getEvent(), requestMap, "request");

                    if (requestMap.getEvent().getMetrics() != null) {
                        requestMap.getEvent().getMetrics().recordServiceRate(1, System.currentTimeMillis() - startTime);
                    }

                    // save the server hit for the request event
                    if (this.trackStats(request)) {
                        ServerHitBin.countEvent(cname + "." + requestMap.getEvent().getInvoke(), request, eventStartTime,
                                System.currentTimeMillis() - eventStartTime, userLogin);
                    }

                    // set the default event return
                    if (eventReturn == null) {
                        nextRequestResponse = ConfigXMLReader.getEmptyNoneRequestResponse();
                    }
                } catch (EventHandlerException e) {
                    // check to see if there is an "error" response, if so go there and make an request error message
                    if (requestMap.getRequestResponseMap().containsKey("error")) {
                        eventReturn = "error";
                        Locale locale = UtilHttp.getLocale(request);
                        String errMsg = UtilProperties.getMessage("WebappUiLabels", "requestHandler.error_call_event", locale);
                        request.setAttribute("_ERROR_MESSAGE_", errMsg + ": " + e.toString());
                    } else {
                        throw new RequestHandlerException("Error calling event and no error response was specified", e);
                    }
                }
            }
        }

        // Process the eventReturn
        // at this point eventReturnString is finalized, so get the RequestResponse
        ConfigXMLReader.RequestResponse eventReturnBasedRequestResponse;
        if (eventReturn == null) {
            eventReturnBasedRequestResponse = null;
        } else {
            eventReturnBasedRequestResponse = requestMap.getRequestResponseMap().get(eventReturn);
            if (eventReturnBasedRequestResponse == null && "none".equals(eventReturn)) {
                eventReturnBasedRequestResponse = ConfigXMLReader.getEmptyNoneRequestResponse();
            }
        }
        if (eventReturnBasedRequestResponse != null) {
            //String eventReturnBasedResponse = requestResponse.value;
            if (Debug.verboseOn()) {
                Debug.logVerbose("[Response Qualified]: " + eventReturnBasedRequestResponse.getName() + ", "
                        + eventReturnBasedRequestResponse.getType()
                        + ":" + eventReturnBasedRequestResponse.getValue() + showSessionId(request), MODULE);
            }

            // If error, then display more error messages:
            if ("error".equals(eventReturnBasedRequestResponse.getName())) {
                String uri = requestMap.getUri();
                if (Debug.errorOn()
                        && !uri.equals("SetTimeZoneFromBrowser")) { // Prevents to uselessly clutter the logs up with SetTimeZoneFromBrowser errors
                    String errorMessageHeader = "Request " + requestMap.getUri() + " caused an error with the following message: ";
                    if (request.getAttribute("_ERROR_MESSAGE_") != null) {
                        Debug.logError(errorMessageHeader + request.getAttribute("_ERROR_MESSAGE_"), MODULE);
                    }
                    if (request.getAttribute("_ERROR_MESSAGE_LIST_") != null) {
                        Debug.logError(errorMessageHeader + request.getAttribute("_ERROR_MESSAGE_LIST_"), MODULE);
                    }
                }
            }
        } else if (eventReturn != null) {
            // only log this warning if there is an eventReturn (ie skip if no event, etc)
            Debug.logWarning("Could not find response in request [" + requestMap.getUri() + "] for event return [" + eventReturn + "]", MODULE);
        }

        // Set the next view (don't use event return if success, default to nextView (which is set to eventReturn later if null); also even if
        // success if it is a type "none" response ignore the nextView, ie use the eventReturn)
        if (eventReturnBasedRequestResponse != null && (!"success".equals(eventReturnBasedRequestResponse.getName())
                || "none".equals(eventReturnBasedRequestResponse.getType()))) {
            nextRequestResponse = eventReturnBasedRequestResponse;
        }

        // get the previous request info
        String previousRequest = (String) request.getSession().getAttribute("_PREVIOUS_REQUEST_");
        String loginPass = (String) request.getAttribute("_LOGIN_PASSED_");

        // restore previous redirected request's attribute, so redirected page can display previous request's error msg etc.
        String preReqAttStr = (String) request.getSession().getAttribute("_REQ_ATTR_MAP_");
        if (preReqAttStr != null) {
            request.getSession().removeAttribute("_REQ_ATTR_MAP_");
            byte[] reqAttrMapBytes = StringUtil.fromHexString(preReqAttStr);
            Map<String, Object> preRequestMap = checkMap(UtilObject.getObject(reqAttrMapBytes), String.class, Object.class);
            if (UtilValidate.isNotEmpty(preRequestMap)) {
                for (Map.Entry<String, Object> entry : preRequestMap.entrySet()) {
                    String key = entry.getKey();
                    if ("_ERROR_MESSAGE_LIST_".equals(key) || "_ERROR_MESSAGE_MAP_".equals(key) || "_ERROR_MESSAGE_".equals(key)
                            || "_WARNING_MESSAGE_LIST_".equals(key) || "_WARNING_MESSAGE_".equals(key)
                            || "_EVENT_MESSAGE_LIST_".equals(key) || "_EVENT_MESSAGE_".equals(key) || "_UNSAFE_EVENT_MESSAGE_".equals(key)) {
                        request.setAttribute(key, entry.getValue());
                    }
                }
            }
        }

        if (Debug.verboseOn()) {
            Debug.logVerbose("[RequestHandler]: previousRequest - " + previousRequest + " (" + loginPass + ")" + showSessionId(request), MODULE);
        }

        // if previous request exists, and a login just succeeded, do that now.
        if (previousRequest != null && loginPass != null && "TRUE".equalsIgnoreCase(loginPass)) {
            request.getSession().removeAttribute("_PREVIOUS_REQUEST_");
            // special case to avoid login/logout looping: if request was "logout" before the login, change to null for default success view; do
            // the same for "login" to avoid going back to the same page
            if ("logout".equals(previousRequest) || "/logout".equals(previousRequest) || "login".equals(previousRequest)
                    || "/login".equals(previousRequest) || "checkLogin".equals(previousRequest) || "/checkLogin".equals(previousRequest)
                    || "/checkLogin/login".equals(previousRequest)) {
                Debug.logWarning("Found special _PREVIOUS_REQUEST_ of [" + previousRequest + "], setting to null to avoid problems, not running "
                        + "request again", MODULE);
            } else {
                if (Debug.infoOn()) {
                    Debug.logInfo("[Doing Previous Request]: " + previousRequest + showSessionId(request), MODULE);
                }

                // note that the previous form parameters are not setup (only the URL ones here), they will be found in the session later and
                // handled when the old request redirect comes back
                Map<String, Object> previousParamMap = UtilGenerics.checkMap(request.getSession().getAttribute("_PREVIOUS_PARAM_MAP_URL_"),
                        String.class, Object.class);
                String queryString = UtilHttp.urlEncodeArgs(previousParamMap, false);
                String redirectTarget = previousRequest;
                if (UtilValidate.isNotEmpty(queryString)) {
                    redirectTarget += "?" + queryString;
                }
                String link = makeLink(request, response, redirectTarget);

                // add / update csrf token to link when required
                String tokenValue = CsrfUtil.generateTokenForNonAjax(request, redirectTarget);
                link = CsrfUtil.addOrUpdateTokenInUrl(link, tokenValue);

                callRedirect(link, response, request, ccfg.getStatusCode());
                return;
            }
        }

        ConfigXMLReader.RequestResponse successResponse = requestMap.getRequestResponseMap().get("success");
        if ((eventReturn == null || "success".equals(eventReturn)) && successResponse != null && "request".equals(successResponse.getType())) {
            // chains will override any url defined views; but we will save the view for the very end
            if (UtilValidate.isNotEmpty(overrideViewUri)) {
                request.setAttribute("_POST_CHAIN_VIEW_", overrideViewUri);
            }
            nextRequestResponse = successResponse;
        }

        // Make sure we have some sort of response to go to
        if (nextRequestResponse == null) nextRequestResponse = successResponse;

        if (nextRequestResponse == null) {
            throw new RequestHandlerException("Illegal response; handler could not process request [" + requestMap.getUri() + "] and event return ["
                    + eventReturn + "].");
        }

        // before follow, analyze if a have a specific event message to return on the request.
        setUserMessageResponseToRequest(request, nextRequestResponse);

        if (Debug.verboseOn()) {
            Debug.logVerbose("[Event Response Selected]  type=" + nextRequestResponse.getType() + ", value=" + nextRequestResponse.getValue()
                    + ". " + showSessionId(request), MODULE);
        }

        // ========== Handle the responses - chains/views ==========

        // if the request has the save-last-view attribute set, save it now before the view can be rendered or other chain done so that the _LAST*
        // session attributes will represent the previous request
        if (nextRequestResponse.isSaveLastView()) {
            // Debug.logInfo("======save last view: " + session.getAttribute("_LAST_VIEW_NAME_"));
            String lastViewName = (String) session.getAttribute("_LAST_VIEW_NAME_");
            // Do not save the view if the last view is the same as the current view and saveCurrentView is false
            if (!(!nextRequestResponse.isSaveCurrentView() && "view".equals(nextRequestResponse.getType()) && nextRequestResponse.getValue()
                    .equals(lastViewName))) {
                session.setAttribute("_SAVED_VIEW_NAME_", session.getAttribute("_LAST_VIEW_NAME_"));
                session.setAttribute("_SAVED_VIEW_PARAMS_", session.getAttribute("_LAST_VIEW_PARAMS_"));
            }
        }
        String saveName = null;
        if (nextRequestResponse.isSaveCurrentView()) {
            saveName = "SAVED";
        }
        if (nextRequestResponse.isSaveHomeView()) {
            saveName = "HOME";
        }

        if ("request".equals(nextRequestResponse.getType())) {
            // chained request
            Debug.logInfo("[RequestHandler.doRequest]: Response is a chained request." + showSessionId(request), MODULE);
            doRequest(request, response, nextRequestResponse.getValue(), userLogin, delegator);
        } else {
            // ======== handle views ========

            // first invoke the post-processor events.
            for (ConfigXMLReader.Event event : ccfg.getPostprocessorEventList().values()) {
                try {
                    String returnString = this.runEvent(request, response, event, requestMap, "postprocessor");
                    if (returnString != null && !"success".equalsIgnoreCase(returnString)) {
                        throw new EventHandlerException("Post-Processor event did not return 'success'.");
                    }
                } catch (EventHandlerException e) {
                    Debug.logError(e, MODULE);
                }
            }

            // The status code used to redirect the HTTP client.
            String redirectSC = UtilValidate.isNotEmpty(nextRequestResponse.getStatusCode())
                    ? nextRequestResponse.getStatusCode() : ccfg.getStatusCode();

            if ("url".equals(nextRequestResponse.getType())) {
                if (Debug.verboseOn()) {
                    Debug.logVerbose("[RequestHandler.doRequest]: Response is a URL redirect." + showSessionId(request), MODULE);
                }
                callRedirect(nextRequestResponse.getValue(), response, request, redirectSC);
            } else if ("url-redirect".equals(nextRequestResponse.getType())) {
                // check for a cross-application redirect
                if (Debug.verboseOn()) {
                    Debug.logVerbose("[RequestHandler.doRequest]: Response is a URL redirect with redirect parameters."
                            + showSessionId(request), MODULE);
                }
                callRedirect(nextRequestResponse.getValue() + this.makeQueryString(request, nextRequestResponse), response,
                        request, redirectSC);
            } else if ("cross-redirect".equals(nextRequestResponse.getType())) {
                // check for a cross-application redirect
                if (Debug.verboseOn()) {
                    Debug.logVerbose("[RequestHandler.doRequest]: Response is a Cross-Application redirect." + showSessionId(request), MODULE);
                }
                String url = nextRequestResponse.getValue().startsWith("/") ? nextRequestResponse.getValue() : "/" + nextRequestResponse.getValue();
                callRedirect(url + this.makeQueryString(request, nextRequestResponse), response, request, redirectSC);
            } else if ("shortener".equals(nextRequestResponse.getType())) {
                // check for a shortener
                if (Debug.verboseOn()) {
                    Debug.logVerbose("[RequestHandler.doRequest]: Response is a shortener redirect." + showSessionId(request), MODULE);
                }
                String url = null;
                try {
                    url = OfbizPathShortener.restoreOriginalPath(delegator, (String) request.getAttribute("shortener"));
                } catch (GenericEntityException e) {
                    throw new RuntimeException(e);
                }
                callRedirect(url, response, request, redirectSC);
            } else if ("request-redirect".equals(nextRequestResponse.getType())) {
                if (Debug.verboseOn()) {
                    Debug.logVerbose("[RequestHandler.doRequest]: Response is a Request redirect." + showSessionId(request), MODULE);
                }
                String link = makeLinkWithQueryString(request, response, "/" + nextRequestResponse.getValue(), nextRequestResponse);

                // add / update csrf token to link when required
                String tokenValue = CsrfUtil.generateTokenForNonAjax(request, nextRequestResponse.getValue());
                link = CsrfUtil.addOrUpdateTokenInUrl(link, tokenValue);

                callRedirect(link, response, request, redirectSC);
            } else if ("request-redirect-noparam".equals(nextRequestResponse.getType())) {
                if (Debug.verboseOn()) {
                    Debug.logVerbose("[RequestHandler.doRequest]: Response is a Request redirect with no parameters." + showSessionId(request),
                            MODULE);
                }
                String link = makeLink(request, response, nextRequestResponse.getValue());

                // add token to link when required
                String tokenValue = CsrfUtil.generateTokenForNonAjax(request, nextRequestResponse.getValue());
                link = CsrfUtil.addOrUpdateTokenInUrl(link, tokenValue);

                callRedirect(link, response, request, redirectSC);
            } else if ("view".equals(nextRequestResponse.getType())) {
                if (Debug.verboseOn()) {
                    Debug.logVerbose("[RequestHandler.doRequest]: Response is a view." + showSessionId(request), MODULE);
                }

                // check for an override view, only used if "success" = eventReturn
                String viewName = (UtilValidate.isNotEmpty(overrideViewUri) && (eventReturn == null || "success".equals(eventReturn)))
                        ? overrideViewUri : nextRequestResponse.getValue();
                renderView(viewName, requestMap.isSecurityExternalView(), request, response, saveName);
            } else if ("view-last".equals(nextRequestResponse.getType())) {
                if (Debug.verboseOn()) {
                    Debug.logVerbose("[RequestHandler.doRequest]: Response is a view." + showSessionId(request), MODULE);
                }

                // check for an override view, only used if "success" = eventReturn
                String viewName = (UtilValidate.isNotEmpty(overrideViewUri) && (eventReturn == null || "success".equals(eventReturn)))
                        ? overrideViewUri : nextRequestResponse.getValue();

                // as a further override, look for the _SAVED and then _HOME and then _LAST session attributes
                Map<String, Object> urlParams = null;
                if (session.getAttribute("_SAVED_VIEW_NAME_") != null) {
                    viewName = (String) session.getAttribute("_SAVED_VIEW_NAME_");
                    urlParams = UtilGenerics.cast(session.getAttribute("_SAVED_VIEW_PARAMS_"));
                } else if (session.getAttribute("_HOME_VIEW_NAME_") != null) {
                    viewName = (String) session.getAttribute("_HOME_VIEW_NAME_");
                    urlParams = UtilGenerics.cast(session.getAttribute("_HOME_VIEW_PARAMS_"));
                } else if (session.getAttribute("_LAST_VIEW_NAME_") != null) {
                    viewName = (String) session.getAttribute("_LAST_VIEW_NAME_");
                    urlParams = UtilGenerics.cast(session.getAttribute("_LAST_VIEW_PARAMS_"));
                } else if (UtilValidate.isNotEmpty(nextRequestResponse.getValue())) {
                    viewName = nextRequestResponse.getValue();
                }
                if (UtilValidate.isEmpty(viewName) && UtilValidate.isNotEmpty(nextRequestResponse.getValue())) {
                    viewName = nextRequestResponse.getValue();
                }
                if (urlParams != null) {
                    for (Map.Entry<String, Object> urlParamEntry : urlParams.entrySet()) {
                        String key = urlParamEntry.getKey();
                        // Don't overwrite messages coming from the current event
                        if (!("_EVENT_MESSAGE_".equals(key) || "_ERROR_MESSAGE_".equals(key)
                                || "_EVENT_MESSAGE_LIST_".equals(key) || "_ERROR_MESSAGE_LIST_".equals(key))) {
                            request.setAttribute(key, urlParamEntry.getValue());
                        }
                    }
                }
                renderView(viewName, requestMap.isSecurityExternalView(), request, response, null);
            } else if ("view-last-noparam".equals(nextRequestResponse.getType())) {
                if (Debug.verboseOn()) {
                    Debug.logVerbose("[RequestHandler.doRequest]: Response is a view." + showSessionId(request), MODULE);
                }

                // check for an override view, only used if "success" = eventReturn
                String viewName = (UtilValidate.isNotEmpty(overrideViewUri) && (eventReturn == null || "success".equals(eventReturn)))
                        ? overrideViewUri : nextRequestResponse.getValue();

                // as a further override, look for the _SAVED and then _HOME and then _LAST session attributes
                if (session.getAttribute("_SAVED_VIEW_NAME_") != null) {
                    viewName = (String) session.getAttribute("_SAVED_VIEW_NAME_");
                } else if (session.getAttribute("_HOME_VIEW_NAME_") != null) {
                    viewName = (String) session.getAttribute("_HOME_VIEW_NAME_");
                } else if (session.getAttribute("_LAST_VIEW_NAME_") != null) {
                    viewName = (String) session.getAttribute("_LAST_VIEW_NAME_");
                } else if (UtilValidate.isNotEmpty(nextRequestResponse.getValue())) {
                    viewName = nextRequestResponse.getValue();
                }
                renderView(viewName, requestMap.isSecurityExternalView(), request, response, null);
            } else if ("view-home".equals(nextRequestResponse.getType())) {
                if (Debug.verboseOn()) {
                    Debug.logVerbose("[RequestHandler.doRequest]: Response is a view." + showSessionId(request), MODULE);
                }

                // check for an override view, only used if "success" = eventReturn
                String viewName = (UtilValidate.isNotEmpty(overrideViewUri) && (eventReturn == null || "success".equals(eventReturn)))
                        ? overrideViewUri : nextRequestResponse.getValue();

                // as a further override, look for the _HOME session attributes
                Map<String, Object> urlParams = null;
                if (session.getAttribute("_HOME_VIEW_NAME_") != null) {
                    viewName = (String) session.getAttribute("_HOME_VIEW_NAME_");
                    urlParams = UtilGenerics.cast(session.getAttribute("_HOME_VIEW_PARAMS_"));
                }
                if (urlParams != null) {
                    for (Map.Entry<String, Object> urlParamEntry : urlParams.entrySet()) {
                        request.setAttribute(urlParamEntry.getKey(), urlParamEntry.getValue());
                    }
                }
                renderView(viewName, requestMap.isSecurityExternalView(), request, response, null);
            } else if ("none".equals(nextRequestResponse.getType())) {
                // no view to render (meaning the return was processed by the event)
                if (Debug.verboseOn()) {
                    Debug.logVerbose("[RequestHandler.doRequest]: Response is handled by the event." + showSessionId(request), MODULE);
                }
            }
        }
        if (originalRequestMap.getMetrics() != null) {
            originalRequestMap.getMetrics().recordServiceRate(1, System.currentTimeMillis() - startTime);
        }
    }