/**
   * Helper function for rerouting
   *
   * @param link
   */
  void reroute_bundles(final Link link) {

    assert !link.isdeleted() : "TableBasedRouter : reroute_bundles, link is deleted";

    // "if the reroute timer fires, the link should be down and there
    // should be at least one bundle queued on it." [DTN2]
    if (link.state() != Link.state_t.UNAVAILABLE) {
      Log.w(
          TAG,
          String.format(
              "reroute timer fired but link %s state is %s, not UNAVAILABLE",
              link, link.state().toString()));
      return;
    }

    Log.d(
        TAG,
        String.format(
            "reroute timer fired -- cancelling %s bundles on link %s",
            link.queue().size(), link.toString()));

    link.queue().get_lock().lock();
    try {
      while (!link.queue().empty()) {
        Bundle bundle = link.queue().front();
        actions_.cancel_bundle(bundle, link);
        assert !bundle.is_queued_on(link.queue())
            : "TableBasedRouter : reroute_bundles, bundle is not queued on link";
      }

      // "there should never have been any in flight since the link is
      // unavailable" [DTN2]
      assert link.inflight().empty()
          : "TableBasedRouter : reroute_bundles, link on flight list is not empty";

    } finally {
      link.queue().get_lock().unlock();
    }
  }
  /** "Try to forward a bundle to a next hop route." [DTN2] */
  protected boolean fwd_to_nexthop(Bundle bundle, RouteEntry route) {

    Link link = route.link();

    // "if the link is available and not open, open it" [DTN2]
    if (link.isNotUnavailable() && (!link.isopen()) && (!link.isopening())) {
      Log.d(TAG, String.format("opening %s because a message is intended for it", link.name()));
      actions_.open_link(link);
    }

    // "if the link is open and has space in the queue, then queue the
    // bundle for transmission there" [DTN2]
    if (link.isopen() && !link.queue_is_full()) {
      Log.d(TAG, String.format("queuing %d on %s", bundle.bundleid(), link.name()));
      actions_.queue_bundle(bundle, link, route.action(), route.custody_spec());
      return true;
    }

    // "otherwise we can't send the bundle now, so put it on the link's
    // deferred list and log reason why we can't forward it" [DTN2]
    DeferredList deferred = deferred_list(link);
    if (!bundle.is_queued_on(deferred.list())) {
      ForwardingInfo info =
          new ForwardingInfo(
              ForwardingInfo.state_t.NONE,
              route.action(),
              link.name_str(),
              0xffffffff,
              link.remote_eid(),
              route.custody_spec());
      deferred.add(bundle, info);
    } else {
      Log.w(
          TAG,
          String.format(
              "bundle %d already exists on deferred list of link %s",
              bundle.bundleid(), link.name()));
    }

    if (!link.isNotUnavailable()) {
      Log.d(
          TAG,
          String.format(
              "can't forward bundle %d to %s because link not available",
              bundle.bundleid(), link.name()));
    } else if (!link.isopen()) {
      Log.d(
          TAG,
          String.format(
              TAG,
              "can't forward bundle %d to %s because link not open",
              bundle.bundleid(),
              link.name()));
    } else if (link.queue_is_full()) {
      Log.d(
          TAG,
          String.format(
              TAG,
              "can't forward bundle %d to %s because link queue is full",
              bundle.bundleid(),
              link.name()));
    } else {
      Log.d(TAG, String.format(TAG, "can't forward %d to %s", bundle.bundleid(), link.name()));
    }

    return false;
  }