| /******************************************************************************* |
| * Copyright (c) 2008-2011 Chair for Applied Software Engineering, |
| * Technische Universitaet Muenchen. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * Jan Finis - initial API and implementation |
| ******************************************************************************/ |
| package org.eclipse.emf.emfstore.internal.client.model.filetransfer; |
| |
| import java.io.File; |
| import java.io.IOException; |
| |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.emf.common.util.EList; |
| import org.eclipse.emf.emfstore.internal.client.model.impl.ProjectSpaceBase; |
| import org.eclipse.emf.emfstore.internal.client.model.util.EMFStoreCommand; |
| import org.eclipse.emf.emfstore.internal.client.model.util.WorkspaceUtil; |
| import org.eclipse.emf.emfstore.internal.server.exceptions.FileTransferException; |
| import org.eclipse.emf.emfstore.internal.server.model.FileIdentifier; |
| import org.eclipse.emf.emfstore.internal.server.model.ModelFactory; |
| |
| /** |
| * The main managing class on the client side for file transfers. Each project |
| * space has an associated FileTransferManager. All file-related request from |
| * the project space are delegated to that manager. The manager provides methods |
| * to add files, get files and retrieve additional information about files. |
| * |
| * @author jfinis |
| */ |
| public class FileTransferManager { |
| |
| /** |
| * The associated cache manager. |
| */ |
| private final FileTransferCacheManager cacheManager; |
| |
| /** |
| * The associated project space. |
| */ |
| private final ProjectSpaceBase projectSpace; |
| |
| /** |
| * Constructor that creates a file transfer manager for a specific project |
| * space. Only to be called in the init of a project space! |
| * |
| * @param projectSpaceImpl |
| * the project space to which this transfer manager belongs |
| */ |
| public FileTransferManager(ProjectSpaceBase projectSpaceImpl) { |
| cacheManager = new FileTransferCacheManager(projectSpaceImpl); |
| projectSpace = projectSpaceImpl; |
| } |
| |
| /** |
| * Adds a file to be transferred (uploaded). |
| * |
| * @param file |
| * the file to be transferred |
| * |
| * @return the {@link FileIdentifier} that associates the file with its ID |
| * |
| * @throws FileTransferException in case the {@code file} is either {@code null}, a directory |
| * or does not exist |
| */ |
| public FileIdentifier addFile(File file) throws FileTransferException { |
| return addFile(file, null); |
| } |
| |
| /** |
| * Adds a file to be transferred (uploaded). |
| * |
| * @param file |
| * the file to be transferred |
| * @param id |
| * the ID that will be associated with the file being uploaded |
| * |
| * @return the {@link FileIdentifier} that associates the file with its ID |
| * |
| * @throws FileTransferException in case the {@code file} is either {@code null}, a directory |
| * or does not exist |
| */ |
| public FileIdentifier addFile(File file, String id) throws FileTransferException { |
| if (file == null) { |
| throw new FileTransferException("File to be added is null!"); |
| } |
| if (file.isDirectory()) { |
| throw new FileTransferException("Can only upload files! File is a directory.\nPath:" |
| + file.getAbsolutePath()); |
| } |
| if (!file.exists()) { |
| throw new FileTransferException("The file to be uploaded does not exist.\nPath:" + file.getAbsolutePath()); |
| } |
| |
| // Create the file identifier |
| final FileIdentifier identifier = ModelFactory.eINSTANCE.createFileIdentifier(); |
| if (id != null) { |
| identifier.setIdentifier(id); |
| } |
| |
| // Move file to cache |
| try { |
| cacheManager.cacheFile(file, identifier); |
| } catch (final IOException e) { |
| throw new FileTransferException("An exception occurred while trying to cache an incoming file:\n" |
| + e.getMessage(), e); |
| } |
| |
| // Add the file to the queue for files that should be |
| // transmitted when a commit is done |
| addToCommitQueue(identifier); |
| |
| return identifier; |
| } |
| |
| /** |
| * Adds a file to the queue of pending uploads. |
| * |
| * @param identifier |
| */ |
| private void addToCommitQueue(final FileIdentifier identifier) { |
| for (final FileIdentifier f : projectSpace.getWaitingUploads()) { |
| if (f.getIdentifier().equals(identifier.getIdentifier())) { |
| return; |
| } |
| |
| } |
| new EMFStoreCommand() { |
| @Override |
| protected void doRun() { |
| projectSpace.getWaitingUploads().add(identifier); |
| projectSpace.saveProjectSpaceOnly(); |
| } |
| }.run(true, projectSpace.getContentEditingDomain()); |
| |
| } |
| |
| /** |
| * Uploads all files in the commit queue. Is called upon committing the |
| * project space. |
| * |
| * @param progress |
| * progress monitor |
| */ |
| public void uploadQueuedFiles(IProgressMonitor progress) { |
| try { |
| final EList<FileIdentifier> uploads = projectSpace.getWaitingUploads(); |
| while (!uploads.isEmpty()) { |
| final FileIdentifier fi = uploads.get(0); |
| |
| // Is the file present in cache? |
| // (it should be, unless there is a severe bug or the user has |
| // manually deleted it) |
| if (!cacheManager.hasCachedFile(fi)) { |
| WorkspaceUtil.logException("The file with the id " + fi.getIdentifier() |
| + " was not found in cache. It was queued for upload but" |
| + " is now removed from the queue. The file will NOT be on the server.", null); |
| // Remove from commit queue |
| new EMFStoreCommand() { |
| @Override |
| protected void doRun() { |
| projectSpace.getWaitingUploads().remove(fi); |
| projectSpace.saveProjectSpaceOnly(); |
| } |
| }.run(true, projectSpace.getContentEditingDomain()); |
| continue; |
| |
| } |
| final FileUploadJob job = new FileUploadJob(this, fi, true); |
| final IStatus result = job.run(progress); |
| |
| if (job.getException() != null) { |
| WorkspaceUtil.logException("An exception occurred while trying to upload a file to the server", |
| job.getException()); |
| return; |
| } |
| |
| if (result.getCode() == IStatus.CANCEL) { |
| return; |
| } |
| } |
| } catch (final FileTransferException e) { |
| WorkspaceUtil.logException("Uploading the waiting files did not succeed", e); |
| } |
| } |
| |
| /** |
| * Returns the download status of the file that is associated with the given {@link FileIdentifier}. |
| * |
| * @param fileIdentifier |
| * the file identifier whose download status should be retrieved |
| * |
| * @return the download status of the file |
| * |
| * @throws FileTransferException in case the given file identifier is {@code null} |
| */ |
| public FileDownloadStatus getFile(FileIdentifier fileIdentifier, boolean isTriggeredByUI) |
| throws FileTransferException { |
| |
| if (fileIdentifier == null) { |
| throw new FileTransferException("File identifier may not be null,"); |
| } |
| |
| // If the file is cached locally, get it |
| if (cacheManager.hasCachedFile(fileIdentifier)) { |
| return FileDownloadStatus.Factory.createAlreadyFinished(projectSpace, fileIdentifier, |
| cacheManager.getCachedFile(fileIdentifier)); |
| } |
| |
| // Otherwise, start a download |
| return startDownload(fileIdentifier, isTriggeredByUI); |
| } |
| |
| /** |
| * Starts a download of a specific file. Returns a status object that can be |
| * queried to check how far the download is. |
| * |
| * @param fileIdentifier |
| * the file to be downloaded |
| * @param monitor |
| * a progress monitor for the download |
| * @return the status |
| */ |
| private FileDownloadStatus startDownload(FileIdentifier fileIdentifier, boolean isTriggeredByUI) { |
| final FileDownloadStatus fds = FileDownloadStatus.Factory.createNew(projectSpace, fileIdentifier); |
| // TODO Check if true is correct here |
| final FileDownloadJob job = new FileDownloadJob(fds, this, fileIdentifier, isTriggeredByUI); |
| job.schedule(); |
| return fds; |
| } |
| |
| /** |
| * Returns the cache manager. |
| * |
| * @return the associated cache manager |
| */ |
| FileTransferCacheManager getCache() { |
| return cacheManager; |
| } |
| |
| /** |
| * Gets the index of a waiting file upload in the upload queue or -1 if this |
| * upload is not in the queue. |
| * |
| * @param fileId |
| * the index to be looked up in the queue |
| * @return the index in the queue or -1 |
| */ |
| private int getWaitingUploadIndex(FileIdentifier fileId) { |
| if (fileId == null) { |
| return -1; |
| } |
| |
| int i = 0; |
| /* |
| * We need to loop over the pending uploads here. This is because we |
| * cannot use .remove(fileId) because remove uses equals to check for |
| * the element. Equals is not well-defined for EObjects, so we cannot |
| * use it here. |
| */ |
| for (final FileIdentifier upload : projectSpace.getWaitingUploads()) { |
| // This is our equals: Compare the strings! |
| if (upload.getIdentifier().equals(fileId.getIdentifier())) { |
| return i; |
| } |
| i++; |
| } |
| return -1; |
| } |
| |
| /** |
| * Removes a waiting upload from the queue. Throws a file transfer exception |
| * if the fil is not in the list. |
| * |
| * @param fileId |
| * the file to remove from the queue |
| * @throws FileTransferException |
| * if the file is not in the queue |
| */ |
| void removeWaitingUpload(FileIdentifier fileId) throws FileTransferException { |
| final int index = getWaitingUploadIndex(fileId); |
| if (index != -1) { |
| projectSpace.getWaitingUploads().remove(index); |
| projectSpace.saveProjectSpaceOnly(); |
| |
| } else { |
| // Not found in list? exception! |
| throw new FileTransferException("Could not remove pending upload with id " + fileId |
| + ": No upload with that id is pending"); |
| } |
| } |
| |
| /** |
| * Return if a specific file is in the pending upload queue. |
| * |
| * @param fileIdentifier |
| * the file to be looked up |
| * @return true, if the file is in the queue |
| */ |
| public boolean hasWaitingUpload(FileIdentifier fileIdentifier) { |
| return getWaitingUploadIndex(fileIdentifier) != -1; |
| } |
| |
| /** |
| * Cancels a pending upload. That means that the upload is removed from the |
| * queue and deleted from cache. If the file is not in the queue, nothing is |
| * done. If it is in the queue but not in the cache, then it is only removed |
| * from the queue. |
| * |
| * @param fileIdentifier |
| * the file to be canceled |
| */ |
| public void cancelPendingUpload(FileIdentifier fileIdentifier) { |
| // Remove from the waiting queue |
| try { |
| removeWaitingUpload(fileIdentifier); |
| } catch (final FileTransferException e) { |
| return; |
| } |
| |
| // Remove from cache |
| cacheManager.removeCachedFile(fileIdentifier); |
| } |
| |
| /** |
| * returns a file information object for a specific file identifier. |
| * |
| * @param fileIdentifier |
| * the identifier |
| * @return the file information for that identifier |
| */ |
| public FileInformation getFileInfo(FileIdentifier fileIdentifier) { |
| return new FileInformation(fileIdentifier, this); |
| } |
| |
| /** |
| * Returns the associated project space. |
| * |
| * @return the project to which this file transfer manager belongs to |
| */ |
| ProjectSpaceBase getProjectSpace() { |
| return projectSpace; |
| } |
| |
| } |