株式会社イーブ|未経験・転職の方も就職可能。Javaプログラマー育成のエキスパート

HOMEJAVA技術者育成システム開発求人情報個人情報保護 移転案内

Javaワンポイント

ネイティブアプリケーションからJavaVMを実行する

JNIではネイティブメソッドを実装するためのAPIだけでなくJavaVMを作成するAPIも用意されています。 これによりJavaプログラムをネイティブアプリケーションから実行することができます。

ではこのAPIを用いてネイティブアプリケーションからJavaVMを作成してHelloWorldを表示してみましょう。


hello.c:
#include 

int main(int argc, char *argv[])
{
  JavaVM *jvm;
  JNIEnv *env;
  JavaVMInitArgs vm_args;
  jclass System, PrintStream;
  jfieldID fid_out;
  jobject out;
  jmethodID mid_println;
  jstring message;

  vm_args.version = JNI_VERSION_1_6; /* 使用するJVMのバージョン */
  JNI_GetDefaultJavaVMInitArgs(&vm_args); /* JVMのオプションをデフォルト値に初期化 */

  JNI_CreateJavaVM(&jvm, (void **) &env, &vm_args); /* JVMを作成 */

  /* java.lang.System.out.println("Hello, World!"); 相当のコードをJNIで実行 */
  System = (*env)->FindClass(env,"java/lang/System");
  fid_out = (*env)->GetStaticFieldID(env, System, "out",
				     "Ljava/io/PrintStream;");
  (*env)->ExceptionDescribe(env);
  out = (*env)->GetStaticObjectField(env, System, fid_out);
  PrintStream = (*env)->FindClass(env, "java/io/PrintStream");
  mid_println = (*env)->GetMethodID(env, PrintStream, "println",
				    "(Ljava/lang/String;)V");
  message = (*env)->NewStringUTF(env, "Hello, World!");
  (*env)->CallVoidMethod(env, out, mid_println, message);

  (*jvm)->DestroyJavaVM(jvm);	/* JVMを破棄 */

  return 0;
}


このプログラムをコンパイルして実行すると「Hello, World!」という文字列がコンソールに出力されます。

ネイティブメソッドで Hello World

Java には native というキーワードがありますが Java の入門書のキーワード一覧で見たことがあるだけで使い道を知らないという人がほとんどではないでしょうか。
このキーワードは native修飾子といい、メソッドがネイティブメソッドであることを示します。 ネイティブメソッドとはメソッドがJavaではない他の言語で実装されているメソッドのことです。 他の言語でメソッドを実装することでOSのシステムコールやライブラリを呼び出すことができるようになりますが、 プラットフォームに依存した実装になるため Java の「Write Once, Run Anywhere」というスローガンは当てはまらなくなります。
では、実際にこの native修飾子を使って以下の Hello World プログラムをネイティブメソッドで書き直してみましょう。

hello/Hello.java

package hello;
public class Hello {
    public static void main(String[] args) {
        System.out.println("Hello, World!");
    }
}

実行結果

>java hello.Hello
Hello, World!

ネイティブメソッド版の Hello World は以下のようになります。

hello/JNIHello.java

package hello;
public class JNIHello {
    static {
        System.loadLibrary("hello_JNIHello");
    }
    public static native void main(String[] args);
}

System.loadLibrary() メソッドは引数の名前のダイナミックライブラリをロードするメソッドです。 このメソッドはネイティブメソッドを呼び出す前に呼び出す必要があるので staticブロックに記述し、 クラスがクラスローダにロードされると同時にダイナミックライブラリもロードされるようにしています。今回ロードしているダイナミックライブラリ hello_JNIHello は後でC言語で実装します。
ネイティブメソッドの実装は他の言語で記述するためJavaのソース上では処理を記述しません。
では次にC言語でネイティブメソッドの処理を実装しましょう。 C言語でネイティブメソッドを実装するには JNI を使用します。 JNI とは C/C++ と Java がやりとりを行うための ABI と API の仕様です。
まずは Java のクラスファイルから C/C++ のヘッダファイルを生成します。 以下のコマンドを実行することでヘッダーファイル hello_JNIHello.h が生成されます。

javah -jni hello.JNIHello

生成されたヘッダファイルの内容は以下のようになります。

hello_JNIHello.h

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

