Nomeação de Java e visão geral da interface de diretório
1. Introdução
A JNDI (Java Naming and Directory Interface) fornece uso consistente de serviços de nomes e/ou diretórios como uma API Java. Essa interface pode ser usada para ligar objetos, procurar ou consultar objetos, além de detectar alterações nos mesmos objetos. .
Embora o uso de JNDI inclua uma lista diversificada de serviço de nomes e serviços de diretório suportados, neste tutorial, focaremos no JDBC enquanto explora a API da JNDI.
2. Descrição JNDI
Qualquer trabalho com JNDI requer uma compreensão do serviço subjacente , bem como uma implementação acessível. Por exemplo, um serviço de conexão com o banco de dados exige propriedades específicas e tratamento de exceções.
No entanto, a abstração da JNDI desacopla a configuração da conexão do aplicativo.
Vamos explorar Name e Context, que contêm a funcionalidade principal do JNDI.
2.1. Interface Name
Name objectName = new CompositeName("java:comp/env/jdbc");
A interface Name fornece a capacidade de gerenciar os nomes e sintaxe dos componentes para nomes JNDI. O primeiro token da string representa o contexto global, depois que cada string adicionada representa o próximo sub-contexto:
Enumeration<String> elements = objectName.getAll();
while(elements.hasMoreElements()) {
System.out.println(elements.nextElement());
}
Nossa saída se parece com:
java:comp
env
jdbc
Como podemos ver, / é o delimitador dos sub-contextos Name. Agora, vamos adicionar um sub-contexto:
objectName.add("example");
Em seguida, testamos nossa adição:
assertEquals("example", objectName.get(objectName.size() - 1));
2.2. Interface Context
Context contém as propriedades do serviço de nomes e diretórios . Aqui, vamos usar algum código auxiliar do Spring por conveniência para criar um Context:
SimpleNamingContextBuilder builder = new SimpleNamingContextBuilder();
builder.activate();
O SimpleNamingContextBuilder do Spring cria um provedor JNDI e, em seguida, ativa o construtor com o NamingManager:
JndiTemplate jndiTemplate = new JndiTemplate();
ctx = (InitialContext) jndiTemplate.getContext();
Finalmente, JndiTemplate nos ajuda a acessar o InitialContext.
3. Ligação e Pesquisa de Objetos JNDI
Agora que vimos como usar Name e Context, vamos usar o JNDI para armazenar um JDBC DataSource:
ds = new DriverManagerDataSource("jdbc:h2:mem:mydb");
3.1. Vinculando objetos JNDI
Como temos um contexto, vamos vincular o objeto a ele:
ctx.bind("java:comp/env/jdbc/datasource", ds);
Em geral, os serviços devem armazenar uma referência de objeto, dados serializados ou atributos em um contexto de diretório. Tudo depende das necessidades do aplicativo.
Observe que usar JNDI dessa maneira é menos comum. Normalmente, o JNDI faz interface com dados gerenciados fora do tempo de execução do aplicativo.
No entanto, se o aplicativo já puder criar ou encontrar seu DataSource, poderá ser mais fácil conectar isso usando o Spring. Por outro lado, se algo fora do nosso aplicativo vincular objetos no JNDI, o aplicativo poderá consumi-los.
3.2. Procurando objetos JNDI
Vamos procurar nosso DataSource:
DataSource ds = (DataSource) ctx.lookup("java:comp/env/jdbc/datasource");
E então vamos testar para garantir que DataSource seja o esperado:
assertNotNull(ds.getConnection());
4. Exceções comuns de JNDI
Trabalhar com JNDI às vezes pode resultar em exceções de tempo de execução. Aqui estão alguns comuns.
4.1. NameNotFoundException
ctx.lookup("badJndiName");
Como esse nome não está vinculado nesse contexto, vemos esse rastreamento de pilha:
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)
Devemos observar que o rastreamento da pilha contém todos os objetos vinculados, o que é útil para rastrear por que a exceção ocorreu.
4.2. NoInitialContextException
Qualquer interação com o InitialContext pode gerar NoInitialContextException:
assertThrows(NoInitialContextException.class, () -> {
JndiTemplate jndiTemplate = new JndiTemplate();
InitialContext ctx = (InitialContext) jndiTemplate.getContext();
ctx.lookup("java:comp/env/jdbc/datasource");
}).printStackTrace();
Devemos observar que esse uso do JNDI é válido, como o usamos anteriormente. No entanto, desta vez, não há provedor de contexto JNDI e uma exceção será lançada:
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 do JNDI na arquitetura moderna de aplicativos
Embora o JNDI desempenhe menos papel em aplicativos Java leves e em contêineres , como o Spring Boot, existem outros usos. Três tecnologias Java que ainda usam JNDI são https://www..com/java-jdbc [JDBC], https://www..com/ejb-intro [EJB] e https://www..com/spring-jms [JMS]. Todos têm uma ampla variedade de usos em aplicativos corporativos Java.
Por exemplo, uma equipe separada do DevOps pode gerenciar variáveis de ambiente, como nome de usuário e senha, para uma conexão sensível ao banco de dados em todos os ambientes. Um recurso JNDI pode ser criado no contêiner de aplicativo da web, com o JNDI usado como uma camada de abstração consistente que funciona em todos os ambientes.
Essa configuração permite que os desenvolvedores criem e controlem uma definição local para fins de desenvolvimento enquanto se conectam a recursos confidenciais em um ambiente de produção com o mesmo nome JNDI.
6. Conclusão
*Neste tutorial, vimos como conectar, vincular e procurar um objeto usando a interface Java Naming and Directory Interface.* Também examinamos as exceções comuns lançadas pelo JNDI.
Finalmente, vimos como o JNDI se encaixa na arquitetura moderna de aplicativos.
Como sempre, o código está disponível no GitHub.