JavaRush /Java Blog /Random-ID /Menggunakan JNDI di Java
Анзор Кармов
Level 31
Санкт-Петербург

Menggunakan JNDI di Java

Dipublikasikan di grup Random-ID
Halo! Hari ini kami akan memperkenalkan Anda kepada JNDI. Mari kita cari tahu apa itu, mengapa dibutuhkan, cara kerjanya, bagaimana kita bisa mengatasinya. Dan kemudian kita akan menulis tes unit Spring Boot, di dalamnya kita akan bermain dengan JNDI ini. Menggunakan JNDI di Java - 1

Perkenalan. Layanan Penamaan dan Direktori

Sebelum mendalami JNDI, mari kita pahami apa itu layanan penamaan dan direktori. Contoh paling nyata dari layanan semacam itu adalah sistem file di PC, laptop, atau ponsel cerdas mana pun. Sistem file mengelola (anehnya) file. File dalam sistem tersebut dikelompokkan dalam struktur pohon. Setiap file memiliki nama lengkap yang unik, misalnya: C:\windows\notepad.exe. Harap diperhatikan: nama lengkap file adalah jalur dari beberapa titik root (drive C) ke file itu sendiri (notepad.exe). Node perantara dalam rantai tersebut adalah direktori (direktori windows). File di dalam direktori memiliki atribut. Misalnya, “Tersembunyi”, “Hanya-Baca”, dll. Penjelasan rinci tentang hal sederhana seperti sistem file akan membantu untuk lebih memahami definisi penamaan dan layanan direktori. Jadi, layanan nama dan direktori adalah sistem yang mengelola pemetaan banyak nama ke banyak objek. Dalam sistem file, kita berinteraksi dengan nama file yang menyembunyikan objek—file itu sendiri dalam berbagai format. Dalam layanan penamaan dan direktori, objek yang diberi nama disusun ke dalam struktur pohon. Dan objek direktori memiliki atribut. Contoh lain dari layanan nama dan direktori adalah DNS (Domain Name System). Sistem ini mengelola pemetaan antara nama domain yang dapat dibaca manusia (misalnya https://javarush.com/) dan alamat IP yang dapat dibaca mesin (misalnya 18.196.51.113). Selain DNS dan sistem file, masih banyak layanan lainnya, seperti:

JNDI

JNDI, atau Java Naming and Directory Interface, adalah Java API untuk mengakses layanan penamaan dan direktori. JNDI adalah API yang menyediakan mekanisme seragam bagi program Java untuk berinteraksi dengan berbagai layanan penamaan dan direktori. Integrasi antara JNDI dan layanan apa pun dilakukan menggunakan Antarmuka Penyedia Layanan (SPI). SPI memungkinkan berbagai layanan penamaan dan direktori terhubung secara transparan, memungkinkan aplikasi Java menggunakan API JNDI untuk mengakses layanan yang terhubung. Gambar di bawah mengilustrasikan arsitektur JNDI: Menggunakan JNDI di Java - 2

Sumber: Tutorial Oracle Java

JNDI. Artinya dengan kata-kata sederhana

Pertanyaan utamanya adalah: mengapa kita membutuhkan JNDI? JNDI diperlukan agar kita bisa mendapatkan objek Java dari beberapa “Registrasi” objek dari kode Java dengan nama objek yang terikat pada objek tersebut. Mari kita pecahkan pernyataan di atas menjadi beberapa hal ini agar banyaknya kata yang diulang-ulang tidak membingungkan kita:
  1. Pada akhirnya kita perlu mendapatkan objek Java.
  2. Kami akan mendapatkan objek ini dari beberapa registri.
  3. Ada banyak objek di registri ini.
  4. Setiap objek dalam registri ini memiliki nama unik.
  5. Untuk mendapatkan objek dari registri, kita harus memberikan nama dalam permintaan kita. Seolah-olah mengatakan: “Tolong beri saya apa yang Anda miliki dengan nama ini dan itu.”
  6. Kita tidak hanya dapat membaca objek berdasarkan namanya dari registri, tetapi juga menyimpan objek dalam registri ini dengan nama tertentu (entah bagaimana objek tersebut berakhir di sana).
Jadi, kami memiliki semacam registri, atau penyimpanan objek, atau Pohon JNDI. Selanjutnya, dengan menggunakan contoh, mari kita coba memahami arti JNDI. Perlu dicatat bahwa sebagian besar JNDI digunakan dalam pengembangan Perusahaan. Dan aplikasi tersebut bekerja di dalam beberapa server aplikasi. Server ini dapat berupa Server Aplikasi Java EE, atau wadah servlet seperti Tomcat, atau wadah lainnya. Registri objek itu sendiri, yaitu Pohon JNDI, biasanya terletak di dalam server aplikasi ini. Yang terakhir ini tidak selalu diperlukan (Anda dapat memiliki pohon seperti itu secara lokal), tetapi ini adalah yang paling umum. JNDI Tree dapat dikelola oleh orang khusus (administrator sistem atau spesialis DevOps) yang akan “menyimpan objek di registri” dengan namanya. Ketika aplikasi kita dan Pohon JNDI ditempatkan bersama di dalam wadah yang sama, kita dapat dengan mudah mengakses objek Java apa pun yang disimpan dalam registri tersebut. Selain itu, registri dan aplikasi kita dapat ditempatkan di wadah yang berbeda dan bahkan di mesin fisik yang berbeda. JNDI pun memungkinkan Anda mengakses objek Java dari jarak jauh. Kasus yang khas. Administrator server Java EE menempatkan objek di registri yang menyimpan informasi yang diperlukan untuk menghubungkan ke database. Oleh karena itu, untuk bekerja dengan database, kami cukup meminta objek yang diperlukan dari pohon JNDI dan mengerjakannya. Sangat nyaman. Kenyamanan juga terletak pada kenyataan bahwa dalam pengembangan usaha terdapat lingkungan yang berbeda. Ada server produksi, dan ada server pengujian (dan seringkali terdapat lebih dari 1 server pengujian). Kemudian, dengan menempatkan objek untuk menghubungkan ke database di setiap server di dalam JNDI dan menggunakan objek ini di dalam aplikasi kita, kita tidak perlu mengubah apa pun saat menyebarkan aplikasi kita dari satu server (pengujian, rilis) ke server lainnya. Akan ada akses ke database di mana saja. Contohnya tentu saja agak disederhanakan, namun saya harap ini akan membantu Anda lebih memahami mengapa JNDI diperlukan. Selanjutnya kita akan mengenal lebih dekat JNDI di Pulau Jawa, dengan beberapa unsur penyerangannya.

API JNDI

JNDI disediakan dalam platform Java SE. Untuk menggunakan JNDI, Anda harus mengimpor kelas JNDI, serta satu atau beberapa penyedia layanan untuk mengakses layanan penamaan dan direktori. JDK mencakup penyedia layanan untuk layanan berikut:
  • Protokol Akses Direktori Ringan (LDAP);
  • Arsitektur Broker Permintaan Objek Umum (CORBA);
  • layanan nama Common Object Services (COS);
  • Registri Invokasi Metode Jarak Jauh Java (RMI);
  • Layanan Nama Domain (DNS).
Kode API JNDI dibagi menjadi beberapa paket:
  • javax.penamaan;
  • javax.penamaan.direktori;
  • javax.penamaan.ldap;
  • javax.penamaan.acara;
  • javax.penamaan.spi.
Kami akan memulai pengenalan kami tentang JNDI dengan dua antarmuka - Nama dan Konteks, yang berisi fungsionalitas utama JNDI

Nama Antarmuka

Antarmuka Nama memungkinkan Anda mengontrol nama komponen serta sintaks penamaan JNDI. Di JNDI, semua operasi nama dan direktori dilakukan sesuai dengan konteksnya. Tidak ada akar yang mutlak. Oleh karena itu, JNDI mendefinisikan InitialContext, yang menyediakan titik awal untuk operasi penamaan dan direktori. Setelah konteks awal diakses, konteks tersebut dapat digunakan untuk mencari objek dan konteks lainnya.
Name objectName = new CompositeName("java:comp/env/jdbc");
Dalam kode di atas, kami mendefinisikan beberapa nama di mana suatu objek berada (mungkin tidak berlokasi, tapi kami mengandalkannya). Tujuan akhir kami adalah mendapatkan referensi ke objek ini dan menggunakannya dalam program kami. Jadi, namanya terdiri dari beberapa bagian (atau token), dipisahkan dengan garis miring. Token seperti ini disebut konteks. Yang pertama hanyalah konteks, semua yang berikutnya adalah subkonteks (selanjutnya disebut subkonteks). Konteks lebih mudah dipahami jika Anda menganggapnya sebagai analogi direktori atau direktori, atau sekadar folder biasa. Konteks root adalah folder root. Subkonteks adalah subfolder. Kita dapat melihat semua komponen (konteks dan subkonteks) dari nama tertentu dengan menjalankan kode berikut:
Enumeration<String> elements = objectName.getAll();
while(elements.hasMoreElements()) {
  System.out.println(elements.nextElement());
}
Outputnya adalah sebagai berikut:

java:comp
env
jdbc
Outputnya menunjukkan bahwa token dalam nama dipisahkan satu sama lain dengan garis miring (namun, kami menyebutkan ini). Setiap token nama memiliki indeksnya sendiri. Pengindeksan token dimulai dari 0. Konteks akar memiliki indeks nol, konteks berikutnya memiliki indeks 1, berikutnya 2, dan seterusnya. Kita bisa mendapatkan nama subkonteks berdasarkan indeksnya:
System.out.println(objectName.get(1)); // -> env
Kita juga dapat menambahkan token tambahan (baik di akhir atau di lokasi tertentu dalam indeks):
objectName.add("sub-context"); // Добавит sub-context в конец
objectName.add(0, "context"); // Добавит context в налачо
Daftar lengkap metode dapat ditemukan di dokumentasi resmi .

Konteks Antarmuka

Antarmuka ini berisi sekumpulan konstanta untuk menginisialisasi konteks, serta sekumpulan metode untuk membuat dan menghapus konteks, mengikat objek ke sebuah nama, dan mencari serta mengambil objek. Mari kita lihat beberapa operasi yang dilakukan menggunakan antarmuka ini. Tindakan paling umum adalah mencari objek berdasarkan nama. Ini dilakukan dengan menggunakan metode:
  • Object lookup(String name)
  • Object lookup(Name name)
Mengikat objek ke nama dilakukan dengan menggunakan metode bind:
  • void bind(Name name, Object obj)
  • void bind(String name, Object obj)
Kedua metode tersebut akan mengikat nama nama ke objek.Operasi Object kebalikan dari pengikatan - melepaskan ikatan objek dari sebuah nama, dilakukan dengan menggunakan metode unbind:
  • void unbind(Name name)
  • void unbind(String name)
Daftar lengkap metode tersedia di situs dokumentasi resmi .

Konteks Awal

InitialContextadalah kelas yang mewakili elemen akar pohon JNDI dan mengimplementasikan file Context. Anda perlu mencari objek berdasarkan nama di dalam pohon JNDI yang berhubungan dengan node tertentu. Node akar pohon dapat berfungsi sebagai node tersebut InitialContext. Kasus penggunaan umum untuk JNDI adalah:
  • Mendapatkan InitialContext.
  • Gunakan InitialContextuntuk mengambil objek berdasarkan nama dari pohon JNDI.
Ada beberapa cara untuk mendapatkannya InitialContext. Itu semua tergantung pada lingkungan di mana program Java berada. Misalnya, jika program Java dan pohon JNDI berjalan di dalam server aplikasi yang sama, maka cukup InitialContextmudah untuk mendapatkannya:
InitialContext context = new InitialContext();
Jika hal ini tidak terjadi, mendapatkan konteks menjadi sedikit lebih sulit. Terkadang perlu meneruskan daftar properti lingkungan untuk menginisialisasi konteks:
Hashtable env = new Hashtable();
env.put(Context.INITIAL_CONTEXT_FACTORY,
    "com.sun.jndi.fscontext.RefFSContextFactory");

Context ctx = new InitialContext(env);
Contoh di atas menunjukkan salah satu cara yang mungkin untuk menginisialisasi konteks dan tidak membawa muatan semantik lainnya. Tidak perlu mendalami kode secara detail.

Contoh penggunaan JNDI di dalam pengujian unit SpringBoot

Di atas kami katakan bahwa agar JNDI dapat berinteraksi dengan layanan penamaan dan direktori, diperlukan SPI (Antarmuka Penyedia Layanan), yang dengannya integrasi antara Java dan layanan penamaan akan dilakukan. JDK standar hadir dengan beberapa SPI berbeda (kami mencantumkannya di atas), yang masing-masing kurang menarik untuk tujuan demonstrasi. Memunculkan aplikasi JNDI dan Java di dalam sebuah container terbilang menarik. Namun, penulis artikel ini adalah orang yang malas, jadi untuk mendemonstrasikan cara kerja JNDI, ia memilih jalur yang paling sedikit hambatannya: jalankan JNDI di dalam pengujian unit aplikasi SpringBoot dan akses konteks JNDI menggunakan peretasan kecil dari Spring Framework. Jadi, rencana kami:
  • Mari kita menulis proyek Spring Boot yang kosong.
  • Mari buat pengujian unit di dalam proyek ini.
  • Di dalam tes ini kami akan mendemonstrasikan cara bekerja dengan JNDI:
    • dapatkan akses ke konteksnya;
    • mengikat (mengikat) beberapa objek dengan nama tertentu di JNDI;
    • dapatkan objek berdasarkan namanya (pencarian);
    • Mari kita periksa apakah objeknya bukan null.
Mari kita mulai secara berurutan. File->Baru->Proyek... Menggunakan JNDI di Java - 3 Selanjutnya, pilih item Spring Initializr : Menggunakan JNDI di Java - 4Isi metadata tentang proyek: Menggunakan JNDI di Java - 5Kemudian pilih komponen Spring Framework yang diperlukan. Kita akan mengikat beberapa objek DataSource, jadi kita memerlukan komponen untuk bekerja dengan database:
  • API JDBC;
  • Basis Data H2.
Menggunakan JNDI di Java - 6Mari kita tentukan lokasi di sistem file: Menggunakan JNDI di Java - 7Dan proyek dibuat. Faktanya, satu unit pengujian dibuat secara otomatis untuk kami, yang akan kami gunakan untuk tujuan demonstrasi. Di bawah ini adalah struktur proyek dan pengujian yang kita perlukan: Menggunakan JNDI di Java - 8Mari mulai menulis kode di dalam pengujian konteksLoads. Retasan kecil dari Spring, yang dibahas di atas, adalah class SimpleNamingContextBuilder. Kelas ini dirancang untuk dengan mudah meningkatkan JNDI di dalam pengujian unit atau aplikasi yang berdiri sendiri. Mari tulis kode untuk mendapatkan konteksnya:
final SimpleNamingContextBuilder simpleNamingContextBuilder
       = new SimpleNamingContextBuilder();
simpleNamingContextBuilder.activate();

final InitialContext context = new InitialContext();
Dua baris kode pertama akan memungkinkan kita menginisialisasi konteks JNDI dengan mudah nanti. Tanpa mereka, InitialContextpengecualian akan muncul saat membuat sebuah instance: javax.naming.NoInitialContextException. Penafian. Kelas tersebut SimpleNamingContextBuilderadalah kelas yang tidak digunakan lagi. Dan contoh ini dimaksudkan untuk menunjukkan bagaimana Anda dapat bekerja dengan JNDI. Ini bukan praktik terbaik untuk menggunakan JNDI di dalam pengujian unit. Hal ini dapat dikatakan sebagai penopang untuk membangun konteks dan mendemonstrasikan pengikatan dan pengambilan objek dari JNDI. Setelah menerima konteks, kita dapat mengekstrak objek darinya atau mencari objek dalam konteks tersebut. Belum ada objek di JNDI, jadi logis untuk meletakkan sesuatu di sana. Misalnya, DriverManagerDataSource:
context.bind("java:comp/env/jdbc/datasource", new DriverManagerDataSource("jdbc:h2:mem:mydb"));
Pada baris ini, kita telah mengikat objek kelas DriverManagerDataSourceke nama java:comp/env/jdbc/datasource. Selanjutnya, kita bisa mendapatkan objek dari konteksnya berdasarkan namanya. Kita tidak punya pilihan selain mendapatkan objek yang baru saja kita masukkan, karena tidak ada objek lain dalam konteksnya =(
final DataSource ds = (DataSource) context.lookup("java:comp/env/jdbc/datasource");
Sekarang mari kita periksa apakah DataSource kita memiliki koneksi (koneksi, koneksi, atau koneksi adalah kelas Java yang dirancang untuk bekerja dengan database):
assert ds.getConnection() != null;
System.out.println(ds.getConnection());
Jika kita melakukan semuanya dengan benar, hasilnya akan seperti ini:

conn1: url=jdbc:h2:mem:mydb user=
Perlu dikatakan bahwa beberapa baris kode mungkin menimbulkan pengecualian. Baris berikut dilemparkan javax.naming.NamingException:
  • simpleNamingContextBuilder.activate()
  • new InitialContext()
  • context.bind(...)
  • context.lookup(...)
Dan ketika bekerja dengan kelas DataSourceitu bisa dilempar java.sql.SQLException. Dalam hal ini, perlu untuk mengeksekusi kode di dalam blok try-catch, atau menunjukkan dalam tanda tangan unit pengujian bahwa ia dapat mengeluarkan pengecualian. Berikut ini kode lengkap kelas tes:
@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();
        }
    }
}
Setelah menjalankan pengujian, Anda dapat melihat log berikut:

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=

Kesimpulan

Hari ini kita melihat JNDI. Kita belajar tentang apa itu layanan penamaan dan direktori, dan bahwa JNDI adalah Java API yang memungkinkan Anda berinteraksi secara seragam dengan berbagai layanan dari program Java. Yaitu, dengan bantuan JNDI, kita dapat merekam objek di pohon JNDI dengan nama tertentu dan menerima objek yang sama berdasarkan namanya. Sebagai tugas bonus, Anda dapat menjalankan contoh cara kerja JNDI. Ikat beberapa objek lain ke dalam konteksnya, lalu baca objek ini berdasarkan namanya.
Komentar
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION