/** Execute the Web Script encapsulated by this Web Script Runtime */
  public final void executeScript() {
    final boolean debug = logger.isDebugEnabled();
    long startRuntime = 0L;
    if (debug) startRuntime = System.nanoTime();

    final String method = getScriptMethod();
    String scriptUrl = null;
    Match match = null;

    try {
      // extract script url
      scriptUrl = getScriptUrl();
      if (scriptUrl == null || scriptUrl.length() == 0) {
        throw new WebScriptException(
            HttpServletResponse.SC_BAD_REQUEST, "Script URL not specified");
      }

      if (debug)
        logger.debug(
            "(Runtime="
                + getName()
                + ", Container="
                + container.getName()
                + ") Processing script url ("
                + method
                + ") "
                + scriptUrl);

      WebScriptRequest scriptReq = null;
      WebScriptResponse scriptRes = null;
      Authenticator auth = null;

      RequiredAuthentication containerRequiredAuth = container.getRequiredAuthentication();

      if (!containerRequiredAuth.equals(RequiredAuthentication.none)) {
        // Create initial request & response
        scriptReq = createRequest(null);
        scriptRes = createResponse();
        auth = createAuthenticator();

        if (debug)
          logger.debug(
              "(Runtime="
                  + getName()
                  + ", Container="
                  + container.getName()
                  + ") Container requires pre-auth: "
                  + containerRequiredAuth);

        boolean preAuth = true;

        if (auth != null && auth.emptyCredentials()) {
          // check default (unauthenticated) domain
          match = container.getRegistry().findWebScript(method, scriptUrl);
          if ((match != null)
              && (match
                  .getWebScript()
                  .getDescription()
                  .getRequiredAuthentication()
                  .equals(RequiredAuthentication.none))) {
            preAuth = false;
          }
        }

        if (preAuth && (!container.authenticate(auth, containerRequiredAuth))) {
          return; // return response (eg. prompt for un/pw if status is 401 or redirect)
        }
      }

      if (match == null) {
        match = container.getRegistry().findWebScript(method, scriptUrl);
      }

      if (match == null || match.getKind() == Match.Kind.URI) {
        if (match == null) {
          String msg = "Script url " + scriptUrl + " does not map to a Web Script.";
          if (debug) logger.debug(msg);
          throw new WebScriptException(HttpServletResponse.SC_NOT_FOUND, msg);
        } else {
          String msg = "Script url " + scriptUrl + " does not support the method " + method;
          if (debug) logger.debug(msg);
          throw new WebScriptException(HttpServletResponse.SC_METHOD_NOT_ALLOWED, msg);
        }
      }

      // create web script request & response
      scriptReq = createRequest(match);
      scriptRes = createResponse();

      if (auth == null) {
        // not pre-authenticated
        auth = createAuthenticator();
      }

      if (debug) logger.debug("Agent: " + scriptReq.getAgent());

      long startScript = System.nanoTime();
      final WebScript script = match.getWebScript();
      final Description description = script.getDescription();

      try {
        if (debug) {
          String reqFormat = scriptReq.getFormat();
          String format =
              (reqFormat == null || reqFormat.length() == 0) ? "[undefined]" : reqFormat;
          Description desc = scriptReq.getServiceMatch().getWebScript().getDescription();
          logger.debug(
              "Invoking Web Script "
                  + description.getId()
                  + " (format "
                  + format
                  + ", style: "
                  + desc.getFormatStyle()
                  + ", default: "
                  + desc.getDefaultFormat()
                  + ")");
        }

        executeScript(scriptReq, scriptRes, auth);
      } finally {
        if (debug) {
          long endScript = System.nanoTime();
          logger.debug(
              "Web Script "
                  + description.getId()
                  + " executed in "
                  + (endScript - startScript) / 1000000f
                  + "ms");
        }
      }
    } catch (Throwable e) {
      if (beforeProcessError(match, e)) {
        if (e instanceof WebScriptException
            && (((WebScriptException) e).getStatus() == HttpServletResponse.SC_NOT_FOUND
                || ((WebScriptException) e).getStatus() == HttpServletResponse.SC_UNAUTHORIZED)) {
          // debug level output for "missing" WebScripts and API URLs entered incorrectly
          String errorCode =
              ((WebScriptException) e).getStatus() == HttpServletResponse.SC_NOT_FOUND
                  ? "NOT FOUND"
                  : "UNAUTHORIZED";
          logger.debug("Webscript did not execute. (" + errorCode + "): " + e.getMessage());
        }
        // log error on server so its not swallowed and lost
        else if (logger.isErrorEnabled()) {
          logger.error(
              "Exception from executeScript - redirecting to status template error: "
                  + e.getMessage(),
              e);
        }

        // setup context
        WebScriptRequest req = createRequest(match);
        WebScriptResponse res = createResponse();
        String format = req.getFormat();

        // extract status code, if specified
        int statusCode = HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
        StatusTemplate statusTemplate = null;
        Map<String, Object> statusModel = null;
        if (e instanceof WebScriptException) {
          WebScriptException we = (WebScriptException) e;
          statusCode = we.getStatus();
          statusTemplate = we.getStatusTemplate();
          statusModel = we.getStatusModel();
        }

        // retrieve status template for response rendering
        if (statusTemplate == null) {
          // locate status template
          // NOTE: search order...
          //   1) root located <status>.ftl
          //   2) root located <format>.status.ftl
          //   3) root located status.ftl
          statusTemplate = getStatusCodeTemplate(statusCode);

          String validTemplatePath =
              container
                  .getTemplateProcessorRegistry()
                  .findValidTemplatePath(statusTemplate.getPath());
          if (validTemplatePath == null) {
            if (format != null && format.length() > 0) {
              // if we have a format try and get the format specific status template
              statusTemplate = getFormatStatusTemplate(format);
              validTemplatePath =
                  container
                      .getTemplateProcessorRegistry()
                      .findValidTemplatePath(statusTemplate.getPath());
            }

            // if we don't have a valid template path get the default status template
            if (validTemplatePath == null) {
              statusTemplate = getStatusTemplate();
              validTemplatePath =
                  container
                      .getTemplateProcessorRegistry()
                      .findValidTemplatePath(statusTemplate.getPath());
            }

            // throw error if a status template could not be found
            if (validTemplatePath == null) {
              throw new WebScriptException(
                  "Failed to find status template "
                      + statusTemplate.getPath()
                      + " (format: "
                      + statusTemplate.getFormat()
                      + ")");
            }
          }
        }

        // create basic model for all information known at this point, if one hasn't been
        // pre-provided
        if (statusModel == null || statusModel.equals(Collections.EMPTY_MAP)) {
          statusModel = new HashMap<String, Object>(8, 1.0f);
          statusModel.putAll(container.getTemplateParameters());
          statusModel.put("url", createURLModel(req));
          if (match != null && match.getWebScript() != null) {
            statusModel.put("webscript", match.getWebScript().getDescription());
          }
        }

        // add status to model
        Status status = new Status();
        status.setCode(statusCode);
        status.setMessage(e.getMessage() != null ? e.getMessage() : e.toString());
        if (exceptionLogger.isDebugEnabled()) {
          status.setException(e);
        }
        statusModel.put("status", status);

        // render output
        String mimetype =
            container.getFormatRegistry().getMimeType(req.getAgent(), statusTemplate.getFormat());
        if (mimetype == null) {
          throw new WebScriptException(
              "Web Script format '" + statusTemplate.getFormat() + "' is not registered");
        }

        if (debug) {
          logger.debug("Force success status header in response: " + req.forceSuccessStatus());
          logger.debug(
              "Sending status " + statusCode + " (Template: " + statusTemplate.getPath() + ")");
          logger.debug("Rendering response: content type=" + mimetype);
        }

        Cache cache = new Cache();
        cache.setNeverCache(true);
        res.setCache(cache);
        res.setStatus(req.forceSuccessStatus() ? HttpServletResponse.SC_OK : statusCode);
        res.setContentType(mimetype + ";charset=UTF-8");
        try {
          String validTemplatePath =
              container
                  .getTemplateProcessorRegistry()
                  .findValidTemplatePath(statusTemplate.getPath());
          TemplateProcessor statusProcessor =
              container.getTemplateProcessorRegistry().getTemplateProcessor(validTemplatePath);
          statusProcessor.process(validTemplatePath, statusModel, res.getWriter());
        } catch (Exception e1) {
          logger.error("Internal error", e1);
          throw new WebScriptException("Internal error", e1);
        }
      }
    } finally {
      if (debug) {
        long endRuntime = System.nanoTime();
        logger.debug(
            "Processed script url ("
                + method
                + ") "
                + scriptUrl
                + " in "
                + (endRuntime - startRuntime) / 1000000f
                + "ms");
      }
    }
  }