OSRM-JNI


Install OSRM

OSRM-backend를 참고하여 OSRM 라이브러리를 설치한다.

  • 예시(Ubuntu 22.04. 소스코드 빌드)
    eraser@eraser-tmax:/usr/local/lib$ ll | grep osrm
    total 34856
    drwxr-xr-x  4 root root     4096  5월 27 15:41 ./
    drwxr-xr-x 10 root root     4096  4월 19 19:02 ../
    -rw-r--r--  1 root root  8759280  5월 27 15:40 libosrm.a
    -rw-r--r--  1 root root  2106694  5월 27 15:41 libosrm_contract.a
    -rw-r--r--  1 root root  1912114  5월 27 15:41 libosrm_customize.a
    -rw-r--r--  1 root root 12127312  5월 27 15:40 libosrm_extract.a
    -rw-r--r--  1 root root  3410924  5월 27 15:40 libosrm_guidance.a
    -rw-r--r--  1 root root  2315194  5월 27 15:40 libosrm_partition.a
    -rw-r--r--  1 root root  2342522  5월 27 15:41 libosrm_store.a
    -rw-r--r--  1 root root  2690284  5월 27 15:41 libosrm_update.a

Create Java class

JNI 인터페이스를 호출하여 OSRM C++ native code에 접근하기 위한 Java class를 작성한다.

  • native 코드에서 호출할 메서드의 시그니처를 작성한다
    • 메서드 몸체는 작성하지 않는다. 작성 시 native methods do not specify body와 같은 컴파일 에러 발생
  • java class에서의 메서드 시그니처 함수 파라미터 타입과 native 코드 함수 시그니처 파라미터 타입에 주의한다
    • primitive type의 경우 값이 복사되나, reference type의 경우 jvm과 native 메모리 상에서 참조 주소를 전달하는 것에 주의 필요
    • JNI Types and Data Structures 참고
public class OsrmJNI {

    private native int main();

    public int returnMain() {
        return main();
    }
}

Create JNI header file

Java Application에서 C++ native module을 호출하기 위한 헤더 파일을 생성한다.

  • 자바 소스 코드 컴파일: javac OsrmJNI.java
  • 헤더 파일 생성: javac -h . OsrmJNI.java
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_eraser_jniosrm_OsrmJNI */

#ifndef _Included_com_eraser_jniosrm_OsrmJNI
#define _Included_com_eraser_jniosrm_OsrmJNI
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_eraser_jniosrm_OsrmJNI
 * Method:    main
 * Signature: ()I
 */
JNIEXPORT jint JNICALL Java_com_eraser_jniosrm_OsrmJNI_main
  (JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif

Implement native methods in C++

C++ 라이브러리를 이용해 수행할 코드를 작성한다.

컴파일 및 링킹을 통해, .so 파일을 생성한다. 이 프로젝트에서는 OSRM backend에서 제공하는 컴파일 방식을 참고해, CMakeLists.txt를 변경해 빌드했다.

mkdir build
cmake -DCMAKE_CXX_COMPILER=/usr/bin/g++ ..
cmake --build .
# generated build script
/usr/bin/g++ -fPIC  -std=c++14 -DBOOST_TEST_DYN_LINK -DBOOST_SPIRIT_USE_PHOENIX_V3 -DBOOST_RESULT_OF_USE_DECLTYPE -DBOOST_FILESYSTEM_NO_DEPRECATED -I/usr/include/lua5.2 -I/usr/local/include -I/usr/local/include/osrm -D_REENTRANT -O3 -DNDEBUG -shared -Wl,-soname,libosrmjni.so -o libosrmjni.so CMakeFiles/osrmjni.dir/osrmJNI.cpp.o  -L/usr/local/lib -losrm -fuse-ld=gold -Wl,--disable-new-dtags -Wl,--gc-sections -Wl,-O1 -Wl,--hash-style=gnu -Wl,--sort-common -L/usr/local/lib -losrm -fuse-ld=gold -Wl,--disable-new-dtags -Wl,--gc-sections -Wl,-O1 -Wl,--hash-style=gnu -Wl,--sort-common /usr/lib/x86_64-linux-gnu/libboost_regex.so.1.74.0 /usr/lib/x86_64-linux-gnu/libboost_date_time.so.1.74.0 /usr/lib/x86_64-linux-gnu/libboost_chrono.so.1.74.0 /usr/lib/x86_64-linux-gnu/libboost_filesystem.so.1.74.0 /usr/lib/x86_64-linux-gnu/libboost_iostreams.so.1.74.0 /usr/lib/x86_64-linux-gnu/libboost_thread.so.1.74.0 /usr/lib/x86_64-linux-gnu/libboost_system.so.1.74.0 -ltbb -ltbbmalloc -lrt -lz 

Load native library in Java application

자바 어플리케이션에서 빌드한 C++ shared library를 로드한다.

public class OsrmJNI {

    private native int main();

    public int returnMain() {
        return main();
    }

    // load library as static
    static {
        System.load("/home/eraser/projects/osrm-jni/jniosrm/src/src++/build/libosrmjni-example.so");
    }
}

Accessing data between Java application and native code


Managing memory allocation

C++ native code에서 객체를 생성하였다면,

  • JVM으로 반환되는 경우
  • JVM으로 반환되지 않고 더 이상 사용되지 않는 경우

local reference로 생성되는 것들은 굳이 해제하지 않아도 된다. 잘못 해제하면 jfieldID 등 reference가 없어서 오류 발생


TODO