#ifndef _Included_hello_JNIHello
#define _Included_hello_JNIHello
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     hello_JNIHello
 * Method:    main
 * Signature: ([Ljava/lang/String;)V
 */
JNIEXPORT void JNICALL Java_hello_JNIHello_main
  (JNIEnv *, jclass, jobjectArray);

#ifdef __cplusplus
}
#endif
#endif

C/C++ の Java_hello_JNIHello_main() 関数が Java の hello.JNIHello.main() ネイティブメソッドと対応します。
C言語で実装した Hello World を以下に示します。

hello_JNIHello.c

#include "hello_JNIHello.h"
#include <stdio.h>

JNIEXPORT void JNICALL
Java_hello_JNIHello_main(JNIEnv * env, jclass cls, jobjectArray args)
{
  printf("Hello, World!\n");
}

生成したヘッダファイルをインクルードし、ネイティブメソッドに対応する関数で Hello World を実装しています。
後は hello_JNIHello.c をコンパイル及びリンクし、ダイナミックライブラリを作成します。
ネイティブメソッド版の実行結果は以下のようになります。

 実行結果

>java -Djava.library.path=. hello.JNIHello
Hello, World!

-Djava.library.path=. は System.loadLibrary() メソッドでロードされるダイナミックライブラリが存在するディレクトリを指定しています。

以上、ネイティブメソッドで Hello World でした。

第 112 回 ~ ソケットチャンネルの入出力 ~

引き続き「ソケットチャンネルの入出力」です。


今回はDatagramChannelによる通信で受信側のプログラムを実装します。
DatagramChannelもSelectableChannelを継承しているので、非ブロックモードで入出力を行うことができます。


次のサンプルコードは、データを受信するプログラムです。



import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.DatagramChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.charset.Charset;
import java.util.Iterator;


public class Main {


