@Override
  public void broadcast(Message message) throws RemoteException {
    logger.debug("Broadcasting message [" + message + "]");

    // instance the gossip probability with the maximum
    double gossipProbability = 1;
    logger.debug("Start broadcasting with a gossip probability of " + gossipProbability);

    // create the random generator for the gossip probability
    Random rand = new Random();

    for (String nodeId :
        ((ClusterAdministrationImpl)
                nodeManagement.getConnectionManager().getClusterAdmimnistration())
            .getGroupNodes()) {
      if (rand.nextDouble() < gossipProbability) {
        logger.debug("Sending the message to node [" + nodeId + "]");
        if (!nodeManagement
            .getConnectionManager()
            .getConnectionManager(nodeId)
            .getGroupCommunication()
            .getListener()
            .onMessageArrive(message)) {
          // lowering the gossip probability
          gossipProbability =
              gossipProbability
                  - 1
                      / ((ClusterAdministrationImpl)
                              nodeManagement.getConnectionManager().getClusterAdmimnistration())
                          .getGroupNodes()
                          .size();
          logger.debug("Gossip probability lowered to " + gossipProbability);
        } else {
          // set the synchronization time of the node
          long timeStamp = new DateTime().getMillis();
          if (synchronizationTime.containsKey(nodeId)) {
            synchronizationTime.replace(nodeId, timeStamp);
          } else {
            synchronizationTime.put(nodeId, timeStamp);
          }
          logger.debug("Node [" + nodeId + "] synchronized at [" + timeStamp + "]");
        }
      }
    }

    // add the message to the broadcasted messages queue
    try {
      broadcastedMessagesQueue.add(new MessageContainer(message));
    } catch (Exception e) {
      logger.info("Queue full. Error message: " + e.getMessage());
    }
    logger.debug("Message added to the broadcasted messages queue");

    logger.debug("Broadcasting ended");
  }
  @Override
  public boolean send(Message message, String nodeId) throws RemoteException {
    logger.debug("Sending message [" + message + "] to node [" + nodeId + "]");

    // get the connection manager of the node id and then execute his onMessageArrive
    return nodeManagement
        .getConnectionManager()
        .getConnectionManager(nodeId)
        .getGroupCommunication()
        .getListener()
        .onMessageArrive(message);
  }
  public MessageManager(NodeManagement nodeManagement) throws RemoteException {
    UnicastRemoteObject.exportObject(this, 0);

    this.nodeManagement = nodeManagement;

    // instance the message queue
    int messagesQueueSize =
        nodeManagement
            .getConfigFile()
            .getProperty("MessagesQueueSize", DEFAULT_MESSAGES_QUEUE_SIZE);
    messagesQueue = new LinkedBlockingQueue<MessageContainer>(messagesQueueSize);

    // instance the broadcasted messages queue
    int broadcastedMessagesQueueSize =
        nodeManagement
            .getConfigFile()
            .getProperty("BroadcastedMessagesQueueSize", DEFAULT_BROADCASTED_MESSAGES_QUEUE_SIZE);
    broadcastedMessagesQueue =
        new LinkedBlockingQueue<MessageContainer>(broadcastedMessagesQueueSize);

    synchronizationTime = new ConcurrentHashMap<String, Long>();
  }
  public void startMessageProcessing() {
    long messageExpirationTime =
        nodeManagement
            .getConfigFile()
            .getProperty("MessageExpirationTime", DEFAULT_MESSAGE_EXPIRATION_TIME);

    // start the message depurator to empty the list of broadcasted messages after their timestamp
    // has expired
    MessageDepurator messageDepurator =
        new MessageDepurator(nodeManagement, broadcastedMessagesQueue, messageExpirationTime);
    new Thread(messageDepurator).start();

    // start the message processor to process the arriving messages
    MessageProcessor messageProcessor = new MessageProcessor(nodeManagement, messagesQueue);
    new Thread(messageProcessor).start();

    // start the message requester to get the new messages from every group node
    if (nodeManagement.getConfigFile().getProperty("MessageRequesterEnabled", false)) {
      MessageRequester messageRequester = new MessageRequester(nodeManagement);
      new Thread(messageRequester).start();
    }
  }