2 * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
8 package org.opendaylight.protocol.framework;
10 import static org.junit.Assert.assertEquals;
11 import static org.junit.Assert.assertFalse;
12 import static org.mockito.Matchers.any;
13 import static org.mockito.Mockito.doNothing;
14 import static org.mockito.Mockito.doReturn;
15 import static org.mockito.Mockito.mock;
16 import static org.mockito.Mockito.timeout;
17 import static org.mockito.Mockito.times;
18 import static org.mockito.Mockito.verify;
20 import io.netty.channel.ChannelFuture;
21 import io.netty.channel.nio.NioEventLoopGroup;
22 import io.netty.util.concurrent.DefaultPromise;
23 import io.netty.util.concurrent.Future;
24 import io.netty.util.concurrent.GlobalEventExecutor;
25 import io.netty.util.concurrent.Promise;
26 import io.netty.util.concurrent.SucceededFuture;
27 import java.io.IOException;
28 import java.net.InetSocketAddress;
29 import java.util.concurrent.ExecutionException;
30 import java.util.concurrent.TimeUnit;
31 import java.util.concurrent.TimeoutException;
32 import org.junit.After;
33 import org.junit.Before;
34 import org.junit.Test;
35 import org.mockito.Mockito;
37 public class ServerTest {
38 SimpleDispatcher clientDispatcher, dispatcher;
40 SimpleSession session = null;
42 ChannelFuture server = null;
44 InetSocketAddress serverAddress;
45 private NioEventLoopGroup eventLoopGroup;
46 // Dedicated loop group for server, needed for testing reconnection client
47 // With dedicated server group we can simulate session drop by shutting only the server group down
48 private NioEventLoopGroup serverLoopGroup;
52 final int port = 10000 + (int)(10000 * Math.random());
53 serverAddress = new InetSocketAddress("127.0.0.1", port);
54 eventLoopGroup = new NioEventLoopGroup();
55 serverLoopGroup = new NioEventLoopGroup();
59 public void tearDown() throws IOException, InterruptedException, ExecutionException {
61 this.server.channel().close();
63 this.eventLoopGroup.shutdownGracefully().get();
64 this.serverLoopGroup.shutdownGracefully().get();
67 } catch (final InterruptedException e) {
68 throw new RuntimeException(e);
73 public void testConnectionRefused() throws Exception {
74 this.clientDispatcher = getClientDispatcher();
76 final ReconnectStrategy mockReconnectStrategy = getMockedReconnectStrategy();
78 this.clientDispatcher.createClient(this.serverAddress, mockReconnectStrategy, SimpleSessionListener::new);
80 Mockito.verify(mockReconnectStrategy, timeout(5000).atLeast(2)).scheduleReconnect(any(Throwable.class));
84 public void testConnectionReestablishInitial() throws Exception {
85 this.clientDispatcher = getClientDispatcher();
87 final ReconnectStrategy mockReconnectStrategy = getMockedReconnectStrategy();
89 this.clientDispatcher.createClient(this.serverAddress, mockReconnectStrategy, SimpleSessionListener::new);
91 Mockito.verify(mockReconnectStrategy, timeout(5000).atLeast(2)).scheduleReconnect(any(Throwable.class));
93 final Promise<Boolean> p = new DefaultPromise<>(GlobalEventExecutor.INSTANCE);
94 this.dispatcher = getServerDispatcher(p);
96 this.server = this.dispatcher.createServer(this.serverAddress, SimpleSessionListener::new);
100 assertEquals(true, p.get(3, TimeUnit.SECONDS));
104 public void testConnectionDrop() throws Exception {
105 final Promise<Boolean> p = new DefaultPromise<>(GlobalEventExecutor.INSTANCE);
107 this.dispatcher = getServerDispatcher(p);
109 this.server = this.dispatcher.createServer(this.serverAddress, SimpleSessionListener::new);
113 this.clientDispatcher = getClientDispatcher();
115 final ReconnectStrategy reconnectStrategy = getMockedReconnectStrategy();
116 this.session = this.clientDispatcher.createClient(this.serverAddress,
117 reconnectStrategy, SimpleSessionListener::new).get(6, TimeUnit.SECONDS);
119 assertEquals(true, p.get(3, TimeUnit.SECONDS));
123 // No reconnect should be scheduled after server drops connection with not-reconnecting client
124 verify(reconnectStrategy, times(0)).scheduleReconnect(any(Throwable.class));
128 public void testConnectionReestablishAfterDrop() throws Exception {
129 final Promise<Boolean> p = new DefaultPromise<>(GlobalEventExecutor.INSTANCE);
131 this.dispatcher = getServerDispatcher(p);
133 this.server = this.dispatcher.createServer(this.serverAddress, SimpleSessionListener::new);
137 this.clientDispatcher = getClientDispatcher();
139 final ReconnectStrategyFactory reconnectStrategyFactory = mock(ReconnectStrategyFactory.class);
140 final ReconnectStrategy reconnectStrategy = getMockedReconnectStrategy();
141 doReturn(reconnectStrategy).when(reconnectStrategyFactory).createReconnectStrategy();
143 this.clientDispatcher.createReconnectingClient(this.serverAddress,
144 reconnectStrategyFactory, SimpleSessionListener::new);
146 assertEquals(true, p.get(3, TimeUnit.SECONDS));
149 verify(reconnectStrategyFactory, timeout(20000).atLeast(2)).createReconnectStrategy();
153 public void testConnectionEstablished() throws Exception {
154 final Promise<Boolean> p = new DefaultPromise<>(GlobalEventExecutor.INSTANCE);
156 this.dispatcher = getServerDispatcher(p);
158 this.server = this.dispatcher.createServer(this.serverAddress, SimpleSessionListener::new);
162 this.clientDispatcher = getClientDispatcher();
164 this.session = this.clientDispatcher.createClient(this.serverAddress,
165 new NeverReconnectStrategy(GlobalEventExecutor.INSTANCE, 5000), SimpleSessionListener::new).get(6,
168 assertEquals(true, p.get(3, TimeUnit.SECONDS));
172 public void testConnectionFailed() throws IOException, InterruptedException, ExecutionException, TimeoutException {
173 final Promise<Boolean> p = new DefaultPromise<>(GlobalEventExecutor.INSTANCE);
175 this.dispatcher = getServerDispatcher(p);
177 this.server = this.dispatcher.createServer(this.serverAddress, SimpleSessionListener::new);
181 this.clientDispatcher = getClientDispatcher();
183 this.session = this.clientDispatcher.createClient(this.serverAddress,
184 new NeverReconnectStrategy(GlobalEventExecutor.INSTANCE, 5000), SimpleSessionListener::new).get(6,
187 final Future<?> session = this.clientDispatcher.createClient(this.serverAddress,
188 new NeverReconnectStrategy(GlobalEventExecutor.INSTANCE, 5000), SimpleSessionListener::new);
189 assertFalse(session.isSuccess());
193 public void testNegotiationFailedReconnect() throws Exception {
194 final Promise<Boolean> p = new DefaultPromise<>(GlobalEventExecutor.INSTANCE);
196 this.dispatcher = getServerDispatcher(p);
198 this.server = this.dispatcher.createServer(this.serverAddress, SimpleSessionListener::new);
202 this.clientDispatcher = new SimpleDispatcher(
203 (factory, channel, promise) -> new SimpleSessionNegotiator(promise, channel) {
205 protected void startNegotiation() throws Exception {
206 negotiationFailed(new IllegalStateException("Negotiation failed"));
208 }, new DefaultPromise<>(GlobalEventExecutor.INSTANCE), eventLoopGroup);
210 final ReconnectStrategyFactory reconnectStrategyFactory = mock(ReconnectStrategyFactory.class);
211 final ReconnectStrategy reconnectStrategy = getMockedReconnectStrategy();
212 doReturn(reconnectStrategy).when(reconnectStrategyFactory).createReconnectStrategy();
214 this.clientDispatcher.createReconnectingClient(this.serverAddress,
215 reconnectStrategyFactory, SimpleSessionListener::new);
218 // Reconnect strategy should be consulted at least twice, for initial connect and reconnect attempts after drop
219 verify(reconnectStrategyFactory, timeout((int) TimeUnit.MINUTES.toMillis(3)).atLeast(2)).createReconnectStrategy();
222 private SimpleDispatcher getClientDispatcher() {
223 return new SimpleDispatcher((factory, channel, promise) -> new SimpleSessionNegotiator(promise, channel), new DefaultPromise<>(GlobalEventExecutor.INSTANCE), eventLoopGroup);
226 private ReconnectStrategy getMockedReconnectStrategy() throws Exception {
227 final ReconnectStrategy mockReconnectStrategy = mock(ReconnectStrategy.class);
228 final Future<Void> future = new SucceededFuture<>(GlobalEventExecutor.INSTANCE, null);
229 doReturn(future).when(mockReconnectStrategy).scheduleReconnect(any(Throwable.class));
230 doReturn(5000).when(mockReconnectStrategy).getConnectTimeout();
231 doNothing().when(mockReconnectStrategy).reconnectSuccessful();
232 return mockReconnectStrategy;
236 private void shutdownServer() throws InterruptedException, ExecutionException {
238 server.channel().close().get();
239 // Closing server channel does not close established connections, eventLoop has to be closed as well to simulate dropped session
240 serverLoopGroup.shutdownGracefully().get();
243 private SimpleDispatcher getServerDispatcher(final Promise<Boolean> p) {
244 return new SimpleDispatcher((factory, channel, promise) -> {
246 return new SimpleSessionNegotiator(promise, channel);
247 }, null, serverLoopGroup);