21 Kasım 2015 Cumartesi

Java Native Interface(JNI)

Java Native Interface(JNI),Java Software Development Kit(Java Yazılım Geliştirme Kit'i)'in bir parçası olan doğal programlama arayüzüdür.JNI, C\C++ gibi diğer dillerde yazılmış kod ve kod kütüphanelerini Java kodu içerisinde kullanmamıza olanak sağlar.Buna karşılık bir Java kodunuda C/C++ kodu içerisine ekleyebiliriz ve ya içerisinden çağırabiliriz diyelim.JVM'nin bütün platformlarda sorunsuz çalıştırılabilmesi,büyük ölçüde JNI sayesinde gerçekleştirilmişdir.Eğer Java ile yazılım geliştiriyorsak,Bellek Yönetimi,Performans,alt seviyeli bir donanıma erişim gibi işlemlerde C\C++ kullanmamız gerekebilir.Bu yazıda Java sınıfları içerisinden C\C++ kod çağırımı yapılmaya bakılacakdır.

  • Kullanma şekli(Windows ortamında)
                           Windows ortamında bu tarz uygulamalar geliştirebilmemiz için lazım olan birkaç araç vardır.
                              1.Java Derleyicisi(javac.exe)
                              2.Java Sanal Makinesi(JVM veya java.exe)
                              3.Native method barındıran Java Class'dan .h dosyası oluşturucu(javah.exe)
                              4. JNI'i tanımlayan kütüphane dosyaları ve header dosyaları(jni.h,jvm.lib,jvm.dll)
                              5.C/C++ Derleyicisi(örn:Mingw(gcc,g++))
                              6.IDE(gerekli değil)
           Not:Bunlardan ilk dördü sisteminizde JDK kuruluysa vardır demekdir.
Örnek1:Bir Java sınıfı içerisinden C kodu çağıralım.(ornek1.java)
public class ornek1{
    
    static{
        System.loadLibrary("lib1");//ornek1 sınıfı belleğe yüklendiyinde lib1.dll
                                  //kütüphanesini kalıcı olarak bellege getir
    }
    private native void merhaba_dunya();//c kodunu bu metotla çagır

    public static void main(String[] args) {
        new ornek1().merhaba_dunya();//c kodunu çalıştır
    }
}
Buradaki static initializer(başlatıcı) ornek1 sınıfının yüklenmesi sırasında System.loadLibrary()'yi lib1.dll native kütüphanesini yüklemek için çağıracakdır.Bu kütüphane(lib1.dll),program çalıştırılırken  Java Library Path yoluna eklenmiş olması lazım,aksi takdirde JVM UnsatisfiedLinkError hatasını vericekdir.Buradaki native anahtar kelimesi bu metotun bir C/C++ metotu oldugunu göstermekdedir,C/C++ kodlarını çagırabilmemiz için native metotlara ihtiyacımız vardır.Şimdi ise çağırılacak olan C kodunu yazalım.Bunun için ilk once yazacağımız C kodu için bir header dosyası oluşturmamız gerekli.Bunu yapmak için ilk once Java kodunu derlememiz lazım,oluşacak olan class dosyamızdan javah.exe programıyla bu header dosyamızı oluşturabiliriz.ornek1.java dosyamızı cmd'yi açarak derleyelim. 
javac ornek1.java
Aynı dizinde ornek1.class dosyası oluşturuldu,şimdi bunu kullanarak C header dosyası oluşturalım.
javah ornek1
Bu komutdan sonra ornek1.h dosyası oluşturuldu.Bu dosyanın içeriği aşağıdaki şekildedir.
ornek1.h 
/* DO NOT EDIT THIS FILE - it is machine generated */
#include jni.h
/* Header for class ornek1 */

#ifndef _Included_ornek1
#define _Included_ornek1
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     ornek1
 * Method:    merhaba_dunya
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_ornek1_merhaba_1dunya
  (JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif
Buradaki extern “C” C++ derleyicisi tarafından algılanır ve fonksiyonların C'nin isimlendirme protokolü kullanılarak derleneceğini söyler.Java_ornek1_merhaba_1dunya metot ismi ornek1  sınıfının içerisindeki merhaba_dunya() metoduna karşılık gelmektedir.Metot isimleri Java_{package_and_classname}_{function_name}(JNI arguments) şeklinde bir standartdır.JNIEnv  parametresi bütün  JNI fonksiyonlarina erişim sağlayan JNI ortam değişkenidir.jobject ise Java daki this nesnesine karşılık gelmektedir.Şimdi ise bu metodun C implementasyonunu yapalım.
ornek1.c
#include "ornek1.h"
#include  "stdio.h"

//merhaba_dunya() metodunun implementasyonu
JNIEXPORT void JNICALL Java_ornek1_merhaba_1dunya(JNIEnv *env, jobject jthis){
    printf("Merhaba Dunya\n");
    return;
  }
Bu C kodunu Java sınıfı içerisinden kullana bilmemiz için yukarıda bahsetdiyimiz gibi .dll dosyasına ihtiyacımız var.Yani ornek1.c dosyasını derleyip .dll uzantılı dosyaya  çevirmemiz lazım.
gcc -I"%JAVA_HOME%\include" -I"%JAVA_HOME%\include\win32" -shared -o  lib1.dll ornek1.c
  • JAVA_HOME:JDK nın kurulu oldugu dizini göstermekde.
  • -shared paylaşımlı bir dll olması gerekdiyini göstermekde.                                              
  • gcc C compiler.
Bu komutu yazdıkdan sonra dizinimizde lib1.dll dosyası oluşturuldu.ornek1.java dosyamızı çalıştırdığımızda bu kütüphanenin java_library_path'e eklenmesi gerek,aksi takdirde öncedende belirtildiyi gibi UnsatisfiedLinkError hatası verilecekdir.Şimdi ise ornek1 sınıfını(Java Kodu) çalıştıralım.
java -Djava.library.path="C:\\Users\heydar\Desktop\calismalar" ornek1
           Aşağıdaki gibi bir çıktı alınmasi lazım.
 Evet,görüldüyü gibi ornek1.c dosyası içerisindeki printf("Merhaba Dunya\n"); kod satırı  ornek1.java dosyasi içerisinde tanımlanmış ornek1 sınıfında çalıştırılmaktadır.
          Şimdi bir C++ örnegine bakacak olursak.
ornek2.java 
public class ornek2{

    static{
        System.loadLibrary("lib2");
    }
    private native double ortalama(int n1,int n2);//iki tam sayının ortalamasını dödüren metod

    public static void main(String[] args) {
        System.out.println("Ortalama:"+new ornek2().ortalama(4,5)); 
    }
}
Yukarıdaki örnekteki gibi kodu derledikden sonra javah.exe ile oluşan header kodumuz.
ornek2.h
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class ornek2 */

#ifndef _Included_ornek2
#define _Included_ornek2
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     ornek2
 * Method:    ortalama
 * Signature: (II)D
 */
JNIEXPORT jdouble JNICALL Java_ornek2_ortalama
  (JNIEnv *, jobject, jint, jint);

#ifdef __cplusplus
}
#endif
#endif
         Şimdi ise C++ implementasyonunu yapalım.
ornek2.cpp
#include "ornek2.h"
#include <iostream>
using namespace std;
JNIEXPORT jdouble JNICALL Java_ornek2_ortalama(JNIEnv *env, jobject jthis, jint n1, jint n2){
    jdouble ortalama;
    cout<<"C++ daki sayilar:"<<n1<<","<<n2<<endl;
    ortalama=((jdouble)n1+n2)/2.0;
    return ortalama;
}
         lib2.dll isminde bir dll dosyası oluşturalım.
NOT:Bazen windowsda “__int64” tipi “long long” tanımlı olmayabiliyor. Bu durumda  gcc/g++ ye aşağıdaki seçenekle bu tip kullanımını soylemelisiniz.
gcc/g++ -D __int="long long"

lib2.dll
g++ -I"%JAVA_HOME%\include" -I"%JAVA_HOME%\include\win32" -D__int64="long long"  -shared -o lib2.dll ornek2.cpp
          Şimdi ise ornek2.java kodumuzu çalıştırdığımızda aşağıdaki gibi çıktı alırız.

2 yorum: