我注意到在使用 LDAP 连接池时,尽管文档另有说明,close()
但调用上下文似乎并未将其返回到池中。因此,当我尝试从池中获取已达到最大大小的项目时,它会挂起。
我设法将其缩小到最小的情况。尽管我相信我close()
确定性地调用了所有相关对象,但它似乎依赖垃圾收集来实际将对象返回到池中,这是出乎意料的。为什么会这样?我应该关闭其他对象吗?
在下面的代码片段中:
- 我人为地将最大池大小设置为 1 以突出问题。
- 我
DirContext
从池中获取一个(第 (2) 行),尝试将其返回到池中(第 (4) 行),然后从池中获取另一个(第 (6) 行),它应该返回相同的返回对象。 - 相反,第二个请求(第 (6) 行)挂在对
Object.wait()
. 我猜它正在等待一个池化对象变得可用。 - 如果通过注释掉(1)来关闭池,它不会挂起(但我想要池!)。
- 如果我注释掉 (3) - 调用
SearchResults.next()
- 它工作正常。 - 如果我取消注释第 (5) 行以强制在“返回池”调用和向池请求新对象之间进行垃圾收集,它不会挂起。
由于注释掉第 (3) 行会使问题消失,也许我没有正确关闭它的返回值,并且它使池连接保持打开状态。但是,在这种情况下,该方法results.next()
返回 a 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