Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8156649

Using JNDI for LDAP access causes a classloader leak

    XMLWordPrintable

Details

    Description

      FULL PRODUCT VERSION :
      java version "1.8.0_65"
      Java(TM) SE Runtime Environment (build 1.8.0_65-b17)
      Java HotSpot(TM) Client VM (build 25.65-b01, mixed mode)

      ADDITIONAL OS VERSION INFORMATION :
      Microsoft Windows [Version 6.1.7601]

      A DESCRIPTION OF THE PROBLEM :
      Whenever LDAP is used in a web application, it causes a classloader leak after redeployment with the following GC root:

      this - value: org.apache.catalina.loader.WebappClassLoader #2
       <- <classLoader> - class: org.example.LDAPLeakDemo, value: org.apache.catalina.loader.WebappClassLoader #2
        <- [10] - class: java.lang.Object[], value: org.example.LDAPLeakDemo class LDAPLeakDemo
         <- [2] - class: java.lang.Object[], value: java.lang.Object[] #3884
          <- backtrace - class: javax.naming.directory.SchemaViolationException, value: java.lang.Object[] #3875
           <- readOnlyEx - class: com.sun.jndi.toolkit.dir.HierMemDirCtx, value: javax.naming.directory.SchemaViolationException #1
            <- EMPTY_SCHEMA (sticky class) - class: com.sun.jndi.ldap.LdapCtx, value: com.sun.jndi.toolkit.dir.HierMemDirCtx #1


      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      1. Make sure you have any version of Tomcat and Maven.
      2. Go to Tomcat’s conf directory, open tomcat-users.xml and add
      <role rolename="manager-gui"/><user username="admin" password="1" roles="manager-gui"/> inside <tomcat-users></ tomcat-users>
      to be able to use Tomcat Web Application Manager.
      3. Download the test project from https://github.com/JohnA2/ldap-leak-demo/
      4. Go to project’s directory and run "mvn package" to build a .war.
      5. Go to Tomcat’s webapps directory, delete everything except the manager directory and copy the .war here.
      6. Run Tomcat’s start script (bin\startup.bat or bin/startup.sh) and open http://localhost:8080/manager/, use username admin and password 1.
      7. Click on Reload to redeploy the web application.
      8. Start Java VisualVM (jvisualvm) from JDK’s bin directory.
      9. Double click on “Tomcat” on the left panel, open the Monitor tab on the right panel, and click on Heap Dump.
      10. Click on OQL Console, enter a query “select x from org.apache.catalina.loader.WebappClassLoader x”, and click on Execute.
      11. You should see three “org.apache.catalina.loader.WebappClassLoader” entries. Find one with the field state set to DESTROYED, this is the classloader for the undeployed web application.
      12. Right click on its instance and select Show Nearest GC Root.
      13. You should see the GC root from the bug description.


      EXPECTED VERSUS ACTUAL BEHAVIOR :
      EXPECTED -
      No classloader leak.
      ACTUAL -
      Classloader leak.

      REPRODUCIBILITY :
      This bug can be reproduced always.

      ---------- BEGIN SOURCE ----------
      package org.example;

      import java.util.Hashtable;

      import javax.naming.Context;
      import javax.naming.NamingException;
      import javax.naming.directory.DirContext;
      import javax.naming.directory.InitialDirContext;
      import javax.servlet.ServletContextEvent;
      import javax.servlet.ServletContextListener;
      import javax.servlet.annotation.WebListener;

      @WebListener
      public class LDAPLeakDemo implements ServletContextListener {
      public void contextInitialized(ServletContextEvent sce) {
      useLDAP();
      }

      public void contextDestroyed(ServletContextEvent sce) {}

      private void useLDAP() {
      Hashtable<String, Object> env = new Hashtable<String, Object>();
      env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
      env.put(Context.PROVIDER_URL, "ldap://ldap.forumsys.com:389");
      env.put(Context.SECURITY_AUTHENTICATION, "simple");
      env.put(Context.SECURITY_PRINCIPAL, "cn=read-only-admin,dc=example,dc=com");
      env.put(Context.SECURITY_CREDENTIALS, "password");
      try {
      DirContext ctx = null;
      try {
      ctx = new InitialDirContext(env);
      System.out.println("Created the initial context");
      } finally {
      if (ctx != null) {
      ctx.close();
      System.out.println("Closed the context");
      }
      }
      } catch (NamingException e) {
      e.printStackTrace();
      }
      }
      }

      ---------- END SOURCE ----------

      Attachments

        Issue Links

          Activity

            People

              prappo Pavel Rappo
              webbuggrp Webbug Group
              Votes:
              0 Vote for this issue
              Watchers:
              5 Start watching this issue

              Dates

                Created:
                Updated:
                Resolved: