2012-08-14 14 views
7

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 

Odpowiedz

7

Po pewnym dochodzeniu odkryłem, że połączenie LDAP nie jest zwracane do puli, ponieważ obiekt SearchResult zawiera odniesienie do obiektu LdapCtx.

Jeśli zastąpić

results.next(); 

z następującymi

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

połączenie zostanie prawidłowo zwrócony do puli. Wygląda to jak błąd w domyślnej implementacji.

Problem nie istnieje podczas używania Spring LDAPTemplate, ponieważ zapewnia niestandardowy "java.naming.factory.object" w środowisku, które zamyka LdapCtx w ramach procesu konstruowania SearchResult. Można to łatwo demostrated dodając bibliotekę Wiosna LDAP na ścieżce klas i dodanie następujących do InitialContext

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

Po wykonaniu tej czynności obiekt przechowywany przez parametr SearchResult zmienia się z com.sun.jndi.ldap.LdapCtx: com.sun.jndi.ldap.LdapCtx na org.springframework.ldap.core.DirContextAdapter. Klasa DefaultDirObjectFactory jest odpowiedzialna za utworzenie obiektu DirContextAdapter i dba o zamknięcie LdapCtx przed zwróceniem DirContextAdapter do DirectoryManager. Oto wreszcie blokować z DefaultDirObjectFactory

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 
      } 

     } 
    } 
+1

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

+0

Jeśli kontynuujesz pętlę, dopóki 'results.next()' nie zwróci false, połączenie zostanie automatycznie zamknięte. – EJP

4

Zmień swój obiekt SearchControls mieć returningObjFlag przypisują fałszywe. Zwykle nie potrzebujesz samego obiektu, tylko jego nazwęNazwaName i jej atrybuty. Potrzebujesz tylko samego obiektu, jeśli zamierzasz tworzyć lub modyfikować podkonteksty.

+0

Dzięki - to działa, a ja lepiej rozumiem działanie flagi na stronie samouczka LDAP w [Wyniki wyszukiwania] (http://docs.oracle.com/javase/tutorial/jndi/ldap /result.html#OBJ) – bacar

Powiązane problemy