alten romania - java jni

36
Java JNI & alternatives Integration between Java and C/C++ Costin Chiriac – ALTEN Romania

Upload: alten-romania

Post on 26-Jan-2015

166 views

Category:

Technology


2 download

DESCRIPTION

This is a presentation about Java JNI.

TRANSCRIPT

Page 1: ALTEN Romania - Java JNI

Java JNI & alternatives

Integration between Java and C/C++

Costin Chiriac – ALTEN Romania

Page 2: ALTEN Romania - Java JNI

Main points

• Aspects on Java code and C/C++ code

• Methods to integrate Java with C/C++

• Java Native Interface – Overview

• Sample application

• Java Native Interface – Features

• Java Native Access (JNA)

• JNR & EP 191: Foreign Function Interface

Page 3: ALTEN Romania - Java JNI

Java code

• Easier to develop and to debug

• Memory management with a GC

• Compiled as bytecode and interpreted by JVM

• Can be slower, the JIT transforms it to native

• Platform independent

• Limited amount of system features

• Advanced language features

Page 4: ALTEN Romania - Java JNI

C/C++ code - advantages

• Compiled into native code, fast exection

• System level APIs available

• RT operations (like DSP) are possible

Page 5: ALTEN Romania - Java JNI

C/C++ code - drawbacks

• Not portable without recompiling

• Makefiles/Linkers/Libraries

• No stacktace, debug less easy

• No Garbage Collector

Page 6: ALTEN Romania - Java JNI

Java and C/C++ integration

• mkfifo: use fopen in C and FileReader in Java

• Pipe stream: popen and System.in stream

• Java Native Interface (JNI)

• Java Native Access (JNA)

• Java Native Runtime (JNR)

Page 7: ALTEN Romania - Java JNI

JNI Overview • Programming interface part of the JDK

• Bi-directional communication

• Deep integration between Java and C/C++

• Exception handling

• Different type of IPC

Java C/C++ JNI

Page 8: ALTEN Romania - Java JNI

JNI - Native libraries

• Compiled and linked as *.dll, *.so

• Provide an API (for C a set of functions)

• Can be used by other programs

• Java may require to use them

Java Program (GUI/Console/Web) Native

library JNI

JVM

Operating System

Page 9: ALTEN Romania - Java JNI

JNI - Native applications

• Written in native code

• Compiled for the host machine

• Use directly native libraries (include, link, run)

• Can require acces to a Java API

Native Application Ex: Qt GUI

Operating System

Java Class Library

JVM

JNI

Page 10: ALTEN Romania - Java JNI

JNI – Java Native Interface

• Create a class declaring the native method

• Compile the class using javac

• Generate header file using javah –jni

• Create the native code using the headers

• Native code compiled as a dynamic library

• Export the mangled methods

• Java application loads the dynamic library

• Native methods are called from Java

Page 11: ALTEN Romania - Java JNI

JNI – Sample App

Windows Management Instrumentation – Provides various information about the system

– Is only available on Windows (not Linux/Unix)

– Can be used by a C/C++ program

– Can provide information not available to the JVM

For this sample application we’ll try to retrive the processor usage percent from WMI and display it in the command line in Java

Page 12: ALTEN Romania - Java JNI

JNIMon_CLI (1) package org.costin.jnimon.cli;

