JDK-8215204 : Thread leak when calling LdapContext.reconnect
  • Type: Bug
  • Component: core-libs
  • Sub-Component: javax.naming
  • Affected Version: 11.0.1
  • Priority: P4
  • Status: Closed
  • Resolution: Duplicate
  • OS: windows_10
  • CPU: x86_64
  • Submitted: 2018-12-08
  • Updated: 2019-02-07
  • Resolved: 2019-02-07
Related Reports
Duplicate :  
Description
A DESCRIPTION OF THE PROBLEM :
When being connected to a domain controller using LDAP the javax.naming library creates a reader thread in order to receive the response from the server.
Calling the reconnect method on the LdapContext instance performs a reconnect and creates a new reader thread. However, it does not stop the old reader thread from the previous connection. That leads to a resource leak, because with every reconnect call, a new thread gets created and the old threads are not being stopped.

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Open a LDAP connection:

env = new Hashtable<String, String>();
    env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
    env.put (Context.SECURITY_PRINCIPAL, username);
    env.put (Context.SECURITY_CREDENTIALS, password);
    
    if((this.security == SecurityProtocol.NONE) || (this.security == SecurityProtocol.TLS)) {
      env.put(Context.PROVIDER_URL, String.format ("ldap://%s:%d", this.hostname, this.port));
    }
    else if(this.security == SecurityProtocol.LDAPS) {
      env.put(Context.PROVIDER_URL, String.format ("ldaps://%s:%d", this.hostname, this.port));
      env.put(Context.SECURITY_PROTOCOL, "ssl");
      env.put("java.naming.ldap.factory.socket", "com.jdisc.inventory.protocols.ldap.LdapSslSocketFactory");
    }
    else {
      throw new IllegalArgumentException("Illegal security setting");
    }
   
    env.put("java.naming.ldap.attributes.binary", "objectGUID objectSID");
    env.put(CONNECT_TIMEOUT_KEY, Integer.toString (connectTimeout));
    env.put(READ_TIMEOUT_KEY, Integer.toString (readTimeout));
    
    connCtls = new Control[] {new FastBindConnectionControl()};
    ctx = new InitialLdapContext(env, connCtls);
    
    if(this.security == SecurityProtocol.TLS) {
      StartTlsResponse tls = (StartTlsResponse) ctx.extendedOperation(new StartTlsRequest());
      tls.setHostnameVerifier (new LdapHostnameVerifier());
      tls.negotiate(new LdapSslSocketFactory());
    }    

 // This creates the additional thread and the original reader thread never stops.
  ctx.reconnect();

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
The reconnect method should either reuse the existing thread or first stop the existing thread and then create the new reader-thread.
ACTUAL -
After the reconnect method, we have two reader threads for the same connection. One of them will never disappear. Even when you disconnect the LDAP context.

---------- BEGIN SOURCE ----------
env = new Hashtable<String, String>();
    env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
    env.put (Context.SECURITY_PRINCIPAL, username);
    env.put (Context.SECURITY_CREDENTIALS, password);
    
    if((this.security == SecurityProtocol.NONE) || (this.security == SecurityProtocol.TLS)) {
      env.put(Context.PROVIDER_URL, String.format ("ldap://%s:%d", this.hostname, this.port));
    }
    else if(this.security == SecurityProtocol.LDAPS) {
      env.put(Context.PROVIDER_URL, String.format ("ldaps://%s:%d", this.hostname, this.port));
      env.put(Context.SECURITY_PROTOCOL, "ssl");
      env.put("java.naming.ldap.factory.socket", "com.jdisc.inventory.protocols.ldap.LdapSslSocketFactory");
    }
    else {
      throw new IllegalArgumentException("Illegal security setting");
    }
   
    env.put("java.naming.ldap.attributes.binary", "objectGUID objectSID");
    env.put(CONNECT_TIMEOUT_KEY, Integer.toString (connectTimeout));
    env.put(READ_TIMEOUT_KEY, Integer.toString (readTimeout));
    
    connCtls = new Control[] {new FastBindConnectionControl()};
    ctx = new InitialLdapContext(env, connCtls);
    
    if(this.security == SecurityProtocol.TLS) {
      StartTlsResponse tls = (StartTlsResponse) ctx.extendedOperation(new StartTlsRequest());
      tls.setHostnameVerifier (new LdapHostnameVerifier());
      tls.negotiate(new LdapSslSocketFactory());
    }    

// The reconnect causes the issue.
 ctx.reconnect();
---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
No workaround known when reconnect required.

FREQUENCY : always



Comments
To submitter: Can you please provide a complete standalone test case to help reproduce the issue locally at our end. Also please let us know if the issue is reproducible on JDK 12-ea as well. You can download it from http://jdk.java.net/12/ .
11-12-2018