Friday, January 15, 2016

JNA & JNI Intro

JNA & JNI


ასე 1 წელიწადი იქნება რაც არაფერი დამიწერია ბლოგზე. მეგობრის თხოვნით პატარა სტატია მივუძღვენი JNA-ს.

Java Native Access  და Java Native Interface ბიბლიოთეკები გამოიყენება C , C++, assembly ზე დაწერილი კოდების გამოსაძახებლად (C++ ვრაფერი JNA არ აქვს, ხელით უნდა დაწეროთ ან შეგიძლიათ გამოიყენოთ JNA-ს შვილობილი პროექტები, როგორიცაა მაგალითად BridJ და სხვა მრავალი).  C/C++ ენაზე მიჩვეული დეველოპერი JNI-ს აირჩევს, რადგან კონვერტაციები Native მხარეს არის იმლემენტირებული, ვისთვისაც Java ენაა უფრო ახლოს, ის JNA აირჩევს, რადგანაც კონვერტაციები Java მხარეს ხდება. გამოყენების არეალი ორივეს დიდი აქვს, მაგალითად JNA-ს იყენებს Apache Cassandra რომელიც არის ერთერთი საუკეთესო NoSQL მონაცემთა ბაზა , რომელიც CAP  Theorem ის მიხედვით Availability სა და Partition Tolerance -ს შორის ზის. ასევე JetBrains ის მიერ შექმნილი ცნობილი InteliJ IDEA, Netbeans IDE და ასე შემდეგ.

JNA ზე მაგალითები , ბიბლიოთეკასთან ერთად, შეგიძლიათ ნახოთ შემდეგ ლინკზე:
https://github.com/java-native-access/jna.git


თვალსაჩინოებისთვის დავწეროთ მარტივი მაგალითი.

header.h
#ifndef HEADER_H_INCLUDED
#define HEADER_H_INCLUDED 

int  buildRequest(char * input, char * answer, int inputLenth);  

typedef void(*NotificationListener)(char *, int);
void callbackTriger(const NotificationListener l); 
void getDeviceRandomStatus(char *answer, int sizeOfChars); 
int randNum( int min,  int max); 

#endif // HEADER_H_INCLUDED



header.c
#include<stdio.h>
#include "header.h"
#include <stdlib.h>
#include <time.h>


int buildRequest(char * input, char * answer, int inputLenth) {
  printf("C: log passed params from java to C: ");
  int i = 0;
  for (; *input; ++input) {
    i++;
    if (i > inputLenth) {
          break;
    }
    char c = *input;
    int i = (int) c;
    printf("%d ", i);
  }
  printf("\n");
  answer[0] = 0xFE;
  answer[1] = 0x81;
  return i - 1;
}

void callbackTriger(const NotificationListener l) {
  int size = randNum(1, 20);
  char answer[size];
  getDeviceRandomStatus(answer, size);
  (*l)(answer, sizeof(answer));
}

void getDeviceRandomStatus(char *answer, int sizeOfChars) {
  int i;
  for (i = 0; i < sizeOfChars; i++) {
        int i = randNum(0, 255);
        answer[i] = i + '0';
  }
}

int randNum(int min, int max) {
  srand(time(NULL));
  double scaled = (double) rand() / RAND_MAX;
  int val = (max - min + 1) * scaled + min;
  return val;
} 




mainc.c    გამოვიყენებთ ბიბლიოთეკის გასატესტად (ის Shared Library რომელიც შემდგომ უნდა ჩაბამათ Java კოდში ში JNA დახმარებით)
#include<stdio.h>
#include <limits.h>
#include <stdlib.h>

int main(void)
{
     char ch [] = {0x01, 0x07, 0x09 ,0xA, 0xB,0xC, 0xD,0xE ,0xF };
     char answer[2];
     int r=buildRequest(ch, answer, 5);
     printf("Returned params: ");

     int i;
     for (i = 0; i < sizeof(answer); ++i){
           printf("%d ", answer[i]);
     }

    return 0; 


}


შევქმნათ Shared ბიბლიოთეკა და გავტესტოთ. ამ სკრიპტებით შექიმნება header.o და libHeader.so, შემდეგ main ს მივაბამთ და გავტესტავთ:
gcc -c -Wall -Werror -fpic header.c 
gcc -shared -o libHeader.so header.o
gcc main.c -o main -lHeader -L/home/vq/Desktop -Wl,-rpath=/home/vq/Desktop
./main



