View Javadoc
1   /**
2    * Logback: the reliable, generic, fast and flexible logging framework.
3    * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
4    *
5    * This program and the accompanying materials are dual-licensed under
6    * either the terms of the Eclipse Public License v1.0 as published by
7    * the Eclipse Foundation
8    *
9    *   or (per the licensee's choosing)
10   *
11   * under the terms of the GNU Lesser General Public License version 2.1
12   * as published by the Free Software Foundation.
13   */
14  package ch.qos.logback.core.net;
15  
16  import java.net.ConnectException;
17  import java.net.ServerSocket;
18  import java.net.Socket;
19  import java.net.SocketAddress;
20  
21  import java.util.concurrent.ExecutorService;
22  import java.util.concurrent.Executors;
23  import java.util.concurrent.Future;
24  import java.util.concurrent.TimeUnit;
25  import java.util.concurrent.TimeoutException;
26  import java.util.concurrent.locks.Condition;
27  import java.util.concurrent.locks.Lock;
28  import java.util.concurrent.locks.ReentrantLock;
29  
30  import org.junit.After;
31  import org.junit.Before;
32  import org.junit.Test;
33  
34  import ch.qos.logback.core.net.SocketConnector.ExceptionHandler;
35  import ch.qos.logback.core.net.server.test.ServerSocketUtil;
36  
37  import static org.junit.Assert.assertNotNull;
38  import static org.junit.Assert.fail;
39  import static org.junit.Assert.assertFalse;
40  import static org.junit.Assert.assertTrue;
41  
42  /**
43   * Unit tests for {@link DefaultSocketConnector}.
44   *
45   * @author Carl Harris
46   */
47  public class DefaultSocketConnectorTest {
48  
49      private static final int DELAY = 1000;
50      private static final int SHORT_DELAY = 10;
51      private static final int RETRY_DELAY = 10;
52  
53      private MockExceptionHandler exceptionHandler = new MockExceptionHandler();
54  
55      private ServerSocket serverSocket;
56      private DefaultSocketConnector connector;
57  
58      ExecutorService executor = Executors.newSingleThreadExecutor();
59  
60      @Before
61      public void setUp() throws Exception {
62          serverSocket = ServerSocketUtil.createServerSocket();
63          connector = new DefaultSocketConnector(serverSocket.getInetAddress(), serverSocket.getLocalPort(), 0, RETRY_DELAY);
64          connector.setExceptionHandler(exceptionHandler);
65      }
66  
67      @After
68      public void tearDown() throws Exception {
69          if (serverSocket != null) {
70              serverSocket.close();
71          }
72      }
73  
74      @Test
75      public void testConnect() throws Exception {
76          Future<Socket> connectorTask = executor.submit(connector);
77  
78          Socket socket = connectorTask.get(2 * DELAY, TimeUnit.MILLISECONDS);
79          assertNotNull(socket);
80          connectorTask.cancel(true);
81  
82          assertTrue(connectorTask.isDone());
83          socket.close();
84      }
85  
86      @Test
87      public void testConnectionFails() throws Exception {
88          serverSocket.close();
89          Future<Socket> connectorTask = executor.submit(connector);
90  
91          // this connection attempt will always timeout
92          try {
93              connectorTask.get(SHORT_DELAY, TimeUnit.MILLISECONDS);
94              fail();
95          } catch (TimeoutException e) {
96          }
97          Exception lastException = exceptionHandler.awaitConnectionFailed(DELAY);
98          assertTrue(lastException instanceof ConnectException);
99          assertFalse(connectorTask.isDone());
100         connectorTask.cancel(true);
101 
102         // thread.join(4 * DELAY);
103         assertTrue(connectorTask.isCancelled());
104     }
105 
106     @Test(timeout = 5000)
107     public void testConnectEventually() throws Exception {
108         serverSocket.close();
109 
110         Future<Socket> connectorTask = executor.submit(connector);
111         // this connection attempt will always timeout
112         try {
113             connectorTask.get(SHORT_DELAY, TimeUnit.MILLISECONDS);
114             fail();
115         } catch (TimeoutException e) {
116         }
117 
118         // on Ceki's machine (Windows 7) this always takes 1second regardless of the value of DELAY
119         Exception lastException = exceptionHandler.awaitConnectionFailed(DELAY);
120         assertNotNull(lastException);
121         assertTrue(lastException instanceof ConnectException);
122 
123         // now rebind to the same local address
124         SocketAddress address = serverSocket.getLocalSocketAddress();
125         serverSocket = new ServerSocket();
126         serverSocket.setReuseAddress(true);
127         serverSocket.bind(address);
128 
129         // now we should be able to connect
130         Socket socket = connectorTask.get(2 * DELAY, TimeUnit.MILLISECONDS);
131 
132         assertNotNull(socket);
133 
134         assertFalse(connectorTask.isCancelled());
135         socket.close();
136     }
137 
138     private static class MockExceptionHandler implements ExceptionHandler {
139 
140         private final Lock lock = new ReentrantLock();
141         private final Condition failedCondition = lock.newCondition();
142 
143         private Exception lastException;
144 
145         public void connectionFailed(SocketConnector connector, Exception ex) {
146             lastException = ex;
147         }
148 
149         public Exception awaitConnectionFailed(long delay) throws InterruptedException {
150             lock.lock();
151             try {
152                 long increment = 10;
153                 while (lastException == null && delay > 0) {
154                     boolean success = failedCondition.await(increment, TimeUnit.MILLISECONDS);
155                     delay -= increment;
156                     if (success)
157                         break;
158 
159                 }
160                 return lastException;
161             } finally {
162                 lock.unlock();
163             }
164         }
165 
166     }
167 
168 }