blob: 1fd44c3cdcbcfd4b1defef3d9c91073a7c69bd83 [file] [log] [blame]
/*******************************************************************************
* 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);
}
}
}