package org.openslx.filetransfer.util; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; import org.openslx.bwlp.thrift.iface.TransferInformation; public abstract class AbstractTransfer { /** * How long to keep this transfer information when the transfer is * (potentially) done */ private static final long FINISH_TIMEOUT = TimeUnit.MINUTES.toMillis( 3 ); /** * How long to keep this transfer information when there are no active * connections and the transfer seems unfinished */ private static final long IDLE_TIMEOUT = TimeUnit.HOURS.toMillis( 6 ); /** * How long to count this transfer towards active transfers when it has * no active connection. */ private static final long HOT_IDLE_TIMEOUT = TimeUnit.MINUTES.toMillis( 10 ); /** * Time stamp of when (we think) the transfer finished. Clients can/might * not tell us they're done, and simply taking "no active connection" as a * sign the download is done might have unwanted effects if the user's * connection drops for a minute. If this time stamp (plus a FINISH_TIMEOUT) * passed, * we consider the download done and flag it for removal. * If set to zero, the transfer is not finished, or not assumed to have * finished. */ protected final AtomicLong potentialFinishTime = new AtomicLong( 0 ); /** * Time of last activity on this transfer. */ protected final AtomicLong lastActivityTime = new AtomicLong( System.currentTimeMillis() ); private final String transferId; protected AtomicInteger connectFails = new AtomicInteger(); public AbstractTransfer( String transferId ) { this.transferId = transferId; } /** * Returns true if the transfer is considered completed. * * @param now pass System.currentTimeMillis() * @return true if the transfer is considered completed */ public boolean isComplete( long now ) { long val = potentialFinishTime.get(); return val != 0 && val + FINISH_TIMEOUT < now; } /** * Returns true if there has been no activity on this transfer for a certain * amount of time. * * @param now pass System.currentTimeMillis() * @return true if the transfer reached its idle timeout */ public final boolean hasReachedIdleTimeout( long now ) { return getActiveConnectionCount() == 0 && lastActivityTime.get() + IDLE_TIMEOUT < now; } public final boolean countsTowardsConnectionLimit( long now ) { return getActiveConnectionCount() > 0 || lastActivityTime.get() + HOT_IDLE_TIMEOUT > now; } public final int connectFailCount() { return connectFails.get(); } public final String getId() { return transferId; } public abstract TransferInformation getTransferInfo(); /** * Returns true if this transfer would potentially accept new connections. * This should NOT return false if there are too many concurrent * connections, as this is used to signal the client whether to keep trying * to connect. * * @return true if this transfer would potentially accept new connections */ public abstract boolean isActive(); /** * Cancel this transfer, aborting all active connections and rejecting * further incoming ones. */ public abstract void cancel(); /** * Returns number of active transfer connections. * * @return number of active transfer connections */ public abstract int getActiveConnectionCount(); public abstract String getRelativePath(); /** * Try to close everything passed to this method. Never throw an exception * if it fails, just silently continue. * * @param item One or more objects that are AutoCloseable */ static void safeClose( AutoCloseable... item ) { if ( item == null ) return; for ( AutoCloseable c : item ) { if ( c == null ) continue; try { c.close(); } catch ( Exception e ) { } } } }