NIHVIVO-160 Create a request wrapper that will parse the uploaded files without losing track of the request parameters.

This commit is contained in:
jeb228 2010-05-27 18:36:04 +00:00
parent 2f46ad2986
commit b565e92b00
3 changed files with 553 additions and 0 deletions

View file

@ -0,0 +1,332 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.filestorage.uploadrequest;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.Principal;
import java.util.Enumeration;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletInputStream;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
/**
* <p>
* Wraps an HTTP request and parses it for file uploads, without losing the
* request parameters.
* </p>
* <p>
* Most methods are declared here, and simply delegate to the wrapped request.
* Methods that have to do with parameters or files are handled differently for
* simple requests and multipart request, and are implemented in the
* sub-classes.
* </p>
*/
public abstract class FileUploadServletRequest implements HttpServletRequest {
// ----------------------------------------------------------------------
// The factory method
// ----------------------------------------------------------------------
/**
* Wrap this {@link HttpServletRequest} in an appropriate wrapper class.
*/
public static FileUploadServletRequest parseRequest(
HttpServletRequest request) throws IOException {
boolean isMultipart = ServletFileUpload.isMultipartContent(request);
if (isMultipart) {
return new MultipartHttpServletRequest(request);
} else {
return new SimpleHttpServletRequestWrapper(request);
}
}
// ----------------------------------------------------------------------
// The constructor and the delegate.
// ----------------------------------------------------------------------
private final HttpServletRequest delegate;
public FileUploadServletRequest(HttpServletRequest delegate) {
this.delegate = delegate;
}
protected HttpServletRequest getDelegate() {
return this.delegate;
}
// ----------------------------------------------------------------------
// New functionality to be implemented by the subclasses.
// ----------------------------------------------------------------------
public abstract boolean isMultipart();
public abstract Map<String, List<FileItem>> getFiles();
// ----------------------------------------------------------------------
// Delegated methods.
// ----------------------------------------------------------------------
@Override
public String getAuthType() {
return delegate.getAuthType();
}
@Override
public String getContextPath() {
return delegate.getContextPath();
}
@Override
public Cookie[] getCookies() {
return delegate.getCookies();
}
@Override
public long getDateHeader(String name) {
return delegate.getDateHeader(name);
}
@Override
public String getHeader(String name) {
return delegate.getHeader(name);
}
@Override
public Enumeration<?> getHeaderNames() {
return delegate.getHeaderNames();
}
@Override
public Enumeration<?> getHeaders(String name) {
return delegate.getHeaders(name);
}
@Override
public int getIntHeader(String name) {
return delegate.getIntHeader(name);
}
@Override
public String getMethod() {
return delegate.getMethod();
}
@Override
public String getPathInfo() {
return delegate.getPathInfo();
}
@Override
public String getPathTranslated() {
return delegate.getPathTranslated();
}
@Override
public String getQueryString() {
return delegate.getQueryString();
}
@Override
public String getRemoteUser() {
return delegate.getRemoteUser();
}
@Override
public String getRequestURI() {
return delegate.getRequestURI();
}
@Override
public StringBuffer getRequestURL() {
return delegate.getRequestURL();
}
@Override
public String getRequestedSessionId() {
return delegate.getRequestedSessionId();
}
@Override
public String getServletPath() {
return delegate.getServletPath();
}
@Override
public HttpSession getSession() {
return delegate.getSession();
}
@Override
public HttpSession getSession(boolean create) {
return delegate.getSession(create);
}
@Override
public Principal getUserPrincipal() {
return delegate.getUserPrincipal();
}
@Override
public boolean isRequestedSessionIdFromCookie() {
return delegate.isRequestedSessionIdFromCookie();
}
@Override
public boolean isRequestedSessionIdFromURL() {
return delegate.isRequestedSessionIdFromURL();
}
@SuppressWarnings("deprecation")
@Override
public boolean isRequestedSessionIdFromUrl() {
return delegate.isRequestedSessionIdFromUrl();
}
@Override
public boolean isRequestedSessionIdValid() {
return delegate.isRequestedSessionIdValid();
}
@Override
public boolean isUserInRole(String role) {
return delegate.isUserInRole(role);
}
@Override
public Object getAttribute(String name) {
return delegate.getAttribute(name);
}
@Override
public Enumeration<?> getAttributeNames() {
return delegate.getAttributeNames();
}
@Override
public String getCharacterEncoding() {
return delegate.getCharacterEncoding();
}
@Override
public int getContentLength() {
return delegate.getContentLength();
}
@Override
public String getContentType() {
return delegate.getContentType();
}
@Override
public ServletInputStream getInputStream() throws IOException {
return delegate.getInputStream();
}
@Override
public String getLocalAddr() {
return delegate.getLocalAddr();
}
@Override
public String getLocalName() {
return delegate.getLocalName();
}
@Override
public int getLocalPort() {
return delegate.getLocalPort();
}
@Override
public Locale getLocale() {
return delegate.getLocale();
}
@Override
public Enumeration<?> getLocales() {
return delegate.getLocales();
}
@Override
public String getProtocol() {
return delegate.getProtocol();
}
@Override
public BufferedReader getReader() throws IOException {
return delegate.getReader();
}
@SuppressWarnings("deprecation")
@Override
public String getRealPath(String path) {
return delegate.getRealPath(path);
}
@Override
public String getRemoteAddr() {
return delegate.getRemoteAddr();
}
@Override
public String getRemoteHost() {
return delegate.getRemoteHost();
}
@Override
public int getRemotePort() {
return delegate.getRemotePort();
}
@Override
public RequestDispatcher getRequestDispatcher(String path) {
return delegate.getRequestDispatcher(path);
}
@Override
public String getScheme() {
return delegate.getScheme();
}
@Override
public String getServerName() {
return delegate.getServerName();
}
@Override
public int getServerPort() {
return delegate.getServerPort();
}
@Override
public boolean isSecure() {
return delegate.isSecure();
}
@Override
public void removeAttribute(String name) {
delegate.removeAttribute(name);
}
@Override
public void setAttribute(String name, Object o) {
delegate.setAttribute(name, o);
}
@Override
public void setCharacterEncoding(String env)
throws UnsupportedEncodingException {
delegate.setCharacterEncoding(env);
}
}

