BAEL-1637: Guide to JNI(Java Native Interface) (#4066)
* BAEL-1546: Java 8 Math additions * Applied feedback to Unit Tests * BAEL-1546 Added missing test annotations * Added code for BAEL-1637 * Added script for Windows C++ code compile * Added compilation script for MacOS * Added some Unit tests
This commit is contained in:
parent
1b7361a197
commit
62b5a591af
BIN
jni/native/linux_x86_64/libnative.so
Executable file
BIN
jni/native/linux_x86_64/libnative.so
Executable file
Binary file not shown.
BIN
jni/native/macos/libnative.dylib
Executable file
BIN
jni/native/macos/libnative.dylib
Executable file
Binary file not shown.
BIN
jni/native/win32/native.dll
Normal file
BIN
jni/native/win32/native.dll
Normal file
Binary file not shown.
15
jni/pom.xml
Normal file
15
jni/pom.xml
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<groupId>com.baeldung</groupId>
|
||||||
|
<artifactId>jni</artifactId>
|
||||||
|
<version>0.0.1-SNAPSHOT</version>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>junit</groupId>
|
||||||
|
<artifactId>junit</artifactId>
|
||||||
|
<version>4.8.1</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</project>
|
48
jni/src/main/cpp/com_baeldung_jni_ExampleObjectsJNI.cpp
Normal file
48
jni/src/main/cpp/com_baeldung_jni_ExampleObjectsJNI.cpp
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
#include "com_baeldung_jni_ExampleObjectsJNI.h"
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: com_baeldung_jni_ExampleObjectsJNI
|
||||||
|
* Method: createUser
|
||||||
|
* Signature: (Ljava/lang/String;D)Lcom/baeldung/jni/UserData;
|
||||||
|
*/
|
||||||
|
JNIEXPORT jobject JNICALL Java_com_baeldung_jni_ExampleObjectsJNI_createUser
|
||||||
|
(JNIEnv *env, jobject thisObject, jstring name, jdouble balance){
|
||||||
|
|
||||||
|
// Create the object of the class UserData
|
||||||
|
jclass userDataClass = env->FindClass("com/baeldung/jni/UserData");
|
||||||
|
jobject newUserData = env->AllocObject(userDataClass);
|
||||||
|
|
||||||
|
// Get UserData fields to set
|
||||||
|
jfieldID nameField = env->GetFieldID(userDataClass , "name", "Ljava/lang/String;");
|
||||||
|
jfieldID balanceField = env->GetFieldID(userDataClass , "balance", "D");
|
||||||
|
|
||||||
|
// Set the values of the new object
|
||||||
|
env->SetObjectField(newUserData, nameField, name);
|
||||||
|
env->SetDoubleField(newUserData, balanceField, balance);
|
||||||
|
|
||||||
|
// Return the created object
|
||||||
|
return newUserData;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: com_baeldung_jni_ExampleObjectsJNI
|
||||||
|
* Method: printUserData
|
||||||
|
* Signature: (Lcom/baeldung/jni/UserData;)Ljava/lang/String;
|
||||||
|
*/
|
||||||
|
JNIEXPORT jstring JNICALL Java_com_baeldung_jni_ExampleObjectsJNI_printUserData
|
||||||
|
(JNIEnv *env, jobject thisObject, jobject userData){
|
||||||
|
|
||||||
|
// Find the class method id
|
||||||
|
jclass userDataClass = env->GetObjectClass(userData);
|
||||||
|
jmethodID methodId = env->GetMethodID(userDataClass, "getUserInfo", "()Ljava/lang/String;");
|
||||||
|
|
||||||
|
// Call the object method and get the result
|
||||||
|
jstring result = (jstring)env->CallObjectMethod(userData, methodId);
|
||||||
|
|
||||||
|
// Print the result
|
||||||
|
std::cout << "C++: User data is: " << env->GetStringUTFChars(result, NULL) << std::endl;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
29
jni/src/main/cpp/com_baeldung_jni_ExampleObjectsJNI.h
Normal file
29
jni/src/main/cpp/com_baeldung_jni_ExampleObjectsJNI.h
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
/* DO NOT EDIT THIS FILE - it is machine generated */
|
||||||
|
#include <jni.h>
|
||||||
|
/* Header for class com_baeldung_jni_ExampleObjectsJNI */
|
||||||
|
|
||||||
|
#ifndef _Included_com_baeldung_jni_ExampleObjectsJNI
|
||||||
|
#define _Included_com_baeldung_jni_ExampleObjectsJNI
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
/*
|
||||||
|
* Class: com_baeldung_jni_ExampleObjectsJNI
|
||||||
|
* Method: createUser
|
||||||
|
* Signature: (Ljava/lang/String;D)Lcom/baeldung/jni/UserData;
|
||||||
|
*/
|
||||||
|
JNIEXPORT jobject JNICALL Java_com_baeldung_jni_ExampleObjectsJNI_createUser
|
||||||
|
(JNIEnv *, jobject, jstring, jdouble);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: com_baeldung_jni_ExampleObjectsJNI
|
||||||
|
* Method: printUserData
|
||||||
|
* Signature: (Lcom/baeldung/jni/UserData;)V
|
||||||
|
*/
|
||||||
|
JNIEXPORT jstring JNICALL Java_com_baeldung_jni_ExampleObjectsJNI_printUserData
|
||||||
|
(JNIEnv *, jobject, jobject);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#endif
|
34
jni/src/main/cpp/com_baeldung_jni_ExampleParametersJNI.cpp
Normal file
34
jni/src/main/cpp/com_baeldung_jni_ExampleParametersJNI.cpp
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
#include "com_baeldung_jni_ExampleParametersJNI.h"
|
||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: com_baeldung_jni_ExampleParametersJNI
|
||||||
|
* Method: sumIntegers
|
||||||
|
* Signature: (II)J
|
||||||
|
*/
|
||||||
|
JNIEXPORT jlong JNICALL Java_com_baeldung_jni_ExampleParametersJNI_sumIntegers (JNIEnv* env, jobject thisObject, jint first, jint second){
|
||||||
|
std::cout << "C++: The numbers received are : " << first << " and " << second << std::endl;
|
||||||
|
return (long)first + (long)second;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: com_baeldung_jni_ExampleParametersJNI
|
||||||
|
* Method: sayHelloToMe
|
||||||
|
* Signature: (Ljava/lang/String;Z)Ljava/lang/String;
|
||||||
|
*/
|
||||||
|
JNIEXPORT jstring JNICALL Java_com_baeldung_jni_ExampleParametersJNI_sayHelloToMe (JNIEnv* env, jobject thisObject, jstring name, jboolean isFemale){
|
||||||
|
const char* nameCharPointer = env->GetStringUTFChars(name, NULL);
|
||||||
|
std::cout << "C++: The string received is: " << nameCharPointer << std::endl;
|
||||||
|
std::string title;
|
||||||
|
if(isFemale){
|
||||||
|
title = "Ms. ";
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
title = "Mr. ";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string fullName = title + nameCharPointer;
|
||||||
|
return env->NewStringUTF(fullName.c_str());
|
||||||
|
}
|
29
jni/src/main/cpp/com_baeldung_jni_ExampleParametersJNI.h
Normal file
29
jni/src/main/cpp/com_baeldung_jni_ExampleParametersJNI.h
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
/* DO NOT EDIT THIS FILE - it is machine generated */
|
||||||
|
#include <jni.h>
|
||||||
|
/* Header for class com_baeldung_jni_ExampleParametersJNI */
|
||||||
|
|
||||||
|
#ifndef _Included_com_baeldung_jni_ExampleParametersJNI
|
||||||
|
#define _Included_com_baeldung_jni_ExampleParametersJNI
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
/*
|
||||||
|
* Class: com_baeldung_jni_ExampleParametersJNI
|
||||||
|
* Method: sumIntegers
|
||||||
|
* Signature: (II)J
|
||||||
|
*/
|
||||||
|
JNIEXPORT jlong JNICALL Java_com_baeldung_jni_ExampleParametersJNI_sumIntegers
|
||||||
|
(JNIEnv*, jobject, jint, jint);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: com_baeldung_jni_ExampleParametersJNI
|
||||||
|
* Method: sayHelloToMe
|
||||||
|
* Signature: (Ljava/lang/String;Z)Ljava/lang/String;
|
||||||
|
*/
|
||||||
|
JNIEXPORT jstring JNICALL Java_com_baeldung_jni_ExampleParametersJNI_sayHelloToMe
|
||||||
|
(JNIEnv*, jobject, jstring, jboolean);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#endif
|
13
jni/src/main/cpp/com_baeldung_jni_HelloWorldJNI.cpp
Normal file
13
jni/src/main/cpp/com_baeldung_jni_HelloWorldJNI.cpp
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
#include "com_baeldung_jni_HelloWorldJNI.h"
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: com_baeldung_jni_HelloWorldJNI
|
||||||
|
* Method: sayHello
|
||||||
|
* Signature: ()Ljava/lang/String;
|
||||||
|
*/
|
||||||
|
JNIEXPORT jstring JNICALL Java_com_baeldung_jni_HelloWorldJNI_sayHello (JNIEnv* env, jobject thisObject) {
|
||||||
|
std::string hello = "Hello from C++ !!";
|
||||||
|
std::cout << hello << std::endl;
|
||||||
|
return env->NewStringUTF(hello.c_str());
|
||||||
|
}
|
21
jni/src/main/cpp/com_baeldung_jni_HelloWorldJNI.h
Normal file
21
jni/src/main/cpp/com_baeldung_jni_HelloWorldJNI.h
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
/* DO NOT EDIT THIS FILE - it is machine generated */
|
||||||
|
#include <jni.h>
|
||||||
|
/* Header for class com_baeldung_jni_HelloWorldJNI */
|
||||||
|
|
||||||
|
#ifndef _Included_com_baeldung_jni_HelloWorldJNI
|
||||||
|
#define _Included_com_baeldung_jni_HelloWorldJNI
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
/*
|
||||||
|
* Class: com_baeldung_jni_HelloWorldJNI
|
||||||
|
* Method: sayHello
|
||||||
|
* Signature: ()V
|
||||||
|
*/
|
||||||
|
JNIEXPORT jstring JNICALL Java_com_baeldung_jni_HelloWorldJNI_sayHello
|
||||||
|
(JNIEnv *, jobject);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#endif
|
6
jni/src/main/cpp/generateNativeLib.bat
Normal file
6
jni/src/main/cpp/generateNativeLib.bat
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
REM Create the header with javac -h . ClassName.java
|
||||||
|
REM Remember to set your JAVA_HOME env var
|
||||||
|
g++ -c -I%JAVA_HOME%\include -I%JAVA_HOME%\include\win32 com_baeldung_jni_HelloWorldJNI.cpp -o com_baeldung_jni_HelloWorldJNI.o
|
||||||
|
g++ -c -I%JAVA_HOME%\include -I%JAVA_HOME%\include\win32 com_baeldung_jni_ExampleParametersJNI.cpp -o com_baeldung_jni_ExampleParametersJNI.o
|
||||||
|
g++ -c -I%JAVA_HOME%\include -I%JAVA_HOME%\include\win32 com_baeldung_jni_ExampleObjectsJNI.cpp -o com_baeldung_jni_ExampleObjectsJNI.o
|
||||||
|
g++ -shared -o ..\..\..\native\win32\native.dll com_baeldung_jni_HelloWorldJNI.o com_baeldung_jni_ExampleParametersJNI.o com_baeldung_jni_ExampleObjectsJNI.o -Wl,--add-stdcall-alias
|
7
jni/src/main/cpp/generateNativeLib.sh
Executable file
7
jni/src/main/cpp/generateNativeLib.sh
Executable file
@ -0,0 +1,7 @@
|
|||||||
|
# Create the header with javac -h . ClassName.java
|
||||||
|
# Remember to set your JAVA_HOME env var
|
||||||
|
g++ -c -fPIC -I${JAVA_HOME}/include -I${JAVA_HOME}/include/linux com_baeldung_jni_HelloWorldJNI.cpp -o com_baeldung_jni_HelloWorldJNI.o
|
||||||
|
g++ -c -fPIC -I${JAVA_HOME}/include -I${JAVA_HOME}/include/linux com_baeldung_jni_ExampleParametersJNI.cpp -o com_baeldung_jni_ExampleParametersJNI.o
|
||||||
|
g++ -c -fPIC -I${JAVA_HOME}/include -I${JAVA_HOME}/include/linux com_baeldung_jni_ExampleObjectsJNI.cpp -o com_baeldung_jni_ExampleObjectsJNI.o
|
||||||
|
g++ -shared -fPIC -o ../../../native/linux_x86_64/libnative.so com_baeldung_jni_HelloWorldJNI.o com_baeldung_jni_ExampleParametersJNI.o com_baeldung_jni_ExampleObjectsJNI.o -lc
|
||||||
|
# Don't forget to set java.library.path to point to the folder where you have the libnative you're loading.
|
6
jni/src/main/cpp/generateNativeLibMac.sh
Executable file
6
jni/src/main/cpp/generateNativeLibMac.sh
Executable file
@ -0,0 +1,6 @@
|
|||||||
|
# Create the header with javac -h . ClassName.java
|
||||||
|
# Remember to set your JAVA_HOME env var
|
||||||
|
g++ -c -fPIC -I${JAVA_HOME}/include -I${JAVA_HOME}/include/darwin com_baeldung_jni_HelloWorldJNI.cpp -o com_baeldung_jni_HelloWorldJNI.o
|
||||||
|
g++ -c -fPIC -I${JAVA_HOME}/include -I${JAVA_HOME}/include/darwin com_baeldung_jni_ExampleParametersJNI.cpp -o com_baeldung_jni_ExampleParametersJNI.o
|
||||||
|
g++ -c -fPIC -I${JAVA_HOME}/include -I${JAVA_HOME}/include/darwin com_baeldung_jni_ExampleObjectsJNI.cpp -o com_baeldung_jni_ExampleObjectsJNI.o
|
||||||
|
g++ -dynamiclib -o ../../../native/macos/libnative.dylib com_baeldung_jni_HelloWorldJNI.o com_baeldung_jni_ExampleParametersJNI.o com_baeldung_jni_ExampleObjectsJNI.o -lc
|
19
jni/src/main/java/com/baeldung/jni/ExampleObjectsJNI.java
Normal file
19
jni/src/main/java/com/baeldung/jni/ExampleObjectsJNI.java
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
package com.baeldung.jni;
|
||||||
|
|
||||||
|
public class ExampleObjectsJNI {
|
||||||
|
|
||||||
|
static {
|
||||||
|
System.loadLibrary("native");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
ExampleObjectsJNI instance = new ExampleObjectsJNI();
|
||||||
|
UserData newUser = instance.createUser("John Doe", 450.67);
|
||||||
|
instance.printUserData(newUser);
|
||||||
|
}
|
||||||
|
|
||||||
|
public native UserData createUser(String name, double balance);
|
||||||
|
|
||||||
|
public native String printUserData(UserData user);
|
||||||
|
|
||||||
|
}
|
20
jni/src/main/java/com/baeldung/jni/ExampleParametersJNI.java
Normal file
20
jni/src/main/java/com/baeldung/jni/ExampleParametersJNI.java
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
package com.baeldung.jni;
|
||||||
|
|
||||||
|
public class ExampleParametersJNI {
|
||||||
|
|
||||||
|
static {
|
||||||
|
System.loadLibrary("native");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
System.out.println("Java: My full name: " + new ExampleParametersJNI().sayHelloToMe("Martin", false));
|
||||||
|
long sumFromNative = new ExampleParametersJNI().sumIntegers(456, 44);
|
||||||
|
System.out.println("Java: The sum coming from native code is: " + sumFromNative);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Declare another method sumIntegers that receives two integers and return a long with the sum
|
||||||
|
public native long sumIntegers(int first, int second);
|
||||||
|
|
||||||
|
// Declare another method sayHelloToMe that receives the name and gender and returns the proper salutation
|
||||||
|
public native String sayHelloToMe(String name, boolean isFemale);
|
||||||
|
}
|
15
jni/src/main/java/com/baeldung/jni/HelloWorldJNI.java
Normal file
15
jni/src/main/java/com/baeldung/jni/HelloWorldJNI.java
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
package com.baeldung.jni;
|
||||||
|
|
||||||
|
public class HelloWorldJNI {
|
||||||
|
|
||||||
|
static {
|
||||||
|
System.loadLibrary("native");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
new HelloWorldJNI().sayHello();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Declare a native method sayHello() that receives no arguments and returns void
|
||||||
|
public native String sayHello();
|
||||||
|
}
|
11
jni/src/main/java/com/baeldung/jni/UserData.java
Normal file
11
jni/src/main/java/com/baeldung/jni/UserData.java
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
package com.baeldung.jni;
|
||||||
|
|
||||||
|
public class UserData {
|
||||||
|
|
||||||
|
public String name;
|
||||||
|
public double balance;
|
||||||
|
|
||||||
|
public String getUserInfo() {
|
||||||
|
return "[name]=" + name + ", [balance]=" + balance;
|
||||||
|
}
|
||||||
|
}
|
58
jni/src/test/java/com/baeldung/jni/JNINativeTests.java
Normal file
58
jni/src/test/java/com/baeldung/jni/JNINativeTests.java
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
package com.baeldung.jni;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertNotNull;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
public class JNINativeTests {
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setup() {
|
||||||
|
System.loadLibrary("native");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void whenNativeHelloWorld_thenOutputIsAsExpected() {
|
||||||
|
HelloWorldJNI helloWorld = new HelloWorldJNI();
|
||||||
|
String helloFromNative = helloWorld.sayHello();
|
||||||
|
assertTrue(!helloFromNative.isEmpty() && helloFromNative.equals("Hello from C++ !!"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void whenSumNative_thenResultIsArithmeticallyCorrect() {
|
||||||
|
ExampleParametersJNI parametersNativeMethods = new ExampleParametersJNI();
|
||||||
|
assertTrue(parametersNativeMethods.sumIntegers(200, 400) == 600L);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void whenSayingNativeHelloToMe_thenResultIsAsExpected() {
|
||||||
|
ExampleParametersJNI parametersNativeMethods = new ExampleParametersJNI();
|
||||||
|
assertEquals(parametersNativeMethods.sayHelloToMe("Orange", true), "Ms. Orange");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void whenCreatingNativeObject_thenObjectIsNotNullAndHasCorrectData() {
|
||||||
|
String name = "Iker Casillas";
|
||||||
|
double balance = 2378.78;
|
||||||
|
ExampleObjectsJNI objectsNativeMethods = new ExampleObjectsJNI();
|
||||||
|
UserData userFromNative = objectsNativeMethods.createUser(name, balance);
|
||||||
|
assertNotNull(userFromNative);
|
||||||
|
assertEquals(userFromNative.name, name);
|
||||||
|
assertTrue(userFromNative.balance == balance);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void whenNativeCallingObjectMethod_thenResultIsAsExpected() {
|
||||||
|
String name = "Sergio Ramos";
|
||||||
|
double balance = 666.77;
|
||||||
|
ExampleObjectsJNI objectsNativeMethods = new ExampleObjectsJNI();
|
||||||
|
UserData userData = new UserData();
|
||||||
|
userData.name = name;
|
||||||
|
userData.balance = balance;
|
||||||
|
assertEquals(objectsNativeMethods.printUserData(userData), "[name]=" + name + ", [balance]=" + balance);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user