JDK-5084229 : javax.naming.NamingEnuermation should be closed when parent DirContext is closed
  • Type: Bug
  • Component: core-libs
  • Sub-Component: javax.naming
  • Affected Version: 1.4.2,6
  • Priority: P3
  • Status: Closed
  • Resolution: Not an Issue
  • OS: generic,windows_2000
  • CPU: generic,x86
  • Submitted: 2004-08-09
  • Updated: 2006-02-03
  • Resolved: 2006-02-03
Description
Name: js151677			Date: 08/08/2004


FULL PRODUCT VERSION :
M:\>java -version
java version "1.4.2_04"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.2_04-b05)
Java HotSpot(TM) Client VM (build 1.4.2_04-b05, mixed mode)

ADDITIONAL OS VERSION INFORMATION :
Microsoft Windows 2000 [Version 5.00.2195]

A DESCRIPTION OF THE PROBLEM :
When a NamingEnumeration is created from DirContext..search() it obviously has a internal connection back to the directory. When the DirContext that  created that NamingEnumeration is closed (via DirContext.close()) the 'child' NamingEnumeration's should be closed too. This is analogous to Statement/ResultSet's being closed when a Connection is closed().





STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Run the test case

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
That  the users NamingEnumeration would be closed as a result of the ctx.close() in the finally block
ACTUAL -
It isn't closed and connections to the directory get exhausted resulting in a hang.

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------

import java.util.Hashtable;

import javax.naming.Context;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;
import javax.naming.directory.SearchControls;

public class Test
{

    /**
     *
     */
    public Test()
    {
        super();
        // TODO Auto-generated constructor stub
    }

    public static void main(String[] args) throws Exception
    {

        try
        {
            Hashtable env = new Hashtable();
            env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
            env.put(Context.SECURITY_PRINCIPAL, PRINCIPAL);
            env.put(Context.SECURITY_CREDENTIALS, PASSWORD);
            env.put(Context.PROVIDER_URL, "ldap://directory:389/");
            DirContext ctx = null;
            SearchControls sc = new SearchControls(SearchControls.SUBTREE_SCOPE, 0, 0, null, false, false);

            for (int i = 0; i < 1000; i++)
            {
                try
                {
                    ctx = new InitialDirContext(env);
                    System.out.println( i + ": Got " + ctx);
                    NamingEnumeration users = ctx
                                    .search("", "(objectClass=*)", new String[] {}, sc);
                    users.close();
                }
                finally
                {
                    System.out.print(i + ":Closing " + ctx);
                    ctx.close();
                    System.out.println("  - CLOSED.");
                }
            }
        }
        catch (NamingException e)
        {
            System.err.println(e);
            throw e;
        }

    }
}
---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
call users.close() in the finally block too.
(Incident Review ID: 295993) 
======================================================================
A DESCRIPTION OF THE FIX :
The file to be changed is com/sun/jndi/ldap/LdapClient.java

The problem is that NamingEnumerations need to be explictly closed otherwise they will hold the connections open.

This is from jdk-6_0-ea-src-b47-jrl-11_aug_2005.jar


$ diff -u LdapClient.java.orig LdapClient.java
--- LdapClient.java.orig        2005-08-18 13:26:33.487425100 -0600
+++ LdapClient.java     2005-08-18 13:27:18.271643700 -0600
@@ -952,6 +952,7 @@
                            "Malformed '" + attr.getID() + "' attribute value");

                    }
                }
+               enum_.close();
            ber.endSeq();
        ber.endSeq();
     }
@@ -999,6 +1000,7 @@
                                encodeAttribute(ber, attr);
                            }
                        }
+                       enum_.close();
                    ber.endSeq();
                ber.endSeq();
                if (isLdapv3) encodeControls(ber, reqCtls);

FIX FOR BUG NUMBER:
5084229

Comments
EVALUATION Changing the behavior now would result in compatibility problems. Hence I am closing this bug/RFE.
03-02-2006

EVALUATION First of all this is not a defect (bug). The JNDI API is designed to provide flexibility to the service providers to implement NamingEnumeration.close() by using appropriate algorithms to manage resources. The idea is to allow implementations to share resources such as underlying connections by different contexts originating from the same root context. From the Javadoc of NamingEnumeration.close(): " Closes this enumeration. After this method has been invoked on this enumeration, the enumeration becomes invalid and subsequent invocation of any of its methods will yield undefined results. This method is intended for aborting an enumeration to free up resources. If an enumeration proceeds to the end--that is, until hasMoreElements() or hasMore() returns false-- resources will be freed up automatically and there is no need to explicitly call close(). This method indicates to the service provider that it is free to release resources associated with the enumeration, and can notify servers to cancel any outstanding requests. The close() method is a hint to implementations for managing their resources. Implementations are encouraged to use appropriate algorithms to manage their resources when client omits the close() calls." The JNDI/LDAP Service Provider avails this flexibility of the API design to share resources between the context and the results returned by it upon a call to search() and lookup(). Since sharing of resources is specific to the implementation, calling a close() on the root context need not make the results of the Enumeration useless by closing them as well. Some other service provider may not share connections. Closing the connection used by the root context need not close the connection used by a sub context. These two contexts are two different entities even though the sub context is operating on the same or sub namespace as that of the root context. This behavior of the JNDI/LDAP Service Provider is also documented in the JNDI tutorial: http://java.sun.com/products/jndi/tutorial/ldap/connect/close.html (Please take look at "Explicit Closures" section)
22-08-2005

EVALUATION Passing to Jayal for review & evaluation. ###@###.### 2004-08-09 Changing the status to 'eval' to get it out of the EC list. ###@###.### 2004-08-11
09-08-2004