LDAP 接続プールを使用しているときに、ドキュメントにそうではないとclose()
書かれているにもかかわらず、コンテキストを呼び出してもプールに返されないように見えることに気付きました。したがって、アイテムがすでに最大サイズになっているときにプールからアイテムを取得しようとすると、ハングします。
最小限のケースに絞り込むことができました。関連するすべてのオブジェクトを決定論的に呼び出していると思いclose()
ますが、実際にオブジェクトをプールに返すのはガベージ コレクションに依存しているように見えますが、これは予想外です。なぜこうなった?閉じる必要がある他のオブジェクトはありますか?
以下のコード スニペットでは:
- 問題を強調するために、最大プール サイズを人為的に 1 に設定しました。
- プールからを取得し
DirContext
(行 (2))、それをプールに戻そうとし (行 (4))、プールから別のオブジェクトを取得し (行 (6))、同じ返されたオブジェクトを返す必要があります。 - 代わりに、2 番目の要求 (行 (6)) は への内部呼び出しでハングします
Object.wait()
。プールされたオブジェクトが利用可能になるのを待っていると思います。 - (1) をコメントアウトしてプーリングをオフにすると、ハングしません (ただし、プーリングが必要です!)。
- (3) をコメントアウトすると - への呼び出し
SearchResults.next()
- 正常に動作します。 - 行 (5) のコメントを外して、'return to pool' 呼び出しとプールへの新しいオブジェクトの要求の間にガベージ コレクションを強制すると、ハングしません。
行(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