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