【问题标题】:JNI: Catching Init-Time ExceptionsJNI:捕获初始化时间异常
【发布时间】:2013-04-15 08:36:15
【问题描述】:

好的,我对此一无所知。有谁知道我如何挂钩到 Java 的异常管道以捕获(并记录到文本文件)所有正在发生的异常?

情况是这样的:我在 JAR 文件 (A) 中有一个库,该库又依赖于第二个 JAR 文件 (B)。 A 没有主类,因为它只是一个类库,我通过 JNI 访问和调用它。我遇到的问题是这个。当我尝试在加载 A 的情况下初始化 JNI 时,JNI 返回一个未指定的错误。

我强烈怀疑此错误源于 Log4J 记录器单元的实例化,该实例化发生在 B 中的静态代码(方法之外)中,我认为由于日志文件的权限问题而引发 IOException .但是,我在找出正在发生的事情时遇到问题,因为在链接阶段(当 A 导入 B 时)抛出异常(我怀疑这是问题的原因),因此无法被 try-catch 捕获堵塞。此外,由于没有 main 方法,因此没有明显的地方可以放置 try-catch 块来捕获此异常。

我想要一些方法来捕获任何一个 JAR 中出现的所有异常并将它们转储到文本文件中。我不能(轻松)修改 B(我没有反编译的 JAR)。有什么想法吗?

这里是使用指定库和选项调用 JNI 的 C 代码:

_DLL_EXPORT PyObject *initVM(PyObject *self, PyObject *args, PyObject *kwds)
{
    static char *kwnames[] = {
        "classpath", "initialheap", "maxheap", "maxstack",
        "vmargs", NULL
    };
    char *classpath = NULL;
    char *initialheap = NULL, *maxheap = NULL, *maxstack = NULL;
    char *vmargs = NULL;

    if (!PyArg_ParseTupleAndKeywords(args, kwds, "|zzzzz", kwnames,
                                     &classpath,
                                     &initialheap, &maxheap, &maxstack,
                                     &vmargs))
        return NULL;

    if (env->vm)
    {
        PyObject *module_cp = NULL;

        if (initialheap || maxheap || maxstack || vmargs)
        {
            PyErr_SetString(PyExc_ValueError,
                            "JVM is already running, options are ineffective");
            return NULL;
        }

        if (classpath == NULL && self != NULL)
        {
            module_cp = PyObject_GetAttrString(self, "CLASSPATH");
            if (module_cp != NULL)
                classpath = PyString_AsString(module_cp);
        }

        if (classpath && classpath[0])
            env->setClassPath(classpath);

        Py_XDECREF(module_cp);

        return getVMEnv(self);
    }
    else
    {
        JavaVMInitArgs vm_args;
        JavaVMOption vm_options[32];
        JNIEnv *vm_env;
        JavaVM *vm;
        unsigned int nOptions = 0;
        PyObject *module_cp = NULL;

        vm_args.version = JNI_VERSION_1_4;
        JNI_GetDefaultJavaVMInitArgs(&vm_args);

        if (classpath == NULL && self != NULL)
        {
            module_cp = PyObject_GetAttrString(self, "CLASSPATH");
            if (module_cp != NULL)
                classpath = PyString_AsString(module_cp);
        }

#ifdef _jcc_lib
        PyObject *jcc = PyImport_ImportModule("jcc");
        PyObject *cp = PyObject_GetAttrString(jcc, "CLASSPATH");

        if (classpath)
            add_paths("-Djava.class.path=", PyString_AsString(cp), classpath,
                      &vm_options[nOptions++]);
        else
            add_option("-Djava.class.path=", PyString_AsString(cp),
                       &vm_options[nOptions++]);

        Py_DECREF(cp);
        Py_DECREF(jcc);
#else
        if (classpath)
            add_option("-Djava.class.path=", classpath,
                       &vm_options[nOptions++]);
#endif

        Py_XDECREF(module_cp);

        if (initialheap)
            add_option("-Xms", initialheap, &vm_options[nOptions++]);
        if (maxheap)
            add_option("-Xmx", maxheap, &vm_options[nOptions++]);
        if (maxstack)
            add_option("-Xss", maxstack, &vm_options[nOptions++]);

        if (vmargs)
        {
#ifdef _MSC_VER
            char *buf = _strdup(vmargs);
#else
            char *buf = strdup(vmargs);
#endif
            char *sep = ",";
            char *option;

            for (option = strtok(buf, sep); option; option = strtok(NULL, sep))
            {
                if (nOptions < sizeof(vm_options) / sizeof(JavaVMOption))
                    add_option("", option, &vm_options[nOptions++]);
                else
                {
                    free(buf);
                    for (unsigned int i = 0; i < nOptions; i++)
                        delete vm_options[i].optionString;
                    PyErr_Format(PyExc_ValueError, "Too many options (> %d)",
                                 nOptions);
                    return NULL;
                }
            }
            free(buf);
        }

        //vm_options[nOptions++].optionString = "-verbose:gc";
        //vm_options[nOptions++].optionString = "-Xcheck:jni";

        vm_args.nOptions = nOptions;
        vm_args.ignoreUnrecognized = JNI_FALSE;
        vm_args.options = vm_options;
 if (JNI_CreateJavaVM(&vm, (void **) &vm_env, &vm_args) < 0)
        {
            for (unsigned int i = 0; i < nOptions; i++)
                delete vm_options[i].optionString;

            PyErr_Format(PyExc_ValueError,
                         "An error occurred while creating Java VM");
            return NULL;
        }

        env->set_vm(vm, vm_env);

        for (unsigned int i = 0; i < nOptions; i++)
            delete vm_options[i].optionString;

        t_jccenv *jccenv = (t_jccenv *) PY_TYPE(JCCEnv).tp_alloc(&PY_TYPE(JCCEnv), 0);
        jccenv->env = env;

#ifdef _jcc_lib
        registerNatives(vm_env);
#endif

        return (PyObject *) jccenv;
    }
}

