Zauważyłem podczas korzystania z puli połączeń LDAP, że wywołanie close()
w kontekście nie wydaje się zwrócić go do puli, pomimo dokumentacji sayingotherwise. Tak więc, gdy próbuję uzyskać element z puli, gdy jest już w jego maksymalnym rozmiarze, zawiesza się.Dlaczego metoda DirContext.close() nie zwraca połączenia LDAP do puli?
Udało mi się zawęzić to do minimalnej wielkości. Chociaż uważam, że wywołuję deterministycznie wszystkie odpowiednie obiekty, wydaje się polegać na zbieraniu śmieci, aby rzeczywiście zwrócić obiekty do puli, co jest nieoczekiwane. Dlaczego tak się dzieje? Czy jest jakiś inny przedmiot, który powinienem zamknąć?
W poniższym fragmencie kodu:
- ja sztucznie ustawić maksymalną wielkość puli 1, aby podkreślić problem.
- Dostaję
DirContext
z puli (linia (2)), próbuję zwrócić ją do puli (linia (4)), a następnie pobrać inną z puli (linia (6)), która powinna zwrócić to samo , zwrócony obiekt. - zamiast tego drugie żądanie (linia (6)) zawiesza się podczas wewnętrznego połączenia z numerem
Object.wait()
. Zgaduję, że czeka na udostępnienie połączonego obiektu. - jeśli wyłączyć pulę przez komentowanie (1), nie zawiesi się (ale chcę się połączyć!).
- jeśli skomentuję (3) - połączenie z
SearchResults.next()
- działa poprawnie. - Jeśli odkomentuję wiersz (5) w celu wymuszenia zbierania śmieci między wywołaniem "powrotu do puli" i zażądaniem nowego obiektu do puli, nie zostanie on zawieszony.
Od momentu skomentowania linii wyjściowej (3) problem znika, być może nie zamykam poprawnie wartości zwracanej i utrzymuję otwarte połączenie. Jednak w tym przypadku metoda results.next()
zwraca w tym przypadku kod SearchResult
, który nie ma sposobu, w jaki sposób zamknąć go w czystej dokumentacji.
Przypadek testowy:
@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)
}
Z kodu jak to jest, moja konsola pokazuje:
Create [email protected][ldapad:389]
Use [email protected]
natomiast jeśli wymusić GC I dodatkowo patrz:
Release [email protected] <-- on GC
Use [email protected] <-- on new InitialDirContext
Dzięki! To frustrujące, że dokumentacja mówi, że aby dostawca usług LDAP prawidłowo zarządzał połączonymi połączeniami, musisz być ostrożny w zakresie wywoływania kontekstu Context.close() w kontekstach, których już nie potrzebujesz, ale nie wyjaśnia, w jaki sposób możesz sensownie zrobić to nie powie ci, które obiekty mogą same obejmować te obiekty, które również wymagają zamknięcia. – bacar
Jeśli kontynuujesz pętlę, dopóki 'results.next()' nie zwróci false, połączenie zostanie automatycznie zamknięte. – EJP