protected AbstractMessage handleMessage(AbstractMessage incomingMessage)
      throws InterruptedException {

    // adds message to the right pipeline

    // BR
    if (incomingMessage instanceof BlockRequestMessage) {

      BlockRequestMessage blockRequestMessage = (BlockRequestMessage) incomingMessage;

      // stats
      this.videoSignaling.getStats().newIncomingRequest(blockRequestMessage);

      this.pipelineBlockRequest.offer(incomingMessage);
    }

    // USM
    else if (incomingMessage instanceof SubscribeMessage
        || incomingMessage instanceof InterestedMessage
        || incomingMessage instanceof DisconnectMessage
            && ((DisconnectMessage) incomingMessage).stopUploading())
      this.pipelineUploadSlotManager.offer(incomingMessage);

    // TUNER
    else if (incomingMessage instanceof PingMessage
        || incomingMessage instanceof HaveMessage
        || incomingMessage instanceof SubscribedMessage
        || incomingMessage instanceof QueuedMessage
        || incomingMessage instanceof GrantedMessage
        || incomingMessage instanceof BlockReplyMessage
        || incomingMessage instanceof PeerSuggestionMessage
        || incomingMessage instanceof DisconnectMessage
            && ((DisconnectMessage) incomingMessage).stopDownloading())
      this.pipelineTuner.offer(incomingMessage);
    else {
      logger.error(
          "Message of type ("
              + incomingMessage.getClass().getCanonicalName()
              + ", "
              + incomingMessage.toString()
              + ") is not recognized");

      // flow control needs to take its MID not to have a gap
      this.pipelineUploadSlotManager.offer(incomingMessage);
      return null;
    }

    AbstractMessage replyMessage = null;
    try {
      replyMessage = this.waitForProcessingGetReply(incomingMessage);
    } catch (Exception e) {
      e.printStackTrace();
      logger.error("Error waiting for message to be processed: " + e.getMessage());
    }

    return replyMessage;
  }
  @Override
  public AbstractMessage getMessage(
      final P2PAddress senderP2pAddress, final byte[] incomingByteArray, final int offset) {

    AbstractMessage incomingMessage = null;
    int countDown = 5;
    while (incomingMessage == null && countDown-- > 0) {
      try {
        incomingMessage =
            this.videoSignaling
                .getMessageFactory()
                .getMessage(senderP2pAddress, incomingByteArray, offset);
      } catch (UnknownHostException e1) {
        logger.error("Message received from unknown host -- dropping");
        e1.printStackTrace();
        return null;
      } catch (SenderNotFoundException e) {
        if (logger.isDebugEnabled())
          logger.debug(
              "sender of message not found. it could be that the message containing the sender is a bit delayed. will wait and retry.");

        try {
          Thread.sleep(50);
        } catch (InterruptedException e1) {
          if (logger.isDebugEnabled()) logger.debug("interrupted");
          break;
        }
      } catch (Exception e) {
        String byteArrayAsString = "[";
        for (byte b : incomingByteArray) byteArrayAsString += b + ",";
        byteArrayAsString += "]";
        logger.error(
            "Error processing incoming message, dropping: "
                + e.getMessage()
                + ", message is: "
                + byteArrayAsString
                + " with offset "
                + offset);

        e.printStackTrace();
        return null;
      }
    }

    return incomingMessage;
  }
        @Override
        public void run() {
          try {
            // receivedMessagesAverage
            logger.info("receivedMessagesAverage: " + receivedMessagesAverage.getAverage());

            // print which are unprocessed
            long currentTime = Clock.getMainClock().getTimeInMillis(false);

            String debug = "";
            synchronized (receivedMessages) {
              for (AbstractMessage message : receivedMessages)
                debug +=
                    message.toString() + "@" + (message.getReceiveTimeMillis() - currentTime) + ":";
            }
            if (logger.isDebugEnabled()) logger.debug("unprocessed messages: " + debug);
          } catch (Exception e) {
            // just so it doesn't die silently if an unhandled exception happened
            e.printStackTrace();
          }
        }
    @Override
    public void run() {
      while (running) {

        AbstractMessage message = null;

        try {
          message = pipeline.pollFirst();
          // final AbstractMessage finalMessage = message;

          long t0 = Clock.getMainClock().getTimeInMillis(false);
          logger.debug("Got message: " + message);

          processMessage(message);

          logger.debug(
              "Done with message: "
                  + message
                  + ", took "
                  + (Clock.getMainClock().getTimeInMillis(false) - t0)
                  + " ms");
        } catch (Exception e) {
          logger.error(
              "Exception processing message "
                  + e.getMessage()
                  + " caught, continuing with next message");
          e.printStackTrace();
        } finally {
          if (message != null)
            synchronized (message) {
              logger.debug("setting processed: " + message);
              message.setProcessed();
              message.notify();
            }
        }
      }
    }