/**
   * Calculate the average number of topics on the currently active hubs and release topics if
   * required. We shed topics if we currently hold topics greater than average + average *
   * tolerancePercentage/100.0 We shed a maximum of maxLoadToShed topics We also hold on to at least
   * one topic.
   *
   * @param loadMap
   * @param callback A return value of true means we tried to rebalance. False means that there was
   *     no need to rebalance.
   * @param ctx
   */
  public void shedLoad(
      final Map<HubInfo, HubLoad> loadMap, final Callback<Boolean> callback, final Object ctx) {

    long totalTopics = 0L;
    for (Map.Entry<HubInfo, HubLoad> entry : loadMap.entrySet()) {
      if (null == entry.getKey() || null == entry.getValue()) {
        continue;
      }
      totalTopics += entry.getValue().toHubLoadData().getNumTopics();
    }

    double averageTopics = (double) totalTopics / loadMap.size();
    logger.info("Total topics in the cluster : {}. Average : {}.", totalTopics, averageTopics);

    // Handle the case when averageTopics == 0. We hold on to at least 1 topic.
    long permissibleTopics =
        Math.max(1L, (long) Math.ceil(averageTopics + averageTopics * tolerancePercentage / 100.0));
    logger.info(
        "Permissible topics : {}. Number of topics this hub holds : {}.",
        permissibleTopics,
        numTopics);
    if (numTopics <= permissibleTopics) {
      // My owned topics are less than those permitted by the current tolerance level. No need to
      // release
      // any topics.
      callback.operationFinished(ctx, false);
      return;
    }

    // The number of topics I own is more than what I should be holding. We shall now attempt to
    // shed some load.
    // We shed at most maxLoadToShed number of topics. We also hold on to at least 1 topic.
    long targetNumTopics =
        Math.max(1L, Math.max((long) Math.ceil(averageTopics), numTopics - maxLoadToShed));

    // Reduce the load on the current hub to the target load we calculated above.
    logger.info("Reducing load on this hub to {} topics.", targetNumTopics);
    reduceLoadTo(
        new HubLoad(targetNumTopics),
        new Callback<Long>() {
          @Override
          public void operationFinished(Object ctx, Long numReleased) {
            logger.info("Released {} topics to shed load.", numReleased);
            callback.operationFinished(ctx, true);
          }

          @Override
          public void operationFailed(Object ctx, PubSubException e) {
            callback.operationFailed(ctx, e);
          }
        },
        ctx);
  }
 @Override
 public void serveSubscribeRequest(
     ByteString topic,
     SubscribeRequest subRequest,
     MessageSeqId consumeSeqId,
     Callback<SubscriptionData> callback,
     Object ctx) {
   if (fail) {
     callback.operationFailed(ctx, new PubSubException.ServiceDownException("Asked to fail"));
     return;
   }
   super.serveSubscribeRequest(topic, subRequest, consumeSeqId, callback, ctx);
 }
  /**
   * Reduce the load on the current hub so that it reaches the target load. We reduce load by
   * releasing topics using the {@link TopicManager} passed to the constructor. We use {@link
   * TopicManager#releaseTopics(int, org.apache.hedwig.util.Callback, Object)} to actually release
   * topics.
   *
   * @param targetLoad
   * @param callback a Callback<Long> that indicates how many topics we tried to release.
   * @param ctx
   */
  public void reduceLoadTo(HubLoad targetLoad, final Callback<Long> callback, final Object ctx) {
    int targetTopics = (int) targetLoad.toHubLoadData().getNumTopics();
    int numTopicsToRelease = (int) numTopics - targetTopics;

    // The number of topics we own is less than the target topic size. We don't release
    // any topics in this case.
    if (numTopicsToRelease <= 0) {
      callback.operationFinished(ctx, 0L);
      return;
    }
    // Call releaseTopics() on the topic manager to do this. We let the manager handle the release
    // policy.
    tm.releaseTopics(numTopicsToRelease, callback, ctx);
  }
 @Override
 public void deliver(
     ByteString topic,
     ByteString subscriberId,
     Message msg,
     Callback<Void> callback,
     Object context) {
   System.out.println(
       "Received message from topic "
           + topic.toStringUtf8()
           + " for subscriber "
           + subscriberId.toStringUtf8()
           + " : "
           + msg.getBody().toStringUtf8());
   callback.operationFinished(context, null);
 }
 @Override
 public void deliver(
     ByteString t,
     ByteString s,
     org.apache.hedwig.protocol.PubSubProtocol.Message msg,
     org.apache.hedwig.util.Callback<Void> callback,
     Object context) {
   if (!t.equals(topic) || !s.equals(subId)) {
     return;
   }
   int num = Integer.parseInt(msg.getBody().toStringUtf8());
   if (num == next) {
     latch.countDown();
     ++next;
   }
   callback.operationFinished(context, null);
 }