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 gr.ebs.gss.client.domain.FileBodyDTO;
22 import gr.ebs.gss.client.domain.FileHeaderDTO;
23 import gr.ebs.gss.client.domain.FolderDTO;
24 import gr.ebs.gss.client.domain.GroupDTO;
25 import gr.ebs.gss.client.domain.PermissionDTO;
26 import gr.ebs.gss.client.exceptions.DuplicateNameException;
27 import gr.ebs.gss.client.exceptions.GSSIOException;
28 import gr.ebs.gss.client.exceptions.InsufficientPermissionsException;
29 import gr.ebs.gss.client.exceptions.ObjectNotFoundException;
30 import gr.ebs.gss.client.exceptions.QuotaExceededException;
31 import gr.ebs.gss.client.exceptions.RpcException;
32 import gr.ebs.gss.server.domain.User;
33 import gr.ebs.gss.server.ejb.ExternalAPI;
34 import gr.ebs.gss.server.webdav.Range;
36 import java.io.BufferedReader;
37 import java.io.ByteArrayInputStream;
38 import java.io.ByteArrayOutputStream;
40 import java.io.FileInputStream;
41 import java.io.FileNotFoundException;
42 import java.io.IOException;
43 import java.io.InputStream;
44 import java.io.InputStreamReader;
45 import java.io.OutputStreamWriter;
46 import java.io.PrintWriter;
47 import java.io.UnsupportedEncodingException;
49 import java.net.URISyntaxException;
50 import java.net.URLDecoder;
51 import java.net.URLEncoder;
52 import java.util.ArrayList;
53 import java.util.HashSet;
54 import java.util.List;
56 import java.util.StringTokenizer;
58 import javax.servlet.ServletContext;
59 import javax.servlet.ServletException;
60 import javax.servlet.ServletOutputStream;
61 import javax.servlet.http.HttpServletRequest;
62 import javax.servlet.http.HttpServletResponse;
64 import org.apache.commons.fileupload.FileItemIterator;
65 import org.apache.commons.fileupload.FileItemStream;
66 import org.apache.commons.fileupload.FileUploadException;
67 import org.apache.commons.fileupload.ProgressListener;
68 import org.apache.commons.fileupload.servlet.ServletFileUpload;
69 import org.apache.commons.logging.Log;
70 import org.apache.commons.logging.LogFactory;
71 import org.json.JSONArray;
72 import org.json.JSONException;
73 import org.json.JSONObject;
77 * A class that handles operations on the 'files' namespace.
81 public class FilesHandler extends RequestHandler {
83 * The request parameter name for fetching a different version.
85 private static final String VERSION_PARAM = "version";
88 * The request attribute containing the owner of the destination URI
89 * in a copy or move request.
91 private static final String DESTINATION_OWNER_ATTRIBUTE = "destOwner";
93 private static final int TRACK_PROGRESS_PERCENT = 5;
98 private static Log logger = LogFactory.getLog(FilesHandler.class);
101 * The servlet context provided by the call site.
103 private ServletContext context;
106 * @param servletContext
108 public FilesHandler(ServletContext servletContext) {
109 context = servletContext;
113 * Serve the specified resource, optionally including the data content.
115 * @param req The servlet request we are processing
116 * @param resp The servlet response we are creating
117 * @param content Should the content be included?
119 * @exception IOException if an input/output error occurs
120 * @exception ServletException if a servlet-specified error occurs
121 * @throws RpcException
122 * @throws InsufficientPermissionsException
123 * @throws ObjectNotFoundException
126 protected void serveResource(HttpServletRequest req, HttpServletResponse resp, boolean content)
127 throws IOException, ServletException {
128 boolean authDeferred = getAuthDeferred(req);
129 String path = getInnerPath(req, PATH_FILES);
132 path = URLDecoder.decode(path, "UTF-8");
134 if (logger.isDebugEnabled())
136 logger.debug("Serving resource '" + path + "' headers and data");
138 logger.debug("Serving resource '" + path + "' headers only");
140 User user = getUser(req);
141 User owner = getOwner(req);
142 if (user == null) user = owner;
143 boolean exists = true;
144 Object resource = null;
145 FileHeaderDTO file = null;
146 FolderDTO folder = null;
148 resource = getService().getResourceAtPath(owner.getId(), path, false);
149 } catch (ObjectNotFoundException e) {
151 } catch (RpcException e) {
152 resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path);
158 // We do not want to leak information if the request
159 // was not authenticated.
160 resp.sendError(HttpServletResponse.SC_FORBIDDEN);
163 resp.sendError(HttpServletResponse.SC_NOT_FOUND, req.getRequestURI());
167 if (resource instanceof FolderDTO)
168 folder = (FolderDTO) resource;
170 file = (FileHeaderDTO) resource;
172 // Now it's time to perform the deferred authentication check.
173 // Since regular signature checking was already performed,
174 // we only need to check the read-all flag.
175 if (authDeferred && file != null && !file.isReadForAll()) {
176 resp.sendError(HttpServletResponse.SC_FORBIDDEN);
180 // If the resource is not a collection, and the resource path
181 // ends with "/" or "\", return NOT FOUND.
183 if (path.endsWith("/") || path.endsWith("\\")) {
184 resp.sendError(HttpServletResponse.SC_NOT_FOUND, req.getRequestURI());
188 // Fetch the version to retrieve, if specified.
189 String verStr = req.getParameter(VERSION_PARAM);
191 FileBodyDTO oldBody = null;
192 if (verStr != null && file != null)
194 version = Integer.valueOf(verStr);
195 } catch (NumberFormatException e) {
196 resp.sendError(HttpServletResponse.SC_BAD_REQUEST, req.getRequestURI());
201 oldBody = getService().getFileVersion(user.getId(), file.getId(), version);
202 } catch (RpcException e) {
203 resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path);
205 } catch (ObjectNotFoundException e) {
206 resp.sendError(HttpServletResponse.SC_NOT_FOUND);
208 } catch (InsufficientPermissionsException e) {
209 resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
213 // Check if the conditions specified in the optional If headers are
214 // satisfied. Doing this for folders would require recursive checking
215 // for all of their children, which in turn would defy the purpose of
218 // Checking If headers.
219 if (!checkIfHeaders(req, resp, file, oldBody))
222 // Find content type.
223 String contentType = null;
225 contentType = version>0 ? oldBody.getMimeType() : file.getMimeType();
226 if (contentType == null) {
227 contentType = context.getMimeType(file.getName());
228 file.setMimeType(contentType);
231 contentType = "application/json;charset=UTF-8";
233 ArrayList ranges = null;
234 long contentLength = -1L;
237 // Parse range specifier
238 ranges = parseRange(req, resp, file, oldBody);
240 resp.setHeader("ETag", getETag(file, oldBody));
241 // Last-Modified header
242 String lastModified = oldBody == null ?
243 getLastModifiedHttp(file.getAuditInfo()) :
244 getLastModifiedHttp(oldBody.getAuditInfo());
245 resp.setHeader("Last-Modified", lastModified);
246 // X-GSS-Metadata header
248 resp.setHeader("X-GSS-Metadata", renderJson(user, file, oldBody));
249 } catch (InsufficientPermissionsException e) {
250 resp.setStatus(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
253 // Get content length
254 contentLength = version>0 ? oldBody.getFileSize() : file.getFileSize();
255 // Special case for zero length files, which would cause a
256 // (silent) ISE when setting the output buffer size
257 if (contentLength == 0L)
261 ServletOutputStream ostream = null;
262 PrintWriter writer = null;
266 ostream = resp.getOutputStream();
267 } catch (IllegalStateException e) {
268 // If it fails, we try to get a Writer instead if we're
269 // trying to serve a text file
270 if ( contentType == null
271 || contentType.startsWith("text")
272 || contentType.endsWith("xml") )
273 writer = resp.getWriter();
279 || (ranges == null || ranges.isEmpty())
280 && req.getHeader("Range") == null
282 // Set the appropriate output headers
283 if (contentType != null) {
284 if (logger.isDebugEnabled())
285 logger.debug("contentType='" + contentType + "'");
286 resp.setContentType(contentType);
288 if (file != null && contentLength >= 0) {
289 if (logger.isDebugEnabled())
290 logger.debug("contentLength=" + contentLength);
291 if (contentLength < Integer.MAX_VALUE)
292 resp.setContentLength((int) contentLength);
294 // Set the content-length as String to be able to use a long
295 resp.setHeader("content-length", "" + contentLength);
298 InputStream renderResult = null;
301 // Serve the directory browser
302 String parentUrl = getContextPath(req, true);
304 renderResult = renderJson(user, folder, parentUrl);
305 } catch (InsufficientPermissionsException e) {
306 resp.setStatus(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
310 // Copy the input stream to our output stream (if requested)
313 resp.setBufferSize(output);
314 } catch (IllegalStateException e) {
319 copy(file, renderResult, ostream, req, oldBody);
321 copy(file, renderResult, writer, req, oldBody);
322 } catch (ObjectNotFoundException e) {
323 resp.sendError(HttpServletResponse.SC_NOT_FOUND);
325 } catch (InsufficientPermissionsException e) {
326 resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
328 } catch (RpcException e) {
329 resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
334 if (ranges == null || ranges.isEmpty())
336 // Partial content response.
337 resp.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
339 if (ranges.size() == 1) {
340 Range range = (Range) ranges.get(0);
341 resp.addHeader("Content-Range", "bytes "
343 + "-" + range.end + "/"
345 long length = range.end - range.start + 1;
346 if (length < Integer.MAX_VALUE)
347 resp.setContentLength((int) length);
349 // Set the content-length as String to be able to use a long
350 resp.setHeader("content-length", "" + length);
352 if (contentType != null) {
353 if (logger.isDebugEnabled())
354 logger.debug("contentType='" + contentType + "'");
355 resp.setContentType(contentType);
360 resp.setBufferSize(output);
361 } catch (IllegalStateException e) {
366 copy(file, ostream, range, req, oldBody);
368 copy(file, writer, range, req, oldBody);
369 } catch (ObjectNotFoundException e) {
370 resp.sendError(HttpServletResponse.SC_NOT_FOUND);
372 } catch (InsufficientPermissionsException e) {
373 resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
375 } catch (RpcException e) {
376 resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
381 resp.setContentType("multipart/byteranges; boundary=" + mimeSeparation);
384 resp.setBufferSize(output);
385 } catch (IllegalStateException e) {
390 copy(file, ostream, ranges.iterator(), contentType, req, oldBody);
392 copy(file, writer, ranges.iterator(), contentType, req, oldBody);
393 } catch (ObjectNotFoundException e) {
394 resp.sendError(HttpServletResponse.SC_NOT_FOUND);
396 } catch (InsufficientPermissionsException e) {
397 resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
399 } catch (RpcException e) {
400 resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
409 * Server a POST request to create/modify a file or folder.
411 * @param req the HTTP request
412 * @param resp the HTTP response
413 * @exception IOException if an input/output error occurs
415 void postResource(HttpServletRequest req, HttpServletResponse resp) throws IOException {
416 if (req.getParameterMap().size() > 1) {
417 resp.sendError(HttpServletResponse.SC_BAD_REQUEST);
420 String path = getInnerPath(req, PATH_FILES);
421 path = path.endsWith("/")? path: path + '/';
422 path = URLDecoder.decode(path, "UTF-8");
423 String newName = req.getParameter(NEW_FOLDER_PARAMETER);
424 boolean hasUpdateParam = req.getParameterMap().containsKey(RESOURCE_UPDATE_PARAMETER);
425 boolean hasTrashParam = req.getParameterMap().containsKey(RESOURCE_TRASH_PARAMETER);
426 boolean hasRestoreParam = req.getParameterMap().containsKey(RESOURCE_RESTORE_PARAMETER);
427 String copyTo = req.getParameter(RESOURCE_COPY_PARAMETER);
428 String moveTo = req.getParameter(RESOURCE_MOVE_PARAMETER);
431 createFolder(req, resp, path, newName);
432 else if (hasUpdateParam)
433 updateResource(req, resp, path);
434 else if (hasTrashParam)
435 trashResource(req, resp, path);
436 else if (hasRestoreParam)
437 restoreResource(req, resp, path);
438 else if (copyTo != null)
439 copyResource(req, resp, path, copyTo);
440 else if (moveTo != null)
441 moveResource(req, resp, path, moveTo);
442 else if (ServletFileUpload.isMultipartContent(req))
443 handleMultipart(req, resp, path);
445 resp.sendError(HttpServletResponse.SC_BAD_REQUEST);
449 * A method for handling multipart POST requests for uploading
450 * files from browser-based JavaScript clients.
452 * @param request the HTTP request
453 * @param response the HTTP response
454 * @param path the resource path
455 * @throws IOException in case an error occurs writing to the
458 private void handleMultipart(HttpServletRequest request, HttpServletResponse response, String path) throws IOException {
459 if (logger.isDebugEnabled())
460 logger.debug("Multipart POST for resource: " + path);
462 User user = getUser(request);
463 User owner = getOwner(request);
464 boolean exists = true;
465 Object resource = null;
466 FileHeaderDTO file = null;
468 resource = getService().getResourceAtPath(owner.getId(), path, false);
469 } catch (ObjectNotFoundException e) {
471 } catch (RpcException e) {
472 response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path);
477 if (resource instanceof FileHeaderDTO)
478 file = (FileHeaderDTO) resource;
480 response.sendError(HttpServletResponse.SC_CONFLICT, path + " is a folder");
484 FolderDTO folder = null;
486 String parentPath = null;
488 parentPath = getParentPath(path);
489 parent = getService().getResourceAtPath(owner.getId(), parentPath, true);
490 } catch (ObjectNotFoundException e) {
491 response.sendError(HttpServletResponse.SC_NOT_FOUND, parentPath);
493 } catch (RpcException e) {
494 response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path);
497 if (!(parent instanceof FolderDTO)) {
498 response.sendError(HttpServletResponse.SC_CONFLICT);
501 folder = (FolderDTO) parent;
502 String fileName = getLastElement(path);
504 FileItemIterator iter;
505 File uploadedFile = null;
507 // Create a new file upload handler.
508 ServletFileUpload upload = new ServletFileUpload();
509 StatusProgressListener progressListener = new StatusProgressListener(getService());
510 upload.setProgressListener(progressListener);
511 iter = upload.getItemIterator(request);
512 while (iter.hasNext()) {
513 FileItemStream item = iter.next();
514 InputStream stream = item.openStream();
515 if (!item.isFormField()) {
516 progressListener.setUserId(user.getId());
517 progressListener.setFilename(fileName);
518 String contentType = item.getContentType();
521 uploadedFile = getService().uploadFile(stream, user.getId());
522 } catch (IOException ex) {
523 throw new GSSIOException(ex, false);
526 getService().createFile(user.getId(), folder.getId(), fileName, contentType, uploadedFile);
528 getService().updateFileContents(user.getId(), file.getId(), contentType, uploadedFile);
531 } catch (FileUploadException e) {
532 String error = "Error while uploading file";
533 logger.error(error, e);
534 response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, error);
535 } catch (GSSIOException e) {
536 if (uploadedFile != null && uploadedFile.exists())
537 uploadedFile.delete();
538 String error = "Error while uploading file";
540 logger.error(error, e);
542 logger.debug(error, e);
543 response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, error);
544 } catch (DuplicateNameException e) {
545 if (uploadedFile != null && uploadedFile.exists())
546 uploadedFile.delete();
547 String error = "The specified file name already exists in this folder";
548 logger.error(error, e);
549 response.sendError(HttpServletResponse.SC_CONFLICT, error);
551 } catch (InsufficientPermissionsException e) {
552 if (uploadedFile != null && uploadedFile.exists())
553 uploadedFile.delete();
554 String error = "You don't have the necessary permissions";
555 logger.error(error, e);
556 response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, error);
558 } catch (QuotaExceededException e) {
559 if (uploadedFile != null && uploadedFile.exists())
560 uploadedFile.delete();
561 String error = "Not enough free space available";
562 if (logger.isDebugEnabled())
563 logger.debug(error, e);
564 response.sendError(HttpServletResponse.SC_REQUEST_ENTITY_TOO_LARGE, error);
566 } catch (ObjectNotFoundException e) {
567 if (uploadedFile != null && uploadedFile.exists())
568 uploadedFile.delete();
569 String error = "A specified object was not found";
570 logger.error(error, e);
571 response.sendError(HttpServletResponse.SC_NOT_FOUND, error);
572 } catch (RpcException e) {
573 if (uploadedFile != null && uploadedFile.exists())
574 uploadedFile.delete();
575 String error = "An error occurred while communicating with the service";
576 logger.error(error, e);
577 response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, error);
582 * Move the resource in the specified path to the specified destination.
584 * @param req the HTTP request
585 * @param resp the HTTP response
586 * @param path the path of the resource
587 * @param moveTo the destination of the move procedure
588 * @throws IOException if an input/output error occurs
590 private void moveResource(HttpServletRequest req, HttpServletResponse resp, String path, String moveTo) throws IOException {
591 User user = getUser(req);
592 User owner = getOwner(req);
593 Object resource = null;
595 resource = getService().getResourceAtPath(owner.getId(), path, true);
596 } catch (ObjectNotFoundException e) {
597 resp.sendError(HttpServletResponse.SC_NOT_FOUND, path);
599 } catch (RpcException e) {
600 resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path);
604 String destination = null;
605 User destOwner = null;
606 boolean exists = true;
608 destination = getDestinationPath(req, encodePath(moveTo));
609 destination = URLDecoder.decode(destination, "UTF-8");
610 destOwner = getDestinationOwner(req);
611 getService().getResourceAtPath(destOwner.getId(), destination, true);
612 } catch (ObjectNotFoundException e) {
614 } catch (URISyntaxException e) {
615 resp.sendError(HttpServletResponse.SC_BAD_REQUEST, e.getMessage());
617 } catch (RpcException e) {
618 resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, destination);
622 resp.sendError(HttpServletResponse.SC_CONFLICT, destination + " already exists");
627 if (resource instanceof FolderDTO) {
628 FolderDTO folder = (FolderDTO) resource;
629 getService().moveFolderToPath(user.getId(), destOwner.getId(), folder.getId(), destination);
631 FileHeaderDTO file = (FileHeaderDTO) resource;
632 getService().moveFileToPath(user.getId(), destOwner.getId(), file.getId(), destination);
634 } catch (InsufficientPermissionsException e) {
635 resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
636 } catch (ObjectNotFoundException e) {
637 resp.sendError(HttpServletResponse.SC_NOT_FOUND, e.getMessage());
638 } catch (RpcException e) {
639 resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, destination);
640 } catch (DuplicateNameException e) {
641 resp.sendError(HttpServletResponse.SC_CONFLICT, e.getMessage());
642 } catch (GSSIOException e) {
643 resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.getMessage());
644 } catch (QuotaExceededException e) {
645 resp.sendError(HttpServletResponse.SC_REQUEST_ENTITY_TOO_LARGE, e.getMessage());
650 * Copy the resource in the specified path to the specified destination.
652 * @param req the HTTP request
653 * @param resp the HTTP response
654 * @param path the path of the resource
655 * @param copyTo the destination of the copy procedure
656 * @throws IOException if an input/output error occurs
658 private void copyResource(HttpServletRequest req, HttpServletResponse resp, String path, String copyTo) throws IOException {
659 User user = getUser(req);
660 User owner = getOwner(req);
661 Object resource = null;
663 resource = getService().getResourceAtPath(owner.getId(), path, true);
664 } catch (ObjectNotFoundException e) {
665 resp.sendError(HttpServletResponse.SC_NOT_FOUND, path);
667 } catch (RpcException e) {
668 resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path);
672 String destination = null;
673 User destOwner = null;
674 boolean exists = true;
676 destination = getDestinationPath(req, encodePath(copyTo));
677 destination = URLDecoder.decode(destination, "UTF-8");
678 destOwner = getDestinationOwner(req);
679 getService().getResourceAtPath(destOwner.getId(), destination, true);
680 } catch (ObjectNotFoundException e) {
682 } catch (URISyntaxException e) {
683 resp.sendError(HttpServletResponse.SC_BAD_REQUEST, e.getMessage());
685 } catch (RpcException e) {
686 resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, destination);
690 resp.sendError(HttpServletResponse.SC_CONFLICT, destination + " already exists");
695 if (resource instanceof FolderDTO) {
696 FolderDTO folder = (FolderDTO) resource;
697 getService().copyFolderStructureToPath(user.getId(), destOwner.getId(), folder.getId(), destination);
699 FileHeaderDTO file = (FileHeaderDTO) resource;
700 getService().copyFileToPath(user.getId(), destOwner.getId(), file.getId(), destination);
702 } catch (InsufficientPermissionsException e) {
703 resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
704 } catch (ObjectNotFoundException e) {
705 resp.sendError(HttpServletResponse.SC_NOT_FOUND, e.getMessage());
706 } catch (RpcException e) {
707 resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, destination);
708 } catch (DuplicateNameException e) {
709 resp.sendError(HttpServletResponse.SC_CONFLICT, e.getMessage());
710 } catch (GSSIOException e) {
711 resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.getMessage());
712 } catch (QuotaExceededException e) {
713 resp.sendError(HttpServletResponse.SC_REQUEST_ENTITY_TOO_LARGE, e.getMessage());
717 private String encodePath(String path) throws UnsupportedEncodingException{
718 StringTokenizer str = new StringTokenizer(path, "/:", true);
719 String result = new String();
720 while(str.hasMoreTokens()){
721 String token = str.nextToken();
722 if(!token.equals("/") && !token.equals(":"))
723 token = URLEncoder.encode(token,"UTF-8");
724 result = result + token;
729 * A helper method that extracts the relative resource path,
730 * after removing the 'files' namespace.
732 * @param req the HTTP request
733 * @param path the specified path
734 * @return the path relative to the root folder
735 * @throws URISyntaxException
736 * @throws RpcException in case an error occurs while communicating
739 private String getDestinationPath(HttpServletRequest req, String path) throws URISyntaxException, RpcException {
740 URI uri = new URI(path);
741 String dest = uri.getPath();
742 // Remove the context path from the destination URI.
743 String contextPath = req.getContextPath();
744 if (!dest.startsWith(contextPath))
745 throw new URISyntaxException(dest, "Destination path does not start with " + contextPath);
746 dest = dest.substring(contextPath.length());
747 // Remove the servlet path from the destination URI.
748 String servletPath = req.getServletPath();
749 if (!dest.startsWith(servletPath))
750 throw new URISyntaxException(dest, "Destination path does not start with " + servletPath);
751 dest = dest.substring(servletPath.length());
752 // Strip the username part
753 if (dest.length() < 2)
754 throw new URISyntaxException(dest, "No username in the destination URI");
755 int slash = dest.substring(1).indexOf('/');
757 throw new URISyntaxException(dest, "No username in the destination URI");
758 String owner = dest.substring(1, slash + 1);
760 o = getService().findUser(owner);
762 throw new URISyntaxException(dest, "User " + owner + " not found");
764 req.setAttribute(DESTINATION_OWNER_ATTRIBUTE, o);
765 dest = dest.substring(slash + 1);
767 // Chop the resource namespace part
768 dest = dest.substring(RequestHandler.PATH_FILES.length());
770 dest = dest.endsWith("/")? dest: dest + '/';
775 * Move the resource in the specified path to the trash bin.
777 * @param req the HTTP request
778 * @param resp the HTTP response
779 * @param path the path of the resource
780 * @throws IOException if an input/output error occurs
782 private void trashResource(HttpServletRequest req, HttpServletResponse resp, String path) throws IOException {
783 User user = getUser(req);
784 User owner = getOwner(req);
785 Object resource = null;
787 resource = getService().getResourceAtPath(owner.getId(), path, true);
788 } catch (ObjectNotFoundException e) {
789 resp.sendError(HttpServletResponse.SC_NOT_FOUND, path);
791 } catch (RpcException e) {
792 resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path);
797 if (resource instanceof FolderDTO) {
798 FolderDTO folder = (FolderDTO) resource;
799 getService().moveFolderToTrash(user.getId(), folder.getId());
801 FileHeaderDTO file = (FileHeaderDTO) resource;
802 getService().moveFileToTrash(user.getId(), file.getId());
804 } catch (InsufficientPermissionsException e) {
805 resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
806 } catch (ObjectNotFoundException e) {
807 resp.sendError(HttpServletResponse.SC_NOT_FOUND, e.getMessage());
808 } catch (RpcException e) {
809 resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path);
814 * Restore the resource in the specified path from the trash bin.
816 * @param req the HTTP request
817 * @param resp the HTTP response
818 * @param path the path of the resource
819 * @throws IOException if an input/output error occurs
821 private void restoreResource(HttpServletRequest req, HttpServletResponse resp, String path) throws IOException {
822 User user = getUser(req);
823 User owner = getOwner(req);
824 Object resource = null;
826 resource = getService().getResourceAtPath(owner.getId(), path, false);
827 } catch (ObjectNotFoundException e) {
828 resp.sendError(HttpServletResponse.SC_NOT_FOUND, path);
830 } catch (RpcException e) {
831 resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path);
836 if (resource instanceof FolderDTO) {
837 FolderDTO folder = (FolderDTO) resource;
838 getService().removeFolderFromTrash(user.getId(), folder.getId());
840 FileHeaderDTO file = (FileHeaderDTO) resource;
841 getService().removeFileFromTrash(user.getId(), file.getId());
843 } catch (InsufficientPermissionsException e) {
844 resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
845 } catch (ObjectNotFoundException e) {
846 resp.sendError(HttpServletResponse.SC_NOT_FOUND, e.getMessage());
847 } catch (RpcException e) {
848 resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path);
853 * Update the resource in the specified path.
855 * @param req the HTTP request
856 * @param resp the HTTP response
857 * @param path the path of the resource
858 * @throws IOException if an input/output error occurs
860 private void updateResource(HttpServletRequest req, HttpServletResponse resp, String path) throws IOException {
861 User user = getUser(req);
862 User owner = getOwner(req);
863 Object resource = null;
865 resource = getService().getResourceAtPath(owner.getId(), path, true);
866 } catch (ObjectNotFoundException e) {
867 resp.sendError(HttpServletResponse.SC_NOT_FOUND, path);
869 } catch (RpcException e) {
870 resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path);
873 //use utf-8 encoding for reading request
874 BufferedReader reader = new BufferedReader(new InputStreamReader(req.getInputStream(),"UTF-8"));
875 StringBuffer input = new StringBuffer();
877 JSONObject json = null;
878 while ((line = reader.readLine()) != null)
882 json = new JSONObject(input.toString());
883 if (logger.isDebugEnabled())
884 logger.debug("JSON update: " + json);
885 if (resource instanceof FolderDTO) {
886 FolderDTO folder = (FolderDTO) resource;
887 String name = json.optString("name");
888 if (!name.isEmpty()){
889 getService().modifyFolder(user.getId(), folder.getId(), name);
890 FolderDTO folderUpdated = getService().getFolder(user.getId(), folder.getId());
891 String parentUrl =URLDecoder.decode(getContextPath(req, true),"UTF-8");
892 String fpath = URLDecoder.decode(req.getPathInfo(), "UTF-8");
893 parentUrl = parentUrl.replaceAll(fpath, "");
894 if(!parentUrl.endsWith("/"))
895 parentUrl = parentUrl+"/";
896 parentUrl = parentUrl+folderUpdated.getOwner().getUsername()+PATH_FILES+folderUpdated.getPath();
897 resp.getWriter().println(parentUrl);
900 JSONArray permissions = json.optJSONArray("permissions");
901 if (permissions != null) {
902 Set<PermissionDTO> perms = parsePermissions(user, permissions);
903 getService().setFolderPermissions(user.getId(), folder.getId(), perms);
906 FileHeaderDTO file = (FileHeaderDTO) resource;
908 if (json.opt("name") != null)
909 name = json.optString("name");
910 JSONArray tagset = json.optJSONArray("tags");
912 StringBuffer t = new StringBuffer();
913 if (tagset != null) {
914 for (int i = 0; i < tagset.length(); i++)
915 t.append(tagset.getString(i) + ',');
918 if (name != null || tags != null)
919 getService().updateFile(user.getId(), file.getId(), name, tags);
921 JSONArray permissions = json.optJSONArray("permissions");
922 Set<PermissionDTO> perms = null;
923 if (permissions != null)
924 perms = parsePermissions(user, permissions);
925 Boolean readForAll = null;
926 if (json.opt("readForAll") != null)
927 readForAll = json.optBoolean("readForAll");
928 if (perms != null || readForAll != null)
929 getService().setFilePermissions(user.getId(), file.getId(), readForAll, perms);
931 if (json.opt("versioned") != null) {
932 boolean versioned = json.getBoolean("versioned");
933 getService().toggleFileVersioning(user.getId(), file.getId(), versioned);
936 } catch (JSONException e) {
937 resp.sendError(HttpServletResponse.SC_BAD_REQUEST, e.getMessage());
938 } catch (InsufficientPermissionsException e) {
939 resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
940 } catch (ObjectNotFoundException e) {
941 resp.sendError(HttpServletResponse.SC_NOT_FOUND, e.getMessage());
942 } catch (DuplicateNameException e) {
943 resp.sendError(HttpServletResponse.SC_CONFLICT, e.getMessage());
944 } catch (RpcException e) {
945 resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path);
950 * Helper method to convert a JSON array of permissions into a set of
951 * PermissionDTO objects.
953 * @param user the current user
954 * @param permissions the JSON array to parse
955 * @return the parsed set of permissions
956 * @throws JSONException if there was an error parsing the JSON object
957 * @throws RpcException if there was an error communicating with the EJB
958 * @throws ObjectNotFoundException if the user could not be found
960 private Set<PermissionDTO> parsePermissions(User user, JSONArray permissions)
961 throws JSONException, RpcException, ObjectNotFoundException {
962 if (permissions == null)
964 Set<PermissionDTO> perms = new HashSet<PermissionDTO>();
965 for (int i = 0; i < permissions.length(); i++) {
966 JSONObject j = permissions.getJSONObject(i);
967 PermissionDTO perm = new PermissionDTO();
968 perm.setModifyACL(j.optBoolean("modifyACL"));
969 perm.setRead(j.optBoolean("read"));
970 perm.setWrite(j.optBoolean("write"));
971 String permUser = j.optString("user");
972 if (!permUser.isEmpty()) {
973 User u = getService().findUser(permUser);
975 throw new ObjectNotFoundException("User " + permUser + " not found");
976 perm.setUser(u.getDTO());
978 String permGroup = j.optString("group");
979 if (!permGroup.isEmpty()) {
980 GroupDTO g = getService().getGroup(user.getId(), permGroup);
983 if (permUser.isEmpty() && permGroup.isEmpty() ||
984 permUser.isEmpty() && permGroup.isEmpty())
985 throw new JSONException("A permission must correspond to either a user or a group");
992 * Creates a new folder with the specified name under the folder in the provided path.
994 * @param req the HTTP request
995 * @param resp the HTTP response
996 * @param path the parent folder path
997 * @param folderName the name of the new folder
998 * @throws IOException if an input/output error occurs
1000 private void createFolder(HttpServletRequest req, HttpServletResponse resp, String path, String folderName) throws IOException {
1001 if (logger.isDebugEnabled())
1002 logger.debug("Creating folder " + folderName + " in '" + path);
1004 User user = getUser(req);
1005 User owner = getOwner(req);
1006 boolean exists = true;
1008 getService().getResourceAtPath(owner.getId(), path + folderName, false);
1009 } catch (ObjectNotFoundException e) {
1011 } catch (RpcException e) {
1012 resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path + folderName);
1017 resp.addHeader("Allow", METHOD_GET + ", " + METHOD_DELETE +
1018 ", " + METHOD_HEAD);
1019 resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
1025 parent = getService().getResourceAtPath(owner.getId(), path, true);
1026 } catch (ObjectNotFoundException e) {
1027 resp.sendError(HttpServletResponse.SC_CONFLICT);
1029 } catch (RpcException e) {
1030 resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path + folderName);
1034 if (parent instanceof FolderDTO) {
1035 FolderDTO folder = (FolderDTO) parent;
1036 getService().createFolder(user.getId(), folder.getId(), folderName);
1037 String newResource = getContextPath(req, true) + folderName;
1038 resp.setHeader("Location", newResource);
1039 resp.setContentType("text/plain");
1040 PrintWriter out = resp.getWriter();
1041 out.println(newResource);
1043 resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
1046 } catch (DuplicateNameException e) {
1047 resp.sendError(HttpServletResponse.SC_CONFLICT);
1049 } catch (InsufficientPermissionsException e) {
1050 resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
1052 } catch (ObjectNotFoundException e) {
1053 resp.sendError(HttpServletResponse.SC_CONFLICT);
1055 } catch (RpcException e) {
1056 resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path + folderName);
1059 resp.setStatus(HttpServletResponse.SC_CREATED);
1065 * @throws IOException
1066 * @throws FileNotFoundException
1068 void putResource(HttpServletRequest req, HttpServletResponse resp) throws IOException, FileNotFoundException {
1069 String path = getInnerPath(req, PATH_FILES);
1070 if (logger.isDebugEnabled())
1071 logger.debug("Updating resource: " + path);
1073 User user = getUser(req);
1074 User owner = getOwner(req);
1075 boolean exists = true;
1076 Object resource = null;
1077 FileHeaderDTO file = null;
1079 resource = getService().getResourceAtPath(owner.getId(), path, false);
1080 } catch (ObjectNotFoundException e) {
1082 } catch (RpcException e) {
1083 resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path);
1088 if (resource instanceof FileHeaderDTO)
1089 file = (FileHeaderDTO) resource;
1091 resp.sendError(HttpServletResponse.SC_CONFLICT, path + " is a folder");
1094 boolean result = true;
1096 // Temporary content file used to support partial PUT.
1097 File contentFile = null;
1099 Range range = parseContentRange(req, resp);
1101 InputStream resourceInputStream = null;
1103 // Append data specified in ranges to existing content for this
1104 // resource - create a temporary file on the local filesystem to
1105 // perform this operation.
1106 // Assume just one range is specified for now
1107 if (range != null) {
1109 contentFile = executePartialPut(req, range, path);
1110 } catch (RpcException e) {
1111 resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path);
1113 } catch (ObjectNotFoundException e) {
1114 resp.sendError(HttpServletResponse.SC_CONFLICT);
1116 } catch (InsufficientPermissionsException e) {
1117 resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
1120 resourceInputStream = new FileInputStream(contentFile);
1122 resourceInputStream = req.getInputStream();
1125 FolderDTO folder = null;
1126 Object parent = getService().getResourceAtPath(owner.getId(), getParentPath(path), true);
1127 if (!(parent instanceof FolderDTO)) {
1128 resp.sendError(HttpServletResponse.SC_CONFLICT);
1131 folder = (FolderDTO) parent;
1132 String name = getLastElement(path);
1133 String mimeType = context.getMimeType(name);
1134 // FIXME: Add attributes
1136 getService().updateFileContents(user.getId(), file.getId(), mimeType, resourceInputStream);
1138 getService().createFile(user.getId(), folder.getId(), name, mimeType, resourceInputStream);
1139 } catch(ObjectNotFoundException e) {
1141 } catch (RpcException e) {
1142 resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path);
1144 } catch (IOException e) {
1145 resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path);
1147 } catch (GSSIOException e) {
1148 resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path);
1150 } catch (DuplicateNameException e) {
1151 resp.sendError(HttpServletResponse.SC_CONFLICT);
1153 } catch (InsufficientPermissionsException e) {
1154 resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
1156 } catch (QuotaExceededException e) {
1157 resp.sendError(HttpServletResponse.SC_REQUEST_ENTITY_TOO_LARGE, e.getMessage());
1163 resp.setStatus(HttpServletResponse.SC_NO_CONTENT);
1165 resp.setStatus(HttpServletResponse.SC_CREATED);
1167 resp.sendError(HttpServletResponse.SC_CONFLICT);
1171 * Delete a resource.
1173 * @param req The servlet request we are processing
1174 * @param resp The servlet response we are processing
1175 * @throws IOException if the response cannot be sent
1177 void deleteResource(HttpServletRequest req, HttpServletResponse resp) throws IOException {
1178 String path = getInnerPath(req, PATH_FILES);
1179 if (logger.isDebugEnabled())
1180 logger.debug("Deleting resource '" + path);
1181 path = URLDecoder.decode(path, "UTF-8");
1182 User user = getUser(req);
1183 User owner = getOwner(req);
1184 boolean exists = true;
1185 Object object = null;
1187 object = getService().getResourceAtPath(owner.getId(), path, false);
1188 } catch (ObjectNotFoundException e) {
1190 } catch (RpcException e) {
1191 resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
1196 resp.sendError(HttpServletResponse.SC_NOT_FOUND);
1200 FolderDTO folder = null;
1201 FileHeaderDTO file = null;
1202 if (object instanceof FolderDTO)
1203 folder = (FolderDTO) object;
1205 file = (FileHeaderDTO) object;
1209 getService().deleteFile(user.getId(), file.getId());
1210 } catch (InsufficientPermissionsException e) {
1211 resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
1213 } catch (ObjectNotFoundException e) {
1214 // Although we had already found the object, it was
1215 // probably deleted from another thread.
1216 resp.sendError(HttpServletResponse.SC_NOT_FOUND);
1218 } catch (RpcException e) {
1219 resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
1222 else if (folder != null)
1224 getService().deleteFolder(user.getId(), folder.getId());
1225 } catch (InsufficientPermissionsException e) {
1226 resp.setStatus(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
1228 } catch (ObjectNotFoundException e) {
1229 resp.setStatus(HttpServletResponse.SC_NOT_FOUND);
1231 } catch (RpcException e) {
1232 resp.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
1235 resp.setStatus(HttpServletResponse.SC_NO_CONTENT);
1240 * Return an InputStream to a JSON representation of the contents
1241 * of this directory.
1243 * @param user the user that made the request
1244 * @param folder the specified directory
1245 * @param folderUrl the URL of the folder
1246 * @return an input stream with the rendered contents
1247 * @throws IOException if the response cannot be sent
1248 * @throws ServletException
1249 * @throws InsufficientPermissionsException if the user does not have
1250 * the necessary privileges to read the directory
1252 private InputStream renderJson(User user, FolderDTO folder, String folderUrl) throws IOException,
1253 ServletException, InsufficientPermissionsException {
1254 JSONObject json = new JSONObject();
1256 json.put("name", folder.getName()).
1257 put("owner", folder.getOwner().getUsername()).
1258 put("createdBy", folder.getAuditInfo().getCreatedBy().getUsername()).
1259 put("creationDate", folder.getAuditInfo().getCreationDate().getTime()).
1260 put("deleted", folder.isDeleted());
1261 if (folder.getParent() != null)
1262 json.put("parent", folder.getParent().getURI());
1263 if (folder.getAuditInfo().getModifiedBy() != null)
1264 json.put("modifiedBy", folder.getAuditInfo().getModifiedBy().getUsername()).
1265 put("modificationDate", folder.getAuditInfo().getModificationDate().getTime());
1266 List<JSONObject> subfolders = new ArrayList<JSONObject>();
1267 for (FolderDTO f: folder.getSubfolders())
1268 if (!f.isDeleted()) {
1269 JSONObject j = new JSONObject();
1270 j.put("name", f.getName()).
1271 put("uri", folderUrl + URLEncoder.encode(f.getName(), "UTF-8"));
1274 json.put("folders", subfolders);
1275 List<JSONObject> files = new ArrayList<JSONObject>();
1276 List<FileHeaderDTO> fileHeaders = getService().getFiles(user.getId(), folder.getId());
1277 for (FileHeaderDTO f: fileHeaders) {
1278 JSONObject j = new JSONObject();
1279 j.put("name", f.getName()).
1280 put("owner", f.getOwner().getUsername()).
1281 put("deleted", f.isDeleted()).
1282 put("version", f.getVersion()).
1283 put("size", f.getFileSize()).
1284 put("creationDate", f.getAuditInfo().getCreationDate().getTime()).
1285 put("uri", folderUrl + URLEncoder.encode(f.getName(), "UTF-8"));
1288 json.put("files", files);
1289 Set<PermissionDTO> perms = getService().getFolderPermissions(user.getId(), folder.getId());
1290 json.put("permissions", renderJson(perms));
1291 } catch (JSONException e) {
1292 throw new ServletException(e);
1293 } catch (ObjectNotFoundException e) {
1294 throw new ServletException(e);
1295 } catch (RpcException e) {
1296 throw new ServletException(e);
1299 // Prepare a writer to a buffered area
1300 ByteArrayOutputStream stream = new ByteArrayOutputStream();
1301 OutputStreamWriter osWriter = new OutputStreamWriter(stream, "UTF8");
1302 PrintWriter writer = new PrintWriter(osWriter);
1304 // Return an input stream to the underlying bytes
1305 writer.write(json.toString());
1307 return new ByteArrayInputStream(stream.toByteArray());
1311 * Return a String with a JSON representation of the metadata
1312 * of the specified file. If an old file body is provided, then
1313 * the metadata of that particular version will be returned.
1315 * @param user the user that made the request
1316 * @param file the specified file header
1317 * @param oldBody the version number
1318 * @return the JSON-encoded file
1319 * @throws ServletException
1320 * @throws InsufficientPermissionsException if the user does not have
1321 * the necessary privileges to read the directory
1323 private String renderJson(User user, FileHeaderDTO file, FileBodyDTO oldBody)
1324 throws IOException, ServletException, InsufficientPermissionsException {
1325 JSONObject json = new JSONObject();
1327 //need to encode file name in order to properly display it in gwt
1328 json.put("name", URLEncoder.encode(file.getName(),"UTF-8")).
1329 put("owner", file.getOwner().getUsername()).
1330 put("versioned", file.isVersioned()).
1331 put("version", oldBody != null ? oldBody.getVersion() : file.getVersion()).
1332 put("readForAll", file.isReadForAll()).
1333 put("tags", file.getTags()).
1334 put("folder", file.getFolder().getURI()).
1335 put("deleted", file.isDeleted());
1336 if (oldBody != null)
1337 json.put("createdBy", oldBody.getAuditInfo().getCreatedBy().getUsername()).
1338 put("creationDate", oldBody.getAuditInfo().getCreationDate().getTime()).
1339 put("modifiedBy", oldBody.getAuditInfo().getModifiedBy().getUsername()).
1340 put("modificationDate", oldBody.getAuditInfo().getModificationDate().getTime());
1342 json.put("createdBy", file.getAuditInfo().getCreatedBy().getUsername()).
1343 put("creationDate", file.getAuditInfo().getCreationDate().getTime()).
1344 put("modifiedBy", file.getAuditInfo().getModifiedBy().getUsername()).
1345 put("modificationDate", file.getAuditInfo().getModificationDate().getTime());
1346 Set<PermissionDTO> perms = getService().getFilePermissions(user.getId(), file.getId());
1347 json.put("permissions", renderJson(perms));
1348 } catch (JSONException e) {
1349 throw new ServletException(e);
1350 } catch (ObjectNotFoundException e) {
1351 throw new ServletException(e);
1352 } catch (RpcException e) {
1353 throw new ServletException(e);
1356 return json.toString();
1360 * Return a String with a JSON representation of the
1361 * specified set of permissions.
1363 * @param permissions the set of permissions
1364 * @return the JSON-encoded object
1365 * @throws JSONException
1367 private JSONArray renderJson(Set<PermissionDTO> permissions) throws JSONException {
1368 JSONArray perms = new JSONArray();
1369 for (PermissionDTO p: permissions) {
1370 JSONObject permission = new JSONObject();
1371 permission.put("read", p.hasRead()).put("write", p.hasWrite()).put("modifyACL", p.hasModifyACL());
1372 if (p.getUser() != null)
1373 permission.put("user", p.getUser().getUsername());
1374 if (p.getGroup() != null)
1375 permission.put("group", p.getGroup().getName());
1376 perms.put(permission);
1382 * Retrieves the user who owns the destination namespace, for a
1383 * copy or move request.
1385 * @param req the HTTP request
1386 * @return the owner of the namespace
1388 protected User getDestinationOwner(HttpServletRequest req) {
1389 return (User) req.getAttribute(DESTINATION_OWNER_ATTRIBUTE);
1393 * A helper inner class for updating the progress status of a file upload.
1397 public static class StatusProgressListener implements ProgressListener {
1398 private int percentLogged = 0;
1399 private long bytesTransferred = 0;
1401 private long fileSize = -100;
1403 private long tenKBRead = -1;
1405 private Long userId;
1407 private String filename;
1409 private ExternalAPI service;
1411 public StatusProgressListener(ExternalAPI aService) {
1416 * Modify the userId.
1418 * @param aUserId the userId to set
1420 public void setUserId(Long aUserId) {
1425 * Modify the filename.
1427 * @param aFilename the filename to set
1429 public void setFilename(String aFilename) {
1430 filename = aFilename;
1433 public void update(long bytesRead, long contentLength, int items) {
1434 //monitoring per percent of bytes uploaded
1435 bytesTransferred = bytesRead;
1436 if (fileSize != contentLength)
1437 fileSize = contentLength;
1438 int percent = new Long(bytesTransferred * 100 / fileSize).intValue();
1440 if (percent < 5 || percent % TRACK_PROGRESS_PERCENT == 0 )
1441 if (percent != percentLogged){
1442 percentLogged = percent;
1444 if (userId != null && filename != null)
1445 service.createFileUploadProgress(userId, filename, bytesTransferred, fileSize);
1446 } catch (ObjectNotFoundException e) {
1447 // Swallow the exception since it is going to be caught
1448 // by previously called methods