
/*
* JBoss, Home of Professional Open Source
* Copyright 2005, JBoss Inc., and individual contributors as indicated
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/

/*
 * Created on Aug 18, 2005
 */
 
package org.jboss.test.remoting.transport.multiplex;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.BindException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketAddress;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.net.UnknownHostException;

import org.jboss.remoting.transport.multiplex.MasterServerSocket;
import org.jboss.remoting.transport.multiplex.MultiplexingManager;
import org.jboss.remoting.transport.multiplex.VirtualServerSocket;
import org.jboss.remoting.transport.multiplex.VirtualServerSocketFactory;
import org.jboss.remoting.transport.multiplex.VirtualSocket;



/**
 * A BasicServerSocketBehaviorClient.

 * @author <a href="mailto:r.sigal@computer.org">Ron Sigal</a>
 * @version $Revision: 1047 $
 * <p>
 * Copyright (c) 2005
 * </p>
 */

public class BasicServerSocketBehaviorClient extends ShutdownClient implements MultiplexConstants
{

/***
 *  FIXME Comment this
 *
 *
 */
   public void testPiggybackedVirtualServerSocket()
   {
      try
      {
         byte[] script = new byte[] {READ, // make server wait until client's ServerSocket is up
                                     CONNECT_TO_CLIENT,
                                     WRITE_TO_CLIENT,
                                     READ_FROM_CLIENT,
                                     CLOSE_CLIENT_SOCKET,
                                     READ, // make server wait until client's ServerSocket is up
                                     CONNECT_TO_CLIENT,
                                     WRITE_TO_CLIENT,
                                     READ_FROM_CLIENT,
                                     CLOSE_CLIENT_SOCKET,
                                     READ, // make server wait until client's ServerSocket is up
                                     CONNECT_TO_CLIENT,
                                     WRITE_TO_CLIENT,
                                     READ_FROM_CLIENT,
                                     CLOSE_CLIENT_SOCKET,
                                     CLOSE_TEST_SOCKET};

         scriptStream.write(script.length);
         scriptStream.write(script);
      }
      catch (IOException e)
      {
         log.error(e);
      }

      VirtualServerSocket serverSocket = null;
      int testSocketPort = testSocket.getLocalPort();

      try
      {
         serverSocket = new VirtualServerSocket(testSocketPort);
         assertTrue(doOneServerSocketTest(serverSocket, testSocketPort));
         serverSocket.close();
         assertTrue(serverSocket.isBound() == true);
         assertTrue(serverSocket.isClosed() == true);
      }
      catch (Exception e)
      {
         log.error(e);
         e.printStackTrace();
         fail();
      }

      try
      {
         // Give remote ServerSocket time to unregister.
         InetSocketAddress address = new InetSocketAddress(serverSocket.getInetAddress(), serverSocket.getRemotePort());
         log.warn("address: " + address);
         while (MultiplexingManager.checkForShareableManager(address))
            Thread.sleep(2000);

         serverSocket = new VirtualServerSocket();
         assertTrue(serverSocket.isBound() == false);
         assertTrue(serverSocket.isClosed() == false);

         // Bind serverSocket to the testSocket's local port.  This will get the MultiplexingManager
         // associated with testSocket, which is already connected
         doBind(serverSocket, null, new InetSocketAddress(clientServerSocketHost, testSocketPort));

         assertTrue(serverSocket.isBound() == true);
         assertTrue(doOneServerSocketTest(serverSocket, testSocketPort));
         serverSocket.close();
         assertTrue(serverSocket.isBound() == true);
         assertTrue(serverSocket.isClosed() == true);
      }
      catch (Exception e)
      {
         log.error(e);
         e.printStackTrace();
         fail();
      }

      try
      {
         // Create a new VirtualServerSocket, and bind it to testSocket's local port.
         // This will get the MultiplexingManager associated with testSocket, which is already connected
         serverSocket = doBind(null,
                               basicBehaviorServerSocketAddress,
                               new InetSocketAddress(clientServerSocketHost, testSocketPort));

         assertTrue(serverSocket.isBound() == true);
         assertTrue(serverSocket.isConnected() == true);
         assertTrue(serverSocket.isClosed() == false);
         assertTrue(doOneServerSocketTest(serverSocket, testSocketPort));
         serverSocket.close();
         assertTrue(serverSocket.isBound() == true);
         assertTrue(serverSocket.isConnected() == true);
         assertTrue(serverSocket.isClosed() == true);
      }
      catch (Exception e)
      {
         log.error(e);
         e.printStackTrace();
         fail();
      }
      
      log.info("testPiggybackedVirtualServerSocket() PASSES");
   }


