summaryrefslogtreecommitdiffstats
path: root/src/main/java/org/openslx/filetransfer/util/AbstractTransfer.java
blob: 8171d93a69b63bc262e4674b3ab9124e3cfb3a70 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
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 isActive() && ( 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 ) {
			}
		}
	}

}