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  import java.util.concurrent.BrokenBarrierException;
21  import java.util.concurrent.CyclicBarrier;
22  import java.util.concurrent.ExecutorService;
23  import java.util.concurrent.Executors;
24  import java.util.concurrent.Future;
25  import java.util.concurrent.TimeUnit;
26  import java.util.concurrent.TimeoutException;
27  
28  import org.junit.jupiter.api.AfterEach;
29  import org.junit.jupiter.api.Assertions;
30  import org.junit.jupiter.api.BeforeEach;
31  import org.junit.jupiter.api.Disabled;
32  
33  import ch.qos.logback.core.net.SocketConnector.ExceptionHandler;
34  import ch.qos.logback.core.net.server.test.ServerSocketUtil;
35  import ch.qos.logback.core.testUtil.EnvUtilForTests;
36  import org.junit.jupiter.api.Test;
37  import org.junit.jupiter.api.Timeout;
38  
39  /**
40   * Unit tests for {@link DefaultSocketConnector}.
41   *
42   * @author Carl Harris
43   */
44  public class DefaultSocketConnectorTest {
45  
46      private static final int DELAY = 2000;
47      private static final int SHORT_DELAY = 10;
48      private static final int RETRY_DELAY = 10;
49  
50      private MockExceptionHandler exceptionHandler = new MockExceptionHandler();
51  
52      private ServerSocket serverSocket;
53      private DefaultSocketConnector connector;
54  
55      ExecutorService executor = Executors.newSingleThreadExecutor();
56  
57      @BeforeEach
58      public void setUp() throws Exception {
59          if(EnvUtilForTests.isGithubAction())
60              return;
61          
62          serverSocket = ServerSocketUtil.createServerSocket();
63          connector = new DefaultSocketConnector(serverSocket.getInetAddress(), serverSocket.getLocalPort(), 0,
64                  RETRY_DELAY);
65          connector.setExceptionHandler(exceptionHandler);
66      }
67  
68      @AfterEach
69      public void tearDown() throws Exception {
70          if(EnvUtilForTests.isGithubAction())
71              return;
72          
73          if (serverSocket != null) {
74              serverSocket.close();
75          }
76      }
77  
78      @Test
79      public void testConnect() throws Exception {
80          if(EnvUtilForTests.isGithubAction())
81              return;
82          
83          Future<Socket> connectorTask = executor.submit(connector);
84  
85          Socket socket = connectorTask.get(DELAY, TimeUnit.MILLISECONDS);
86          Assertions.assertNotNull(socket);
87          connectorTask.cancel(true);
88  
89          Assertions.assertTrue(connectorTask.isDone());
90          socket.close();
91      }
92  
93      @Test
94      public void testConnectionFails() throws Exception {
95          if(EnvUtilForTests.isGithubAction())
96              return;
97          
98          serverSocket.close();
99          Future<Socket> connectorTask = executor.submit(connector);
100 
101         // this connection attempt will always time out
102         try {
103             connectorTask.get(SHORT_DELAY, TimeUnit.MILLISECONDS);
104             Assertions.fail();
105         } catch (TimeoutException e) {
106         }
107         Exception lastException = exceptionHandler.awaitConnectionFailed();
108         Assertions.assertTrue(lastException instanceof ConnectException);
109         Assertions.assertFalse(connectorTask.isDone());
110         connectorTask.cancel(true);
111 
112         // thread.join(4 * DELAY);
113         Assertions.assertTrue(connectorTask.isCancelled());
114     }
115 
116     @Disabled
117     @Test
118     @Timeout(value=5, unit = TimeUnit.SECONDS)
119     public void testConnectEventually() throws Exception {
120         if(EnvUtilForTests.isGithubAction())
121             return;
122         
123         serverSocket.close();
124 
125         Future<Socket> connectorTask = executor.submit(connector);
126         // this connection attempt will always time out
127         try {
128             connectorTask.get(SHORT_DELAY, TimeUnit.MILLISECONDS);
129             Assertions.fail();
130         } catch (TimeoutException e) {
131         }
132 
133         // the following call requires over 1000 millis
134         Exception lastException = exceptionHandler.awaitConnectionFailed();
135         Assertions.assertNotNull(lastException);
136         Assertions.assertTrue(lastException instanceof ConnectException);
137 
138         // now rebind to the same local address
139         SocketAddress address = serverSocket.getLocalSocketAddress();
140         serverSocket = new ServerSocket();
141         serverSocket.setReuseAddress(true);
142         serverSocket.bind(address);
143 
144         // now we should be able to connect
145         Socket socket = connectorTask.get(2 * DELAY, TimeUnit.MILLISECONDS);
146 
147         Assertions.assertNotNull(socket);
148 
149         Assertions.assertFalse(connectorTask.isCancelled());
150         socket.close();
151     }
152 
153     private static class MockExceptionHandler implements ExceptionHandler {
154 
155         private final CyclicBarrier failureBarrier = new CyclicBarrier(2);
156 
157         private Exception lastException;
158 
159         public void connectionFailed(SocketConnector connector, Exception ex) {
160             lastException = ex;
161             try {
162                 failureBarrier.await();
163             } catch (InterruptedException | BrokenBarrierException e) {
164                 e.printStackTrace();
165             }
166         }
167 
168         public Exception awaitConnectionFailed() throws InterruptedException {
169             if (lastException != null) {
170                 return lastException;
171             }
172 
173             try {
174                 failureBarrier.await();
175             } catch (InterruptedException | BrokenBarrierException e) {
176                 e.printStackTrace();
177             }
178             return lastException;
179         }
180 
181     }
182 
183 }