   public void testIndependentVirtualServerSocket()
   {
      try
      {
         byte[] script = new byte[] {
                                     ACCEPT_SERVER_SOCKET,
                                     READ, // make server wait until client's ServerSocket is up
                                     CONNECT_TO_CLIENT_VSS,
                                     WRITE_TO_CLIENT,
		               				 READ_FROM_CLIENT,
		               				 CLOSE_CLIENT_SOCKET,
	                                 ACCEPT_SERVER_SOCKET,
		               				 READ, // make server wait until client's ServerSocket is up
	                                 WRITE,
		               				 CONNECT_TO_CLIENT_VSS,
                                     WRITE_TO_CLIENT,
		               				 READ_FROM_CLIENT,
		               				 CLOSE_CLIENT_SOCKET,
		               			     CLOSE_TEST_SOCKET
		               			     };

         scriptStream.write(script.length);
         scriptStream.write(script);
      }
      catch (IOException e)
      {
         log.error(e);
      }

      VirtualServerSocket serverSocket = null;

      try
      {
         serverSocket = new VirtualServerSocket();
         assertTrue(serverSocket.isBound() == false);
         assertTrue(serverSocket.isClosed() == false);

         // Bind serverSocket to an unused local address.  This will lead to the creation of a new
         // real socket, bound to that address, and a MultiplexingManager to wrap it.
         log.info("binding to: " + nextBindPort);
         doBind(serverSocket, null, new InetSocketAddress(clientServerSocketHost, nextBindPort));

         assertTrue(serverSocket.isBound() == true);

         // Connect serverSocket to a remote address.  This will cause the real socket to connect to a
         // MasterServerSocket at that address, leading to the creation of a real socket on the
         // remote host and a MultiplexingManager to wrap it.
         log.info("connecting server socket to port: " + ++nextConnectPort);
         InetSocketAddress address1 = new InetSocketAddress(basicBehaviorServerHost, nextConnectPort);
         is = testSocket.getInputStream();
         is.read();
         serverSocket.connect(address1);

         assertTrue(serverSocket.isConnected());
         assertTrue(doOneServerSocketTest(serverSocket, nextBindPort));
         serverSocket.close();
         assertTrue(serverSocket.isBound() == true);
         assertTrue(serverSocket.isClosed() == true);
      }
      catch (Exception e)
      {
         log.error(e);
         e.printStackTrace();
         fail();
      }

      try
      {
         // Create a new VirtualServerSocket, bind it to an unused local address, and connect it
         // to the server.  This will lead to the creation of a new real socket, wrapped by a MultiplexingManager,
         // bound to that address and connected to the server.

         nextBindPort++;
         InetSocketAddress bindAddress = new InetSocketAddress(clientServerSocketHost, nextBindPort);
         log.info("binding to: " + nextBindPort);
         log.info("connecting server socket to port: " + ++nextConnectPort);
         InetSocketAddress address2 = new InetSocketAddress(basicBehaviorServerHost, nextConnectPort);
         is.read();
         serverSocket = doBind(null, address2, bindAddress);

         assertTrue(serverSocket.isBound() == true);
         assertTrue(serverSocket.isConnected() == true);
         assertTrue(serverSocket.isClosed() == false);
         assertTrue(doOneServerSocketTest(serverSocket, nextBindPort));
         serverSocket.close();
         assertTrue(serverSocket.isBound() == true);
         assertTrue(serverSocket.isConnected() == true);
         assertTrue(serverSocket.isClosed() == true);
      }
      catch (Exception e)
      {
         log.error(e);
         e.printStackTrace();
         fail();
      }
      
      log.info("testIndependentVirtualServerSocket() PASSES");
   }


/**
 *  FIXME Comment this
 *
 *
 */
   public void testMasterServerSocket()
   {
      try
      {
         byte[] script = new byte[] {READ, // make server wait until client's ServerSocket is up
                                     CONNECT_TO_CLIENT_MSS,
                                     WRITE_TO_CLIENT,
                                     READ_FROM_CLIENT,
                                     CLOSE_CLIENT_SOCKET,
                                     CLOSE_TEST_SOCKET
         };

         scriptStream.write(script.length);
         scriptStream.write(script);
      }
      catch (IOException e)
      {
         log.error(e);
      }

      MasterServerSocket serverSocket = null;

      try
      {
         InetAddress bindAddress = InetAddress.getByName(clientServerSocketHost);
         nextBindPort++;
         log.info("binding to: " + nextBindPort);
         serverSocket = new MasterServerSocket(nextBindPort, 50, bindAddress);
         assertTrue(doOneServerSocketTest(serverSocket, nextBindPort));
         serverSocket.close();
         log.info("serverSocket bind address: " + serverSocket.getLocalPort());
         log.info("serverSocket.isClosed(): " + serverSocket.isClosed());
      }
      catch (Exception e)
      {
         log.error(e);
         e.printStackTrace();
         fail();
      }
      
      log.info("testMasterServerSocket() PASSES");
   }


/**
 *  FIXME Comment this
 *
 *
 */
   public void testServerSocketFactory()
   {
      log.info("entering testServerSocketFactory()");

      try
      {
         byte[] script = new byte[]
         {     ACCEPT_SERVER_SOCKET,
               READ, // make server wait until client's ServerSocket is up
               CONNECT_TO_CLIENT_VSS,
               WRITE_TO_CLIENT,
               READ_FROM_CLIENT,
               CLOSE_CLIENT_SOCKET,
               READ, // make server wait until client's ServerSocket is up
               CONNECT_TO_CLIENT_MSS,
               WRITE_TO_CLIENT,
               READ_FROM_CLIENT,
               CLOSE_CLIENT_SOCKET,
               CLOSE_TEST_SOCKET
         };

         scriptStream.write(script.length);
         scriptStream.write(script);
      }
      catch (IOException e)
      {
         log.error(e);
      }

      try
      {
         VirtualServerSocketFactory serverSocketFactory
         	= (VirtualServerSocketFactory) VirtualServerSocketFactory.getDefault();
         serverSocketFactory.setOnClient();
         nextBindPort++;
         log.info("binding to: " + nextBindPort);
         InetAddress bindAddress = InetAddress.getByName(clientServerSocketHost);
         VirtualServerSocket serverSocket
         	= (VirtualServerSocket) serverSocketFactory.createServerSocket(nextBindPort, 50, bindAddress);
         log.info("connecting server socket to port: " + ++nextConnectPort);
         InetSocketAddress address = new InetSocketAddress(basicBehaviorServerHost, nextConnectPort);
         is = testSocket.getInputStream();
         is.read();
         serverSocket.connect(address);
         assertTrue(doOneServerSocketTest(serverSocket, nextBindPort));
         serverSocket.close();
      }
      catch (Exception e)
      {
         log.error(e);
         e.printStackTrace();
         fail();
      }

      try
      {
         VirtualServerSocketFactory serverSocketFactory
         	= (VirtualServerSocketFactory) VirtualServerSocketFactory.getDefault();
         serverSocketFactory.setOnServer();
         nextBindPort++;
         log.info("binding to: " + nextBindPort);
         InetAddress bindAddress = InetAddress.getByName(clientServerSocketHost);
         MasterServerSocket serverSocket
         	= (MasterServerSocket) serverSocketFactory.createServerSocket(nextBindPort, 50, bindAddress);
         assertTrue(doOneServerSocketTest(serverSocket, nextBindPort));
         serverSocket.close();
      }
      catch (Exception e)
      {
         log.error(e);
         e.printStackTrace();
         fail();
      }
      
      log.info("testServerSocketFactory() PASSES");
   }


/**
 *  FIXME Comment this
 *
 *
 */
   public void testConnectTimeout()
   {
      log.info("entering testConnectTimeout()");

      try
      {
         byte[] script = new byte[]
         {
            RUN_SERVER_TIMEOUT_TEST,
            CLOSE_TEST_SOCKET
         };

         scriptStream.write(script.length);
         scriptStream.write(script);
      }
      catch (IOException e)
      {
         log.error(e);
      }

      try
      {
         Socket socket1 = new VirtualSocket();
         SocketAddress address1 = new InetSocketAddress(basicBehaviorServerHost, basicBehaviorServerPort + 100);
         is = testSocket.getInputStream();
         os = testSocket.getOutputStream();

         is.read();

         try
         {
            socket1.connect(address1, 200);
            fail();
         }
         catch (SocketTimeoutException e)
         {
            if (e.getMessage().equals("connect timed out"))
               log.info("received expected timeout exception");
            else
               fail();
         }

         while (!socket1.isClosed())
         {
            try
            {
               Thread.sleep(500);
               log.error("waiting...");
            }
            catch (InterruptedException ignored) {}
         }
         
         try
         {
            socket1.connect(address1, 2000);
            fail();
         }
         catch (SocketException e)
         {
            if (e.getMessage().equals("Socket is closed"))
               log.info("received expected exception");
            else
            {
               log.error(e);
               fail();
            }
         }

         Socket socket2 = new VirtualSocket();
         socket2.connect(address1, 20000);

         socket1.close();
         socket2.close();


         new Thread ()
         {
            public void run()
            {

               MasterServerSocket mss;
               try
               {
                  mss = new MasterServerSocket(clientServerSocketPort + 101);
                  log.info(mss.toString() + ": MasterServerSocket accepting");
                  os.write(3);
                  Socket s = mss.accept();
                  log.info(mss.toString() + ": accepted");
                  s.close();
                  mss.close();
               }
               catch (IOException e)
               {
                  log.error(e);
               }
            }
         }.start();

         os = testSocket.getOutputStream();
         Socket socket3 = new VirtualSocket();
         SocketAddress address2 = new InetSocketAddress(basicBehaviorServerHost, basicBehaviorServerPort + 101);
         is.read();

         try
         {
            log.info("connecting to: " + address2);
            log.info("shareable: " + MultiplexingManager.checkForShareableManager((InetSocketAddress)address2));
            socket3.connect(address2, 400);
            fail();
         }
         catch (SocketTimeoutException e)
         {
            if (e.getMessage().equals("connect timed out"))
               log.info("received expected timeout exception");
            else
               fail();
         }

         try
         {
            socket3.connect(address1, 200);
            fail();
         }
         catch (SocketException e)
         {
            if (e.getMessage().equals("Socket is closed"))
               log.info("received expected exception");
            else
            {
               log.error(e);
               fail();
            }
         }

         VirtualSocket socket4 = new VirtualSocket();
         socket4.connect(address2, 20000);
//         socket4.connect(address2);

         socket3.close();
         socket4.close();
      }
      catch(Exception e)
      {
         log.error(e);
         e.printStackTrace();
         fail();
      }
      
      log.info("testConnectTimeout() PASSES");
   }


