Aaron Kruglikov
Committed by Gerrit Code Review

Migrating netty messaging into netty messaging manager.

Change-Id: I971db195c9dc155cdf76850f0427ef9b9210113c
1 /* 1 /*
2 - * Copyright 2014-2015 Open Networking Laboratory 2 + * Copyright 2016 Open Networking Laboratory
3 * 3 *
4 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License. 5 * you may not use this file except in compliance with the License.
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
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.impl;
17 17
18 /** 18 /**
19 * State transitions a decoder goes through as it is decoding an incoming message. 19 * State transitions a decoder goes through as it is decoding an incoming message.
......
1 /* 1 /*
2 - * Copyright 2014-2015 Open Networking Laboratory 2 + * Copyright 2016 Open Networking Laboratory
3 * 3 *
4 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License. 5 * you may not use this file except in compliance with the License.
...@@ -13,13 +13,12 @@ ...@@ -13,13 +13,12 @@
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.impl;
17 17
18 +import com.google.common.base.MoreObjects;
18 import org.onlab.util.ByteArraySizeHashPrinter; 19 import org.onlab.util.ByteArraySizeHashPrinter;
19 import org.onosproject.store.cluster.messaging.Endpoint; 20 import org.onosproject.store.cluster.messaging.Endpoint;
20 21
21 -import com.google.common.base.MoreObjects;
22 -
23 /** 22 /**
24 * Internal message representation with additional attributes 23 * Internal message representation with additional attributes
25 * for supporting, synchronous request/reply behavior. 24 * for supporting, synchronous request/reply behavior.
......
1 /* 1 /*
2 - * Copyright 2014-2015 Open Networking Laboratory 2 + * Copyright 2016 Open Networking Laboratory
3 * 3 *
4 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License. 5 * you may not use this file except in compliance with the License.
...@@ -13,21 +13,19 @@ ...@@ -13,21 +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.impl;
17 17
18 +import com.google.common.base.Charsets;
18 import io.netty.buffer.ByteBuf; 19 import io.netty.buffer.ByteBuf;
19 import io.netty.channel.ChannelHandlerContext; 20 import io.netty.channel.ChannelHandlerContext;
20 import io.netty.handler.codec.ReplayingDecoder; 21 import io.netty.handler.codec.ReplayingDecoder;
21 -
22 -import java.util.List;
23 -
24 import org.onlab.packet.IpAddress; 22 import org.onlab.packet.IpAddress;
25 import org.onlab.packet.IpAddress.Version; 23 import org.onlab.packet.IpAddress.Version;
26 import org.onosproject.store.cluster.messaging.Endpoint; 24 import org.onosproject.store.cluster.messaging.Endpoint;
27 import org.slf4j.Logger; 25 import org.slf4j.Logger;
28 import org.slf4j.LoggerFactory; 26 import org.slf4j.LoggerFactory;
29 27
30 -import com.google.common.base.Charsets; 28 +import java.util.List;
31 29
32 import static com.google.common.base.Preconditions.checkState; 30 import static com.google.common.base.Preconditions.checkState;
33 31
...@@ -54,7 +52,7 @@ public class MessageDecoder extends ReplayingDecoder<DecoderState> { ...@@ -54,7 +52,7 @@ public class MessageDecoder extends ReplayingDecoder<DecoderState> {
54 } 52 }
55 53
56 @Override 54 @Override
57 - @java.lang.SuppressWarnings("squid:S128") // suppress switch fall through warning 55 + @SuppressWarnings("squid:S128") // suppress switch fall through warning
58 protected void decode( 56 protected void decode(
59 ChannelHandlerContext context, 57 ChannelHandlerContext context,
60 ByteBuf buffer, 58 ByteBuf buffer,
...@@ -97,9 +95,9 @@ public class MessageDecoder extends ReplayingDecoder<DecoderState> { ...@@ -97,9 +95,9 @@ public class MessageDecoder extends ReplayingDecoder<DecoderState> {
97 byte[] payload = new byte[contentLength]; 95 byte[] payload = new byte[contentLength];
98 buffer.readBytes(payload); 96 buffer.readBytes(payload);
99 InternalMessage message = new InternalMessage(messageId, 97 InternalMessage message = new InternalMessage(messageId,
100 - new Endpoint(senderIp, senderPort), 98 + new Endpoint(senderIp, senderPort),
101 - messageType, 99 + messageType,
102 - payload); 100 + payload);
103 out.add(message); 101 out.add(message);
104 checkpoint(DecoderState.READ_MESSAGE_PREAMBLE); 102 checkpoint(DecoderState.READ_MESSAGE_PREAMBLE);
105 break; 103 break;
......
1 /* 1 /*
2 - * Copyright 2014-2015 Open Networking Laboratory 2 + * Copyright 2016 Open Networking Laboratory
3 * 3 *
4 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License. 5 * you may not use this file except in compliance with the License.
...@@ -13,22 +13,20 @@ ...@@ -13,22 +13,20 @@
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.impl;
17 17
18 +import com.google.common.base.Charsets;
18 import io.netty.buffer.ByteBuf; 19 import io.netty.buffer.ByteBuf;
19 import io.netty.channel.ChannelHandler.Sharable; 20 import io.netty.channel.ChannelHandler.Sharable;
20 import io.netty.channel.ChannelHandlerContext; 21 import io.netty.channel.ChannelHandlerContext;
21 import io.netty.handler.codec.MessageToByteEncoder; 22 import io.netty.handler.codec.MessageToByteEncoder;
22 -
23 -import java.io.IOException;
24 -
25 import org.onlab.packet.IpAddress; 23 import org.onlab.packet.IpAddress;
26 import org.onlab.packet.IpAddress.Version; 24 import org.onlab.packet.IpAddress.Version;
27 import org.onosproject.store.cluster.messaging.Endpoint; 25 import org.onosproject.store.cluster.messaging.Endpoint;
28 import org.slf4j.Logger; 26 import org.slf4j.Logger;
29 import org.slf4j.LoggerFactory; 27 import org.slf4j.LoggerFactory;
30 28
31 -import com.google.common.base.Charsets; 29 +import java.io.IOException;
32 30
33 /** 31 /**
34 * Encode InternalMessage out into a byte buffer. 32 * Encode InternalMessage out into a byte buffer.
......
...@@ -17,29 +17,114 @@ package org.onosproject.store.cluster.messaging.impl; ...@@ -17,29 +17,114 @@ package org.onosproject.store.cluster.messaging.impl;
17 17
18 import com.google.common.base.Strings; 18 import com.google.common.base.Strings;
19 19
20 +import com.google.common.cache.Cache;
21 +import com.google.common.cache.CacheBuilder;
22 +import com.google.common.cache.RemovalListener;
23 +import com.google.common.cache.RemovalNotification;
24 +import com.google.common.util.concurrent.MoreExecutors;
25 +import io.netty.bootstrap.Bootstrap;
26 +import io.netty.bootstrap.ServerBootstrap;
27 +import io.netty.buffer.PooledByteBufAllocator;
28 +import io.netty.channel.Channel;
29 +import io.netty.channel.ChannelFuture;
30 +import io.netty.channel.ChannelHandler;
31 +import io.netty.channel.ChannelHandlerContext;
32 +import io.netty.channel.ChannelInitializer;
33 +import io.netty.channel.ChannelOption;
34 +import io.netty.channel.EventLoopGroup;
35 +import io.netty.channel.ServerChannel;
36 +import io.netty.channel.SimpleChannelInboundHandler;
37 +import io.netty.channel.epoll.EpollEventLoopGroup;
38 +import io.netty.channel.epoll.EpollServerSocketChannel;
39 +import io.netty.channel.epoll.EpollSocketChannel;
40 +import io.netty.channel.nio.NioEventLoopGroup;
41 +import io.netty.channel.socket.SocketChannel;
42 +import io.netty.channel.socket.nio.NioServerSocketChannel;
43 +import io.netty.channel.socket.nio.NioSocketChannel;
44 +import org.apache.commons.pool.KeyedPoolableObjectFactory;
45 +import org.apache.commons.pool.impl.GenericKeyedObjectPool;
20 import org.apache.felix.scr.annotations.Activate; 46 import org.apache.felix.scr.annotations.Activate;
21 import org.apache.felix.scr.annotations.Component; 47 import org.apache.felix.scr.annotations.Component;
22 import org.apache.felix.scr.annotations.Deactivate; 48 import org.apache.felix.scr.annotations.Deactivate;
23 import org.apache.felix.scr.annotations.Reference; 49 import org.apache.felix.scr.annotations.Reference;
24 import org.apache.felix.scr.annotations.ReferenceCardinality; 50 import org.apache.felix.scr.annotations.ReferenceCardinality;
25 import org.apache.felix.scr.annotations.Service; 51 import org.apache.felix.scr.annotations.Service;
26 -import org.onlab.netty.NettyMessaging; 52 +import org.onlab.netty.InternalMessage;
53 +import org.onlab.netty.MessageDecoder;
54 +import org.onlab.netty.MessageEncoder;
55 +import org.onlab.util.Tools;
27 import org.onosproject.cluster.ClusterMetadataService; 56 import org.onosproject.cluster.ClusterMetadataService;
28 import org.onosproject.cluster.ControllerNode; 57 import org.onosproject.cluster.ControllerNode;
29 import org.onosproject.store.cluster.messaging.Endpoint; 58 import org.onosproject.store.cluster.messaging.Endpoint;
59 +import org.onosproject.store.cluster.messaging.MessagingService;
30 import org.slf4j.Logger; 60 import org.slf4j.Logger;
31 import org.slf4j.LoggerFactory; 61 import org.slf4j.LoggerFactory;
32 62
63 +import javax.net.ssl.KeyManagerFactory;
64 +import javax.net.ssl.SSLContext;
65 +import javax.net.ssl.SSLEngine;
66 +import javax.net.ssl.TrustManagerFactory;
67 +import java.io.FileInputStream;
68 +import java.io.IOException;
69 +import java.security.KeyStore;
70 +import java.util.Map;
71 +import java.util.concurrent.CompletableFuture;
72 +import java.util.concurrent.ConcurrentHashMap;
73 +import java.util.concurrent.Executor;
74 +import java.util.concurrent.RejectedExecutionException;
75 +import java.util.concurrent.TimeUnit;
76 +import java.util.concurrent.TimeoutException;
77 +import java.util.concurrent.atomic.AtomicBoolean;
78 +import java.util.concurrent.atomic.AtomicLong;
79 +import java.util.function.BiConsumer;
80 +import java.util.function.BiFunction;
81 +import java.util.function.Consumer;
82 +
33 /** 83 /**
34 * Netty based MessagingService. 84 * Netty based MessagingService.
35 */ 85 */
36 @Component(immediate = true, enabled = true) 86 @Component(immediate = true, enabled = true)
37 @Service 87 @Service
38 -public class NettyMessagingManager extends NettyMessaging { 88 +public class NettyMessagingManager implements MessagingService {
89 +
90 + private static final short MIN_KS_LENGTH = 6;
39 91
40 private final Logger log = LoggerFactory.getLogger(getClass()); 92 private final Logger log = LoggerFactory.getLogger(getClass());
41 93
42 - private static final short MIN_KS_LENGTH = 6; 94 + private static final String REPLY_MESSAGE_TYPE = "NETTY_MESSAGING_REQUEST_REPLY";
95 +
96 + private Endpoint localEp;
97 + private int preamble;
98 + private final AtomicBoolean started = new AtomicBoolean(false);
99 + private final Map<String, Consumer<InternalMessage>> handlers = new ConcurrentHashMap<>();
100 + private final AtomicLong messageIdGenerator = new AtomicLong(0);
101 + private final Cache<Long, Callback> callbacks = CacheBuilder.newBuilder()
102 + .expireAfterWrite(10, TimeUnit.SECONDS)
103 + .removalListener(new RemovalListener<Long, Callback>() {
104 + @Override
105 + public void onRemoval(RemovalNotification<Long, Callback> entry) {
106 + if (entry.wasEvicted()) {
107 + entry.getValue().completeExceptionally(new TimeoutException("Timedout waiting for reply"));
108 + }
109 + }
110 + })
111 + .build();
112 +
113 + private final GenericKeyedObjectPool<Endpoint, Connection> channels
114 + = new GenericKeyedObjectPool<Endpoint, Connection>(new OnosCommunicationChannelFactory());
115 +
116 + private EventLoopGroup serverGroup;
117 + private EventLoopGroup clientGroup;
118 + private Class<? extends ServerChannel> serverChannelClass;
119 + private Class<? extends Channel> clientChannelClass;
120 +
121 + protected static final boolean TLS_DISABLED = false;
122 + protected boolean enableNettyTls = TLS_DISABLED;
123 +
124 + protected String ksLocation;
125 + protected String tsLocation;
126 + protected char[] ksPwd;
127 + protected char[] tsPwd;
43 128
44 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) 129 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
45 protected ClusterMetadataService clusterMetadataService; 130 protected ClusterMetadataService clusterMetadataService;
...@@ -48,14 +133,32 @@ public class NettyMessagingManager extends NettyMessaging { ...@@ -48,14 +133,32 @@ public class NettyMessagingManager extends NettyMessaging {
48 public void activate() throws Exception { 133 public void activate() throws Exception {
49 ControllerNode localNode = clusterMetadataService.getLocalNode(); 134 ControllerNode localNode = clusterMetadataService.getLocalNode();
50 getTlsParameters(); 135 getTlsParameters();
51 - super.start(clusterMetadataService.getClusterMetadata().getName().hashCode(), 136 +
52 - new Endpoint(localNode.ip(), localNode.tcpPort())); 137 + if (started.get()) {
138 + log.warn("Already running at local endpoint: {}", localEp);
139 + return;
140 + }
141 + this.preamble = clusterMetadataService.getClusterMetadata().getName().hashCode();
142 + this.localEp = new Endpoint(localNode.ip(), localNode.tcpPort());
143 + channels.setLifo(true);
144 + channels.setTestOnBorrow(true);
145 + channels.setTestOnReturn(true);
146 + channels.setMinEvictableIdleTimeMillis(60_000L);
147 + channels.setTimeBetweenEvictionRunsMillis(30_000L);
148 + initEventLoopGroup();
149 + startAcceptingConnections();
150 + started.set(true);
53 log.info("Started"); 151 log.info("Started");
54 } 152 }
55 153
56 @Deactivate 154 @Deactivate
57 public void deactivate() throws Exception { 155 public void deactivate() throws Exception {
58 - super.stop(); 156 + if (started.get()) {
157 + channels.close();
158 + serverGroup.shutdownGracefully();
159 + clientGroup.shutdownGracefully();
160 + started.set(false);
161 + }
59 log.info("Stopped"); 162 log.info("Stopped");
60 } 163 }
61 164
...@@ -86,4 +189,409 @@ public class NettyMessagingManager extends NettyMessaging { ...@@ -86,4 +189,409 @@ public class NettyMessagingManager extends NettyMessaging {
86 } 189 }
87 } 190 }
88 } 191 }
192 + private void initEventLoopGroup() {
193 + // try Epoll first and if that does work, use nio.
194 + try {
195 + clientGroup = new EpollEventLoopGroup();
196 + serverGroup = new EpollEventLoopGroup();
197 + serverChannelClass = EpollServerSocketChannel.class;
198 + clientChannelClass = EpollSocketChannel.class;
199 + return;
200 + } catch (Throwable e) {
201 + log.debug("Failed to initialize native (epoll) transport. "
202 + + "Reason: {}. Proceeding with nio.", e.getMessage());
203 + }
204 + clientGroup = new NioEventLoopGroup();
205 + serverGroup = new NioEventLoopGroup();
206 + serverChannelClass = NioServerSocketChannel.class;
207 + clientChannelClass = NioSocketChannel.class;
208 + }
209 +
210 + @Override
211 + public CompletableFuture<Void> sendAsync(Endpoint ep, String type, byte[] payload) {
212 + InternalMessage message = new InternalMessage(messageIdGenerator.incrementAndGet(),
213 + localEp,
214 + type,
215 + payload);
216 + return sendAsync(ep, message);
217 + }
218 +
219 + protected CompletableFuture<Void> sendAsync(Endpoint ep, InternalMessage message) {
220 + if (ep.equals(localEp)) {
221 + try {
222 + dispatchLocally(message);
223 + } catch (IOException e) {
224 + return Tools.exceptionalFuture(e);
225 + }
226 + return CompletableFuture.completedFuture(null);
227 + }
228 +
229 + CompletableFuture<Void> future = new CompletableFuture<>();
230 + try {
231 + Connection connection = null;
232 + try {
233 + connection = channels.borrowObject(ep);
234 + connection.send(message, future);
235 + } finally {
236 + channels.returnObject(ep, connection);
237 + }
238 + } catch (Exception e) {
239 + future.completeExceptionally(e);
240 + }
241 + return future;
242 + }
243 +
244 + @Override
245 + public CompletableFuture<byte[]> sendAndReceive(Endpoint ep, String type, byte[] payload) {
246 + return sendAndReceive(ep, type, payload, MoreExecutors.directExecutor());
247 + }
248 +
249 + @Override
250 + public CompletableFuture<byte[]> sendAndReceive(Endpoint ep, String type, byte[] payload, Executor executor) {
251 + CompletableFuture<byte[]> response = new CompletableFuture<>();
252 + Callback callback = new Callback(response, executor);
253 + Long messageId = messageIdGenerator.incrementAndGet();
254 + callbacks.put(messageId, callback);
255 + InternalMessage message = new InternalMessage(messageId, localEp, type, payload);
256 + return sendAsync(ep, message).whenComplete((r, e) -> {
257 + if (e != null) {
258 + callbacks.invalidate(messageId);
259 + }
260 + }).thenCompose(v -> response);
261 + }
262 +
263 + @Override
264 + public void registerHandler(String type, BiConsumer<Endpoint, byte[]> handler, Executor executor) {
265 + handlers.put(type, message -> executor.execute(() -> handler.accept(message.sender(), message.payload())));
266 + }
267 +
268 + @Override
269 + public void registerHandler(String type, BiFunction<Endpoint, byte[], byte[]> handler, Executor executor) {
270 + handlers.put(type, message -> executor.execute(() -> {
271 + byte[] responsePayload = handler.apply(message.sender(), message.payload());
272 + if (responsePayload != null) {
273 + InternalMessage response = new InternalMessage(message.id(),
274 + localEp,
275 + REPLY_MESSAGE_TYPE,
276 + responsePayload);
277 + sendAsync(message.sender(), response).whenComplete((result, error) -> {
278 + if (error != null) {
279 + log.debug("Failed to respond", error);
280 + }
281 + });
282 + }
283 + }));
284 + }
285 +
286 + @Override
287 + public void registerHandler(String type, BiFunction<Endpoint, byte[], CompletableFuture<byte[]>> handler) {
288 + handlers.put(type, message -> {
289 + handler.apply(message.sender(), message.payload()).whenComplete((result, error) -> {
290 + if (error == null) {
291 + InternalMessage response = new InternalMessage(message.id(),
292 + localEp,
293 + REPLY_MESSAGE_TYPE,
294 + result);
295 + sendAsync(message.sender(), response).whenComplete((r, e) -> {
296 + if (e != null) {
297 + log.debug("Failed to respond", e);
298 + }
299 + });
300 + }
301 + });
302 + });
303 + }
304 +
305 + @Override
306 + public void unregisterHandler(String type) {
307 + handlers.remove(type);
308 + }
309 +
310 + private void startAcceptingConnections() throws InterruptedException {
311 + ServerBootstrap b = new ServerBootstrap();
312 + b.option(ChannelOption.WRITE_BUFFER_HIGH_WATER_MARK, 32 * 1024);
313 + b.option(ChannelOption.WRITE_BUFFER_LOW_WATER_MARK, 8 * 1024);
314 + b.option(ChannelOption.SO_RCVBUF, 1048576);
315 + b.option(ChannelOption.TCP_NODELAY, true);
316 + b.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);
317 + b.group(serverGroup, clientGroup);
318 + b.channel(serverChannelClass);
319 + if (enableNettyTls) {
320 + b.childHandler(new SslServerCommunicationChannelInitializer());
321 + } else {
322 + b.childHandler(new OnosCommunicationChannelInitializer());
323 + }
324 + b.option(ChannelOption.SO_BACKLOG, 128);
325 + b.childOption(ChannelOption.SO_KEEPALIVE, true);
326 +
327 + // Bind and start to accept incoming connections.
328 + b.bind(localEp.port()).sync().addListener(future -> {
329 + if (future.isSuccess()) {
330 + log.info("{} accepting incoming connections on port {}", localEp.host(), localEp.port());
331 + } else {
332 + log.warn("{} failed to bind to port {}", localEp.host(), localEp.port(), future.cause());
333 + }
334 + });
335 + }
336 +
337 + private class OnosCommunicationChannelFactory
338 + implements KeyedPoolableObjectFactory<Endpoint, Connection> {
339 +
340 + @Override
341 + public void activateObject(Endpoint endpoint, Connection connection)
342 + throws Exception {
343 + }
344 +
345 + @Override
346 + public void destroyObject(Endpoint ep, Connection connection) throws Exception {
347 + log.debug("Closing connection to {}", ep);
348 + //Is this the right way to destroy?
349 + connection.destroy();
350 + }
351 +
352 + @Override
353 + public Connection makeObject(Endpoint ep) throws Exception {
354 + Bootstrap bootstrap = new Bootstrap();
355 + bootstrap.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);
356 + bootstrap.option(ChannelOption.WRITE_BUFFER_HIGH_WATER_MARK, 10 * 64 * 1024);
357 + bootstrap.option(ChannelOption.WRITE_BUFFER_LOW_WATER_MARK, 10 * 32 * 1024);
358 + bootstrap.option(ChannelOption.SO_SNDBUF, 1048576);
359 + bootstrap.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 1000);
360 + bootstrap.group(clientGroup);
361 + // TODO: Make this faster:
362 + // http://normanmaurer.me/presentations/2014-facebook-eng-netty/slides.html#37.0
363 + bootstrap.channel(clientChannelClass);
364 + bootstrap.option(ChannelOption.SO_KEEPALIVE, true);
365 + if (enableNettyTls) {
366 + bootstrap.handler(new SslClientCommunicationChannelInitializer());
367 + } else {
368 + bootstrap.handler(new OnosCommunicationChannelInitializer());
369 + }
370 + // Start the client.
371 + CompletableFuture<Channel> retFuture = new CompletableFuture<>();
372 + ChannelFuture f = bootstrap.connect(ep.host().toString(), ep.port());
373 +
374 + f.addListener(future -> {
375 + if (future.isSuccess()) {
376 + retFuture.complete(f.channel());
377 + } else {
378 + retFuture.completeExceptionally(future.cause());
379 + }
380 + });
381 + log.debug("Established a new connection to {}", ep);
382 + return new Connection(retFuture);
383 + }
384 +
385 + @Override
386 + public void passivateObject(Endpoint ep, Connection connection)
387 + throws Exception {
388 + }
389 +
390 + @Override
391 + public boolean validateObject(Endpoint ep, Connection connection) {
392 + return connection.validate();
393 + }
394 + }
395 +
396 + private class SslServerCommunicationChannelInitializer extends ChannelInitializer<SocketChannel> {
397 +
398 + private final ChannelHandler dispatcher = new InboundMessageDispatcher();
399 + private final ChannelHandler encoder = new MessageEncoder(preamble);
400 +
401 + @Override
402 + protected void initChannel(SocketChannel channel) throws Exception {
403 + TrustManagerFactory tmFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
404 + KeyStore ts = KeyStore.getInstance("JKS");
405 + ts.load(new FileInputStream(tsLocation), tsPwd);
406 + tmFactory.init(ts);
407 +
408 + KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
409 + KeyStore ks = KeyStore.getInstance("JKS");
410 + ks.load(new FileInputStream(ksLocation), ksPwd);
411 + kmf.init(ks, ksPwd);
412 +
413 + SSLContext serverContext = SSLContext.getInstance("TLS");
414 + serverContext.init(kmf.getKeyManagers(), tmFactory.getTrustManagers(), null);
415 +
416 + SSLEngine serverSslEngine = serverContext.createSSLEngine();
417 +
418 + serverSslEngine.setNeedClientAuth(true);
419 + serverSslEngine.setUseClientMode(false);
420 + serverSslEngine.setEnabledProtocols(serverSslEngine.getSupportedProtocols());
421 + serverSslEngine.setEnabledCipherSuites(serverSslEngine.getSupportedCipherSuites());
422 + serverSslEngine.setEnableSessionCreation(true);
423 +
424 + channel.pipeline().addLast("ssl", new io.netty.handler.ssl.SslHandler(serverSslEngine))
425 + .addLast("encoder", encoder)
426 + .addLast("decoder", new MessageDecoder(preamble))
427 + .addLast("handler", dispatcher);
428 + }
429 + }
430 +
431 + private class SslClientCommunicationChannelInitializer extends ChannelInitializer<SocketChannel> {
432 +
433 + private final ChannelHandler dispatcher = new InboundMessageDispatcher();
434 + private final ChannelHandler encoder = new MessageEncoder(preamble);
435 +
436 + @Override
437 + protected void initChannel(SocketChannel channel) throws Exception {
438 + TrustManagerFactory tmFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
439 + KeyStore ts = KeyStore.getInstance("JKS");
440 + ts.load(new FileInputStream(tsLocation), tsPwd);
441 + tmFactory.init(ts);
442 +
443 + KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
444 + KeyStore ks = KeyStore.getInstance("JKS");
445 + ks.load(new FileInputStream(ksLocation), ksPwd);
446 + kmf.init(ks, ksPwd);
447 +
448 + SSLContext clientContext = SSLContext.getInstance("TLS");
449 + clientContext.init(kmf.getKeyManagers(), tmFactory.getTrustManagers(), null);
450 +
451 + SSLEngine clientSslEngine = clientContext.createSSLEngine();
452 +
453 + clientSslEngine.setUseClientMode(true);
454 + clientSslEngine.setEnabledProtocols(clientSslEngine.getSupportedProtocols());
455 + clientSslEngine.setEnabledCipherSuites(clientSslEngine.getSupportedCipherSuites());
456 + clientSslEngine.setEnableSessionCreation(true);
457 +
458 + channel.pipeline().addLast("ssl", new io.netty.handler.ssl.SslHandler(clientSslEngine))
459 + .addLast("encoder", encoder)
460 + .addLast("decoder", new MessageDecoder(preamble))
461 + .addLast("handler", dispatcher);
462 + }
463 + }
464 +
465 + private class OnosCommunicationChannelInitializer extends ChannelInitializer<SocketChannel> {
466 +
467 + private final ChannelHandler dispatcher = new InboundMessageDispatcher();
468 + private final ChannelHandler encoder = new MessageEncoder(preamble);
469 +
470 + @Override
471 + protected void initChannel(SocketChannel channel) throws Exception {
472 + channel.pipeline()
473 + .addLast("encoder", encoder)
474 + .addLast("decoder", new MessageDecoder(preamble))
475 + .addLast("handler", dispatcher);
476 + }
477 + }
478 +
479 + @ChannelHandler.Sharable
480 + private class InboundMessageDispatcher extends SimpleChannelInboundHandler<InternalMessage> {
481 +
482 + @Override
483 + protected void channelRead0(ChannelHandlerContext ctx, InternalMessage message) throws Exception {
484 + try {
485 + dispatchLocally(message);
486 + } catch (RejectedExecutionException e) {
487 + log.warn("Unable to dispatch message due to {}", e.getMessage());
488 + }
489 + }
490 +
491 + @Override
492 + public void exceptionCaught(ChannelHandlerContext context, Throwable cause) {
493 + log.error("Exception inside channel handling pipeline.", cause);
494 + context.close();
495 + }
496 + }
497 + private void dispatchLocally(InternalMessage message) throws IOException {
498 + String type = message.type();
499 + if (REPLY_MESSAGE_TYPE.equals(type)) {
500 + try {
501 + Callback callback =
502 + callbacks.getIfPresent(message.id());
503 + if (callback != null) {
504 + callback.complete(message.payload());
505 + } else {
506 + log.warn("Received a reply for message id:[{}]. "
507 + + " from {}. But was unable to locate the"
508 + + " request handle", message.id(), message.sender());
509 + }
510 + } finally {
511 + callbacks.invalidate(message.id());
512 + }
513 + return;
514 + }
515 + Consumer<InternalMessage> handler = handlers.get(type);
516 + if (handler != null) {
517 + handler.accept(message);
518 + } else {
519 + log.debug("No handler registered for {}", type);
520 + }
521 + }
522 +
523 + private final class Callback {
524 + private final CompletableFuture<byte[]> future;
525 + private final Executor executor;
526 +
527 + public Callback(CompletableFuture<byte[]> future, Executor executor) {
528 + this.future = future;
529 + this.executor = executor;
530 + }
531 +
532 + public void complete(byte[] value) {
533 + executor.execute(() -> future.complete(value));
534 + }
535 +
536 + public void completeExceptionally(Throwable error) {
537 + executor.execute(() -> future.completeExceptionally(error));
538 + }
539 + }
540 + private final class Connection {
541 + private final CompletableFuture<Channel> internalFuture;
542 +
543 + public Connection(CompletableFuture<Channel> internalFuture) {
544 + this.internalFuture = internalFuture;
545 + }
546 +
547 + /**
548 + * Sends a message out on its channel and associated the message with a
549 + * completable future used for signaling.
550 + * @param message the message to be sent
551 + * @param future a future that is completed normally or exceptionally if
552 + * message sending succeeds or fails respectively
553 + */
554 + public void send(Object message, CompletableFuture<Void> future) {
555 + internalFuture.whenComplete((channel, throwable) -> {
556 + if (throwable == null) {
557 + channel.writeAndFlush(message).addListener(channelFuture -> {
558 + if (!channelFuture.isSuccess()) {
559 + future.completeExceptionally(channelFuture.cause());
560 + } else {
561 + future.complete(null);
562 + }
563 + });
564 + } else {
565 + future.completeExceptionally(throwable);
566 + }
567 + });
568 + }
569 +
570 + /**
571 + * Destroys a channel by closing its channel (if it exists) and
572 + * cancelling its future.
573 + */
574 + public void destroy() {
575 + Channel channel = internalFuture.getNow(null);
576 + if (channel != null) {
577 + channel.close();
578 + }
579 + internalFuture.cancel(false);
580 + }
581 +
582 + /**
583 + * Determines whether the connection is valid meaning it is either
584 + * complete with and active channel
585 + * or it has not yet completed.
586 + * @return true if the channel has an active connection or has not
587 + * yet completed
588 + */
589 + public boolean validate() {
590 + if (internalFuture.isCompletedExceptionally()) {
591 + return false;
592 + }
593 + Channel channel = internalFuture.getNow(null);
594 + return channel == null || channel.isActive();
595 + }
596 + }
89 } 597 }
......
1 -package org.onlab.netty; 1 +package org.onosproject.store.cluster.messaging.impl;
2 2
3 import java.util.Arrays; 3 import java.util.Arrays;
4 import java.util.concurrent.CompletableFuture; 4 import java.util.concurrent.CompletableFuture;
...@@ -9,10 +9,17 @@ import java.util.concurrent.atomic.AtomicBoolean; ...@@ -9,10 +9,17 @@ import java.util.concurrent.atomic.AtomicBoolean;
9 import java.util.concurrent.atomic.AtomicReference; 9 import java.util.concurrent.atomic.AtomicReference;
10 import java.util.function.BiFunction; 10 import java.util.function.BiFunction;
11 11
12 +import com.google.common.collect.Sets;
12 import org.junit.After; 13 import org.junit.After;
13 import org.junit.Before; 14 import org.junit.Before;
14 import org.junit.Test; 15 import org.junit.Test;
15 import org.onlab.packet.IpAddress; 16 import org.onlab.packet.IpAddress;
17 +import org.onosproject.cluster.ClusterMetadata;
18 +import org.onosproject.cluster.ClusterMetadataEventListener;
19 +import org.onosproject.cluster.ClusterMetadataService;
20 +import org.onosproject.cluster.ControllerNode;
21 +import org.onosproject.cluster.NodeId;
22 +import org.onosproject.net.provider.ProviderId;
16 import org.onosproject.store.cluster.messaging.Endpoint; 23 import org.onosproject.store.cluster.messaging.Endpoint;
17 24
18 import com.google.common.util.concurrent.MoreExecutors; 25 import com.google.common.util.concurrent.MoreExecutors;
...@@ -24,34 +31,39 @@ import static org.onlab.junit.TestTools.findAvailablePort; ...@@ -24,34 +31,39 @@ import static org.onlab.junit.TestTools.findAvailablePort;
24 /** 31 /**
25 * Unit tests for NettyMessaging. 32 * Unit tests for NettyMessaging.
26 */ 33 */
27 -public class NettyMessagingTest { 34 +public class NettyMessagingManagerTest {
28 35
29 - NettyMessaging netty1; 36 + NettyMessagingManager netty1;
30 - NettyMessaging netty2; 37 + NettyMessagingManager netty2;
31 38
32 - Endpoint ep1 = new Endpoint(IpAddress.valueOf("127.0.0.1"), 5001); 39 + private static final String DUMMY_NAME = "node";
33 - Endpoint ep2 = new Endpoint(IpAddress.valueOf("127.0.0.1"), 5002); 40 + private static final String IP_STRING = "127.0.0.1";
34 - Endpoint invalidEndPoint = new Endpoint(IpAddress.valueOf("127.0.0.1"), 5003); 41 +
42 + Endpoint ep1 = new Endpoint(IpAddress.valueOf(IP_STRING), 5001);
43 + Endpoint ep2 = new Endpoint(IpAddress.valueOf(IP_STRING), 5002);
44 + Endpoint invalidEndPoint = new Endpoint(IpAddress.valueOf(IP_STRING), 5003);
35 45
36 @Before 46 @Before
37 public void setUp() throws Exception { 47 public void setUp() throws Exception {
38 ep1 = new Endpoint(IpAddress.valueOf("127.0.0.1"), findAvailablePort(5001)); 48 ep1 = new Endpoint(IpAddress.valueOf("127.0.0.1"), findAvailablePort(5001));
39 - netty1 = new NettyMessaging(); 49 + netty1 = new NettyMessagingManager();
40 - netty1.start(12, ep1); 50 + netty1.clusterMetadataService = dummyMetadataService(DUMMY_NAME, IP_STRING, ep1);
51 + netty1.activate();
41 52
42 ep2 = new Endpoint(IpAddress.valueOf("127.0.0.1"), findAvailablePort(5003)); 53 ep2 = new Endpoint(IpAddress.valueOf("127.0.0.1"), findAvailablePort(5003));
43 - netty2 = new NettyMessaging(); 54 + netty2 = new NettyMessagingManager();
44 - netty2.start(12, ep2); 55 + netty2.clusterMetadataService = dummyMetadataService(DUMMY_NAME, IP_STRING, ep2);
56 + netty2.activate();
45 } 57 }
46 58
47 @After 59 @After
48 public void tearDown() throws Exception { 60 public void tearDown() throws Exception {
49 if (netty1 != null) { 61 if (netty1 != null) {
50 - netty1.stop(); 62 + netty1.deactivate();
51 } 63 }
52 64
53 if (netty2 != null) { 65 if (netty2 != null) {
54 - netty2.stop(); 66 + netty2.deactivate();
55 } 67 }
56 } 68 }
57 69
...@@ -113,9 +125,9 @@ public class NettyMessagingTest { ...@@ -113,9 +125,9 @@ public class NettyMessagingTest {
113 netty2.registerHandler("test-subject", handler, handlerExecutor); 125 netty2.registerHandler("test-subject", handler, handlerExecutor);
114 126
115 CompletableFuture<byte[]> response = netty1.sendAndReceive(ep2, 127 CompletableFuture<byte[]> response = netty1.sendAndReceive(ep2,
116 - "test-subject", 128 + "test-subject",
117 - "hello world".getBytes(), 129 + "hello world".getBytes(),
118 - completionExecutor); 130 + completionExecutor);
119 response.whenComplete((r, e) -> { 131 response.whenComplete((r, e) -> {
120 completionThreadName.set(Thread.currentThread().getName()); 132 completionThreadName.set(Thread.currentThread().getName());
121 }); 133 });
...@@ -125,4 +137,40 @@ public class NettyMessagingTest { ...@@ -125,4 +137,40 @@ public class NettyMessagingTest {
125 assertEquals("completion-thread", completionThreadName.get()); 137 assertEquals("completion-thread", completionThreadName.get());
126 assertEquals("handler-thread", handlerThreadName.get()); 138 assertEquals("handler-thread", handlerThreadName.get());
127 } 139 }
128 -} 140 +
141 + private ClusterMetadataService dummyMetadataService(String name, String ipAddress, Endpoint ep) {
142 + return new ClusterMetadataService() {
143 + @Override
144 + public ClusterMetadata getClusterMetadata() {
145 + return new ClusterMetadata(new ProviderId(DUMMY_NAME, DUMMY_NAME),
146 + name, Sets.newHashSet(), Sets.newHashSet());
147 + }
148 +
149 + @Override
150 + public ControllerNode getLocalNode() {
151 + return new ControllerNode() {
152 + @Override
153 + public NodeId id() {
154 + return null;
155 + }
156 +
157 + @Override
158 + public IpAddress ip() {
159 + return IpAddress.valueOf(ipAddress);
160 + }
161 +
162 + @Override
163 + public int tcpPort() {
164 + return ep.port();
165 + }
166 + };
167 + }
168 +
169 + @Override
170 + public void addListener(ClusterMetadataEventListener listener) {}
171 +
172 + @Override
173 + public void removeListener(ClusterMetadataEventListener listener) {}
174 + };
175 + }
176 +}
...\ No newline at end of file ...\ No newline at end of file
......
1 -<?xml version="1.0" encoding="UTF-8"?>
2 -<!--
3 - ~ Copyright 2014 Open Networking Laboratory
4 - ~
5 - ~ Licensed under the Apache License, Version 2.0 (the "License");
6 - ~ you may not use this file except in compliance with the License.
7 - ~ You may obtain a copy of the License at
8 - ~
9 - ~ http://www.apache.org/licenses/LICENSE-2.0
10 - ~
11 - ~ Unless required by applicable law or agreed to in writing, software
12 - ~ distributed under the License is distributed on an "AS IS" BASIS,
13 - ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 - ~ See the License for the specific language governing permissions and
15 - ~ limitations under the License.
16 - -->
17 -<project xmlns="http://maven.apache.org/POM/4.0.0"
18 - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
19 - xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
20 - <modelVersion>4.0.0</modelVersion>
21 -
22 - <parent>
23 - <groupId>org.onosproject</groupId>
24 - <artifactId>onlab-utils</artifactId>
25 - <version>1.5.0-SNAPSHOT</version>
26 - <relativePath>../pom.xml</relativePath>
27 - </parent>
28 -
29 - <artifactId>onlab-netty</artifactId>
30 - <packaging>bundle</packaging>
31 -
32 - <description>Network I/O using Netty framework</description>
33 -
34 - <dependencies>
35 - <dependency>
36 - <groupId>com.google.guava</groupId>
37 - <artifactId>guava-testlib</artifactId>
38 - <scope>test</scope>
39 - </dependency>
40 - <dependency>
41 - <groupId>org.onosproject</groupId>
42 - <artifactId>onos-api</artifactId>
43 - </dependency>
44 - <dependency>
45 - <groupId>org.onosproject</groupId>
46 - <artifactId>onlab-misc</artifactId>
47 - </dependency>
48 - <dependency>
49 - <groupId>org.onosproject</groupId>
50 - <artifactId>onlab-junit</artifactId>
51 - <scope>test</scope>
52 - </dependency>
53 - <dependency>
54 - <groupId>commons-pool</groupId>
55 - <artifactId>commons-pool</artifactId>
56 - </dependency>
57 - <dependency>
58 - <groupId>io.netty</groupId>
59 - <artifactId>netty-common</artifactId>
60 - </dependency>
61 - <dependency>
62 - <groupId>io.netty</groupId>
63 - <artifactId>netty-buffer</artifactId>
64 - </dependency>
65 - <dependency>
66 - <groupId>io.netty</groupId>
67 - <artifactId>netty-transport</artifactId>
68 - </dependency>
69 - <dependency>
70 - <groupId>io.netty</groupId>
71 - <artifactId>netty-handler</artifactId>
72 - </dependency>
73 - <dependency>
74 - <groupId>io.netty</groupId>
75 - <artifactId>netty-codec</artifactId>
76 - </dependency>
77 - <dependency>
78 - <groupId>io.netty</groupId>
79 - <artifactId>netty-transport-native-epoll</artifactId>
80 - <version>${netty4.version}</version>
81 - </dependency>
82 - </dependencies>
83 -</project>
1 -/*
2 - * Copyright 2014-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.netty;
17 -
18 -import com.google.common.cache.Cache;
19 -import com.google.common.cache.CacheBuilder;
20 -import com.google.common.cache.RemovalListener;
21 -import com.google.common.cache.RemovalNotification;
22 -import com.google.common.util.concurrent.MoreExecutors;
23 -
24 -import io.netty.bootstrap.Bootstrap;
25 -import io.netty.bootstrap.ServerBootstrap;
26 -import io.netty.buffer.PooledByteBufAllocator;
27 -import io.netty.channel.Channel;
28 -import io.netty.channel.ChannelFuture;
29 -import io.netty.channel.ChannelHandler;
30 -import io.netty.channel.ChannelHandlerContext;
31 -import io.netty.channel.ChannelInitializer;
32 -import io.netty.channel.ChannelOption;
33 -import io.netty.channel.EventLoopGroup;
34 -import io.netty.channel.ServerChannel;
35 -import io.netty.channel.SimpleChannelInboundHandler;
36 -import io.netty.channel.epoll.EpollEventLoopGroup;
37 -import io.netty.channel.epoll.EpollServerSocketChannel;
38 -import io.netty.channel.epoll.EpollSocketChannel;
39 -import io.netty.channel.nio.NioEventLoopGroup;
40 -import io.netty.channel.socket.SocketChannel;
41 -import io.netty.channel.socket.nio.NioServerSocketChannel;
42 -import io.netty.channel.socket.nio.NioSocketChannel;
43 -
44 -import org.apache.commons.pool.KeyedPoolableObjectFactory;
45 -import org.apache.commons.pool.impl.GenericKeyedObjectPool;
46 -import org.onlab.util.Tools;
47 -import org.onosproject.store.cluster.messaging.Endpoint;
48 -import org.onosproject.store.cluster.messaging.MessagingService;
49 -import org.slf4j.Logger;
50 -import org.slf4j.LoggerFactory;
51 -
52 -import javax.net.ssl.KeyManagerFactory;
53 -import javax.net.ssl.SSLContext;
54 -import javax.net.ssl.SSLEngine;
55 -import javax.net.ssl.TrustManagerFactory;
56 -
57 -import java.io.FileInputStream;
58 -import java.io.IOException;
59 -import java.security.KeyStore;
60 -import java.util.Map;
61 -import java.util.concurrent.CompletableFuture;
62 -import java.util.concurrent.ConcurrentHashMap;
63 -import java.util.concurrent.Executor;
64 -import java.util.concurrent.RejectedExecutionException;
65 -import java.util.concurrent.TimeUnit;
66 -import java.util.concurrent.TimeoutException;
67 -import java.util.concurrent.atomic.AtomicBoolean;
68 -import java.util.concurrent.atomic.AtomicLong;
69 -import java.util.function.BiConsumer;
70 -import java.util.function.BiFunction;
71 -import java.util.function.Consumer;
72 -
73 -/**
74 - * Implementation of MessagingService based on <a href="http://netty.io/">Netty</a> framework.
75 - */
76 -public class NettyMessaging implements MessagingService {
77 -
78 - private final Logger log = LoggerFactory.getLogger(getClass());
79 -
80 - private static final String REPLY_MESSAGE_TYPE = "NETTY_MESSAGING_REQUEST_REPLY";
81 -
82 - private Endpoint localEp;
83 - private int preamble;
84 - private final AtomicBoolean started = new AtomicBoolean(false);
85 - private final Map<String, Consumer<InternalMessage>> handlers = new ConcurrentHashMap<>();
86 - private final AtomicLong messageIdGenerator = new AtomicLong(0);
87 - private final Cache<Long, Callback> callbacks = CacheBuilder.newBuilder()
88 - .expireAfterWrite(10, TimeUnit.SECONDS)
89 - .removalListener(new RemovalListener<Long, Callback>() {
90 - @Override
91 - public void onRemoval(RemovalNotification<Long, Callback> entry) {
92 - if (entry.wasEvicted()) {
93 - entry.getValue().completeExceptionally(new TimeoutException("Timedout waiting for reply"));
94 - }
95 - }
96 - })
97 - .build();
98 -
99 - private final GenericKeyedObjectPool<Endpoint, Connection> channels
100 - = new GenericKeyedObjectPool<Endpoint, Connection>(new OnosCommunicationChannelFactory());
101 -
102 - private EventLoopGroup serverGroup;
103 - private EventLoopGroup clientGroup;
104 - private Class<? extends ServerChannel> serverChannelClass;
105 - private Class<? extends Channel> clientChannelClass;
106 -
107 - protected static final boolean TLS_DISABLED = false;
108 - protected boolean enableNettyTls = TLS_DISABLED;
109 -
110 - protected String ksLocation;
111 - protected String tsLocation;
112 - protected char[] ksPwd;
113 - protected char[] tsPwd;
114 -
115 - @SuppressWarnings("squid:S1181")
116 - // We really need to catch Throwable due to netty native epoll() handling
117 - private void initEventLoopGroup() {
118 - // try Epoll first and if that does work, use nio.
119 - try {
120 - clientGroup = new EpollEventLoopGroup();
121 - serverGroup = new EpollEventLoopGroup();
122 - serverChannelClass = EpollServerSocketChannel.class;
123 - clientChannelClass = EpollSocketChannel.class;
124 - return;
125 - } catch (Throwable e) {
126 - log.debug("Failed to initialize native (epoll) transport. "
127 - + "Reason: {}. Proceeding with nio.", e.getMessage());
128 - }
129 - clientGroup = new NioEventLoopGroup();
130 - serverGroup = new NioEventLoopGroup();
131 - serverChannelClass = NioServerSocketChannel.class;
132 - clientChannelClass = NioSocketChannel.class;
133 - }
134 -
135 - public void start(int preamble, Endpoint localEp) throws Exception {
136 - if (started.get()) {
137 - log.warn("Already running at local endpoint: {}", localEp);
138 - return;
139 - }
140 - this.preamble = preamble;
141 - this.localEp = localEp;
142 - channels.setLifo(true);
143 - channels.setTestOnBorrow(true);
144 - channels.setTestOnReturn(true);
145 - channels.setMinEvictableIdleTimeMillis(60_000L);
146 - channels.setTimeBetweenEvictionRunsMillis(30_000L);
147 - initEventLoopGroup();
148 - startAcceptingConnections();
149 - started.set(true);
150 - }
151 -
152 - public void stop() throws Exception {
153 - if (started.get()) {
154 - channels.close();
155 - serverGroup.shutdownGracefully();
156 - clientGroup.shutdownGracefully();
157 - started.set(false);
158 - }
159 - }
160 -
161 - @Override
162 - public CompletableFuture<Void> sendAsync(Endpoint ep, String type, byte[] payload) {
163 - InternalMessage message = new InternalMessage(messageIdGenerator.incrementAndGet(),
164 - localEp,
165 - type,
166 - payload);
167 - return sendAsync(ep, message);
168 - }
169 -
170 - protected CompletableFuture<Void> sendAsync(Endpoint ep, InternalMessage message) {
171 - if (ep.equals(localEp)) {
172 - try {
173 - dispatchLocally(message);
174 - } catch (IOException e) {
175 - return Tools.exceptionalFuture(e);
176 - }
177 - return CompletableFuture.completedFuture(null);
178 - }
179 -
180 - CompletableFuture<Void> future = new CompletableFuture<>();
181 - try {
182 - Connection connection = null;
183 - try {
184 - connection = channels.borrowObject(ep);
185 - connection.send(message, future);
186 -
187 - } finally {
188 - channels.returnObject(ep, connection);
189 - }
190 - } catch (Exception e) {
191 - future.completeExceptionally(e);
192 - }
193 - return future;
194 - }
195 -
196 - @Override
197 - public CompletableFuture<byte[]> sendAndReceive(Endpoint ep, String type, byte[] payload) {
198 - return sendAndReceive(ep, type, payload, MoreExecutors.directExecutor());
199 - }
200 -
201 - @Override
202 - public CompletableFuture<byte[]> sendAndReceive(Endpoint ep, String type, byte[] payload, Executor executor) {
203 - CompletableFuture<byte[]> response = new CompletableFuture<>();
204 - Callback callback = new Callback(response, executor);
205 - Long messageId = messageIdGenerator.incrementAndGet();
206 - callbacks.put(messageId, callback);
207 - InternalMessage message = new InternalMessage(messageId, localEp, type, payload);
208 - return sendAsync(ep, message).whenComplete((r, e) -> {
209 - if (e != null) {
210 - callbacks.invalidate(messageId);
211 - }
212 - }).thenCompose(v -> response);
213 - }
214 -
215 - @Override
216 - public void registerHandler(String type, BiConsumer<Endpoint, byte[]> handler, Executor executor) {
217 - handlers.put(type, message -> executor.execute(() -> handler.accept(message.sender(), message.payload())));
218 - }
219 -
220 - @Override
221 - public void registerHandler(String type, BiFunction<Endpoint, byte[], byte[]> handler, Executor executor) {
222 - handlers.put(type, message -> executor.execute(() -> {
223 - byte[] responsePayload = handler.apply(message.sender(), message.payload());
224 - if (responsePayload != null) {
225 - InternalMessage response = new InternalMessage(message.id(),
226 - localEp,
227 - REPLY_MESSAGE_TYPE,
228 - responsePayload);
229 - sendAsync(message.sender(), response).whenComplete((result, error) -> {
230 - if (error != null) {
231 - log.debug("Failed to respond", error);
232 - }
233 - });
234 - }
235 - }));
236 - }
237 -
238 - @Override
239 - public void registerHandler(String type, BiFunction<Endpoint, byte[], CompletableFuture<byte[]>> handler) {
240 - handlers.put(type, message -> {
241 - handler.apply(message.sender(), message.payload()).whenComplete((result, error) -> {
242 - if (error == null) {
243 - InternalMessage response = new InternalMessage(message.id(),
244 - localEp,
245 - REPLY_MESSAGE_TYPE,
246 - result);
247 - sendAsync(message.sender(), response).whenComplete((r, e) -> {
248 - if (e != null) {
249 - log.debug("Failed to respond", e);
250 - }
251 - });
252 - }
253 - });
254 - });
255 - }
256 -
257 - @Override
258 - public void unregisterHandler(String type) {
259 - handlers.remove(type);
260 - }
261 -
262 - private void startAcceptingConnections() throws InterruptedException {
263 - ServerBootstrap b = new ServerBootstrap();
264 - b.option(ChannelOption.WRITE_BUFFER_HIGH_WATER_MARK, 32 * 1024);
265 - b.option(ChannelOption.WRITE_BUFFER_LOW_WATER_MARK, 8 * 1024);
266 - b.option(ChannelOption.SO_RCVBUF, 1048576);
267 - b.option(ChannelOption.TCP_NODELAY, true);
268 - b.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);
269 - b.group(serverGroup, clientGroup);
270 - b.channel(serverChannelClass);
271 - if (enableNettyTls) {
272 - b.childHandler(new SslServerCommunicationChannelInitializer());
273 - } else {
274 - b.childHandler(new OnosCommunicationChannelInitializer());
275 - }
276 - b.option(ChannelOption.SO_BACKLOG, 128);
277 - b.childOption(ChannelOption.SO_KEEPALIVE, true);
278 -
279 - // Bind and start to accept incoming connections.
280 - b.bind(localEp.port()).sync().addListener(future -> {
281 - if (future.isSuccess()) {
282 - log.info("{} accepting incoming connections on port {}", localEp.host(), localEp.port());
283 - } else {
284 - log.warn("{} failed to bind to port {}", localEp.host(), localEp.port(), future.cause());
285 - }
286 - });
287 - }
288 -
289 - private class OnosCommunicationChannelFactory
290 - implements KeyedPoolableObjectFactory<Endpoint, Connection> {
291 -
292 - @Override
293 - public void activateObject(Endpoint endpoint, Connection connection)
294 - throws Exception {
295 - }
296 -
297 - @Override
298 - public void destroyObject(Endpoint ep, Connection connection) throws Exception {
299 - log.debug("Closing connection to {}", ep);
300 - //Is this the right way to destroy?
301 - connection.destroy();
302 - }
303 -
304 - @Override
305 - public Connection makeObject(Endpoint ep) throws Exception {
306 - Bootstrap bootstrap = new Bootstrap();
307 - bootstrap.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);
308 - bootstrap.option(ChannelOption.WRITE_BUFFER_HIGH_WATER_MARK, 10 * 64 * 1024);
309 - bootstrap.option(ChannelOption.WRITE_BUFFER_LOW_WATER_MARK, 10 * 32 * 1024);
310 - bootstrap.option(ChannelOption.SO_SNDBUF, 1048576);
311 - bootstrap.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 1000);
312 - bootstrap.group(clientGroup);
313 - // TODO: Make this faster:
314 - // http://normanmaurer.me/presentations/2014-facebook-eng-netty/slides.html#37.0
315 - bootstrap.channel(clientChannelClass);
316 - bootstrap.option(ChannelOption.SO_KEEPALIVE, true);
317 - if (enableNettyTls) {
318 - bootstrap.handler(new SslClientCommunicationChannelInitializer());
319 - } else {
320 - bootstrap.handler(new OnosCommunicationChannelInitializer());
321 - }
322 - // Start the client.
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 - });
333 - log.debug("Established a new connection to {}", ep);
334 - return new Connection(retFuture);
335 - }
336 -
337 - @Override
338 - public void passivateObject(Endpoint ep, Connection connection)
339 - throws Exception {
340 - }
341 -
342 - @Override
343 - public boolean validateObject(Endpoint ep, Connection connection) {
344 - return connection.validate();
345 - }
346 - }
347 -
348 - private class SslServerCommunicationChannelInitializer extends ChannelInitializer<SocketChannel> {
349 -
350 - private final ChannelHandler dispatcher = new InboundMessageDispatcher();
351 - private final ChannelHandler encoder = new MessageEncoder(preamble);
352 -
353 - @Override
354 - protected void initChannel(SocketChannel channel) throws Exception {
355 - TrustManagerFactory tmFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
356 - KeyStore ts = KeyStore.getInstance("JKS");
357 - ts.load(new FileInputStream(tsLocation), tsPwd);
358 - tmFactory.init(ts);
359 -
360 - KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
361 - KeyStore ks = KeyStore.getInstance("JKS");
362 - ks.load(new FileInputStream(ksLocation), ksPwd);
363 - kmf.init(ks, ksPwd);
364 -
365 - SSLContext serverContext = SSLContext.getInstance("TLS");
366 - serverContext.init(kmf.getKeyManagers(), tmFactory.getTrustManagers(), null);
367 -
368 - SSLEngine serverSslEngine = serverContext.createSSLEngine();
369 -
370 - serverSslEngine.setNeedClientAuth(true);
371 - serverSslEngine.setUseClientMode(false);
372 - serverSslEngine.setEnabledProtocols(serverSslEngine.getSupportedProtocols());
373 - serverSslEngine.setEnabledCipherSuites(serverSslEngine.getSupportedCipherSuites());
374 - serverSslEngine.setEnableSessionCreation(true);
375 -
376 - channel.pipeline().addLast("ssl", new io.netty.handler.ssl.SslHandler(serverSslEngine))
377 - .addLast("encoder", encoder)
378 - .addLast("decoder", new MessageDecoder(preamble))
379 - .addLast("handler", dispatcher);
380 - }
381 -
382 - }
383 -
384 - private class SslClientCommunicationChannelInitializer extends ChannelInitializer<SocketChannel> {
385 -
386 - private final ChannelHandler dispatcher = new InboundMessageDispatcher();
387 - private final ChannelHandler encoder = new MessageEncoder(preamble);
388 -
389 - @Override
390 - protected void initChannel(SocketChannel channel) throws Exception {
391 - TrustManagerFactory tmFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
392 - KeyStore ts = KeyStore.getInstance("JKS");
393 - ts.load(new FileInputStream(tsLocation), tsPwd);
394 - tmFactory.init(ts);
395 -
396 - KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
397 - KeyStore ks = KeyStore.getInstance("JKS");
398 - ks.load(new FileInputStream(ksLocation), ksPwd);
399 - kmf.init(ks, ksPwd);
400 -
401 - SSLContext clientContext = SSLContext.getInstance("TLS");
402 - clientContext.init(kmf.getKeyManagers(), tmFactory.getTrustManagers(), null);
403 -
404 - SSLEngine clientSslEngine = clientContext.createSSLEngine();
405 -
406 - clientSslEngine.setUseClientMode(true);
407 - clientSslEngine.setEnabledProtocols(clientSslEngine.getSupportedProtocols());
408 - clientSslEngine.setEnabledCipherSuites(clientSslEngine.getSupportedCipherSuites());
409 - clientSslEngine.setEnableSessionCreation(true);
410 -
411 - channel.pipeline().addLast("ssl", new io.netty.handler.ssl.SslHandler(clientSslEngine))
412 - .addLast("encoder", encoder)
413 - .addLast("decoder", new MessageDecoder(preamble))
414 - .addLast("handler", dispatcher);
415 - }
416 -
417 - }
418 -
419 - private class OnosCommunicationChannelInitializer extends ChannelInitializer<SocketChannel> {
420 -
421 - private final ChannelHandler dispatcher = new InboundMessageDispatcher();
422 - private final ChannelHandler encoder = new MessageEncoder(preamble);
423 -
424 - @Override
425 - protected void initChannel(SocketChannel channel) throws Exception {
426 - channel.pipeline()
427 - .addLast("encoder", encoder)
428 - .addLast("decoder", new MessageDecoder(preamble))
429 - .addLast("handler", dispatcher);
430 - }
431 - }
432 -
433 - @ChannelHandler.Sharable
434 - private class InboundMessageDispatcher extends SimpleChannelInboundHandler<InternalMessage> {
435 -
436 - @Override
437 - protected void channelRead0(ChannelHandlerContext ctx, InternalMessage message) throws Exception {
438 - try {
439 - dispatchLocally(message);
440 - } catch (RejectedExecutionException e) {
441 - log.warn("Unable to dispatch message due to {}", e.getMessage());
442 - }
443 - }
444 -
445 - @Override
446 - public void exceptionCaught(ChannelHandlerContext context, Throwable cause) {
447 - log.error("Exception inside channel handling pipeline.", cause);
448 - context.close();
449 - }
450 - }
451 - private void dispatchLocally(InternalMessage message) throws IOException {
452 - String type = message.type();
453 - if (REPLY_MESSAGE_TYPE.equals(type)) {
454 - try {
455 - Callback callback =
456 - callbacks.getIfPresent(message.id());
457 - if (callback != null) {
458 - callback.complete(message.payload());
459 - } else {
460 - log.warn("Received a reply for message id:[{}]. "
461 - + " from {}. But was unable to locate the"
462 - + " request handle", message.id(), message.sender());
463 - }
464 - } finally {
465 - callbacks.invalidate(message.id());
466 - }
467 - return;
468 - }
469 - Consumer<InternalMessage> handler = handlers.get(type);
470 - if (handler != null) {
471 - handler.accept(message);
472 - } else {
473 - log.debug("No handler registered for {}", type);
474 - }
475 - }
476 -
477 - private final class Callback {
478 - private final CompletableFuture<byte[]> future;
479 - private final Executor executor;
480 -
481 - public Callback(CompletableFuture<byte[]> future, Executor executor) {
482 - this.future = future;
483 - this.executor = executor;
484 - }
485 -
486 - public void complete(byte[] value) {
487 - executor.execute(() -> future.complete(value));
488 - }
489 -
490 - public void completeExceptionally(Throwable error) {
491 - executor.execute(() -> future.completeExceptionally(error));
492 - }
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 - }
552 -}
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 -
17 -/**
18 - * Asynchronous messaging APIs implemented using the Netty framework.
19 - */
20 -package org.onlab.netty;
...@@ -34,7 +34,6 @@ ...@@ -34,7 +34,6 @@
34 <modules> 34 <modules>
35 <module>junit</module> 35 <module>junit</module>
36 <module>misc</module> 36 <module>misc</module>
37 - <module>netty</module>
38 <module>nio</module> 37 <module>nio</module>
39 <module>yangutils</module> 38 <module>yangutils</module>
40 <module>osgi</module> 39 <module>osgi</module>
......