Java tutorial
/* * Copyright 2010 Manuel Carrasco Moino. (manolo at apache/org) * http://code.google.com/p/gwtupload * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. */ package com.dien.upload.server; import static com.dien.upload.shared.UConsts.PARAM_DELAY; import static com.dien.upload.shared.UConsts.TAG_BLOBSTORE; import static com.dien.upload.shared.UConsts.TAG_BLOBSTORE_PATH; import static com.dien.upload.shared.UConsts.TAG_CANCELED; import static com.dien.upload.shared.UConsts.TAG_ERROR; import static com.dien.upload.shared.UConsts.TAG_FINISHED; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.PrintWriter; import java.io.StringWriter; import java.text.MessageFormat; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.ResourceBundle; import java.util.Vector; import javax.servlet.Servlet; import javax.servlet.ServletConfig; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import net.sf.json.JSONObject; import org.apache.commons.fileupload.FileItem; import org.apache.commons.fileupload.FileItemFactory; 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.IOUtils; import com.dien.upload.server.exceptions.UploadCanceledException; import com.dien.upload.server.exceptions.UploadException; import com.dien.upload.server.exceptions.UploadSizeLimitException; import com.dien.upload.server.exceptions.UploadTimeoutException; import com.dien.upload.shared.UConsts; /** * <p> * Upload servlet for the GwtUpload library. * </p> * * <ul> * <li>For customizable application actions, it's better to extend the UloadAction * class instead of this.</li> * * <li> * This servlet supports to be deployed in google application engine. It is able to * detect this environment and in this case it does: * <ul> * <li>Set the request size to 512 KB which is the maximal size allowed</li> * <li>Store received data in memory and cache instead of file system</li> * <li>Uses memcache for session tracking instead of normal session objects, * because objects stored in session seem not to be available until the thread finishes</li> * </ul> * </li> * </ul> * * * <p> * <b>Example of web.xml</b> * </p> * * <pre> * <context-param> * <!-- max size of the upload request --> * <param-name>maxSize</param-name> * <param-value>3145728</param-value> * </context-param> * * <context-param> * <!-- useful in development mode to see the upload progress bar in fast networks. (sleep time in milliseconds) --> * <param-name>slowUploads</param-name> * <param-value>200</param-value> * </context-param> * * <servlet> * <servlet-name>uploadServlet</servlet-name> * <servlet-class>com.dien.upload.server.UploadServlet</servlet-class> * </servlet> * * <servlet-mapping> * <servlet-name>uploadServlet</servlet-name> * <url-pattern>*.gupld</url-pattern> * </servlet-mapping> * * * </pre> * * @author Manolo Carrasco Moino * */ public class UploadServlet extends HttpServlet implements Servlet { protected static final String SESSION_FILES = "FILES"; protected static final String SESSION_LAST_FILES = "LAST_FILES"; protected static final int DEFAULT_REQUEST_LIMIT_KB = 5 * 1024 * 1024; protected static final int DEFAULT_SLOW_DELAY_MILLIS = 300; protected static final String XML_CANCELED_TRUE = "{ret:true,type:'canceled',msg:'??'}"; protected static final String XML_DELETED_TRUE = "{ret:true,type:'delete',msg:'?'}"; protected static final String XML_ERROR_ITEM_NOT_FOUND = "{ret:false,type:'not_found',msg:'item not found'}"; protected static final String XML_ERROR_TIMEOUT = "{ret:false,type:'timeout',msg:'timeout receiving file'}"; protected static final String XML_FINISHED_OK = "{ret:false,type:'finished',msg:'ok'}"; protected static UploadLogger logger = UploadLogger.getLogger(UploadServlet.class); protected static final ThreadLocal<HttpServletRequest> perThreadRequest = new ThreadLocal<HttpServletRequest>(); private static boolean appEngine = false; private static final long serialVersionUID = 2740693677625051632L; /** * Copy the content of an input stream to an output one. * * @param in * @param out * @throws IOException */ public static void copyFromInputStreamToOutputStream(InputStream in, OutputStream out) throws IOException { IOUtils.copy(in, out); } /** * Utility method to get a fileItem of type file from a vector using either * the file name or the attribute name. * * @param sessionFiles * @param parameter * @return fileItem of the file found or null */ public static FileItem findFileItem(List<FileItem> sessionFiles, String parameter) { if (sessionFiles == null || parameter == null) { return null; } FileItem item = findItemByFieldName(sessionFiles, parameter); if (item == null) { item = findItemByFileName(sessionFiles, parameter); } if (item != null && !item.isFormField()) { return item; } return null; } /** * Utility method to get a fileItem from a vector using the attribute name. * * @param sessionFiles * @param attrName * @return fileItem found or null */ public static FileItem findItemByFieldName(List<FileItem> sessionFiles, String attrName) { if (sessionFiles != null) { for (FileItem fileItem : sessionFiles) { if (fileItem.getFieldName().equalsIgnoreCase(attrName)) { return fileItem; } } } return null; } /** * Utility method to get a fileItem from a vector using the file name It * only returns items of type file. * * @param sessionFiles * @param fileName * @return fileItem of the file found or null */ public static FileItem findItemByFileName(List<FileItem> sessionFiles, String fileName) { if (sessionFiles != null) { for (FileItem fileItem : sessionFiles) { if (fileItem.isFormField() == false && fileItem.getName().equalsIgnoreCase(fileName)) { return fileItem; } } } return null; } /** * Return the list of FileItems stored in session. * * @param request * @return FileItems stored in session */ @SuppressWarnings("unchecked") public static List<FileItem> getSessionFileItems(HttpServletRequest request) { return (Vector<FileItem>) request.getSession().getAttribute(SESSION_FILES); } /** * @deprecated use getSessionFileItems */ public static List<FileItem> getSessionItems(HttpServletRequest request) { return getSessionFileItems(request); } /** * Returns the localized text of a key. */ public static String getMessage(String key, Object... pars) { ResourceBundle res = ResourceBundle.getBundle(UploadServlet.class.getName(), getThreadLocalRequest().getLocale()); return new MessageFormat(res.getString(key), getThreadLocalRequest().getLocale()).format(pars); } public static final HttpServletRequest getThreadLocalRequest() { return perThreadRequest.get(); } /** * Just a method to detect whether the web container is running with appengine * restrictions. * * @return true if the case of the application is running in appengine */ public boolean isAppEngine() { return appEngine; } /** * Removes all FileItems stored in session and the temporary data. * * @param request */ public static void removeSessionFileItems(HttpServletRequest request) { removeSessionFileItems(request, true); } /** * Removes all FileItems stored in session, but in this case * the user can specify whether the temporary data is removed from disk. * * @param request * @param removeData * true: the file data is deleted. * false: use it when you are referencing file items * instead of copying them. */ public static void removeSessionFileItems(HttpServletRequest request, boolean removeData) { logger.debug("UPLOAD-SERVLET (" + request.getSession().getId() + ") removeSessionFileItems: removeData=" + removeData); @SuppressWarnings("unchecked") Vector<FileItem> sessionFiles = (Vector<FileItem>) request.getSession().getAttribute(SESSION_FILES); if (removeData && sessionFiles != null) { for (FileItem fileItem : sessionFiles) { if (fileItem != null && !fileItem.isFormField()) { fileItem.delete(); } } } request.getSession().removeAttribute(SESSION_FILES); } /** * * @deprecated use removeSessionFileItems */ public static void removeSessionFiles(HttpServletRequest request) { removeSessionFileItems(request); } /** * Delete an uploaded file. * * @param request * @param response * @return FileItem * @throws IOException */ protected static FileItem removeUploadedFile(HttpServletRequest request, HttpServletResponse response) throws IOException { String parameter = request.getParameter(UConsts.PARAM_REMOVE); FileItem item = findFileItem(getSessionFileItems(request), parameter); if (item != null) { getSessionFileItems(request).remove(item); renderXmlResponse(request, response, XML_DELETED_TRUE); logger.debug("UPLOAD-SERVLET (" + request.getSession().getId() + ") removeUploadedFile: " + parameter + " " + item.getName() + " " + item.getSize()); } else { renderXmlResponse(request, response, XML_ERROR_ITEM_NOT_FOUND); logger.info("UPLOAD-SERVLET (" + request.getSession().getId() + ") removeUploadedFile: " + parameter + " unable to delete file because it isn't in session."); } return item; } /** * Writes a response to the client. */ protected static void renderMessage(HttpServletResponse response, String message, String contentType) throws IOException { response.setContentType(contentType + "; charset=UTF-8"); response.setCharacterEncoding("UTF-8"); PrintWriter out = response.getWriter(); out.print(message); out.flush(); out.close(); } /** * Writes an XML response to the client. */ protected void renderHtmlMessage(HttpServletResponse response, String message) throws IOException { renderMessage(response, message, "text/html"); } /** * Writes a XML response to the client. * The message must be a text which will be wrapped in an XML structure. * * Note: if the request is a POST, the response should set the content type * to text/html or text/plain in order to be able in the client side to * read the iframe body (submitCompletEvent.getResults()), otherwise the * method returns null * * @param request * @param response * @param message * @param post * specify whether the request is post or not. * @throws IOException */ protected static void renderXmlResponse(HttpServletRequest request, HttpServletResponse response, String message, boolean post) throws IOException { //json???? response.setContentType("text/html;charset=utf-8"); PrintWriter out; try { out = response.getWriter(); out.write("<html><body><textarea>" + message + "</textarea></body></html>"); out.flush(); out.close(); } catch (IOException e) { e.printStackTrace(); } } protected static void renderJSONResponse(HttpServletRequest request, HttpServletResponse response, String message) throws IOException { //json???? response.setContentType("text/html;charset=utf-8"); PrintWriter out; try { out = response.getWriter(); out.write(message); out.flush(); out.close(); } catch (IOException e) { e.printStackTrace(); } } protected static void renderXmlResponse(HttpServletRequest request, HttpServletResponse response, String message) throws IOException { renderXmlResponse(request, response, message, false); } protected static void setThreadLocalRequest(HttpServletRequest request) { perThreadRequest.set(request); } /** * Simple method to get a string from the exception stack. * * @param e * @return string */ protected static String stackTraceToString(Exception e) { StringWriter writer = new StringWriter(); e.printStackTrace(new PrintWriter(writer)); return writer.getBuffer().toString(); } protected long maxSize = DEFAULT_REQUEST_LIMIT_KB; protected int uploadDelay = 0; protected boolean useBlobstore = false; /** * Mark the current upload process to be canceled. * * @param request */ public void cancelUpload(HttpServletRequest request) { logger.debug("UPLOAD-SERVLET (" + request.getSession().getId() + ") cancelling Upload"); AbstractUploadListener listener = getCurrentListener(request); if (listener != null && !listener.isCanceled()) { listener.setException(new UploadCanceledException()); } } /** * Override this method if you want to check the request before it is passed * to commons-fileupload parser. * * @param request * @throws RuntimeException */ public void checkRequest(HttpServletRequest request) { logger.debug("UPLOAD-SERVLET (" + request.getSession().getId() + ") procesing a request with size: " + request.getContentLength() + " bytes."); if (request.getContentLength() > maxSize) { throw new UploadSizeLimitException(maxSize, request.getContentLength()); } } /** * Get an uploaded file item. * * @param request * @param response * @throws IOException */ public void getUploadedFile(HttpServletRequest request, HttpServletResponse response) throws IOException { String parameter = request.getParameter(UConsts.PARAM_SHOW); FileItem item = findFileItem(getSessionFileItems(request), parameter); if (item != null) { logger.debug("UPLOAD-SERVLET (" + request.getSession().getId() + ") getUploadedFile: " + parameter + " returning: " + item.getContentType() + ", " + item.getName() + ", " + item.getSize() + " bytes"); response.setContentType(item.getContentType()); copyFromInputStreamToOutputStream(item.getInputStream(), response.getOutputStream()); } else { logger.info("UPLOAD-SERVLET (" + request.getSession().getId() + ") getUploadedFile: " + parameter + " file isn't in session."); renderJSONResponse(request, response, XML_ERROR_ITEM_NOT_FOUND); } } @Override public String getInitParameter(String name) { String value = getServletContext().getInitParameter(name); if (value == null) { value = super.getInitParameter(name); } return value; } /** * Read configurable parameters during the servlet initialization. */ public void init(ServletConfig config) throws ServletException { super.init(config); String size = getInitParameter("maxSize"); if (size != null) { try { maxSize = Long.parseLong(size); } catch (NumberFormatException e) { } } String slow = getInitParameter("slowUploads"); if (slow != null) { if ("true".equalsIgnoreCase(slow)) { uploadDelay = DEFAULT_SLOW_DELAY_MILLIS; } else { try { uploadDelay = Integer.valueOf(slow); } catch (NumberFormatException e) { } } } String timeout = getInitParameter("noDataTimeout"); if (timeout != null) { try { UploadListener.setNoDataTimeout(Integer.parseInt(timeout)); } catch (NumberFormatException e) { } } String appe = getInitParameter("appEngine"); if (appe != null) { appEngine = "true".equalsIgnoreCase(appe); } else { appEngine = isAppEngine(); } logger.info("UPLOAD-SERVLET init: maxSize=" + maxSize + ", slowUploads=" + slow + ", isAppEngine=" + isAppEngine()); } /** * Create a new listener for this session. * * @param request * @return the appropriate listener */ protected AbstractUploadListener createNewListener(HttpServletRequest request) { if (isAppEngine()) { return new MemoryUploadListener(uploadDelay, request.getContentLength()); } else { return new UploadListener(uploadDelay, request.getContentLength()); } } /** * The get method is used to monitor the uploading process or to get the * content of the uploaded files. */ protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { perThreadRequest.set(request); if (request.getParameter(UConsts.PARAM_SESSION) != null) { logger.debug("UPLOAD-SERVLET (" + request.getSession().getId() + ") new session, blobstore=" + (isAppEngine() && useBlobstore)); request.getSession(); renderJSONResponse(request, response, "{ret:true," + TAG_BLOBSTORE + ":" + (isAppEngine() && useBlobstore) + "}"); } else if (isAppEngine() && (request.getParameter(UConsts.PARAM_BLOBSTORE) != null || request.getParameterMap().size() == 0)) { String blobStorePath = getBlobstorePath(request); logger.debug("UPLOAD-SERVLET (" + request.getSession().getId() + ") getBlobstorePath=" + blobStorePath); renderJSONResponse(request, response, "{ret:true," + TAG_BLOBSTORE_PATH + ":'" + blobStorePath + "'}"); } else if (request.getParameter(UConsts.PARAM_SHOW) != null) { getUploadedFile(request, response); } else if (request.getParameter(UConsts.PARAM_CANCEL) != null) { cancelUpload(request); renderJSONResponse(request, response, XML_CANCELED_TRUE); } else if (request.getParameter(UConsts.PARAM_REMOVE) != null) { removeUploadedFile(request, response); } else if (request.getParameter(UConsts.PARAM_CLEAN) != null) { logger.debug("UPLOAD-SERVLET (" + request.getSession().getId() + ") cleanListener"); AbstractUploadListener listener = getCurrentListener(request); if (listener != null) { listener.remove(); } renderJSONResponse(request, response, XML_FINISHED_OK); } else { String message = statusToString( getUploadStatus(request, request.getParameter(UConsts.PARAM_FILENAME), null)); renderJSONResponse(request, response, message); } perThreadRequest.set(null); } protected String statusToString(Map<String, String> status) { return JSONObject.fromObject(status).toString(); } /** * The post method is used to receive the file and save it in the user * session. It returns a very XML page that the client receives in an * iframe. * * The content of this xml document has a tag error in the case of error in * the upload process or the string OK in the case of success. * */ protected void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { perThreadRequest.set(request); String error; try { error = parsePostRequest(request, response); finish(request); Map<String, String> stat = new HashMap<String, String>(); if (error != null && error.length() > 0) { stat.put(TAG_ERROR, error); } else { getFileItemsSummary(request, stat); } renderXmlResponse(request, response, statusToString(stat), true); } catch (UploadCanceledException e) { renderXmlResponse(request, response, XML_CANCELED_TRUE, true); } catch (UploadTimeoutException e) { renderXmlResponse(request, response, XML_ERROR_TIMEOUT, true); } catch (UploadSizeLimitException e) { renderXmlResponse(request, response, "{ret:false,msg='" + e.getMessage() + "'}", true); } catch (Exception e) { logger.error("UPLOAD-SERVLET (" + request.getSession().getId() + ") Exception -> " + e.getMessage() + "\n" + stackTraceToString(e)); error = e.getMessage(); renderXmlResponse(request, response, "{ret:false,msg='" + error + "'}", true); } finally { perThreadRequest.set(null); } } protected Map<String, String> getFileItemsSummary(HttpServletRequest request, Map<String, String> ret) { if (ret == null) { ret = new HashMap<String, String>(); } @SuppressWarnings("unchecked") List<FileItem> s = (List<FileItem>) request.getSession().getAttribute(SESSION_LAST_FILES); if (s != null) { for (FileItem i : s) { if (false == i.isFormField()) { ret.put("ctype", i.getContentType() != null ? i.getContentType() : "unknown"); ret.put("size", "" + i.getSize()); ret.put("name", "" + i.getName()); ret.put("field", "" + i.getFieldName()); } } ret.put(TAG_FINISHED, "ok"); } return ret; } /** * Notify to the listener that the upload has finished. * * @param request */ protected void finish(HttpServletRequest request) { AbstractUploadListener listener = getCurrentListener(request); if (listener != null) { listener.setFinished(true); } } protected String getBlobstorePath(HttpServletRequest request) { return null; } /** * Get the listener active in this session. * * @param request * @return the listener active */ protected AbstractUploadListener getCurrentListener(HttpServletRequest request) { if (isAppEngine()) { return MemoryUploadListener.current(request.getSession().getId()); } else { return UploadListener.current(request); } } /** * Override this method if you want to implement a different ItemFactory. * * @return FileItemFactory */ protected FileItemFactory getFileItemFactory(int requestSize) { return new DiskFileItemFactory(); } /** * Method executed each time the client asks the server for the progress status. * It uses the listener to generate the adequate response * * @param request * @param fieldname * @return a map of tag/values to be rendered */ protected Map<String, String> getUploadStatus(HttpServletRequest request, String fieldname, Map ret) { perThreadRequest.set(request); HttpSession session = request.getSession(); if (ret == null) { ret = new HashMap(); } long currentBytes = 0; long totalBytes = 0; long percent = 0; AbstractUploadListener listener = getCurrentListener(request); if (listener != null) { if (listener.getException() != null) { if (listener.getException() instanceof UploadCanceledException) { ret.put(TAG_CANCELED, true); ret.put(TAG_FINISHED, TAG_CANCELED); logger.error("UPLOAD-SERVLET (" + session.getId() + ") getUploadStatus: " + fieldname + " canceled by the user after " + listener.getBytesRead() + " Bytes"); } else { String errorMsg = getMessage("server_error", listener.getException().getMessage()); ret.put(TAG_ERROR, errorMsg); ret.put(TAG_FINISHED, TAG_ERROR); logger.error("UPLOAD-SERVLET (" + session.getId() + ") getUploadStatus: " + fieldname + " finished with error: " + listener.getException().getMessage()); } } else { currentBytes = listener.getBytesRead(); totalBytes = listener.getContentLength(); percent = totalBytes != 0 ? currentBytes * 100 / totalBytes : 0; // logger.debug("UPLOAD-SERVLET (" + session.getId() + ") getUploadStatus: " + fieldname + " " + currentBytes + "/" + totalBytes + " " + percent + "%"); ret.put("percent", "" + percent); ret.put("currentBytes", "" + currentBytes); ret.put("totalBytes", "" + totalBytes); ret.put(UConsts.TAG_RET, "true"); if (listener.isFinished()) { ret.put(TAG_FINISHED, "ok"); } } } else if (getSessionFileItems(request) != null) { if (fieldname == null) { ret.put(TAG_FINISHED, "ok"); logger.debug("UPLOAD-SERVLET (" + session.getId() + ") getUploadStatus: " + request.getQueryString() + " finished with files: " + session.getAttribute(SESSION_FILES)); } else { Vector<FileItem> sessionFiles = (Vector<FileItem>) getSessionFileItems(request); for (FileItem file : sessionFiles) { if (file.isFormField() == false && file.getFieldName().equals(fieldname)) { ret.put(TAG_FINISHED, "ok"); ret.put(UConsts.PARAM_FILENAME, fieldname); logger.debug("UPLOAD-SERVLET (" + session.getId() + ") getUploadStatus: " + fieldname + " finished with files: " + session.getAttribute(SESSION_FILES)); } } } } else { logger.debug("UPLOAD-SERVLET (" + session.getId() + ") getUploadStatus: no listener in session"); ret.put("wait", "listener is null"); ret.put(UConsts.TAG_RET, false); ret.put(UConsts.TAG_MSG, "listener is null"); } if (ret.containsKey(TAG_FINISHED)) { removeCurrentListener(request); } perThreadRequest.set(null); return ret; } /** * This method parses the submit action, puts in session a listener where the * progress status is updated, and eventually stores the received data in * the user session. * * returns null in the case of success or a string with the error * */ @SuppressWarnings("unchecked") protected String parsePostRequest(HttpServletRequest request, HttpServletResponse response) { try { String delay = request.getParameter(PARAM_DELAY); uploadDelay = Integer.parseInt(delay); } catch (Exception e) { } HttpSession session = request.getSession(); logger.debug("UPLOAD-SERVLET (" + session.getId() + ") new upload request received."); AbstractUploadListener listener = getCurrentListener(request); if (listener != null) { if (listener.isFrozen() || listener.isCanceled() || listener.getPercent() >= 100) { removeCurrentListener(request); } else { String error = getMessage("busy"); logger.error("UPLOAD-SERVLET (" + session.getId() + ") " + error); return error; } } // Create a file upload progress listener, and put it in the user session, // so the browser can use ajax to query status of the upload process listener = createNewListener(request); List<FileItem> uploadedItems; try { // Call to a method which the user can override checkRequest(request); // Create the factory used for uploading files, FileItemFactory factory = getFileItemFactory(request.getContentLength()); ServletFileUpload uploader = new ServletFileUpload(factory); uploader.setSizeMax(maxSize); uploader.setProgressListener(listener); // Receive the files logger.debug("UPLOAD-SERVLET (" + session.getId() + ") parsing HTTP POST request "); uploadedItems = uploader.parseRequest(request); logger.debug("UPLOAD-SERVLET (" + session.getId() + ") parsed request, " + uploadedItems.size() + " items received."); // Received files are put in session Vector<FileItem> sessionFiles = (Vector<FileItem>) getSessionFileItems(request); if (sessionFiles == null) { sessionFiles = new Vector<FileItem>(); } String error = ""; session.setAttribute(SESSION_LAST_FILES, uploadedItems); if (uploadedItems.size() > 0) { sessionFiles.addAll(uploadedItems); String msg = ""; for (FileItem i : sessionFiles) { msg += i.getFieldName() + " => " + i.getName() + "(" + i.getSize() + " bytes),"; } logger.debug("UPLOAD-SERVLET (" + session.getId() + ") puting items in session: " + msg); session.setAttribute(SESSION_FILES, sessionFiles); } else { logger.error("UPLOAD-SERVLET (" + session.getId() + ") error NO DATA received "); error += getMessage("no_data"); } return error.length() > 0 ? error : null; } catch (SizeLimitExceededException e) { RuntimeException ex = new UploadSizeLimitException(e.getPermittedSize(), e.getActualSize()); listener.setException(ex); throw ex; } catch (UploadSizeLimitException e) { listener.setException(e); throw e; } catch (UploadCanceledException e) { listener.setException(e); throw e; } catch (UploadTimeoutException e) { listener.setException(e); throw e; } catch (Exception e) { logger.error("UPLOAD-SERVLET (" + request.getSession().getId() + ") Unexpected Exception -> " + e.getMessage() + "\n" + stackTraceToString(e)); e.printStackTrace(); RuntimeException ex = new UploadException(e); listener.setException(ex); throw ex; } } /** * Remove the listener active in this session. * * @param request */ protected void removeCurrentListener(HttpServletRequest request) { AbstractUploadListener listener = getCurrentListener(request); if (listener != null) { listener.remove(); } } }