2 * Copyright 2008, 2009 Electronic Business Systems Ltd.
4 * This file is part of GSS.
6 * GSS is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
11 * GSS is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with GSS. If not, see <http://www.gnu.org/licenses/>.
19 package gr.ebs.gss.server.rest;
21 import static gr.ebs.gss.server.configuration.GSSConfigurationFactory.getConfiguration;
22 import gr.ebs.gss.client.exceptions.DuplicateNameException;
23 import gr.ebs.gss.client.exceptions.GSSIOException;
24 import gr.ebs.gss.client.exceptions.InsufficientPermissionsException;
25 import gr.ebs.gss.client.exceptions.ObjectNotFoundException;
26 import gr.ebs.gss.client.exceptions.QuotaExceededException;
27 import gr.ebs.gss.client.exceptions.RpcException;
28 import gr.ebs.gss.server.Login;
29 import gr.ebs.gss.server.domain.FileUploadStatus;
30 import gr.ebs.gss.server.domain.User;
31 import gr.ebs.gss.server.domain.dto.FileBodyDTO;
32 import gr.ebs.gss.server.domain.dto.FileHeaderDTO;
33 import gr.ebs.gss.server.domain.dto.FolderDTO;
34 import gr.ebs.gss.server.domain.dto.GroupDTO;
35 import gr.ebs.gss.server.domain.dto.PermissionDTO;
36 import gr.ebs.gss.server.ejb.ExternalAPI;
37 import gr.ebs.gss.server.ejb.TransactionHelper;
38 import gr.ebs.gss.server.webdav.Range;
40 import java.io.BufferedReader;
41 import java.io.ByteArrayInputStream;
42 import java.io.ByteArrayOutputStream;
44 import java.io.FileInputStream;
45 import java.io.FileNotFoundException;
46 import java.io.IOException;
47 import java.io.InputStream;
48 import java.io.InputStreamReader;
49 import java.io.OutputStreamWriter;
50 import java.io.PrintWriter;
51 import java.io.UnsupportedEncodingException;
53 import java.net.URISyntaxException;
54 import java.net.URLDecoder;
55 import java.net.URLEncoder;
56 import java.util.ArrayList;
57 import java.util.Arrays;
58 import java.util.Collection;
59 import java.util.Date;
60 import java.util.HashSet;
61 import java.util.List;
63 import java.util.StringTokenizer;
64 import java.util.concurrent.Callable;
66 import javax.servlet.ServletContext;
67 import javax.servlet.ServletException;
68 import javax.servlet.ServletOutputStream;
69 import javax.servlet.http.Cookie;
70 import javax.servlet.http.HttpServletRequest;
71 import javax.servlet.http.HttpServletResponse;
73 import org.apache.commons.codec.binary.Base64;
74 import org.apache.commons.fileupload.FileItemIterator;
75 import org.apache.commons.fileupload.FileItemStream;
76 import org.apache.commons.fileupload.FileUploadException;
77 import org.apache.commons.fileupload.ProgressListener;
78 import org.apache.commons.fileupload.servlet.ServletFileUpload;
79 import org.apache.commons.fileupload.util.Streams;
80 import org.apache.commons.httpclient.util.DateParseException;
81 import org.apache.commons.httpclient.util.DateUtil;
82 import org.apache.commons.logging.Log;
83 import org.apache.commons.logging.LogFactory;
84 import org.json.JSONArray;
85 import org.json.JSONException;
86 import org.json.JSONObject;
90 * A class that handles operations on the 'files' namespace.
94 public class FilesHandler extends RequestHandler {
96 * The request parameter name for fetching a different version.
98 private static final String VERSION_PARAM = "version";
101 * The request attribute containing the owner of the destination URI
102 * in a copy or move request.
104 private static final String DESTINATION_OWNER_ATTRIBUTE = "destOwner";
106 private static final int TRACK_PROGRESS_PERCENT = 5;
109 * The form parameter name that contains the signature in a browser POST upload.
111 private static final String AUTHORIZATION_PARAMETER = "Authorization";
114 * The form parameter name that contains the date in a browser POST upload.
116 private static final String DATE_PARAMETER = "Date";
119 * The request parameter name for making an upload progress request.
121 private static final String PROGRESS_PARAMETER = "progress";
124 * The request parameter name for restoring a previous version of a file.
126 private static final String RESTORE_VERSION_PARAMETER = "restoreVersion";
131 private static Log logger = LogFactory.getLog(FilesHandler.class);
134 * The servlet context provided by the call site.
136 private ServletContext context;
139 * @param servletContext
141 public FilesHandler(ServletContext servletContext) {
142 context = servletContext;
145 private void updateAccounting(final User user, final Date date, final long bandwidthDiff) {
147 new TransactionHelper<Void>().tryExecute(new Callable<Void>() {
149 public Void call() throws Exception {
150 getService().updateAccounting(user, date, bandwidthDiff);
154 } catch (RuntimeException e) {
156 } catch (Exception e) {
157 // updateAccounting() doesn't throw any checked exceptions
163 * Serve the specified resource, optionally including the data content.
165 * @param req The servlet request we are processing
166 * @param resp The servlet response we are creating
167 * @param content Should the content be included?
169 * @exception IOException if an input/output error occurs
170 * @exception ServletException if a servlet-specified error occurs
171 * @throws RpcException
172 * @throws InsufficientPermissionsException
173 * @throws ObjectNotFoundException
176 protected void serveResource(HttpServletRequest req, HttpServletResponse resp, boolean content)
177 throws IOException, ServletException {
178 boolean authDeferred = getAuthDeferred(req);
179 String path = getInnerPath(req, PATH_FILES);
183 path = URLDecoder.decode(path, "UTF-8");
184 } catch (IllegalArgumentException e) {
185 resp.sendError(HttpServletResponse.SC_BAD_REQUEST, e.getMessage());
188 String progress = req.getParameter(PROGRESS_PARAMETER);
190 if (logger.isDebugEnabled())
192 logger.debug("Serving resource '" + path + "' headers and data");
194 logger.debug("Serving resource '" + path + "' headers only");
196 User user = getUser(req);
197 User owner = getOwner(req);
198 if (user == null) user = owner;
199 boolean exists = true;
200 Object resource = null;
201 FileHeaderDTO file = null;
202 FolderDTO folder = null;
204 resource = getService().getResourceAtPath(owner.getId(), path, false);
205 } catch (ObjectNotFoundException e) {
207 } catch (RpcException e) {
208 resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path);
214 // We do not want to leak information if the request
215 // was not authenticated.
216 resp.sendError(HttpServletResponse.SC_FORBIDDEN);
219 // A request for upload progress.
220 if (progress != null && content) {
221 serveProgress(req, resp, progress, user, null);
225 resp.sendError(HttpServletResponse.SC_NOT_FOUND, req.getRequestURI());
229 if (resource instanceof FolderDTO)
230 folder = (FolderDTO) resource;
232 file = (FileHeaderDTO) resource;
234 // Now it's time to perform the deferred authentication check.
235 // Since regular signature checking was already performed,
236 // we need to check the read-all flag or the signature-in-parameters.
238 if (file != null && !file.isReadForAll() && content) {
239 // Check for GET with the signature in the request parameters.
240 String auth = req.getParameter(AUTHORIZATION_PARAMETER);
241 String dateParam = req.getParameter(DATE_PARAMETER);
242 if (auth == null || dateParam == null) {
243 // Check for a valid authentication cookie.
244 if (req.getCookies() != null) {
245 boolean found = false;
246 for (Cookie cookie : req.getCookies())
247 if (Login.AUTH_COOKIE.equals(cookie.getName())) {
248 String cookieauth = cookie.getValue();
249 int sepIndex = cookieauth.indexOf(Login.COOKIE_SEPARATOR);
250 if (sepIndex == -1) {
251 handleAuthFailure(req, resp);
254 String username = URLDecoder.decode(cookieauth.substring(0, sepIndex), "US-ASCII");
257 user = getService().findUser(username);
258 } catch (RpcException e) {
259 resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path);
263 resp.sendError(HttpServletResponse.SC_FORBIDDEN);
266 req.setAttribute(USER_ATTRIBUTE, user);
267 String token = cookieauth.substring(sepIndex + 1);
268 if (user.getAuthToken() == null) {
269 resp.sendError(HttpServletResponse.SC_FORBIDDEN);
272 if (!Arrays.equals(user.getAuthToken(), Base64.decodeBase64(token))) {
273 resp.sendError(HttpServletResponse.SC_FORBIDDEN);
280 handleAuthFailure(req, resp);
284 handleAuthFailure(req, resp);
290 timestamp = DateUtil.parseDate(dateParam).getTime();
291 } catch (DateParseException e) {
292 resp.sendError(HttpServletResponse.SC_FORBIDDEN, e.getMessage());
295 if (!isTimeValid(timestamp)) {
296 resp.sendError(HttpServletResponse.SC_FORBIDDEN);
300 // Fetch the Authorization parameter and find the user specified in it.
301 String[] authParts = auth.split(" ");
302 if (authParts.length != 2) {
303 resp.sendError(HttpServletResponse.SC_FORBIDDEN);
306 String username = authParts[0];
307 String signature = authParts[1];
310 user = getService().findUser(username);
311 } catch (RpcException e) {
312 resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path);
316 resp.sendError(HttpServletResponse.SC_FORBIDDEN);
319 req.setAttribute(USER_ATTRIBUTE, user);
321 // Remove the servlet path from the request URI.
322 String p = req.getRequestURI();
323 String servletPath = req.getContextPath() + req.getServletPath();
324 p = p.substring(servletPath.length());
325 // Validate the signature in the Authorization parameter.
326 String data = req.getMethod() + dateParam + p;
327 if (!isSignatureValid(signature, user, data)) {
328 resp.sendError(HttpServletResponse.SC_FORBIDDEN);
332 } else if (file != null && !file.isReadForAll() || folder != null && !folder.isReadForAll()) {
333 // Check for a read-for-all file request.
334 resp.sendError(HttpServletResponse.SC_FORBIDDEN);
338 // If the resource is not a collection, and the resource path
339 // ends with "/" or "\", return NOT FOUND.
341 if (path.endsWith("/") || path.endsWith("\\")) {
342 resp.sendError(HttpServletResponse.SC_NOT_FOUND, req.getRequestURI());
346 // Workaround for IE's broken caching behavior.
348 resp.setHeader("Expires", "-1");
350 // A request for upload progress.
351 if (progress != null && content) {
353 resp.sendError(HttpServletResponse.SC_BAD_REQUEST);
356 serveProgress(req, resp, progress, user, file);
360 // Fetch the version to retrieve, if specified.
361 String verStr = req.getParameter(VERSION_PARAM);
363 FileBodyDTO oldBody = null;
364 if (verStr != null && file != null)
366 version = Integer.valueOf(verStr);
367 } catch (NumberFormatException e) {
368 resp.sendError(HttpServletResponse.SC_BAD_REQUEST, req.getRequestURI());
373 oldBody = getService().getFileVersion(user.getId(), file.getId(), version);
374 } catch (RpcException e) {
375 resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path);
377 } catch (ObjectNotFoundException e) {
378 resp.sendError(HttpServletResponse.SC_NOT_FOUND);
380 } catch (InsufficientPermissionsException e) {
381 resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
385 // Check if the conditions specified in the optional If headers are
386 // satisfied. Doing this for folders would require recursive checking
387 // for all of their children, which in turn would defy the purpose of
390 // Checking If headers.
391 if (!checkIfHeaders(req, resp, file, oldBody))
394 else if(!checkIfModifiedSince(req, resp, folder))
397 // Find content type.
398 String contentType = null;
400 contentType = version>0 ? oldBody.getMimeType() : file.getMimeType();
401 if (contentType == null) {
402 contentType = context.getMimeType(file.getName());
403 file.setMimeType(contentType);
406 contentType = "application/json;charset=UTF-8";
408 ArrayList ranges = null;
409 long contentLength = -1L;
412 // Parse range specifier.
413 ranges = parseRange(req, resp, file, oldBody);
415 resp.setHeader("ETag", getETag(file, oldBody));
416 // Last-Modified header.
417 String lastModified = oldBody == null ?
418 getLastModifiedHttp(file.getAuditInfo()) :
419 getLastModifiedHttp(oldBody.getAuditInfo());
420 resp.setHeader("Last-Modified", lastModified);
421 // X-GSS-Metadata header.
423 resp.setHeader("X-GSS-Metadata", renderJson(user, file, oldBody));
424 } catch (InsufficientPermissionsException e) {
425 resp.setStatus(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
428 // Get content length.
429 contentLength = version>0 ? oldBody.getFileSize() : file.getFileSize();
430 // Special case for zero length files, which would cause a
431 // (silent) ISE when setting the output buffer size.
432 if (contentLength == 0L)
435 // Set the folder X-GSS-Metadata header.
437 resp.setHeader("X-GSS-Metadata", renderJsonMetadata(user, folder));
438 } catch (InsufficientPermissionsException e) {
439 resp.setStatus(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
443 ServletOutputStream ostream = null;
444 PrintWriter writer = null;
448 ostream = resp.getOutputStream();
449 } catch (IllegalStateException e) {
450 // If it fails, we try to get a Writer instead if we're
451 // trying to serve a text file
452 if ( contentType == null
453 || contentType.startsWith("text")
454 || contentType.endsWith("xml") )
455 writer = resp.getWriter();
461 || (ranges == null || ranges.isEmpty())
462 && req.getHeader("Range") == null
464 // Set the appropriate output headers
465 if (contentType != null) {
466 if (logger.isDebugEnabled())
467 logger.debug("contentType='" + contentType + "'");
468 resp.setContentType(contentType);
470 if (file != null && contentLength >= 0) {
471 if (logger.isDebugEnabled())
472 logger.debug("contentLength=" + contentLength);
473 if (contentLength < Integer.MAX_VALUE)
474 resp.setContentLength((int) contentLength);
476 // Set the content-length as String to be able to use a long
477 resp.setHeader("content-length", "" + contentLength);
480 InputStream renderResult = null;
483 // Serve the directory browser
485 renderResult = renderJson(user, folder);
486 } catch (InsufficientPermissionsException e) {
487 resp.setStatus(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
490 // Copy the input stream to our output stream (if requested)
493 resp.setBufferSize(output);
494 } catch (IllegalStateException e) {
499 if (needsContentDisposition(req))
500 resp.setHeader("Content-Disposition","attachment; filename*=UTF-8''"+getDispositionFilename(file));
502 resp.setHeader("Content-Disposition","inline; filename*=UTF-8''"+getDispositionFilename(file));
504 copy(file, renderResult, ostream, req, oldBody);
506 copy(file, renderResult, writer, req, oldBody);
507 if (file!=null) updateAccounting(owner, new Date(), contentLength);
508 } catch (ObjectNotFoundException e) {
509 resp.sendError(HttpServletResponse.SC_NOT_FOUND);
511 } catch (InsufficientPermissionsException e) {
512 resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
514 } catch (RpcException e) {
515 resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
520 if (ranges == null || ranges.isEmpty())
522 // Partial content response.
523 resp.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
525 if (ranges.size() == 1) {
526 Range range = (Range) ranges.get(0);
527 resp.addHeader("Content-Range", "bytes "
529 + "-" + range.end + "/"
531 long length = range.end - range.start + 1;
532 if (length < Integer.MAX_VALUE)
533 resp.setContentLength((int) length);
535 // Set the content-length as String to be able to use a long
536 resp.setHeader("content-length", "" + length);
538 if (contentType != null) {
539 if (logger.isDebugEnabled())
540 logger.debug("contentType='" + contentType + "'");
541 resp.setContentType(contentType);
546 resp.setBufferSize(output);
547 } catch (IllegalStateException e) {
552 copy(file, ostream, range, req, oldBody);
554 copy(file, writer, range, req, oldBody);
555 updateAccounting(owner, new Date(), contentLength);
556 } catch (ObjectNotFoundException e) {
557 resp.sendError(HttpServletResponse.SC_NOT_FOUND);
559 } catch (InsufficientPermissionsException e) {
560 resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
562 } catch (RpcException e) {
563 resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
568 resp.setContentType("multipart/byteranges; boundary=" + mimeSeparation);
571 resp.setBufferSize(output);
572 } catch (IllegalStateException e) {
577 copy(file, ostream, ranges.iterator(), contentType, req, oldBody);
579 copy(file, writer, ranges.iterator(), contentType, req, oldBody);
580 updateAccounting(owner, new Date(), contentLength);
581 } catch (ObjectNotFoundException e) {
582 resp.sendError(HttpServletResponse.SC_NOT_FOUND);
584 } catch (InsufficientPermissionsException e) {
585 resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
587 } catch (RpcException e) {
588 resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
597 * Handles an authentication failure. If no Authorization or Date request
598 * parameters and no Authorization, Date or X-GSS-Date headers were present,
599 * this is a browser request, so redirect to login and then let the user get
600 * back to the file. Otherwise it's a bogus client request and Forbidden is
603 private void handleAuthFailure(HttpServletRequest req, HttpServletResponse resp) throws IOException {
604 if (req.getParameter(AUTHORIZATION_PARAMETER) == null &&
605 req.getParameter(DATE_PARAMETER) == null &&
606 req.getHeader(AUTHORIZATION_HEADER) == null &&
607 req.getDateHeader(DATE_HEADER) == -1 &&
608 req.getDateHeader(GSS_DATE_HEADER) == -1)
609 resp.sendRedirect(getConfiguration().getString("loginUrl") +
610 "?next=" + req.getRequestURL().toString());
612 resp.sendError(HttpServletResponse.SC_FORBIDDEN);
616 * Return the filename of the specified file properly formatted for
617 * including in the Content-Disposition header.
619 private String getDispositionFilename(FileHeaderDTO file) throws UnsupportedEncodingException {
620 return URLEncoder.encode(file.getName(),"UTF-8").replaceAll("\\+", "%20");
624 * Determines whether the user agent needs the Content-Disposition
625 * header to be set, in order to properly download a file.
627 * @param req the HTTP request
628 * @return true if the Content-Disposition HTTP header must be set
630 private boolean needsContentDisposition(HttpServletRequest req) {
631 /*String agent = req.getHeader("user-agent");
632 if (agent != null && agent.contains("MSIE"))
634 String dl = req.getParameter("dl");
641 * Sends a progress update on the amount of bytes received until now for
642 * a file that the current user is currently uploading.
644 * @param req the HTTP request
645 * @param resp the HTTP response
646 * @param parameter the value for the progress request parameter
647 * @param user the current user
648 * @param file the file being uploaded, or null if the request is about a new file
649 * @throws IOException if an I/O error occurs
651 private void serveProgress(HttpServletRequest req, HttpServletResponse resp,
652 String parameter, User user, FileHeaderDTO file) throws IOException {
653 String filename = file == null ? parameter : file.getName();
655 FileUploadStatus status = getService().getFileUploadStatus(user.getId(), filename);
656 if (status == null) {
657 resp.sendError(HttpServletResponse.SC_NOT_FOUND);
660 JSONObject json = new JSONObject();
661 json.put("bytesUploaded", status.getBytesUploaded()).
662 put("bytesTotal", status.getFileSize());
663 sendJson(req, resp, json.toString());
665 // Workaround for IE's broken caching behavior.
666 resp.setHeader("Expires", "-1");
668 } catch (RpcException e) {
669 resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
671 } catch (JSONException e) {
672 resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.getMessage());
678 * Server a POST request to create/modify a file or folder.
680 * @param req the HTTP request
681 * @param resp the HTTP response
682 * @exception IOException if an input/output error occurs
684 void postResource(HttpServletRequest req, HttpServletResponse resp) throws IOException {
685 boolean authDeferred = getAuthDeferred(req);
686 if (!authDeferred && req.getParameterMap().size() > 1) {
687 resp.sendError(HttpServletResponse.SC_BAD_REQUEST);
690 String path = getInnerPath(req, PATH_FILES);
691 path = path.endsWith("/")? path: path + '/';
693 path = URLDecoder.decode(path, "UTF-8");
694 } catch (IllegalArgumentException e) {
695 resp.sendError(HttpServletResponse.SC_BAD_REQUEST, e.getMessage());
698 // We only defer authenticating multipart POST requests.
700 if (!ServletFileUpload.isMultipartContent(req)) {
701 resp.sendError(HttpServletResponse.SC_FORBIDDEN);
704 handleMultipart(req, resp, path);
708 String newName = req.getParameter(NEW_FOLDER_PARAMETER);
709 boolean hasUpdateParam = req.getParameterMap().containsKey(RESOURCE_UPDATE_PARAMETER);
710 boolean hasTrashParam = req.getParameterMap().containsKey(RESOURCE_TRASH_PARAMETER);
711 boolean hasRestoreParam = req.getParameterMap().containsKey(RESOURCE_RESTORE_PARAMETER);
712 String copyTo = req.getParameter(RESOURCE_COPY_PARAMETER);
713 String moveTo = req.getParameter(RESOURCE_MOVE_PARAMETER);
714 String restoreVersion = req.getParameter(RESTORE_VERSION_PARAMETER);
717 createFolder(req, resp, path, newName);
718 else if (hasUpdateParam)
719 updateResource(req, resp, path);
720 else if (hasTrashParam)
721 trashResource(req, resp, path);
722 else if (hasRestoreParam)
723 restoreResource(req, resp, path);
724 else if (copyTo != null)
725 copyResource(req, resp, path, copyTo);
726 else if (moveTo != null)
727 moveResource(req, resp, path, moveTo);
728 else if (restoreVersion != null)
729 restoreVersion(req, resp, path, restoreVersion);
731 // IE with Gears uses POST for multiple uploads.
732 putResource(req, resp);
736 * Restores a previous version for a file.
738 * @param req the HTTP request
739 * @param resp the HTTP response
740 * @param path the resource path
741 * @param version the version number to restore
742 * @throws IOException if an I/O error occurs
744 private void restoreVersion(HttpServletRequest req, HttpServletResponse resp, String path, String version) throws IOException {
745 final User user = getUser(req);
746 User owner = getOwner(req);
747 Object resource = null;
749 resource = getService().getResourceAtPath(owner.getId(), path, true);
750 } catch (ObjectNotFoundException e) {
751 resp.sendError(HttpServletResponse.SC_NOT_FOUND, path);
753 } catch (RpcException e) {
754 resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path);
757 if (resource instanceof FolderDTO) {
758 resp.sendError(HttpServletResponse.SC_CONFLICT);
763 final FileHeaderDTO file = (FileHeaderDTO) resource;
764 final int oldVersion = Integer.parseInt(version);
766 new TransactionHelper<Void>().tryExecute(new Callable<Void>() {
768 public Void call() throws Exception {
769 getService().restoreVersion(user.getId(), file.getId(), oldVersion);
773 } catch (InsufficientPermissionsException e) {
774 resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
775 } catch (ObjectNotFoundException e) {
776 resp.sendError(HttpServletResponse.SC_NOT_FOUND, e.getMessage());
777 } catch (RpcException e) {
778 resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path);
779 } catch (GSSIOException e) {
780 resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.getMessage());
781 } catch (QuotaExceededException e) {
782 resp.sendError(HttpServletResponse.SC_REQUEST_ENTITY_TOO_LARGE, e.getMessage());
783 } catch (NumberFormatException e) {
784 resp.sendError(HttpServletResponse.SC_BAD_REQUEST, e.getMessage());
785 } catch (Exception e) {
786 resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.getMessage());
791 * A method for handling multipart POST requests for uploading
792 * files from browser-based JavaScript clients.
794 * @param request the HTTP request
795 * @param response the HTTP response
796 * @param path the resource path
797 * @throws IOException in case an error occurs writing to the
800 private void handleMultipart(HttpServletRequest request, HttpServletResponse response, String path) throws IOException {
801 if (logger.isDebugEnabled())
802 logger.debug("Multipart POST for resource: " + path);
804 User owner = getOwner(request);
805 boolean exists = true;
806 Object resource = null;
807 FileHeaderDTO file = null;
809 resource = getService().getResourceAtPath(owner.getId(), path, false);
810 } catch (ObjectNotFoundException e) {
812 } catch (RpcException e) {
813 response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path);
818 if (resource instanceof FileHeaderDTO) {
819 file = (FileHeaderDTO) resource;
820 if (file.isDeleted()) {
821 response.sendError(HttpServletResponse.SC_CONFLICT, file.getName() + " is in the trash");
825 response.sendError(HttpServletResponse.SC_CONFLICT, path + " is a folder");
830 String parentPath = null;
832 parentPath = getParentPath(path);
833 parent = getService().getResourceAtPath(owner.getId(), parentPath, true);
834 } catch (ObjectNotFoundException e) {
835 response.sendError(HttpServletResponse.SC_NOT_FOUND, parentPath);
837 } catch (RpcException e) {
838 response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path);
841 if (!(parent instanceof FolderDTO)) {
842 response.sendError(HttpServletResponse.SC_CONFLICT);
845 final FolderDTO folder = (FolderDTO) parent;
846 final String fileName = getLastElement(path);
848 FileItemIterator iter;
849 File uploadedFile = null;
851 // Create a new file upload handler.
852 ServletFileUpload upload = new ServletFileUpload();
853 StatusProgressListener progressListener = new StatusProgressListener(getService());
854 upload.setProgressListener(progressListener);
855 iter = upload.getItemIterator(request);
856 String dateParam = null;
858 while (iter.hasNext()) {
859 FileItemStream item = iter.next();
860 String name = item.getFieldName();
861 InputStream stream = item.openStream();
862 if (item.isFormField()) {
863 final String value = Streams.asString(stream);
864 if (name.equals(DATE_PARAMETER))
866 else if (name.equals(AUTHORIZATION_PARAMETER))
869 if (logger.isDebugEnabled())
870 logger.debug(name + ":" + value);
872 // Fetch the timestamp used to guard against replay attacks.
873 if (dateParam == null) {
874 response.sendError(HttpServletResponse.SC_FORBIDDEN, "No Date parameter");
880 timestamp = DateUtil.parseDate(dateParam).getTime();
881 } catch (DateParseException e) {
882 response.sendError(HttpServletResponse.SC_FORBIDDEN, e.getMessage());
885 if (!isTimeValid(timestamp)) {
886 response.sendError(HttpServletResponse.SC_FORBIDDEN);
890 // Fetch the Authorization parameter and find the user specified in it.
892 response.sendError(HttpServletResponse.SC_FORBIDDEN, "No Authorization parameter");
895 String[] authParts = auth.split(" ");
896 if (authParts.length != 2) {
897 response.sendError(HttpServletResponse.SC_FORBIDDEN);
900 String username = authParts[0];
901 String signature = authParts[1];
904 user = getService().findUser(username);
905 } catch (RpcException e) {
906 response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path);
910 response.sendError(HttpServletResponse.SC_FORBIDDEN);
913 request.setAttribute(USER_ATTRIBUTE, user);
915 // Remove the servlet path from the request URI.
916 String p = request.getRequestURI();
917 String servletPath = request.getContextPath() + request.getServletPath();
918 p = p.substring(servletPath.length());
919 // Validate the signature in the Authorization parameter.
920 String data = request.getMethod() + dateParam + p;
921 if (!isSignatureValid(signature, user, data)) {
922 response.sendError(HttpServletResponse.SC_FORBIDDEN);
926 progressListener.setUserId(user.getId());
927 progressListener.setFilename(fileName);
928 final String contentType = item.getContentType();
931 uploadedFile = getService().uploadFile(stream, user.getId());
932 } catch (IOException ex) {
933 throw new GSSIOException(ex, false);
935 FileHeaderDTO fileDTO = null;
936 final File upf = uploadedFile;
937 final FileHeaderDTO f = file;
940 fileDTO = new TransactionHelper<FileHeaderDTO>().tryExecute(new Callable<FileHeaderDTO>() {
942 public FileHeaderDTO call() throws Exception {
943 return getService().createFile(u.getId(), folder.getId(), fileName, contentType, upf.getCanonicalFile().length(), upf.getAbsolutePath());
947 fileDTO = new TransactionHelper<FileHeaderDTO>().tryExecute(new Callable<FileHeaderDTO>() {
949 public FileHeaderDTO call() throws Exception {
950 return getService().updateFileContents(u.getId(), f.getId(), contentType, upf.getCanonicalFile().length(), upf.getAbsolutePath());
953 updateAccounting(owner, new Date(), fileDTO.getFileSize());
954 getService().removeFileUploadProgress(user.getId(), fileName);
957 // We can't return 204 here since GWT's onSubmitComplete won't fire.
958 response.setContentType("text/html");
959 response.getWriter().print("<pre></pre>");
960 } catch (FileUploadException e) {
961 String error = "Error while uploading file";
962 logger.error(error, e);
963 response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, error);
964 } catch (GSSIOException e) {
965 if (uploadedFile != null && uploadedFile.exists())
966 uploadedFile.delete();
967 String error = "Error while uploading file";
969 logger.error(error, e);
971 logger.debug(error, e);
972 response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, error);
973 } catch (DuplicateNameException e) {
974 if (uploadedFile != null && uploadedFile.exists())
975 uploadedFile.delete();
976 String error = "The specified file name already exists in this folder";
977 logger.error(error, e);
978 response.sendError(HttpServletResponse.SC_CONFLICT, error);
980 } catch (InsufficientPermissionsException e) {
981 if (uploadedFile != null && uploadedFile.exists())
982 uploadedFile.delete();
983 String error = "You don't have the necessary permissions";
984 logger.error(error, e);
985 response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, error);
987 } catch (QuotaExceededException e) {
988 if (uploadedFile != null && uploadedFile.exists())
989 uploadedFile.delete();
990 String error = "Not enough free space available";
991 if (logger.isDebugEnabled())
992 logger.debug(error, e);
993 response.sendError(HttpServletResponse.SC_REQUEST_ENTITY_TOO_LARGE, error);
995 } catch (ObjectNotFoundException e) {
996 if (uploadedFile != null && uploadedFile.exists())
997 uploadedFile.delete();
998 String error = "A specified object was not found";
999 logger.error(error, e);
1000 response.sendError(HttpServletResponse.SC_NOT_FOUND, error);
1001 } catch (RpcException e) {
1002 if (uploadedFile != null && uploadedFile.exists())
1003 uploadedFile.delete();
1004 String error = "An error occurred while communicating with the service";
1005 logger.error(error, e);
1006 response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, error);
1007 } catch (Exception e) {
1008 if (uploadedFile != null && uploadedFile.exists())
1009 uploadedFile.delete();
1010 String error = "An internal server error occurred";
1011 logger.error(error, e);
1012 response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, error);
1017 * Move the resource in the specified path to the specified destination.
1019 * @param req the HTTP request
1020 * @param resp the HTTP response
1021 * @param path the path of the resource
1022 * @param moveTo the destination of the move procedure
1023 * @throws IOException if an input/output error occurs
1025 private void moveResource(HttpServletRequest req, HttpServletResponse resp, String path, String moveTo) throws IOException {
1026 final User user = getUser(req);
1027 User owner = getOwner(req);
1028 Object resource = null;
1030 resource = getService().getResourceAtPath(owner.getId(), path, true);
1031 } catch (ObjectNotFoundException e) {
1032 resp.sendError(HttpServletResponse.SC_NOT_FOUND, path);
1034 } catch (RpcException e) {
1035 resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path);
1039 String destination = null;
1040 User destOwner = null;
1041 boolean exists = true;
1043 destination = getDestinationPath(req, encodePath(moveTo));
1044 destination = URLDecoder.decode(destination, "UTF-8");
1045 destOwner = getDestinationOwner(req);
1046 getService().getResourceAtPath(destOwner.getId(), destination, true);
1047 } catch (ObjectNotFoundException e) {
1049 } catch (URISyntaxException e) {
1050 resp.sendError(HttpServletResponse.SC_BAD_REQUEST, e.getMessage());
1052 } catch (RpcException e) {
1053 resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, destination);
1057 resp.sendError(HttpServletResponse.SC_CONFLICT, destination + " already exists");
1062 final User dOwner = destOwner;
1063 final String dest = destination;
1064 if (resource instanceof FolderDTO) {
1065 final FolderDTO folder = (FolderDTO) resource;
1066 new TransactionHelper<Void>().tryExecute(new Callable<Void>() {
1068 public Void call() throws Exception {
1069 getService().moveFolderToPath(user.getId(), dOwner.getId(), folder.getId(), dest);
1074 final FileHeaderDTO file = (FileHeaderDTO) resource;
1075 new TransactionHelper<Void>().tryExecute(new Callable<Void>() {
1077 public Void call() throws Exception {
1078 getService().moveFileToPath(user.getId(), dOwner.getId(), file.getId(), dest);
1084 } catch (InsufficientPermissionsException e) {
1085 resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
1086 } catch (ObjectNotFoundException e) {
1087 resp.sendError(HttpServletResponse.SC_NOT_FOUND, e.getMessage());
1088 } catch (RpcException e) {
1089 resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, destination);
1090 } catch (DuplicateNameException e) {
1091 resp.sendError(HttpServletResponse.SC_CONFLICT, e.getMessage());
1092 } catch (GSSIOException e) {
1093 resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.getMessage());
1094 } catch (QuotaExceededException e) {
1095 resp.sendError(HttpServletResponse.SC_REQUEST_ENTITY_TOO_LARGE, e.getMessage());
1096 } catch (Exception e) {
1097 resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, destination);
1102 * Copy the resource in the specified path to the specified destination.
1104 * @param req the HTTP request
1105 * @param resp the HTTP response
1106 * @param path the path of the resource
1107 * @param copyTo the destination of the copy procedure
1108 * @throws IOException if an input/output error occurs
1110 private void copyResource(HttpServletRequest req, HttpServletResponse resp, String path, String copyTo) throws IOException {
1111 final User user = getUser(req);
1112 User owner = getOwner(req);
1113 Object resource = null;
1115 resource = getService().getResourceAtPath(owner.getId(), path, true);
1116 } catch (ObjectNotFoundException e) {
1117 resp.sendError(HttpServletResponse.SC_NOT_FOUND, path);
1119 } catch (RpcException e) {
1120 resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path);
1124 String destination = null;
1125 User destOwner = null;
1126 boolean exists = true;
1128 String destinationEncoded = getDestinationPath(req, encodePath(copyTo));
1129 destination = URLDecoder.decode(destinationEncoded, "UTF-8");
1130 destOwner = getDestinationOwner(req);
1131 getService().getResourceAtPath(destOwner.getId(), destinationEncoded, true);
1132 } catch (ObjectNotFoundException e) {
1134 } catch (URISyntaxException e) {
1135 resp.sendError(HttpServletResponse.SC_BAD_REQUEST, e.getMessage());
1137 } catch (RpcException e) {
1138 resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, destination);
1142 resp.sendError(HttpServletResponse.SC_CONFLICT, destination + " already exists");
1147 final User dOwner = destOwner;
1148 final String dest = destination;
1149 if (resource instanceof FolderDTO) {
1150 final FolderDTO folder = (FolderDTO) resource;
1151 new TransactionHelper<Void>().tryExecute(new Callable<Void>() {
1153 public Void call() throws Exception {
1154 getService().copyFolderStructureToPath(user.getId(), dOwner.getId(), folder.getId(), dest);
1159 final FileHeaderDTO file = (FileHeaderDTO) resource;
1160 new TransactionHelper<Void>().tryExecute(new Callable<Void>() {
1162 public Void call() throws Exception {
1163 getService().copyFileToPath(user.getId(), dOwner.getId(), file.getId(), dest);
1168 } catch (InsufficientPermissionsException e) {
1169 resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
1170 } catch (ObjectNotFoundException e) {
1171 resp.sendError(HttpServletResponse.SC_NOT_FOUND, e.getMessage());
1172 } catch (RpcException e) {
1173 resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, destination);
1174 } catch (DuplicateNameException e) {
1175 resp.sendError(HttpServletResponse.SC_CONFLICT, e.getMessage());
1176 } catch (GSSIOException e) {
1177 resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.getMessage());
1178 } catch (QuotaExceededException e) {
1179 resp.sendError(HttpServletResponse.SC_REQUEST_ENTITY_TOO_LARGE, e.getMessage());
1180 } catch (Exception e) {
1181 resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, destination);
1185 private String encodePath(String path) throws UnsupportedEncodingException{
1186 StringTokenizer str = new StringTokenizer(path, "/:", true);
1187 String result = new String();
1188 while(str.hasMoreTokens()){
1189 String token = str.nextToken();
1190 if(!token.equals("/") && !token.equals(":"))
1191 token = URLEncoder.encode(token,"UTF-8");
1192 result = result + token;
1197 * A helper method that extracts the relative resource path,
1198 * after removing the 'files' namespace.
1199 * The path returned is <i>not</i> URL-decoded.
1201 * @param req the HTTP request
1202 * @param path the specified path
1203 * @return the path relative to the root folder
1204 * @throws URISyntaxException
1205 * @throws RpcException in case an error occurs while communicating
1207 * @throws UnsupportedEncodingException
1209 private String getDestinationPath(HttpServletRequest req, String path) throws URISyntaxException, RpcException, UnsupportedEncodingException {
1210 URI uri = new URI(path);
1211 String dest = uri.getRawPath();
1212 // Remove the context path from the destination URI.
1213 String contextPath = req.getContextPath();
1214 if (!dest.startsWith(contextPath))
1215 throw new URISyntaxException(dest, "Destination path does not start with " + contextPath);
1216 dest = dest.substring(contextPath.length());
1217 // Remove the servlet path from the destination URI.
1218 String servletPath = req.getServletPath();
1219 if (!dest.startsWith(servletPath))
1220 throw new URISyntaxException(dest, "Destination path does not start with " + servletPath);
1221 dest = dest.substring(servletPath.length());
1222 // Strip the username part
1223 if (dest.length() < 2)
1224 throw new URISyntaxException(dest, "No username in the destination URI");
1225 int slash = dest.substring(1).indexOf('/');
1227 throw new URISyntaxException(dest, "No username in the destination URI");
1228 // Decode the user to get the proper characters (mainly the @)
1229 String owner = URLDecoder.decode(dest.substring(1, slash + 1), "UTF-8");
1231 o = getService().findUser(owner);
1233 throw new URISyntaxException(dest, "User " + owner + " not found");
1235 req.setAttribute(DESTINATION_OWNER_ATTRIBUTE, o);
1236 dest = dest.substring(slash + 1);
1238 // Chop the resource namespace part
1239 dest = dest.substring(RequestHandler.PATH_FILES.length());
1241 dest = dest.endsWith("/")? dest: dest + '/';
1246 * Move the resource in the specified path to the trash bin.
1248 * @param req the HTTP request
1249 * @param resp the HTTP response
1250 * @param path the path of the resource
1251 * @throws IOException if an input/output error occurs
1253 private void trashResource(HttpServletRequest req, HttpServletResponse resp, String path) throws IOException {
1254 final User user = getUser(req);
1255 User owner = getOwner(req);
1256 Object resource = null;
1258 resource = getService().getResourceAtPath(owner.getId(), path, true);
1259 } catch (ObjectNotFoundException e) {
1260 resp.sendError(HttpServletResponse.SC_NOT_FOUND, path);
1262 } catch (RpcException e) {
1263 resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path);
1268 if (resource instanceof FolderDTO) {
1269 final FolderDTO folder = (FolderDTO) resource;
1270 new TransactionHelper<Void>().tryExecute(new Callable<Void>() {
1272 public Void call() throws Exception {
1273 getService().moveFolderToTrash(user.getId(), folder.getId());
1278 final FileHeaderDTO file = (FileHeaderDTO) resource;
1279 new TransactionHelper<Void>().tryExecute(new Callable<Void>() {
1281 public Void call() throws Exception {
1282 getService().moveFileToTrash(user.getId(), file.getId());
1287 } catch (InsufficientPermissionsException e) {
1288 resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
1289 } catch (ObjectNotFoundException e) {
1290 resp.sendError(HttpServletResponse.SC_NOT_FOUND, e.getMessage());
1291 } catch (RpcException e) {
1292 resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path);
1293 } catch (Exception e) {
1294 resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path);
1299 * Restore the resource in the specified path from the trash bin.
1301 * @param req the HTTP request
1302 * @param resp the HTTP response
1303 * @param path the path of the resource
1304 * @throws IOException if an input/output error occurs
1306 private void restoreResource(HttpServletRequest req, HttpServletResponse resp, String path) throws IOException {
1307 final User user = getUser(req);
1308 User owner = getOwner(req);
1309 Object resource = null;
1311 resource = getService().getResourceAtPath(owner.getId(), path, false);
1312 } catch (ObjectNotFoundException e) {
1313 resp.sendError(HttpServletResponse.SC_NOT_FOUND, path);
1315 } catch (RpcException e) {
1316 resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path);
1321 if (resource instanceof FolderDTO) {
1322 final FolderDTO folder = (FolderDTO) resource;
1323 new TransactionHelper<Void>().tryExecute(new Callable<Void>() {
1325 public Void call() throws Exception {
1326 getService().removeFolderFromTrash(user.getId(), folder.getId());
1331 final FileHeaderDTO file = (FileHeaderDTO) resource;
1332 new TransactionHelper<Void>().tryExecute(new Callable<Void>() {
1334 public Void call() throws Exception {
1335 getService().removeFileFromTrash(user.getId(), file.getId());
1340 } catch (InsufficientPermissionsException e) {
1341 resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
1342 } catch (ObjectNotFoundException e) {
1343 resp.sendError(HttpServletResponse.SC_NOT_FOUND, e.getMessage());
1344 } catch (RpcException e) {
1345 resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path);
1346 } catch (Exception e) {
1347 resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path);
1352 * Update the resource in the specified path.
1354 * @param req the HTTP request
1355 * @param resp the HTTP response
1356 * @param path the path of the resource
1357 * @throws IOException if an input/output error occurs
1359 private void updateResource(HttpServletRequest req, HttpServletResponse resp, String path) throws IOException {
1360 final User user = getUser(req);
1361 User owner = getOwner(req);
1362 Object resource = null;
1365 resource = getService().getResourceAtPath(owner.getId(), path, false);
1366 } catch (ObjectNotFoundException e) {
1367 resp.sendError(HttpServletResponse.SC_NOT_FOUND, path);
1369 } catch (RpcException e) {
1370 resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path);
1373 StringBuffer input = new StringBuffer();
1374 JSONObject json = null;
1375 if (req.getContentType() != null && req.getContentType().startsWith("application/x-www-form-urlencoded"))
1376 input.append(req.getParameter(RESOURCE_UPDATE_PARAMETER));
1378 // Assume application/json
1379 BufferedReader reader = new BufferedReader(new InputStreamReader(req.getInputStream(),"UTF-8"));
1381 while ((line = reader.readLine()) != null)
1386 json = new JSONObject(input.toString());
1387 if (logger.isDebugEnabled())
1388 logger.debug("JSON update: " + json);
1389 if (resource instanceof FolderDTO) {
1390 final FolderDTO folder = (FolderDTO) resource;
1391 String name = json.optString("name");
1392 if (!name.isEmpty())
1394 name = URLDecoder.decode(name, "UTF-8");
1395 } catch (IllegalArgumentException e) {
1396 resp.sendError(HttpServletResponse.SC_BAD_REQUEST, e.getMessage());
1399 JSONArray permissions = json.optJSONArray("permissions");
1400 Set<PermissionDTO> perms = null;
1401 if (permissions != null)
1402 perms = parsePermissions(user, permissions);
1403 Boolean readForAll = null;
1404 if (json.opt("readForAll") != null)
1405 readForAll = json.optBoolean("readForAll");
1406 if (!name.isEmpty() || permissions != null || readForAll != null) {
1407 final String fName = name.isEmpty()? null: name;
1408 final Boolean freadForAll = readForAll;
1409 final Set<PermissionDTO> fPerms = perms;
1410 FolderDTO folderUpdated = new TransactionHelper<FolderDTO>().tryExecute(new Callable<FolderDTO>() {
1412 public FolderDTO call() throws Exception {
1413 return getService().updateFolder(user.getId(), folder.getId(), fName, freadForAll, fPerms);
1417 resp.getWriter().println(getNewUrl(req, folderUpdated));
1420 final FileHeaderDTO file = (FileHeaderDTO) resource;
1422 if (json.opt("name") != null)
1423 name = json.optString("name");
1424 Long modificationDate = null;
1425 if (json.optLong("modificationDate") != 0)
1426 modificationDate = json.optLong("modificationDate");
1427 Boolean versioned = null;
1428 if (json.opt("versioned") != null)
1429 versioned = json.getBoolean("versioned");
1430 JSONArray tagset = json.optJSONArray("tags");
1432 StringBuffer t = new StringBuffer();
1433 if (tagset != null) {
1434 for (int i = 0; i < tagset.length(); i++)
1435 t.append(tagset.getString(i) + ',');
1436 tags = t.toString();
1438 JSONArray permissions = json.optJSONArray("permissions");
1439 Set<PermissionDTO> perms = null;
1440 if (permissions != null)
1441 perms = parsePermissions(user, permissions);
1442 Boolean readForAll = null;
1443 if (json.opt("readForAll") != null)
1444 readForAll = json.optBoolean("readForAll");
1445 if (name != null || tags != null || modificationDate != null
1446 || versioned != null || perms != null
1447 || readForAll != null) {
1448 final String fName = name;
1449 final String fTags = tags;
1450 final Date mDate = modificationDate != null? new Date(modificationDate): null;
1451 final Boolean fVersioned = versioned;
1452 final Boolean fReadForAll = readForAll;
1453 final Set<PermissionDTO> fPerms = perms;
1454 new TransactionHelper<Object>().tryExecute(new Callable<Object>() {
1456 public Object call() throws Exception {
1457 getService().updateFile(user.getId(), file.getId(),
1458 fName, fTags, mDate, fVersioned,
1459 fReadForAll, fPerms);
1466 } catch (JSONException e) {
1467 resp.sendError(HttpServletResponse.SC_BAD_REQUEST, e.getMessage());
1468 } catch (InsufficientPermissionsException e) {
1469 resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
1470 } catch (ObjectNotFoundException e) {
1471 resp.sendError(HttpServletResponse.SC_NOT_FOUND, e.getMessage());
1472 } catch (DuplicateNameException e) {
1473 resp.sendError(HttpServletResponse.SC_CONFLICT, e.getMessage());
1474 } catch (RpcException e) {
1475 resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path);
1476 } catch (Exception e) {
1477 resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path);
1483 * Returns the new URL of an updated folder.
1485 private String getNewUrl(HttpServletRequest req, FolderDTO folder) throws UnsupportedEncodingException {
1486 String parentUrl =URLDecoder.decode(getContextPath(req, true),"UTF-8");
1487 String fpath = URLDecoder.decode(req.getPathInfo(), "UTF-8");
1488 if (parentUrl.indexOf(fpath) != -1)
1489 parentUrl = parentUrl.substring(0, parentUrl.indexOf(fpath));
1490 if(!parentUrl.endsWith("/"))
1491 parentUrl = parentUrl+"/";
1492 parentUrl = parentUrl+folder.getOwner().getUsername()+PATH_FILES+folder.getPath();
1497 * Helper method to convert a JSON array of permissions into a set of
1498 * PermissionDTO objects.
1500 * @param user the current user
1501 * @param permissions the JSON array to parse
1502 * @return the parsed set of permissions
1503 * @throws JSONException if there was an error parsing the JSON object
1504 * @throws RpcException if there was an error communicating with the EJB
1505 * @throws ObjectNotFoundException if the user could not be found
1506 * @throws UnsupportedEncodingException
1508 private Set<PermissionDTO> parsePermissions(User user, JSONArray permissions)
1509 throws JSONException, RpcException, ObjectNotFoundException, UnsupportedEncodingException {
1510 if (permissions == null)
1512 Set<PermissionDTO> perms = new HashSet<PermissionDTO>();
1513 for (int i = 0; i < permissions.length(); i++) {
1514 JSONObject j = permissions.getJSONObject(i);
1515 PermissionDTO perm = new PermissionDTO();
1516 perm.setModifyACL(j.optBoolean("modifyACL"));
1517 perm.setRead(j.optBoolean("read"));
1518 perm.setWrite(j.optBoolean("write"));
1519 String permUser = j.optString("user");
1520 if (!permUser.isEmpty()) {
1521 User u = getService().findUser(permUser);
1523 throw new ObjectNotFoundException("User " + permUser + " not found");
1524 perm.setUser(u.getDTO());
1526 // 31/8/2009: Add optional groupUri which takes priority if it exists
1527 String permGroupUri = j.optString("groupUri");
1528 String permGroup = j.optString("group");
1529 if (!permGroupUri.isEmpty()) {
1530 String[] names = permGroupUri.split("/");
1531 String grp = URLDecoder.decode(names[names.length - 1], "UTF-8");
1532 String usr = URLDecoder.decode(names[names.length - 3], "UTF-8");
1533 User u = getService().findUser(usr);
1535 throw new ObjectNotFoundException("User " + permUser + " not found");
1536 GroupDTO g = getService().getGroup(u.getId(), grp);
1539 else if (!permGroup.isEmpty()) {
1540 GroupDTO g = getService().getGroup(user.getId(), permGroup);
1543 if (permUser.isEmpty() && permGroupUri.isEmpty() && permGroup.isEmpty())
1544 throw new JSONException("A permission must correspond to either a user or a group");
1551 * Creates a new folder with the specified name under the folder in the provided path.
1553 * @param req the HTTP request
1554 * @param resp the HTTP response
1555 * @param path the parent folder path
1556 * @param folderName the name of the new folder
1557 * @throws IOException if an input/output error occurs
1559 private void createFolder(HttpServletRequest req, HttpServletResponse resp, String path, final String folderName) throws IOException {
1560 if (logger.isDebugEnabled())
1561 logger.debug("Creating folder " + folderName + " in '" + path);
1563 final User user = getUser(req);
1564 User owner = getOwner(req);
1565 boolean exists = true;
1567 getService().getResourceAtPath(owner.getId(), path + folderName, false);
1568 } catch (ObjectNotFoundException e) {
1570 } catch (RpcException e) {
1571 resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path + folderName);
1576 resp.addHeader("Allow", METHOD_GET + ", " + METHOD_DELETE +
1577 ", " + METHOD_HEAD);
1578 resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
1584 parent = getService().getResourceAtPath(owner.getId(), path, true);
1585 } catch (ObjectNotFoundException e) {
1586 resp.sendError(HttpServletResponse.SC_CONFLICT);
1588 } catch (RpcException e) {
1589 resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path + folderName);
1593 if (parent instanceof FolderDTO) {
1594 final FolderDTO folder = (FolderDTO) parent;
1595 FolderDTO newFolder = new TransactionHelper<FolderDTO>().tryExecute(new Callable<FolderDTO>() {
1597 public FolderDTO call() throws Exception {
1598 return getService().createFolder(user.getId(), folder.getId(), folderName);
1602 String newResource = getApiRoot() + newFolder.getURI();
1603 resp.setHeader("Location", newResource);
1604 resp.setContentType("text/plain");
1605 PrintWriter out = resp.getWriter();
1606 out.println(newResource);
1608 resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
1611 } catch (DuplicateNameException e) {
1612 resp.sendError(HttpServletResponse.SC_CONFLICT);
1614 } catch (InsufficientPermissionsException e) {
1615 resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
1617 } catch (ObjectNotFoundException e) {
1618 resp.sendError(HttpServletResponse.SC_CONFLICT);
1620 } catch (RpcException e) {
1621 resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path + folderName);
1623 } catch (Exception e) {
1624 resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path);
1627 resp.setStatus(HttpServletResponse.SC_CREATED);
1633 * @throws IOException
1634 * @throws FileNotFoundException
1636 void putResource(HttpServletRequest req, HttpServletResponse resp) throws IOException, FileNotFoundException {
1637 String path = getInnerPath(req, PATH_FILES);
1639 path = URLDecoder.decode(path, "UTF-8");
1640 } catch (IllegalArgumentException e) {
1641 resp.sendError(HttpServletResponse.SC_BAD_REQUEST, e.getMessage());
1644 if (logger.isDebugEnabled())
1645 logger.debug("Updating resource: " + path);
1647 final User user = getUser(req);
1648 User owner = getOwner(req);
1649 boolean exists = true;
1650 Object resource = null;
1651 FileHeaderDTO file = null;
1653 resource = getService().getResourceAtPath(owner.getId(), path, false);
1654 } catch (ObjectNotFoundException e) {
1656 } catch (RpcException e) {
1657 resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path);
1662 if (resource instanceof FileHeaderDTO)
1663 file = (FileHeaderDTO) resource;
1665 resp.sendError(HttpServletResponse.SC_CONFLICT, path + " is a folder");
1668 boolean result = true;
1670 // Temporary content file used to support partial PUT.
1671 File contentFile = null;
1673 Range range = parseContentRange(req, resp);
1675 InputStream resourceInputStream = null;
1677 // Append data specified in ranges to existing content for this
1678 // resource - create a temporary file on the local filesystem to
1679 // perform this operation.
1680 // Assume just one range is specified for now
1681 if (range != null) {
1683 contentFile = executePartialPut(req, range, path);
1684 } catch (RpcException e) {
1685 resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path);
1687 } catch (ObjectNotFoundException e) {
1688 resp.sendError(HttpServletResponse.SC_CONFLICT);
1690 } catch (InsufficientPermissionsException e) {
1691 resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
1694 resourceInputStream = new FileInputStream(contentFile);
1696 resourceInputStream = req.getInputStream();
1699 FolderDTO folder = null;
1700 Object parent = getService().getResourceAtPath(owner.getId(), getParentPath(path), true);
1701 if (!(parent instanceof FolderDTO)) {
1702 resp.sendError(HttpServletResponse.SC_CONFLICT);
1705 folder = (FolderDTO) parent;
1706 final String name = getLastElement(path);
1707 final String mimeType = context.getMimeType(name);
1708 File uploadedFile = null;
1710 uploadedFile = getService().uploadFile(resourceInputStream, user.getId());
1711 } catch (IOException ex) {
1712 throw new GSSIOException(ex, false);
1714 FileHeaderDTO fileDTO = null;
1715 final File uploadedf = uploadedFile;
1716 final FolderDTO parentf = folder;
1717 final FileHeaderDTO f = file;
1719 fileDTO = new TransactionHelper<FileHeaderDTO>().tryExecute(new Callable<FileHeaderDTO>() {
1721 public FileHeaderDTO call() throws Exception {
1722 return getService().updateFileContents(user.getId(), f.getId(), mimeType, uploadedf.getCanonicalFile().length(), uploadedf.getAbsolutePath());
1726 fileDTO = new TransactionHelper<FileHeaderDTO>().tryExecute(new Callable<FileHeaderDTO>() {
1728 public FileHeaderDTO call() throws Exception {
1729 return getService().createFile(user.getId(), parentf.getId(), name, mimeType, uploadedf.getCanonicalFile().length(), uploadedf.getAbsolutePath());
1733 updateAccounting(owner, new Date(), fileDTO.getFileSize());
1734 getService().removeFileUploadProgress(user.getId(), fileDTO.getName());
1735 } catch(ObjectNotFoundException e) {
1737 } catch (RpcException e) {
1738 resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path);
1740 } catch (IOException e) {
1741 resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path);
1743 } catch (GSSIOException e) {
1744 resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path);
1746 } catch (DuplicateNameException e) {
1747 resp.sendError(HttpServletResponse.SC_CONFLICT);
1749 } catch (InsufficientPermissionsException e) {
1750 resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
1752 } catch (QuotaExceededException e) {
1753 resp.sendError(HttpServletResponse.SC_REQUEST_ENTITY_TOO_LARGE, e.getMessage());
1755 } catch (Exception e) {
1756 resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path);
1762 resp.setStatus(HttpServletResponse.SC_NO_CONTENT);
1764 resp.setStatus(HttpServletResponse.SC_CREATED);
1766 resp.sendError(HttpServletResponse.SC_CONFLICT);
1770 * Delete a resource.
1772 * @param req The servlet request we are processing
1773 * @param resp The servlet response we are processing
1774 * @throws IOException if the response cannot be sent
1776 void deleteResource(HttpServletRequest req, HttpServletResponse resp) throws IOException {
1777 String path = getInnerPath(req, PATH_FILES);
1778 if (logger.isDebugEnabled())
1779 logger.debug("Deleting resource '" + path);
1780 path = URLDecoder.decode(path, "UTF-8");
1781 final User user = getUser(req);
1782 User owner = getOwner(req);
1783 boolean exists = true;
1784 Object object = null;
1786 object = getService().getResourceAtPath(owner.getId(), path, false);
1787 } catch (ObjectNotFoundException e) {
1789 } catch (RpcException e) {
1790 resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
1795 resp.sendError(HttpServletResponse.SC_NOT_FOUND);
1799 FolderDTO folder = null;
1800 FileHeaderDTO file = null;
1801 if (object instanceof FolderDTO)
1802 folder = (FolderDTO) object;
1804 file = (FileHeaderDTO) object;
1808 final FileHeaderDTO f = file;
1809 new TransactionHelper<Void>().tryExecute(new Callable<Void>() {
1811 public Void call() throws Exception {
1812 getService().deleteFile(user.getId(), f.getId());
1816 } catch (InsufficientPermissionsException e) {
1817 resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
1819 } catch (ObjectNotFoundException e) {
1820 // Although we had already found the object, it was
1821 // probably deleted from another thread.
1822 resp.sendError(HttpServletResponse.SC_NOT_FOUND);
1824 } catch (RpcException e) {
1825 resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
1827 } catch (Exception e) {
1828 resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
1831 else if (folder != null)
1833 final FolderDTO fo = folder;
1834 new TransactionHelper<Void>().tryExecute(new Callable<Void>() {
1836 public Void call() throws Exception {
1837 getService().deleteFolder(user.getId(), fo.getId());
1841 } catch (InsufficientPermissionsException e) {
1842 resp.setStatus(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
1844 } catch (ObjectNotFoundException e) {
1845 resp.setStatus(HttpServletResponse.SC_NOT_FOUND);
1847 } catch (RpcException e) {
1848 resp.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
1850 } catch (Exception e) {
1851 resp.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
1854 resp.setStatus(HttpServletResponse.SC_NO_CONTENT);
1859 * Return an InputStream to a JSON representation of the contents
1860 * of this directory.
1862 * @param user the user that made the request
1863 * @param folder the specified directory
1864 * @return an input stream with the rendered contents
1865 * @throws IOException if the response cannot be sent
1866 * @throws ServletException
1867 * @throws InsufficientPermissionsException if the user does not have
1868 * the necessary privileges to read the directory
1870 private InputStream renderJson(User user, FolderDTO folder) throws IOException,
1871 ServletException, InsufficientPermissionsException {
1872 JSONObject json = new JSONObject();
1874 json.put("name", folder.getName()).
1875 put("owner", folder.getOwner().getUsername()).
1876 put("createdBy", folder.getAuditInfo().getCreatedBy().getUsername()).
1877 put("creationDate", folder.getAuditInfo().getCreationDate().getTime()).
1878 put("deleted", folder.isDeleted()).
1879 put("readForAll", folder.isReadForAll());
1881 if (folder.getAuditInfo().getModifiedBy() != null)
1882 json.put("modifiedBy", folder.getAuditInfo().getModifiedBy().getUsername()).
1883 put("modificationDate", folder.getAuditInfo().getModificationDate().getTime());
1884 if (folder.getParent() != null) {
1885 JSONObject j = new JSONObject();
1886 j.put("uri", getApiRoot() + folder.getParent().getURI());
1887 j.put("name", folder.getParent().getName());
1888 json.put("parent", j);
1890 List<JSONObject> subfolders = new ArrayList<JSONObject>();
1891 for (FolderDTO f: folder.getSubfolders())
1892 if (!f.isDeleted()) {
1893 JSONObject j = new JSONObject();
1894 j.put("name", f.getName()).
1895 put("uri", getApiRoot() + f.getURI());
1898 json.put("folders", subfolders);
1899 List<JSONObject> files = new ArrayList<JSONObject>();
1900 List<FileHeaderDTO> fileHeaders = getService().getFiles(user.getId(), folder.getId(), false);
1901 for (FileHeaderDTO f: fileHeaders) {
1902 JSONObject j = new JSONObject();
1903 j.put("name", f.getName()).
1904 put("owner", f.getOwner().getUsername()).
1905 put("deleted", f.isDeleted()).
1906 put("version", f.getVersion()).
1907 put("content", f.getMimeType()).
1908 put("size", f.getFileSize()).
1909 put("creationDate", f.getAuditInfo().getCreationDate().getTime()).
1910 put("path", f.getFolder().getPath()).
1911 put("uri", getApiRoot() + f.getURI());
1912 if (f.getAuditInfo().getModificationDate() != null)
1913 j.put("modificationDate", f.getAuditInfo().getModificationDate().getTime());
1916 json.put("files", files);
1917 Set<PermissionDTO> perms = getService().getFolderPermissions(user.getId(), folder.getId());
1918 json.put("permissions", renderJson(perms));
1919 } catch (JSONException e) {
1920 throw new ServletException(e);
1921 } catch (ObjectNotFoundException e) {
1922 throw new ServletException(e);
1923 } catch (RpcException e) {
1924 throw new ServletException(e);
1927 // Prepare a writer to a buffered area
1928 ByteArrayOutputStream stream = new ByteArrayOutputStream();
1929 OutputStreamWriter osWriter = new OutputStreamWriter(stream, "UTF8");
1930 PrintWriter writer = new PrintWriter(osWriter);
1932 // Return an input stream to the underlying bytes
1933 writer.write(json.toString());
1935 return new ByteArrayInputStream(stream.toByteArray());
1939 * Return a String with a JSON representation of the metadata
1940 * of the specified folder.
1941 * @throws RpcException
1942 * @throws InsufficientPermissionsException
1943 * @throws ObjectNotFoundException
1945 private String renderJsonMetadata(User user, FolderDTO folder)
1946 throws ServletException, InsufficientPermissionsException {
1947 // Check if the user has read permission.
1949 if (!getService().canReadFolder(user.getId(), folder.getId()))
1950 throw new InsufficientPermissionsException();
1951 } catch (ObjectNotFoundException e) {
1952 throw new ServletException(e);
1953 } catch (RpcException e) {
1954 throw new ServletException(e);
1957 JSONObject json = new JSONObject();
1959 json.put("name", folder.getName()).
1960 put("owner", folder.getOwner().getUsername()).
1961 put("createdBy", folder.getAuditInfo().getCreatedBy().getUsername()).
1962 put("creationDate", folder.getAuditInfo().getCreationDate().getTime()).
1963 put("deleted", folder.isDeleted());
1964 if (folder.getAuditInfo().getModifiedBy() != null)
1965 json.put("modifiedBy", folder.getAuditInfo().getModifiedBy().getUsername()).
1966 put("modificationDate", folder.getAuditInfo().getModificationDate().getTime());
1967 } catch (JSONException e) {
1968 throw new ServletException(e);
1970 return json.toString();
1974 * Return a String with a JSON representation of the metadata
1975 * of the specified file. If an old file body is provided, then
1976 * the metadata of that particular version will be returned.
1978 * @param user the user that made the request
1979 * @param file the specified file header
1980 * @param oldBody the version number
1981 * @return the JSON-encoded file
1982 * @throws ServletException
1983 * @throws InsufficientPermissionsException if the user does not have
1984 * the necessary privileges to read the directory
1986 private String renderJson(User user, FileHeaderDTO file, FileBodyDTO oldBody)
1987 throws ServletException, InsufficientPermissionsException {
1988 JSONObject json = new JSONObject();
1990 // Need to encode file name in order to properly display it in the web client.
1991 json.put("name", URLEncoder.encode(file.getName(),"UTF-8")).
1992 put("owner", file.getOwner().getUsername()).
1993 put("versioned", file.isVersioned()).
1994 put("version", oldBody != null ? oldBody.getVersion() : file.getVersion()).
1995 put("readForAll", file.isReadForAll()).
1996 put("tags", renderJson(file.getTags())).
1997 put("path", file.getFolder().getPath()).
1998 put("uri", getApiRoot() + file.getURI()).
1999 put("deleted", file.isDeleted());
2000 JSONObject j = new JSONObject();
2001 j.put("uri", getApiRoot() + file.getFolder().getURI()).
2002 put("name", URLEncoder.encode(file.getFolder().getName(),"UTF-8"));
2003 json.put("folder", j);
2004 if (oldBody != null)
2005 json.put("createdBy", oldBody.getAuditInfo().getCreatedBy().getUsername()).
2006 put("creationDate", oldBody.getAuditInfo().getCreationDate().getTime()).
2007 put("modifiedBy", oldBody.getAuditInfo().getModifiedBy().getUsername()).
2008 put("modificationDate", oldBody.getAuditInfo().getModificationDate().getTime()).
2009 put("content", oldBody.getMimeType()).
2010 put("size", oldBody.getFileSize());
2012 json.put("createdBy", file.getAuditInfo().getCreatedBy().getUsername()).
2013 put("creationDate", file.getAuditInfo().getCreationDate().getTime()).
2014 put("modifiedBy", file.getAuditInfo().getModifiedBy().getUsername()).
2015 put("modificationDate", file.getAuditInfo().getModificationDate().getTime()).
2016 put("content", file.getMimeType()).
2017 put("size", file.getFileSize());
2018 Set<PermissionDTO> perms = getService().getFilePermissions(user.getId(), file.getId());
2019 json.put("permissions", renderJson(perms));
2020 } catch (JSONException e) {
2021 throw new ServletException(e);
2022 } catch (ObjectNotFoundException e) {
2023 throw new ServletException(e);
2024 } catch (RpcException e) {
2025 throw new ServletException(e);
2026 } catch (UnsupportedEncodingException e) {
2027 throw new ServletException(e);
2030 return json.toString();
2034 * Return a String with a JSON representation of the
2035 * specified set of permissions.
2037 * @param permissions the set of permissions
2038 * @return the JSON-encoded object
2039 * @throws JSONException
2040 * @throws UnsupportedEncodingException
2042 private JSONArray renderJson(Set<PermissionDTO> permissions) throws JSONException, UnsupportedEncodingException {
2043 JSONArray perms = new JSONArray();
2044 for (PermissionDTO p: permissions) {
2045 JSONObject permission = new JSONObject();
2046 permission.put("read", p.hasRead()).put("write", p.hasWrite()).put("modifyACL", p.hasModifyACL());
2047 if (p.getUser() != null)
2048 permission.put("user", p.getUser().getUsername());
2049 if (p.getGroup() != null) {
2050 GroupDTO group = p.getGroup();
2051 permission.put("groupUri", getApiRoot() + group.getOwner().getUsername() + PATH_GROUPS + "/" + URLEncoder.encode(group.getName(),"UTF-8"));
2052 permission.put("group", URLEncoder.encode(p.getGroup().getName(),"UTF-8"));
2054 perms.put(permission);
2060 * Return a String with a JSON representation of the
2061 * specified collection of tags.
2063 * @param tags the collection of tags
2064 * @return the JSON-encoded object
2065 * @throws JSONException
2066 * @throws UnsupportedEncodingException
2068 private JSONArray renderJson(Collection<String> tags) throws JSONException, UnsupportedEncodingException {
2069 JSONArray tagArray = new JSONArray();
2070 for (String t: tags)
2071 tagArray.put(URLEncoder.encode(t,"UTF-8"));
2076 * Retrieves the user who owns the destination namespace, for a
2077 * copy or move request.
2079 * @param req the HTTP request
2080 * @return the owner of the namespace
2082 protected User getDestinationOwner(HttpServletRequest req) {
2083 return (User) req.getAttribute(DESTINATION_OWNER_ATTRIBUTE);
2087 * A helper inner class for updating the progress status of a file upload.
2091 public static class StatusProgressListener implements ProgressListener {
2092 private int percentLogged = 0;
2093 private long bytesTransferred = 0;
2095 private long fileSize = -100;
2097 private Long userId;
2099 private String filename;
2101 private ExternalAPI service;
2103 public StatusProgressListener(ExternalAPI aService) {
2108 * Modify the userId.
2110 * @param aUserId the userId to set
2112 public void setUserId(Long aUserId) {
2117 * Modify the filename.
2119 * @param aFilename the filename to set
2121 public void setFilename(String aFilename) {
2122 filename = aFilename;
2126 public void update(long bytesRead, long contentLength, int items) {
2127 //monitoring per percent of bytes uploaded
2128 bytesTransferred = bytesRead;
2129 if (fileSize != contentLength)
2130 fileSize = contentLength;
2131 int percent = new Long(bytesTransferred * 100 / fileSize).intValue();
2133 if (percent < 5 || percent % TRACK_PROGRESS_PERCENT == 0 )
2134 if (percent != percentLogged){
2135 percentLogged = percent;
2137 if (userId != null && filename != null)
2138 service.createFileUploadProgress(userId, filename, bytesTransferred, fileSize);
2139 } catch (ObjectNotFoundException e) {
2140 // Swallow the exception since it is going to be caught
2141 // by previously called methods