简体   繁体   English

如果缺少类依赖项,如何确定缺少哪个类?

[英]If a class dependency is missing, how can I determine which class is missing?

I am getting the error "Could not find or load main class" even though the class is there. 我收到错误“无法找到或加载主类”,即使班级在那里。 From this answer , I learned that the class can likely be found, but not loaded, due to missing dependencies. 这个答案中 ,我了解到由于缺少依赖性,可能会找到该类,但是没有加载。

Aside from manually decompiling the class, checking all its dependencies, and if those are on the classpath, and so on for each dependent class ad infinitum....is there any way to determine exactly which class is missing from the classpath therefore causing Java to be unable to load my main class? 除了手动反编译类,检查它的所有依赖项,以及它们是否在类路径上,依此类推,每个依赖类广告无限....有没有办法确定类路径中缺少哪个类因此导致Java无法加载我的主类?

That other question was a question of a missing parent/interface class. 另一个问题是缺少父/接口类的问题。 But I have manually checked that all ancestor classes are either on the specified classpath or in the JDK, as below. 但我已手动检查所有祖先类是在指定的类路径上还是在JDK中,如下所示。

$ cd ~/picketbox
$ java -cp picketbox-4.1.1.Final-redhat-1.jar org.picketbox.datasource.security.SecureIdentityLoginModule HelloWorld
Error: Could not find or load main class org.picketbox.datasource.security.SecureIdentityLoginModule
$ jar xvf picketbox-4.1.1.Final-redhat-1.jar > jarxvf.txt
$ cat jarxvf.txt | grep SecureIdentityLoginModule
 inflated: org/picketbox/datasource/security/SecureIdentityLoginModule.class
$ cd org/picketbox/datasource/security/
$ javap SecureIdentityLoginModule.class | grep main
  public static void main(java.lang.String[]) throws java.lang.Exception;
$ javap SecureIdentityLoginModule.class | grep extends
public class org.picketbox.datasource.security.SecureIdentityLoginModule extends org.picketbox.datasource.security.AbstractPasswordCredentialLoginModule {
$ cat ~/picketbox/jarxvf.txt | grep AbstractPasswordCredentialLoginModule
 inflated: org/picketbox/datasource/security/AbstractPasswordCredentialLoginModule.class
$ javap AbstractPasswordCredentialLoginModule.class | grep extends
public abstract class org.picketbox.datasource.security.AbstractPasswordCredentialLoginModule extends org.jboss.security.auth.spi.AbstractServerLoginModule {
$ cat ~/picketbox/jarxvf.txt | grep AbstractServerLoginModule
 inflated: org/jboss/security/auth/spi/AbstractServerLoginModule.class
$ cd ~/picketbox/org/jboss/security/auth/spi/
$ javap AbstractServerLoginModule.class | grep implements
public abstract class org.jboss.security.auth.spi.AbstractServerLoginModule implements javax.security.auth.spi.LoginModule {
$ cd ~/rtjar
$ cp /usr/java/jdk1.8.0_141/jre/lib/rt.jar ./
$ jar xvf rt.jar | grep spi/LoginModule
extracted: javax/security/auth/spi/LoginModule.class
$ cd javax/security/auth/spi/
$ javap LoginModule.class | grep interface
public interface javax.security.auth.spi.LoginModule {
$

Create a helper class: 创建一个帮助器类:

public class Helper {
    public static void main(String[] args) {
        YourActualMainClass.main(args);
    }
}

and try to run this helper class instead of YourActualMainClass . 并尝试运行此助手类而不是YourActualMainClass

The key point is that neither Helper 's inheritance tree nor the signatures of its members depend on the problematic class, so loading and even initialization will succeed, given HotSpot's lazy resolving strategy, so it will only attempt to load and resolve YourActualMainClass when trying to execute the Helper.main method. 关键是Helper的继承树和其成员的签名都不依赖于有问题的类,因此加载甚至初始化都会成功,因为HotSpot的延迟解析策略,所以它只会尝试加载和解析YourActualMainClass执行Helper.main方法。 At this point, it will throw a detailed error telling you which class was actually missing. 此时,它会抛出一个详细错误,告诉您实际上缺少哪个类。

This matches the behavior described in the linked answer that the use of a class leads to a specific error message when the inheritance of the using class does not depend on it. 这与链接答案中描述的行为相匹配,即当使用类的继承不依赖于它时,类的使用会导致特定的错误消息。


Alternatively, you could try to run the application with Java 9, as its launcher will print the cause when the loading of the main class failed. 或者,您可以尝试使用Java 9运行应用程序,因为它的启动程序将在主类加载失败时打印原因。

You can try to use JVMTI to display all exceptions that were thrown. 您可以尝试使用JVMTI显示抛出的所有异常。 Starting with Identifying exceptions through JVMTI . 通过JVMTI识别异常开始。 Just use the following updated agent.c source code. 只需使用以下更新的agent.c源代码即可。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <jni.h>
#include <jvmti.h>

#define CHECK_JVMTI_ERROR(x,call) \
    { if (x != JVMTI_ERROR_NONE) { fprintf (stderr, "Error during %s in %s:%d\n", #call, __FILE__, __LINE__); } }

/* Global static data */
static jvmtiEnv     *jvmti;

static void JNICALL cb_Exception (jvmtiEnv *jvmti_env, JNIEnv* jni_env,
    jthread thread, jmethodID method, jlocation location, jobject exception,
    jmethodID catch_method, jlocation catch_location)
{
    jclass exceptionClass = (*jni_env)->GetObjectClass(jni_env, exception);

    jmethodID methodId = (*jni_env)->GetMethodID(jni_env, exceptionClass,
                         "printStackTrace",
                         "()V");

    (*jni_env)->CallVoidMethod(jni_env, exception, methodId);
}

JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *vm, char *options, void *reserved)
{
    jint                rc;
    jvmtiError          r;
    jvmtiCapabilities   capabilities;
    jvmtiEventCallbacks callbacks;

    /* Get JVMTI environment */
    rc = (*vm)->GetEnv(vm, (void **)&jvmti, JVMTI_VERSION);
    if (rc != JNI_OK)
    {
        fprintf (stderr, "Error!: Unable to create jvmtiEnv, rc=%d\n", rc);
        return -1;
    }

    /* Get/Add JVMTI capabilities */
    memset(&capabilities, 0, sizeof(capabilities));
    capabilities.can_generate_exception_events = 1;
    r = (*jvmti)->AddCapabilities(jvmti, &capabilities);
    CHECK_JVMTI_ERROR(r, AddCapabilities);

    /* Set callbacks and enable event notifications */
    memset(&callbacks, 0, sizeof(callbacks));
    callbacks.Exception               = &cb_Exception;
    r = (*jvmti)->SetEventCallbacks(jvmti, &callbacks, sizeof(callbacks));
    CHECK_JVMTI_ERROR(r, SetEventCallbacks);

    /* Exception events */
    r = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE, 
      JVMTI_EVENT_EXCEPTION, NULL);
    CHECK_JVMTI_ERROR(r, SetEventNotificationMode);

    return 0;
}

JNIEXPORT void JNICALL Agent_OnUnload(JavaVM *vm)
{
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM