Panoramica dei nomi Java e dell'interfaccia directory

1. Introduzione

Java Naming and Directory Interface (JNDI) fornisce un uso coerente dei servizi di denominazione e / o directory come API Java. Questa interfaccia può essere utilizzata per collegare oggetti, cercare o interrogare oggetti, nonché rilevare modifiche sugli stessi oggetti.

Sebbene l'utilizzo di JNDI includa un elenco diversificato di servizi di denominazione e directory supportati, in questo tutorial ci concentreremo su JDBC durante l'esplorazione dell'API di JNDI.

2. Descrizione JNDI

Qualsiasi lavoro con JNDI richiede una comprensione del servizio sottostante e un'implementazione accessibile. Ad esempio, un servizio di connessione al database richiede proprietà specifiche e gestione delle eccezioni.

Tuttavia, l'astrazione di JNDI separa la configurazione della connessione dall'applicazione.

Esploriamo Name e Context , che contengono le funzionalità principali di JNDI.

2.1. Interfaccia nome

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

L' interfaccia Nome fornisce la capacità di gestire i nomi dei componenti e la sintassi per i nomi JNDI. Il primo token della stringa rappresenta il contesto globale, dopodiché ogni stringa aggiunta rappresenta il successivo sottocontesto:

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

Il nostro output è simile a:

java:comp env jdbc

Come possiamo vedere, / è il delimitatore per i sottocontesti Name . Ora aggiungiamo un sottocontesto:

objectName.add("example");

Quindi testiamo la nostra aggiunta:

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

2.2. Interfaccia contestuale

Il contesto contiene le proprietà per il servizio di denominazione e directory . Qui, usiamo un po 'di codice di supporto da Spring per comodità per costruire un contesto :

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

SimpleNamingContextBuilder di Spring crea un provider JNDI e quindi attiva il builder con NamingManager :

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

Infine, JndiTemplate ci aiuta ad accedere a InitialContext .

3. Binding e ricerca di oggetti JNDI

Ora che abbiamo visto come utilizzare Nome e contesto , usiamo JNDI per memorizzare un'origine dati JDBC :

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

3.1. Binding di oggetti JNDI

Dato che abbiamo un contesto, leghiamo l'oggetto ad esso:

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

In generale, i servizi dovrebbero memorizzare un riferimento a un oggetto, dati serializzati o attributi in un contesto di directory. Tutto dipende dalle esigenze dell'applicazione.

Notare che l'utilizzo di JNDI in questo modo è meno comune. In genere, JNDI si interfaccia con i dati gestiti al di fuori del runtime dell'applicazione.

Tuttavia, se l'applicazione è già in grado di creare o trovare il proprio DataSource , potrebbe essere più semplice cablarlo utilizzando Spring. Al contrario, se qualcosa al di fuori della nostra applicazione ha associato gli oggetti in JNDI, l'applicazione potrebbe consumarli.

3.2. Ricerca di oggetti JNDI

Diamo un'occhiata alla nostra DataSource :

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

E poi testiamo per assicurarci che DataSource sia come previsto:

assertNotNull(ds.getConnection());

4. Eccezioni JNDI comuni

Lavorare con JNDI a volte può provocare eccezioni di runtime. Eccone alcuni comuni.

4.1. NameNotFoundException

ctx.lookup("badJndiName");

Since this name is not bound in this context, we see this stack trace:

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)

We should note that the stack trace contains all objects bound, which is useful for tracking down why the exception occurred.

4.2. NoInitialContextException

Any interaction with the InitialContext can throw NoInitialContextException:

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

We should note that this use of JNDI is valid, as we used it earlier. However, this time there is no JNDI context provider, and an exception will be thrown:

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. Role of JNDI in Modern Application Architecture

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

In questo tutorial, abbiamo visto la connessione, l'associazione e la ricerca di un oggetto utilizzando Java Naming and Directory Interface. Abbiamo anche esaminato le eccezioni comuni lanciate da JNDI.

Infine, abbiamo esaminato come JNDI si inserisce nell'architettura delle applicazioni moderne.

Come sempre, il codice è disponibile su GitHub.