/**
   * Send to server Payload
   *
   * @param payloadSend
   * @throws IOException
   * @see com.reversemind.hypergate.Payload
   */
  public void send(Payload payloadSend) throws IOException {
    this.setPayload(null);

    // clean & start a timer
    if (this.channel != null && this.channel.isConnected()) {
      LOG.info("Connected:" + this.channel.isConnected());

      if (payloadSend != null) {
        LOG.info("Send from HyperGateClient payload:" + payloadSend.toString());
        payloadSend.setClientTimestamp(System.currentTimeMillis());

        // client is occupied
        this.occupied = true;
        // TODO make it #6
        this.channel.write(payloadSend);

        this.shutDownExecutor();
        this.executor = this.getExecutor();
        this.executor.execute(this.createFutureTask());

        return;
      }
    }
    throw new IOException("Channel is closed or even not opened");
  }
  private void handleIncomingConfirmableCoapRequest(ChannelHandlerContext ctx, MessageEvent me) {
    InetSocketAddress remoteEndpoint = (InetSocketAddress) me.getRemoteAddress();
    CoapMessage coapMessage = (CoapMessage) me.getMessage();

    IncomingReliableMessageExchange newMessageExchange =
        new IncomingReliableMessageExchange(remoteEndpoint, coapMessage.getMessageID());

    IncomingMessageExchange oldMessageExchange =
        ongoingMessageExchanges.get(remoteEndpoint, coapMessage.getMessageID());

    // Check if there is an ongoing
    if (oldMessageExchange != null) {

      if (oldMessageExchange instanceof IncomingReliableMessageExchange) {

        // if the old message exchange is reliable and the empty ACK was already sent send another
        // empty ACK
        if (((IncomingReliableMessageExchange) oldMessageExchange).isAcknowledgementSent())
          writeEmptyAcknowledgement(remoteEndpoint, coapMessage.getMessageID());

      }

      // if the old message was unreliable and the duplicate message is confirmable send empty ACK
      else writeEmptyAcknowledgement(remoteEndpoint, coapMessage.getMessageID());

      // As the message is already being processed there is nothing more to do
      return;
    }

    // try to add new reliable message exchange
    boolean added = false;
    synchronized (monitor) {
      Long time = System.currentTimeMillis() + MIN_EMPTY_ACK_DELAY_MILLIS;

      // Add message exchange to set of ongoing exchanges to detect duplicates
      if (!ongoingMessageExchanges.contains(remoteEndpoint, coapMessage.getMessageID())) {
        ongoingMessageExchanges.put(remoteEndpoint, coapMessage.getMessageID(), newMessageExchange);
        added = true;
      }

      // If the scheduling of the empty ACK does not work then it was already scheduled
      if (!emptyAcknowledgementSchedule.put(time, newMessageExchange)) {
        log.error("Could not schedule empty ACK for message: {}", coapMessage);
        ongoingMessageExchanges.remove(remoteEndpoint, coapMessage.getMessageID());
        added = false;
      }
    }

    // everything is fine, so further process message
    if (added) ctx.sendUpstream(me);
  }
    @Override
    public void run() {
      try {
        long now = System.currentTimeMillis();

        synchronized (monitor) {

          // Send due empty acknowledgements
          Iterator<Map.Entry<Long, Collection<IncomingReliableMessageExchange>>>
              dueAcknowledgements =
                  emptyAcknowledgementSchedule.asMap().headMap(now, true).entrySet().iterator();

          while (dueAcknowledgements.hasNext()) {
            Map.Entry<Long, Collection<IncomingReliableMessageExchange>> part =
                dueAcknowledgements.next();

            for (IncomingReliableMessageExchange messageExchange : part.getValue()) {
              if (!messageExchange.isAcknowledgementSent()) {
                InetSocketAddress remoteEndpoint = messageExchange.getRemoteEndpoint();
                int messageID = messageExchange.getMessageID();

                writeEmptyAcknowledgement(remoteEndpoint, messageID);

                messageExchange.setAcknowledgementSent();
              }
            }

            dueAcknowledgements.remove();
          }

          // Retire open NON messages

        }
      } catch (Exception e) {
        log.error("Error in reliability task for incoming message exchanges!", e);
      }
    }
 public void pingReceived() {
   myLastPingTime = System.currentTimeMillis();
 }
  public static void main(String[] args) {
    final int payloadSize = 100;
    int CYCLE_SIZE = 50000;
    final long NUMBER_OF_ITERATIONS = 500000;

    ChannelBuffer message = ChannelBuffers.buffer(100);
    for (int i = 0; i < message.capacity(); i++) {
      message.writeByte((byte) i);
    }

    // Configure the server.
    ServerBootstrap serverBootstrap =
        new ServerBootstrap(
            new NioServerSocketChannelFactory(
                Executors.newCachedThreadPool(), Executors.newCachedThreadPool()));

    // Set up the pipeline factory.
    serverBootstrap.setPipelineFactory(
        new ChannelPipelineFactory() {
          @Override
          public ChannelPipeline getPipeline() throws Exception {
            return Channels.pipeline(new EchoServerHandler());
          }
        });

    // Bind and start to accept incoming connections.
    serverBootstrap.bind(new InetSocketAddress(9000));

    ClientBootstrap clientBootstrap =
        new ClientBootstrap(
            new NioClientSocketChannelFactory(
                Executors.newCachedThreadPool(), Executors.newCachedThreadPool()));

    //        ClientBootstrap clientBootstrap = new ClientBootstrap(
    //                new OioClientSocketChannelFactory(Executors.newCachedThreadPool()));

    // Set up the pipeline factory.
    final EchoClientHandler clientHandler = new EchoClientHandler();
    clientBootstrap.setPipelineFactory(
        new ChannelPipelineFactory() {
          @Override
          public ChannelPipeline getPipeline() throws Exception {
            return Channels.pipeline(clientHandler);
          }
        });

    // Start the connection attempt.
    ChannelFuture future = clientBootstrap.connect(new InetSocketAddress("localhost", 9000));
    future.awaitUninterruptibly();
    Channel clientChannel = future.getChannel();

    System.out.println("Warming up...");
    for (long i = 0; i < 10000; i++) {
      clientHandler.latch = new CountDownLatch(1);
      clientChannel.write(message);
      try {
        clientHandler.latch.await();
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    }
    System.out.println("Warmed up");

    long start = System.currentTimeMillis();
    long cycleStart = System.currentTimeMillis();
    for (long i = 1; i < NUMBER_OF_ITERATIONS; i++) {
      clientHandler.latch = new CountDownLatch(1);
      clientChannel.write(message);
      try {
        clientHandler.latch.await();
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
      if ((i % CYCLE_SIZE) == 0) {
        long cycleEnd = System.currentTimeMillis();
        System.out.println(
            "Ran 50000, TPS " + (CYCLE_SIZE / ((double) (cycleEnd - cycleStart) / 1000)));
        cycleStart = cycleEnd;
      }
    }
    long end = System.currentTimeMillis();
    long seconds = (end - start) / 1000;
    System.out.println(
        "Ran ["
            + NUMBER_OF_ITERATIONS
            + "] iterations, payload ["
            + payloadSize
            + "]: took ["
            + seconds
            + "], TPS: "
            + ((double) NUMBER_OF_ITERATIONS) / seconds);

    clientChannel.close().awaitUninterruptibly();
    clientBootstrap.releaseExternalResources();
    serverBootstrap.releaseExternalResources();
  }