   public void testAcceptTimeout()
   {
      try
      {
         byte[] script = new byte[] {
                                     ACCEPT_SERVER_SOCKET,
                                     READ, // make server wait until client's ServerSocket is up
                                     CONNECT_TO_CLIENT_VSS,
		               				 CLOSE_CLIENT_SOCKET,
		               				 READ, // make server wait until client's ServerSocket is up
		               				 CONNECT_TO_CLIENT_MSS,
		               				 CLOSE_CLIENT_SOCKET,
		               			     CLOSE_TEST_SOCKET
		               			     };

         scriptStream.write(script.length);
         scriptStream.write(script);
      }
      catch (IOException e)
      {
         log.error(e);
      }

      VirtualServerSocket serverSocket1 = null;

      try
      {
         Socket virtualSocket1 = null;
         Socket virtualSocket2 = null;
         Socket virtualSocket3 = null;
         Socket virtualSocket4 = null;

         serverSocket1 = new VirtualServerSocket();
         log.info("binding to: " + ++nextBindPort);
         doBind(serverSocket1, null, new InetSocketAddress(clientServerSocketHost, nextBindPort));
         log.info("connecting server socket to port: " + ++nextConnectPort);
         InetSocketAddress address1 = new InetSocketAddress(basicBehaviorServerHost, nextConnectPort);
         is = testSocket.getInputStream();
         is.read();
         serverSocket1.connect(address1);
         serverSocket1.setSoTimeout(2000);

         try
         {
            virtualSocket1 = serverSocket1.accept();
            fail();
         }
         catch (SocketTimeoutException e)
         {
            if (e.getMessage().equals("Accept timed out"))
               log.info("received expected timeout exception");
            else
               fail();
         }

         assertTrue(virtualSocket1 == null);
         os = testSocket.getOutputStream();
         os.write(3);
         virtualSocket2 = serverSocket1.accept();
         assertTrue(virtualSocket2.getSoTimeout() == 0);
         virtualSocket2.close();
         serverSocket1.close();

         log.info("binding to: " + ++nextBindPort);
         MasterServerSocket serverSocket2 = new MasterServerSocket(nextBindPort);
         serverSocket2.setSoTimeout(2000);

         try
         {
            virtualSocket3 = serverSocket2.accept();
            fail();
         }
         catch (SocketTimeoutException e)
         {
            if (e.getMessage().equals("Accept timed out"))
               log.info("received expected timeout exception");
            else
               fail();
         }

         assertTrue(virtualSocket3 == null);
         os.write(5);
         serverSocket2.setSoTimeout(20000);
         virtualSocket4 = serverSocket2.accept();
         assertTrue(virtualSocket4.getSoTimeout() == 0);
         virtualSocket4.close();
         serverSocket2.close();
      }
      catch (Exception e)
      {
         log.error(e);
         e.printStackTrace();
         fail();
      }
      
      log.info("testAcceptTimeout() PASSES");
   }


/**
 *  FIXME Comment this
 *
 *
 */
   public void testVSS_to_VSS()
   {
      try
      {
         byte[] script = new byte[]
         {
               RUN_VSS_TO_VSS,
               CLOSE_TEST_SOCKET
		 };

         scriptStream.write(script.length);
         scriptStream.write(script);
      }
      catch (IOException e)
      {
         log.error(e);
      }

      VirtualServerSocket serverSocket = null;

      try
      {
         serverSocket = new VirtualServerSocket();
         log.info("binding to: " + ++nextBindPort);
         doBind(serverSocket, null, new InetSocketAddress(clientServerSocketHost, nextBindPort));
         log.info("connecting server socket to port: " + ++nextConnectPort);
         InetSocketAddress address1 = new InetSocketAddress(basicBehaviorServerHost, nextConnectPort);
         is = testSocket.getInputStream();
         is.read();
         serverSocket.connect(address1);
         log.info("server socket is connected");
         os = testSocket.getOutputStream();
         os.write(5);
         is.read();
         int port = serverSocket.getRemotePort();
         log.info("new virtual socket connecting to: " + port);
         Socket virtualSocket1 = new VirtualSocket(clientServerSocketHost, port);
         log.info("first virtual socket connected");
         InputStream is1 = virtualSocket1.getInputStream();
         OutputStream os1 = virtualSocket1.getOutputStream();
         log.info("server socket accepting on port: " + serverSocket.getLocalPort());
         Socket virtualSocket2 = serverSocket.accept();
         log.info("second virtual socket created");
         InputStream is2 = virtualSocket2.getInputStream();
         OutputStream os2 = virtualSocket2.getOutputStream();
         os1.write(9);
         assertTrue(is1.read() == 9);
         os2.write(11);
         assertTrue(is2.read() == 11);
         virtualSocket1.close();
         virtualSocket2.close();
         serverSocket.close();
      }
      catch (Exception e)
      {
         log.error(e);
         e.printStackTrace();
         fail();
      }
      
      log.info("testVSS_to_VSS() PASSES");
   }


