001/** 002 * Logback: the reliable, generic, fast and flexible logging framework. 003 * Copyright (C) 1999-2015, QOS.ch. All rights reserved. 004 * 005 * This program and the accompanying materials are dual-licensed under 006 * either the terms of the Eclipse Public License v1.0 as published by 007 * the Eclipse Foundation 008 * 009 * or (per the licensee's choosing) 010 * 011 * under the terms of the GNU Lesser General Public License version 2.1 012 * as published by the Free Software Foundation. 013 */ 014package ch.qos.logback.core.net; 015 016import java.net.ConnectException; 017import java.net.ServerSocket; 018import java.net.Socket; 019import java.net.SocketAddress; 020 021import java.util.concurrent.ExecutorService; 022import java.util.concurrent.Executors; 023import java.util.concurrent.Future; 024import java.util.concurrent.TimeUnit; 025import java.util.concurrent.TimeoutException; 026import java.util.concurrent.locks.Condition; 027import java.util.concurrent.locks.Lock; 028import java.util.concurrent.locks.ReentrantLock; 029 030import org.junit.After; 031import org.junit.Before; 032import org.junit.Test; 033 034import ch.qos.logback.core.net.SocketConnector.ExceptionHandler; 035import ch.qos.logback.core.net.server.test.ServerSocketUtil; 036 037import static org.junit.Assert.assertNotNull; 038import static org.junit.Assert.fail; 039import static org.junit.Assert.assertFalse; 040import static org.junit.Assert.assertTrue; 041 042/** 043 * Unit tests for {@link DefaultSocketConnector}. 044 * 045 * @author Carl Harris 046 */ 047public class DefaultSocketConnectorTest { 048 049 private static final int DELAY = 1000; 050 private static final int SHORT_DELAY = 10; 051 private static final int RETRY_DELAY = 10; 052 053 private MockExceptionHandler exceptionHandler = new MockExceptionHandler(); 054 055 private ServerSocket serverSocket; 056 private DefaultSocketConnector connector; 057 058 ExecutorService executor = Executors.newSingleThreadExecutor(); 059 060 @Before 061 public void setUp() throws Exception { 062 serverSocket = ServerSocketUtil.createServerSocket(); 063 connector = new DefaultSocketConnector(serverSocket.getInetAddress(), serverSocket.getLocalPort(), 0, RETRY_DELAY); 064 connector.setExceptionHandler(exceptionHandler); 065 } 066 067 @After 068 public void tearDown() throws Exception { 069 if (serverSocket != null) { 070 serverSocket.close(); 071 } 072 } 073 074 @Test 075 public void testConnect() throws Exception { 076 Future<Socket> connectorTask = executor.submit(connector); 077 078 Socket socket = connectorTask.get(2 * DELAY, TimeUnit.MILLISECONDS); 079 assertNotNull(socket); 080 connectorTask.cancel(true); 081 082 assertTrue(connectorTask.isDone()); 083 socket.close(); 084 } 085 086 @Test 087 public void testConnectionFails() throws Exception { 088 serverSocket.close(); 089 Future<Socket> connectorTask = executor.submit(connector); 090 091 // this connection attempt will always timeout 092 try { 093 connectorTask.get(SHORT_DELAY, TimeUnit.MILLISECONDS); 094 fail(); 095 } catch (TimeoutException e) { 096 } 097 Exception lastException = exceptionHandler.awaitConnectionFailed(DELAY); 098 assertTrue(lastException instanceof ConnectException); 099 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}