public void upload(
      @Observes ControllerFound event,
      MutableRequest request,
      MultipartConfig config,
      Validator validator) {

    if (!ServletFileUpload.isMultipartContent(request)) {
      return;
    }

    logger.info("Request contains multipart data. Try to parse with commons-upload.");

    final Multiset<String> indexes = HashMultiset.create();
    final Multimap<String, String> params = LinkedListMultimap.create();

    ServletFileUpload uploader = createServletFileUpload(config);

    UploadSizeLimit uploadSizeLimit =
        event.getMethod().getMethod().getAnnotation(UploadSizeLimit.class);
    uploader.setSizeMax(
        uploadSizeLimit != null ? uploadSizeLimit.sizeLimit() : config.getSizeLimit());
    uploader.setFileSizeMax(
        uploadSizeLimit != null ? uploadSizeLimit.fileSizeLimit() : config.getFileSizeLimit());
    logger.debug(
        "Setting file sizes: total={}, file={}", uploader.getSizeMax(), uploader.getFileSizeMax());

    try {
      final List<FileItem> items = uploader.parseRequest(request);
      logger.debug(
          "Found {} attributes in the multipart form submission. Parsing them.", items.size());

      for (FileItem item : items) {
        String name = item.getFieldName();
        name = fixIndexedParameters(name, indexes);

        if (item.isFormField()) {
          logger.debug("{} is a field", name);
          params.put(name, getValue(item, request));

        } else if (isNotEmpty(item)) {
          logger.debug("{} is a file", name);
          processFile(item, name, request);

        } else {
          logger.debug("A file field is empty: {}", item.getFieldName());
        }
      }

      for (String paramName : params.keySet()) {
        Collection<String> paramValues = params.get(paramName);
        request.setParameter(paramName, paramValues.toArray(new String[paramValues.size()]));
      }

    } catch (final SizeLimitExceededException e) {
      reportSizeLimitExceeded(e, validator);

    } catch (FileUploadException e) {
      reportFileUploadException(e, validator);
    }
  }
  /**
   * if the filter is configured to parse file uploads, AND the request is multipart (typically a
   * file upload), then parse the request.
   *
   * @return If there is a file upload, and the filter handles it, return the wrapped request that
   *     has the results of the parsed file upload. Parses the files using Apache
   *     commons-fileuplaod. Exposes the results through a wrapped request. Files are available
   *     like: fileItem = (FileItem) request.getAttribute("myHtmlFileUploadId");
   */
  protected HttpServletRequest handleFileUpload(
      HttpServletRequest req, HttpServletResponse resp, List<FileItem> tempFiles)
      throws ServletException, UnsupportedEncodingException {
    if (!m_uploadEnabled
        || !ServletFileUpload.isMultipartContent(req)
        || req.getAttribute(ATTR_UPLOADS_DONE) != null) {
      return req;
    }

    // mark that the uploads have been parsed, so they aren't parsed again on this request
    req.setAttribute(ATTR_UPLOADS_DONE, ATTR_UPLOADS_DONE);

    // Result - map that will be created of request parameters,
    // parsed parameters, and uploaded files
    Map map = new HashMap();

    // parse using commons-fileupload

    // Create a factory for disk-based file items
    DiskFileItemFactory factory = new DiskFileItemFactory();

    // set the factory parameters: the temp dir and the keep-in-memory-if-smaller threshold
    if (m_uploadTempDir != null) factory.setRepository(new File(m_uploadTempDir));
    if (m_uploadThreshold > 0) factory.setSizeThreshold(m_uploadThreshold);

    // Create a new file upload handler
    ServletFileUpload upload = new ServletFileUpload(factory);

    // set the encoding
    String encoding = req.getCharacterEncoding();
    if (encoding != null && encoding.length() > 0) upload.setHeaderEncoding(encoding);

    // set the max upload size
    long uploadMax = -1;
    if (m_uploadMaxSize > 0) uploadMax = m_uploadMaxSize;

    // check for request-scoped override to upload.max (value in megs)
    String override = req.getParameter(CONFIG_UPLOAD_MAX);
    if (override != null) {
      try {
        // get the max in bytes
        uploadMax = Long.parseLong(override) * 1024L * 1024L;
      } catch (NumberFormatException e) {
        M_log.warn(CONFIG_UPLOAD_MAX + " set to non-numeric: " + override);
      }
    }

    // limit to the ceiling
    if (uploadMax > m_uploadCeiling) {
      M_log.warn(
          "Upload size exceeds ceiling: "
              + ((uploadMax / 1024L) / 1024L)
              + " > "
              + ((m_uploadCeiling / 1024L) / 1024L)
              + " megs");

      uploadMax = m_uploadCeiling;
    }

    // to let commons-fileupload throw the exception on over-max, and also halt full processing of
    // input fields
    if (!m_uploadContinue) {
      // TODO: when we switch to commons-fileupload 1.2
      // // either per file or overall request, as configured
      // if (m_uploadMaxPerFile)
      // {
      // upload.setFileSizeMax(uploadMax);
      // }
      // else
      // {
      // upload.setSizeMax(uploadMax);
      // }

      upload.setSizeMax(uploadMax);
    }

    try {
      // parse multipart encoded parameters
      boolean uploadOk = true;
      List list = upload.parseRequest(req);
      for (int i = 0; i < list.size(); i++) {
        FileItem item = (FileItem) list.get(i);

        if (item.isFormField()) {
          String str = item.getString(encoding);

          Object obj = map.get(item.getFieldName());
          if (obj == null) {
            map.put(item.getFieldName(), new String[] {str});
          } else if (obj instanceof String[]) {
            String[] old_vals = (String[]) obj;
            String[] values = new String[old_vals.length + 1];
            for (int i1 = 0; i1 < old_vals.length; i1++) {
              values[i1] = old_vals[i1];
            }
            values[values.length - 1] = str;
            map.put(item.getFieldName(), values);
          } else if (obj instanceof String) {
            String[] values = new String[2];
            values[0] = (String) obj;
            values[1] = str;
            map.put(item.getFieldName(), values);
          }
        } else {
          // collect it for delete at the end of the request
          tempFiles.add(item);

          // check the max, unless we are letting commons-fileupload throw exception on max exceeded
          // Note: the continue option assumes the max is per-file, not overall.
          if (m_uploadContinue && (item.getSize() > uploadMax)) {
            uploadOk = false;

            M_log.info("Upload size limit exceeded: " + ((uploadMax / 1024L) / 1024L));

            req.setAttribute("upload.status", "size_limit_exceeded");
            // TODO: for 1.2 commons-fileupload, switch this to a FileSizeLimitExceededException
            req.setAttribute(
                "upload.exception",
                new FileUploadBase.SizeLimitExceededException("", item.getSize(), uploadMax));
            req.setAttribute("upload.limit", new Long((uploadMax / 1024L) / 1024L));
          } else {
            req.setAttribute(item.getFieldName(), item);
          }
        }
      }

      // unless we had an upload file that exceeded max, set the upload status to "ok"
      if (uploadOk) {
        req.setAttribute("upload.status", "ok");
      }
    } catch (FileUploadBase.SizeLimitExceededException ex) {
      M_log.info("Upload size limit exceeded: " + ((upload.getSizeMax() / 1024L) / 1024L));

      // DON'T throw an exception, instead note the exception
      // so that the tool down-the-line can handle the problem
      req.setAttribute("upload.status", "size_limit_exceeded");
      req.setAttribute("upload.exception", ex);
      req.setAttribute("upload.limit", new Long((upload.getSizeMax() / 1024L) / 1024L));
    }
    // TODO: put in for commons-fileupload 1.2
    // catch (FileUploadBase.FileSizeLimitExceededException ex)
    // {
    // M_log.info("Upload size limit exceeded: " + ((upload.getFileSizeMax() / 1024L) / 1024L));
    //
    // // DON'T throw an exception, instead note the exception
    // // so that the tool down-the-line can handle the problem
    // req.setAttribute("upload.status", "size_limit_exceeded");
    // req.setAttribute("upload.exception", ex);
    // req.setAttribute("upload.limit", new Long((upload.getFileSizeMax() / 1024L) / 1024L));
    // }
    catch (FileUploadException ex) {
      M_log.info("Unexpected exception in upload parsing", ex);
      req.setAttribute("upload.status", "exception");
      req.setAttribute("upload.exception", ex);
    }

    // add any parameters that were in the URL - make sure to get multiples
    for (Enumeration e = req.getParameterNames(); e.hasMoreElements(); ) {
      String name = (String) e.nextElement();
      String[] values = req.getParameterValues(name);
      map.put(name, values);
    }

    // return a wrapped response that exposes the parsed parameters and files
    return new WrappedRequestFileUpload(req, map);
  }