@Test
 public void testGoodNetMultiplex() throws Exception {
   final int port = SocketUtils.findAvailableServerSocket();
   final CountDownLatch latch = new CountDownLatch(1);
   final AtomicBoolean done = new AtomicBoolean();
   Executors.newSingleThreadExecutor()
       .execute(
           new Runnable() {
             public void run() {
               try {
                 ServerSocket server =
                     ServerSocketFactory.getDefault().createServerSocket(port, 10);
                 latch.countDown();
                 int i = 0;
                 Socket socket = server.accept();
                 while (true) {
                   ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
                   ois.readObject();
                   ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
                   oos.writeObject("Reply" + (i++));
                 }
               } catch (Exception e) {
                 if (!done.get()) {
                   e.printStackTrace();
                 }
               }
             }
           });
   AbstractConnectionFactory ccf = new TcpNetClientConnectionFactory("localhost", port);
   ccf.setSerializer(new DefaultSerializer());
   ccf.setDeserializer(new DefaultDeserializer());
   ccf.setSoTimeout(10000);
   ccf.setSingleUse(false);
   ccf.start();
   assertTrue(latch.await(10000, TimeUnit.MILLISECONDS));
   TcpOutboundGateway gateway = new TcpOutboundGateway();
   gateway.setConnectionFactory(ccf);
   QueueChannel replyChannel = new QueueChannel();
   gateway.setRequiresReply(true);
   gateway.setOutputChannel(replyChannel);
   for (int i = 100; i < 110; i++) {
     gateway.handleMessage(MessageBuilder.withPayload("Test" + i).build());
   }
   Set<String> replies = new HashSet<String>();
   for (int i = 100; i < 110; i++) {
     Message<?> m = replyChannel.receive(10000);
     assertNotNull(m);
     replies.add((String) m.getPayload());
   }
   for (int i = 0; i < 10; i++) {
     assertTrue(replies.remove("Reply" + i));
   }
   done.set(true);
   gateway.stop();
 }
 @Test
 public void testGoodNetSingle() throws Exception {
   final int port = SocketUtils.findAvailableServerSocket();
   AbstractConnectionFactory ccf = new TcpNetClientConnectionFactory("localhost", port);
   final CountDownLatch latch = new CountDownLatch(1);
   final AtomicBoolean done = new AtomicBoolean();
   Executors.newSingleThreadExecutor()
       .execute(
           new Runnable() {
             public void run() {
               try {
                 ServerSocket server =
                     ServerSocketFactory.getDefault().createServerSocket(port, 100);
                 latch.countDown();
                 List<Socket> sockets = new ArrayList<Socket>();
                 int i = 0;
                 while (true) {
                   Socket socket = server.accept();
                   ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
                   ois.readObject();
                   ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
                   oos.writeObject("Reply" + (i++));
                   sockets.add(socket);
                 }
               } catch (Exception e) {
                 if (!done.get()) {
                   e.printStackTrace();
                 }
               }
             }
           });
   ccf.setSerializer(new DefaultSerializer());
   ccf.setDeserializer(new DefaultDeserializer());
   ccf.setSoTimeout(10000);
   ccf.setSingleUse(true);
   ccf.start();
   assertTrue(latch.await(10000, TimeUnit.MILLISECONDS));
   TcpOutboundGateway gateway = new TcpOutboundGateway();
   gateway.setConnectionFactory(ccf);
   QueueChannel replyChannel = new QueueChannel();
   gateway.setRequiresReply(true);
   gateway.setOutputChannel(replyChannel);
   // check the default remote timeout
   assertEquals(
       Long.valueOf(10000), TestUtils.getPropertyValue(gateway, "remoteTimeout", Long.class));
   gateway.setSendTimeout(123);
   // ensure this also changed the remote timeout
   assertEquals(
       Long.valueOf(123), TestUtils.getPropertyValue(gateway, "remoteTimeout", Long.class));
   gateway.setRemoteTimeout(60000);
   gateway.setSendTimeout(61000);
   // ensure this did NOT change the remote timeout
   assertEquals(
       Long.valueOf(60000), TestUtils.getPropertyValue(gateway, "remoteTimeout", Long.class));
   gateway.setRequestTimeout(60000);
   for (int i = 100; i < 200; i++) {
     gateway.handleMessage(MessageBuilder.withPayload("Test" + i).build());
   }
   Set<String> replies = new HashSet<String>();
   for (int i = 100; i < 200; i++) {
     Message<?> m = replyChannel.receive(10000);
     assertNotNull(m);
     replies.add((String) m.getPayload());
   }
   for (int i = 0; i < 100; i++) {
     assertTrue(replies.remove("Reply" + i));
   }
 }
  private void testGWPropagatesSocketCloseGuts(final int port, AbstractClientConnectionFactory ccf)
      throws Exception {
    final CountDownLatch latch = new CountDownLatch(1);
    final AtomicBoolean done = new AtomicBoolean();
    final AtomicReference<String> lastReceived = new AtomicReference<String>();
    final CountDownLatch serverLatch = new CountDownLatch(1);

    Executors.newSingleThreadExecutor()
        .execute(
            new Runnable() {

              public void run() {
                try {
                  ServerSocket server = ServerSocketFactory.getDefault().createServerSocket(port);
                  latch.countDown();
                  int i = 0;
                  while (!done.get()) {
                    Socket socket = server.accept();
                    i++;
                    while (!socket.isClosed()) {
                      try {
                        ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
                        String request = (String) ois.readObject();
                        logger.debug("Read " + request + " closing socket");
                        socket.close();
                        lastReceived.set(request);
                        serverLatch.countDown();
                      } catch (IOException e) {
                        socket.close();
                      }
                    }
                  }
                } catch (Exception e) {
                  if (!done.get()) {
                    e.printStackTrace();
                  }
                }
              }
            });
    assertTrue(latch.await(10000, TimeUnit.MILLISECONDS));
    final TcpOutboundGateway gateway = new TcpOutboundGateway();
    gateway.setConnectionFactory(ccf);
    gateway.setRequestTimeout(Integer.MAX_VALUE);
    QueueChannel replyChannel = new QueueChannel();
    gateway.setRequiresReply(true);
    gateway.setOutputChannel(replyChannel);
    gateway.setRemoteTimeout(5000);
    gateway.afterPropertiesSet();
    gateway.start();
    try {
      gateway.handleMessage(MessageBuilder.withPayload("Test").build());
      fail("expected failure");
    } catch (Exception e) {
      assertTrue(e.getCause() instanceof EOFException);
    }
    assertEquals(0, TestUtils.getPropertyValue(gateway, "pendingReplies", Map.class).size());
    Message<?> reply = replyChannel.receive(0);
    assertNull(reply);
    done.set(true);
    ccf.getConnection();
  }
  @Test
  public void testFailoverCached() throws Exception {
    final int port = SocketUtils.findAvailableServerSocket();
    final CountDownLatch latch = new CountDownLatch(1);
    final AtomicBoolean done = new AtomicBoolean();
    final CountDownLatch serverLatch = new CountDownLatch(1);

    Executors.newSingleThreadExecutor()
        .execute(
            new Runnable() {

              public void run() {
                try {
                  ServerSocket server = ServerSocketFactory.getDefault().createServerSocket(port);
                  latch.countDown();
                  while (!done.get()) {
                    Socket socket = server.accept();
                    while (!socket.isClosed()) {
                      try {
                        ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
                        String request = (String) ois.readObject();
                        logger.debug("Read " + request);
                        ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
                        oos.writeObject("bar");
                        logger.debug("Replied to " + request);
                        serverLatch.countDown();
                      } catch (IOException e) {
                        logger.debug("error on write " + e.getClass().getSimpleName());
                        socket.close();
                      }
                    }
                  }
                } catch (Exception e) {
                  if (!done.get()) {
                    e.printStackTrace();
                  }
                }
              }
            });
    assertTrue(latch.await(10000, TimeUnit.MILLISECONDS));

    // Cache
    AbstractClientConnectionFactory factory1 = mock(AbstractClientConnectionFactory.class);
    TcpConnectionSupport mockConn1 = makeMockConnection();
    when(factory1.getConnection()).thenReturn(mockConn1);
    doThrow(new IOException("fail")).when(mockConn1).send(Mockito.any(Message.class));
    CachingClientConnectionFactory cachingFactory1 =
        new CachingClientConnectionFactory(factory1, 1);

    AbstractClientConnectionFactory factory2 = new TcpNetClientConnectionFactory("localhost", port);
    factory2.setSerializer(new DefaultSerializer());
    factory2.setDeserializer(new DefaultDeserializer());
    factory2.setSoTimeout(10000);
    factory2.setSingleUse(false);
    CachingClientConnectionFactory cachingFactory2 =
        new CachingClientConnectionFactory(factory2, 1);

    // Failover
    List<AbstractClientConnectionFactory> factories =
        new ArrayList<AbstractClientConnectionFactory>();
    factories.add(cachingFactory1);
    factories.add(cachingFactory2);
    FailoverClientConnectionFactory failoverFactory =
        new FailoverClientConnectionFactory(factories);
    failoverFactory.start();

    TcpOutboundGateway gateway = new TcpOutboundGateway();
    gateway.setConnectionFactory(failoverFactory);
    PollableChannel outputChannel = new QueueChannel();
    gateway.setOutputChannel(outputChannel);
    gateway.afterPropertiesSet();
    gateway.start();

    GenericMessage<String> message = new GenericMessage<String>("foo");
    gateway.handleMessage(message);
    Message<?> reply = outputChannel.receive(0);
    assertNotNull(reply);
    assertEquals("bar", reply.getPayload());
    done.set(true);
    gateway.stop();
    verify(mockConn1).send(Mockito.any(Message.class));
  }
  /**
   * Sends 2 concurrent messages on a shared connection. The GW single threads these requests. The
   * first will timeout; the second should receive its own response, not that for the first.
   *
   * @throws Exception
   */
  private void testGoodNetGWTimeoutGuts(final int port, AbstractConnectionFactory ccf)
      throws InterruptedException {
    final CountDownLatch latch = new CountDownLatch(1);
    final AtomicBoolean done = new AtomicBoolean();
    /*
     * The payload of the last message received by the remote side;
     * used to verify the correct response is received.
     */
    final AtomicReference<String> lastReceived = new AtomicReference<String>();
    final CountDownLatch serverLatch = new CountDownLatch(2);

    Executors.newSingleThreadExecutor()
        .execute(
            new Runnable() {

              public void run() {
                try {
                  ServerSocket server = ServerSocketFactory.getDefault().createServerSocket(port);
                  latch.countDown();
                  int i = 0;
                  while (!done.get()) {
                    Socket socket = server.accept();
                    i++;
                    while (!socket.isClosed()) {
                      try {
                        ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
                        String request = (String) ois.readObject();
                        logger.debug("Read " + request);
                        ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
                        if (i < 2) {
                          Thread.sleep(1000);
                        }
                        oos.writeObject(request.replace("Test", "Reply"));
                        logger.debug("Replied to " + request);
                        lastReceived.set(request);
                        serverLatch.countDown();
                      } catch (IOException e) {
                        logger.debug("error on write " + e.getClass().getSimpleName());
                        socket.close();
                      }
                    }
                  }
                } catch (Exception e) {
                  if (!done.get()) {
                    e.printStackTrace();
                  }
                }
              }
            });
    assertTrue(latch.await(10000, TimeUnit.MILLISECONDS));
    final TcpOutboundGateway gateway = new TcpOutboundGateway();
    gateway.setConnectionFactory(ccf);
    gateway.setRequestTimeout(Integer.MAX_VALUE);
    QueueChannel replyChannel = new QueueChannel();
    gateway.setRequiresReply(true);
    gateway.setOutputChannel(replyChannel);
    gateway.setRemoteTimeout(500);
    @SuppressWarnings("unchecked")
    Future<Integer>[] results = new Future[2];
    for (int i = 0; i < 2; i++) {
      final int j = i;
      results[j] =
          (Executors.newSingleThreadExecutor()
              .submit(
                  new Callable<Integer>() {
                    public Integer call() throws Exception {
                      // increase the timeout after the first send
                      if (j > 0) {
                        gateway.setRemoteTimeout(5000);
                      }
                      gateway.handleMessage(MessageBuilder.withPayload("Test" + j).build());
                      return j;
                    }
                  }));
      Thread.sleep(50);
    }
    // wait until the server side has processed both requests
    assertTrue(serverLatch.await(10, TimeUnit.SECONDS));
    List<String> replies = new ArrayList<String>();
    int timeouts = 0;
    for (int i = 0; i < 2; i++) {
      try {
        int result = results[i].get();
        String reply = (String) replyChannel.receive(1000).getPayload();
        logger.debug(i + " got " + result + " " + reply);
        replies.add(reply);
      } catch (ExecutionException e) {
        if (timeouts >= 2) {
          fail("Unexpected " + e.getMessage());
        } else {
          assertNotNull(e.getCause());
          assertTrue(e.getCause() instanceof MessageTimeoutException);
        }
        timeouts++;
        continue;
      }
    }
    assertEquals("Expected exactly one ExecutionException", 1, timeouts);
    assertEquals(1, replies.size());
    assertEquals(lastReceived.get().replace("Test", "Reply"), replies.get(0));
    done.set(true);
    assertEquals(0, TestUtils.getPropertyValue(gateway, "pendingReplies", Map.class).size());
    gateway.stop();
  }
 @Test
 public void testGoodNetTimeout() throws Exception {
   final int port = SocketUtils.findAvailableServerSocket();
   final CountDownLatch latch = new CountDownLatch(1);
   final AtomicBoolean done = new AtomicBoolean();
   Executors.newSingleThreadExecutor()
       .execute(
           new Runnable() {
             public void run() {
               try {
                 ServerSocket server = ServerSocketFactory.getDefault().createServerSocket(port);
                 latch.countDown();
                 int i = 0;
                 Socket socket = server.accept();
                 while (true) {
                   ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
                   ois.readObject();
                   ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
                   Thread.sleep(1000);
                   oos.writeObject("Reply" + (i++));
                 }
               } catch (Exception e) {
                 if (!done.get()) {
                   e.printStackTrace();
                 }
               }
             }
           });
   AbstractConnectionFactory ccf = new TcpNetClientConnectionFactory("localhost", port);
   ccf.setSerializer(new DefaultSerializer());
   ccf.setDeserializer(new DefaultDeserializer());
   ccf.setSoTimeout(10000);
   ccf.setSingleUse(false);
   ccf.start();
   assertTrue(latch.await(10000, TimeUnit.MILLISECONDS));
   final TcpOutboundGateway gateway = new TcpOutboundGateway();
   gateway.setConnectionFactory(ccf);
   gateway.setRequestTimeout(1);
   QueueChannel replyChannel = new QueueChannel();
   gateway.setRequiresReply(true);
   gateway.setOutputChannel(replyChannel);
   @SuppressWarnings("unchecked")
   Future<Integer>[] results = new Future[2];
   for (int i = 0; i < 2; i++) {
     final int j = i;
     results[j] =
         (Executors.newSingleThreadExecutor()
             .submit(
                 new Callable<Integer>() {
                   public Integer call() throws Exception {
                     gateway.handleMessage(MessageBuilder.withPayload("Test" + j).build());
                     return 0;
                   }
                 }));
   }
   Set<String> replies = new HashSet<String>();
   int timeouts = 0;
   for (int i = 0; i < 2; i++) {
     try {
       results[i].get();
     } catch (ExecutionException e) {
       if (timeouts > 0) {
         fail("Unexpected " + e.getMessage());
       } else {
         assertNotNull(e.getCause());
         assertTrue(e.getCause() instanceof MessageTimeoutException);
       }
       timeouts++;
       continue;
     }
     Message<?> m = replyChannel.receive(10000);
     assertNotNull(m);
     replies.add((String) m.getPayload());
   }
   if (timeouts < 1) {
     fail("Expected ExecutionException");
   }
   for (int i = 0; i < 1; i++) {
     assertTrue(replies.remove("Reply" + i));
   }
   done.set(true);
   gateway.stop();
 }