static VersionedConnection createVersionedConnection(
      final Channel channel, final Map<String, ?> environment, final JMXServiceURL serviceURL)
      throws IOException {
    // We don't want to start chaining the use of IoFutures otherwise multiple threads are tied up
    // for a single negotiation process so negotiate the connection sequentially.

    IoFuture<InitialHeader> futureHeader = ClientVersionReceiver.getInitialHeader(channel);
    IoFuture.Status result = futureHeader.await(5, TimeUnit.SECONDS);
    switch (result) {
      case DONE:
        break;
      case FAILED:
        throw futureHeader.getException();
      default:
        throw new IOException("Timeout out waiting for header, status=" + result.toString());
    }

    InitialHeader header = futureHeader.get();

    Versions versions = new Versions(environment);
    Set<Byte> supportedVersions =
        versions.getSupportedVersions(getRequiredCapabilities(serviceURL));

    // Find the highest version. - By this point the exceptional handling of version 0x00 will have
    // completed.
    byte highest = 0x00;
    for (byte current : header.versions) {
      // Only accept it if it is one of the supported versions otherwise ignore as noise.
      if (supportedVersions.contains(current) && current > highest) {
        highest = current;
      }
    }

    if (highest == 0x00) {
      throw new IllegalStateException("No matching supported protocol version found.");
    }

    // getVersionedConnection may also make use of an IoFuture but our previous use of one has
    // ended.
    return versions.getVersionedConnection(highest, channel, serviceURL);
  }
  public Connection connectSync(
      CallbackHandler handler, Map<String, String> saslOptions, SSLContext sslContext)
      throws IOException {
    CallbackHandler actualHandler = handler != null ? handler : new AnonymousCallbackHandler();
    WrapperCallbackHandler wrapperHandler = new WrapperCallbackHandler(actualHandler);
    final IoFuture<Connection> future = connect(wrapperHandler, saslOptions, sslContext);
    long timeoutMillis = configuration.getConnectionTimeout();
    IoFuture.Status status = future.await(timeoutMillis, TimeUnit.MILLISECONDS);
    while (status == IoFuture.Status.WAITING) {
      if (wrapperHandler.isInCall()) {
        // If there is currently an interaction with the user just wait again.
        status = future.await(timeoutMillis, TimeUnit.MILLISECONDS);
      } else {
        long lastInteraction = wrapperHandler.getCallFinished();
        if (lastInteraction > 0) {
          long now = System.currentTimeMillis();
          long timeSinceLast = now - lastInteraction;
          if (timeSinceLast < timeoutMillis) {
            // As this point we are setting the timeout based on the time of the last interaction
            // with the user, if there is any time left we will wait for that time but dont wait for
            // a full timeout.
            status = future.await(timeoutMillis - timeSinceLast, TimeUnit.MILLISECONDS);
          } else {
            status = null;
          }
        } else {
          status = null; // Just terminate status processing.
        }
      }
    }

    if (status == IoFuture.Status.DONE) {
      return future.get();
    }
    if (status == IoFuture.Status.FAILED) {
      throw ProtocolMessages.MESSAGES.failedToConnect(uri, future.getException());
    }
    throw ProtocolMessages.MESSAGES.couldNotConnect(uri);
  }