protected void publishSupport() {
    DHTTransport transport = dht.getTransport();

    if (TESTING || !transport.isReachable()) {

      DHTTransportContact local_contact = transport.getLocalContact();

      // see if the rendezvous has failed and therefore we are required to find a new one

      boolean force =
          rendezvous_target != null
              && failed_rendezvous.containsKey(rendezvous_target.getAddress());

      if (rendezvous_local_contact != null && !force) {

        if (local_contact.getAddress().equals(rendezvous_local_contact.getAddress())) {

          // already running for the current local contact

          return;
        }
      }

      DHTTransportContact explicit =
          (DHTTransportContact) explicit_rendezvous_map.get(local_contact.getAddress());

      if (explicit != null) {

        try {
          pub_mon.enter();

          rendezvous_local_contact = local_contact;
          rendezvous_target = explicit;

          runRendezvous();

        } finally {

          pub_mon.exit();
        }
      } else {

        final DHTTransportContact[] new_rendezvous_target = {null};

        DHTTransportContact[] reachables = dht.getTransport().getReachableContacts();

        int reachables_tried = 0;
        int reachables_skipped = 0;

        final Semaphore sem = plugin_interface.getUtilities().getSemaphore();

        for (int i = 0; i < reachables.length; i++) {

          DHTTransportContact contact = reachables[i];

          try {
            pub_mon.enter();

            // see if we've found a good one yet

            if (new_rendezvous_target[0] != null) {

              break;
            }

            // skip any known bad ones

            if (failed_rendezvous.containsKey(contact.getAddress())) {

              reachables_skipped++;

              sem.release();

              continue;
            }
          } finally {

            pub_mon.exit();
          }

          if (i > 0) {

            try {
              Thread.sleep(1000);

            } catch (Throwable e) {

            }
          }

          reachables_tried++;

          contact.sendPing(
              new DHTTransportReplyHandlerAdapter() {
                public void pingReply(DHTTransportContact ok_contact) {
                  trace("Punch:" + ok_contact.getString() + " OK");

                  try {
                    pub_mon.enter();

                    if (new_rendezvous_target[0] == null) {

                      new_rendezvous_target[0] = ok_contact;
                    }
                  } finally {

                    pub_mon.exit();

                    sem.release();
                  }
                }

                public void failed(DHTTransportContact failed_contact, Throwable e) {
                  try {
                    trace("Punch:" + failed_contact.getString() + " Failed");

                  } finally {

                    sem.release();
                  }
                }
              });
        }

        for (int i = 0; i < reachables.length; i++) {

          sem.reserve();

          try {
            pub_mon.enter();

            if (new_rendezvous_target[0] != null) {

              rendezvous_target = new_rendezvous_target[0];
              rendezvous_local_contact = local_contact;

              log(
                  "Rendezvous found: "
                      + rendezvous_local_contact.getString()
                      + " -> "
                      + rendezvous_target.getString());

              runRendezvous();

              break;
            }
          } finally {

            pub_mon.exit();
          }
        }

        if (new_rendezvous_target[0] == null) {

          log(
              "No rendezvous found: candidates="
                  + reachables.length
                  + ",tried="
                  + reachables_tried
                  + ",skipped="
                  + reachables_skipped);

          try {
            pub_mon.enter();

            rendezvous_local_contact = null;
            rendezvous_target = null;

          } finally {

            pub_mon.exit();
          }
        }
      }
    } else {

      try {
        pub_mon.enter();

        rendezvous_local_contact = null;
        rendezvous_target = null;

      } finally {

        pub_mon.exit();
      }
    }
  }
  public void start() {
    if (started) {

      return;
    }

    started = true;

    DHTTransport transport = dht.getTransport();

    transport.addListener(
        new DHTTransportListener() {
          public void localContactChanged(DHTTransportContact local_contact) {
            publish(false);
          }

          public void currentAddress(String address) {}

          public void reachabilityChanged(boolean reacheable) {
            publish(false);
          }
        });

    transport.registerTransferHandler(
        transfer_handler_key,
        new DHTTransportTransferHandler() {
          public String getName() {
            return ("NAT Traversal");
          }

          public byte[] handleRead(DHTTransportContact originator, byte[] key) {
            return (null);
          }

          public byte[] handleWrite(DHTTransportContact originator, byte[] key, byte[] value) {
            return (receiveRequest((DHTTransportUDPContact) originator, value));
          }
        });

    timer.addPeriodicEvent(
        REPUBLISH_TIME_MIN,
        new UTTimerEventPerformer() {
          public void perform(UTTimerEvent event) {
            publish(false);
          }
        });

    timer.addPeriodicEvent(
        RENDEZVOUS_SERVER_TIMEOUT / 2,
        new UTTimerEventPerformer() {
          public void perform(UTTimerEvent event) {
            long now = plugin_interface.getUtilities().getCurrentSystemTime();

            try {
              server_mon.enter();

              Iterator it = rendezvous_bindings.values().iterator();

              while (it.hasNext()) {

                Object[] entry = (Object[]) it.next();

                long time = ((Long) entry[1]).longValue();

                boolean removed = false;

                if (time > now) {

                  // clock change, easiest approach is to remove it

                  it.remove();

                  removed = true;

                } else if (now - time > RENDEZVOUS_SERVER_TIMEOUT) {

                  // timeout

                  it.remove();

                  removed = true;
                }

                if (removed) {

                  log(
                      "Rendezvous "
                          + ((DHTTransportContact) entry[0]).getString()
                          + " removed due to inactivity");
                }
              }
            } finally {

              server_mon.exit();
            }
          }
        });

    publish(false);
  }