Merge branch 'feature/parseMultipartRequest' into develop

This commit is contained in:
j2blake 2013-12-20 17:13:35 -05:00
commit b701201c7f
14 changed files with 489 additions and 597 deletions

View file

@ -45,7 +45,6 @@ import edu.cornell.mannlib.vitro.webapp.dao.IndividualDao;
import edu.cornell.mannlib.vitro.webapp.dao.ModelAccess;
import edu.cornell.mannlib.vitro.webapp.dao.VitroVocabulary;
import edu.cornell.mannlib.vitro.webapp.dao.WebappDaoFactory;
import edu.cornell.mannlib.vitro.webapp.filestorage.uploadrequest.FileUploadServletRequest;
import fedora.client.FedoraClient;
import fedora.common.Constants;
import fedora.server.management.FedoraAPIM;
@ -220,12 +219,22 @@ public class FedoraDatastreamController extends VitroHttpServlet implements Cons
}
@Override
public long maximumMultipartFileSize() {
return maxFileSize;
}
@Override
public boolean stashFileSizeException() {
return true;
}
@Override
public void doPost(HttpServletRequest rawRequest, HttpServletResponse res)
throws ServletException, IOException {
try{
FileUploadServletRequest req = FileUploadServletRequest.parseRequest(rawRequest, maxFileSize);
if (req.hasFileUploadException()) {
throw new FdcException("Size limit exceeded: " + req.getFileUploadException().getLocalizedMessage());
try{
VitroRequest req = new VitroRequest(rawRequest);
if (req.hasFileSizeException()) {
throw new FdcException("Size limit exceeded: " + req.getFileSizeException().getLocalizedMessage());
}
if (!req.isMultipart()) {
throw new FdcException("Must POST a multipart encoded request");

View file

@ -0,0 +1,288 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.controller;
import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.FileUploadBase.FileSizeLimitExceededException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Wraps a multipart HTTP request, and pre-parses it for file uploads, without
* losing the request parameters.
*
* Parsing through the request with an Apache ServletFileUpload builds a list of
* FileItems that includes the parameters and the file parts. After that,
* however, the parameters are no longer accessible from the request. This
* wrapper will see that they don't get lost.
*
* The List of FileItems includes both "formField" items and "file" items. As
* with the usual parameters on a request, we can have more than one value with
* the same name. So this creates a map of <String, List<String>> to hold the
* parameters, and a map of <String, List<FileItem>> to hold the files.
*
* The parameters will be available to the wrapper through the normal methods.
* The files will be available as an attribute that holds the map. Also, a
* separate attribute will hold a Boolean to indicate that this was indeed a
* multipart request.
*
* Conveninence methods in VitroRequest will make these easy to handle, without
* actually touching the attributes.
*/
public class MultipartRequestWrapper extends HttpServletRequestWrapper {
private static final Log log = LogFactory
.getLog(MultipartRequestWrapper.class);
private static final String CLASS_NAME = MultipartRequestWrapper.class
.getSimpleName();
public static final String ATTRIBUTE_IS_MULTIPART = CLASS_NAME
+ "_isMultipart";
public static final String ATTRIBUTE_FILE_ITEM_MAP = CLASS_NAME
+ "_fileItemMap";
public static final String ATTRIBUTE_FILE_SIZE_EXCEPTION = CLASS_NAME
+ "_fileSizeException";
private static final String[] EMPTY_ARRAY = new String[0];
// ----------------------------------------------------------------------
// The factory
// ----------------------------------------------------------------------
/**
* If this is a multipart request, wrap it. Otherwise, just return the
* request as it is.
*/
public static HttpServletRequest parse(HttpServletRequest req,
ParsingStrategy strategy) throws IOException {
if (!ServletFileUpload.isMultipartContent(req)) {
return req;
}
ListsMap<String> parameters = new ListsMap<>();
ListsMap<FileItem> files = new ListsMap<>();
parseQueryString(req.getQueryString(), parameters);
parseFileParts(req, parameters, files, strategy);
return new MultipartRequestWrapper(req, parameters, files);
}
/**
* Pull any parameters out of the URL.
*/
private static void parseQueryString(String queryString,
ListsMap<String> parameters) {
log.debug("Query string is : '" + queryString + "'");
if (queryString != null) {
String[] pieces = queryString.split("&");
for (String piece : pieces) {
int equalsHere = piece.indexOf('=');
if (piece.trim().isEmpty()) {
// Ignore an empty piece.
} else if (equalsHere <= 0) {
// A parameter without a value.
parameters.add(decode(piece), "");
} else {
// A parameter with a value.
String key = piece.substring(0, equalsHere);
String value = piece.substring(equalsHere + 1);
parameters.add(decode(key), decode(value));
}
}
}
log.debug("Parameters from query string are: " + parameters);
}
/**
* Remove any special URL-style encoding.
*/
private static String decode(String encoded) {
try {
return URLDecoder.decode(encoded, "UTF-8");
} catch (UnsupportedEncodingException e) {
log.error(e, e);
return encoded;
}
}
private static void parseFileParts(HttpServletRequest req,
ListsMap<String> parameters, ListsMap<FileItem> files,
ParsingStrategy strategy) throws IOException {
ServletFileUpload upload = createUploadHandler(req,
strategy.maximumMultipartFileSize());
List<FileItem> items = parseRequestIntoFileItems(req, upload, strategy);
for (FileItem item : items) {
// Process a regular form field
String name = item.getFieldName();
if (item.isFormField()) {
String value;
try {
value = item.getString("UTF-8");
} catch (UnsupportedEncodingException e) {
value = item.getString();
}
parameters.add(name, value);
log.debug("Form field (parameter) " + name + "=" + value);
} else {
files.add(name, item);
log.debug("File " + name + ": " + item.getSize() + " bytes.");
}
}
}
/**
* Create an upload handler that will throw an exception if the file is too
* large.
*/
private static ServletFileUpload createUploadHandler(
HttpServletRequest req, long maxFileSize) {
File tempDir = figureTemporaryDirectory(req);
DiskFileItemFactory factory = new DiskFileItemFactory();
factory.setSizeThreshold(DiskFileItemFactory.DEFAULT_SIZE_THRESHOLD);
factory.setRepository(tempDir);
ServletFileUpload upload = new ServletFileUpload(factory);
upload.setSizeMax(maxFileSize);
return upload;
}
/**
* Find the temporary storage directory for this webapp.
*/
private static File figureTemporaryDirectory(HttpServletRequest request) {
return (File) request.getSession().getServletContext()
.getAttribute("javax.servlet.context.tempdir");
}
/**
* Parse the raw request into a list of parts.
*
* If there is a parsing error, let the strategy handle it. If the strategy
* throws it back, wrap it in an IOException and throw it on up.
*/
@SuppressWarnings("unchecked")
private static List<FileItem> parseRequestIntoFileItems(
HttpServletRequest req, ServletFileUpload upload,
ParsingStrategy strategy) throws IOException {
try {
return upload.parseRequest(req);
} catch (FileSizeLimitExceededException e) {
if (strategy.stashFileSizeException()) {
req.setAttribute(ATTRIBUTE_FILE_SIZE_EXCEPTION, e);
return Collections.emptyList();
} else {
throw new IOException(e);
}
} catch (FileUploadException e) {
throw new IOException(e);
}
}
// ----------------------------------------------------------------------
// The instance
// ----------------------------------------------------------------------
private final ListsMap<String> parameters;
public MultipartRequestWrapper(HttpServletRequest req,
ListsMap<String> parameters, ListsMap<FileItem> files) {
super(req);
this.parameters = parameters;
req.setAttribute(ATTRIBUTE_IS_MULTIPART, Boolean.TRUE);
req.setAttribute(ATTRIBUTE_FILE_ITEM_MAP, files);
}
/**
* Look in the map of parsed parameters.
*/
@Override
public String getParameter(String name) {
if (parameters.containsKey(name)) {
return parameters.get(name).get(0);
} else {
return null;
}
}
/**
* Look in the map of parsed parameters. Make a protective copy.
*/
@Override
public Enumeration<?> getParameterNames() {
return Collections.enumeration(new HashSet<>(parameters.keySet()));
}
/**
* Look in the map of parsed parameters.
*/
@Override
public String[] getParameterValues(String name) {
if (parameters.containsKey(name)) {
return parameters.get(name).toArray(EMPTY_ARRAY);
} else {
return null;
}
}
/**
* Make a copy of the map of parsed parameters;
*/
@Override
public Map<String, String[]> getParameterMap() {
Map<String, String[]> result = new HashMap<String, String[]>();
for (String key : parameters.keySet()) {
result.put(key, parameters.get(key).toArray(EMPTY_ARRAY));
}
return result;
}
// ----------------------------------------------------------------------
// Helper classes
// ----------------------------------------------------------------------
private static class ListsMap<T> extends HashMap<String, List<T>> {
void add(String key, T value) {
if (!containsKey(key)) {
put(key, new ArrayList<T>());
}
get(key).add(value);
}
}
public interface ParsingStrategy {
long maximumMultipartFileSize();
/**
* Allows you to handle the exception in your code.
*
* Be aware that the multipart parameters have been lost, and that may
* include form fields.
*/
boolean stashFileSizeException();
}
}

View file

@ -30,14 +30,13 @@ import edu.cornell.mannlib.vedit.beans.LoginStatusBean;
import edu.cornell.mannlib.vitro.webapp.auth.policy.PolicyHelper;
import edu.cornell.mannlib.vitro.webapp.auth.requestedAction.Actions;
import edu.cornell.mannlib.vitro.webapp.auth.requestedAction.ifaces.RequestedAction;
import edu.cornell.mannlib.vitro.webapp.beans.BaseResourceBean;
import edu.cornell.mannlib.vitro.webapp.beans.DisplayMessage;
import edu.cornell.mannlib.vitro.webapp.beans.ResourceBean;
import edu.cornell.mannlib.vitro.webapp.controller.authenticate.LogoutRedirector;
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.UrlBuilder;
import edu.cornell.mannlib.vitro.webapp.i18n.I18n;
public class VitroHttpServlet extends HttpServlet {
public class VitroHttpServlet extends HttpServlet implements MultipartRequestWrapper.ParsingStrategy {
private static final long serialVersionUID = 1L;
protected static DateFormat publicDateFormat = new SimpleDateFormat(
@ -60,8 +59,9 @@ public class VitroHttpServlet extends HttpServlet {
if ((req instanceof HttpServletRequest)
&& (resp instanceof HttpServletResponse)) {
HttpServletRequest hreq = (HttpServletRequest) req;
HttpServletResponse hresp = (HttpServletResponse) resp;
hreq = MultipartRequestWrapper.parse(hreq, this);
if (log.isTraceEnabled()) {
dumpRequestHeaders(hreq);
}
@ -70,6 +70,25 @@ public class VitroHttpServlet extends HttpServlet {
super.service(req, resp);
}
/**
* Override this to change the maximum size of uploaded files in multipart
* requests.
*/
@Override
public long maximumMultipartFileSize() {
return 50 * 1024 * 1024; // default is 50 megabytes
}
/**
* Override this to change the way that exceptions are handled when parsing
* a multipart request. Be aware that multipart parameters have been lost,
* and that may include form fields.
*/
@Override
public boolean stashFileSizeException() {
return false;
}
/**
* doGet does nothing.
*/

View file

@ -3,12 +3,20 @@
package edu.cornell.mannlib.vitro.webapp.controller;
import static edu.cornell.mannlib.vitro.webapp.controller.MultipartRequestWrapper.ATTRIBUTE_FILE_ITEM_MAP;
import static edu.cornell.mannlib.vitro.webapp.controller.MultipartRequestWrapper.ATTRIBUTE_FILE_SIZE_EXCEPTION;
import static edu.cornell.mannlib.vitro.webapp.controller.MultipartRequestWrapper.ATTRIBUTE_IS_MULTIPART;
import java.text.Collator;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadBase.FileSizeLimitExceededException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@ -231,4 +239,59 @@ public class VitroRequest extends HttpServletRequestWrapper {
public WebappDaoFactory getLanguageNeutralWebappDaoFactory() {
return (WebappDaoFactory) getAttribute("languageNeutralWebappDaoFactory");
}
// ----------------------------------------------------------------------
// Deal with parsed multipart requests.
// ----------------------------------------------------------------------
public boolean isMultipart() {
return getAttribute(ATTRIBUTE_IS_MULTIPART) != null;
}
@SuppressWarnings("unchecked")
public Map<String, List<FileItem>> getFiles() {
Map<String, List<FileItem>> map;
map = (Map<String, List<FileItem>>) getAttribute(ATTRIBUTE_FILE_ITEM_MAP);
if (map == null) {
return Collections.emptyMap();
} else {
return map;
}
}
/**
* There may be more than one file item with the given name. If the first
* one is empty (size is zero), keep looking for a non-empty one.
*/
public FileItem getFileItem(String name) {
Map<String, List<FileItem>> map = getFiles();
List<FileItem> items = map.get(name);
if (items == null) {
return null;
}
for (FileItem item : items) {
if (item.getSize() > 0L) {
return item;
}
}
return null;
}
/**
* If the uploaded file exceeded the maximum size, and if the strategy said
* to stash the exception, it will be stored as a request attribute.
*/
public boolean hasFileSizeException() {
return getFileSizeException() != null;
}
public FileSizeLimitExceededException getFileSizeException() {
Object e = getAttribute(ATTRIBUTE_FILE_SIZE_EXCEPTION);
if (e instanceof FileSizeLimitExceededException) {
return (FileSizeLimitExceededException) e;
} else {
return null;
}
}
}

View file

@ -2,12 +2,6 @@
package edu.cornell.mannlib.vitro.webapp.controller.freemarker;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.Map;
import java.util.Map.Entry;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.UnavailableException;
import javax.servlet.http.HttpServletRequest;
@ -36,7 +30,6 @@ import edu.cornell.mannlib.vitro.webapp.filestorage.backend.FileStorage;
import edu.cornell.mannlib.vitro.webapp.filestorage.backend.FileStorageSetup;
import edu.cornell.mannlib.vitro.webapp.filestorage.model.FileInfo;
import edu.cornell.mannlib.vitro.webapp.filestorage.model.ImageInfo;
import edu.cornell.mannlib.vitro.webapp.filestorage.uploadrequest.FileUploadServletRequest;
import edu.cornell.mannlib.vitro.webapp.i18n.I18n;
import edu.cornell.mannlib.vitro.webapp.web.images.PlaceholderUtil;
@ -52,6 +45,7 @@ public class ImageUploadController extends FreemarkerHttpServlet {
private static final String ERROR_CODE_UNRECOGNIZED_URI = "imageUpload.errorUnrecognizedURI";
private static final String ERROR_CODE_NO_URI = "imageUpload.errorNoURI";
private static final String ERROR_CODE_FILE_TOO_BIG = "imageUpload.errorFileTooBig";
/** Limit file size to 6 megabytes. */
public static final int MAXIMUM_FILE_SIZE = 6 * 1024 * 1024;
@ -136,6 +130,22 @@ public class ImageUploadController extends FreemarkerHttpServlet {
+ FileStorage.class.getName() + "'");
}
}
/**
* How large an image file will we accept?
*/
@Override
public long maximumMultipartFileSize() {
return MAXIMUM_FILE_SIZE;
}
/**
* What will we do if there is a problem parsing the request?
*/
@Override
public boolean stashFileSizeException() {
return true;
}
/**
* The required action depends on what we are trying to do.
@ -171,52 +181,17 @@ public class ImageUploadController extends FreemarkerHttpServlet {
}
}
/**
* <p>
* Parse the multi-part request, process the request, and produce the
* output.
* </p>
* <p>
* If the request was a multi-part file upload, it will parse to a
* normal-looking request with a "file_item_map" attribute.
* </p>
* <p>
* The processing will produce a {@link ResponseValues} object, which
* represents either a request for a FreeMarker template or a forwarding
* operation.
* <ul>
* <li>If a FreeMarker template, we emulate the actions that
* FreeMarkerHttpServlet would have taken to produce the output.</li>
* <li>If a forwarding operation, we create a {@link RequestDispatcher} to
* do the forwarding.</li>
* </ul>
* </p>
*/
@Override
protected ResponseValues processRequest(VitroRequest vreq) {
try {
// Parse the multi-part request.
FileUploadServletRequest.parseRequest(vreq, MAXIMUM_FILE_SIZE);
if (log.isTraceEnabled()) {
dumpRequestDetails(vreq);
}
return buildTheResponse(vreq);
} catch (Exception e) {
// log.error("Could not produce response page", e);
return new ExceptionResponseValues(e);
}
}
/**
* Handle the different actions. If not specified, the default action is to
* show the intro screen.
*/
private ResponseValues buildTheResponse(VitroRequest vreq) {
String action = vreq.getParameter(PARAMETER_ACTION);
@Override
protected ResponseValues processRequest(VitroRequest vreq) {
try {
checkForFileTooBigException(vreq);
String action = vreq.getParameter(PARAMETER_ACTION);
Individual entity = validateEntityUri(vreq);
if (ACTION_UPLOAD.equals(action)) {
return doUploadImage(vreq, entity);
@ -240,6 +215,19 @@ public class ImageUploadController extends FreemarkerHttpServlet {
}
}
/**
* If our exception handler caught a "file too big" exception, we need to
* deal with it before anything else, since we can't trust the other
* parameters.
*/
private void checkForFileTooBigException(VitroRequest vreq)
throws UserMistakeException {
if (vreq.hasFileSizeException()) {
int limit = MAXIMUM_FILE_SIZE / (1024 * 1024);
throw new UserMistakeException(ERROR_CODE_FILE_TOO_BIG, limit);
}
}
/**
* We are just starting the upload process. Record where we came from, so if
* they hit "cancel" we know where to send them. If we have problems, just
@ -626,31 +614,6 @@ public class ImageUploadController extends FreemarkerHttpServlet {
}
/**
* For debugging, dump all sorts of information about the request.
*
* WARNING: if "req" represents a Multi-part request which has not yet been
* parsed, then reading these parameters will consume them.
*/
@SuppressWarnings("unchecked")
private void dumpRequestDetails(HttpServletRequest req) {
log.trace("Request is " + req.getClass().getName());
Map<String, String[]> parms = req.getParameterMap();
for (Entry<String, String[]> entry : parms.entrySet()) {
log.trace("Parameter '" + entry.getKey() + "'="
+ Arrays.deepToString(entry.getValue()));
}
Enumeration<String> attrs = req.getAttributeNames();
while (attrs.hasMoreElements()) {
String key = attrs.nextElement();
String valueString = String.valueOf(req.getAttribute(key));
String valueOneLine = valueString.replace("\n", " | ");
log.trace("Attribute '" + key + "'=" + valueOneLine);
}
}
static class Dimensions {
final int width;
final int height;

View file

@ -2,12 +2,9 @@
package edu.cornell.mannlib.vitro.webapp.controller.freemarker;
import static edu.cornell.mannlib.vitro.webapp.controller.freemarker.ImageUploadController.MAXIMUM_FILE_SIZE;
import static edu.cornell.mannlib.vitro.webapp.controller.freemarker.ImageUploadController.PARAMETER_UPLOADED_FILE;
import static edu.cornell.mannlib.vitro.webapp.controller.freemarker.ImageUploadController.THUMBNAIL_HEIGHT;
import static edu.cornell.mannlib.vitro.webapp.controller.freemarker.ImageUploadController.THUMBNAIL_WIDTH;
import static edu.cornell.mannlib.vitro.webapp.filestorage.uploadrequest.FileUploadServletRequest.FILE_ITEM_MAP;
import static edu.cornell.mannlib.vitro.webapp.filestorage.uploadrequest.FileUploadServletRequest.FILE_UPLOAD_EXCEPTION;
import java.io.FileNotFoundException;
import java.io.IOException;
@ -21,7 +18,6 @@ import javax.media.jai.JAI;
import javax.media.jai.RenderedOp;
import javax.media.jai.util.ImagingListener;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.io.FilenameUtils;
@ -41,7 +37,6 @@ import edu.cornell.mannlib.vitro.webapp.filestorage.UploadedFileHelper;
import edu.cornell.mannlib.vitro.webapp.filestorage.backend.FileAlreadyExistsException;
import edu.cornell.mannlib.vitro.webapp.filestorage.backend.FileStorage;
import edu.cornell.mannlib.vitro.webapp.filestorage.model.FileInfo;
import edu.cornell.mannlib.vitro.webapp.filestorage.uploadrequest.FileUploadServletRequest;
/**
* Handle the mechanics of validating, storing, and deleting file images.
@ -55,7 +50,6 @@ public class ImageUploadHelper {
private static final String ERROR_CODE_NO_IMAGE_TO_CROP = "imageUpload.errorNoImageForCropping";
private static final String ERROR_CODE_IMAGE_TOO_SMALL = "imageUpload.errorImageTooSmall";
private static final String ERROR_CODE_UNKNOWN = "imageUpload.errorUnknown";
private static final String ERROR_CODE_FILE_TOO_BIG = "imageUpload.errorFileTooBig";
private static final String ERROR_CODE_UNRECOGNIZED_FILE_TYPE = "imageUpload.errorUnrecognizedFileType";
private static final String ERROR_CODE_NO_PHOTO_SELECTED = "imageUpload.errorNoPhotoSelected";
private static final String ERROR_CODE_BAD_MULTIPART_REQUEST = "imageUpload.errorBadMultipartRequest";
@ -133,20 +127,13 @@ public class ImageUploadHelper {
* if there is no file, if it is empty, or if it is not an image
* file.
*/
@SuppressWarnings("unchecked")
FileItem validateImageFromRequest(HttpServletRequest request)
FileItem validateImageFromRequest(VitroRequest vreq)
throws UserMistakeException {
Object exception = request.getAttribute(FILE_UPLOAD_EXCEPTION);
if (exception != null) {
int limit = MAXIMUM_FILE_SIZE / (1024 * 1024);
throw new UserMistakeException(ERROR_CODE_FILE_TOO_BIG, limit);
}
Map<String, List<FileItem>> map = (Map<String, List<FileItem>>) request
.getAttribute(FILE_ITEM_MAP);
Map<String, List<FileItem>> map = vreq.getFiles();
if (map == null) {
throw new IllegalStateException(ERROR_CODE_BAD_MULTIPART_REQUEST);
}
List<FileItem> list = map.get(PARAMETER_UPLOADED_FILE);
if ((list == null) || list.isEmpty()) {
throw new UserMistakeException(ERROR_CODE_FORM_FIELD_MISSING,

View file

@ -25,7 +25,6 @@ import com.hp.hpl.jena.rdf.model.ModelMaker;
import edu.cornell.mannlib.vitro.webapp.auth.permissions.SimplePermission;
import edu.cornell.mannlib.vitro.webapp.controller.Controllers;
import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest;
import edu.cornell.mannlib.vitro.webapp.filestorage.uploadrequest.FileUploadServletRequest;
import edu.cornell.mannlib.vitro.webapp.utils.Csv2Rdf;
import edu.cornell.mannlib.vitro.webapp.utils.jena.JenaIngestUtils;
@ -37,6 +36,17 @@ public class JenaCsv2RdfController extends JenaIngestController {
private static final String CSV2RDF_SELECT_URI_JSP = "/jenaIngest/csv2rdfSelectUri.jsp";
private static int maxFileSizeInBytes = 1024 * 1024 * 2000; //2000mb
@Override
public long maximumMultipartFileSize() {
return maxFileSizeInBytes;
}
@Override
public boolean stashFileSizeException() {
return true;
}
@Override
public void doPost(HttpServletRequest rawRequest,
HttpServletResponse response) throws ServletException, IOException {
@ -45,15 +55,13 @@ public class JenaCsv2RdfController extends JenaIngestController {
return;
}
FileUploadServletRequest req = FileUploadServletRequest.parseRequest(rawRequest,
maxFileSizeInBytes);
if (req.hasFileUploadException()) {
forwardToFileUploadError(req.getFileUploadException().getLocalizedMessage(), req, response);
VitroRequest request = new VitroRequest(rawRequest);
if (request.hasFileSizeException()) {
forwardToFileUploadError(request.getFileSizeException().getLocalizedMessage(), request, response);
return;
}
VitroRequest request = new VitroRequest(req);
Map<String, List<FileItem>> fileStreams = req.getFiles();
Map<String, List<FileItem>> fileStreams = request.getFiles();
FileItem fileStream = fileStreams.get("filePath").get(0);
String filePath = fileStreams.get("filePath").get(0).getName();
@ -70,7 +78,7 @@ public class JenaCsv2RdfController extends JenaIngestController {
csv2rdfResult = doExecuteCsv2Rdf(
request, fileStream, filePath);
}catch(Exception ex){
forwardToFileUploadError(ex.getMessage(),req,response);
forwardToFileUploadError(ex.getMessage(),request,response);
return;
}
ModelMaker maker = getVitroJenaModelMaker(request);

View file

@ -36,7 +36,6 @@ import com.hp.hpl.jena.shared.Lock;
import edu.cornell.mannlib.vitro.webapp.auth.permissions.SimplePermission;
import edu.cornell.mannlib.vitro.webapp.controller.Controllers;
import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest;
import edu.cornell.mannlib.vitro.webapp.filestorage.uploadrequest.FileUploadServletRequest;
public class JenaXMLFileUpload extends JenaIngestController {
Log log = LogFactory.getLog(JenaXMLFileUpload.class);
@ -76,6 +75,16 @@ public class JenaXMLFileUpload extends JenaIngestController {
}
}
@Override
public long maximumMultipartFileSize() {
return maxFileSize;
}
@Override
public boolean stashFileSizeException() {
return true;
}
/**
* Each file will be converted to RDF/XML and loaded to the target model.
* If any of the files fail, no data will be loaded.
@ -86,14 +95,14 @@ public class JenaXMLFileUpload extends JenaIngestController {
*
*/
@Override
public void doPost(HttpServletRequest rawRequest, HttpServletResponse resp)
public void doPost(HttpServletRequest request, HttpServletResponse resp)
throws ServletException, IOException {
FileUploadServletRequest request = FileUploadServletRequest.parseRequest(rawRequest, maxFileSize);
if (request.hasFileUploadException()) {
VitroRequest vreq = new VitroRequest(request);
if (vreq.hasFileSizeException()) {
throw new ServletException("Size limit exceeded: "
+ request.getFileUploadException().getLocalizedMessage());
+ vreq.getFileSizeException().getLocalizedMessage());
}
if (request.isMultipart()) {
if (vreq.isMultipart()) {
log.debug("multipart content detected");
} else {
// TODO: forward to error message
@ -105,7 +114,6 @@ public class JenaXMLFileUpload extends JenaIngestController {
return;
}
VitroRequest vreq = new VitroRequest(request);
ModelMaker modelMaker = getVitroJenaModelMaker(vreq);
String targetModel = request.getParameter("targetModel");
if (targetModel == null) {
@ -117,7 +125,7 @@ public class JenaXMLFileUpload extends JenaIngestController {
throw new ServletException("targetModel '" + targetModel + "' was not found.");
request.setAttribute("targetModel", targetModel);
List<File> filesToLoad = saveFiles( request.getFiles() );
List<File> filesToLoad = saveFiles( vreq.getFiles() );
List<File> rdfxmlToLoad = convertFiles( filesToLoad);
List<Model> modelsToLoad = loadRdfXml( rdfxmlToLoad );
@ -136,7 +144,7 @@ public class JenaXMLFileUpload extends JenaIngestController {
request.setAttribute("title","Uploaded files and converted to RDF");
request.setAttribute("bodyJsp","/jenaIngest/xmlFileUploadSuccess.jsp");
request.setAttribute("fileItems",request.getFiles());
request.setAttribute("fileItems",vreq.getFiles());
RequestDispatcher rd = request.getRequestDispatcher(Controllers.BASIC_JSP);
request.setAttribute("css", "<link rel=\"stylesheet\" type=\"text/css\" href=\""+vreq.getAppBean().getThemeDir()+"css/edit.css\"/>");

View file

@ -42,7 +42,6 @@ import edu.cornell.mannlib.vitro.webapp.dao.jena.OntModelSelector;
import edu.cornell.mannlib.vitro.webapp.dao.jena.RDFServiceGraph;
import edu.cornell.mannlib.vitro.webapp.dao.jena.event.BulkUpdateEvent;
import edu.cornell.mannlib.vitro.webapp.dao.jena.event.EditEvent;
import edu.cornell.mannlib.vitro.webapp.filestorage.uploadrequest.FileUploadServletRequest;
import edu.cornell.mannlib.vitro.webapp.rdfservice.ChangeSet;
import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFService;
import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFServiceException;
@ -56,31 +55,39 @@ public class RDFUploadController extends JenaIngestController {
private static FileItem fileStream = null;
private static final String LOAD_RDF_DATA_JSP="/jenaIngest/loadRDFData.jsp";
public void doPost(HttpServletRequest rawRequest,
@Override
public long maximumMultipartFileSize() {
return maxFileSizeInBytes;
}
@Override
public boolean stashFileSizeException() {
return true;
}
public void doPost(HttpServletRequest req,
HttpServletResponse response) throws ServletException, IOException {
if (!isAuthorizedToDisplayPage(rawRequest, response,
if (!isAuthorizedToDisplayPage(req, response,
SimplePermission.USE_ADVANCED_DATA_TOOLS_PAGES.ACTIONS)) {
return;
}
FileUploadServletRequest req = FileUploadServletRequest.parseRequest(
rawRequest, maxFileSizeInBytes);
if (req.hasFileUploadException()) {
VitroRequest request = new VitroRequest(req);
if (request.hasFileSizeException()) {
forwardToFileUploadError(
req.getFileUploadException().getLocalizedMessage(),
request.getFileSizeException().getLocalizedMessage(),
req, response);
return;
}
Map<String, List<FileItem>> fileStreams = req.getFiles();
Map<String, List<FileItem>> fileStreams = request.getFiles();
VitroRequest request = new VitroRequest(req);
LoginStatusBean loginBean = LoginStatusBean.getBean(request);
try {
String modelName = req.getParameter("modelName");
if(modelName!=null){
loadRDF(req,request,response);
loadRDF(request,response);
return;
}
} catch (Exception e) {
@ -234,15 +241,13 @@ public class RDFUploadController extends JenaIngestController {
}
}
public void loadRDF(FileUploadServletRequest req,
VitroRequest request,
HttpServletResponse response)
throws ServletException, IOException {
Map<String, List<FileItem>> fileStreams = req.getFiles();
public void loadRDF(VitroRequest request, HttpServletResponse response)
throws ServletException {
Map<String, List<FileItem>> fileStreams = request.getFiles();
String filePath = fileStreams.get("filePath").get(0).getName();
fileStream = fileStreams.get("filePath").get(0);
String modelName = req.getParameter("modelName");
String docLoc = req.getParameter("docLoc");
String modelName = request.getParameter("modelName");
String docLoc = request.getParameter("docLoc");
String languageStr = request.getParameter("language");
ModelMaker maker = getVitroJenaModelMaker(request);

View file

@ -1,112 +0,0 @@
/* $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.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadException;
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>
* The request will have an attribute named by {@link #FILE_ITEM_MAP}. Either
* this attribute or the call to {@link #getFiles()} will produce a map that may
* be empty but is never null. The keys to the map are the field names for the
* file fields. Since a form may have multiple fields with the same name, each
* field name maps to a list of items. If a user does not provide a file to be
* uploaded in a given field, the length of the file will be 0.
* </p>
* <p>
* If the uploaded file(s) would be larger than the <code>maxFileSize</code>,
* {@link #parseRequest(HttpServletRequest, int)} does not throw an Exception.
* Instead, it records the exception in a request attribute named by
* {@link #FILE_UPLOAD_EXCEPTION}. This attribute can be accessed directly, or
* indirectly via the methods {@link #hasFileUploadException()} and
* {@link #getFileUploadException()}. If there is an exception, the file item
* map (see above) will still be non-null, but it will be empty.
* </p>
* <p>
* Most methods are declared here simply delegate to the wrapped request.
* Methods that have to do with parameters, files, or parsing exceptions, are
* handled differently for simple requests and multipart request, and are
* implemented in the sub-classes.
* </p>
*/
public abstract class FileUploadServletRequest extends HttpServletRequestWrapper {
public static final String FILE_ITEM_MAP = "file_item_map";
public static final String FILE_UPLOAD_EXCEPTION = "file_upload_exception";
// ----------------------------------------------------------------------
// The factory method
// ----------------------------------------------------------------------
/**
* Wrap this {@link HttpServletRequest} in an appropriate wrapper class.
*/
public static FileUploadServletRequest parseRequest(
HttpServletRequest request, int maxFileSize) throws IOException {
boolean isMultipart = ServletFileUpload.isMultipartContent(request);
if (isMultipart) {
return new MultipartHttpServletRequest(request, maxFileSize);
} else {
return new SimpleHttpServletRequestWrapper(request);
}
}
// ----------------------------------------------------------------------
// The constructor and the delegate.
// ----------------------------------------------------------------------
private final HttpServletRequest delegate;
public FileUploadServletRequest(HttpServletRequest delegate) {
super(delegate);
this.delegate = delegate;
}
protected HttpServletRequest getDelegate() {
return this.delegate;
}
// ----------------------------------------------------------------------
// New functionality to be implemented by the subclasses.
// ----------------------------------------------------------------------
/** Was this a multipart HTTP request? */
public abstract boolean isMultipart();
/**
* Get the map of file items, by name.
*/
public abstract Map<String, List<FileItem>> getFiles();
/**
* Find a non-empty file item with this name.
*
* @return the first such file item, or <code>null</code> if no matching,
* non-empty items were found.
*/
public abstract FileItem getFileItem(String string);
/**
* Was there an exception when uploading the file items?
*/
public abstract boolean hasFileUploadException();
/**
* Get the exception that occurred when uploading the file items. If no such
* exception, return <code>null</code>.
*/
public abstract FileUploadException getFileUploadException();
}

View file

@ -1,261 +0,0 @@
/* $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.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
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.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* 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 Log log = LogFactory
.getLog(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;
private FileUploadException fileUploadException;
/**
* Parse the multipart request. Store the info about the request parameters
* and the uploaded files.
*/
public MultipartHttpServletRequest(HttpServletRequest request,
int maxFileSize) throws IOException {
super(request);
Map<String, List<String>> parameters = new HashMap<String, List<String>>();
Map<String, List<FileItem>> files = new HashMap<String, List<FileItem>>();
File tempDir = figureTemporaryDirectory(request);
ServletFileUpload upload = createUploadHandler(maxFileSize, tempDir);
parseQueryString(request.getQueryString(), parameters);
try {
List<FileItem> items = parseRequestIntoFileItems(request, upload);
for (FileItem item : items) {
// Process a regular form field
if (item.isFormField()) {
addToParameters(parameters, item.getFieldName(), item
.getString("UTF-8"));
log.debug("Form field (parameter) " + item.getFieldName()
+ "=" + item.getString());
} else {
addToFileItems(files, item);
log.debug("File " + item.getFieldName() + ": "
+ item.getName());
}
}
} catch (FileUploadException e) {
fileUploadException = e;
request.setAttribute(
FileUploadServletRequest.FILE_UPLOAD_EXCEPTION, e);
}
this.parameters = Collections.unmodifiableMap(parameters);
log.debug("Parameters are: " + this.parameters);
this.files = Collections.unmodifiableMap(files);
log.debug("Files are: " + this.files);
request.setAttribute(FILE_ITEM_MAP, this.files);
}
/**
* Pull any parameters out of the URL.
*/
private void parseQueryString(String queryString,
Map<String, List<String>> parameters) {
log.debug("Query string is : '" + queryString + "'");
if (queryString != null) {
String[] pieces = queryString.split("&");
for (String piece : pieces) {
int equalsHere = piece.indexOf('=');
if (piece.trim().isEmpty()) {
// Ignore an empty piece.
} else if (equalsHere <= 0) {
// A parameter without a value.
addToParameters(parameters, decode(piece), "");
} else {
// A parameter with a value.
String key = piece.substring(0, equalsHere);
String value = piece.substring(equalsHere + 1);
addToParameters(parameters, decode(key), decode(value));
}
}
}
log.debug("Parameters from query string are: " + parameters);
}
/**
* Remove any special URL-style encoding.
*/
private String decode(String encoded) {
try {
return URLDecoder.decode(encoded, "UTF-8");
} catch (UnsupportedEncodingException e) {
log.error(e, e);
return encoded;
}
}
/**
* Find the temporary storage directory for this webapp.
*/
private File figureTemporaryDirectory(HttpServletRequest request) {
return (File) request.getSession().getServletContext().getAttribute(
"javax.servlet.context.tempdir");
}
/**
* Create an upload handler that will throw an exception if the file is too
* large.
*/
private ServletFileUpload createUploadHandler(int maxFileSize, File tempDir) {
DiskFileItemFactory factory = new DiskFileItemFactory();
factory.setSizeThreshold(DiskFileItemFactory.DEFAULT_SIZE_THRESHOLD);
factory.setRepository(tempDir);
ServletFileUpload upload = new ServletFileUpload(factory);
upload.setSizeMax(maxFileSize);
return upload;
}
/** 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. If there
// was an exception during parsing, make that available too.
// ----------------------------------------------------------------------
@Override
public boolean isMultipart() {
return true;
}
@Override
public Map<String, List<FileItem>> getFiles() {
return files;
}
/**
* {@inheritDoc}
* <p>
* There may be more than one file item with the given name. If the first
* one is empty (size is zero), keep looking for a non-empty one.
* </p>
*/
@Override
public FileItem getFileItem(String name) {
List<FileItem> items = files.get(name);
if (items == null) {
return null;
}
for (FileItem item : items) {
if (item.getSize() > 0L) {
return item;
}
}
return null;
}
@Override
public FileUploadException getFileUploadException() {
return fileUploadException;
}
@Override
public boolean hasFileUploadException() {
return fileUploadException != null;
}
// ----------------------------------------------------------------------
// 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<String, String[]> 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

@ -1,81 +0,0 @@
/* $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;
import org.apache.commons.fileupload.FileUploadException;
/**
* 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);
request.setAttribute(FILE_ITEM_MAP, Collections.EMPTY_MAP);
}
// ----------------------------------------------------------------------
// Not a multipart request, so there are no files or upload exceptions.
// ----------------------------------------------------------------------
@Override
public boolean isMultipart() {
return false;
}
@Override
public Map<String, List<FileItem>> getFiles() {
return Collections.emptyMap();
}
@Override
public FileItem getFileItem(String string) {
return null;
}
@Override
public FileUploadException getFileUploadException() {
return null;
}
@Override
public boolean hasFileUploadException() {
return false;
}
// ----------------------------------------------------------------------
// 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);
}
}

View file

@ -23,7 +23,6 @@ import edu.cornell.mannlib.vitro.webapp.controller.freemarker.FreemarkerHttpServ
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.ExceptionResponseValues;
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.ResponseValues;
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.TemplateResponseValues;
import edu.cornell.mannlib.vitro.webapp.filestorage.uploadrequest.FileUploadServletRequest;
import edu.cornell.mannlib.vitro.webapp.search.indexing.IndexBuilder;
/**
@ -35,7 +34,10 @@ public class SearchServiceController extends FreemarkerHttpServlet {
.getLog(SearchServiceController.class);
/** Limit file size to 1 Gigabyte. */
public static final int MAXIMUM_FILE_SIZE = 1024 * 1024 * 1024;
@Override
public long maximumMultipartFileSize() {
return 1024 * 1024 * 1024;
}
/**
* Handle the different actions. If not specified, the default action is to
@ -44,9 +46,6 @@ public class SearchServiceController extends FreemarkerHttpServlet {
@Override
protected ResponseValues processRequest(VitroRequest req) {
try {
req = new VitroRequest(FileUploadServletRequest.parseRequest(req,
MAXIMUM_FILE_SIZE));
// Check the authorization here, because we don't want to redirect
// to the login page if they are not authorized. (The file upload
// would be lost.

View file

@ -2,8 +2,6 @@
package edu.cornell.mannlib.vitro.webapp.search.controller;
import static edu.cornell.mannlib.vitro.webapp.filestorage.uploadrequest.FileUploadServletRequest.FILE_ITEM_MAP;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
@ -21,6 +19,7 @@ import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest;
import edu.cornell.mannlib.vitro.webapp.search.indexing.IndexBuilder;
/**
@ -41,9 +40,7 @@ public class UpdateUrisInIndex {
*/
protected int doUpdateUris(HttpServletRequest req, IndexBuilder builder)
throws ServletException, IOException {
@SuppressWarnings("unchecked")
Map<String, List<FileItem>> map = (Map<String, List<FileItem>>) req
.getAttribute(FILE_ITEM_MAP);
Map<String, List<FileItem>> map = new VitroRequest(req).getFiles();
if (map == null) {
throw new ServletException("Expected Multipart Content");
}
@ -54,13 +51,13 @@ public class UpdateUrisInIndex {
for (String name : map.keySet()) {
for (FileItem item : map.get(name)) {
log.debug("Found " + item.getSize() + " byte file for '" + name + "'");
uriCount += processFileItem(builder, name, item, enc);
uriCount += processFileItem(builder, item, enc);
}
}
return uriCount;
}
private int processFileItem(IndexBuilder builder, String name,
private int processFileItem(IndexBuilder builder,
FileItem item, Charset enc) throws IOException {
int count = 0;
Reader reader = new InputStreamReader(item.getInputStream(), enc.name());