从C ++调用JavaVM(使用Cygwin)时如何加载JAR?

时间:2017-05-25 12:40:06

标签: java c++ bash java-native-interface cygwin

我正在用C ++编写应用程序。我想从这个应用程序启动JVM并调用Java方法。我使用Cygwin在Windows 10上运行。

根据Java Invocation API页面,我相信我正在调用JVM;但是,当我打印系统属性“java.class.path”时,它显示为空。

如果我将类文件复制到$PWD/main/MyClass.class一切正常,但如果我将此类文件打包到$PWD/main.jar并尝试以相同的方式引用,那么我得到:

java.lang.NoClassDefFoundError: main/MyClass
Caused by: java.lang.ClassNotFoundException: main.MyClass
        at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
        at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:335)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:357)

以下程序的输出是:

Value of 'java/lang/System.getProperty(java.class.path)' is ''.
Value of 'java/lang/System.getProperty(user.dir)' is 'C:\Users\admin\java_from_cpp_stack\bin'.
Failed to find class 'main/MyClass'.

这是C ++代码:

#include <jni.h>
#include <iostream>

bool
printSysemProperty(JNIEnv* env, std::string property)
{
  std::string className = "java/lang/System";
  jclass cls = env->FindClass(className.c_str());
  if (cls == 0) {
    std::cout << "Failed to find class '" << className.c_str() << "'.\n";
    return false;
  }
  std::string methodName = "getProperty";
  jmethodID mid = env->GetStaticMethodID(cls, methodName.c_str(), "(Ljava/lang/String;)Ljava/lang/String;");
  if (mid == 0) {
    std::cout << "Failed to find method '" << className.c_str() << "." << methodName.c_str() << "'.\n";
    return false;
  }
  jstring cp = env->NewStringUTF(property.c_str());
  jstring val = (jstring)env->CallStaticObjectMethod(cls, mid, (jstring)cp);
  const char* valCStr = env->GetStringUTFChars(val, JNI_FALSE);
  std::cout << "Value of '" << className.c_str() << "." << methodName.c_str() << "(" << property.c_str() << ")' is '" << (char*)valCStr << "'.\n";
  return true;
}

int
main(int argc, char **argv)
{
  // Create JVM
  JavaVM* vm;  
  JNIEnv* env;  
  JavaVMInitArgs vm_args;  
  JavaVMOption* options = new JavaVMOption[1];  
  //options[0].optionString = (char*)"-Djava.class.path=.";
  //options[0].optionString = (char*)"-Djava.class.path=C:\\Users\\admin\\java_from_cpp_stack\\bin\\main.jar";
  //options[0].optionString = (char*)"-Djava.class.path=/cygdrive/c/Users/admin/java_from_cpp_stack/bin/main.jar";
  options[0].optionString = (char*)"-Djava.class.path=main.jar";
  vm_args.version = JNI_VERSION_1_6;  
  vm_args.nOptions = 1;
  vm_args.options = options;  
  vm_args.ignoreUnrecognized = false; 
  int ret = JNI_CreateJavaVM(&vm, (void**)&env, &vm_args);  
  delete options;
  if (ret != 0)
    std::cerr << "Failed to create JVM.\n";

  // Print System Properties
  bool rc = true;
  rc &= printSysemProperty( env, "java.class.path");
  rc &= printSysemProperty( env, "user.dir");
  if (!rc) {
    std::cout << "Printing system properties failed.\n";
    return rc;
  }

  // Call Static Method from JAR
  std::string className = "main/MyClass";
  jclass cls = env->FindClass(className.c_str());
  if (cls == 0) {
    std::cout << "Failed to find class '" << className.c_str() << "'.\n";
    return -3;
  }
  jmethodID mid = env->GetStaticMethodID(cls, "hello", "()V");
  if (mid == 0) {
    std::cout << "Failed to find method '" << className.c_str() << ".hello'.\n";
    return -4;
  }
  std::cout << "Calling '" << className.c_str() << ".main'.\n";
  env->CallStaticVoidMethod(cls, mid);

  // Clean up
  vm->DestroyJavaVM();
  std::cout << "Complete.\n";
  return 0;

}

