Kamusta! Ngayon ay ipapakilala namin sa iyo ang JNDI. Alamin natin kung ano ito, bakit ito kailangan, paano ito gumagana, kung paano natin ito magagawa. At pagkatapos ay magsusulat kami ng pagsubok sa yunit ng Spring Boot, kung saan lalaruin namin ang mismong JNDI na ito.
Panimula. Mga Serbisyo sa Pangalan at Direktoryo
Bago sumabak sa JNDI, unawain natin kung ano ang mga serbisyo sa pagpapangalan at direktoryo. Ang pinaka-halatang halimbawa ng naturang serbisyo ay ang file system sa anumang PC, laptop o smartphone. Ang file system ay namamahala ng (kakaiba) na mga file. Ang mga file sa naturang mga sistema ay pinagsama-sama sa isang istraktura ng puno. Ang bawat file ay may natatanging buong pangalan, halimbawa: C:\windows\notepad.exe. Pakitandaan: ang buong pangalan ng file ay ang landas mula sa ilang root point (drive C) patungo sa file mismo (notepad.exe). Ang mga intermediate node sa naturang chain ay mga direktoryo (windows directory). Ang mga file sa loob ng mga direktoryo ay may mga katangian. Halimbawa, "Nakatago", "Read-Only", atbp. Ang isang detalyadong paglalarawan ng isang simpleng bagay bilang isang file system ay makakatulong upang mas maunawaan ang kahulugan ng pagbibigay ng pangalan at mga serbisyo ng direktoryo. Kaya, ang isang pangalan at serbisyo ng direktoryo ay isang sistema na namamahala sa pagmamapa ng maraming mga pangalan sa maraming mga bagay. Sa aming file system, nakikipag-ugnayan kami sa mga pangalan ng file na nagtatago ng mga bagay—ang mga file mismo sa iba't ibang mga format. Sa serbisyo ng pagbibigay ng pangalan at direktoryo, ang mga pinangalanang bagay ay isinaayos sa isang istraktura ng puno. At ang mga object ng direktoryo ay may mga katangian. Ang isa pang halimbawa ng isang pangalan at serbisyo ng direktoryo ay DNS (Domain Name System). Pinamamahalaan ng system na ito ang pagmamapa sa pagitan ng mga domain name na nababasa ng tao (halimbawa, https://javarush.com/) at mga IP address na nababasa ng machine (halimbawa, 18.196.51.113). Bukod sa DNS at mga file system, marami pang ibang serbisyo, gaya ng:- Lightweight Directory Access Protocol (LDAP) ;
- CORBA pagpapangalan serbisyo ;
- Network Information Service (NIS) ;
- At iba pa.
JNDI
Ang JNDI, o Java Naming and Directory Interface, ay isang Java API para sa pag-access ng mga serbisyo sa pagpapangalan at direktoryo. Ang JNDI ay isang API na nagbibigay ng pare-parehong mekanismo para sa isang Java program na makipag-ugnayan sa iba't ibang mga serbisyo sa pagbibigay ng pangalan at direktoryo. Sa ilalim ng hood, ang pagsasama sa pagitan ng JNDI at anumang ibinigay na serbisyo ay nagagawa gamit ang isang Service Provider Interface (SPI). Ang SPI ay nagbibigay-daan sa iba't ibang mga serbisyo ng pagbibigay ng pangalan at direktoryo na malinaw na konektado, na nagpapahintulot sa isang Java application na gamitin ang JNDI API upang ma-access ang mga konektadong serbisyo. Ang figure sa ibaba ay naglalarawan ng JNDI architecture:Pinagmulan: Oracle Java Tutorials
JNDI. Kahulugan sa simpleng salita
Ang pangunahing tanong ay: bakit kailangan natin ng JNDI? Kailangan ang JNDI upang makakuha tayo ng Java object mula sa ilang “Registration” ng mga object mula sa Java code sa pangalan ng object na nakatali sa object na ito. Hatiin natin ang pahayag sa itaas sa mga theses upang ang kasaganaan ng paulit-ulit na mga salita ay hindi malito sa atin:- Sa huli kailangan nating kumuha ng Java object.
- Makukuha namin ang bagay na ito mula sa ilang registry.
- Mayroong isang grupo ng mga bagay sa registry na ito.
- Ang bawat bagay sa registry na ito ay may natatanging pangalan.
- Upang makakuha ng isang bagay mula sa pagpapatala, dapat kaming magpasa ng isang pangalan sa aming kahilingan. Para bang sinasabing: "Pakibigay sa akin kung ano ang mayroon ka sa ilalim ng ganoon at ganoong pangalan."
- Hindi lamang natin mababasa ang mga bagay sa pamamagitan ng kanilang pangalan mula sa registry, ngunit i-save din ang mga bagay sa registry na ito sa ilalim ng ilang mga pangalan (sa anumang paraan napupunta sila doon).
JNDI API
Ang JNDI ay ibinibigay sa loob ng platform ng Java SE. Upang magamit ang JNDI, dapat kang mag-import ng mga klase ng JNDI, pati na rin ang isa o higit pang mga service provider upang ma-access ang mga serbisyo ng pagpapangalan at direktoryo. Kasama sa JDK ang mga service provider para sa mga sumusunod na serbisyo:- Lightweight Directory Access Protocol (LDAP);
- Common Object Request Broker Architecture (CORBA);
- Serbisyo ng pangalan ng Common Object Services (COS);
- Java Remote Method Invocation (RMI) Registry;
- Serbisyo ng Domain Name (DNS).
- javax.naming;
- javax.naming.directory;
- javax.naming.ldap;
- javax.naming.event;
- javax.naming.spi.
Pangalan ng Interface
Binibigyang-daan ka ng interface ng Pangalan na kontrolin ang mga pangalan ng bahagi pati na rin ang syntax ng pagpapangalan ng JNDI. Sa JNDI, ang lahat ng mga pagpapatakbo ng pangalan at direktoryo ay isinagawa kaugnay ng konteksto. Walang ganap na mga ugat. Samakatuwid, ang JNDI ay tumutukoy sa isang InitialContext, na nagbibigay ng panimulang punto para sa pagpapangalan at direktoryo. Kapag na-access na ang paunang konteksto, maaari itong magamit upang maghanap ng mga bagay at iba pang konteksto.Name objectName = new CompositeName("java:comp/env/jdbc");
Sa code sa itaas, tinukoy namin ang ilang pangalan kung saan matatagpuan ang ilang bagay (maaaring hindi ito matatagpuan, ngunit umaasa kami dito). Ang aming huling layunin ay upang makakuha ng isang sanggunian sa bagay na ito at gamitin ito sa aming programa. Kaya, ang pangalan ay binubuo ng ilang bahagi (o mga token), na pinaghihiwalay ng isang slash. Ang ganitong mga token ay tinatawag na mga konteksto. Ang pinakauna ay simpleng konteksto, lahat ng kasunod ay sub-context (mula rito ay tinutukoy bilang subcontext). Mas madaling maunawaan ang mga konteksto kung iisipin mo ang mga ito bilang kahalintulad sa mga direktoryo o direktoryo, o mga regular na folder lamang. Ang root context ay ang root folder. Ang subcontext ay isang subfolder. Makikita natin ang lahat ng bahagi (konteksto at subcontext) ng isang ibinigay na pangalan sa pamamagitan ng pagpapatakbo ng sumusunod na code:
Enumeration<String> elements = objectName.getAll();
while(elements.hasMoreElements()) {
System.out.println(elements.nextElement());
}
Ang magiging output ay ang mga sumusunod:
java:comp
env
jdbc
Ang output ay nagpapakita na ang mga token sa pangalan ay pinaghihiwalay mula sa isa't isa sa pamamagitan ng isang slash (gayunpaman, binanggit namin ito). Ang bawat token ng pangalan ay may sariling index. Ang pag-index ng token ay nagsisimula sa 0. Ang konteksto ng ugat ay may index zero, ang susunod na konteksto ay may index 1, ang susunod na 2, atbp. Makukuha natin ang pangalan ng subcontext sa pamamagitan ng index nito:
System.out.println(objectName.get(1)); // -> env
Maaari din kaming magdagdag ng mga karagdagang token (sa dulo man o sa isang partikular na lokasyon sa index):
objectName.add("sub-context"); // Добавит sub-context в конец
objectName.add(0, "context"); // Добавит context в налачо
Ang kumpletong listahan ng mga pamamaraan ay matatagpuan sa opisyal na dokumentasyon .
Konteksto ng Interface
Ang interface na ito ay naglalaman ng isang hanay ng mga constant para sa pagsisimula ng isang konteksto, pati na rin ang isang hanay ng mga pamamaraan para sa paglikha at pagtanggal ng mga konteksto, pagbubuklod ng mga bagay sa isang pangalan, at paghahanap at pagkuha ng mga bagay. Tingnan natin ang ilan sa mga operasyon na ginagawa gamit ang interface na ito. Ang pinakakaraniwang aksyon ay ang paghahanap para sa isang bagay ayon sa pangalan. Ginagawa ito gamit ang mga pamamaraan:Object lookup(String name)
Object lookup(Name name)
bind
:
void bind(Name name, Object obj)
void bind(String name, Object obj)
Object
Ang kabaligtaran na operasyon ng pagbubuklod - pag-alis ng isang bagay mula sa isang pangalan, ay isinasagawa gamit ang mga pamamaraan unbind
:
void unbind(Name name)
void unbind(String name)
InitialContext
InitialContext
ay isang klase na kumakatawan sa root element ng JNDI tree at nagpapatupad ng Context
. Kailangan mong maghanap ng mga bagay ayon sa pangalan sa loob ng puno ng JNDI na may kaugnayan sa isang partikular na node. Ang root node ng puno ay maaaring magsilbi bilang isang node InitialContext
. Ang isang karaniwang kaso ng paggamit para sa JNDI ay:
- Kunin ang
InitialContext
. - Gamitin
InitialContext
upang kunin ang mga bagay ayon sa pangalan mula sa puno ng JNDI.
InitialContext
. Ang lahat ay nakasalalay sa kapaligiran kung saan matatagpuan ang Java program. Halimbawa, kung ang isang Java program at isang JNDI tree ay tumatakbo sa loob ng parehong application server, ito ay InitialContext
medyo simple upang makakuha ng:
InitialContext context = new InitialContext();
Kung hindi ito ang kaso, ang pagkuha ng konteksto ay nagiging mas mahirap. Minsan kinakailangan na magpasa ng isang listahan ng mga katangian ng kapaligiran upang masimulan ang konteksto:
Hashtable env = new Hashtable();
env.put(Context.INITIAL_CONTEXT_FACTORY,
"com.sun.jndi.fscontext.RefFSContextFactory");
Context ctx = new InitialContext(env);
Ang halimbawa sa itaas ay nagpapakita ng isa sa mga posibleng paraan upang simulan ang isang konteksto at hindi nagdadala ng anumang iba pang semantic load. Hindi na kailangang sumisid sa code nang detalyado.
Isang halimbawa ng paggamit ng JNDI sa loob ng isang SpringBoot unit test
Sa itaas, sinabi namin na para makipag-ugnayan ang JNDI sa serbisyo ng pagbibigay ng pangalan at direktoryo, kinakailangang magkaroon ng SPI (Service Provider Interface) sa kamay, sa tulong ng kung aling integrasyon sa pagitan ng Java at serbisyo ng pagbibigay ng pangalan ang isasagawa. Ang karaniwang JDK ay kasama ng maraming iba't ibang SPI (inilista namin ang mga ito sa itaas), bawat isa ay hindi gaanong interesado para sa mga layunin ng pagpapakita. Ang pagtataas ng JNDI at Java application sa loob ng isang lalagyan ay medyo kawili-wili. Gayunpaman, ang may-akda ng artikulong ito ay isang tamad na tao, kaya upang ipakita kung paano gumagana ang JNDI, pinili niya ang landas ng hindi bababa sa pagtutol: patakbuhin ang JNDI sa loob ng isang pagsubok sa yunit ng aplikasyon ng SpringBoot at i-access ang konteksto ng JNDI gamit ang isang maliit na hack mula sa Spring Framework. Kaya, ang aming plano:- Sumulat tayo ng walang laman na proyekto ng Spring Boot.
- Gumawa tayo ng unit test sa loob ng proyektong ito.
- Sa loob ng pagsubok ipapakita namin ang pakikipagtulungan sa JNDI:
- makakuha ng access sa konteksto;
- itali (bind) ang ilang bagay sa ilalim ng ilang pangalan sa JNDI;
- makuha ang bagay sa pamamagitan ng pangalan nito (lookup);
- Suriin natin na ang bagay ay hindi null.
- JDBC API;
- H2 DDatabase.
SimpleNamingContextBuilder
. Idinisenyo ang klase na ito para madaling itaas ang JNDI sa loob ng mga unit test o stand-alone na application. Isulat natin ang code upang makuha ang konteksto:
final SimpleNamingContextBuilder simpleNamingContextBuilder
= new SimpleNamingContextBuilder();
simpleNamingContextBuilder.activate();
final InitialContext context = new InitialContext();
Ang unang dalawang linya ng code ay magbibigay-daan sa amin na madaling masimulan ang konteksto ng JNDI sa ibang pagkakataon. Kung wala ang mga ito, InitialContext
isang pagbubukod ang itatapon kapag lumilikha ng isang halimbawa: javax.naming.NoInitialContextException
. Disclaimer. Ang klase SimpleNamingContextBuilder
ay isang Hindi na ginagamit na klase. At ang halimbawang ito ay inilaan upang ipakita kung paano ka makakapagtrabaho sa JNDI. Ang mga ito ay hindi pinakamahusay na kagawian para sa paggamit ng JNDI sa loob ng mga pagsubok sa yunit. Ito ay masasabing isang saklay para sa pagbuo ng konteksto at pagpapakita ng pagbubuklod at pagkuha ng mga bagay mula sa JNDI. Kapag nakatanggap ng konteksto, maaari tayong mag-extract ng mga bagay mula dito o maghanap ng mga bagay sa konteksto. Wala pang mga bagay sa JNDI, kaya lohikal na maglagay ng isang bagay doon. Halimbawa, DriverManagerDataSource
:
context.bind("java:comp/env/jdbc/datasource", new DriverManagerDataSource("jdbc:h2:mem:mydb"));
Sa linyang ito, itinali namin ang object ng klase DriverManagerDataSource
sa pangalan java:comp/env/jdbc/datasource
. Susunod, maaari nating makuha ang bagay mula sa konteksto ayon sa pangalan. Wala kaming pagpipilian kundi kunin ang bagay na inilagay lang namin, dahil walang ibang mga bagay sa konteksto =(
final DataSource ds = (DataSource) context.lookup("java:comp/env/jdbc/datasource");
Ngayon suriin natin kung ang aming DataSource ay may koneksyon (koneksyon, koneksyon o koneksyon ay isang klase ng Java na idinisenyo upang gumana sa isang database):
assert ds.getConnection() != null;
System.out.println(ds.getConnection());
Kung ginawa namin ang lahat ng tama, ang output ay magiging katulad nito:
conn1: url=jdbc:h2:mem:mydb user=
Ito ay nagkakahalaga na sabihin na ang ilang mga linya ng code ay maaaring magtapon ng mga pagbubukod. Ang mga sumusunod na linya ay itinapon javax.naming.NamingException
:
simpleNamingContextBuilder.activate()
new InitialContext()
context.bind(...)
context.lookup(...)
DataSource
maaari itong itapon java.sql.SQLException
. Sa pagsasaalang-alang na ito, kinakailangan na isagawa ang code sa loob ng isang bloke try-catch
, o ipahiwatig sa lagda ng yunit ng pagsubok na maaari itong magtapon ng mga pagbubukod. Narito ang kumpletong code ng klase ng pagsubok:
@SpringBootTest
class JndiExampleApplicationTests {
@Test
void contextLoads() {
try {
final SimpleNamingContextBuilder simpleNamingContextBuilder
= new SimpleNamingContextBuilder();
simpleNamingContextBuilder.activate();
final InitialContext context = new InitialContext();
context.bind("java:comp/env/jdbc/datasource", new DriverManagerDataSource("jdbc:h2:mem:mydb"));
final DataSource ds = (DataSource) context.lookup("java:comp/env/jdbc/datasource");
assert ds.getConnection() != null;
System.out.println(ds.getConnection());
} catch (SQLException | NamingException e) {
e.printStackTrace();
}
}
}
Pagkatapos patakbuhin ang pagsubok, makikita mo ang mga sumusunod na log:
o.s.m.jndi.SimpleNamingContextBuilder : Activating simple JNDI environment
o.s.mock.jndi.SimpleNamingContext : Static JNDI binding: [java:comp/env/jdbc/datasource] = [org.springframework.jdbc.datasource.DriverManagerDataSource@4925f4f5]
conn1: url=jdbc:h2:mem:mydb user=
GO TO FULL VERSION