14
14
import uk .co .real_logic .aeron .Publication ;
15
15
import uk .co .real_logic .aeron .Subscription ;
16
16
import uk .co .real_logic .aeron .logbuffer .Header ;
17
+ import uk .co .real_logic .agrona .BitUtil ;
17
18
import uk .co .real_logic .agrona .DirectBuffer ;
18
19
import uk .co .real_logic .agrona .collections .Int2ObjectHashMap ;
19
20
import uk .co .real_logic .agrona .concurrent .UnsafeBuffer ;
20
21
21
22
import java .nio .ByteBuffer ;
23
+ import java .util .concurrent .CountDownLatch ;
24
+ import java .util .concurrent .TimeUnit ;
25
+
26
+ import static io .reactivesocket .aeron .Constants .CLIENT_STREAM_ID ;
27
+ import static io .reactivesocket .aeron .Constants .EMTPY ;
28
+ import static io .reactivesocket .aeron .Constants .SERVER_STREAM_ID ;
22
29
23
30
/**
24
31
* Created by rroeser on 8/13/15.
25
32
*/
26
33
public class ReactivesocketAeronClient {
27
- private static final byte [] EMTPY = new byte [0 ];
28
-
29
34
private static final ThreadLocal <UnsafeBuffer > buffers = ThreadLocal .withInitial (() -> new UnsafeBuffer (EMTPY ));
30
35
31
36
private static final Int2ObjectHashMap <Subscription > subscriptions = new Int2ObjectHashMap <>();
32
37
33
38
private static final Int2ObjectHashMap <PublishSubject <Frame >> subjects = new Int2ObjectHashMap <>();
34
39
35
- private static final int SERVER_STREAM_ID = 1 ;
36
-
37
- private static final int CLIENT_STREAM_ID = 2 ;
40
+ private static final Int2ObjectHashMap <CountDownLatch > establishConnectionLatches = new Int2ObjectHashMap <>();
38
41
39
42
private final ReactiveSocketClientProtocol rsClientProtocol ;
40
43
@@ -52,6 +55,8 @@ private ReactivesocketAeronClient(String host, int port) {
52
55
53
56
final String channel = "udp://" + host + ":" + port ;
54
57
58
+ System .out .println ("Creating a publication to channel => " + channel );
59
+
55
60
final Publication publication = aeron .addPublication (channel , SERVER_STREAM_ID );
56
61
57
62
final int sessionId = publication .sessionId ();
@@ -68,11 +73,13 @@ private ReactivesocketAeronClient(String host, int port) {
68
73
return subscription ;
69
74
});
70
75
76
+ establishConnection (publication , sessionId );
77
+
71
78
this .rsClientProtocol =
72
79
ReactiveSocketClientProtocol .create (new DuplexConnection () {
73
80
74
81
public Publisher <Frame > getInput () {
75
- PublishSubject publishSubject = subjects .get (port );
82
+ PublishSubject publishSubject = subjects .get (sessionId );
76
83
return RxReactiveStreams .toPublisher (publishSubject );
77
84
}
78
85
@@ -81,9 +88,14 @@ public Publisher<Void> write(Publisher<Frame> o) {
81
88
Observable <Void > req = RxReactiveStreams
82
89
.toObservable (o )
83
90
.map (frame -> {
91
+ final ByteBuffer frameBuffer = frame .getByteBuffer ();
92
+ final int frameBufferLength = frameBuffer .capacity ();
84
93
final UnsafeBuffer buffer = buffers .get ();
85
- ByteBuffer byteBuffer = frame .getByteBuffer ();
86
- buffer .wrap (byteBuffer );
94
+ final byte [] bytes = new byte [frameBufferLength + BitUtil .SIZE_OF_INT ];
95
+
96
+ buffer .wrap (bytes );
97
+ buffer .putInt (0 , MessageType .FRAME .getEncodedType ());
98
+ buffer .putBytes (BitUtil .SIZE_OF_INT , frameBuffer , frameBufferLength );
87
99
88
100
for (;;) {
89
101
final long offer = publication .offer (buffer );
@@ -112,11 +124,20 @@ public static ReactivesocketAeronClient create(String host) {
112
124
}
113
125
114
126
void fragmentHandler (DirectBuffer buffer , int offset , int length , Header header ) {
115
- final PublishSubject <Frame > subject = subjects .get (header .sessionId ());
116
- ByteBuffer bytes = ByteBuffer .allocate (buffer .capacity ());
117
- buffer .getBytes (0 , bytes , buffer .capacity ());
118
- final Frame frame = Frame .from (bytes );
119
- subject .onNext (frame );
127
+ int messageTypeInt = buffer .getInt (0 );
128
+ MessageType messageType = MessageType .from (messageTypeInt );
129
+ if (messageType == MessageType .FRAME ) {
130
+ final PublishSubject <Frame > subject = subjects .get (header .sessionId ());
131
+ ByteBuffer bytes = ByteBuffer .allocate (buffer .capacity ());
132
+ buffer .getBytes (BitUtil .SIZE_OF_INT , bytes , buffer .capacity ());
133
+ final Frame frame = Frame .from (bytes );
134
+ subject .onNext (frame );
135
+ } else if (messageType == MessageType .ESTABLISH_CONNECTION_RESPONSE ) {
136
+ CountDownLatch latch = establishConnectionLatches .get (header .sessionId ());
137
+ latch .countDown ();
138
+ } else {
139
+ System .out .println ("Unknow message type => " + messageTypeInt );
140
+ }
120
141
}
121
142
122
143
void poll (FragmentAssembler fragmentAssembler , Subscription subscription , Scheduler .Worker worker ) {
@@ -128,6 +149,44 @@ void poll(FragmentAssembler fragmentAssembler, Subscription subscription, Schedu
128
149
}
129
150
}
130
151
152
+ /**
153
+ * Establishes a connection between the client and server. Waits for 30 seconds before throwing a exception.
154
+ */
155
+ void establishConnection (final Publication publication , final int sessionId ) {
156
+ try {
157
+ UnsafeBuffer buffer = buffers .get ();
158
+ buffer .wrap (new byte [BitUtil .SIZE_OF_INT ]);
159
+ buffer .putInt (0 , MessageType .ESTABLISH_CONNECTION_REQUEST .getEncodedType ());
160
+
161
+ CountDownLatch latch = new CountDownLatch (1 );
162
+ establishConnectionLatches .put (sessionId , latch );
163
+
164
+ long offer = -1 ;
165
+ final long start = System .nanoTime ();
166
+ for (;;) {
167
+ final long current = System .nanoTime ();
168
+ if (current - start > TimeUnit .SECONDS .toNanos (30 )) {
169
+ throw new RuntimeException ("Timed out waiting to establish connection for session id => " + sessionId );
170
+ }
171
+
172
+ if (offer < 0 ) {
173
+ offer = publication .offer (buffer );
174
+ }
175
+
176
+ if (latch .getCount () > 0 ) {
177
+ break ;
178
+ }
179
+ }
180
+
181
+ System .out .println (String .format ("Connection established for channel => %s, stream id => %d" ,
182
+ publication .channel (),
183
+ publication .sessionId ()));
184
+ } finally {
185
+ establishConnectionLatches .remove (sessionId );
186
+ }
187
+
188
+ }
189
+
131
190
public Publisher <String > requestResponse (String payload ) {
132
191
return rsClientProtocol .requestResponse (payload );
133
192
}
0 commit comments