【问题讨论】:

  • 在所有 Exception 的构造函数中放置一个断点,并使用调试器运行代码。另外,我真的不明白您为什么要尝试使用 jni 运行 jar? JNI 用于访问本机库而不是 java 库。
  • 显示您的 JNI 端代码。如果 Java 抛出异常,您应该能够在 JNI 中使用ExceptionOccurred 检索它。另外,您是否尝试过创建一个简单的 Java main() 来调用该操作?
  • Muel:作为 Web 开发项目的一部分,我正在使用 JCC 来支持 Python-Java 集成。有一个我想用于智能推理的库,它只存在于 Java 中,但 Web 应用程序必须用 Python 编写。 JCC 使用 JNI 实例化 JVM,然后与 Java 对象交互。
  • Parsifal:有很多 JNI 端代码,因为 JCC 为传递给它的每个 Java 类创建包装器对象。该问题特别与使用库 A 初始化 JVM 时发生的异常有关。在此工作之后抛出的异常很好。但是,我将发布 JCC 函数,该函数初始化 JVM 并引发了我得到的 Python 异常。
  • 那么错误发生在哪里呢?是来自JNI_CreateJavaVM 电话吗?这通常不会表明任何实际的 Java 代码存在问题,因为您实际上并没有调用任何 Java 代码(Java 是首次使用时加载的)。但是,如果是这种情况,则需要报告JNI_CreateJavaVM返回的值,而不是简单地忽略它;这将为您提供有关正在发生的事情的更多信息。如果错误发生在其他地方,请显示该代码。

标签: java debugging exception


【解决方案1】:

好的,我已经找到了我想要的解决方案。解决方案是对问题中列出的以下代码段的更新:

    if (JNI_CreateJavaVM(&vm, (void **) &vm_env, &vm_args) < 0)
    {
        for (unsigned int i = 0; i < nOptions; i++)
            delete vm_options[i].optionString;

        PyErr_Format(PyExc_ValueError,
                     "An error occurred while creating Java VM");
        return NULL;
    }

该适配支持构建更详细的错误消息,其中添加了两条特定信息:

  1. JNI_CreateJavaVM 方法返回的错误代码(如果有);
  2. 出现此类错误代码时发生的详细 Java 异常。

上面的原始代码中的 sn-p 被替换为以下内容:

    vmInitSuccess = JNI_CreateJavaVM(&vm, (void **) &vm_env, &vm_args);
    if (vmInitSuccess < 0)
    {
        for (unsigned int i = 0; i < nOptions; i++)
            delete vm_options[i].optionString;

        //Set up basic error message
        sprintf(strVMInitSuccess, "%d", vmInitSuccess);
        strcpy(strVMError, "An error occurred while creating Java VM (No Exception): ");
        strcat(strVMError, strVMInitSuccess);

        //Get exception if there is one
        if((exc = vm_env->ExceptionOccurred()))
        {
            //Clear the exception since we have it now
            vm_env->ExceptionClear();
            //Get the getMessage() method
            if ((java_class = vm_env->FindClass ("java/lang/Throwable")))
            {
                if ((method = vm_env->GetMethodID(java_class, "getMessage", "()Ljava/lang/String;")))
                {
                    int size;
                    strExc = static_cast<jstring>(vm_env->CallObjectMethod(exc, method)); 
                    charExc = vm_env->GetStringUTFChars(strExc, NULL);
                    size = sizeof(strVMError) + sizeof(charExc);
                    char strVMException[size];
                    strcpy(strVMException, "An error occurred while creating Java VM (Exception): ");
                    strcat(strVMException, charExc);
                    PyErr_Format(PyExc_ValueError, strVMException);
                    return NULL;
                }
            }
        }
        PyErr_Format(PyExc_ValueError, strVMError);
        return NULL;
    }

感谢 @Parsifal 对此解决方案的帮助。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多