Aaron Kruglikov
Committed by Gerrit Code Review

Makes establishing connections between onos nodes asynchronous, prevents threads…

… blocking when nodes are inaccessible.

Change-Id: I46ce54505e8c4c34b56009412ddb1d645c83aaa3
...@@ -96,8 +96,8 @@ public class NettyMessaging implements MessagingService { ...@@ -96,8 +96,8 @@ public class NettyMessaging implements MessagingService {
96 }) 96 })
97 .build(); 97 .build();
98 98
99 - private final GenericKeyedObjectPool<Endpoint, Channel> channels 99 + private final GenericKeyedObjectPool<Endpoint, Connection> channels
100 - = new GenericKeyedObjectPool<Endpoint, Channel>(new OnosCommunicationChannelFactory()); 100 + = new GenericKeyedObjectPool<Endpoint, Connection>(new OnosCommunicationChannelFactory());
101 101
102 private EventLoopGroup serverGroup; 102 private EventLoopGroup serverGroup;
103 private EventLoopGroup clientGroup; 103 private EventLoopGroup clientGroup;
...@@ -179,18 +179,13 @@ public class NettyMessaging implements MessagingService { ...@@ -179,18 +179,13 @@ public class NettyMessaging implements MessagingService {
179 179
180 CompletableFuture<Void> future = new CompletableFuture<>(); 180 CompletableFuture<Void> future = new CompletableFuture<>();
181 try { 181 try {
182 - Channel channel = null; 182 + Connection connection = null;
183 try { 183 try {
184 - channel = channels.borrowObject(ep); 184 + connection = channels.borrowObject(ep);
185 - channel.writeAndFlush(message).addListener(channelFuture -> { 185 + connection.send(message, future);
186 - if (!channelFuture.isSuccess()) { 186 +
187 - future.completeExceptionally(channelFuture.cause());
188 - } else {
189 - future.complete(null);
190 - }
191 - });
192 } finally { 187 } finally {
193 - channels.returnObject(ep, channel); 188 + channels.returnObject(ep, connection);
194 } 189 }
195 } catch (Exception e) { 190 } catch (Exception e) {
196 future.completeExceptionally(e); 191 future.completeExceptionally(e);
...@@ -292,21 +287,22 @@ public class NettyMessaging implements MessagingService { ...@@ -292,21 +287,22 @@ public class NettyMessaging implements MessagingService {
292 } 287 }
293 288
294 private class OnosCommunicationChannelFactory 289 private class OnosCommunicationChannelFactory
295 - implements KeyedPoolableObjectFactory<Endpoint, Channel> { 290 + implements KeyedPoolableObjectFactory<Endpoint, Connection> {
296 291
297 @Override 292 @Override
298 - public void activateObject(Endpoint endpoint, Channel channel) 293 + public void activateObject(Endpoint endpoint, Connection connection)
299 throws Exception { 294 throws Exception {
300 } 295 }
301 296
302 @Override 297 @Override
303 - public void destroyObject(Endpoint ep, Channel channel) throws Exception { 298 + public void destroyObject(Endpoint ep, Connection connection) throws Exception {
304 log.debug("Closing connection to {}", ep); 299 log.debug("Closing connection to {}", ep);
305 - channel.close(); 300 + //Is this the right way to destroy?
301 + connection.destroy();
306 } 302 }
307 303
308 @Override 304 @Override
309 - public Channel makeObject(Endpoint ep) throws Exception { 305 + public Connection makeObject(Endpoint ep) throws Exception {
310 Bootstrap bootstrap = new Bootstrap(); 306 Bootstrap bootstrap = new Bootstrap();
311 bootstrap.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT); 307 bootstrap.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);
312 bootstrap.option(ChannelOption.WRITE_BUFFER_HIGH_WATER_MARK, 10 * 64 * 1024); 308 bootstrap.option(ChannelOption.WRITE_BUFFER_HIGH_WATER_MARK, 10 * 64 * 1024);
...@@ -324,19 +320,28 @@ public class NettyMessaging implements MessagingService { ...@@ -324,19 +320,28 @@ public class NettyMessaging implements MessagingService {
324 bootstrap.handler(new OnosCommunicationChannelInitializer()); 320 bootstrap.handler(new OnosCommunicationChannelInitializer());
325 } 321 }
326 // Start the client. 322 // Start the client.
327 - ChannelFuture f = bootstrap.connect(ep.host().toString(), ep.port()).sync(); 323 + CompletableFuture<Channel> retFuture = new CompletableFuture<>();
324 + ChannelFuture f = bootstrap.connect(ep.host().toString(), ep.port());
325 +
326 + f.addListener(future -> {
327 + if (future.isSuccess()) {
328 + retFuture.complete(f.channel());
329 + } else {
330 + retFuture.completeExceptionally(future.cause());
331 + }
332 + });
328 log.debug("Established a new connection to {}", ep); 333 log.debug("Established a new connection to {}", ep);
329 - return f.channel(); 334 + return new Connection(retFuture);
330 } 335 }
331 336
332 @Override 337 @Override
333 - public void passivateObject(Endpoint ep, Channel channel) 338 + public void passivateObject(Endpoint ep, Connection connection)
334 throws Exception { 339 throws Exception {
335 } 340 }
336 341
337 @Override 342 @Override
338 - public boolean validateObject(Endpoint ep, Channel channel) { 343 + public boolean validateObject(Endpoint ep, Connection connection) {
339 - return channel.isOpen(); 344 + return connection.validate();
340 } 345 }
341 } 346 }
342 347
...@@ -486,4 +491,62 @@ public class NettyMessaging implements MessagingService { ...@@ -486,4 +491,62 @@ public class NettyMessaging implements MessagingService {
486 executor.execute(() -> future.completeExceptionally(error)); 491 executor.execute(() -> future.completeExceptionally(error));
487 } 492 }
488 } 493 }
494 + private final class Connection {
495 + private final CompletableFuture<Channel> internalFuture;
496 +
497 + public Connection(CompletableFuture<Channel> internalFuture) {
498 + this.internalFuture = internalFuture;
499 + }
500 +
501 + /**
502 + * Sends a message out on its channel and associated the message with a
503 + * completable future used for signaling.
504 + * @param message the message to be sent
505 + * @param future a future that is completed normally or exceptionally if
506 + * message sending succeeds or fails respectively
507 + */
508 + public void send(Object message, CompletableFuture<Void> future) {
509 + internalFuture.whenComplete((channel, throwable) -> {
510 + if (throwable == null) {
511 + channel.writeAndFlush(message).addListener(channelFuture -> {
512 + if (!channelFuture.isSuccess()) {
513 + future.completeExceptionally(channelFuture.cause());
514 + } else {
515 + future.complete(null);
516 + }
517 + });
518 + } else {
519 + future.completeExceptionally(throwable);
520 + }
521 +
522 + });
523 + }
524 +
525 + /**
526 + * Destroys a channel by closing its channel (if it exists) and
527 + * cancelling its future.
528 + */
529 + public void destroy() {
530 + Channel channel = internalFuture.getNow(null);
531 + if (channel != null) {
532 + channel.close();
533 + }
534 + internalFuture.cancel(false);
535 + }
536 +
537 + /**
538 + * Determines whether the connection is valid meaning it is either
539 + * complete with and active channel
540 + * or it has not yet completed.
541 + * @return true if the channel has an active connection or has not
542 + * yet completed
543 + */
544 + public boolean validate() {
545 + if (internalFuture.isCompletedExceptionally()) {
546 + return false;
547 + }
548 + Channel channel = internalFuture.getNow(null);
549 + return channel == null || channel.isActive();
550 + }
551 + }
489 } 552 }
......