3
3
import java .io .IOException ;
4
4
import java .net .InetSocketAddress ;
5
5
import java .nio .channels .SocketChannel ;
6
+ import java .util .ArrayList ;
6
7
import java .util .Arrays ;
8
+ import java .util .Collection ;
9
+ import java .util .Collections ;
10
+ import java .util .List ;
11
+ import java .util .concurrent .atomic .AtomicInteger ;
7
12
8
13
/**
9
14
* Basic reconnection strategy that changes addresses in a round-robin fashion.
10
15
* To be used with {@link TarantoolClientImpl}.
11
16
*/
12
- public class RoundRobinSocketProviderImpl implements SocketChannelProvider {
13
- /** Timeout to establish socket connection with an individual server. */
17
+ public class RoundRobinSocketProviderImpl extends BaseSocketChannelProvider {
18
+
19
+ /**
20
+ * Timeout to establish socket connection with an individual server.
21
+ */
14
22
private int timeout ; // 0 is infinite.
15
- /** Limit of retries. */
16
- private int retriesLimit = -1 ; // No-limit.
17
- /** Server addresses as configured. */
18
- private final String [] addrs ;
19
- /** Socket addresses. */
20
- private final InetSocketAddress [] sockAddrs ;
21
- /** Current position within {@link #sockAddrs} array. */
22
- private int pos ;
23
+
24
+ /**
25
+ * Server addresses as configured.
26
+ */
27
+ private final List <String > addresses = new ArrayList <>();
28
+
29
+ /**
30
+ * Socket addresses.
31
+ */
32
+ private final List <InetSocketAddress > socketAddresses = new ArrayList <>();
33
+
34
+ /**
35
+ * Current position within {@link #socketAddresses} array.
36
+ */
37
+ private AtomicInteger currentPosition = new AtomicInteger (-1 );
38
+
39
+ /**
40
+ * Lock
41
+ */
42
+ // private ReadWriteLock addressListLock = new ReentrantReadWriteLock();
23
43
24
44
/**
25
45
* Constructs an instance.
26
46
*
27
- * @param addrs Array of addresses in a form of [host]:[port].
47
+ * @param addresses Array of addresses in a form of [host]:[port].
28
48
*/
29
- public RoundRobinSocketProviderImpl (String ... addrs ) {
30
- if (addrs == null || addrs .length == 0 )
31
- throw new IllegalArgumentException ("addrs is null or empty." );
32
-
33
- this .addrs = Arrays .copyOf (addrs , addrs .length );
49
+ public RoundRobinSocketProviderImpl (String ... addresses ) {
50
+ if (addresses == null || addresses .length == 0 ) {
51
+ throw new IllegalArgumentException ("Addresses are null or empty." );
52
+ }
34
53
35
- sockAddrs = new InetSocketAddress [this .addrs .length ];
54
+ updateAddressList (Arrays .asList (addresses ));
55
+ }
36
56
37
- for (int i = 0 ; i < this .addrs .length ; i ++) {
38
- sockAddrs [i ] = parseAddress (this .addrs [i ]);
57
+ private void updateAddressList (Collection <String > addresses ) {
58
+ String lastAddress = getLastObtainedAddress ();
59
+ this .addresses .clear ();
60
+ this .addresses .addAll (addresses );
61
+ this .addresses .forEach (address -> socketAddresses .add (parseAddress (address )));
62
+ if (lastAddress != null ) {
63
+ int recoveredPosition = this .addresses .indexOf (lastAddress );
64
+ currentPosition .set (recoveredPosition );
39
65
}
40
66
}
41
67
42
68
/**
43
69
* @return Configured addresses in a form of [host]:[port].
44
70
*/
45
- public String [] getAddresses () {
46
- return this .addrs ;
71
+ public List <String > getAddresses () {
72
+ return Collections .unmodifiableList (this .addresses );
73
+ }
74
+
75
+ public String getLastObtainedAddress () {
76
+ int index = currentPosition .get ();
77
+ return index >= 0 ? addresses .get (index ) : null ;
47
78
}
48
79
49
80
/**
50
81
* Sets maximum amount of time to wait for a socket connection establishment
51
82
* with an individual server.
52
- *
83
+ * <p>
53
84
* Zero means infinite timeout.
54
85
*
55
86
* @param timeout Timeout value, ms.
56
87
* @return {@code this}.
57
88
* @throws IllegalArgumentException If timeout is negative.
58
89
*/
59
90
public RoundRobinSocketProviderImpl setTimeout (int timeout ) {
60
- if (timeout < 0 )
91
+ if (timeout < 0 ) {
61
92
throw new IllegalArgumentException ("timeout is negative." );
93
+ }
62
94
63
95
this .timeout = timeout ;
64
96
@@ -67,58 +99,20 @@ public RoundRobinSocketProviderImpl setTimeout(int timeout) {
67
99
68
100
/**
69
101
* @return Maximum amount of time to wait for a socket connection establishment
70
- * with an individual server.
102
+ * with an individual server.
71
103
*/
72
104
public int getTimeout () {
73
105
return timeout ;
74
106
}
75
107
76
- /**
77
- * Sets maximum amount of reconnect attempts to be made before an exception is raised.
78
- * The retry count is maintained by a {@link #get(int, Throwable)} caller
79
- * when a socket level connection was established.
80
- *
81
- * Negative value means unlimited.
82
- *
83
- * @param retriesLimit Limit of retries to use.
84
- * @return {@code this}.
85
- */
86
- public RoundRobinSocketProviderImpl setRetriesLimit (int retriesLimit ) {
87
- this .retriesLimit = retriesLimit ;
88
-
89
- return this ;
90
- }
91
-
92
- /**
93
- * @return Maximum reconnect attempts to make before raising exception.
94
- */
95
- public int getRetriesLimit () {
96
- return retriesLimit ;
97
- }
98
-
99
- /** {@inheritDoc} */
100
108
@ Override
101
- public SocketChannel get (int retryNumber , Throwable lastError ) {
102
- if (areRetriesExhausted (retryNumber )) {
103
- throw new CommunicationException ("Connection retries exceeded." , lastError );
104
- }
109
+ protected SocketChannel doRetry (int retryNumber , Throwable lastError ) {
105
110
int attempts = getAddressCount ();
106
111
long deadline = System .currentTimeMillis () + timeout * attempts ;
107
112
while (!Thread .currentThread ().isInterrupted ()) {
108
- SocketChannel channel = null ;
109
113
try {
110
- channel = SocketChannel .open ();
111
- InetSocketAddress addr = getNextSocketAddress ();
112
- channel .socket ().connect (addr , timeout );
113
- return channel ;
114
+ return openChannel (getNextSocketAddress (), timeout );
114
115
} catch (IOException e ) {
115
- if (channel != null ) {
116
- try {
117
- channel .close ();
118
- } catch (IOException ignored ) {
119
- // No-op.
120
- }
121
- }
122
116
long now = System .currentTimeMillis ();
123
117
if (deadline <= now ) {
124
118
throw new CommunicationException ("Connection time out." , e );
@@ -141,42 +135,21 @@ public SocketChannel get(int retryNumber, Throwable lastError) {
141
135
* @return Number of configured addresses.
142
136
*/
143
137
protected int getAddressCount () {
144
- return sockAddrs . length ;
138
+ return socketAddresses . size () ;
145
139
}
146
140
147
141
/**
148
142
* @return Socket address to use for the next reconnection attempt.
149
143
*/
150
144
protected InetSocketAddress getNextSocketAddress () {
151
- InetSocketAddress res = sockAddrs [pos ];
152
- pos = (pos + 1 ) % sockAddrs .length ;
153
- return res ;
154
- }
155
-
156
- /**
157
- * Parse a string address in the form of [host]:[port]
158
- * and builds a socket address.
159
- *
160
- * @param addr Server address.
161
- * @return Socket address.
162
- */
163
- protected InetSocketAddress parseAddress (String addr ) {
164
- int idx = addr .indexOf (':' );
165
- String host = (idx < 0 ) ? addr : addr .substring (0 , idx );
166
- int port = (idx < 0 ) ? 3301 : Integer .parseInt (addr .substring (idx + 1 ));
167
- return new InetSocketAddress (host , port );
145
+ int position = currentPosition .updateAndGet (i -> (i + 1 ) % socketAddresses .size ());
146
+ return socketAddresses .get (position );
168
147
}
169
148
170
- /**
171
- * Provides a decision on whether retries limit is hit.
172
- *
173
- * @param retries Current count of retries.
174
- * @return {@code true} if retries are exhausted.
175
- */
176
- private boolean areRetriesExhausted (int retries ) {
177
- int limit = getRetriesLimit ();
178
- if (limit < 0 )
179
- return false ;
180
- return retries >= limit ;
149
+ public void setAddresses (Collection <String > addresses ) {
150
+ if (addresses == null || addresses .isEmpty ()) {
151
+ throw new IllegalArgumentException ("Addresses are null or empty." );
152
+ }
153
+ updateAddressList (addresses );
181
154
}
182
155
}
0 commit comments