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