   class AcceptInterruptThread extends Thread
   {
      private ServerSocket ss;
      private boolean success = false;
      public AcceptInterruptThread(ServerSocket ss) {this.ss = ss;}
      public boolean getSuccess() {return success;}

      public void run()
      {
         try
         {
            log.info("now accepting on port: " + ss.getLocalPort());
            ss.accept();
         }
         catch (Exception e)
         {
            if (e instanceof SocketException && e.getMessage().equals("Socket closed"))
               success = true;
         }
      }
   }


   public void testAcceptInterrupt()
   {
      try
      {
         byte[] script = new byte[]
         {
               ACCEPT_SERVER_SOCKET,
               WRITE,
               READ,
               CLOSE_TEST_SOCKET
		 };

         scriptStream.write(script.length);
         scriptStream.write(script);
      }
      catch (IOException e)
      {
         log.error(e);
      }

      try
      {
         VirtualServerSocket vss = new VirtualServerSocket();
         log.info("binding to: " + ++nextBindPort);
         BasicServerSocketBehaviorClient.doBind(vss, null, new InetSocketAddress(clientServerSocketHost, nextBindPort));
         log.info("connecting server socket to port: " + ++nextConnectPort);
         InetSocketAddress address1 = new InetSocketAddress(basicBehaviorServerHost, nextConnectPort);
         is = testSocket.getInputStream();
         is.read();
         vss.connect(address1);

         AcceptInterruptThread t1 = new AcceptInterruptThread(vss);
         // Now that accept() is synchronized, only one thread can be in accept().
//         AcceptInterruptThread t2 = new AcceptInterruptThread(vss);
         t1.start();
//         t2.start();
         Thread.sleep(3000);
         vss.close();
         os = testSocket.getOutputStream();
         os.write(7);
         t1.join();
//         t2.join();
         assertTrue(t1.getSuccess());
//         assertTrue(t2.getSuccess());
      }
      catch (Exception e)
      {
         log.error(e);
         e.printStackTrace();
         fail();
      }
      
      log.info("testAcceptInterrupt() PASSES");
   }


/**
 *  FIXME Comment this
 *
 * @param serverSocket
 * @param localPort
 */
   protected boolean doOneServerSocketTest(final ServerSocket serverSocket, int localPort)
   {
      log.info("entering doOneServerSocketTest");
      boolean success = true;
      InetAddress clientServerSocket_Address = null;
      InetSocketAddress clientServerSocket_SocketAddress = null;

      try
      {
         clientServerSocket_Address = InetAddress.getByName(clientServerSocketHost);
         clientServerSocket_SocketAddress = new InetSocketAddress(clientServerSocket_Address, localPort);

      }
      catch (UnknownHostException e)
      {
         log.error(e);
         fail();
      }

      assertTrue(serverSocket.isBound() == true);
      assertTrue(serverSocket.isClosed() == false);
      assertTrue(serverSocket.getInetAddress().equals(clientServerSocket_Address));
      assertTrue(serverSocket.getLocalSocketAddress().equals(clientServerSocket_SocketAddress));
      assertTrue(serverSocket.getLocalPort() == localPort);

      try
      {
         class StartSocketThread extends Thread
         {
            public VirtualSocket clientSocket;

            public void run()
            {
               try
               {
                  clientSocket = (VirtualSocket) serverSocket.accept();
               }
               catch (Exception e)
               {
                  log.error(e);
                  e.printStackTrace();
               }
            }
         }

         StartSocketThread startSocketThread = new StartSocketThread();
         startSocketThread.start();
         os = testSocket.getOutputStream();
         os.write(3); // server is waiting to read, after which it will connect

         VirtualSocket clientSocket = startSocketThread.clientSocket;

         while (clientSocket == null)
         {
            try
            {
               Thread.sleep(500);
            }
            catch (Exception e)
            {
               log.error(e);
            }

            clientSocket = startSocketThread.clientSocket;
         }

         log.info("VirtualServerSocket executed accept()");
         InputStream is_client = clientSocket.getInputStream();
         OutputStream os_client = clientSocket.getOutputStream();
         int b = is_client.read();
         os_client.write(b);
         clientSocket.close();
         serverSocket.close();
      }
      catch (IOException e)
      {
         log.error(e);
         fail();
      }

      log.debug("leaving doOneServerSocketTest");
      return success;
   }


