@Test
  public void testVersion_from0to0() throws Exception {
    serviceA.registerHandler(
        "/version",
        new BaseTransportRequestHandler<Version0Request>() {
          @Override
          public Version0Request newInstance() {
            return new Version0Request();
          }

          @Override
          public void messageReceived(Version0Request request, TransportChannel channel)
              throws Exception {
            assertThat(request.value1, equalTo(1));
            Version0Response response = new Version0Response();
            response.value1 = 1;
            channel.sendResponse(response);
          }

          @Override
          public String executor() {
            return ThreadPool.Names.SAME;
          }
        });

    Version0Request version0Request = new Version0Request();
    version0Request.value1 = 1;
    Version0Response version0Response =
        serviceA
            .submitRequest(
                nodeA,
                "/version",
                version0Request,
                new BaseTransportResponseHandler<Version0Response>() {
                  @Override
                  public Version0Response newInstance() {
                    return new Version0Response();
                  }

                  @Override
                  public void handleResponse(Version0Response response) {
                    assertThat(response.value1, equalTo(1));
                  }

                  @Override
                  public void handleException(TransportException exp) {
                    exp.printStackTrace();
                    fail();
                  }

                  @Override
                  public String executor() {
                    return ThreadPool.Names.SAME;
                  }
                })
            .txGet();

    assertThat(version0Response.value1, equalTo(1));
  }
  @Test
  public void testErrorMessage() {
    serviceA.registerHandler(
        "sayHelloException",
        new BaseTransportRequestHandler<StringMessageRequest>() {
          @Override
          public StringMessageRequest newInstance() {
            return new StringMessageRequest();
          }

          @Override
          public String executor() {
            return ThreadPool.Names.GENERIC;
          }

          @Override
          public void messageReceived(StringMessageRequest request, TransportChannel channel)
              throws Exception {
            assertThat("moshe", equalTo(request.message));
            throw new RuntimeException("bad message !!!");
          }
        });

    TransportFuture<StringMessageResponse> res =
        serviceB.submitRequest(
            nodeA,
            "sayHelloException",
            new StringMessageRequest("moshe"),
            new BaseTransportResponseHandler<StringMessageResponse>() {
              @Override
              public StringMessageResponse newInstance() {
                return new StringMessageResponse();
              }

              @Override
              public String executor() {
                return ThreadPool.Names.GENERIC;
              }

              @Override
              public void handleResponse(StringMessageResponse response) {
                assertThat("got response instead of exception", false, equalTo(true));
              }

              @Override
              public void handleException(TransportException exp) {
                assertThat("bad message !!!", equalTo(exp.getCause().getMessage()));
              }
            });

    try {
      res.txGet();
      assertThat("exception should be thrown", false, equalTo(true));
    } catch (Exception e) {
      assertThat("bad message !!!", equalTo(e.getCause().getMessage()));
    }

    serviceA.removeHandler("sayHelloException");
  }
  @Test
  public void testNotifyOnShutdown() throws Exception {
    final CountDownLatch latch2 = new CountDownLatch(1);

    serviceA.registerHandler(
        "foobar",
        new BaseTransportRequestHandler<StringMessageRequest>() {
          @Override
          public StringMessageRequest newInstance() {
            return new StringMessageRequest();
          }

          @Override
          public String executor() {
            return ThreadPool.Names.GENERIC;
          }

          @Override
          public void messageReceived(StringMessageRequest request, TransportChannel channel) {

            try {
              latch2.await();
              logger.info("Stop ServiceB now");
              serviceB.stop();
            } catch (Exception e) {
              fail(e.getMessage());
            }
          }
        });
    TransportFuture<TransportResponse.Empty> foobar =
        serviceB.submitRequest(
            nodeA,
            "foobar",
            new StringMessageRequest(""),
            options(),
            EmptyTransportResponseHandler.INSTANCE_SAME);
    latch2.countDown();
    try {
      foobar.txGet();
      fail("TransportException expected");
    } catch (TransportException ex) {

    }
    serviceA.removeHandler("sayHelloTimeoutDelayedResponse");
  }
  @Test
  public void testTimeoutSendExceptionWithDelayedResponse() throws Exception {
    serviceA.registerHandler(
        "sayHelloTimeoutDelayedResponse",
        new BaseTransportRequestHandler<StringMessageRequest>() {
          @Override
          public StringMessageRequest newInstance() {
            return new StringMessageRequest();
          }

          @Override
          public String executor() {
            return ThreadPool.Names.GENERIC;
          }

          @Override
          public void messageReceived(StringMessageRequest request, TransportChannel channel) {
            TimeValue sleep = TimeValue.parseTimeValue(request.message, null);
            try {
              Thread.sleep(sleep.millis());
            } catch (InterruptedException e) {
              // ignore
            }
            try {
              channel.sendResponse(new StringMessageResponse("hello " + request.message));
            } catch (IOException e) {
              e.printStackTrace();
              assertThat(e.getMessage(), false, equalTo(true));
            }
          }
        });
    final CountDownLatch latch = new CountDownLatch(1);
    TransportFuture<StringMessageResponse> res =
        serviceB.submitRequest(
            nodeA,
            "sayHelloTimeoutDelayedResponse",
            new StringMessageRequest("300ms"),
            options().withTimeout(100),
            new BaseTransportResponseHandler<StringMessageResponse>() {
              @Override
              public StringMessageResponse newInstance() {
                return new StringMessageResponse();
              }

              @Override
              public String executor() {
                return ThreadPool.Names.GENERIC;
              }

              @Override
              public void handleResponse(StringMessageResponse response) {
                latch.countDown();
                assertThat("got response instead of exception", false, equalTo(true));
              }

              @Override
              public void handleException(TransportException exp) {
                latch.countDown();
                assertThat(exp, instanceOf(ReceiveTimeoutTransportException.class));
              }
            });

    try {
      StringMessageResponse message = res.txGet();
      assertThat("exception should be thrown", false, equalTo(true));
    } catch (Exception e) {
      assertThat(e, instanceOf(ReceiveTimeoutTransportException.class));
    }
    latch.await();

    for (int i = 0; i < 10; i++) {
      final int counter = i;
      // now, try and send another request, this times, with a short timeout
      res =
          serviceB.submitRequest(
              nodeA,
              "sayHelloTimeoutDelayedResponse",
              new StringMessageRequest(counter + "ms"),
              options().withTimeout(3000),
              new BaseTransportResponseHandler<StringMessageResponse>() {
                @Override
                public StringMessageResponse newInstance() {
                  return new StringMessageResponse();
                }

                @Override
                public String executor() {
                  return ThreadPool.Names.GENERIC;
                }

                @Override
                public void handleResponse(StringMessageResponse response) {
                  assertThat("hello " + counter + "ms", equalTo(response.message));
                }

                @Override
                public void handleException(TransportException exp) {
                  exp.printStackTrace();
                  assertThat(
                      "got exception instead of a response for "
                          + counter
                          + ": "
                          + exp.getDetailedMessage(),
                      false,
                      equalTo(true));
                }
              });

      StringMessageResponse message = res.txGet();
      assertThat(message.message, equalTo("hello " + counter + "ms"));
    }

    serviceA.removeHandler("sayHelloTimeoutDelayedResponse");
  }
  @Test
  public void testTimeoutSendExceptionWithNeverSendingBackResponse() throws Exception {
    serviceA.registerHandler(
        "sayHelloTimeoutNoResponse",
        new BaseTransportRequestHandler<StringMessageRequest>() {
          @Override
          public StringMessageRequest newInstance() {
            return new StringMessageRequest();
          }

          @Override
          public String executor() {
            return ThreadPool.Names.GENERIC;
          }

          @Override
          public void messageReceived(StringMessageRequest request, TransportChannel channel) {
            assertThat("moshe", equalTo(request.message));
            // don't send back a response
            //                try {
            //                    channel.sendResponse(new StringMessage("hello " +
            // request.message));
            //                } catch (IOException e) {
            //                    e.printStackTrace();
            //                    assertThat(e.getMessage(), false, equalTo(true));
            //                }
          }
        });

    TransportFuture<StringMessageResponse> res =
        serviceB.submitRequest(
            nodeA,
            "sayHelloTimeoutNoResponse",
            new StringMessageRequest("moshe"),
            options().withTimeout(100),
            new BaseTransportResponseHandler<StringMessageResponse>() {
              @Override
              public StringMessageResponse newInstance() {
                return new StringMessageResponse();
              }

              @Override
              public String executor() {
                return ThreadPool.Names.GENERIC;
              }

              @Override
              public void handleResponse(StringMessageResponse response) {
                assertThat("got response instead of exception", false, equalTo(true));
              }

              @Override
              public void handleException(TransportException exp) {
                assertThat(exp, instanceOf(ReceiveTimeoutTransportException.class));
              }
            });

    try {
      StringMessageResponse message = res.txGet();
      assertThat("exception should be thrown", false, equalTo(true));
    } catch (Exception e) {
      assertThat(e, instanceOf(ReceiveTimeoutTransportException.class));
    }

    serviceA.removeHandler("sayHelloTimeoutNoResponse");
  }
  @Test
  public void testHelloWorldCompressed() {
    serviceA.registerHandler(
        "sayHello",
        new BaseTransportRequestHandler<StringMessageRequest>() {
          @Override
          public StringMessageRequest newInstance() {
            return new StringMessageRequest();
          }

          @Override
          public String executor() {
            return ThreadPool.Names.GENERIC;
          }

          @Override
          public void messageReceived(StringMessageRequest request, TransportChannel channel) {
            assertThat("moshe", equalTo(request.message));
            try {
              channel.sendResponse(
                  new StringMessageResponse("hello " + request.message),
                  TransportResponseOptions.options().withCompress(true));
            } catch (IOException e) {
              e.printStackTrace();
              assertThat(e.getMessage(), false, equalTo(true));
            }
          }
        });

    TransportFuture<StringMessageResponse> res =
        serviceB.submitRequest(
            nodeA,
            "sayHello",
            new StringMessageRequest("moshe"),
            TransportRequestOptions.options().withCompress(true),
            new BaseTransportResponseHandler<StringMessageResponse>() {
              @Override
              public StringMessageResponse newInstance() {
                return new StringMessageResponse();
              }

              @Override
              public String executor() {
                return ThreadPool.Names.GENERIC;
              }

              @Override
              public void handleResponse(StringMessageResponse response) {
                assertThat("hello moshe", equalTo(response.message));
              }

              @Override
              public void handleException(TransportException exp) {
                exp.printStackTrace();
                assertThat(
                    "got exception instead of a response: " + exp.getMessage(),
                    false,
                    equalTo(true));
              }
            });

    try {
      StringMessageResponse message = res.get();
      assertThat("hello moshe", equalTo(message.message));
    } catch (Exception e) {
      assertThat(e.getMessage(), false, equalTo(true));
    }

    serviceA.removeHandler("sayHello");
  }
  @Test
  public void testVoidMessageCompressed() {
    serviceA.registerHandler(
        "sayHello",
        new BaseTransportRequestHandler<TransportRequest.Empty>() {
          @Override
          public TransportRequest.Empty newInstance() {
            return TransportRequest.Empty.INSTANCE;
          }

          @Override
          public String executor() {
            return ThreadPool.Names.GENERIC;
          }

          @Override
          public void messageReceived(TransportRequest.Empty request, TransportChannel channel) {
            try {
              channel.sendResponse(
                  TransportResponse.Empty.INSTANCE,
                  TransportResponseOptions.options().withCompress(true));
            } catch (IOException e) {
              e.printStackTrace();
              assertThat(e.getMessage(), false, equalTo(true));
            }
          }
        });

    TransportFuture<TransportResponse.Empty> res =
        serviceB.submitRequest(
            nodeA,
            "sayHello",
            TransportRequest.Empty.INSTANCE,
            TransportRequestOptions.options().withCompress(true),
            new BaseTransportResponseHandler<TransportResponse.Empty>() {
              @Override
              public TransportResponse.Empty newInstance() {
                return TransportResponse.Empty.INSTANCE;
              }

              @Override
              public String executor() {
                return ThreadPool.Names.GENERIC;
              }

              @Override
              public void handleResponse(TransportResponse.Empty response) {}

              @Override
              public void handleException(TransportException exp) {
                exp.printStackTrace();
                assertThat(
                    "got exception instead of a response: " + exp.getMessage(),
                    false,
                    equalTo(true));
              }
            });

    try {
      TransportResponse.Empty message = res.get();
      assertThat(message, notNullValue());
    } catch (Exception e) {
      assertThat(e.getMessage(), false, equalTo(true));
    }

    serviceA.removeHandler("sayHello");
  }
  @Test
  public void testHostOnMessages() throws InterruptedException {
    final CountDownLatch latch = new CountDownLatch(2);
    final AtomicReference<TransportAddress> addressA = new AtomicReference<>();
    final AtomicReference<TransportAddress> addressB = new AtomicReference<>();
    serviceB.registerHandler(
        "action1",
        new TransportRequestHandler<TestRequest>() {
          @Override
          public TestRequest newInstance() {
            return new TestRequest();
          }

          @Override
          public void messageReceived(TestRequest request, TransportChannel channel)
              throws Exception {
            addressA.set(request.remoteAddress());
            channel.sendResponse(new TestResponse());
            latch.countDown();
          }

          @Override
          public String executor() {
            return ThreadPool.Names.SAME;
          }

          @Override
          public boolean isForceExecution() {
            return false;
          }
        });
    serviceA.sendRequest(
        nodeB,
        "action1",
        new TestRequest(),
        new TransportResponseHandler<TestResponse>() {
          @Override
          public TestResponse newInstance() {
            return new TestResponse();
          }

          @Override
          public void handleResponse(TestResponse response) {
            addressB.set(response.remoteAddress());
            latch.countDown();
          }

          @Override
          public void handleException(TransportException exp) {
            latch.countDown();
          }

          @Override
          public String executor() {
            return ThreadPool.Names.SAME;
          }
        });

    if (!latch.await(10, TimeUnit.SECONDS)) {
      fail("message round trip did not complete within a sensible time frame");
    }

    assertTrue(nodeA.address().sameHost(addressA.get()));
    assertTrue(nodeB.address().sameHost(addressB.get()));
  }
  @Test
  public void testMockUnresponsiveRule() {
    serviceA.registerHandler(
        "sayHello",
        new BaseTransportRequestHandler<StringMessageRequest>() {
          @Override
          public StringMessageRequest newInstance() {
            return new StringMessageRequest();
          }

          @Override
          public String executor() {
            return ThreadPool.Names.GENERIC;
          }

          @Override
          public void messageReceived(StringMessageRequest request, TransportChannel channel)
              throws Exception {
            assertThat("moshe", equalTo(request.message));
            throw new RuntimeException("bad message !!!");
          }
        });

    serviceB.addUnresponsiveRule(nodeA);

    TransportFuture<StringMessageResponse> res =
        serviceB.submitRequest(
            nodeA,
            "sayHello",
            new StringMessageRequest("moshe"),
            TransportRequestOptions.options().withTimeout(100),
            new BaseTransportResponseHandler<StringMessageResponse>() {
              @Override
              public StringMessageResponse newInstance() {
                return new StringMessageResponse();
              }

              @Override
              public String executor() {
                return ThreadPool.Names.GENERIC;
              }

              @Override
              public void handleResponse(StringMessageResponse response) {
                assertThat("got response instead of exception", false, equalTo(true));
              }

              @Override
              public void handleException(TransportException exp) {
                assertThat(exp, instanceOf(ReceiveTimeoutTransportException.class));
              }
            });

    try {
      res.txGet();
      assertThat("exception should be thrown", false, equalTo(true));
    } catch (Exception e) {
      assertThat(e, instanceOf(ReceiveTimeoutTransportException.class));
    }

    try {
      serviceB.connectToNode(nodeA);
      assertThat("exception should be thrown", false, equalTo(true));
    } catch (ConnectTransportException e) {
      // all is well
    }

    try {
      serviceB.connectToNodeLight(nodeA);
      assertThat("exception should be thrown", false, equalTo(true));
    } catch (ConnectTransportException e) {
      // all is well
    }

    serviceA.removeHandler("sayHello");
  }
  @Test
  public void testMockFailToSendNoConnectRule() {
    serviceA.registerHandler(
        "sayHello",
        new BaseTransportRequestHandler<StringMessageRequest>() {
          @Override
          public StringMessageRequest newInstance() {
            return new StringMessageRequest();
          }

          @Override
          public String executor() {
            return ThreadPool.Names.GENERIC;
          }

          @Override
          public void messageReceived(StringMessageRequest request, TransportChannel channel)
              throws Exception {
            assertThat("moshe", equalTo(request.message));
            throw new RuntimeException("bad message !!!");
          }
        });

    serviceB.addFailToSendNoConnectRule(nodeA);

    TransportFuture<StringMessageResponse> res =
        serviceB.submitRequest(
            nodeA,
            "sayHello",
            new StringMessageRequest("moshe"),
            new BaseTransportResponseHandler<StringMessageResponse>() {
              @Override
              public StringMessageResponse newInstance() {
                return new StringMessageResponse();
              }

              @Override
              public String executor() {
                return ThreadPool.Names.GENERIC;
              }

              @Override
              public void handleResponse(StringMessageResponse response) {
                assertThat("got response instead of exception", false, equalTo(true));
              }

              @Override
              public void handleException(TransportException exp) {
                assertThat(exp.getCause().getMessage(), endsWith("DISCONNECT: simulated"));
              }
            });

    try {
      res.txGet();
      assertThat("exception should be thrown", false, equalTo(true));
    } catch (Exception e) {
      assertThat(e.getCause().getMessage(), endsWith("DISCONNECT: simulated"));
    }

    try {
      serviceB.connectToNode(nodeA);
      assertThat("exception should be thrown", false, equalTo(true));
    } catch (ConnectTransportException e) {
      // all is well
    }

    try {
      serviceB.connectToNodeLight(nodeA);
      assertThat("exception should be thrown", false, equalTo(true));
    } catch (ConnectTransportException e) {
      // all is well
    }

    serviceA.removeHandler("sayHello");
  }