这是Java代码:

package main;

public class MyClass {
  public MyClass() {
  }

  public static void hello() {
    System.out.println("From Java: Hello World!");
  }
}

这是我用来编译的BASH脚本:

#!/bin/bash

set -e
set -x

rm -rf ./build
mkdir ./build

rm -rf ./bin/
mkdir ./bin/

g++ \
  -o ./bin/myprog \
  ./src/cpp/myprog.c \
  -D__int64=int64_t \
  -L/cygdrive/c/Program\ Files/Java/jdk1.8.0_131/jre/bin/server/ \
  -ljvm \
  -I/cygdrive/c/Program\ Files/Java/jdk1.8.0_131/include \
  -I/cygdrive/c/Program\ Files/Java/jdk1.8.0_131/include/win32

/cygdrive/c/Program\ Files/Java/jdk1.8.0_131/bin/javac \
  -Xlint:all \
  -Werror \
  -g \
  -verbose \
  -cp ./build \
  -sourcepath ./src/java \
  -d ./build \
  ./src/java/main/MyClass.java

/cygdrive/c/Program\ Files/Java/jdk1.8.0_131/bin/jar \
  cvf ./bin/main.jar \
  -C ./build \
  main/MyClass.class

#mkdir ./bin/main
#cp ./build/main/MyClass.class ./bin/main

cd ./bin
PATH=/cygdrive/c/Program\ Files/Java/jdk1.8.0_131/jre/bin/server/:/cygdrive/c/Program\ Files/Java/jdk1.8.0_131/bin/:$PATH ./myprog

1 个答案:

答案 0 :(得分:0)

事实证明解决方案发布在这里: JNI JVM Invocation Classpath

  

在x86-64上,Oracle Windows JDK标头定义jint为long。这是   使用Microsoft编译器的32位(编写Oracle JDK)   但64位与Cygwin gcc。由于$.ajaxSetup({ headers: { 'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content') } }); 包含一些字段   在这种类型中,它的二进制布局会因这种差异而改变。

另见: Error when compiling in cygwin

我的修复是:

  1. JavaVMInitArgs命令行

  2. 中删除-D__int64=int64_t
  3. 添加标题文件g++

  4. 然后jni_local.h被识别。

  5. JavaVMInitArgs文件内容为:

    src/cpp/jni_local.h

    #include "stdint.h" #define __int64 int64_t #define long int32_t #include "jni_md.h" #undef long #include_next "jni.h" 被缩减为:

    src/cpp/myprog.c

    #include "jni_local.h" #include <iostream> int main(int argc, char **argv) { // Create JVM JavaVM* vm; JNIEnv* env; JavaVMInitArgs vm_args; JavaVMOption* options = new JavaVMOption[1]; options[0].optionString = (char*)"-Djava.class.path=myPackage.jar"; vm_args.version = JNI_VERSION_1_6; vm_args.nOptions = 1; vm_args.options = options; vm_args.ignoreUnrecognized = false; int ret = JNI_CreateJavaVM(&vm, (void**)&env, &vm_args); delete options; if (ret != 0) { std::cerr << "Failed to create JVM.\n"; return ret; } // Call Static Method from JAR std::string className = "myPackage/MyClass"; jclass cls = env->FindClass(className.c_str()); if (cls == 0) { std::cerr << "Failed to find class '" << className.c_str() << "'.\n"; return -3; } jmethodID mid = env->GetStaticMethodID(cls, "hello", "()V"); if (mid == 0) { std::cerr << "Failed to find method '" << className.c_str() << ".hello'.\n"; return -4; } std::cerr << "Calling 'myPackage." << className.c_str() << ".hello()'\n"; env->CallStaticVoidMethod(cls, mid); // Clean up vm->DestroyJavaVM(); std::cerr << "Complete.\n"; return 0; } 未被更改。

    src/java/myPackage/MyClass.java文件已更新为:

    run.sh