29 Ağustos 2009 Cumartesi

JNI (Java Native Interface)

JNI nedir? Ne değildir?
JNI, Java uygulamalarınıza sanal makine de uygulanmamış ve saf Java ile uygulanması imkansız yada çok zor olan yetenekleri katabilmenize yarar. Java, felsefesi gereği platform spesifik fonksiyonelliği bünyesinde barındırmaz. Bundan ötürüdür ki JNI, Java kullanarak platform spesifik işleri halledebileceğiniz neredeyse tek yoldur. Ayrıca JNI farklı diller ile geliştirilmiş, C altından çağrılabilir uygulama ve kütüphanelerin Java üzerinden erişilebilmesini mümkün kılar. Tüm bunların yanında JNI performans gerektiren işlerin native kod kullanılarak JVM’in sağlayabileceğinden daha yüksek verim ile halledilebilmesine imkan tanır. Peki bütün bunların yanında JNI’nin zararlı yan etkileri yokmudur? Evet vardır. Öncelikle native bağlantısı olan Java programları JVM’nin sunduğu yüksek taşınabilirlik yeteneğini kaybederler. Dolayısıyla programların platformlar arasında taşınabilirliği daha zor bir iş haline gelir. Ayrıca JNI üzerinden Java sanal makinesine bağlanmış kodlarda bulunan bir hatanın saptanması zordur, daha da kötüsü bu hata tüm sanal makine de etkili olabilecek ciddi hatalara ve çökmeye yol açabilir. Artık JNI’nin ne işe yaradığını avantajlarını-dezavantajlarını bildiğimize göre nasıl kullanıldığını bir örnekle inceleyebiliriz:

Native.java dosyasının içeriği:

public class Native {

static {
System.loadLibrary("native_library");
}

public static native int sum(int x, int y);

public static void main(String[] args) {
System.out.println(sum(3, 5));
}

}


Yukarıda son derece sade bir Java sınıfı görüyoruz. Bu sınıf, sınıf yükleyicisi tarafından sınıfın yüklendiği anda çalıştırılacak static tanımlı blok, bir native metod deklerasyonu ve Java programının çalışmaya başlayacağı main metoduna sahip. Sınıfımızı javac ile derliyoruz:

javac Native.java

Elde ettiğimiz byte kodun(Native.class dosyası) içinde geçen native metodun(sum) javah kullanarak C deklerasyonunu elde ediyoruz:

javah -jni Native

Şimdi elimizde iki adet dosya var: Native.class ve Native.h

Burda Native.class bilindiği üzere JVM üzerinde işletilecek bytecode, Native.h ise Native.class içinde çağrıda bulunulan native metodun(sum) C deklerasyonu. Şimdi yapmamız gereken Native.h dosyasında deklare edilmiş c fonksiyonu uygulamak.

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class Native */

#ifndef _Included_Native
#define _Included_Native
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: Native
* Method: sum
* Signature: (II)I
*/
JNIEXPORT jint JNICALL Java_Native_sum
(JNIEnv *, jclass, jint, jint);

#ifdef __cplusplus
}
#endif
#endif


Yukarıda gördüğümüz Native.h dosyasının içeriğidir. Görüldüğü üzere bu başlık dosyası

JNIEXPORT jint JNICALL Java_Native_sum
(JNIEnv *, jclass, jint, jint);


biçminde bir fonksiyon prototipi içeriyor.
Native.cpp adında bir C++ kaynak kod dosyası oluşturdum ve dosya içinde

#include "Native.h"

JNIEXPORT jint JNICALL
Java_Native_sum(JNIEnv *env, jclass cls, jint num1, jint num2) {
return num1 + num2;
}


yukarıda görüldüğü gibi Java_Native_sum fonksiyonunu implemente ettim. Bu işi de hallettiğime göre geriye yalnız herşeyi birleştirmek kaldı.

Linux yada Mingw kurulu ve Path konfigurasyonu yapılmış bir Windows üzerinde aşağıda ki kabuk komutu ile derleme işini halledebiliriz.

gcc -Wall -D_JNI_IMPLEMENTATION_ -Wl,--kill-at -I"D:/Program Files/Java/jdk1.6.0_14/include" -I"D:/Program Files/Java/jdk1.6.0_14/include/win32" -shared Native.cpp -o native_library.dll


Burada üç noktaya dikkat etmelisiniz. Birincisi -I parametresi ile gcc'ye bildirilen jni.h ve jni_md.h dosyalarının gerçek lokasyonları sizin makinanızda kurulum farklılıklarına göre değişebilir. İkincisi -Wl,--kill-at bağlayıcı(linker) direktifi verilmediği taktirde JVM üretilen shared kütüphanede ki tanımlara erişemez. Üçüncüsü kullandığınız işletim sistemine göre -o parametresinde verilen derleyici çıktı dosyası adını native_library.dll yada native_library.so biçminde düzenlemelisiniz.

Evet herşeyi doğru yaptıysak elimizde java kaynak kodu ile beraber Native.class, Native.h, Native.cpp ve native_library.so yada native_library.dll dosyaları bulunmaktadır. Artık java Native deyip ekranda 8 yazdığını görebiliriz :D Küçük fakat iyi bir başlangıç yapmış olduk. Bununla beraber JNI tabiki iki sayıyı toplamak için geliştirilmemiştir. Aksine genellikle Java'nın tasarımı ve felsefesi gereği uygun olmadığı işleri halletmek yada farklı dillerde yazılmış kütüphanelerin Java altından erişilebilir hale getirilmesinde kullanılır ve oldukça ayrıntılı bir programlama arayüzüdür. Şimdilik bu yazımı zaman sıkıntım dolayısı ile bitirmek zorundayım. Fakat yakın bir zaman içerisinde temel JNI fonksiyonelliğine değinen bir yazı daha hazırlama planım var. Umarım zaman bulabilirim. Hoşçakalın.

2 yorum: