Invoking JavaVM from C or C++ for manipulating Java object
- はじめに
- Invoking JavaVM for calling HelloWorld Java object
- Invoking JavaVM for calling remote object via HORB
- Invoking JavaVM for calling remote object via RMI
- Invoking JavaVM for calling remote object on JAS via RMI for Geant4
ここではC++やC言語を使ったプログラムからJavaのVMを走らせることによって
Javaのオブジェクトを扱う方法を検討します。CやC++などの既存のプログラムから
Javaの分散オブジェクトを扱うことでより簡単に分散Javaの恩恵に与れます。
Invoking JavaVM for calling HelloWorld Java object
このMakefileは単にHello Worldというメッセージを標準出力に出すだけの
プログラムHelloWorld.javaをC言語で書かれたプログラムcreateJVM.c
から呼ぶためのものです。C言語とJavaを結びつけるためにはJava Native Interface
(JNI)が必要です。また、LIBで定義されているようなJavaのライブラリも
Cプログラムのリンクには必要になります。
#JDK=/usr/asis.local/i386_redhat60/usr.local/libexec/jdk/1.2.2/
JDK=/usr/jdk1.2
CC = gcc
JAVAC = javac
JNI = -I$(JDK)/include/linux -I$(JDK)/include
#LIB = -L$(JDK)/jre/lib/i386/classic -L$(JDK)/jre/lib/i386/native_threads -ljvm -lhpi -L/usr/lib/ -lpthread -lutil
LIB = -L$(JDK)/jre/lib/i386/classic -L$(JDK)/jre/lib/i386/green_threads -ljvm -lhpi
all: createJVM_Hello HelloWorld.class
createJVM.o: createJVM.c
$(CC) $(JNI) -c createJVM.c
createJVM_Hello: createJVM.o
$(CC) -o createJVM_Hello createJVM.o $(LIB)
HelloWorld.class: HelloWorld.java
$(JAVAC) HelloWorld.java
clean:
rm -f createJVM_Hello createJVM.o
さて、下記のプログラムのミソは
res = JNI_CreateJavaVM(&jvm,&env,&vm_args);
と
(*env)->CallStaticVoidMethod(env, cls, mid, args);
です。前者はJava Virtual Machineを起動しています。後者は
HelloWorld.javaをコンパイルして得られたHelloWorld.classを
呼び出して、Javaのプログラムを実行しています。
その他はJavaプログラム実行に必要な準備、たとえばJava classを
見付けたり、引数である文字列をJavaのオブジェクトに変えたりなど
です。
#include <jni.h>
#define PATH_SEPARATOR ':'
#define USER_CLASSPATH "." /* where Prog.class is */
main(int argc, char** argv) {
JNIEnv *env;
JavaVM *jvm;
JDK1_1InitArgs vm_args;
jint res;
jclass cls;
jmethodID mid;
jstring jstr;
jobjectArray args;
char classpath[1024];
/* IMPORTANT: specify vm_args version # if you use JDK1.1.2 and beyond */
vm_args.version = 0x00010001;
JNI_GetDefaultJavaVMInitArgs(&vm_args);
/* Append USER_CLASSPATH to the end of default system class path */
sprintf(classpath, "%s%c%s",
vm_args.classpath, PATH_SEPARATOR, USER_CLASSPATH);
vm_args.classpath = classpath;
/* Create the Java VM */
res = JNI_CreateJavaVM(&jvm,&env,&vm_args);
if (res < 0) {
fprintf(stderr, "Can't create Java VM\n");
exit(1);
}
cls = (*env)->FindClass(env, "HelloWorld");
if (cls == 0) {
fprintf(stderr, "Can't find HelloWorld class\n");
exit(1);
}
mid = (*env)->GetStaticMethodID(env, cls, "main", "([Ljava/lang/String;)V");
if (mid == 0) {
fprintf(stderr, "Can't find HelloWorld.main\n");
exit(1);
}
jstr = (*env)->NewStringUTF(env, (argc==2)? argv[1] : " really from C!");
if (jstr == 0) {
fprintf(stderr, "Out of memory\n");
exit(1);
}
args = (*env)->NewObjectArray(env, 1,
(*env)->FindClass(env, "java/lang/String"), jstr);
if (args == 0) {
fprintf(stderr, "Out of memory\n");
exit(1);
}
(*env)->CallStaticVoidMethod(env, cls, mid, args);
(*jvm)->DestroyJavaVM(jvm);
}
これはJavaのプログラムです。説明の必要はないですね。
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello World : " + args[0]);
}
}
プログラムを実行させる前に、動的にライブラリを見付けに行きますので
パスを設定してあげる必要があります、LD_LIBRARY_PATHです。
setenv JDK /usr/asis.local/i386_redhat60/usr.local/libexec/jdk/1.2.2/
setenv LD_LIBRARY_PATH $JDK/jre/lib/i386/:$JDK/jre/lib/i386/classic:$JDK/jre/lib/i386/native_threads:$LD_LIBRARY_PATH
さてこれは実行例です。CプログラムcreateJVM_Helloが実行されると
下記のようなメッセージがでます。
[yasu@pcatutt1 C-call-JavaHelloWorld]$ createJVM_Hello
Hello World : really from C!
Invoking JavaVM for calling remote object via HORB
このMakefileはHORBデーモン上にあるリモートオブジェクトServer.javaを
CプログラムcallHORBDaemon.cから呼び出すためのものです。
horbcはHORBコンパイラでServer.javaからServer_Skeleton.classと
Server_Proxy.classを生成します。
CC = gcc
JAVAC = javac
JNI = -I$(JDK)/include/linux -I$(JDK)/include
LIB = -L$(JDK)/jre/lib/i386/classic -L$(JDK)/jre/lib/i386/green_threads -ljvm -lhpi
all: callHorbDaemon Server.class Server_Proxy.class Server_Skeleton.class Command.class
test:
java Command
clean:
-rm -f callHorbDaemon Server_Skeleton.java Server_Proxy.java *~
callHorbDaemon.o: callHorbDaemon.c
$(CC) $(JNI) -c callHorbDaemon.c
callHorbDaemon: callHorbDaemon.o
$(CC) -o callHorbDaemon callHorbDaemon.o $(LIB)
fullclean: clean
-rm -f callHorbDaemon callHorbDaemon.o Command.class \
Server_Skeleton.class Server_Proxy.class \
Server.class
Command.class: Server.class Command.java
javac Command.java
Server_Skeleton.class Server_Proxy.class Server.class: Server.java
horbc Server.java
これは以前のcreateJVM.cと基本的には同じことをしています。
#include <jni.h>
#define PATH_SEPARATOR ':'
#define USER_CLASSPATH ".:/usr/asis.local/i386_redhat61/usr.local/libexec/jdk/1.2.2/jre/lib/i386/:/usr/asis.local/i386_redhat61/usr.local/libexec/jdk/1.2.2/jre/lib/i386/classic:/usr/asis.local/i386_redhat61/usr.local/libexec/jdk/1.2.2/jre/lib/i386/green_thread:/afs/cern.ch/user/y/yasu/java/horb/horb2.0/classes" /* where Prog.class is */
main(int argc, char** argv) {
JNIEnv *env;
JavaVM *jvm;
JDK1_1InitArgs vm_args;
jint res;
jclass cls;
jmethodID mid;
jstring jstr;
jobjectArray args;
char classpath[1024];
/* IMPORTANT: specify vm_args version # if you use JDK1.1.2 and beyond */
vm_args.version = 0x00010001;
JNI_GetDefaultJavaVMInitArgs(&vm_args);
/* Append USER_CLASSPATH to the end of default system class path */
sprintf(classpath, "%s%c%s",
vm_args.classpath, PATH_SEPARATOR, USER_CLASSPATH);
vm_args.classpath = classpath;
/* Create the Java VM */
res = JNI_CreateJavaVM(&jvm,&env,&vm_args);
if (res < 0) {
fprintf(stderr, "Can't create Java VM\n");
exit(1);
}
cls = (*env)->FindClass(env, "Server");
if (cls == 0) {
fprintf(stderr, "Can't find Server class\n");
exit(1);
}
mid = (*env)->GetStaticMethodID(env, cls, "main", "([Ljava/lang/String;)V");
if (mid == 0) {
fprintf(stderr, "Can't find HelloWorld.main\n");
exit(1);
}
jstr = (*env)->NewStringUTF(env, (argc==2)? argv[1] : " really from C!");
if (jstr == 0) {
fprintf(stderr, "Out of memory\n");
exit(1);
}
args = (*env)->NewObjectArray(env, 1,
(*env)->FindClass(env, "java/lang/String"), jstr);
if (args == 0) {
fprintf(stderr, "Out of memory\n");
exit(1);
}
printf("module id = %x\n", mid);
(*env)->CallStaticVoidMethod(env, cls, mid, args);
(*jvm)->DestroyJavaVM(jvm);
}
import horb.orb.*;
public class Server {
int numClients = 0;
public static void main(String argv[]) throws HORBException {
// start a HORBServer
HORBServer hs = new HORBServer(8887, "horbServer8887", null);
// register an object as a HORB object
Server server = new Server();
HORBServer.registerObject("Server", server,"daemonServer");
}
synchronized int command(int num) {
System.out.println("current command is "+num);
return ++numClients;
}
}
import horb.orb.*;
class Command {
public static void main(String argv[]) {
int num = (argv.length >= 1) ? Integer.valueOf(argv[0]).intValue() : 1;
HorbURL url = new HorbURL("-", "daemonServer");
Server_Proxy s = new Server_Proxy(url);
int result = s.command(num);
System.out.println(result+" commands have been accpeted to "+s._getObjectURL());
}
}
Invoking JavaVM for calling remote object via RMI
JAVAC = javac -O
RMIC = rmic -keepgenerated -O
CC = gcc
JAVAC = javac
JNI = -I$(JDK)/include/linux -I$(JDK)/include
LIB = -L$(JDK)/jre/lib/i386/classic -L$(JDK)/jre/lib/i386/green_threads -ljvm
-lhpi
all: callDaemon Server.class ServerImpl.class ServerImpl_Stub.class ServerImpl_S
kel.class Command.class
clean:
-rm -f callDaemon ServerImpl_Skel.java ServerImpl_Stub.java Server_Skel.
java Server_Stub.java *~
fullclean: clean
-rm -f callDaemon *.class
callDaemon.o: callDaemon.c
$(CC) $(JNI) -c callDaemon.c
callDaemon: callDaemon.o
$(CC) -o callDaemon callDaemon.o $(LIB)
Server.class: Server.java
$(JAVAC) Server.java
ServerImpl.class: ServerImpl.java
$(JAVAC) ServerImpl.java
ServerImpl_Skel.class ServerImpl_Stub.class: ServerImpl.class
$(RMIC) ServerImpl
Command.class: Server.class Command.java
$(JAVAC) Command.java
#include <jni.h>
#define PATH_SEPARATOR ':'
#define USER_CLASSPATH ".:/usr/asis.local/i386_redhat61/usr.local/libexec/jdk/1.2.2/jre/lib/i386/classic/:/usr/asis.local/i386_redhat61/usr.local/libexec/jdk/1.2.2/jre/lib/i386/:/usr/asis.local/i386_redhat61/usr.local/libexec/jdk/1.2.2/jre/lib/i386/green_thread" /* where Prog.class is */
main(int argc, char** argv) {
JNIEnv *env;
JavaVM *jvm;
JDK1_1InitArgs vm_args;
jint res;
jclass cls;
jmethodID mid;
jstring jstr;
jobjectArray args;
char classpath[1024];
/* IMPORTANT: specify vm_args version # if you use JDK1.1.2 and beyond */
vm_args.version = 0x00010001;
JNI_GetDefaultJavaVMInitArgs(&vm_args);
/* Append USER_CLASSPATH to the end of default system class path */
sprintf(classpath, "%s%c%s",
vm_args.classpath, PATH_SEPARATOR, USER_CLASSPATH);
vm_args.classpath = classpath;
/* Create the Java VM */
res = JNI_CreateJavaVM(&jvm,&env,&vm_args);
if (res < 0) {
fprintf(stderr, "Can't create Java VM\n");
exit(1);
}
cls = (*env)->FindClass(env, "ServerImpl");
if (cls == 0) {
fprintf(stderr, "Can't find Server class\n");
exit(1);
}
mid = (*env)->GetStaticMethodID(env, cls, "main", "([Ljava/lang/String;)V");
if (mid == 0) {
fprintf(stderr, "Can't find HelloWorld.main\n");
exit(1);
}
jstr = (*env)->NewStringUTF(env, (argc==2)? argv[1] : " really from C!");
if (jstr == 0) {
fprintf(stderr, "Out of memory\n");
exit(1);
}
args = (*env)->NewObjectArray(env, 1,
(*env)->FindClass(env, "java/lang/String"), jstr);
if (args == 0) {
fprintf(stderr, "Out of memory\n");
exit(1);
}
(*env)->CallStaticVoidMethod(env, cls, mid, args);
(*jvm)->DestroyJavaVM(jvm);
}
import java.rmi.*;
import java.rmi.server.*;
public class ServerImpl extends UnicastRemoteObject implements Server {
static String serverName;
static int numCommands;
public ServerImpl() throws RemoteException {
super();
}
public int command(int num) throws RemoteException {
System.out.println("current command is "+num);
return ++numCommands;
}
public static void main(String argv[]) throws Exception {
if( System.getSecurityManager() == null)
System.setSecurityManager(new RMISecurityManager());
try {
ServerImpl obj = new ServerImpl();
System.err.println("ServerImpl obj = new ServerImpl():done");
Naming.bind("//"+"localhost"+"/Server", obj);
// Naming.rebind("//"+"localhost"+"/Server", obj);
// Naming.rebind("//"+serverName+"/Server", obj);
System.out.println("bind done");
} catch (Exception e) {
System.err.println("ServerImpl exception: "+
e.getMessage());
e.printStackTrace();
}
}
}
import java.rmi.*;
public interface Server extends java.rmi.Remote {
int command(int num) throws RemoteException;
}
Invoking JavaVM for calling remote object on JAS via RMI for Geant4
#
# Makefile for linux JavaAnalysisStudio distribution.
#
# point this at a *stable* JDK
JDK=/home/geant4/jdk1.2.2
CERNLIB=../../../ExternalLibraries/linux
cpp=gcc -c
jni=-I$(JDK)/include/linux -I$(JDK)/include
link=gcc
libs=-L$(JDK)/jre/lib/i386/classic -L$(JDK)/jre/lib/i386/native_threads -ljvm -l
hpi
linkopts=
all: JHistogram1D.o JHistogramFactory.o
aida: JHistogram1D.o JHistogramFactory.o
$(link) JHistogram1D.o JHistogramFactory.o $(libs) $(linkopts) -o aida
JHistogram1D.o: JHistogram1D.cpp JHistogram1D.h
$(cpp) $(jni) JHistogram1D.cpp
JHistogramFactory.o: JHistogramFactory.cpp JHistogramFactory.h
$(cpp) $(jni) JHistogramFactory.cpp
clean:
rm *.o aida
// JHistogramFactory.cpp: implementation of the JHistogramFactory class.
//
//////////////////////////////////////////////////////////////////////
#include <jni.h>
#include <string.h>
#include <stdlib.h>
#include "JHistogramFactory.h"
#include "JHistogram1D.h"
JNIEnv *env;
JavaVM *jvm;
JHistogramFactory::JHistogramFactory()
{
createJVM();
const char* title = "G4Job";
// Create a Job to encapsulate the histograms
cls = env->FindClass("JASG4Driver");
if (cls == NULL) error("Could not find class JASG4Driver");
jmethodID constructor = env->GetMethodID(cls,"","(Ljava/lang/String;)V");
if (constructor == NULL) error("Could not find constructor");
jstring jtitle = env->NewStringUTF(title);
job = env->NewObject(cls,constructor,jtitle);
if (job == NULL) error("Could not create job");
getCommand = env->GetMethodID(cls,"getCommand","()Ljava/lang/String;");
if (getCommand == NULL) error("Could not find getCommand method");
log = env->GetMethodID(cls,"log","(Ljava/lang/String;)V");
if (log == NULL) error("Could not find log method");
}
JHistogramFactory::~JHistogramFactory()
{
//const char* filename = "aida.javahist";
//jstring jfilename = env->NewStringUTF(filename);
//env->CallVoidMethod(job,save,jfilename);
//env->CallVoidMethod(job,dump);
destroyJVM();
}
void JHistogramFactory::RegisterImmediateFunction(void* fnptr)
{
JNINativeMethod nativeMethod;
nativeMethod.name = "immediateCommand";
nativeMethod.signature = "(Ljava/lang/String;)V";
nativeMethod.fnPtr = fnptr;
int rc = env->RegisterNatives(cls,&nativeMethod,1);
printf("rc=%d\n",rc);
}
const char* JHistogramFactory::GetCommand()
{
jboolean isCopy;
jstring command = (jstring) env->CallObjectMethod(job,getCommand);
return env->GetStringUTFChars(command,&isCopy);
}
void JHistogramFactory::FreeCommand(const char* c)
{
env->ReleaseStringUTFChars(command,c);
}
JHistogram1D* JHistogramFactory::create1DHistogram(const char* name)
{
return new JHistogram1D(this,name);
}
void JHistogramFactory::error(const char* msg)
{
fprintf(stderr,"Error: %s\n",msg);
exit(1);
}
JNIEnv* JHistogramFactory::getEnv()
{
return env;
}
void JHistogramFactory::Log(const char* msg)
{
jstring jmsg = env->NewStringUTF(msg);
env->CallVoidMethod(job,log,jmsg);
}
void JHistogramFactory::createJVM()
{
JavaVMInitArgs vm_args;
JavaVMOption options[2];
char* cp = getenv("CLASSPATH");
//char* cp ="c:\\program files\\java analysis studio\\lib\\hep.jar";
char* opt = new char[strlen(cp)+100];
strcpy(opt,"-Djava.class.path=");
strcat(opt,cp);
printf("cp=%s\n",opt);
options[0].optionString = opt;
options[1].optionString = "-verbose";
vm_args.version = JNI_VERSION_1_2;
vm_args.options = options;
vm_args.nOptions = 1;
vm_args.ignoreUnrecognized = 1;
int rc = JNI_CreateJavaVM(&jvm, (void **)&env, &vm_args);
if (rc < 0) error("Failed to create Java VM");
delete opt;
}
void JHistogramFactory::destroyJVM()
{
jvm->DestroyJavaVM();
}
// JHistogramFactory.h: interface for the JHistogramFactory class.
//////////////////////////////////////////////////////////////////////
#ifndef JHISTOGRAMFACTORY
#define JHISTOGRAMFACTORY
class JHistogram1D;
class JHistogramFactory
{
private:
jclass cls;
jobject job;
jstring command;
jmethodID getCommand;
jmethodID log;
jmethodID save;
jmethodID dump;
void createJVM();
void destroyJVM();
public:
JHistogramFactory();
virtual ~JHistogramFactory();
void RegisterImmediateFunction(void* fnptr);
JHistogram1D* create1DHistogram(const char* name);
JNIEnv* getEnv();
void error(const char* message);
const char* GetCommand();
void FreeCommand(const char* command);
void Log(const char* message);
};
#endif
import jas.server.*;
import java.io.PrintWriter;
import java.util.Vector;
public class JASG4Driver extends HistogramServer
{
public JASG4Driver(String name)
{
super(name);
}
protected void fireMessageReceived(MessageEvent e)
{
super.fireMessageReceived(e);
String command = (String) e.getMessage();
System.out.println("Message Received!!!"+e.getMessage());
if (command.startsWith("!"))
{
immediateCommand(command);
}
else synchronized (queue)
{
if (queue.isEmpty()) queue.notifyAll();
queue.add(command);
}
}
public void log(String msg)
{
if (print == null) print = new PrintWriter(getLogStream());
print.print(msg);
print.flush();
}
public String getCommand()
{
try
{
synchronized (queue)
{
if (queue.isEmpty()) queue.wait();
String command = (String) queue.firstElement();
queue.removeElementAt(0);
return command;
}
}
catch (InterruptedException x)
{
return "exit";
}
}
private native void immediateCommand(String command);
private Vector queue = new Vector();
private PrintWriter print;
}
import jas.plugin.*;
import java.awt.event.*;
import java.awt.*;
import javax.swing.*;
public class JASG4Plugin extends Plugin implements ActionListener
{
public void init()
{
JMenu menu = new JMenu("Geant4");
JMenuItem start = new JMenuItem("BeamOn");
start.setActionCommand("/run/beamOn 1000000");
start.addActionListener(this);
menu.add(start);
JMenuItem stop = new JMenuItem("BeamOff");
stop.setActionCommand("!stop");
stop.addActionListener(this);
menu.add(stop);
JMenuItem trap = new JCheckBoxMenuItem("Trap Geant4 Output");
trap.setActionCommand("#trap");
trap.addActionListener(this);
menu.add(trap);
JMenuItem other = new JCheckBoxMenuItem("Show Command Window");
other.setActionCommand("#console");
other.addActionListener(this);
menu.add(other);
addMenu(menu);
}
public void actionPerformed(ActionEvent e)
{
String command = e.getActionCommand();
if (command.equals("#console"))
{
if (g4console == null)
{
g4console = installConsole("G4Console",new G4Console());
}
else
{
g4console.close();
g4console = null;
}
}
else if (command.equals("#trap"))
{
boolean trap = ((JCheckBoxMenuItem) e.getSource()).isSelected();
if (trap) sendJob("!startTrap");
else sendJob("!stopTrap");
}
else sendJob(command);
}
private PageContext g4console;
private class G4Console extends JPanel
{
G4Console()
{
super(new BorderLayout());
JTextField field = new JTextField();
add(field,BorderLayout.SOUTH);
field.addActionListener(JASG4Plugin.this);
}
}
}
// JHistogram.cpp: implementation of the JHistogram1D class.
//////////////////////////////////////////////////////////////////////
#include <jni.h>
#include <string.h>
#include <stdlib.h>
#include "JHistogram1D.h"
#include "JHistogramFactory.h"
JHistogram1D::JHistogram1D(JHistogramFactory* factory, const char* title)
{
env = factory->getEnv();
// Create the corresponding Java histogram
jclass cls = env->FindClass("hep/analysis/Histogram");
if (cls == NULL) factory->error("Could not create hep.analysis.Histogram
");
jmethodID constructor = env->GetMethodID(cls, "", "(Ljava/lang/Str
ing;)V");
if (constructor == NULL) factory->error("Could not find constructor for
hep.analysis.Histogram");
fillMethod = env->GetMethodID(cls,"fill","(D)V");
if (fillMethod == NULL) factory->error("Could not find fill method for h
ep.analysis.Histogram");
jstring jtitle = env->NewStringUTF(title);
jhist = env->NewObject(cls,constructor,jtitle);
}
JHistogram1D::~JHistogram1D()
{
env->DeleteLocalRef(jhist);
}
void JHistogram1D::fill(double value)
{
env->CallVoidMethod(jhist,fillMethod,value);
}
//test
//int main(int argc, char** argv)
//{
// JHistogramFactory factory;
// JHistogram1D* hist = factory.create1DHistogram("test");
// for (int i=0; i<1000; i++)
// {
// hist->fill(i);
// }
// return 0;
//}
#ifndef JHISTOGRAM1D
#define JHISTOGRAM1D
class JHistogramFactory;
class JHistogram1D
{
private:
jmethodID fillMethod;
jobject jhist;
JNIEnv* env;
public:
JHistogram1D(JHistogramFactory *factory, const char *title);
virtual ~JHistogram1D();
void fill(double value);
};
#endif