package freenet.clients.fcp;
import java.util.Date;
import freenet.client.async.ClientRequester;
import freenet.client.events.SplitfileProgressEvent;
import freenet.clients.fcp.ClientRequest.Persistence;
import freenet.keys.FreenetURI;
import freenet.l10n.NodeL10n;
import freenet.support.CurrentTimeUTC;
/** The status of a request. Cached copy i.e. can be accessed outside the database thread
* even for a persistent request.
*
* Methods that change the status should be package-local, and called either
* within freenet.clients.fcp, or via RequestStatusCache. Hence we should be
* able to lock the RequestStatusCache and be confident that nothing is going
* to change under us.
*
* @author toad
*/
public abstract class RequestStatus implements Cloneable {
private final String identifier;
private boolean hasStarted;
private boolean hasFinished;
private boolean hasSucceeded;
private short priority;
private int totalBlocks;
private int minBlocks;
private int fetchedBlocks;
/** @see ClientRequester#latestSuccess */
private Date latestSuccess;
private int fatallyFailedBlocks;
private int failedBlocks;
/* @see ClientRequester#latestFailure */
private Date latestFailure;
private boolean isTotalFinalized;
private final Persistence persistence;
/** The download or upload has finished.
* @param success Did it succeed? */
synchronized void setFinished(boolean success) {
this.latestSuccess = CurrentTimeUTC.get();
this.hasFinished = true;
this.hasSucceeded = success;
this.hasStarted = true;
this.isTotalFinalized = true;
}
synchronized void restart(boolean started) {
// See ClientRequester.getLatestSuccess() for why this defaults to current time.
this.latestSuccess = CurrentTimeUTC.get();
this.hasFinished = false;
this.hasSucceeded = false;
this.hasStarted = started;
this.isTotalFinalized = false;
}
/** Constructor for creating a status from a request that has already started, e.g. on
* startup. We will also create status when a request is created. */
RequestStatus(String identifier, Persistence persistence, boolean started, boolean finished,
boolean success, int total, int min, int fetched, Date latestSuccess, int fatal,
int failed, Date latestFailure, boolean totalFinalized, short prio) {
this.identifier = identifier;
this.hasStarted = started;
this.hasFinished = finished;
this.hasSucceeded = success;
this.priority = prio;
this.totalBlocks = total;
this.minBlocks = min;
this.fetchedBlocks = fetched;
// clone() because Date is mutable
this.latestSuccess
= latestSuccess != null ? (Date)latestSuccess.clone() : null;
this.fatallyFailedBlocks = fatal;
this.failedBlocks = failed;
// clone() because Date is mutable
this.latestFailure
= latestFailure != null ? (Date)latestFailure.clone() : null;
this.isTotalFinalized = totalFinalized;
this.persistence = persistence;
}
public boolean hasSucceeded() {
return hasSucceeded;
}
public boolean hasFinished() {
return hasFinished;
}
public short getPriority() {
return priority;
}
public String getIdentifier() {
return identifier;
}
public int getTotalBlocks() {
return totalBlocks;
}
public boolean isTotalFinalized() {
return isTotalFinalized;
}
public int getMinBlocks() {
return minBlocks;
}
public int getFetchedBlocks() {
return fetchedBlocks;
}
/** @deprecated Use {@link #getLastSuccess()} instead. */
@Deprecated
public long getLastActivity() {
return latestSuccess != null ? latestSuccess.getTime() : 0;
}
public Date getLastSuccess() {
// clone() because Date is mutable.
return latestSuccess != null ? (Date)latestSuccess.clone() : null;
}
public Date getLastFailure() {
// clone() because Date is mutable.
return latestFailure != null ? (Date)latestFailure.clone() : null;
}
/** Get the original URI for a fetch or the final URI for an insert. */
public abstract FreenetURI getURI();
public abstract long getDataSize();
public boolean isPersistentForever() {
return persistence == Persistence.FOREVER;
}
public boolean isPersistent() {
return persistence != Persistence.CONNECTION;
}
public int getFatalyFailedBlocks() {
return fatallyFailedBlocks;
}
public int getFailedBlocks() {
return failedBlocks;
}
public boolean isStarted() {
return hasStarted;
}
public abstract String getFailureReason(boolean longDescription);
public synchronized void updateStatus(SplitfileProgressEvent event) {
this.failedBlocks = event.failedBlocks;
this.fatallyFailedBlocks = event.fatallyFailedBlocks;
// clone() because Date is mutable
this.latestFailure = event.latestFailure != null ? (Date)event.latestFailure.clone() : null;
this.fetchedBlocks = event.succeedBlocks;
// clone() because Date is mutable
this.latestSuccess = event.latestSuccess != null ? (Date)event.latestSuccess.clone() : null;
this.isTotalFinalized = event.finalizedTotal;
this.minBlocks = event.minSuccessfulBlocks;
this.totalBlocks = event.totalBlocks;
}
public synchronized void setPriority(short newPriority) {
this.priority = newPriority;
}
public synchronized void setStarted(boolean started) {
this.hasStarted = started;
}
/** Get the preferred filename, from the URI, the filename, etc.
* @return A filename or null if not enough information to give one. */
public abstract String getPreferredFilename();
/** Get the preferred filename, from the URI or the filename etc.
* @return A filename, including the localised version of "unknown" if
* we don't know enough.
*/
public String getPreferredFilenameSafe() {
String ret = getPreferredFilename();
if(ret == null)
return NodeL10n.getBase().getString("RequestStatus.unknownFilename");
else
return ret;
}
public RequestStatus clone() {
try {
return (RequestStatus) super.clone();
} catch (CloneNotSupportedException e) {
throw new Error(e);
}
}
}