   protected static VirtualServerSocket doBind(VirtualServerSocket serverSocket,
                                                         InetSocketAddress remoteAddress,
                                                         InetSocketAddress localAddress)
   {
      log.info("entering doBind()");
      log.info("serverSocket = " + serverSocket);
      log.info("remote address: " + remoteAddress);
      log.info("local address: " + localAddress);
      VirtualServerSocket newServerSocket = null;

      for (int i = 0; i < 5; i++)
      {
         try
         {
            if (serverSocket == null)
            {
               if (remoteAddress == null)
                  newServerSocket = new VirtualServerSocket(localAddress.getPort());
               else
                  newServerSocket = new VirtualServerSocket(remoteAddress, localAddress, 0, null);
            }
            else
            {
               // ServerSockets created in testServerSocketFactory() may come in already bound.
               if (!serverSocket.isBound())
                  serverSocket.bind(localAddress);

               if (!serverSocket.isConnected() && remoteAddress != null)
                  serverSocket.connect(remoteAddress);
            }

            break;
         }
         catch (BindException e)
         {
            log.info("bind attempt failed: try again: " + e);

            if (newServerSocket != null)
            {
               try
               {
                  newServerSocket.close();
                  newServerSocket = null;
               }
               catch (IOException ignored)
               {
                  log.error(e);
               }
            }

            try
            {
               Thread.sleep(2000);
            }
            catch (InterruptedException ignored) {}
         }
         catch (SocketException e)
         {
            String message = e.getMessage();

            if (message.equals("Already bound") || message.equals("Cannot assign requested address"))
            {
               log.info("bind attempt failed: try again");
               log.info(e);

               if (newServerSocket != null)
               {
                  try
                  {
                     log.info("calling newServerSocket.close()");
                     newServerSocket.close();
                     newServerSocket = null;
                  }
                  catch (IOException ignored)
                  {
                     log.error(e);
                  }
               }

               try
               {
                  Thread.sleep(2000);
               }
               catch (InterruptedException ignored) {}
            }
            else
            {
               log.error(e);
               e.printStackTrace();
               fail();
            }
         }
         catch (Exception e)
         {
            log.error(e);
            e.printStackTrace();
            fail();
         }
      }

      if (newServerSocket != null)
         serverSocket = newServerSocket;

      if (serverSocket == null || !serverSocket.isBound())
         fail();

      return serverSocket;
   }
}

