
nextapp.echo.filetransfer.receiver.JakartaUploadProcessor Maven / Gradle / Ivy
/*
* This file is part of the Echo File Transfer Library.
* Copyright (C) 2002-2009 NextApp, Inc.
*
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*/
package nextapp.echo.filetransfer.receiver;
import java.io.File;
import java.io.IOException;
import javax.servlet.http.HttpServletRequest;
import nextapp.echo.filetransfer.model.Upload;
import nextapp.echo.filetransfer.model.UploadProcess;
import nextapp.echo.filetransfer.model.event.UploadProcessEvent;
import nextapp.echo.filetransfer.model.event.UploadProcessListener;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileItemIterator;
import org.apache.commons.fileupload.FileItemStream;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.ProgressListener;
import org.apache.commons.fileupload.FileUploadBase.SizeLimitExceededException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.io.FilenameUtils;
/**
* {@link UploadProcessor} implementation that uses the Jakarta Commons FileUpload library.
*
* See http://jakarta.apache.org/commons/fileupload for details.
*/
public class JakartaUploadProcessor
implements UploadProcessor {
private static final int DEFAULT_MEMORY_CACHE_THRESHOLD = 16 * 1024; // 16 KB
private static final File DEFAULT_TEMP_LOCATION = new File(System.getProperty("java.io.tmpdir", "."));
private static final int DEFAULT_UPLOAD_SIZE_LIMIT = 20 * 1024 * 1024; // 20 MB
/**
* Constant indicating that there is no size limit.
*/
public static final short NO_SIZE_LIMIT = -1;
/**
* Lowest interval at which {@link UploadProcess#progress} should be invoked.
*/
private static final int PROGRESS_INTERVAL = 250;
/**
* Global bandwidth allocator.
*/
private static final BandwidthAllocator allocator = new BandwidthAllocator(3 * 1024 * 1024);
/**
* Returns the collective bandwidth (in bytes per second) available for all file transfers, for all users combined.
* A value of zero indicates bandwidth is not throttled.
*
* @return the bandwidth
*/
public static int getBandwidth() {
if (allocator.isThrottling()) {
return allocator.getBandwidth();
} else {
return 0;
}
}
/**
* Sets the collective bandwidth (in bytes per second) available for all file transfers, for all users combined.
* A value of zero indicates bandwidth is not throttled.
*
* @param newValue the new bandwidth setting
*/
public static void setBandwidth(int newValue) {
if (newValue < 0) {
throw new IllegalArgumentException("Invalid bandwidth value.");
} else if (newValue == 0) {
allocator.setThrottling(false);
} else {
allocator.setThrottling(true);
allocator.setBandwidth(newValue);
}
}
/**
* Stateful object used to process upload.
*/
private class Instance
implements ProgressListener {
private boolean aborted = false;
/**
* The current {@link Upload} object being processed. Progress events will be forwarded
* to this upload object.
*/
private Upload currentUpload;
/**
* The next system time (in milliseconds) after which progress may be reported to the component.
* This is used to avoid inundating the component with events.
*/
private long nextProgressTime = 0;
/**
* {@link BandwidthAllocator.Tracker} implementation.
*/
private BandwidthAllocator.Tracker allocatorTracker = new BandwidthAllocator.Tracker() {
/**
* @see nextapp.echo.filetransfer.receiver.BandwidthAllocator.Tracker#bytesTransferred(long)
*/
public void bytesTransferred(long bytes) { }
/**
* @see nextapp.echo.filetransfer.receiver.BandwidthAllocator.Tracker#isAborted()
*/
public boolean isAborted() {
return aborted;
}
};
/** The incoming {@link HttpServletRequest}. */
private HttpServletRequest request;
/** The id of the {@link UploadProcess} */
private String id;
/** The {@link UploadProcess} in which this processor is participating. */
private UploadProcess uploadProcess = null;
/** Listener temporarily registered to {@link UploadProcess} to determine if the operation has been canceled */
private UploadProcessListener uploadProcessListener = new UploadProcessListener(){
/**
* @see nextapp.echo.filetransfer.model.event.UploadProcessListener#uploadStart(
* nextapp.echo.filetransfer.model.event.UploadProcessEvent)
*/
public void uploadStart(UploadProcessEvent e) { }
/**
* @see nextapp.echo.filetransfer.model.event.UploadProcessListener#uploadProgress(
* nextapp.echo.filetransfer.model.event.UploadProcessEvent)
*/
public void uploadProgress(UploadProcessEvent e) { }
/**
* @see nextapp.echo.filetransfer.model.event.UploadProcessListener#uploadComplete(
* nextapp.echo.filetransfer.model.event.UploadProcessEvent)
*/
public void uploadComplete(UploadProcessEvent e) { }
/**
* @see nextapp.echo.filetransfer.model.event.UploadProcessListener#uploadCancel(
* nextapp.echo.filetransfer.model.event.UploadProcessEvent)
*/
public void uploadCancel(UploadProcessEvent e) {
aborted = true;
}
};
/**
* Creates a new upload processing instance.
*
* @param request the incoming {@link HttpServletRequest}
* @param id the id of the {@link UploadProcess}
*/
private Instance(HttpServletRequest request, String id) {
super();
this.request = request;
this.id = id;
}
/**
* Processes the file upload.
*/
private void process() {
DiskFileItemFactory itemFactory = new DiskFileItemFactory();
itemFactory.setRepository(getDiskCacheLocation());
itemFactory.setSizeThreshold(getMemoryCacheThreshold());
String encoding = request.getCharacterEncoding();
if (encoding == null) {
encoding = "UTF-8";
}
ServletFileUpload sfu = new ServletFileUpload(itemFactory);
sfu.setHeaderEncoding(encoding);
sfu.setProgressListener(this);
if (getFileUploadSizeLimit() != NO_SIZE_LIMIT) {
sfu.setSizeMax(getFileUploadSizeLimit());
}
uploadProcess = UploadProcessManager.get(request, id, true);
uploadProcess.addProcessListener(uploadProcessListener);
try {
FileItemIterator iter = sfu.getItemIterator(request);
int uploadIndex = 0;
while (!aborted && iter.hasNext()) {
FileItemStream stream = iter.next();
if (!stream.isFormField()) {
currentUpload = uploadProcess.createUpload();
String fileName = FilenameUtils.getName(stream.getName());
FileItem item = itemFactory.createItem(stream.getFieldName(), stream.getContentType(), false,
stream.getName());
uploadProcess.configure(currentUpload, item.getContentType(), fileName);
uploadProcess.start(currentUpload);
if (currentUpload.getStatus() == Upload.STATUS_IN_PROGRESS) {
allocator.copy(allocatorTracker, stream.openStream(), item.getOutputStream());
uploadProcess.complete(currentUpload, item.getInputStream(), item.getSize());
}
++uploadIndex;
}
}
} catch (SizeLimitExceededException ex) {
uploadProcess.setStatus(Upload.STATUS_ERROR_OVERSIZE);
} catch (IOException ex) {
uploadProcess.setStatus(Upload.STATUS_ERROR_IO);
} catch (FileUploadException ex) {
uploadProcess.setStatus(Upload.STATUS_ERROR_IO);
} finally {
uploadProcess.removeProcessListener(uploadProcessListener);
}
}
/**
* @see org.apache.commons.fileupload.ProgressListener#update(long, long, int)
*/
public void update(long pBytesRead, long pContentLength, int pItems) {
if (!uploadProcess.isInitialized()) {
uploadProcess.init(pContentLength);
}
if (currentUpload != null && System.currentTimeMillis() > nextProgressTime) {
uploadProcess.progress(currentUpload, pBytesRead);
nextProgressTime = System.currentTimeMillis() + PROGRESS_INTERVAL;
}
}
}
/**
* Returns the location where cached files should be stored to disk.
*
* @return the disk cache location
*/
public File getDiskCacheLocation() {
return DEFAULT_TEMP_LOCATION;
}
/**
* Returns the maximum allowed file upload size, in bytes.
*
* @return the maximum allowed file upload size, in bytes
*/
public long getFileUploadSizeLimit() {
return DEFAULT_UPLOAD_SIZE_LIMIT;
}
/**
* Returns the maximum file size that may be stored in memory. Files larger than this size will be stored in the disk cache.
*
* @return the maximum file size that may be stored in memory
*/
public int getMemoryCacheThreshold() {
return DEFAULT_MEMORY_CACHE_THRESHOLD;
}
/**
* @see nextapp.echo.filetransfer.receiver.UploadProcessor#processUpload(HttpServletRequest,
* String)
*/
public void processUpload(HttpServletRequest request, String id) {
Instance instance = new Instance(request, id);
instance.process();
}
}