| /******************************************************************************* |
| * 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 implementaiton |
| ******************************************************************************/ |
| package org.eclipse.emf.emfstore.internal.client.model.filetransfer; |
| |
| import java.io.File; |
| import java.text.MessageFormat; |
| import java.util.Observable; |
| import java.util.Observer; |
| |
| import org.eclipse.emf.emfstore.internal.client.model.ProjectSpace; |
| import org.eclipse.emf.emfstore.internal.common.model.util.ModelUtil; |
| import org.eclipse.emf.emfstore.internal.server.exceptions.FileNotOnServerException; |
| import org.eclipse.emf.emfstore.internal.server.exceptions.FileTransferException; |
| import org.eclipse.emf.emfstore.internal.server.model.FileIdentifier; |
| |
| import com.google.common.base.Optional; |
| |
| /** |
| * An object of this class is returned from any workspace method that starts a |
| * file transfer. It provides information about this file transfer and allows to |
| * add Progressmonitors and Observers |
| * |
| * @author jfinis |
| */ |
| public final class FileDownloadStatus { |
| |
| /* PRIVATE MEMBERS */ |
| private final ProjectSpace transferringProjectSpace; |
| private final Observable finishedObservable = new Obs(); |
| private final Observable failedObservable = new Obs(); |
| private final Observable cancelledObservable = new Obs(); |
| |
| private final FileTransferStatistics statistics = new FileTransferStatistics(this); |
| private Status status; |
| private File transferredFile; |
| private final FileIdentifier id; |
| private Exception exception; |
| |
| private FileDownloadStatus(ProjectSpace transferringProjectSpace, FileIdentifier id, Status status, |
| File transferredFile) { |
| this.transferringProjectSpace = transferringProjectSpace; |
| this.status = status; |
| this.id = id; |
| this.transferredFile = transferredFile; |
| } |
| |
| /** |
| * Return value of the getStatus method. |
| * |
| * @author jfinis |
| */ |
| public enum Status { |
| /** |
| * The file transfer was not yet started. |
| */ |
| NOT_STARTED, |
| |
| /** |
| * The transfer is actively transfering data. |
| */ |
| TRANSFERING, |
| |
| /** |
| * The transfer is finished. |
| */ |
| FINISHED, |
| |
| /** |
| * The transfer was cancelled. |
| */ |
| CANCELLED, |
| |
| /** |
| * If an exception was thrown. The exception can then be retrieved by |
| * calling getException() |
| */ |
| FAILED |
| } |
| |
| /** |
| * Constant signaling "not available". |
| */ |
| public static final int NOT_AVAILABLE = -1; |
| |
| /** |
| * Returns the identifier of the file to be downloaded. |
| * |
| * @return the identifier |
| */ |
| public FileIdentifier getFileIdentifier() { |
| return id; |
| } |
| |
| /** |
| * This is the ultimate method for checking in which "stage" a file transfer |
| * currently is. i.e. if it is still waiting, transferring or finished. See |
| * the enum Status for possible values. |
| * |
| * @return the stage in which the file transfer currently is |
| */ |
| public Status getStatus() { |
| return status; |
| } |
| |
| /** |
| * Adds an observer that is notified once the transfer is finished. If the |
| * transfer is already finished at the moment when this method is called, |
| * then the observer is instantly notified. The object that is passed to the |
| * observer's update message is this instance of FileTransferStatus |
| * |
| * @param o |
| * an observer to be notified when the transfer is finished |
| */ |
| public void addTransferFinishedObserver(Observer o) { |
| // Add |
| finishedObservable.addObserver(o); |
| |
| // Instantly notify if the transfer is already finished |
| if (isTransferFinished()) { |
| o.update(finishedObservable, this); |
| } |
| } |
| |
| private void addTransferCancelledObserver(Observer o) { |
| // Add |
| cancelledObservable.addObserver(o); |
| |
| // Instantly notify if the transfer is already finished |
| if (status == Status.CANCELLED) { |
| o.update(cancelledObservable, this); |
| } |
| } |
| |
| /** |
| * Adds an observer which is notified if the transfer fails due to an |
| * exception. The getException method can then be used to retrieve the |
| * thrown exception. |
| * |
| * @param o |
| * an observer that is notified if the transfer fails |
| */ |
| public void addTransferFailedObserver(Observer o) { |
| |
| failedObservable.addObserver(o); |
| |
| // If the transfer has already failed, notify instantly |
| if (status == Status.FAILED) { |
| o.update(failedObservable, this); |
| } |
| } |
| |
| /** |
| * Adds a default observer that is notified when the transfer fails. This |
| * observer logs the failure. |
| */ |
| public void addDefaultFailObserver() { |
| addTransferFailedObserver(new Observer() { |
| |
| public void update(Observable arg0, Object arg1) { |
| final FileDownloadStatus status = (FileDownloadStatus) arg1; |
| final Exception e = status.getException(); |
| ModelUtil.logException(Messages.FileDownloadStatus_TransferFailed, e); |
| |
| } |
| }); |
| } |
| |
| /** |
| * Returns true if the transfer is finished. |
| * |
| * @return Whether the transfer is finished |
| */ |
| public boolean isTransferFinished() { |
| return status == Status.FINISHED; |
| } |
| |
| /** |
| * Whether the file to be downloaded is on server. |
| * |
| * @return {@code true} if the file is not on the server, {@code false} otherwise |
| */ |
| public boolean isNotOnServer() { |
| return status == Status.FAILED && getException() instanceof FileNotOnServerException; |
| } |
| |
| /** |
| * Gets the statistics object for this file transfer, which provides useful |
| * information, especially while the transfer is active. It provides |
| * information like the file size, the amount of already transfered bytes |
| * the estimated remaining time and more. |
| * |
| * @return the statistics for this transfer |
| */ |
| public FileTransferStatistics getStatistics() { |
| return statistics; |
| } |
| |
| /** |
| * Returns the transferred file. If this is a download, then the file is |
| * only available, if the download is finished. If it is not finished yet, |
| * an file transfer exception is thrown. |
| * |
| * @return the transferred file or a FileTransferException exception if the |
| * file is not yet transferred. |
| * @throws FileTransferException |
| * if the file is not yet fully transferred |
| */ |
| public File getTransferredFile() throws FileTransferException { |
| if (isNotOnServer()) { |
| throw new FileNotOnServerException(MessageFormat.format( |
| Messages.FileDownloadStatus_FileNotOnServer, |
| id.getIdentifier())); |
| } else if (!isTransferFinished()) { |
| throw new FileTransferException(MessageFormat.format( |
| Messages.FileDownloadStatus_TransferNotFinishedYet, |
| id.getIdentifier())); |
| } |
| return transferredFile; |
| } |
| |
| /** |
| * Similar to {@link #getTransferredFile()}, but this method blocks the |
| * client thread. |
| * |
| * @param block |
| * whether to block or not |
| * @return the transferred file |
| * @throws FileTransferException |
| * in case of an error during transfer |
| */ |
| public File getTransferredFile(boolean block) throws FileTransferException { |
| if (!isTransferFinished() && block) { |
| /** |
| * TODO: Double-check this code |
| */ |
| final Observer observer = new Observer() { |
| public void update(Observable arg0, Object arg1) { |
| synchronized (this) { |
| // TODO: notify should be sufficient here |
| notifyAll(); |
| } |
| } |
| }; |
| addTransferFailedObserver(observer); |
| addTransferFinishedObserver(observer); |
| addTransferCancelledObserver(observer); |
| try { |
| synchronized (observer) { |
| observer.wait(); |
| } |
| } catch (final InterruptedException e) { |
| throw new FileTransferException(Messages.FileDownloadStatus_BlockedGetInitFailed, e); |
| } |
| } |
| return getTransferredFile(); |
| } |
| |
| /** |
| * Returns the project space which started this file transfer. |
| * |
| * @return the project space owning this file transfer |
| */ |
| public Optional<ProjectSpace> getTransferringProjectSpace() { |
| return Optional.fromNullable(transferringProjectSpace); |
| } |
| |
| /** |
| * Returns the exception that caused the download to fail, if the status == |
| * FAILED. Otherwise, returns null |
| * |
| * @return the exception that caused the failure or null if not failed |
| */ |
| public Exception getException() { |
| return exception; |
| } |
| |
| /** |
| * Called internally if the transfer is canceled. |
| */ |
| void transferCancelled() { |
| if (status == Status.FINISHED) { |
| return; |
| } |
| statistics.registerStop(); |
| status = Status.CANCELLED; |
| cancelledObservable.notifyObservers(this); |
| } |
| |
| /** |
| * Called internally when the transfer is started. |
| * |
| * @param fileSize |
| * the file size |
| * @throws FileTransferException |
| * if the transfer has already been started |
| */ |
| void transferStarted(int fileSize) throws FileTransferException { |
| if (status != Status.NOT_STARTED) { |
| throw new FileTransferException(Messages.FileDownloadStatus_CannotStartJob + status.name()); |
| } |
| statistics.registerStart(fileSize); |
| status = Status.TRANSFERING; |
| } |
| |
| /** |
| * Called internally when the transfer is finished. |
| * |
| * @param result |
| * the resulting file in the cache |
| */ |
| void transferFinished(File result) { |
| status = Status.FINISHED; |
| transferredFile = result; |
| statistics.registerStop(); |
| finishedObservable.notifyObservers(this); |
| } |
| |
| /** |
| * Called internally if the transfer fails. |
| * |
| * @param ex |
| * the exception that caused the failure |
| */ |
| void transferFailed(Exception ex) { |
| status = Status.FAILED; |
| statistics.registerStop(); |
| exception = ex; |
| failedObservable.notifyObservers(this); |
| } |
| |
| /** |
| * This observerable is always changed, so notifying will always notify |
| * Observers. |
| * |
| * @author jfinis |
| */ |
| private static class Obs extends Observable { |
| |
| @Override |
| public void notifyObservers(Object arg) { |
| super.setChanged(); |
| super.notifyObservers(arg); |
| } |
| } |
| |
| /** |
| * This factory class creates the different possible download stats. |
| * |
| * @author jfinis |
| */ |
| static class Factory { |
| |
| /** |
| * Creates an already finished download status (status FINISHED). |
| * |
| * @param p |
| * the project space containing the download |
| * @param id |
| * the identifier of the download |
| * @param transferredFile |
| * the file where the download can be found |
| * @return the created status object |
| */ |
| public static FileDownloadStatus createAlreadyFinished(ProjectSpace p, FileIdentifier id, |
| File transferredFile) { |
| return new FileDownloadStatus(p, id, Status.FINISHED, transferredFile); |
| } |
| |
| /** |
| * Creates a new download status which is initially in the NOT_STARTED |
| * state. |
| * |
| * @param p |
| * the project space containing the download |
| * @param id |
| * the identifier of the file to be downloaded |
| * @return the created status object |
| */ |
| public static FileDownloadStatus createNew(ProjectSpace p, FileIdentifier id) { |
| return new FileDownloadStatus(p, id, Status.NOT_STARTED, null); |
| } |
| } |
| |
| } |