Package org.hive2hive.core.network.messages.futures

Source Code of org.hive2hive.core.network.messages.futures.FutureDirectListener

package org.hive2hive.core.network.messages.futures;

import java.security.PublicKey;
import java.util.concurrent.CountDownLatch;

import net.tomp2p.futures.BaseFutureAdapter;
import net.tomp2p.futures.FutureDirect;

import org.hive2hive.core.network.messages.AcceptanceReply;
import org.hive2hive.core.network.messages.BaseMessage;
import org.hive2hive.core.network.messages.MessageManager;
import org.hive2hive.core.network.messages.direct.BaseDirectMessage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* Use this future adapter when sending a {@link BaseDirectMessage}. Attach this listener to the future which
* gets
* returned at {@link MessageManager#send(BaseMessage)} to enable a appropriate failure handling. Use the
* {@link FutureRoutedListener#await()} method to wait blocking until the message is sent (or
* not).</br></br>
* <b>Failure Handling</b></br>
* Sending a direct message can fail when the future object failed, when the future object contains wrong data
* or the
* responding node detected a failure. See {@link AcceptanceReply} for possible failures. If sending of a
* message fails the message gets re-send as long as
* {@link BaseDirectMessage#handleSendingFailure(AcceptanceReply)} of the sent message recommends to re-send.
* Depending on the {@link BaseDirectMessage#needsRedirectedSend()} flag a possible fall back is to use the
* routing mechanism of {@link MessageManager#send(BaseMessage)}. For that another adapter
* (see {@link FutureDirectListener}) is attached.
*
* @author Seppi, Nico
*/
public class FutureDirectListener extends BaseFutureAdapter<FutureDirect> {

  private static final Logger logger = LoggerFactory.getLogger(FutureDirectListener.class);

  private final BaseDirectMessage message;
  private final PublicKey receiverPublicKey;
  private final MessageManager messageManager;
  private final CountDownLatch latch;
  private DeliveryState state;

  private enum DeliveryState {
    SUCCESS,
    ERROR,
    RESEND_DIRECT,
    RESEND_ROUTED
  }

  /**
   * Constructor for a future adapter.
   *
   * @param message
   *            message which has been sent (needed for re-sending)
   * @param receiverPublicKey
   *            the receivers public key which was used for encryption
   * @param networkManager
   *            reference needed for re-sending)
   */
  public FutureDirectListener(BaseDirectMessage message, PublicKey receiverPublicKey,
      MessageManager messageManager) {
    this.message = message;
    this.receiverPublicKey = receiverPublicKey;
    this.messageManager = messageManager;
    this.latch = new CountDownLatch(1);
  }

  /**
   * Wait (blocking) until the message is sent
   *
   * @return true if successful, false if not successful
   */
  public boolean await() {
    try {
      latch.await();
    } catch (InterruptedException e) {
      logger.error("Could not wait until the message is sent successfully.");
    }

    switch (state) {
      case SUCCESS:
        // successfully delivered the message
        return true;
      case ERROR:
        // failed to deliver message. Resend not recommended
        return false;
      case RESEND_DIRECT:
        // resend direct is recommended
        return messageManager.sendDirect(message, receiverPublicKey);
      case RESEND_ROUTED:
        // resend (this time routed) is recommended
        return messageManager.send(message, receiverPublicKey);
      default:
        // invalid state
        logger.error("The sending procedure has not finished, but the lock has already been released.");
        return false;
    }
  }

  @Override
  public void operationComplete(FutureDirect future) throws Exception {
    AcceptanceReply reply = extractAcceptanceReply(future);
    if (reply == AcceptanceReply.OK || reply == AcceptanceReply.OK_PROVISIONAL) {
      // notify the listener about the success of sending the message
      state = DeliveryState.SUCCESS;
      latch.countDown();
    } else {
      // check if a direct re-send is necessary / wished
      boolean directResending = message.handleSendingFailure(reply);
      if (directResending) {
        // re-send directly the message
        state = DeliveryState.RESEND_DIRECT;
        latch.countDown();
      } else {
        // check if the routed sending fall back is allowed
        if (message.needsRedirectedSend()) {
          logger.warn("Sending direct message failed. Using normal routed sending as fallback. Target key = '{}', Target address = '{}'.",
                  message.getTargetKey(), message.getTargetAddress());
          // re-send the message (routed)
          state = DeliveryState.RESEND_ROUTED;
          latch.countDown();
        } else {
          // notify the listener about the fail of sending the message
          state = DeliveryState.ERROR;
          latch.countDown();
        }
      }
    }
  }

  /**
   * Check if the given future contains any useful results and log if something went wrong while sending.
   * Generate an acceptance reply.
   *
   * @param future
   *            a future
   * @return a reply showing the result of sending
   */
  private AcceptanceReply extractAcceptanceReply(FutureDirect future) {
    String errorReason = "";
    if (future.isSuccess()) {
      Object responseObject;
      try {
        responseObject = future.object();
        if (responseObject == null) {
          errorReason = "Returned object is null";
        } else if (responseObject instanceof AcceptanceReply) {
          AcceptanceReply reply = (AcceptanceReply) responseObject;
          return reply;
        } else {
          errorReason = "The returned object was not of type AcceptanceReply!";
        }
      } catch (Exception e) {
        errorReason = "Exception occured while getting the object.";
      }
      logger.error("A failure while sending a message occured. Reason = '{}'.",
          errorReason);
      return AcceptanceReply.FAILURE;
    } else {
      logger.error("Future not successful. Reason = '{}'.", future.getFailedReason());
      return AcceptanceReply.FUTURE_FAILURE;
    }
  }

}
TOP

Related Classes of org.hive2hive.core.network.messages.futures.FutureDirectListener

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.