public NamingProvider createProvider(
      final URI providerUri, final FastHashtable<String, Object> env) throws NamingException {
    // Legacy naming constants
    final Endpoint endpoint = getEndpoint(env);
    final String callbackClass = getStringProperty(CALLBACK_HANDLER_KEY, env);
    final String userName = getStringProperty(Context.SECURITY_PRINCIPAL, env);
    final String password = getStringProperty(Context.SECURITY_CREDENTIALS, env);
    final String passwordBase64 = getStringProperty(PASSWORD_BASE64_KEY, env);
    final String realm = getStringProperty(REALM_KEY, env);

    boolean useSeparateConnection =
        Boolean.parseBoolean(String.valueOf(env.get(USE_SEPARATE_CONNECTION)));

    AuthenticationContext captured = AuthenticationContext.captureCurrent();
    AuthenticationConfiguration mergedConfiguration =
        AUTH_CONFIGURATION_CLIENT.getAuthenticationConfiguration(providerUri, captured);
    if (callbackClass != null && (userName != null || password != null)) {
      throw Messages.log.callbackHandlerAndUsernameAndPasswordSpecified();
    }
    if (callbackClass != null) {
      final ClassLoader classLoader = secureGetContextClassLoader();
      try {
        final Class<?> clazz = Class.forName(callbackClass, true, classLoader);
        final CallbackHandler callbackHandler = (CallbackHandler) clazz.newInstance();
        if (callbackHandler != null) {
          mergedConfiguration = mergedConfiguration.useCallbackHandler(callbackHandler);
        }
      } catch (ClassNotFoundException e) {
        throw Messages.log.failedToLoadCallbackHandlerClass(e, callbackClass);
      } catch (Exception e) {
        throw Messages.log.failedToInstantiateCallbackHandlerInstance(e, callbackClass);
      }
    } else if (userName != null) {
      if (password != null && passwordBase64 != null) {
        throw Messages.log.plainTextAndBase64PasswordSpecified();
      }
      final String decodedPassword =
          passwordBase64 != null
              ? CodePointIterator.ofString(passwordBase64)
                  .base64Decode()
                  .asUtf8String()
                  .drainToString()
              : password;
      mergedConfiguration =
          mergedConfiguration.useName(userName).usePassword(decodedPassword).useRealm(realm);
    }
    final AuthenticationContext context =
        AuthenticationContext.empty().with(MatchRule.ALL, mergedConfiguration);

    if (useSeparateConnection) {
      // create a brand new connection - if there is authentication info in the env, use it
      final Connection connection;
      try {
        connection = endpoint.connect(providerUri, OptionMap.EMPTY, context).get();
      } catch (IOException e) {
        throw Messages.log.connectFailed(e);
      }
      final RemoteNamingProvider provider = new RemoteNamingProvider(connection, context, env);
      connection.getAttachments().attach(PROVIDER_KEY, provider);
      return provider;
    } else if (env.containsKey(CONNECTION)) {
      final Connection connection = (Connection) env.get(CONNECTION);
      final RemoteNamingProvider provider = new RemoteNamingProvider(connection, context, env);
      connection.getAttachments().attach(PROVIDER_KEY, provider);
      return provider;
    } else {
      final Attachments attachments = endpoint.getAttachments();
      ProviderMap map = attachments.getAttachment(PROVIDER_MAP_KEY);
      if (map == null) {
        ProviderMap appearing =
            attachments.attachIfAbsent(PROVIDER_MAP_KEY, map = new ProviderMap());
        if (appearing != null) {
          map = appearing;
        }
      }
      final URIKey key =
          new URIKey(
              providerUri.getScheme(),
              providerUri.getUserInfo(),
              providerUri.getHost(),
              providerUri.getPort());
      RemoteNamingProvider provider = map.get(key);
      if (provider == null) {
        RemoteNamingProvider appearing =
            map.putIfAbsent(
                key, provider = new RemoteNamingProvider(endpoint, providerUri, context, env));
        if (appearing != null) {
          provider = appearing;
        }
      }
      return provider;
    }
  }
    public void handleEvent(final ConnectedMessageChannel channel) {
      final Pooled<ByteBuffer> pooledReceiveBuffer = connection.allocate();
      try {
        final ByteBuffer receiveBuffer = pooledReceiveBuffer.getResource();
        synchronized (connection.getLock()) {
          int res;
          try {
            res = channel.receive(receiveBuffer);
          } catch (IOException e) {
            connection.handleException(e);
            return;
          }
          if (res == -1) {
            connection.handleException(client.abruptClose(connection));
            return;
          }
          if (res == 0) {
            return;
          }
        }
        receiveBuffer.flip();
        boolean starttls = false;
        final Set<String> serverSaslMechs = new LinkedHashSet<String>();
        final byte msgType = receiveBuffer.get();
        switch (msgType) {
          case Protocol.CONNECTION_ALIVE:
            {
              client.trace("Client received connection alive");
              connection.sendAliveResponse();
              return;
            }
          case Protocol.CONNECTION_ALIVE_ACK:
            {
              client.trace("Client received connection alive ack");
              return;
            }
          case Protocol.CONNECTION_CLOSE:
            {
              client.trace("Client received connection close request");
              connection.handlePreAuthCloseRequest();
              return;
            }
          case Protocol.CAPABILITIES:
            {
              client.trace("Client received capabilities response");
              String remoteEndpointName = null;
              int version = Protocol.VERSION;
              int behavior = Protocol.BH_FAULTY_MSG_SIZE;
              boolean useDefaultChannels = true;
              int channelsIn = 40;
              int channelsOut = 40;
              while (receiveBuffer.hasRemaining()) {
                final byte type = receiveBuffer.get();
                final int len = receiveBuffer.get() & 0xff;
                final ByteBuffer data = Buffers.slice(receiveBuffer, len);
                switch (type) {
                  case Protocol.CAP_VERSION:
                    {
                      version = data.get() & 0xff;
                      client.tracef(
                          "Client received capability: version %d",
                          Integer.valueOf(version & 0xff));
                      break;
                    }
                  case Protocol.CAP_SASL_MECH:
                    {
                      final String mechName = Buffers.getModifiedUtf8(data);
                      client.tracef("Client received capability: SASL mechanism %s", mechName);
                      if (!failedMechs.containsKey(mechName)
                          && !disallowedMechs.contains(mechName)
                          && (allowedMechs == null || allowedMechs.contains(mechName))) {
                        client.tracef("SASL mechanism %s added to allowed set", mechName);
                        serverSaslMechs.add(mechName);
                      }
                      break;
                    }
                  case Protocol.CAP_STARTTLS:
                    {
                      client.trace("Client received capability: STARTTLS");
                      starttls = true;
                      break;
                    }
                  case Protocol.CAP_ENDPOINT_NAME:
                    {
                      remoteEndpointName = Buffers.getModifiedUtf8(data);
                      client.tracef(
                          "Client received capability: remote endpoint name \"%s\"",
                          remoteEndpointName);
                      break;
                    }
                  case Protocol.CAP_MESSAGE_CLOSE:
                    {
                      behavior |= Protocol.BH_MESSAGE_CLOSE;
                      // remote side must be >= 3.2.11.GA
                      // but, we'll assume it's >= 3.2.14.GA because no AS or EAP release included
                      // 3.2.8.SP1 < x < 3.2.14.GA
                      behavior &= ~Protocol.BH_FAULTY_MSG_SIZE;
                      client.tracef("Client received capability: message close protocol supported");
                      break;
                    }
                  case Protocol.CAP_VERSION_STRING:
                    {
                      // remote side must be >= 3.2.16.GA
                      behavior &= ~Protocol.BH_FAULTY_MSG_SIZE;
                      final String remoteVersionString = Buffers.getModifiedUtf8(data);
                      client.tracef(
                          "Client received capability: remote version is \"%s\"",
                          remoteVersionString);
                      break;
                    }
                  case Protocol.CAP_CHANNELS_IN:
                    {
                      useDefaultChannels = false;
                      // their channels in is our channels out
                      channelsOut = ProtocolUtils.readIntData(data, len);
                      client.tracef(
                          "Client received capability: remote channels in is \"%d\"", channelsOut);
                      break;
                    }
                  case Protocol.CAP_CHANNELS_OUT:
                    {
                      useDefaultChannels = false;
                      // their channels out is our channels in
                      channelsIn = ProtocolUtils.readIntData(data, len);
                      client.tracef(
                          "Client received capability: remote channels out is \"%d\"", channelsIn);
                      break;
                    }
                  default:
                    {
                      client.tracef(
                          "Client received unknown capability %02x", Integer.valueOf(type & 0xff));
                      // unknown, skip it for forward compatibility.
                      break;
                    }
                }
              }
              if (useDefaultChannels) {
                channelsIn = 40;
                channelsOut = 40;
              }
              if (starttls) {
                // only initiate starttls if not forbidden by config
                if (optionMap.get(Options.SSL_STARTTLS, true)) {
                  // Prepare the request message body
                  final Pooled<ByteBuffer> pooledSendBuffer = connection.allocate();
                  boolean ok = false;
                  try {
                    final ByteBuffer sendBuffer = pooledSendBuffer.getResource();
                    sendBuffer.put(Protocol.STARTTLS);
                    sendBuffer.flip();
                    connection.setReadListener(new StartTls(remoteServerName), true);
                    connection.send(pooledSendBuffer);
                    ok = true;
                    // all set
                    return;
                  } finally {
                    if (!ok) pooledSendBuffer.free();
                  }
                }
              }

              if (serverSaslMechs.isEmpty()) {
                if (failedMechs.isEmpty()) {
                  connection.handleException(
                      new SaslException(
                          "Authentication failed: the server presented no authentication mechanisms"));
                } else {
                  // At this point we have been attempting to use mechanisms as they have been
                  // presented to us but we have now exhausted the list.
                  connection.handleException(allMechanismsFailed());
                }
                return;
              }

              // OK now send our authentication request
              final AuthenticationContextConfigurationClient configurationClient =
                  AUTH_CONFIGURATION_CLIENT;
              AuthenticationConfiguration configuration =
                  configurationClient.getAuthenticationConfiguration(uri, authenticationContext);
              final SaslClient saslClient;
              try {
                saslClient =
                    configurationClient.createSaslClient(
                        uri, configuration, saslClientFactory, serverSaslMechs);
              } catch (SaslException e) {
                // apparently no more mechanisms can succeed
                connection.handleException(e);
                return;
              }
              if (saslClient == null) {
                connection.handleException(allMechanismsFailed());
                return;
              }
              final String mechanismName = saslClient.getMechanismName();
              client.tracef("Client initiating authentication using mechanism %s", mechanismName);

              connection.getChannel().suspendReads();
              final int negotiatedVersion = version;
              final SaslClient usedSaslClient = saslClient;
              final Authentication authentication =
                  new Authentication(
                      usedSaslClient,
                      remoteServerName,
                      remoteEndpointName,
                      behavior,
                      channelsIn,
                      channelsOut);
              connection
                  .getExecutor()
                  .execute(
                      () -> {
                        final byte[] response;
                        try {
                          response =
                              usedSaslClient.hasInitialResponse()
                                  ? usedSaslClient.evaluateChallenge(EMPTY_BYTES)
                                  : null;
                        } catch (SaslException e) {
                          client.tracef("Client authentication failed: %s", e);
                          saslDispose(usedSaslClient);
                          failedMechs.put(mechanismName, e.toString());
                          sendCapRequest(remoteServerName);
                          return;
                        }
                        // Prepare the request message body
                        final Pooled<ByteBuffer> pooledSendBuffer = connection.allocate();
                        boolean ok = false;
                        try {
                          final ByteBuffer sendBuffer = pooledSendBuffer.getResource();
                          sendBuffer.put(Protocol.AUTH_REQUEST);
                          if (negotiatedVersion < 1) {
                            sendBuffer.put(mechanismName.getBytes(Protocol.UTF_8));
                          } else {
                            ProtocolUtils.writeString(sendBuffer, mechanismName);
                            if (response != null) {
                              sendBuffer.put(response);
                            }
                          }

                          sendBuffer.flip();
                          connection.send(pooledSendBuffer);
                          ok = true;
                          connection.setReadListener(authentication, true);
                          return;
                        } finally {
                          if (!ok) pooledSendBuffer.free();
                        }
                      });
              return;
            }
          default:
            {
              client.unknownProtocolId(msgType);
              connection.handleException(client.invalidMessage(connection));
              return;
            }
        }
      } catch (BufferUnderflowException | BufferOverflowException e) {
        connection.handleException(client.invalidMessage(connection));
        return;
      } finally {
        pooledReceiveBuffer.free();
      }
    }