ახლა დავწეროთ რამდენიმე მაგალითი, ერთი უბრალოდ Java დან C კოდის გამოძახება, მეორე კი პირიქით ანუ callback. განვიხილოთ buildRequest ფუნქციის გამოძახების მაგალითი -  ბაიტების მასივი წარმოდგენილი გვაქვს როგორც char *, როგორც input ისე output რაც უნდა დაბრუნდეს, ასევე აქ გვჭირდება ერთი დამხარე პრიმიტივი ბაიტების მასივის ზომის განსასაზღვრად. callbackTriger -ის შემთხვევაში პოინტერი გვაქვს პარამეტრად NotificationListener ის რომელიც ჰედერშია გამოცხადებული, pointer-to-function ტიპი. რაც შეეხება randNum და getDeviceRundomStatus, ესენი უბრალო მეთდებია შემთხვევითი ზომის და მნიშვნელობების ბაიტების მასივის დასაბრუნებლად.



import java.util.Arrays;
import java.util.logging.Logger;

import com.sun.jna.Callback;
import com.sun.jna.Library;
import com.sun.jna.Memory;
import com.sun.jna.Native;
import com.sun.jna.Pointer;

/**
 * 
 * @author vakhtang Koroghlishvili
 * 
 */
public class BuildRequestMaker {

public static Logger log = Logger.getLogger(CallBack.class
  .getSimpleName());

public interface CLibrary extends Library {

 public int buildRequest(Pointer input, Pointer output,
   int inputLenth);
}

public interface CLibraryCallBack extends Library {

 public interface NotificationListener extends Callback {
  void invoke(Pointer val, int lenth);
 }

 public static class NotificationListenerImpl implements
   NotificationListener {
  @Override
  public void invoke(Pointer val, int lenth) {
   log.info("java mehtod, callback: "
     + Arrays.toString(val.getByteArray(0, lenth)));
  }
 }

 public void callbackTriger(NotificationListener callback);
}

public byte[] buildRequest(byte[] inputArr) {

 CLibrary clib = (CLibrary) Native.loadLibrary(
   "/home/vq/Desktop/libHeader.so", CLibrary.class);

 Pointer input = new Memory(inputArr.length
   * Native.getNativeSize(Byte.TYPE));
 Pointer answer = new Memory(inputArr.length
   * Native.getNativeSize(Byte.TYPE));

 for (int i = 0; i < inputArr.length; i++) {
  input.setByte(i * Native.getNativeSize(Byte.TYPE),
    inputArr[i]);
 }

 int resultSize = clib.buildRequest(input, answer,
   inputArr.length);

 log.info("returned value from c lib is: " + resultSize);
 byte[] resultByte = answer.getByteArray(0, resultSize);

 log.info("returned value array from c lib is: "
   + Arrays.toString(resultByte));

 return resultByte;

}

public void callBackListener() {

 CLibraryCallBack clib = (CLibraryCallBack) Native
   .loadLibrary("/home/vq/Desktop/libHeader.so",
     CLibraryCallBack.class);

 // instantiate a callback wrapper instance
 CLibraryCallBack.NotificationListenerImpl callbackImpl = new CLibraryCallBack.NotificationListenerImpl();

 // pass the callback wrapper to the C library
 clib.callbackTriger(callbackImpl);

 }

}


და ტესტები:


import org.junit.Assert;
import org.junit.Test;

public class JNATests {

 @Test
 public void buildRequestTest() {
  BuildRequestMaker m = new BuildRequestMaker();
  byte[] input = { (byte) 0x81, (byte) 0xFE };
  byte[] expect = { (byte) 0xFE, (byte) 0x81 };
  Assert.assertArrayEquals(expect, m.buildRequest(input));
 }

 @Test
 public void callBacklTest() {
  BuildRequestMaker m = new BuildRequestMaker();
  Exception ex = null;
  try {
   m.callBackListener();
  } catch (Exception e) {
   ex = e;
  }
  Assert.assertEquals(null, ex);
 }
შედეგები:
INFO: started call back listener
INFO: java mehtod, callback: [-64, -28, 119, 124, 84, 127, 0, 0]
...
INFO: started building request
INFO: returned value from c lib is: 2
INFO: returned value array from c lib is: [-2, -127]
C: log passed params from java to C: -127 -2 


ეს მაგალითი, ისე, დამატებითი დეტალებისთვის Just Google