티스토리 뷰



JRE 또는 JDK를 설치하여 자바 가상 머신(JVM, Java Virtual Machine)이 준비되어야 실행 가능한 자바(Java) 프로그램은 기본적으로는 특정 프로세서의 기계어(Machine)로 제작된 실행 파일이나 라이브러리를 사용할 수 없습니다. 자바 프로그램은 컴파일하면 특정 프로세서가 인식할 수 있는 기계어가 아닌 p-code 또는 바이트 코드라 불리는 중간 코드 형태로 만들어 지기 때문입니다. 닷넷 플랫폼에서 C#이나 VB.NET으로 프로그램을 개발하면 CIL(Common Intermediate Language)이라는 중간코드로 제작되는 것과 같은 이치입니다. 그렇지만, 서드파티에서 제공하는 모듈과 연동해야 되거나 코드 재개발이 난해한 기존 라이브러리를 활용해야 하는 경우가 있기 때문에 이런 경우를 위해서 만들어진 장치가 JNI((Java Native Interface)와 JNA(Java Native Access)입니다.


JNI((Java Native Interface)와 JNA(Java Native Access) 모두 자바 중간 코드가 아닌 특정 프로세서 아키텍처에 맞도록 작성된 라이브러리를 사용할 수 있도록 하여 개발자 및 사용자에게 편의성은 제공하지만 이렇게 제작된 응용 프로그램은 특정 플랫폼에 종속될 수 밖에 없기 때문에 자바의 핵심 특성인 크로스 플랫폼이라는 장점을 훼손시킬 수 밖에 없습니다. 어찌했든 JNI는 자바 시스템에서 공식적으로 지원하는 인터페이스이고 자바 시스템에서 원하는 형태로 C++ 라이브러리를 제작하거나 JNI 규격에 따르는 동시에 기존 라이브러리를 호출하는 C++ 래핑(Wrapping) 라이브러리를 제작해야 한다는 특성이 있습니다. C++ Wrapping 클래스 제작이라는 과정이 반드시 필요한 단점이 있습니다. 반면에 JNA는 커뮤니티에서 젲가하고 있는 라이브러리(깃허브에서 배포)이기 때문에 다운로드 받아서 프로젝트에 포함시켜야 하는 약간의 불편함이 있지만 래핑 클래스 제작 없이 간편하게 기존 라이브러리를 자바에서 사용할 수 있습니다.


JNI를 이용해서 개발하는 과정은 아래와 같습니다.(윈도우에서 JDK가 설치되어 있는 MinGW를 설치하여 C++프로그램을 컴파일하는 환경입니다)

public class JNITest {
	public native void dosdir();
	public native void dosecho(String str);

	static { System.loadLibrary("jnidll.dll"); }
	
	public static void main(String args[]) {
		JNITest wrapcls = new JNITest();
		wrapcls.dosdir();
		wrapcls.dosecho("Hello World");
	}
}

2개의 C++ 함수를 jnidll.dll이란 라이브러리에서 호출하는 형태를 테스트하는 구조입니다. 위의 예제 클래스에서는 클래스 내부에 main()을 두어 테스트를 진행했지만 실제로는 mani()없이 자바쪽 래핑 클래스로 다른 클래스에서 호출할 메소드를 선언만 있는 스터브(stub) 형태로 정의합니다.

javac JNITest.java

코드가 준비되었으면 위와 같이 컴파일하여 코드에 문제가 없는지 확인하면서 자바 프로그램을 빌드합니다.


javah JNITest

javah 도구를 사용해서 C++ 헤더 파일을 생성합니다. "javah 클래스명"으로 입력하면 됩니다.

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class JNITest */

#ifndef _Included_JNITest
#define _Included_JNITest
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     JNITest
 * Method:    dosdir
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_JNITest_dosdir
  (JNIEnv *, jobject);

/*
 * Class:     JNITest
 * Method:    dosecho
 * Signature: (Ljava/lang/String;)V
 */
JNIEXPORT void JNICALL Java_JNITest_dosecho
  (JNIEnv *, jobject, jstring);

#ifdef __cplusplus
}
#endif
#endif

위의 코드는 javah로 생성된 C/C++ 헤더 파일입니다.


#include <stdio.h>
#include <jni.h>
#include "JNITest.h"
 
JNIEXPORT void JNICALL Java_JNITest_dosdir(JNIEnv *env, jobject thisObj) {
   system("dir");
   return;
}

JNIEXPORT void JNICALL Java_JNITest_dosecho(JNIEnv *env, jobject thisObj, jstring msg) {
   printf("\n%s", msg);
   return;
}

위의 코드는 Java 프로그램에서 Stub로 선언한 실제 함수를 C언어로 작성한 것입니다. 작성해야 할 함수의 프로토타입은 javah로 생성한 헤더 파일을 참조합니다. 함수의 이름을 보면 "Java_클래스명_"이 앞에 붙는 것을 확인할 수 있습니다. 자바 응용에서는 dosdir()로 호출하지만 실제 DLL에서는 Java_JNITest_dosdir()을 호출한다는 것입니다. 또한 함수 파라미터 역시 JNIEnv 와 jobject  타입의 변수가 기본적으로 배치되고 이후에 사용자가 지정한 파라미터가 오는 것을 확인할 수 있습니다. 기존 라이브러리를 수정하지 않고 JNI에서 사용하려면 위와 같은 래핑 함수에서 기존 함수를 호출하는 방식으로 작성하면 됩니다.

gcc -Wl,--add-stdcall-alias -I"C:\Program Files\Java\jdk1.7.0_45\include" -I"C:\Program Files\Java\jdk1.7.0_45\include\win32" -shared -o jnidll.dll JNITest.c

C 헤더와 소스 코드가 준비되면 위와 같이 gcc로 컴파일하면 JNI에서 활용할 수 있는 DLL을 제작할 수 있습니다. 이제 앞서 준비한 자바 코드를 "java JNITest"와 같이 실행해서 테스트하면 됩니다.


그런데, JNA에서는 JNI와 같은 C++ DLL제작 과정이 필요없습니다. 사전에 JNA 사용을 위한 JAR 파일만 다운로드 받아서 라이브러리로 추가만 해주면 간편하게 C/C++ 라이브러리를 사용할 수 있습니다.


https://github.com/java-native-access/jna


jna.jar파일은 위의 주소에서 다운로드 받을 수 있습니다. 위의 페이지를 열고 "Download" 섹션에 있는 jna.jar를 다운로드 받으시면 됩니다.

import com.sun.jna.*;

public class PaymentDriver {
    public static native int ModuleVer(byte[] ret);
    static {
        Native.register("/libTEST.dll");
    }
}

라이브러리를 로드하고 함수 정의하는 과정은 JNI와 비슷하지만 JNA는 jna.jar를 빌드에 포함시키고 위와 같이 *.dll에 있는 함수 이름 그대로 프로토타입을 정의하면 별다른 과정 없이 바로 사용할 수 있습니다. 함수 정의 과정에 C++의 타입을 Java 타입으로 적절하게 전환해 주어야 하는데 위의 코드에서 int ModuleVer(byte[] ret);는 C/C++의 프로토타입은 int ModuleVer(char *ret); 입니다.





댓글
댓글쓰기 폼
«   2022/12   »
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
글 보관함