    public static void main(String[] args) {
        DatagramChannel datagramChannel = null;
        try {
            datagramChannel = DatagramChannel.open();
            InetSocketAddress inetSocketAddress = new InetSocketAddress(9999);
            datagramChannel.socket().bind(inetSocketAddress);
            datagramChannel.configureBlocking(false);
            Selector selector = Selector.open();
            datagramChannel.register(selector, SelectionKey.OP_READ);
            while (selector.select() > 0) {
                for (Iterator<SelectionKey> iterator = selector.selectedKeys().iterator(); iterator.hasNext();) {
                    SelectionKey selectionKey = iterator.next();
                    iterator.remove();
                    if (selectionKey.isReadable()) {
                        doReceive(selectionKey);
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                datagramChannel.close();
            } catch (Exception e) {}
        }


    }


    private static void doReceive(SelectionKey selectionKey) {
        DatagramChannel datagramChannel = (DatagramChannel) selectionKey.channel();
        ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
        Charset charset = Charset.forName("Shift-JIS");
        try {
            datagramChannel.receive(byteBuffer);
            byteBuffer.flip();
            System.out.println(charset.decode(byteBuffer));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


}



DatagramChannelはconfigureBlockingメソッドで非ブロックモードに設定し、セレクタに登録しています。
DatagramChannelはデータ受信が可能なときにセレクタによって選択されるので、receiveメソッドは待機することなくデータを受信することができます。

第 111 回 ~ ソケットチャンネルの入出力 ~
第 110 回 ~ ソケットチャンネルの入出力 ~
第 109 回 ~ ソケットチャンネルの入出力 ~
第 108 回 ~ ソケットチャンネルの入出力 ~
第 107 回 ~ ファイルチャンネルの入出力 ~
第 106 回 ~ ファイルチャンネルの入出力 ~
第 105 回 ~ ファイルチャンネルの入出力 ~
第 104 回 ~ ファイルチャンネルの入出力 ~
第 103 回 ~ ファイルチャンネルの入出力 ~
第 102 回 ~ バッファの操作 ~
第 101 回 ~ チャンネルの入出力 ~
第 100 回 ~ チャンネルの入出力 ~
第 99 回 ~ ソケットの入出力 ~
第 98 回 ~ ソケットの入出力 ~
第 97 回 ~ ソケットの入出力 ~
第 96 回 ~ ソケットの入出力 ~
第 95 回 ~ ZIPファイルの入出力 ~
第 94 回 ~ ZIPファイルの入出力 ~
第 93 回 ~ 入出力の基本 ~
第 92 回 ~ 入出力の基本 ~
第 91 回 ~ 入出力の基本 ~
第 90 回 ~ 入出力の基本 ~
第 89 回 ~ 入出力の基本 ~
第 88 回 ~ 入出力の基本 ~
第 87 回 ~ Java Print Service API その6 ~
第 86 回 ~ Java Print Service API その5 ~
第 85 回 ~ Java Print Service API その4 ~
第 84 回 ~ Java Print Service API その3 ~
第 83 回 ~ Java Print Service API その2 ~
第 82 回 ~ Java Print Service API その1 ~
第 81 回 ~ JUnit4 その10 ~
第 80 回 ~ JUnit4 その9 ~
第 79 回 ~ JUnit4 その8 ~
第 78 回 ~ JUnit4 その7 ~
第 77 回 ~ JUnit4 その6 ~
第 76 回 ~ JUnit4 その5 ~
第 75 回 ~ JUnit4 その4 ~
第 74 回 ~ JUnit4 その3 ~
第 73 回 ~ JUnit4 その2 ~
第 72 回 ~ JUnit4 その1 ~
第 71 回 ~ ロギングその11 ~
第 70 回 ~ ロギングその10 ~
第 69 回 ~ ロギングその9 ~
第 68 回 ~ ロギングその8 ~
第 67 回 ~ ロギングその7 ~
第 66 回 ~ ロギングその6 ~
第 65 回 ~ ロギングその5 ~
第 64 回 ~ ロギングその4 ~
第 63 回 ~ ロギングその3 ~
第 62 回 ~ ロギングその2 ~
第 61 回 ~ ロギングその1 ~
第 60 回 ~ 割り込み ~
第 59 回 ~ 割り込み ~
第 58 回 ~ Swingのスレッドポリシー ~
第 57 回 ~ Swingのスレッドポリシー ~
第 56 回 ~ ロック編 ReadWriteLockインターフェース ~
第 55 回 ~ ロック編 Conditionインターフェース ~
第 54 回 ~ ロック編 Lockインターフェース ~
第 53 回 ~ ロック編 synchronizedによるロック ~
第 52 回 ~ シンクロナイザ編 Exchanger ~
第 51 回 ~ シンクロナイザ編 Semaphore ~
第 50 回 ~ シンクロナイザ編 CyclicBarrier ~
第 49 回 ~ シンクロナイザ編 CountDownLatch ~
第 48 回 ~ マルチスレッド編 CompletionServiceインターフェース ~
第 47 回 ~ マルチスレッド編 ScheduledExecutorServiceインターフェース ~
第 46 回 ~ マルチスレッド編 ExecutorServiceインターフェース ~
第 45 回 ~ マルチスレッド編 Callableインターフェース RunnableFutureインターフェース ~
第 44 回 ~ BlockingQueueインターフェース ~
第 43 回 ~ Queueインターフェース ~
第 42 回 ~ 線形探索と二分探索 ~
第 41 回 ~ 配列のソート ~
第 40 回 ~ RandomAccessインターフェース ~
第 39 回 ~ Listの操作にかかる時間 ~
第 38 回 ~ Listの範囲操作 ~
第 37 回 ~ ListIterator ~
第 36 回 ~ 拡張for文とIterator ~
第 35 回 ~ コレクションの変更不可ラッパー ~
第 34 回 ~ 集合 ~
第 33 回 ~ コレクションの動的な型保証 ~
第 32 回 ~ 直列化と継承 ~
第 31 回 ~ 直列化のカスタマイズ ~
第 30 回 ~ transient ~
第 29 回 ~ 直列化 ~
第 28 回 ~ シャローコピーとディープコピー ~
第 27 回 ~ cloneメソッド ~
第 26 回 ~ ジェネリックスクラス ~
第 25 回 ~ ジェネリックスメソッド ~
第 24 回 ~ int型の限界 ~
第 24 回 ~ Guiceその2 ~
第 23 回 ~ Guiceその1 ~
第 22 回 ~ アノテーション ~
第 21 回 ~ DI ~
第 20 回 ~ クラスの依存関係 ~
第 19 回 ~ Javaを学ぶ ~
第 18 回 ~ 他言語とのコラボレーション ~
第 17 回 ~ プロセスの実行 ~
第 16 回 ~ ホームページを読み込む ~
第 15 回 ~ 天文学的な数値計算 ~
第 14 回 ~ プロパティキャッシュ ~
第 13 回 ~ 列挙型(enum)使いまくり ~