Descripción general de la interfaz de directorio y nombres de Java

1. Introducción

La interfaz de directorio y nombres de Java (JNDI) proporciona un uso coherente de los servicios de directorio y / o nombres como una API de Java. Esta interfaz se puede utilizar para vincular objetos, buscar o consultar objetos, así como para detectar cambios en los mismos objetos.

Si bien el uso de JNDI incluye una lista diversa de servicios de directorio y nombres admitidos, en este tutorial nos centraremos en JDBC mientras exploramos la API de JNDI.

2. Descripción JNDI

Cualquier trabajo con JNDI requiere una comprensión del servicio subyacente , así como una implementación accesible. Por ejemplo, un servicio de conexión de base de datos requiere propiedades específicas y manejo de excepciones.

Sin embargo, la abstracción de JNDI desacopla la configuración de la conexión de la aplicación.

Exploremos el nombre y el contexto , que contienen la funcionalidad principal de JNDI.

2.1. Interfaz de nombre

Name objectName = new CompositeName("java:comp/env/jdbc");

La interfaz de nombre ofrece la posibilidad de gestionar los nombres de los componentes y la sintaxis de los nombres JNDI. El primer token de la cadena representa el contexto global, después de que cada cadena agregada representa el siguiente subcontexto:

Enumeration elements = objectName.getAll(); while(elements.hasMoreElements()) { System.out.println(elements.nextElement()); }

Nuestra salida se ve así:

java:comp env jdbc

Como podemos ver, / es el delimitador de los subcontextos de Nombre . Ahora, agreguemos un subcontexto:

objectName.add("example");

Luego probamos nuestra suma:

assertEquals("example", objectName.get(objectName.size() - 1));

2.2. Interfaz de contexto

El contexto contiene las propiedades del servicio de nombres y directorios . Aquí, usemos un código auxiliar de Spring por conveniencia para construir un contexto :

SimpleNamingContextBuilder builder = new SimpleNamingContextBuilder(); builder.activate();

El SimpleNamingContextBuilder de Spring crea un proveedor JNDI y luego activa el constructor con NamingManager :

JndiTemplate jndiTemplate = new JndiTemplate(); ctx = (InitialContext) jndiTemplate.getContext();

Finalmente, JndiTemplate nos ayuda a acceder al InitialContext .

3. Enlace y búsqueda de objetos JNDI

Ahora que hemos visto cómo usar el nombre y el contexto , usemos JNDI para almacenar una fuente de datos JDBC :

ds = new DriverManagerDataSource("jdbc:h2:mem:mydb");

3.1. Vinculación de objetos JNDI

Como tenemos un contexto, vinculemos el objeto a él:

ctx.bind("java:comp/env/jdbc/datasource", ds);

En general, los servicios deben almacenar una referencia de objeto, datos serializados o atributos en un contexto de directorio. Todo depende de las necesidades de la aplicación.

Tenga en cuenta que usar JNDI de esta manera es menos común. Normalmente, JNDI interactúa con datos que se gestionan fuera del tiempo de ejecución de la aplicación.

Sin embargo, si la aplicación ya puede crear o encontrar su DataSource , podría ser más fácil conectarlo usando Spring. Por el contrario, si algo fuera de los objetos vinculados a nuestra aplicación en JNDI, la aplicación podría consumirlos.

3.2. Búsqueda de objetos JNDI

Busquemos nuestra fuente de datos :

DataSource ds = (DataSource) ctx.lookup("java:comp/env/jdbc/datasource");

Y luego probemos para asegurarnos de que DataSource sea ​​el esperado:

assertNotNull(ds.getConnection());

4. Excepciones comunes de JNDI

Trabajar con JNDI a veces puede resultar en excepciones en tiempo de ejecución. Éstos son algunos de los más comunes.

4.1. NameNotFoundException

ctx.lookup("badJndiName");

Dado que este nombre no está vinculado en este contexto, vemos este seguimiento de pila:

javax.naming.NameNotFoundException: Name [badJndiName] not bound; 0 bindings: [] at org.springframework.mock.jndi.SimpleNamingContext.lookup(SimpleNamingContext.java:140) at java.naming/javax.naming.InitialContext.lookup(InitialContext.java:409)

Debemos tener en cuenta que el seguimiento de la pila contiene todos los objetos enlazados, lo que es útil para rastrear por qué ocurrió la excepción.

4.2. NoInitialContextException

Cualquier interacción con InitialContext puede generar NoInitialContextException :

assertThrows(NoInitialContextException.class, () -> { JndiTemplate jndiTemplate = new JndiTemplate(); InitialContext ctx = (InitialContext) jndiTemplate.getContext(); ctx.lookup("java:comp/env/jdbc/datasource"); }).printStackTrace();

Debemos notar que este uso de JNDI es válido, como lo usamos anteriormente. Sin embargo, esta vez no hay un proveedor de contexto JNDI y se lanzará una excepción:

javax.naming.NoInitialContextException: Need to specify class name in environment or system property, or in an application resource file: java.naming.factory.initial at java.naming/javax.naming.spi.NamingManager.getInitialContext(NamingManager.java:685)

5. Papel de JNDI en la arquitectura de aplicaciones moderna

While JNDI plays less of a role in lightweight, containerized Java applications such as Spring Boot, there are other uses. Three Java technologies that still use JNDI are JDBC, EJB, and JMS. All have a wide array of uses across Java enterprise applications.

For example, a separate DevOps team may manage environment variables such as username and password for a sensitive database connection in all environments. A JNDI resource can be created in the web application container, with JNDI used as a layer of consistent abstraction that works in all environments.

This setup allows developers to create and control a local definition for development purposes while connecting to sensitive resources in a production environment through the same JNDI name.

6. Conclusion

En este tutorial, vimos cómo conectar, vincular y buscar un objeto utilizando la interfaz de directorio y nombres de Java. También analizamos las excepciones comunes lanzadas por JNDI.

Finalmente, analizamos cómo encaja JNDI en la arquitectura de aplicaciones moderna.

Como siempre, el código está disponible en GitHub.