2
2
3
3
import java .io .IOException ;
4
4
import java .net .InetSocketAddress ;
5
+ import java .net .SocketAddress ;
5
6
import java .nio .channels .SocketChannel ;
6
7
import java .util .ArrayList ;
7
8
import java .util .Arrays ;
8
9
import java .util .Collection ;
9
10
import java .util .Collections ;
10
11
import java .util .List ;
11
12
import java .util .concurrent .atomic .AtomicInteger ;
13
+ import java .util .concurrent .locks .Lock ;
14
+ import java .util .concurrent .locks .ReadWriteLock ;
15
+ import java .util .concurrent .locks .ReentrantReadWriteLock ;
16
+ import java .util .stream .Collectors ;
12
17
13
18
/**
14
19
* Basic reconnection strategy that changes addresses in a round-robin fashion.
15
20
* To be used with {@link TarantoolClientImpl}.
16
21
*/
17
- public class RoundRobinSocketProviderImpl extends BaseSocketChannelProvider {
18
-
19
- /**
20
- * Timeout to establish socket connection with an individual server.
21
- */
22
- private int timeout ; // 0 is infinite.
23
-
24
- /**
25
- * Server addresses as configured.
26
- */
27
- private final List <String > addresses = new ArrayList <>();
22
+ public class RoundRobinSocketProviderImpl extends BaseSocketChannelProvider implements RefreshableSocketProvider {
28
23
29
24
/**
30
25
* Socket addresses.
@@ -39,79 +34,73 @@ public class RoundRobinSocketProviderImpl extends BaseSocketChannelProvider {
39
34
/**
40
35
* Lock
41
36
*/
42
- // private ReadWriteLock addressListLock = new ReentrantReadWriteLock();
37
+ private ReadWriteLock addressListLock = new ReentrantReadWriteLock ();
43
38
44
39
/**
45
40
* Constructs an instance.
46
41
*
47
42
* @param addresses Array of addresses in a form of [host]:[port].
48
43
*/
49
44
public RoundRobinSocketProviderImpl (String ... addresses ) {
50
- if (addresses == null || addresses .length == 0 ) {
51
- throw new IllegalArgumentException ("Addresses are null or empty ." );
45
+ if (addresses .length == 0 ) {
46
+ throw new IllegalArgumentException ("Addresses list must contain at least one address ." );
52
47
}
53
48
54
49
updateAddressList (Arrays .asList (addresses ));
55
50
}
56
51
57
52
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 );
53
+ Lock writeLock = addressListLock .writeLock ();
54
+ writeLock .lock ();
55
+ try {
56
+ InetSocketAddress lastAddress = getLastObtainedAddress ();
57
+ socketAddresses .clear ();
58
+ addresses .stream ()
59
+ .map (this ::parseAddress )
60
+ .collect (Collectors .toCollection (() -> socketAddresses ));
61
+ if (lastAddress != null ) {
62
+ int recoveredPosition = socketAddresses .indexOf (lastAddress );
63
+ currentPosition .set (recoveredPosition );
64
+ } else {
65
+ currentPosition .set (-1 );
66
+ }
67
+ } finally {
68
+ writeLock .unlock ();
65
69
}
66
70
}
67
71
68
72
/**
69
- * @return Configured addresses in a form of [host]:[port].
70
- */
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 ;
78
- }
79
-
80
- /**
81
- * Sets maximum amount of time to wait for a socket connection establishment
82
- * with an individual server.
83
- * <p>
84
- * Zero means infinite timeout.
85
- *
86
- * @param timeout Timeout value, ms.
87
- * @return {@code this}.
88
- * @throws IllegalArgumentException If timeout is negative.
73
+ * @return resolved socket addresses
89
74
*/
90
- public RoundRobinSocketProviderImpl setTimeout (int timeout ) {
91
- if (timeout < 0 ) {
92
- throw new IllegalArgumentException ("timeout is negative." );
75
+ public List <SocketAddress > getAddresses () {
76
+ Lock readLock = addressListLock .readLock ();
77
+ readLock .lock ();
78
+ try {
79
+ return Collections .unmodifiableList (this .socketAddresses );
80
+ } finally {
81
+ readLock .unlock ();
93
82
}
94
-
95
- this .timeout = timeout ;
96
-
97
- return this ;
98
83
}
99
84
100
- /**
101
- * @return Maximum amount of time to wait for a socket connection establishment
102
- * with an individual server.
103
- */
104
- public int getTimeout () {
105
- return timeout ;
85
+ protected InetSocketAddress getLastObtainedAddress () {
86
+ Lock readLock = addressListLock .readLock ();
87
+ readLock .lock ();
88
+ try {
89
+ int index = currentPosition .get ();
90
+ return index >= 0 ? socketAddresses .get (index ) : null ;
91
+ } finally {
92
+ readLock .unlock ();
93
+ }
106
94
}
107
95
108
96
@ Override
109
97
protected SocketChannel doRetry (int retryNumber , Throwable lastError ) {
110
98
int attempts = getAddressCount ();
111
- long deadline = System .currentTimeMillis () + timeout * attempts ;
99
+ // todo: recalc deadline?
100
+ long deadline = System .currentTimeMillis () + getTimeout () * attempts ;
112
101
while (!Thread .currentThread ().isInterrupted ()) {
113
102
try {
114
- return openChannel (getNextSocketAddress (), timeout );
103
+ return openChannel (getNextSocketAddress ());
115
104
} catch (IOException e ) {
116
105
long now = System .currentTimeMillis ();
117
106
if (deadline <= now ) {
@@ -135,18 +124,30 @@ protected SocketChannel doRetry(int retryNumber, Throwable lastError) {
135
124
* @return Number of configured addresses.
136
125
*/
137
126
protected int getAddressCount () {
138
- return socketAddresses .size ();
127
+ Lock readLock = addressListLock .readLock ();
128
+ readLock .lock ();
129
+ try {
130
+ return socketAddresses .size ();
131
+ } finally {
132
+ readLock .unlock ();
133
+ }
139
134
}
140
135
141
136
/**
142
137
* @return Socket address to use for the next reconnection attempt.
143
138
*/
144
139
protected InetSocketAddress getNextSocketAddress () {
145
- int position = currentPosition .updateAndGet (i -> (i + 1 ) % socketAddresses .size ());
146
- return socketAddresses .get (position );
140
+ Lock readLock = addressListLock .readLock ();
141
+ readLock .lock ();
142
+ try {
143
+ int position = currentPosition .updateAndGet (i -> (i + 1 ) % socketAddresses .size ());
144
+ return socketAddresses .get (position );
145
+ } finally {
146
+ readLock .unlock ();
147
+ }
147
148
}
148
149
149
- public void setAddresses (Collection <String > addresses ) {
150
+ public void refreshAddresses (Collection <String > addresses ) {
150
151
if (addresses == null || addresses .isEmpty ()) {
151
152
throw new IllegalArgumentException ("Addresses are null or empty." );
152
153
}
0 commit comments