1   /*
2    * $Id: LingerExperimentMule2067TestCase.java 7976 2007-08-21 14:26:13Z dirk.olmes $
3    * --------------------------------------------------------------------------------------
4    * Copyright (c) MuleSource, Inc.  All rights reserved.  http://www.mulesource.com
5    *
6    * The software in this package is published under the terms of the CPAL v1.0
7    * license, a copy of which has been included with this distribution in the
8    * LICENSE.txt file.
9    */
10  
11  package org.mule.providers.tcp.issues;
12  
13  import java.io.IOException;
14  import java.net.ServerSocket;
15  import java.net.Socket;
16  import java.net.InetSocketAddress;
17  
18  import junit.framework.TestCase;
19  import org.apache.commons.logging.Log;
20  import org.apache.commons.logging.LogFactory;
21  
22  /**
23   * Are the "address already in use" errors coming from lingering sockets?
24   *
25   * We see "address already in use" errors when trying to re-use sockets very quickly,
26   * but the tests below don't give much information, except that:
27   * - data needs to be sent
28   * - explicitly setting or disabling the SO_LINGER value has little effect
29   */
30  public class LingerExperimentMule2067TestCase extends TestCase
31  {
32  
33      private static final int NO_LINGER = -1;
34      private static final int HARD_CLOSE = 0;
35      private static final int NO_WAIT = -1;
36      private static final int PORT = 65432;
37  
38      private Log logger = LogFactory.getLog(getClass());
39  
40      public void testInoffensive() throws IOException
41      {
42          // this shows it's not simple open/close that causes a problem
43          openCloseServer(1000, PORT); // ok
44          openCloseClientServer(1000, PORT, NO_LINGER, NO_LINGER); // ok
45      }
46  
47      public void testThisShowsTheProblem() throws IOException
48      {
49          // this shows a problem with repeated open/close with a client/server pair
50          repeatOpenCloseClientServer(10, 10, PORT, 1000); // ok
51          repeatOpenCloseClientServer(10, 10, PORT, 100); // ok
52          repeatOpenCloseClientServer(10, 10, PORT, 10); // ok
53          repeatOpenCloseClientServer(10, 10, PORT, 1); // ok
54          repeatOpenCloseClientServer(10, 10, PORT, 0); // intermittent
55          repeatOpenCloseClientServer(10, 10, PORT, NO_WAIT); // intermittent
56      }
57  
58      public void testWithClientLinger() throws IOException
59      {
60          // this shows it's not simple client linger time, or the later tests would always fail
61          repeatOpenCloseClientServer(10, 10, PORT, NO_WAIT, NO_LINGER); // intermittent, as above
62          repeatOpenCloseClientServer(10, 10, PORT, 100, 1); // ok
63          repeatOpenCloseClientServer(10, 10, PORT, 10, 1); // intermittent
64          repeatOpenCloseClientServer(10, 10, PORT, 100, 2); // intermittent
65          repeatOpenCloseClientServer(10, 10, PORT, 100, 30); // intermittent
66          // hard close on client doesn't help
67          repeatOpenCloseClientServer(10, 10, PORT, 10, HARD_CLOSE); // intermittent
68          repeatOpenCloseClientServer(10, 10, PORT, NO_WAIT, HARD_CLOSE); // intermittent
69      }
70  
71      public void testWithServerLinger() throws IOException
72      {
73          // server linger seems to improve things(?!), but still have problems
74          repeatOpenCloseClientServer(10, 10, PORT, 10, NO_LINGER, 1); // ok
75          repeatOpenCloseClientServer(10, 10, PORT, 10, NO_LINGER, 1); // ok
76          repeatOpenCloseClientServer(10, 10, PORT, 10, NO_LINGER, 2); // ok
77          repeatOpenCloseClientServer(10, 10, PORT, 10, NO_LINGER, 30); // ok
78          repeatOpenCloseClientServer(10, 10, PORT, NO_WAIT, NO_LINGER, 1); // intermittent
79      }
80  
81      public void testHardClose() throws IOException
82      {
83          // this gives (very?) occasional "already in use" and also a "connection reset by peer"
84          // at the client, due to server closing so quickly
85          repeatOpenCloseClientServer(10, 10, PORT, NO_WAIT, HARD_CLOSE, HARD_CLOSE); // intermittent
86      }
87  
88      protected void openCloseServer(int numberOfSockets, int port) throws IOException
89      {
90          for (int i = 0; i < numberOfSockets; i++)
91          {
92              ServerSocket socket = new ServerSocket(port);
93              socket.close();
94          }
95      }
96  
97      protected void repeatOpenCloseClientServer(int numberOfRepeats, int numberOfConnections, int port, long pause)
98              throws IOException
99      {
100         repeatOpenCloseClientServer(numberOfRepeats, numberOfConnections, port, pause, NO_LINGER);
101     }
102 
103     protected void repeatOpenCloseClientServer(int numberOfRepeats, int numberOfConnections, int port,
104                                                long pause, int clientLinger)
105             throws IOException
106     {
107         repeatOpenCloseClientServer(numberOfRepeats, numberOfConnections, port, pause, clientLinger, NO_LINGER);
108     }
109 
110     protected void repeatOpenCloseClientServer(int numberOfRepeats, int numberOfConnections, int port,
111                                                long pause, int clientLinger, int serverLinger)
112             throws IOException
113     {
114         logger.info("Repeating openCloseClientServer with pauses of " + pause + " ms and lingers of "
115                 + clientLinger + "/" + serverLinger + " s (client/server)");
116         for (int i = 0; i < numberOfRepeats; i++)
117         {
118             if (0 != i && pause != NO_WAIT)
119             {
120                 try
121                 {
122                     if (pause > 0)
123                     {
124                         Thread.sleep(pause);
125                     }
126                 }
127                 catch (InterruptedException e)
128                 {
129                     // ignore
130                 }
131             }
132             openCloseClientServer(numberOfConnections, port, clientLinger, serverLinger);
133         }
134     }
135 
136     protected void openCloseClientServer(int numberOfConnections, int port, int clientLinger, int serverLinger)
137             throws IOException
138     {
139         Server server = new Server(port, serverLinger);
140         try
141         {
142             new Thread(server).start();
143             for (int i = 0; i < numberOfConnections; i++)
144             {
145                 logger.debug("opening socket " + i);
146                 Socket client = new Socket("localhost", port);
147                 if (NO_LINGER != clientLinger)
148                 {
149                     client.setSoLinger(true, clientLinger);
150                 }
151                 client.close();
152             }
153         }
154         finally
155         {
156             server.close();
157         }
158     }
159 
160     protected static class Server implements Runnable
161     {
162 
163         private Log logger = LogFactory.getLog(getClass());
164         private ServerSocket server;
165         private int linger;
166 
167         public Server(int port, int linger) throws IOException
168         {
169             this.linger = linger;
170             server = new ServerSocket();
171             server.bind(new InetSocketAddress("localhost", port));
172         }
173 
174         public void run()
175         {
176             try
177             {
178                 while (true)
179                 {
180                     Socket socket = server.accept();
181                     if (NO_LINGER != linger)
182                     {
183                         socket.setSoLinger(true, linger);
184                     }
185                     socket.close();
186                 }
187             }
188             catch (Exception e)
189             {
190                 logger.debug("Expected - dirty closedown: " + e);
191             }
192         }
193 
194         public void close() throws IOException
195         {
196             server.close();
197             server = null;
198         }
199     }
200 
201 }