Committed by
Gerrit Code Review
Migrating netty messaging into netty messaging manager.
Change-Id: I971db195c9dc155cdf76850f0427ef9b9210113c
Showing
10 changed files
with
596 additions
and
701 deletions
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 | ... | ... |
utils/netty/pom.xml
deleted
100644 → 0
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> | ... | ... |
-
Please register or login to post a comment