@Override
  public AbstractMessage handleIncomingMessage(AbstractMessage incomingMessage) {

    this.receivedMessagesAverage.inputValue(1);

    logger.info("in handleIncomingMessage(" + incomingMessage.toString() + ")");

    // adds them to a list to see later which got stuck here
    incomingMessage.setReceiveTimeMillis(Clock.getMainClock().getTimeInMillis(false));
    synchronized (receivedMessages) {
      this.receivedMessages.add(incomingMessage);
    }

    // handles the message
    AbstractMessage replyMessage = null;
    try {
      replyMessage = this.handleMessage(incomingMessage);
    } catch (InterruptedException e) {
      logger.debug("interrupted");
      e.printStackTrace();
    }

    synchronized (receivedMessages) {
      this.receivedMessages.remove(incomingMessage);
    }

    return replyMessage;
  }
  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 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();
          }
        }