public class App {

public native double get_processor_time_percent();

public native void get_processor_time_percent_async();

public double processor_time_percent_async;

/**

* Callback for get_processor_time_percent_async

* @param percent

*/

public synchronized void setProcessorTimePercent(double percent) {

this.processor_time_percent_async = percent;

System.out.println("Processor usage: " + percent + "%");

this.notifyAll();

}

static {

System.loadLibrary("JNIMon_API");

}

Page 13: ALTEN Romania - Java JNI

JNIMon_CLI (2) public static void main(String[] args) {

final App jniCli = new App(); System.out.println("Calling JNI synchronously"); System.out.println("Processor usage: “ + jniCli.get_processor_time_percent() + "%");

System.out.println("Calling JNI asynchronously"); jniCli.processor_time_percent_async = -1; Thread t = new Thread(new Runnable() { @Override public void run() {

synchronized (jniCli) { int seconds = 0; while (jniCli.processor_time_percent_async == -1) {

try { jniCli.wait(1000); seconds++; System.out.println("Waited for " + seconds + "second(s) for processor

time percent"); } catch (InterruptedException e) { e.printStackTrace(); }

} }

} }); t.start(); jniCli.get_processor_time_percent_async(); }

}

Page 14: ALTEN Romania - Java JNI

Steps to integrate

• Compile the JNIMon_CLI project

• Use javah –jni org.....App to obtain the header

• Create the project JNIMon_API

• Include the header into JNIMon_API

• Implement the native methods

Page 15: ALTEN Romania - Java JNI

JNIMon_API

#include "stdafx.h"

JNIEXPORT jdouble JNICALL Java_org_costin_jnimon_cli_App_get_1processor_1time_1percent

(JNIEnv *, jobject) {

printf("Into native code.\n");

return retrieve_processor_percent();

}

JNIEXPORT void JNICALL Java_org_costin_jnimon_cli_App_get_1processor_1time_1percent_1async

(JNIEnv *env, jobject obj) {

printf("Into native code.\n");

jclass cls = env->GetObjectClass(obj);

jmethodID mid = env->GetMethodID(cls, "setProcessorTimePercent", "(D)V");

if (mid == 0)

return;

env->CallVoidMethod(obj, mid, retrieve_processor_percent());

}

Page 16: ALTEN Romania - Java JNI

Run JNIMon_CLI > cd bin

> jar cvf JNIMon_CLI.jar *

> java -Djava.library.path=. org.costin.jnimon.cli.App

Calling JNI synchronously

Into native code.

Connected to ROOT\CIMV2 WMI namespace

[…]

Proc Name : _Total

PercentProcessorTime : 17974296875

Timestamp_Sys100NS : 130535491786367187

Seeping 2 seconds... […] Proc Name : _Total

PercentProcessorTime : 17993632812

Timestamp_Sys100NS : 130535491806933593

n0: 7974296875 d0: 1786367187 n1: 7993632812 d1: 1806933593

PercentProcessorTotal : 5.98291Processor usage: 5.982907271207227%

Calling JNI asynchronously

Into native code.

Connected to ROOT\CIMV2 WMI namespace […] Proc Name : _Total

PercentProcessorTime : 17994492187

Timestamp_Sys100NS : 130535491808193359

Seeping 2 seconds...

Waited for 1second(s) for processor time percent

Waited for 2second(s) for processor time percent

[…] Proc Name : _Total

PercentProcessorTime : 18013359375

Timestamp_Sys100NS : 130535491828720703

n0: 7994492187 d0: 1808193359 n1: 8013359375 d1: 1828720703

PercentProcessorTotal : 8.08753Processor usage: 8.087534363919657%

Waited for 3second(s) for processor time percent

Page 17: ALTEN Romania - Java JNI

JNI Development process

Native API JNI API JNI

Client Java Application JNI

Page 18: ALTEN Romania - Java JNI

Using Native Methods

• Declare with the native keyword

• No Java implementation for these methods

• Java classes must be compiled

• Used to generate header file with javah

• Implement the prototypes in native code

• Method naming Java_{package_and_classname}_{function_name}(JNI args)

• Parameters: JNIEnv*, jobject/jclass, arguments

Page 19: ALTEN Romania - Java JNI

Mapping Java – Native types

Java Type Native Type Size in bits

boolean jboolean 8, unsigned

byte jbyte 8

char jchar 16, unsigned

short jshort 16

int jint 32

long jlong 64

float jfloat 32

double jdouble 64

void void n/a

Page 20: ALTEN Romania - Java JNI

Reference types • jobject (all Java objects)

• jclass (java.lang.Class objects)

• jstring (java.lang.String objects)

• jarray (arrays)

• jobjectArray (object arrays)

• jbooleanArray (boolean arrays)

• jbyteArray (byte arrays)

• jcharArray (char arrays)

• jshortArray (short arrays)

• jintArray (int arrays)

• jlongArray (long arrays)

• jfloatArray (float arrays)

• jdoubleArray (double arrays)

• jthrowable (java.lang.Throwable object)

Page 21: ALTEN Romania - Java JNI

Mixing C and C++

• Create the header file (example.h)

• Create the C implementation (example.c)

• Call the C++ code from C (example.cpp)

Page 22: ALTEN Romania - Java JNI

Accessing Java Strings • jstring is different than char*

• Conversion to/from UTF-8 or Unicode

• From jstring to char* char* GetStringUTFChars(JNIEnv*, jstring, jboolean*)

• From char* to jstring jstring NewStringUTF(JNIEnv*, char*)

• Length can be obtained with jsize GetStringUTFLength(JNIEnv *env, jstring string);

• Cleanup with ReleaseStringUTFChars(JNIEnv *env, jstring string, const char *utf)

• For Unicode GetStringChars, ReleaseStringChars, NewString, GetStringLength, GetStringRegion

Page 23: ALTEN Romania - Java JNI

Accessing Java Arrays

• Cannot iterate directly on jarray or subclasses

• A pointer to the elements of the array must be obtained

• This pointer can be used in indexed access jint *body = (*env)->GetIntArrayElements(env, arr, 0);

for (i=0; i<len; i++) { sum += body[i]; }

• The JVM provides a non-movable array – a reference to the Java array or a copy

• The reference to the array must be released

Page 24: ALTEN Romania - Java JNI

Calling Java Methods

• Java methods can be called from native code JNIEXPORT void JNICALL

Java_Callbacks_nativeMethod(JNIEnv *env, jobject obj, jint depth)

{

jclass cls = (*env)->GetObjectClass(env, obj);

jmethodID mid = (*env)->GetMethodID(env, cls, "callback", "(I)V");

if (mid == 0) {

return;

}

printf("In C, depth = %d, about to enter Java\n", depth);

(*env)->CallVoidMethod(env, obj, mid, depth);

printf("In C, depth = %d, back from Java\n", depth);

}

• To find out a method signature, you can construct it from the docs, or use javap -s [-p]

• Method IDs should be cached

• A reference to the class should exist as long as the cache

Page 25: ALTEN Romania - Java JNI

Local and global references

• By default JNI creates local references

• They are invalidated exiting the native method

• Local references cannot be cached

• JNI allows to create global references

• Global references must be explicitly freed jobject NewGlobalRef(JNIEnv *env, jobject obj);

void DeleteGlobalRef(JNIEnv *env, jobject globalRef);

jobject NewLocalRef(JNIEnv *env, jobject ref);

void DeleteLocalRef(JNIEnv *env, jobject localRef);

Page 26: ALTEN Romania - Java JNI

Caching Method ID /* This code is OK */ static jclass cls = 0; static jfieldID fld; JNIEXPORT void JNICALL Java_FieldAccess_accessFields(JNIEnv *env, jobject obj) { ... if (cls == 0) { jclass cls1 = (*env)->GetObjectClass(env, obj); if (cls1 == 0) ... /* error */ cls = (*env)->NewGlobalRef(env, cls1); if (cls == 0) ... /* error */ fid = (*env)->GetStaticFieldID(env, cls, "si", "I"); } ... /* access the field using cls and fid */ }

see: http://journals.ecs.soton.ac.uk/java/tutorial/native1.1/implementing/refs.html

Page 27: ALTEN Romania - Java JNI

Accessing Java Fields

• Native code can obtain access to static or instance fields

• The field ID must be obtained jfieldID GetFieldID(JNIEnv *env, jclass clazz, const char *name, const char *sig);

jfieldID GetStaticFieldID(JNIEnv *env, jclass clazz, const char *name, const char *sig);

• To get the value, call the appropriate function Get<type>Field

GetStatic<type>Field

• Field signatures are obtained as methods

Page 28: ALTEN Romania - Java JNI

Catching and Throwing Exceptions

• Java code called from native can throw an exception

• JNI provides functions to check, print and clear an exception

• If an exception has occurred the native code should not continue before clearing that exception

• Native code can create and throw exceptions to Java code jint Throw(JNIEnv *env, jthrowable obj);

jint ThrowNew(JNIEnv *env, jclass clazz,const char *message);

jthrowable ExceptionOccurred(JNIEnv *env);

void ExceptionDescribe(JNIEnv *env);

void ExceptionClear(JNIEnv *env);

jboolean ExceptionCheck(JNIEnv *env);

Page 29: ALTEN Romania - Java JNI

Threads and native methods

• The JNI interface pointer (JNIEnv *) is only valid in the current thread

• Local references must not be passed from one thread to another

• Global references should be safely accessed from multiple threads

• The equivalent of synchronized block in Java can be achieved in native code with 2 JNI functions

jint MonitorEnter(JNIEnv *env, jobject obj); jint MonitorExit(JNIEnv *env, jobject obj);

Page 30: ALTEN Romania - Java JNI

Java ByteBuffer

• JNI functions for accessing java.nio.ByteBuffer jobject NewDirectByteBuffer(JNIEnv* env, void* address, jlong capacity);

void* GetDirectBufferAddress(JNIEnv* env, jobject buf);

jlong GetDirectBufferCapacity(JNIEnv* env, jobject buf);

• Objects can be created either native or Java side

• Java side, use ByteBuffer.allocateDirect() and retrieve with GetDirectBufferAddress native side

• Can be used as parameter in a Java callback

Page 31: ALTEN Romania - Java JNI

Invoking the JVM

• Example code #include <jni.h> /* where everything is defined */ ... JavaVM *jvm; /* denotes a Java VM */ JNIEnv *env; /* pointer to native method interface */ JavaVMInitArgs vm_args; /* JDK/JRE 6 VM initialization arguments */ JavaVMOption* options = new JavaVMOption[1]; options[0].optionString = "-Djava.class.path=/usr/lib/java"; vm_args.version = JNI_VERSION_1_6; vm_args.nOptions = 1; vm_args.options = options; vm_args.ignoreUnrecognized = false; /* load and initialize a Java VM, return a JNI interface * pointer in env */ JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args); delete options; /* invoke the Main.test method using the JNI */ jclass cls = env->FindClass("Main"); jmethodID mid = env->GetStaticMethodID(cls, "test", "(I)V"); env->CallStaticVoidMethod(cls, mid, 100); /* We are done. */ jvm->DestroyJavaVM();

• Compile with include directory containing jni.h and link with javai.lib

• Specify at runtime the place to the libjav.so/javai.dll

Page 32: ALTEN Romania - Java JNI

Java Native Access (JNA)

• Allows to access native code through mapping

• Native libraries are loaded dynamically

• Mapping can be explicit or implicit

• Types are automatically converted

• Features: Primitive Types, Pointers, Strings, Wide (UNICODE) Strings, Primitive Arrays, Buffers/Memory Blocks, Callbacks/Function Pointers, Variable Argument Lists (Varargs), Structures, Unions, Java Objects, Last Error

• https://github.com/twall/jna

Page 33: ALTEN Romania - Java JNI

JEP 191: Foreign Function Interface

• Continues the JNR project: https://github.com/jnr/jnr-ffi

• Allows to load native libraries without writing JNI

• No C glue code between Java and native libraries

• Used by JRuby Projec

• From http://openjdk.java.net/jeps/191: The JDK FFI API will provide the following to JDK developers:

A metadata system to describe native library calls (call protocol, argument list structure, argument types, return type) and native memory structure (size, layout, typing, lifecycle).

Mechanisms for discovering and loading native libraries. These capabilities may be provided by current System.loadLibrary or may include additional enhancements for locating platform or version-specific binaries appropriate to the host system.

Mechanisms for binding, based on metadata, a given library/function coordinate to a Java endpoint, likely via a user-defined interface backed by plumbing to make the native downcall.

Mechanisms for binding, based on metadata, a specific memory structure (layout, endianness, logical types) to a Java endpoint, either via a user-defined interface or a user-defined class, in both cases backed by plumbing to manage a real block of native memory.

Appropriate support code for marshaling Java data types to native data types and vice-versa. This will in some cases require the creation of FFI-specific types to support bit widths and numeric signs that Java can't represent.

Page 34: ALTEN Romania - Java JNI

Conclusions

• Integrating Java and C/C++ is possible

• There are some benefits since the languages and environments have different strengths

• There are various methods to perform the integration

• JNI has the advantage of the Invocation API

• The integration between Java and C/C++ is rather interesting for research activities

Page 35: ALTEN Romania - Java JNI

Perspectives

• JNA/JNR provides an advanced basis for new JVM features the JEP 191

• In Java 9 the JEP 102 Process API will be enhanced and the JEP 191 is under study

• The integration between Java and C/C++ seems to face a paradox: the more the JVM will be integrated in the OS the more appealing C/C++ integration will become

Page 36: ALTEN Romania - Java JNI

Java JNI & alternatives

Q&A contact: [email protected]