View file

@ -0,0 +1,157 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.filestorage.uploadrequest;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileItemFactory;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.log4j.Logger;
/**
* A wrapper for a servlet request that holds multipart content. Parsing the
* request will consume the parameters, so we need to hold them here to answer
* any parameter-related requests. File-related information will also be held
* here, to answer file-related requests.
*/
class MultipartHttpServletRequest extends FileUploadServletRequest {
private static final Logger LOG = Logger
.getLogger(MultipartHttpServletRequest.class);
private static final String[] EMPTY_ARRAY = new String[0];
private final Map<String, List<String>> parameters;
private final Map<String, List<FileItem>> files;
/**
* Parse the multipart request. Store the info about the request parameters
* and the uploaded files.
*/
public MultipartHttpServletRequest(HttpServletRequest request)
throws IOException {
super(request);
Map<String, List<String>> parameters = new HashMap<String, List<String>>();
Map<String, List<FileItem>> files = new HashMap<String, List<FileItem>>();
try {
FileItemFactory factory = new DiskFileItemFactory();
ServletFileUpload upload = new ServletFileUpload(factory);
List<FileItem> items = parseRequestIntoFileItems(request, upload);
for (FileItem item : items) {
// Process a regular form field
if (item.isFormField()) {
addToParameters(parameters, item.getFieldName(), item
.getString());
LOG.debug("Form field (parameter) " + item.getFieldName()
+ "=" + item.getString());
} else {
addToFileItems(files, item);
LOG.debug("File " + item.getFieldName() + ": "
+ item.getName());
}
}
} catch (FileUploadException e) {
String message = "Failed to parse a multipart-content request.";
LOG.error(message, e);
throw new IOException(message, e);
}
this.parameters = Collections.unmodifiableMap(parameters);
LOG.debug("Parameters are: " + this.parameters);
this.files = Collections.unmodifiableMap(files);
LOG.debug("Files are: " + this.files);
}
/** Either create a new List for the value, or add to an existing List. */
private void addToParameters(Map<String, List<String>> map, String name,
String value) {
if (!map.containsKey(name)) {
map.put(name, new ArrayList<String>());
}
map.get(name).add(value);
}
/** Either create a new List for the file, or add to an existing List. */
private void addToFileItems(Map<String, List<FileItem>> map,
FileItem file) {
String name = file.getFieldName();
if (!map.containsKey(name)) {
map.put(name, new ArrayList<FileItem>());
}
map.get(name).add(file);
}
/** Minimize the code that uses the unchecked cast. */
@SuppressWarnings("unchecked")
private List<FileItem> parseRequestIntoFileItems(HttpServletRequest req,
ServletFileUpload upload) throws FileUploadException {
return upload.parseRequest(req);
}
// ----------------------------------------------------------------------
// This is a multipart request, so make the file info available.
// ----------------------------------------------------------------------
@Override
public boolean isMultipart() {
return true;
}
@Override
public Map<String, List<FileItem>> getFiles() {
return files;
}
// ----------------------------------------------------------------------
// Parameter-related methods won't find anything on the delegate request,
// since parsing consumed the parameters. So we need to look to the parsed
// info for the answers.
// ----------------------------------------------------------------------
@Override
public String getParameter(String name) {
if (parameters.containsKey(name)) {
return parameters.get(name).get(0);
} else {
return null;
}
}
@Override
public Enumeration<?> getParameterNames() {
return Collections.enumeration(parameters.keySet());
}
@Override
public String[] getParameterValues(String name) {
if (parameters.containsKey(name)) {
return parameters.get(name).toArray(EMPTY_ARRAY);
} else {
return null;
}
}
@Override
public Map<?, ?> getParameterMap() {
Map<String, String[]> result = new HashMap<String, String[]>();
for (Entry<String, List<String>> entry : parameters.entrySet()) {
result.put(entry.getKey(), entry.getValue().toArray(EMPTY_ARRAY));
}
LOG.debug("resulting parameter map: " + result);
return result;
}
}

View file

@ -0,0 +1,64 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.filestorage.uploadrequest;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.fileupload.FileItem;
/**
* A wrapper for a servlet request that does not hold multipart content. Pass
* all parameter-related requests to the delegate, and give simple answers to
* all file-related requests.
*/
class SimpleHttpServletRequestWrapper extends FileUploadServletRequest {
SimpleHttpServletRequestWrapper(HttpServletRequest request) {
super(request);
}
// ----------------------------------------------------------------------
// Not a multipart request, so there are no files.
// ----------------------------------------------------------------------
@Override
public boolean isMultipart() {
return false;
}
@Override
public Map<String, List<FileItem>> getFiles() {
return Collections.emptyMap();
}
// ----------------------------------------------------------------------
// Since this is not a multipart request, the parameter methods can be
// delegated.
// ----------------------------------------------------------------------
@Override
public String getParameter(String name) {
return getDelegate().getParameter(name);
}
@Override
public Map<?, ?> getParameterMap() {
return getDelegate().getParameterMap();
}
@Override
public Enumeration<?> getParameterNames() {
return getDelegate().getParameterNames();
}
@Override
public String[] getParameterValues(String name) {
return getDelegate().getParameterValues(name);
}
}