java - 为什么 DirContext.close() 不将 LDAP 连接返回到池中?

标签 java ldap jndi connection-pooling

我注意到在使用 LDAP 连接池时,尽管有文档 saying,但在上下文中调用 close() 似乎并未将其返回到池中otherwise .因此,当我尝试从池中获取已达到其最大大小的项目时,它会挂起。

我设法将其缩小到最小范围。尽管我相信我正在确定性地对所有相关对象调用 close(),但它似乎依赖于垃圾收集来实际将对象返回到池中,这是出乎意料的。 为什么会这样?是否还有其他一些我应该关闭的对象?

在下面的代码片段中:

  • 我人为地将最大池大小设置为 1 以突出问题。
  • 我从池中得到一个 DirContext(第 (2) 行),尝试将它返回到池中(第 (4) 行),然后从池中得到另一个(第 (6) 行) ) 应该返回相同的返回对象。
  • 相反,第二个请求(第 (6) 行)挂起在对 Object.wait() 的一些内部调用上。我猜它正在等待池化对象可用。
  • 如果通过注释掉 (1) 关闭池化,它不会挂起(但我想要池化!)。
  • 如果我注释掉 (3) - 调用 SearchResults.next() - 它工作正常。
  • 如果我取消注释第 (5) 行以在“返回池”调用和向池请求新对象之间强制进行垃圾收集,它不会挂起。

由于注释掉第 (3) 行使问题消失了,也许我没有正确关闭它的返回值,它使池连接保持打开状态。但是,方法 results.next() 返回 SearchResult在这种情况下,它没有 close 方法,其文档中也没有关于如何彻底关闭它的指导。

测试用例:

@Test
public void testHangs() throws NamingException {

    System.setProperty("com.sun.jndi.ldap.connect.pool.debug", "fine");
    System.setProperty("com.sun.jndi.ldap.connect.pool.maxsize", "1");

    Hashtable<String,String> env = new Hashtable<String,String>();
    env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
    env.put(Context.SECURITY_PRINCIPAL, user);
    env.put(Context.SECURITY_CREDENTIALS, passwd);
    env.put(Context.SECURITY_AUTHENTICATION, "simple");
    env.put(Context.PROVIDER_URL, ldapUrl);

    // use a connection pool
    env.put("com.sun.jndi.ldap.connect.pool", "true"); // -----------------  (1)

    // get a context from the pool.
    DirContext context = new InitialDirContext(env); // -------------------- (2)
    NamingEnumeration<SearchResult> results = context.search("", query, getSC());
    // obviously the next two lines would normally be in a 
    // while(results.hasMore()) { ... = results.next(); } loop.
    assertTrue(results.hasMore()); // this is only a problem when there are some results.
    results.next(); // ----------------------------------------------------- (3)

    // ensure the context is returned to the pool.
    results.close();
    context.close(); // ---------------------------------------------------- (4)

    //System.gc(); // ------------------------------------------------------ (5)

    new InitialDirContext(env);  // hangs here! ---------------------------- (6)
}

使用原样的代码,我的控制台显示:

Create com.sun.jndi.ldap.LdapClient@1a7bf11[ldapad:389]
Use com.sun.jndi.ldap.LdapClient@1a7bf11

而如果我强制执行 GC,我还会看到:

Release com.sun.jndi.ldap.LdapClient@93dee9 <-- on GC
Use com.sun.jndi.ldap.LdapClient@93dee9     <-- on new InitialDirContext

最佳答案

经过一些调查后,我发现 LDAP 连接没有返回到池中,因为 SearchResult 对象包含对 LdapCtx 对象的引用。

如果你更换

results.next();

随着以下内容

SeachResult ob = results.next();
((Context)ob.getObject()).close();

连接将正确返回到池中。这似乎是默认实现中的错误。

使用 Spring LDAPTemplate 时不存在此问题,因为它在关闭 LdapCtx 的环境中提供了自定义“java.naming.factory.object”,作为构建 SearchResult 过程的一部分。这可以通过将 Spring LDAP 库添加到类路径并将以下内容添加到 InitialContext 来轻松演示

java.naming.factory.object=org.springframework.ldap.core.support.DefaultDirObjectFactory

完成后,SearchResult 持有的对象从 com.sun.jndi.ldap.LdapCtx:com.sun.jndi.ldap.LdapCtx 变为 org.springframework.ldap .core.DirContextAdapterDefaultDirObjectFactory 类负责创建 DirContextAdapter 并负责在将 DirContextAdapter 返回给 DirectoryManager 之前关闭 LdapCtx。这是 DefaultDirObjectFactory

的 finally block
finally {
        // It seems that the object supplied to the obj parameter is a
        // DirContext instance with reference to the same Ldap connection as
        // the original context. Since it is not the same instance (that's
        // the nameCtx parameter) this one really needs to be closed in
        // order to correctly clean up and return the connection to the pool
        // when we're finished with the surrounding operation.
        if (obj instanceof Context) {

            Context ctx = (Context) obj;
            try {
                ctx.close();
            }
            catch (Exception e) {
                // Never mind this
            }

        }
    }

关于java - 为什么 DirContext.close() 不将 LDAP 连接返回到池中?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11955041/

相关文章:

ruby - Ldap gem 在 Rails 中抛出与服务器异常的连接

java - 使用 JNDI 的 LDAP 身份验证

java - 为什么我对 QueueConnectionFactory 的 JNDI 查找返回空值?

mysql - grails production jdbc pooling commons-dbcp vs tomcat 7 jndi

Java 8 格式化日期输入

java - json中的双引号简单

ldap - liferay 7 使​​用 ldap 与 cas 集成

java - JMS QueueConnectionFactory 与 ConnectionFactory

java - 从 ImageIO.read 返回 Null(new ByteArrayInputStream(bs));

java - 升级到 Spring Boot 2 后,ObjectMapper 无法在没有默认构造函数的情况下反序列化