Madan Jampani

Added a messaging service implementation on top of IOLoop. Added ability to easi…

…ly switch between netty and io loop (default is netty)

Change-Id: Id9af0756bf0a542f832f3611b486b2ac680b91e4
Showing 25 changed files with 483 additions and 403 deletions
...@@ -15,7 +15,6 @@ ...@@ -15,7 +15,6 @@
15 */ 15 */
16 package org.onosproject.store.cluster.messaging; 16 package org.onosproject.store.cluster.messaging;
17 17
18 -import java.io.IOException;
19 import java.nio.ByteBuffer; 18 import java.nio.ByteBuffer;
20 import java.util.Arrays; 19 import java.util.Arrays;
21 import java.util.Objects; 20 import java.util.Objects;
...@@ -35,6 +34,7 @@ public class ClusterMessage { ...@@ -35,6 +34,7 @@ public class ClusterMessage {
35 private final NodeId sender; 34 private final NodeId sender;
36 private final MessageSubject subject; 35 private final MessageSubject subject;
37 private final byte[] payload; 36 private final byte[] payload;
37 + private transient byte[] response;
38 38
39 /** 39 /**
40 * Creates a cluster message. 40 * Creates a cluster message.
...@@ -77,13 +77,21 @@ public class ClusterMessage { ...@@ -77,13 +77,21 @@ public class ClusterMessage {
77 } 77 }
78 78
79 /** 79 /**
80 - * Sends a response to the sender. 80 + * Records the response to be sent to the sender.
81 * 81 *
82 - * @param data payload response. 82 + * @param data response payload
83 - * @throws IOException when I/O exception of some sort has occurred
84 */ 83 */
85 - public void respond(byte[] data) throws IOException { 84 + public void respond(byte[] data) {
86 - throw new IllegalStateException("One can only respond to message received from others."); 85 + response = data;
86 + }
87 +
88 + /**
89 + * Returns the response to be sent to the sender.
90 + *
91 + * @return response bytes
92 + */
93 + public byte[] response() {
94 + return response;
87 } 95 }
88 96
89 @Override 97 @Override
......
...@@ -13,9 +13,9 @@ ...@@ -13,9 +13,9 @@
13 * See the License for the specific language governing permissions and 13 * See the License for the specific language governing permissions and
14 * limitations under the License. 14 * limitations under the License.
15 */ 15 */
16 -package org.onlab.netty; 16 +package org.onosproject.store.cluster.messaging;
17 17
18 -import static com.google.common.base.Preconditions.*; 18 +import static com.google.common.base.Preconditions.checkNotNull;
19 19
20 import java.util.Objects; 20 import java.util.Objects;
21 21
......
...@@ -13,16 +13,19 @@ ...@@ -13,16 +13,19 @@
13 * See the License for the specific language governing permissions and 13 * See the License for the specific language governing permissions and
14 * limitations under the License. 14 * limitations under the License.
15 */ 15 */
16 -package org.onlab.netty; 16 +package org.onosproject.store.cluster.messaging;
17 17
18 import java.io.IOException; 18 import java.io.IOException;
19 import java.util.concurrent.CompletableFuture; 19 import java.util.concurrent.CompletableFuture;
20 import java.util.concurrent.Executor; 20 import java.util.concurrent.Executor;
21 +import java.util.function.Consumer;
22 +import java.util.function.Function;
21 23
22 /** 24 /**
23 * Interface for low level messaging primitives. 25 * Interface for low level messaging primitives.
24 */ 26 */
25 public interface MessagingService { 27 public interface MessagingService {
28 +
26 /** 29 /**
27 * Sends a message asynchronously to the specified communication end point. 30 * Sends a message asynchronously to the specified communication end point.
28 * The message is specified using the type and payload. 31 * The message is specified using the type and payload.
...@@ -31,7 +34,7 @@ public interface MessagingService { ...@@ -31,7 +34,7 @@ public interface MessagingService {
31 * @param payload message payload bytes. 34 * @param payload message payload bytes.
32 * @throws IOException when I/O exception of some sort has occurred 35 * @throws IOException when I/O exception of some sort has occurred
33 */ 36 */
34 - public void sendAsync(Endpoint ep, String type, byte[] payload) throws IOException; 37 + void sendAsync(Endpoint ep, String type, byte[] payload) throws IOException;
35 38
36 /** 39 /**
37 * Sends a message synchronously and waits for a response. 40 * Sends a message synchronously and waits for a response.
...@@ -40,7 +43,7 @@ public interface MessagingService { ...@@ -40,7 +43,7 @@ public interface MessagingService {
40 * @param payload message payload. 43 * @param payload message payload.
41 * @return a response future 44 * @return a response future
42 */ 45 */
43 - public CompletableFuture<byte[]> sendAndReceive(Endpoint ep, String type, byte[] payload); 46 + CompletableFuture<byte[]> sendAndReceive(Endpoint ep, String type, byte[] payload);
44 47
45 /** 48 /**
46 * Registers a new message handler for message type. 49 * Registers a new message handler for message type.
...@@ -48,19 +51,19 @@ public interface MessagingService { ...@@ -48,19 +51,19 @@ public interface MessagingService {
48 * @param handler message handler 51 * @param handler message handler
49 * @param executor executor to use for running message handler logic. 52 * @param executor executor to use for running message handler logic.
50 */ 53 */
51 - public void registerHandler(String type, MessageHandler handler, Executor executor); 54 + void registerHandler(String type, Consumer<byte[]> handler, Executor executor);
52 55
53 /** 56 /**
54 * Registers a new message handler for message type. 57 * Registers a new message handler for message type.
55 * @param type message type. 58 * @param type message type.
56 * @param handler message handler 59 * @param handler message handler
60 + * @param executor executor to use for running message handler logic.
57 */ 61 */
58 - @Deprecated 62 + void registerHandler(String type, Function<byte[], byte[]> handler, Executor executor);
59 - public void registerHandler(String type, MessageHandler handler);
60 63
61 /** 64 /**
62 * Unregister current handler, if one exists for message type. 65 * Unregister current handler, if one exists for message type.
63 * @param type message type 66 * @param type message type
64 */ 67 */
65 - public void unregisterHandler(String type); 68 + void unregisterHandler(String type);
66 } 69 }
......
...@@ -53,6 +53,12 @@ ...@@ -53,6 +53,12 @@
53 53
54 <dependency> 54 <dependency>
55 <groupId>org.onosproject</groupId> 55 <groupId>org.onosproject</groupId>
56 + <artifactId>onlab-nio</artifactId>
57 + <version>${project.version}</version>
58 + </dependency>
59 +
60 + <dependency>
61 + <groupId>org.onosproject</groupId>
56 <artifactId>onlab-misc</artifactId> 62 <artifactId>onlab-misc</artifactId>
57 <version>${project.version}</version> 63 <version>${project.version}</version>
58 </dependency> 64 </dependency>
......
...@@ -19,14 +19,12 @@ import com.google.common.collect.ImmutableSet; ...@@ -19,14 +19,12 @@ import com.google.common.collect.ImmutableSet;
19 import com.google.common.collect.Maps; 19 import com.google.common.collect.Maps;
20 import com.google.common.collect.Sets; 20 import com.google.common.collect.Sets;
21 import com.hazelcast.util.AddressUtil; 21 import com.hazelcast.util.AddressUtil;
22 +
22 import org.apache.felix.scr.annotations.Activate; 23 import org.apache.felix.scr.annotations.Activate;
23 import org.apache.felix.scr.annotations.Component; 24 import org.apache.felix.scr.annotations.Component;
24 import org.apache.felix.scr.annotations.Deactivate; 25 import org.apache.felix.scr.annotations.Deactivate;
25 import org.apache.felix.scr.annotations.Service; 26 import org.apache.felix.scr.annotations.Service;
26 import org.joda.time.DateTime; 27 import org.joda.time.DateTime;
27 -import org.onlab.netty.Endpoint;
28 -import org.onlab.netty.Message;
29 -import org.onlab.netty.MessageHandler;
30 import org.onlab.netty.NettyMessagingService; 28 import org.onlab.netty.NettyMessagingService;
31 import org.onlab.packet.IpAddress; 29 import org.onlab.packet.IpAddress;
32 import org.onlab.util.KryoNamespace; 30 import org.onlab.util.KryoNamespace;
...@@ -38,6 +36,7 @@ import org.onosproject.cluster.ControllerNode.State; ...@@ -38,6 +36,7 @@ import org.onosproject.cluster.ControllerNode.State;
38 import org.onosproject.cluster.DefaultControllerNode; 36 import org.onosproject.cluster.DefaultControllerNode;
39 import org.onosproject.cluster.NodeId; 37 import org.onosproject.cluster.NodeId;
40 import org.onosproject.store.AbstractStore; 38 import org.onosproject.store.AbstractStore;
39 +import org.onosproject.store.cluster.messaging.Endpoint;
41 import org.onosproject.store.consistent.impl.DatabaseDefinition; 40 import org.onosproject.store.consistent.impl.DatabaseDefinition;
42 import org.onosproject.store.consistent.impl.DatabaseDefinitionStore; 41 import org.onosproject.store.consistent.impl.DatabaseDefinitionStore;
43 import org.onosproject.store.serializers.KryoNamespaces; 42 import org.onosproject.store.serializers.KryoNamespaces;
...@@ -56,6 +55,7 @@ import java.util.concurrent.ExecutorService; ...@@ -56,6 +55,7 @@ import java.util.concurrent.ExecutorService;
56 import java.util.concurrent.Executors; 55 import java.util.concurrent.Executors;
57 import java.util.concurrent.ScheduledExecutorService; 56 import java.util.concurrent.ScheduledExecutorService;
58 import java.util.concurrent.TimeUnit; 57 import java.util.concurrent.TimeUnit;
58 +import java.util.function.Consumer;
59 import java.util.stream.Collectors; 59 import java.util.stream.Collectors;
60 60
61 import static com.google.common.base.Preconditions.checkNotNull; 61 import static com.google.common.base.Preconditions.checkNotNull;
...@@ -108,7 +108,7 @@ public class DistributedClusterStore ...@@ -108,7 +108,7 @@ public class DistributedClusterStore
108 private final Map<NodeId, ControllerNode> allNodes = Maps.newConcurrentMap(); 108 private final Map<NodeId, ControllerNode> allNodes = Maps.newConcurrentMap();
109 private final Map<NodeId, State> nodeStates = Maps.newConcurrentMap(); 109 private final Map<NodeId, State> nodeStates = Maps.newConcurrentMap();
110 private final Map<NodeId, DateTime> nodeStateLastUpdatedTimes = Maps.newConcurrentMap(); 110 private final Map<NodeId, DateTime> nodeStateLastUpdatedTimes = Maps.newConcurrentMap();
111 - private NettyMessagingService messagingService = new NettyMessagingService(); 111 + private NettyMessagingService messagingService;
112 private ScheduledExecutorService heartBeatSender = Executors.newSingleThreadScheduledExecutor( 112 private ScheduledExecutorService heartBeatSender = Executors.newSingleThreadScheduledExecutor(
113 groupedThreads("onos/cluster/membership", "heartbeat-sender")); 113 groupedThreads("onos/cluster/membership", "heartbeat-sender"));
114 private ExecutorService heartBeatMessageHandler = Executors.newSingleThreadExecutor( 114 private ExecutorService heartBeatMessageHandler = Executors.newSingleThreadExecutor(
...@@ -149,7 +149,6 @@ public class DistributedClusterStore ...@@ -149,7 +149,6 @@ public class DistributedClusterStore
149 establishSelfIdentity(); 149 establishSelfIdentity();
150 150
151 messagingService = new NettyMessagingService(HEARTBEAT_FD_PORT); 151 messagingService = new NettyMessagingService(HEARTBEAT_FD_PORT);
152 -
153 try { 152 try {
154 messagingService.activate(); 153 messagingService.activate();
155 } catch (InterruptedException e) { 154 } catch (InterruptedException e) {
...@@ -376,10 +375,10 @@ public class DistributedClusterStore ...@@ -376,10 +375,10 @@ public class DistributedClusterStore
376 throw new IllegalStateException("Unable to determine local ip"); 375 throw new IllegalStateException("Unable to determine local ip");
377 } 376 }
378 377
379 - private class HeartbeatMessageHandler implements MessageHandler { 378 + private class HeartbeatMessageHandler implements Consumer<byte[]> {
380 @Override 379 @Override
381 - public void handle(Message message) throws IOException { 380 + public void accept(byte[] message) {
382 - HeartbeatMessage hb = SERIALIZER.decode(message.payload()); 381 + HeartbeatMessage hb = SERIALIZER.decode(message);
383 failureDetector.report(hb.source().id()); 382 failureDetector.report(hb.source().id());
384 hb.knownPeers().forEach(node -> { 383 hb.knownPeers().forEach(node -> {
385 allNodes.put(node.id(), node); 384 allNodes.put(node.id(), node);
......
...@@ -21,18 +21,17 @@ import org.apache.felix.scr.annotations.Deactivate; ...@@ -21,18 +21,17 @@ import org.apache.felix.scr.annotations.Deactivate;
21 import org.apache.felix.scr.annotations.Reference; 21 import org.apache.felix.scr.annotations.Reference;
22 import org.apache.felix.scr.annotations.ReferenceCardinality; 22 import org.apache.felix.scr.annotations.ReferenceCardinality;
23 import org.apache.felix.scr.annotations.Service; 23 import org.apache.felix.scr.annotations.Service;
24 -import org.onlab.netty.Endpoint;
25 -import org.onlab.netty.Message;
26 -import org.onlab.netty.MessageHandler;
27 -import org.onlab.netty.MessagingService;
28 import org.onlab.netty.NettyMessagingService; 24 import org.onlab.netty.NettyMessagingService;
25 +import org.onlab.nio.service.IOLoopMessagingService;
29 import org.onosproject.cluster.ClusterService; 26 import org.onosproject.cluster.ClusterService;
30 import org.onosproject.cluster.ControllerNode; 27 import org.onosproject.cluster.ControllerNode;
31 import org.onosproject.cluster.NodeId; 28 import org.onosproject.cluster.NodeId;
32 import org.onosproject.store.cluster.messaging.ClusterCommunicationService; 29 import org.onosproject.store.cluster.messaging.ClusterCommunicationService;
33 import org.onosproject.store.cluster.messaging.ClusterMessage; 30 import org.onosproject.store.cluster.messaging.ClusterMessage;
34 import org.onosproject.store.cluster.messaging.ClusterMessageHandler; 31 import org.onosproject.store.cluster.messaging.ClusterMessageHandler;
32 +import org.onosproject.store.cluster.messaging.Endpoint;
35 import org.onosproject.store.cluster.messaging.MessageSubject; 33 import org.onosproject.store.cluster.messaging.MessageSubject;
34 +import org.onosproject.store.cluster.messaging.MessagingService;
36 import org.slf4j.Logger; 35 import org.slf4j.Logger;
37 import org.slf4j.LoggerFactory; 36 import org.slf4j.LoggerFactory;
38 37
...@@ -64,17 +63,28 @@ public class ClusterCommunicationManager ...@@ -64,17 +63,28 @@ public class ClusterCommunicationManager
64 // TODO: This probably should not be a OSGi service. 63 // TODO: This probably should not be a OSGi service.
65 private MessagingService messagingService; 64 private MessagingService messagingService;
66 65
66 + private final boolean useNetty = true;
67 +
67 @Activate 68 @Activate
68 public void activate() { 69 public void activate() {
69 ControllerNode localNode = clusterService.getLocalNode(); 70 ControllerNode localNode = clusterService.getLocalNode();
70 - NettyMessagingService netty = new NettyMessagingService(localNode.ip(), localNode.tcpPort()); 71 + if (useNetty) {
71 - // FIXME: workaround until it becomes a service. 72 + NettyMessagingService netty = new NettyMessagingService(localNode.ip(), localNode.tcpPort());
72 - try { 73 + try {
73 - netty.activate(); 74 + netty.activate();
74 - } catch (Exception e) { 75 + messagingService = netty;
75 - log.error("NettyMessagingService#activate", e); 76 + } catch (Exception e) {
77 + log.error("NettyMessagingService#activate", e);
78 + }
79 + } else {
80 + IOLoopMessagingService ioLoop = new IOLoopMessagingService(localNode.ip(), localNode.tcpPort());
81 + try {
82 + ioLoop.activate();
83 + messagingService = ioLoop;
84 + } catch (Exception e) {
85 + log.error("IOLoopMessagingService#activate", e);
86 + }
76 } 87 }
77 - messagingService = netty;
78 log.info("Started on {}:{}", localNode.ip(), localNode.tcpPort()); 88 log.info("Started on {}:{}", localNode.ip(), localNode.tcpPort());
79 } 89 }
80 90
...@@ -83,9 +93,13 @@ public class ClusterCommunicationManager ...@@ -83,9 +93,13 @@ public class ClusterCommunicationManager
83 // TODO: cleanup messageingService if needed. 93 // TODO: cleanup messageingService if needed.
84 // FIXME: workaround until it becomes a service. 94 // FIXME: workaround until it becomes a service.
85 try { 95 try {
86 - ((NettyMessagingService) messagingService).deactivate(); 96 + if (useNetty) {
97 + ((NettyMessagingService) messagingService).deactivate();
98 + } else {
99 + ((IOLoopMessagingService) messagingService).deactivate();
100 + }
87 } catch (Exception e) { 101 } catch (Exception e) {
88 - log.error("NettyMessagingService#deactivate", e); 102 + log.error("MessagingService#deactivate", e);
89 } 103 }
90 log.info("Stopped"); 104 log.info("Stopped");
91 } 105 }
...@@ -232,7 +246,9 @@ public class ClusterCommunicationManager ...@@ -232,7 +246,9 @@ public class ClusterCommunicationManager
232 public void addSubscriber(MessageSubject subject, 246 public void addSubscriber(MessageSubject subject,
233 ClusterMessageHandler subscriber, 247 ClusterMessageHandler subscriber,
234 ExecutorService executor) { 248 ExecutorService executor) {
235 - messagingService.registerHandler(subject.value(), new InternalClusterMessageHandler(subscriber), executor); 249 + messagingService.registerHandler(subject.value(),
250 + new InternalClusterMessageHandler(subscriber),
251 + executor);
236 } 252 }
237 253
238 @Override 254 @Override
...@@ -240,31 +256,6 @@ public class ClusterCommunicationManager ...@@ -240,31 +256,6 @@ public class ClusterCommunicationManager
240 messagingService.unregisterHandler(subject.value()); 256 messagingService.unregisterHandler(subject.value());
241 } 257 }
242 258
243 - private final class InternalClusterMessageHandler implements MessageHandler {
244 -
245 - private final ClusterMessageHandler handler;
246 -
247 - public InternalClusterMessageHandler(ClusterMessageHandler handler) {
248 - this.handler = handler;
249 - }
250 -
251 - @Override
252 - public void handle(Message message) {
253 - final ClusterMessage clusterMessage;
254 - try {
255 - clusterMessage = ClusterMessage.fromBytes(message.payload());
256 - } catch (Exception e) {
257 - log.error("Failed decoding {}", message, e);
258 - throw e;
259 - }
260 - try {
261 - handler.handle(new InternalClusterMessage(clusterMessage, message));
262 - } catch (Exception e) {
263 - log.trace("Failed handling {}", clusterMessage, e);
264 - throw e;
265 - }
266 - }
267 - }
268 259
269 @Override 260 @Override
270 public <M, R> void addSubscriber(MessageSubject subject, 261 public <M, R> void addSubscriber(MessageSubject subject,
...@@ -287,7 +278,22 @@ public class ClusterCommunicationManager ...@@ -287,7 +278,22 @@ public class ClusterCommunicationManager
287 executor); 278 executor);
288 } 279 }
289 280
290 - private class InternalMessageResponder<M, R> implements MessageHandler { 281 + private class InternalClusterMessageHandler implements Function<byte[], byte[]> {
282 + private ClusterMessageHandler handler;
283 +
284 + public InternalClusterMessageHandler(ClusterMessageHandler handler) {
285 + this.handler = handler;
286 + }
287 +
288 + @Override
289 + public byte[] apply(byte[] bytes) {
290 + ClusterMessage message = ClusterMessage.fromBytes(bytes);
291 + handler.handle(message);
292 + return message.response();
293 + }
294 + }
295 +
296 + private class InternalMessageResponder<M, R> implements Function<byte[], byte[]> {
291 private final Function<byte[], M> decoder; 297 private final Function<byte[], M> decoder;
292 private final Function<R, byte[]> encoder; 298 private final Function<R, byte[]> encoder;
293 private final Function<M, R> handler; 299 private final Function<M, R> handler;
...@@ -299,14 +305,15 @@ public class ClusterCommunicationManager ...@@ -299,14 +305,15 @@ public class ClusterCommunicationManager
299 this.encoder = encoder; 305 this.encoder = encoder;
300 this.handler = handler; 306 this.handler = handler;
301 } 307 }
308 +
302 @Override 309 @Override
303 - public void handle(Message message) throws IOException { 310 + public byte[] apply(byte[] bytes) {
304 - R response = handler.apply(decoder.apply(ClusterMessage.fromBytes(message.payload()).payload())); 311 + R reply = handler.apply(decoder.apply(ClusterMessage.fromBytes(bytes).payload()));
305 - message.respond(encoder.apply(response)); 312 + return encoder.apply(reply);
306 } 313 }
307 } 314 }
308 315
309 - private class InternalMessageConsumer<M> implements MessageHandler { 316 + private class InternalMessageConsumer<M> implements Consumer<byte[]> {
310 private final Function<byte[], M> decoder; 317 private final Function<byte[], M> decoder;
311 private final Consumer<M> consumer; 318 private final Consumer<M> consumer;
312 319
...@@ -314,24 +321,10 @@ public class ClusterCommunicationManager ...@@ -314,24 +321,10 @@ public class ClusterCommunicationManager
314 this.decoder = decoder; 321 this.decoder = decoder;
315 this.consumer = consumer; 322 this.consumer = consumer;
316 } 323 }
317 - @Override
318 - public void handle(Message message) throws IOException {
319 - consumer.accept(decoder.apply(ClusterMessage.fromBytes(message.payload()).payload()));
320 - }
321 - }
322 -
323 - public static final class InternalClusterMessage extends ClusterMessage {
324 -
325 - private final Message rawMessage;
326 -
327 - public InternalClusterMessage(ClusterMessage clusterMessage, Message rawMessage) {
328 - super(clusterMessage.sender(), clusterMessage.subject(), clusterMessage.payload());
329 - this.rawMessage = rawMessage;
330 - }
331 324
332 @Override 325 @Override
333 - public void respond(byte[] response) throws IOException { 326 + public void accept(byte[] bytes) {
334 - rawMessage.respond(response); 327 + consumer.accept(decoder.apply(ClusterMessage.fromBytes(bytes).payload()));
335 } 328 }
336 } 329 }
337 } 330 }
......
...@@ -77,7 +77,6 @@ import org.onosproject.store.serializers.impl.DistributedStoreSerializers; ...@@ -77,7 +77,6 @@ import org.onosproject.store.serializers.impl.DistributedStoreSerializers;
77 import org.osgi.service.component.ComponentContext; 77 import org.osgi.service.component.ComponentContext;
78 import org.slf4j.Logger; 78 import org.slf4j.Logger;
79 79
80 -import java.io.IOException;
81 import java.util.ArrayList; 80 import java.util.ArrayList;
82 import java.util.Arrays; 81 import java.util.Arrays;
83 import java.util.Collections; 82 import java.util.Collections;
...@@ -278,11 +277,7 @@ public class DistributedFlowRuleStore ...@@ -278,11 +277,7 @@ public class DistributedFlowRuleStore
278 FlowRule rule = SERIALIZER.decode(message.payload()); 277 FlowRule rule = SERIALIZER.decode(message.payload());
279 log.trace("received get flow entry request for {}", rule); 278 log.trace("received get flow entry request for {}", rule);
280 FlowEntry flowEntry = flowTable.getFlowEntry(rule); //getFlowEntryInternal(rule); 279 FlowEntry flowEntry = flowTable.getFlowEntry(rule); //getFlowEntryInternal(rule);
281 - try { 280 + message.respond(SERIALIZER.encode(flowEntry));
282 - message.respond(SERIALIZER.encode(flowEntry));
283 - } catch (IOException e) {
284 - log.error("Failed to respond back", e);
285 - }
286 } 281 }
287 }, executor); 282 }, executor);
288 283
...@@ -293,11 +288,7 @@ public class DistributedFlowRuleStore ...@@ -293,11 +288,7 @@ public class DistributedFlowRuleStore
293 DeviceId deviceId = SERIALIZER.decode(message.payload()); 288 DeviceId deviceId = SERIALIZER.decode(message.payload());
294 log.trace("Received get flow entries request for {} from {}", deviceId, message.sender()); 289 log.trace("Received get flow entries request for {} from {}", deviceId, message.sender());
295 Set<FlowEntry> flowEntries = flowTable.getFlowEntries(deviceId); 290 Set<FlowEntry> flowEntries = flowTable.getFlowEntries(deviceId);
296 - try { 291 + message.respond(SERIALIZER.encode(flowEntries));
297 - message.respond(SERIALIZER.encode(flowEntries));
298 - } catch (IOException e) {
299 - log.error("Failed to respond to peer's getFlowEntries request", e);
300 - }
301 } 292 }
302 }, executor); 293 }, executor);
303 294
...@@ -308,11 +299,7 @@ public class DistributedFlowRuleStore ...@@ -308,11 +299,7 @@ public class DistributedFlowRuleStore
308 FlowEntry rule = SERIALIZER.decode(message.payload()); 299 FlowEntry rule = SERIALIZER.decode(message.payload());
309 log.trace("received get flow entry request for {}", rule); 300 log.trace("received get flow entry request for {}", rule);
310 FlowRuleEvent event = removeFlowRuleInternal(rule); 301 FlowRuleEvent event = removeFlowRuleInternal(rule);
311 - try { 302 + message.respond(SERIALIZER.encode(event));
312 - message.respond(SERIALIZER.encode(event));
313 - } catch (IOException e) {
314 - log.error("Failed to respond back", e);
315 - }
316 } 303 }
317 }, executor); 304 }, executor);
318 } 305 }
...@@ -691,11 +678,7 @@ public class DistributedFlowRuleStore ...@@ -691,11 +678,7 @@ public class DistributedFlowRuleStore
691 // TODO: we might want to wrap response in envelope 678 // TODO: we might want to wrap response in envelope
692 // to distinguish sw programming failure and hand over 679 // to distinguish sw programming failure and hand over
693 // it make sense in the latter case to retry immediately. 680 // it make sense in the latter case to retry immediately.
694 - try { 681 + message.respond(SERIALIZER.encode(allFailed));
695 - message.respond(SERIALIZER.encode(allFailed));
696 - } catch (IOException e) {
697 - log.error("Failed to respond back", e);
698 - }
699 return; 682 return;
700 } 683 }
701 684
......
...@@ -51,7 +51,6 @@ import org.onosproject.store.serializers.StoreSerializer; ...@@ -51,7 +51,6 @@ import org.onosproject.store.serializers.StoreSerializer;
51 import org.onosproject.store.serializers.impl.DistributedStoreSerializers; 51 import org.onosproject.store.serializers.impl.DistributedStoreSerializers;
52 import org.slf4j.Logger; 52 import org.slf4j.Logger;
53 53
54 -import java.io.IOException;
55 import java.util.Collection; 54 import java.util.Collection;
56 import java.util.Collections; 55 import java.util.Collections;
57 import java.util.HashSet; 56 import java.util.HashSet;
...@@ -143,11 +142,7 @@ public class DefaultFlowRuleExtRouter ...@@ -143,11 +142,7 @@ public class DefaultFlowRuleExtRouter
143 @Override 142 @Override
144 public void run() { 143 public void run() {
145 FlowExtCompletedOperation result = Futures.getUnchecked(f); 144 FlowExtCompletedOperation result = Futures.getUnchecked(f);
146 - try { 145 + message.respond(SERIALIZER.encode(result));
147 - message.respond(SERIALIZER.encode(result));
148 - } catch (IOException e) {
149 - log.error("Failed to respond back", e);
150 - }
151 } 146 }
152 }, futureListeners); 147 }, futureListeners);
153 } 148 }
......
...@@ -22,7 +22,6 @@ import static org.onosproject.mastership.MastershipEvent.Type.MASTER_CHANGED; ...@@ -22,7 +22,6 @@ import static org.onosproject.mastership.MastershipEvent.Type.MASTER_CHANGED;
22 import static org.slf4j.LoggerFactory.getLogger; 22 import static org.slf4j.LoggerFactory.getLogger;
23 import static com.google.common.base.Preconditions.checkArgument; 23 import static com.google.common.base.Preconditions.checkArgument;
24 24
25 -import java.io.IOException;
26 import java.util.List; 25 import java.util.List;
27 import java.util.Map; 26 import java.util.Map;
28 import java.util.Set; 27 import java.util.Set;
...@@ -300,11 +299,7 @@ public class ConsistentDeviceMastershipStore ...@@ -300,11 +299,7 @@ public class ConsistentDeviceMastershipStore
300 @Override 299 @Override
301 public void handle(ClusterMessage message) { 300 public void handle(ClusterMessage message) {
302 DeviceId deviceId = SERIALIZER.decode(message.payload()); 301 DeviceId deviceId = SERIALIZER.decode(message.payload());
303 - try { 302 + message.respond(SERIALIZER.encode(getRole(localNodeId, deviceId)));
304 - message.respond(SERIALIZER.encode(getRole(localNodeId, deviceId)));
305 - } catch (IOException e) {
306 - log.error("Failed to responsd to role query", e);
307 - }
308 } 303 }
309 } 304 }
310 305
...@@ -318,11 +313,7 @@ public class ConsistentDeviceMastershipStore ...@@ -318,11 +313,7 @@ public class ConsistentDeviceMastershipStore
318 @Override 313 @Override
319 public void handle(ClusterMessage message) { 314 public void handle(ClusterMessage message) {
320 DeviceId deviceId = SERIALIZER.decode(message.payload()); 315 DeviceId deviceId = SERIALIZER.decode(message.payload());
321 - try { 316 + message.respond(SERIALIZER.encode(relinquishRole(localNodeId, deviceId)));
322 - message.respond(SERIALIZER.encode(relinquishRole(localNodeId, deviceId)));
323 - } catch (IOException e) {
324 - log.error("Failed to relinquish role.", e);
325 - }
326 } 317 }
327 } 318 }
328 319
...@@ -371,4 +362,4 @@ public class ConsistentDeviceMastershipStore ...@@ -371,4 +362,4 @@ public class ConsistentDeviceMastershipStore
371 return m.matches(); 362 return m.matches();
372 } 363 }
373 364
374 -}
...\ No newline at end of file ...\ No newline at end of file
365 +}
......
...@@ -43,7 +43,6 @@ import org.onosproject.store.serializers.KryoNamespaces; ...@@ -43,7 +43,6 @@ import org.onosproject.store.serializers.KryoNamespaces;
43 import org.onosproject.store.serializers.KryoSerializer; 43 import org.onosproject.store.serializers.KryoSerializer;
44 import org.slf4j.Logger; 44 import org.slf4j.Logger;
45 45
46 -import java.io.IOException;
47 import java.util.Collections; 46 import java.util.Collections;
48 import java.util.HashSet; 47 import java.util.HashSet;
49 import java.util.Map; 48 import java.util.Map;
...@@ -118,11 +117,7 @@ public class DistributedStatisticStore implements StatisticStore { ...@@ -118,11 +117,7 @@ public class DistributedStatisticStore implements StatisticStore {
118 @Override 117 @Override
119 public void handle(ClusterMessage message) { 118 public void handle(ClusterMessage message) {
120 ConnectPoint cp = SERIALIZER.decode(message.payload()); 119 ConnectPoint cp = SERIALIZER.decode(message.payload());
121 - try { 120 + message.respond(SERIALIZER.encode(getCurrentStatisticInternal(cp)));
122 - message.respond(SERIALIZER.encode(getCurrentStatisticInternal(cp)));
123 - } catch (IOException e) {
124 - log.error("Failed to respond back", e);
125 - }
126 } 121 }
127 }, messageHandlingExecutor); 122 }, messageHandlingExecutor);
128 123
...@@ -131,11 +126,7 @@ public class DistributedStatisticStore implements StatisticStore { ...@@ -131,11 +126,7 @@ public class DistributedStatisticStore implements StatisticStore {
131 @Override 126 @Override
132 public void handle(ClusterMessage message) { 127 public void handle(ClusterMessage message) {
133 ConnectPoint cp = SERIALIZER.decode(message.payload()); 128 ConnectPoint cp = SERIALIZER.decode(message.payload());
134 - try { 129 + message.respond(SERIALIZER.encode(getPreviousStatisticInternal(cp)));
135 - message.respond(SERIALIZER.encode(getPreviousStatisticInternal(cp)));
136 - } catch (IOException e) {
137 - log.error("Failed to respond back", e);
138 - }
139 } 130 }
140 }, messageHandlingExecutor); 131 }, messageHandlingExecutor);
141 log.info("Started"); 132 log.info("Started");
......
...@@ -39,6 +39,10 @@ ...@@ -39,6 +39,10 @@
39 </dependency> 39 </dependency>
40 <dependency> 40 <dependency>
41 <groupId>org.onosproject</groupId> 41 <groupId>org.onosproject</groupId>
42 + <artifactId>onos-api</artifactId>
43 + </dependency>
44 + <dependency>
45 + <groupId>org.onosproject</groupId>
42 <artifactId>onlab-misc</artifactId> 46 <artifactId>onlab-misc</artifactId>
43 </dependency> 47 </dependency>
44 <dependency> 48 <dependency>
......
1 -/*
2 - * Copyright 2014 Open Networking Laboratory
3 - *
4 - * Licensed under the Apache License, Version 2.0 (the "License");
5 - * you may not use this file except in compliance with the License.
6 - * You may obtain a copy of the License at
7 - *
8 - * http://www.apache.org/licenses/LICENSE-2.0
9 - *
10 - * Unless required by applicable law or agreed to in writing, software
11 - * distributed under the License is distributed on an "AS IS" BASIS,
12 - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 - * See the License for the specific language governing permissions and
14 - * limitations under the License.
15 - */
16 -package org.onlab.netty;
17 -
18 -import java.io.IOException;
19 -
20 -import org.slf4j.Logger;
21 -import org.slf4j.LoggerFactory;
22 -
23 -//FIXME: Should be move out to test or app
24 -/**
25 - * Message handler that echos the message back to the sender.
26 - */
27 -public class EchoHandler implements MessageHandler {
28 -
29 - private final Logger log = LoggerFactory.getLogger(getClass());
30 -
31 - @Override
32 - public void handle(Message message) throws IOException {
33 - log.info("Received message. Echoing it back to the sender.");
34 - message.respond(message.payload());
35 - }
36 -}
...@@ -15,9 +15,8 @@ ...@@ -15,9 +15,8 @@
15 */ 15 */
16 package org.onlab.netty; 16 package org.onlab.netty;
17 17
18 -import java.io.IOException;
19 -
20 import org.onlab.util.ByteArraySizeHashPrinter; 18 import org.onlab.util.ByteArraySizeHashPrinter;
19 +import org.onosproject.store.cluster.messaging.Endpoint;
21 20
22 import com.google.common.base.MoreObjects; 21 import com.google.common.base.MoreObjects;
23 22
...@@ -25,20 +24,14 @@ import com.google.common.base.MoreObjects; ...@@ -25,20 +24,14 @@ import com.google.common.base.MoreObjects;
25 * Internal message representation with additional attributes 24 * Internal message representation with additional attributes
26 * for supporting, synchronous request/reply behavior. 25 * for supporting, synchronous request/reply behavior.
27 */ 26 */
28 -public final class InternalMessage implements Message { 27 +public final class InternalMessage {
29 -
30 - public static final String REPLY_MESSAGE_TYPE = "NETTY_MESSAGING_REQUEST_REPLY";
31 28
32 - private long id; 29 + private final long id;
33 - private Endpoint sender; 30 + private final Endpoint sender;
34 - private String type; 31 + private final String type;
35 - private byte[] payload; 32 + private final byte[] payload;
36 - private transient NettyMessagingService messagingService;
37 33
38 - // Must be created using the Builder. 34 + public InternalMessage(long id, Endpoint sender, String type, byte[] payload) {
39 - private InternalMessage() {}
40 -
41 - InternalMessage(long id, Endpoint sender, String type, byte[] payload) {
42 this.id = id; 35 this.id = id;
43 this.sender = sender; 36 this.sender = sender;
44 this.type = type; 37 this.type = type;
...@@ -57,26 +50,10 @@ public final class InternalMessage implements Message { ...@@ -57,26 +50,10 @@ public final class InternalMessage implements Message {
57 return sender; 50 return sender;
58 } 51 }
59 52
60 - @Override
61 public byte[] payload() { 53 public byte[] payload() {
62 return payload; 54 return payload;
63 } 55 }
64 56
65 - protected void setMessagingService(NettyMessagingService messagingService) {
66 - this.messagingService = messagingService;
67 - }
68 -
69 - @Override
70 - public void respond(byte[] data) throws IOException {
71 - Builder builder = new Builder(messagingService);
72 - InternalMessage message = builder.withId(this.id)
73 - .withSender(messagingService.localEp())
74 - .withPayload(data)
75 - .withType(REPLY_MESSAGE_TYPE)
76 - .build();
77 - messagingService.sendAsync(sender, message);
78 - }
79 -
80 @Override 57 @Override
81 public String toString() { 58 public String toString() {
82 return MoreObjects.toStringHelper(this) 59 return MoreObjects.toStringHelper(this)
...@@ -86,39 +63,4 @@ public final class InternalMessage implements Message { ...@@ -86,39 +63,4 @@ public final class InternalMessage implements Message {
86 .add("payload", ByteArraySizeHashPrinter.of(payload)) 63 .add("payload", ByteArraySizeHashPrinter.of(payload))
87 .toString(); 64 .toString();
88 } 65 }
89 -
90 - /**
91 - * Builder for InternalMessages.
92 - */
93 - public static final class Builder {
94 - private InternalMessage message;
95 -
96 - public Builder(NettyMessagingService messagingService) {
97 - message = new InternalMessage();
98 - message.messagingService = messagingService;
99 - }
100 -
101 - public Builder withId(long id) {
102 - message.id = id;
103 - return this;
104 - }
105 -
106 - public Builder withType(String type) {
107 - message.type = type;
108 - return this;
109 - }
110 -
111 - public Builder withSender(Endpoint sender) {
112 - message.sender = sender;
113 - return this;
114 - }
115 - public Builder withPayload(byte[] payload) {
116 - message.payload = payload;
117 - return this;
118 - }
119 -
120 - public InternalMessage build() {
121 - return message;
122 - }
123 - }
124 } 66 }
......
1 -/*
2 - * Copyright 2014 Open Networking Laboratory
3 - *
4 - * Licensed under the Apache License, Version 2.0 (the "License");
5 - * you may not use this file except in compliance with the License.
6 - * You may obtain a copy of the License at
7 - *
8 - * http://www.apache.org/licenses/LICENSE-2.0
9 - *
10 - * Unless required by applicable law or agreed to in writing, software
11 - * distributed under the License is distributed on an "AS IS" BASIS,
12 - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 - * See the License for the specific language governing permissions and
14 - * limitations under the License.
15 - */
16 -package org.onlab.netty;
17 -
18 -import org.slf4j.Logger;
19 -import org.slf4j.LoggerFactory;
20 -
21 -/**
22 - * A MessageHandler that simply logs the information.
23 - */
24 -public class LoggingHandler implements MessageHandler {
25 -
26 - private final Logger log = LoggerFactory.getLogger(getClass());
27 -
28 - @Override
29 - public void handle(Message message) {
30 - log.info("Received message. Payload has {} bytes", message.payload().length);
31 - }
32 -}
1 -/*
2 - * Copyright 2014 Open Networking Laboratory
3 - *
4 - * Licensed under the Apache License, Version 2.0 (the "License");
5 - * you may not use this file except in compliance with the License.
6 - * You may obtain a copy of the License at
7 - *
8 - * http://www.apache.org/licenses/LICENSE-2.0
9 - *
10 - * Unless required by applicable law or agreed to in writing, software
11 - * distributed under the License is distributed on an "AS IS" BASIS,
12 - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 - * See the License for the specific language governing permissions and
14 - * limitations under the License.
15 - */
16 -package org.onlab.netty;
17 -
18 -import java.io.IOException;
19 -
20 -/**
21 - * A unit of communication.
22 - * Has a payload. Also supports a feature to respond back to the sender.
23 - */
24 -public interface Message {
25 -
26 - /**
27 - * Returns the payload of this message.
28 - * @return message payload.
29 - */
30 - public byte[] payload();
31 -
32 - /**
33 - * Sends a reply back to the sender of this message.
34 - * @param data payload of the response.
35 - * @throws IOException if there is a communication error.
36 - */
37 - public void respond(byte[] data) throws IOException;
38 -}
...@@ -24,6 +24,7 @@ import java.util.List; ...@@ -24,6 +24,7 @@ import java.util.List;
24 24
25 import org.onlab.packet.IpAddress; 25 import org.onlab.packet.IpAddress;
26 import org.onlab.packet.IpAddress.Version; 26 import org.onlab.packet.IpAddress.Version;
27 +import org.onosproject.store.cluster.messaging.Endpoint;
27 import org.slf4j.Logger; 28 import org.slf4j.Logger;
28 import org.slf4j.LoggerFactory; 29 import org.slf4j.LoggerFactory;
29 30
...@@ -36,8 +37,6 @@ public class MessageDecoder extends ReplayingDecoder<DecoderState> { ...@@ -36,8 +37,6 @@ public class MessageDecoder extends ReplayingDecoder<DecoderState> {
36 37
37 private final Logger log = LoggerFactory.getLogger(getClass()); 38 private final Logger log = LoggerFactory.getLogger(getClass());
38 39
39 - private final NettyMessagingService messagingService;
40 -
41 private long messageId; 40 private long messageId;
42 private Version ipVersion; 41 private Version ipVersion;
43 private IpAddress senderIp; 42 private IpAddress senderIp;
...@@ -46,9 +45,8 @@ public class MessageDecoder extends ReplayingDecoder<DecoderState> { ...@@ -46,9 +45,8 @@ public class MessageDecoder extends ReplayingDecoder<DecoderState> {
46 private String messageType; 45 private String messageType;
47 private int contentLength; 46 private int contentLength;
48 47
49 - public MessageDecoder(NettyMessagingService messagingService) { 48 + public MessageDecoder() {
50 super(DecoderState.READ_MESSAGE_ID); 49 super(DecoderState.READ_MESSAGE_ID);
51 - this.messagingService = messagingService;
52 } 50 }
53 51
54 @Override 52 @Override
...@@ -91,7 +89,6 @@ public class MessageDecoder extends ReplayingDecoder<DecoderState> { ...@@ -91,7 +89,6 @@ public class MessageDecoder extends ReplayingDecoder<DecoderState> {
91 new Endpoint(senderIp, senderPort), 89 new Endpoint(senderIp, senderPort),
92 messageType, 90 messageType,
93 payload); 91 payload);
94 - message.setMessagingService(messagingService);
95 out.add(message); 92 out.add(message);
96 checkpoint(DecoderState.READ_MESSAGE_ID); 93 checkpoint(DecoderState.READ_MESSAGE_ID);
97 break; 94 break;
......
...@@ -24,6 +24,7 @@ import java.io.IOException; ...@@ -24,6 +24,7 @@ import java.io.IOException;
24 24
25 import org.onlab.packet.IpAddress; 25 import org.onlab.packet.IpAddress;
26 import org.onlab.packet.IpAddress.Version; 26 import org.onlab.packet.IpAddress.Version;
27 +import org.onosproject.store.cluster.messaging.Endpoint;
27 import org.slf4j.Logger; 28 import org.slf4j.Logger;
28 import org.slf4j.LoggerFactory; 29 import org.slf4j.LoggerFactory;
29 30
......
1 -/*
2 - * Copyright 2014 Open Networking Laboratory
3 - *
4 - * Licensed under the Apache License, Version 2.0 (the "License");
5 - * you may not use this file except in compliance with the License.
6 - * You may obtain a copy of the License at
7 - *
8 - * http://www.apache.org/licenses/LICENSE-2.0
9 - *
10 - * Unless required by applicable law or agreed to in writing, software
11 - * distributed under the License is distributed on an "AS IS" BASIS,
12 - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 - * See the License for the specific language governing permissions and
14 - * limitations under the License.
15 - */
16 -package org.onlab.netty;
17 -
18 -import java.io.IOException;
19 -
20 -/**
21 - * Handler for a message.
22 - */
23 -public interface MessageHandler {
24 -
25 - /**
26 - * Handles the message.
27 - *
28 - * @param message message.
29 - * @throws IOException if an error is encountered handling the message
30 - */
31 - public void handle(Message message) throws IOException;
32 -}
...@@ -46,10 +46,14 @@ import java.util.concurrent.Executor; ...@@ -46,10 +46,14 @@ import java.util.concurrent.Executor;
46 import java.util.concurrent.TimeUnit; 46 import java.util.concurrent.TimeUnit;
47 import java.util.concurrent.TimeoutException; 47 import java.util.concurrent.TimeoutException;
48 import java.util.concurrent.atomic.AtomicLong; 48 import java.util.concurrent.atomic.AtomicLong;
49 +import java.util.function.Consumer;
50 +import java.util.function.Function;
49 51
50 import org.apache.commons.pool.KeyedPoolableObjectFactory; 52 import org.apache.commons.pool.KeyedPoolableObjectFactory;
51 import org.apache.commons.pool.impl.GenericKeyedObjectPool; 53 import org.apache.commons.pool.impl.GenericKeyedObjectPool;
52 import org.onlab.packet.IpAddress; 54 import org.onlab.packet.IpAddress;
55 +import org.onosproject.store.cluster.messaging.Endpoint;
56 +import org.onosproject.store.cluster.messaging.MessagingService;
53 import org.slf4j.Logger; 57 import org.slf4j.Logger;
54 import org.slf4j.LoggerFactory; 58 import org.slf4j.LoggerFactory;
55 59
...@@ -59,17 +63,18 @@ import com.google.common.cache.RemovalListener; ...@@ -59,17 +63,18 @@ import com.google.common.cache.RemovalListener;
59 import com.google.common.cache.RemovalNotification; 63 import com.google.common.cache.RemovalNotification;
60 64
61 /** 65 /**
62 - * A Netty based implementation of MessagingService. 66 + * Implementation of MessagingService based on <a href="http://netty.io/">Netty</a> framework.
63 */ 67 */
64 public class NettyMessagingService implements MessagingService { 68 public class NettyMessagingService implements MessagingService {
65 69
66 private final Logger log = LoggerFactory.getLogger(getClass()); 70 private final Logger log = LoggerFactory.getLogger(getClass());
67 71
72 + private static final String REPLY_MESSAGE_TYPE = "NETTY_MESSAGING_REQUEST_REPLY";
73 +
68 private final Endpoint localEp; 74 private final Endpoint localEp;
69 - private final ConcurrentMap<String, MessageHandler> handlers = new ConcurrentHashMap<>(); 75 + private final ConcurrentMap<String, Consumer<InternalMessage>> handlers = new ConcurrentHashMap<>();
70 private final AtomicLong messageIdGenerator = new AtomicLong(0); 76 private final AtomicLong messageIdGenerator = new AtomicLong(0);
71 private final Cache<Long, CompletableFuture<byte[]>> responseFutures = CacheBuilder.newBuilder() 77 private final Cache<Long, CompletableFuture<byte[]>> responseFutures = CacheBuilder.newBuilder()
72 - .maximumSize(100000)
73 .expireAfterWrite(10, TimeUnit.SECONDS) 78 .expireAfterWrite(10, TimeUnit.SECONDS)
74 .removalListener(new RemovalListener<Long, CompletableFuture<byte[]>>() { 79 .removalListener(new RemovalListener<Long, CompletableFuture<byte[]>>() {
75 @Override 80 @Override
...@@ -124,6 +129,7 @@ public class NettyMessagingService implements MessagingService { ...@@ -124,6 +129,7 @@ public class NettyMessagingService implements MessagingService {
124 } 129 }
125 130
126 public void activate() throws InterruptedException { 131 public void activate() throws InterruptedException {
132 + channels.setLifo(false);
127 channels.setTestOnBorrow(true); 133 channels.setTestOnBorrow(true);
128 channels.setTestOnReturn(true); 134 channels.setTestOnReturn(true);
129 initEventLoopGroup(); 135 initEventLoopGroup();
...@@ -146,12 +152,10 @@ public class NettyMessagingService implements MessagingService { ...@@ -146,12 +152,10 @@ public class NettyMessagingService implements MessagingService {
146 152
147 @Override 153 @Override
148 public void sendAsync(Endpoint ep, String type, byte[] payload) throws IOException { 154 public void sendAsync(Endpoint ep, String type, byte[] payload) throws IOException {
149 - InternalMessage message = new InternalMessage.Builder(this) 155 + InternalMessage message = new InternalMessage(messageIdGenerator.incrementAndGet(),
150 - .withId(messageIdGenerator.incrementAndGet()) 156 + localEp,
151 - .withSender(localEp) 157 + type,
152 - .withType(type) 158 + payload);
153 - .withPayload(payload)
154 - .build();
155 sendAsync(ep, message); 159 sendAsync(ep, message);
156 } 160 }
157 161
...@@ -164,7 +168,7 @@ public class NettyMessagingService implements MessagingService { ...@@ -164,7 +168,7 @@ public class NettyMessagingService implements MessagingService {
164 try { 168 try {
165 try { 169 try {
166 channel = channels.borrowObject(ep); 170 channel = channels.borrowObject(ep);
167 - channel.eventLoop().execute(new WriteTask(channel, message)); 171 + channel.writeAndFlush(message).addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
168 } finally { 172 } finally {
169 channels.returnObject(ep, channel); 173 channels.returnObject(ep, channel);
170 } 174 }
...@@ -173,7 +177,6 @@ public class NettyMessagingService implements MessagingService { ...@@ -173,7 +177,6 @@ public class NettyMessagingService implements MessagingService {
173 } catch (Exception e) { 177 } catch (Exception e) {
174 throw new IOException(e); 178 throw new IOException(e);
175 } 179 }
176 -
177 } 180 }
178 181
179 @Override 182 @Override
...@@ -181,12 +184,7 @@ public class NettyMessagingService implements MessagingService { ...@@ -181,12 +184,7 @@ public class NettyMessagingService implements MessagingService {
181 CompletableFuture<byte[]> response = new CompletableFuture<>(); 184 CompletableFuture<byte[]> response = new CompletableFuture<>();
182 Long messageId = messageIdGenerator.incrementAndGet(); 185 Long messageId = messageIdGenerator.incrementAndGet();
183 responseFutures.put(messageId, response); 186 responseFutures.put(messageId, response);
184 - InternalMessage message = new InternalMessage.Builder(this) 187 + InternalMessage message = new InternalMessage(messageId, localEp, type, payload);
185 - .withId(messageId)
186 - .withSender(localEp)
187 - .withType(type)
188 - .withPayload(payload)
189 - .build();
190 try { 188 try {
191 sendAsync(ep, message); 189 sendAsync(ep, message);
192 } catch (Exception e) { 190 } catch (Exception e) {
...@@ -197,24 +195,26 @@ public class NettyMessagingService implements MessagingService { ...@@ -197,24 +195,26 @@ public class NettyMessagingService implements MessagingService {
197 } 195 }
198 196
199 @Override 197 @Override
200 - public void registerHandler(String type, MessageHandler handler) { 198 + public void registerHandler(String type, Consumer<byte[]> handler, Executor executor) {
201 - handlers.put(type, handler); 199 + handlers.put(type, message -> executor.execute(() -> handler.accept(message.payload())));
202 } 200 }
203 201
204 @Override 202 @Override
205 - public void registerHandler(String type, MessageHandler handler, Executor executor) { 203 + public void registerHandler(String type, Function<byte[], byte[]> handler, Executor executor) {
206 - handlers.put(type, new MessageHandler() { 204 + handlers.put(type, message -> executor.execute(() -> {
207 - @Override 205 + byte[] responsePayload = handler.apply(message.payload());
208 - public void handle(Message message) throws IOException { 206 + if (responsePayload != null) {
209 - executor.execute(() -> { 207 + InternalMessage response = new InternalMessage(message.id(),
210 - try { 208 + localEp,
211 - handler.handle(message); 209 + REPLY_MESSAGE_TYPE,
212 - } catch (Exception e) { 210 + responsePayload);
213 - log.debug("Failed to process message of type {}", type, e); 211 + try {
214 - } 212 + sendAsync(message.sender(), response);
215 - }); 213 + } catch (IOException e) {
214 + log.debug("Failed to respond", e);
215 + }
216 } 216 }
217 - }); 217 + }));
218 } 218 }
219 219
220 @Override 220 @Override
...@@ -222,14 +222,12 @@ public class NettyMessagingService implements MessagingService { ...@@ -222,14 +222,12 @@ public class NettyMessagingService implements MessagingService {
222 handlers.remove(type); 222 handlers.remove(type);
223 } 223 }
224 224
225 - private MessageHandler getMessageHandler(String type) {
226 - return handlers.get(type);
227 - }
228 -
229 private void startAcceptingConnections() throws InterruptedException { 225 private void startAcceptingConnections() throws InterruptedException {
230 ServerBootstrap b = new ServerBootstrap(); 226 ServerBootstrap b = new ServerBootstrap();
231 b.option(ChannelOption.WRITE_BUFFER_HIGH_WATER_MARK, 32 * 1024); 227 b.option(ChannelOption.WRITE_BUFFER_HIGH_WATER_MARK, 32 * 1024);
232 b.option(ChannelOption.WRITE_BUFFER_LOW_WATER_MARK, 8 * 1024); 228 b.option(ChannelOption.WRITE_BUFFER_LOW_WATER_MARK, 8 * 1024);
229 + b.option(ChannelOption.SO_RCVBUF, 1048576);
230 + b.option(ChannelOption.TCP_NODELAY, true);
233 b.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT); 231 b.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);
234 b.group(serverGroup, clientGroup) 232 b.group(serverGroup, clientGroup)
235 .channel(serverChannelClass) 233 .channel(serverChannelClass)
...@@ -258,8 +256,9 @@ public class NettyMessagingService implements MessagingService { ...@@ -258,8 +256,9 @@ public class NettyMessagingService implements MessagingService {
258 public Channel makeObject(Endpoint ep) throws Exception { 256 public Channel makeObject(Endpoint ep) throws Exception {
259 Bootstrap bootstrap = new Bootstrap(); 257 Bootstrap bootstrap = new Bootstrap();
260 bootstrap.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT); 258 bootstrap.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);
261 - bootstrap.option(ChannelOption.WRITE_BUFFER_HIGH_WATER_MARK, 32 * 1024); 259 + bootstrap.option(ChannelOption.WRITE_BUFFER_HIGH_WATER_MARK, 10 * 64 * 1024);
262 - bootstrap.option(ChannelOption.WRITE_BUFFER_LOW_WATER_MARK, 8 * 1024); 260 + bootstrap.option(ChannelOption.WRITE_BUFFER_LOW_WATER_MARK, 10 * 32 * 1024);
261 + bootstrap.option(ChannelOption.SO_SNDBUF, 1048576);
263 bootstrap.group(clientGroup); 262 bootstrap.group(clientGroup);
264 // TODO: Make this faster: 263 // TODO: Make this faster:
265 // http://normanmaurer.me/presentations/2014-facebook-eng-netty/slides.html#37.0 264 // http://normanmaurer.me/presentations/2014-facebook-eng-netty/slides.html#37.0
...@@ -268,6 +267,7 @@ public class NettyMessagingService implements MessagingService { ...@@ -268,6 +267,7 @@ public class NettyMessagingService implements MessagingService {
268 bootstrap.handler(new OnosCommunicationChannelInitializer()); 267 bootstrap.handler(new OnosCommunicationChannelInitializer());
269 // Start the client. 268 // Start the client.
270 ChannelFuture f = bootstrap.connect(ep.host().toString(), ep.port()).sync(); 269 ChannelFuture f = bootstrap.connect(ep.host().toString(), ep.port()).sync();
270 + log.info("Established a new connection to {}", ep);
271 return f.channel(); 271 return f.channel();
272 } 272 }
273 273
...@@ -291,27 +291,11 @@ public class NettyMessagingService implements MessagingService { ...@@ -291,27 +291,11 @@ public class NettyMessagingService implements MessagingService {
291 protected void initChannel(SocketChannel channel) throws Exception { 291 protected void initChannel(SocketChannel channel) throws Exception {
292 channel.pipeline() 292 channel.pipeline()
293 .addLast("encoder", encoder) 293 .addLast("encoder", encoder)
294 - .addLast("decoder", new MessageDecoder(NettyMessagingService.this)) 294 + .addLast("decoder", new MessageDecoder())
295 .addLast("handler", dispatcher); 295 .addLast("handler", dispatcher);
296 } 296 }
297 } 297 }
298 298
299 - private static class WriteTask implements Runnable {
300 -
301 - private final InternalMessage message;
302 - private final Channel channel;
303 -
304 - public WriteTask(Channel channel, InternalMessage message) {
305 - this.channel = channel;
306 - this.message = message;
307 - }
308 -
309 - @Override
310 - public void run() {
311 - channel.writeAndFlush(message).addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
312 - }
313 - }
314 -
315 @ChannelHandler.Sharable 299 @ChannelHandler.Sharable
316 private class InboundMessageDispatcher extends SimpleChannelInboundHandler<InternalMessage> { 300 private class InboundMessageDispatcher extends SimpleChannelInboundHandler<InternalMessage> {
317 301
...@@ -329,10 +313,10 @@ public class NettyMessagingService implements MessagingService { ...@@ -329,10 +313,10 @@ public class NettyMessagingService implements MessagingService {
329 313
330 private void dispatchLocally(InternalMessage message) throws IOException { 314 private void dispatchLocally(InternalMessage message) throws IOException {
331 String type = message.type(); 315 String type = message.type();
332 - if (InternalMessage.REPLY_MESSAGE_TYPE.equals(type)) { 316 + if (REPLY_MESSAGE_TYPE.equals(type)) {
333 try { 317 try {
334 CompletableFuture<byte[]> futureResponse = 318 CompletableFuture<byte[]> futureResponse =
335 - NettyMessagingService.this.responseFutures.getIfPresent(message.id()); 319 + responseFutures.getIfPresent(message.id());
336 if (futureResponse != null) { 320 if (futureResponse != null) {
337 futureResponse.complete(message.payload()); 321 futureResponse.complete(message.payload());
338 } else { 322 } else {
...@@ -341,13 +325,13 @@ public class NettyMessagingService implements MessagingService { ...@@ -341,13 +325,13 @@ public class NettyMessagingService implements MessagingService {
341 + " request handle", message.id(), message.sender()); 325 + " request handle", message.id(), message.sender());
342 } 326 }
343 } finally { 327 } finally {
344 - NettyMessagingService.this.responseFutures.invalidate(message.id()); 328 + responseFutures.invalidate(message.id());
345 } 329 }
346 return; 330 return;
347 } 331 }
348 - MessageHandler handler = NettyMessagingService.this.getMessageHandler(type); 332 + Consumer<InternalMessage> handler = handlers.get(type);
349 if (handler != null) { 333 if (handler != null) {
350 - handler.handle(message); 334 + handler.accept(message);
351 } else { 335 } else {
352 log.debug("No handler registered for {}", type); 336 log.debug("No handler registered for {}", type);
353 } 337 }
......
...@@ -18,13 +18,17 @@ package org.onlab.netty; ...@@ -18,13 +18,17 @@ package org.onlab.netty;
18 import static org.junit.Assert.assertArrayEquals; 18 import static org.junit.Assert.assertArrayEquals;
19 19
20 import java.net.InetAddress; 20 import java.net.InetAddress;
21 -import java.util.concurrent.Future; 21 +import java.util.concurrent.CompletableFuture;
22 import java.util.concurrent.TimeUnit; 22 import java.util.concurrent.TimeUnit;
23 +import java.util.function.Function;
23 24
24 import org.apache.commons.lang3.RandomUtils; 25 import org.apache.commons.lang3.RandomUtils;
25 import org.junit.Ignore; 26 import org.junit.Ignore;
26 import org.junit.Test; 27 import org.junit.Test;
27 import org.onlab.packet.IpAddress; 28 import org.onlab.packet.IpAddress;
29 +import org.onosproject.store.cluster.messaging.Endpoint;
30 +
31 +import com.google.common.util.concurrent.MoreExecutors;
28 32
29 /** 33 /**
30 * Simple ping-pong test that exercises NettyMessagingService. 34 * Simple ping-pong test that exercises NettyMessagingService.
...@@ -39,9 +43,9 @@ public class PingPongTest { ...@@ -39,9 +43,9 @@ public class PingPongTest {
39 try { 43 try {
40 pinger.activate(); 44 pinger.activate();
41 ponger.activate(); 45 ponger.activate();
42 - ponger.registerHandler("echo", new EchoHandler()); 46 + ponger.registerHandler("echo", Function.identity(), MoreExecutors.directExecutor());
43 byte[] payload = RandomUtils.nextBytes(100); 47 byte[] payload = RandomUtils.nextBytes(100);
44 - Future<byte[]> responseFuture = 48 + CompletableFuture<byte[]> responseFuture =
45 pinger.sendAndReceive( 49 pinger.sendAndReceive(
46 new Endpoint(IpAddress.valueOf(InetAddress.getLocalHost()), 9086), "echo", payload); 50 new Endpoint(IpAddress.valueOf(InetAddress.getLocalHost()), 9086), "echo", payload);
47 assertArrayEquals(payload, responseFuture.get(10000, TimeUnit.MILLISECONDS)); 51 assertArrayEquals(payload, responseFuture.get(10000, TimeUnit.MILLISECONDS));
......
...@@ -37,6 +37,14 @@ ...@@ -37,6 +37,14 @@
37 <artifactId>guava-testlib</artifactId> 37 <artifactId>guava-testlib</artifactId>
38 <scope>test</scope> 38 <scope>test</scope>
39 </dependency> 39 </dependency>
40 + <dependency>
41 + <groupId>commons-pool</groupId>
42 + <artifactId>commons-pool</artifactId>
43 + </dependency>
44 + <dependency>
45 + <groupId>org.onosproject</groupId>
46 + <artifactId>onos-api</artifactId>
47 + </dependency>
40 <dependency> 48 <dependency>
41 <groupId>org.onosproject</groupId> 49 <groupId>org.onosproject</groupId>
42 <artifactId>onlab-misc</artifactId> 50 <artifactId>onlab-misc</artifactId>
......
1 +/*
2 + * Copyright 2015 Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +package org.onlab.nio.service;
17 +
18 +import java.io.IOException;
19 +import java.nio.channels.ByteChannel;
20 +import java.nio.channels.SelectionKey;
21 +import java.util.List;
22 +import java.util.function.Consumer;
23 +
24 +import org.onlab.nio.IOLoop;
25 +import org.onlab.nio.MessageStream;
26 +
27 +/**
28 + * IOLoop for transporting DefaultMessages.
29 + */
30 +public class DefaultIOLoop extends IOLoop<DefaultMessage, DefaultMessageStream> {
31 +
32 + public static final int SELECT_TIMEOUT_MILLIS = 500;
33 + private static final int MAX_IDLE_TIMEOUT_MILLIS = 1000;
34 + private static final int BUFFER_SIZE = 1024 * 1024;
35 + private final Consumer<DefaultMessage> consumer;
36 +
37 + public DefaultIOLoop(Consumer<DefaultMessage> consumer) throws IOException {
38 + this(SELECT_TIMEOUT_MILLIS, consumer);
39 + }
40 +
41 + public DefaultIOLoop(long timeout, Consumer<DefaultMessage> consumer) throws IOException {
42 + super(timeout);
43 + this.consumer = consumer;
44 + }
45 +
46 + @Override
47 + protected DefaultMessageStream createStream(ByteChannel byteChannel) {
48 + return new DefaultMessageStream(this, byteChannel, BUFFER_SIZE, MAX_IDLE_TIMEOUT_MILLIS);
49 + }
50 +
51 + @Override
52 + protected void processMessages(List<DefaultMessage> messages, MessageStream<DefaultMessage> stream) {
53 + messages.forEach(consumer);
54 + }
55 +
56 + @Override
57 + protected void connect(SelectionKey key) throws IOException {
58 + DefaultMessageStream stream = (DefaultMessageStream) key.attachment();
59 + try {
60 + super.connect(key);
61 + stream.connected();
62 + } catch (Exception e) {
63 + stream.connectFailed(e);
64 + }
65 + }
66 +}
...\ No newline at end of file ...\ No newline at end of file
1 +/*
2 + * Copyright 2015 Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +package org.onlab.nio.service;
17 +
18 +import static com.google.common.base.Preconditions.checkNotNull;
19 +
20 +import org.onlab.nio.AbstractMessage;
21 +import org.onlab.packet.IpAddress;
22 +import org.onlab.util.ByteArraySizeHashPrinter;
23 +import org.onosproject.store.cluster.messaging.Endpoint;
24 +
25 +import com.google.common.base.Charsets;
26 +import com.google.common.base.MoreObjects;
27 +
28 +/**
29 + * Default message.
30 + */
31 +public class DefaultMessage extends AbstractMessage {
32 +
33 + private long id;
34 + private Endpoint sender;
35 + private String type;
36 + private byte[] payload;
37 +
38 + /**
39 + * Creates a new message with the specified data.
40 + *
41 + * @param id message id
42 + * @param type message type
43 + * @param sender sender endpoint
44 + * @param payload message payload
45 + */
46 + DefaultMessage(long id, Endpoint sender, String type, byte[] payload) {
47 + this.id = id;
48 + this.type = checkNotNull(type, "Type cannot be null");
49 + this.sender = checkNotNull(sender, "Sender cannot be null");
50 + this.payload = checkNotNull(payload, "Payload cannot be null");
51 +
52 + byte[] messageTypeBytes = type.getBytes(Charsets.UTF_8);
53 + IpAddress senderIp = sender.host();
54 + byte[] ipOctets = senderIp.toOctets();
55 +
56 + length = 25 + ipOctets.length + messageTypeBytes.length + payload.length;
57 + }
58 +
59 + /**
60 + * Returns message id.
61 + *
62 + * @return message id
63 + */
64 + public long id() {
65 + return id;
66 + }
67 +
68 + /**
69 + * Returns message sender.
70 + *
71 + * @return message sender
72 + */
73 + public Endpoint sender() {
74 + return sender;
75 + }
76 +
77 + /**
78 + * Returns message type.
79 + *
80 + * @return message type
81 + */
82 + public String type() {
83 + return type;
84 + }
85 +
86 + /**
87 + * Returns message payload.
88 + *
89 + * @return payload
90 + */
91 + public byte[] payload() {
92 + return payload;
93 + }
94 +
95 + @Override
96 + public String toString() {
97 + return MoreObjects.toStringHelper(this)
98 + .add("id", id)
99 + .add("type", type)
100 + .add("sender", sender)
101 + .add("payload", ByteArraySizeHashPrinter.of(payload))
102 + .toString();
103 + }
104 +}
...\ No newline at end of file ...\ No newline at end of file
1 +/*
2 + * Copyright 2015 Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +package org.onlab.nio.service;
17 +
18 +import java.nio.ByteBuffer;
19 +import java.nio.channels.ByteChannel;
20 +import java.util.concurrent.CompletableFuture;
21 +import java.util.concurrent.atomic.AtomicInteger;
22 +
23 +import org.onlab.nio.IOLoop;
24 +import org.onlab.nio.MessageStream;
25 +import org.onlab.packet.IpAddress;
26 +import org.onlab.packet.IpAddress.Version;
27 +import org.onosproject.store.cluster.messaging.Endpoint;
28 +
29 +import com.google.common.base.Charsets;
30 +
31 +/**
32 + * Default bi-directional message stream for transferring messages to &amp; from the
33 + * network via two byte buffers.
34 + */
35 +public class DefaultMessageStream extends MessageStream<DefaultMessage> {
36 +
37 + private final CompletableFuture<Void> connectFuture = new CompletableFuture<>();
38 +
39 + public DefaultMessageStream(
40 + IOLoop<DefaultMessage, ?> loop,
41 + ByteChannel byteChannel,
42 + int bufferSize,
43 + int maxIdleMillis) {
44 + super(loop, byteChannel, bufferSize, maxIdleMillis);
45 + }
46 +
47 + public CompletableFuture<DefaultMessageStream> connectedFuture() {
48 + return connectFuture.thenApply(v -> this);
49 + }
50 +
51 + private final AtomicInteger messageLength = new AtomicInteger(-1);
52 +
53 + @Override
54 + protected DefaultMessage read(ByteBuffer buffer) {
55 + if (messageLength.get() == -1) {
56 + // check if we can read the message length.
57 + if (buffer.remaining() < Integer.BYTES) {
58 + return null;
59 + } else {
60 + messageLength.set(buffer.getInt());
61 + }
62 + }
63 +
64 + if (buffer.remaining() < messageLength.get()) {
65 + return null;
66 + }
67 +
68 + long id = buffer.getLong();
69 + Version ipVersion = buffer.get() == 0x0 ? Version.INET : Version.INET6;
70 + byte[] octects = new byte[IpAddress.byteLength(ipVersion)];
71 + buffer.get(octects);
72 + IpAddress senderIp = IpAddress.valueOf(ipVersion, octects);
73 + int senderPort = buffer.getInt();
74 + int messageTypeByteLength = buffer.getInt();
75 + byte[] messageTypeBytes = new byte[messageTypeByteLength];
76 + buffer.get(messageTypeBytes);
77 + String messageType = new String(messageTypeBytes, Charsets.UTF_8);
78 + int payloadLength = buffer.getInt();
79 + byte[] payloadBytes = new byte[payloadLength];
80 + buffer.get(payloadBytes);
81 +
82 + // reset for next message
83 + messageLength.set(-1);
84 +
85 + return new DefaultMessage(id, new Endpoint(senderIp, senderPort), messageType, payloadBytes);
86 + }
87 +
88 + @Override
89 + protected void write(DefaultMessage message, ByteBuffer buffer) {
90 + Endpoint sender = message.sender();
91 + byte[] messageTypeBytes = message.type().getBytes(Charsets.UTF_8);
92 + IpAddress senderIp = sender.host();
93 + byte[] ipOctets = senderIp.toOctets();
94 + byte[] payload = message.payload();
95 +
96 + int messageLength = 21 + ipOctets.length + messageTypeBytes.length + payload.length;
97 +
98 + buffer.putInt(messageLength);
99 +
100 + buffer.putLong(message.id());
101 +
102 + if (senderIp.version() == Version.INET) {
103 + buffer.put((byte) 0x0);
104 + } else {
105 + buffer.put((byte) 0x1);
106 + }
107 + buffer.put(ipOctets);
108 +
109 + // write sender port
110 + buffer.putInt(sender.port());
111 +
112 + // write length of message type
113 + buffer.putInt(messageTypeBytes.length);
114 +
115 + // write message type bytes
116 + buffer.put(messageTypeBytes);
117 +
118 + // write payload length
119 + buffer.putInt(payload.length);
120 +
121 + // write payload.
122 + buffer.put(payload);
123 + }
124 +
125 + /**
126 + * Callback invoked when the stream is successfully connected.
127 + */
128 + public void connected() {
129 + connectFuture.complete(null);
130 + }
131 +
132 + /**
133 + * Callback invoked when the stream fails to connect.
134 + * @param cause failure cause
135 + */
136 + public void connectFailed(Throwable cause) {
137 + connectFuture.completeExceptionally(cause);
138 + }
139 +}
...\ No newline at end of file ...\ No newline at end of file