2012-08-14 8 views
7

Me di cuenta al utilizar un grupo de conexiones LDAP que llamar al close() en el contexto no parecía devolverlo al grupo, a pesar de la documentación sayingotherwise. Por lo tanto, cuando intento obtener un elemento del grupo cuando ya está en su tamaño máximo, se cuelga.¿Por qué no DirContext.close() devuelve la conexión LDAP al grupo?

Logré reducirlo a una caja mínima. Aunque creo que llamo al close() en todos los objetos relevantes de manera determinista, parece depender de la recolección de basura para devolver objetos al grupo, lo cual es inesperado. ¿Por qué está pasando esto? ¿Hay algún otro objeto que deba cerrar?

En el siguiente fragmento de código:

  • He configurado artificialmente el tamaño de grupo máximo a 1 para resaltar el problema.
  • Obtengo DirContext del grupo (línea (2)), intento devolverlo al grupo (línea (4)), luego obtengo otro del grupo (línea (6)) que debería devolver el mismo , objeto devuelto
  • en su lugar, la segunda solicitud (línea (6)) se cuelga en alguna llamada interna al Object.wait(). Supongo que está esperando que un objeto agrupado esté disponible.
  • si desactiva la agrupación comentando (1), no se cuelga (¡pero quiero agrupar!).
  • si hago un comentario (3) - una llamada al SearchResults.next() - funciona bien.
  • si elimino el comentario de la línea (5) para forzar la recolección de elementos no utilizados entre la llamada 'devolver a la agrupación' y la solicitud de un nuevo objeto al grupo, no se cuelga.

Como comentar la línea (3) hace que el problema desaparezca, quizás no estoy cerrando correctamente el valor de retorno y mantiene abierta la conexión agrupada. Sin embargo, el método results.next() devuelve SearchResult en este caso, que no tiene el método close y no hay instrucciones en su documentación sobre cómo cerrarlo limpiamente.

El caso de prueba:

@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) 
} 

Con el código tal como es, mi consola muestra:

Create [email protected][ldapad:389] 
Use [email protected] 

Mientras que si fuerzo al CG I, además, veo:

Release [email protected] <-- on GC 
Use [email protected]  <-- on new InitialDirContext 

Respuesta

7

Después de un poco de investigación, descubrí que la conexión LDAP no se devuelve al grupo porque el objeto SearchResult contiene un referencia al objeto LdapCtx.

Si reemplaza

results.next(); 

Con el siguiente

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

La conexión será devuelto correctamente a la piscina. Esto parece un error en la implementación predeterminada.

El problema no existe cuando se utiliza la plantilla Spring LDAP porque proporciona un "java.naming.factory.object" personalizado en el entorno que cierra LdapCtx como parte del proceso de construcción de SearchResult. Esto puede ser fácilmente demostrated mediante la adición de la biblioteca primavera LDAP a la ruta de clases y añadiendo lo siguiente a la InitialContext

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

Cuando se hace esto, el objeto retenido por SearchResult cambia de com.sun.jndi.ldap.LdapCtx: com.sun.jndi.ldap.LdapCtx a org.springframework.ldap.core.DirContextAdapter. El clase DefaultDirObjectFactory es responsable de crear el DirContextAdapter y se encarga de cerrar el LdapCtx antes de devolver el DirContextAdapter a la DirectoryManager. Aquí es el bloque finally de la 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

Gracias! Es frustrante que la documentación indique que para que el proveedor de LDAP administre adecuadamente las conexiones agrupadas, debe ser diligente al invocar Context.close() en contextos que ya no necesita, pero no explica cómo hacerlo de manera significativa. esto tampoco le dice qué objetos provistos pueden ellos mismos hacerse cargo de estos objetos que también necesitan cerrarse. – bacar

+0

Si continúa el ciclo hasta que 'results.next()' devuelve falso, la conexión se cierra automáticamente. – EJP

4

Cambiar el objeto de tener la SearchControlsreturningObjFlag atribuyen falsas. Por lo general, no necesita el objeto en sí, solo su nombre InNamespace y sus atributos. Solo necesita el objeto si va a crear o modificar subcontextos.

+0

Gracias - parece funcionar, y tengo una mejor comprensión de lo que hace la bandera de la página del tutorial de LDAP en [Resultados de la búsqueda] (http://docs.oracle.com/javase/tutorial/jndi/ldap /result.html#OBJ) – bacar

Cuestiones relacionadas