1

我在使用tools.jarpresent in时遇到问题jdk1.8.0_121/lib/tools.jar

$JAVA_HOME的设置为:

# echo $JAVA_HOME
/usr/local/java/jdk1.8.0_121

路径tools.jar是:

# ls /usr/local/java/jdk1.8.0_121/lib/tools.jar
/usr/local/java/jdk1.8.0_121/lib/tools.jar

我使用以下java可执行文件来运行代码:

/usr/local/java/jdk1.8.0_161/bin/java

但是,当我访问VirtualMachine类时,它会抛出

Caused by: java.lang.ClassNotFoundException: com.sun.tools.attach.VirtualMachine
        at java.net.URLClassLoader.findClass(URLClassLoader.java:381) ~[na:1.8.0_161]
        at java.lang.ClassLoader.loadClass(ClassLoader.java:424) ~[na:1.8.0_161]
        at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:338) ~[na:1.8.0_161]
        at java.lang.ClassLoader.loadClass(ClassLoader.java:357) ~[na:1.8.0_161]
        ... 72 common frames omitted

有人可以解释为什么Java无法lib/tools.jar在其类路径中找到吗?我该怎么做才能纠正这种行为?


为了在我的本地机器上运行,我在我的 pom 中添加了以下依赖项:

<dependency>
    <groupId>com.sun</groupId>
    <artifactId>tools</artifactId>
    <version>1.8</version>
    <scope>system</scope>
    <systemPath>${java.home}/../lib/tools.jar</systemPath>
</dependency>

但是,当我在服务器上部署它时,由于system范围的原因,这个 jar 没有被打包,它也没有在服务器的 jdk 路径上找到这个 jar。

它不应该自动找到所有的jdk jar吗?

我还尝试$JAVA_HOME在 jar 的 MANIFEST 文件的类路径条目中添加 env 变量,如下所示:

Manifest-Version: 1.0
Archiver-Version: Plexus Archiver
Built-By: pankajsinghal
Class-Path: $JAVA_HOME/lib/
Created-By: Apache Maven 3.5.4
Build-Jdk: 1.8.0_181

但是,这也行不通。另外,我不想在我的代码中显式添加这个库的 jar,因为它是一个 JDK 库,我想访问它的正确方法是从系统的 JDK 路径本身。因此,寻找这个方向本身的解决方案。

非常感谢任何帮助。

4

3 回答 3

1

你可以这样尝试:

java -cp "/path/your.jar:/usr/local/java/jdk1.8.0_121/lib/tools.jar" your.MainClass

或参考以下内容:

“com.sun.tools.javac.util.Assert”类型不可访问

希望它对你有所帮助。

于 2020-04-02T09:26:21.067 回答
0

您可以直接将 toos.jar 添加到您当前的类加载器中,但这只是一个想法。

File getJar = new File(folderLibsPath + File.separator + "tools.jar");
URLClassLoader classLoaderExt = (URLClassLoader) this.getClassLoader();
URL jarUrl = getJar.toURI().toURL();
Method method = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
method.setAccessible(true);
method.invoke(classLoaderExt, jarUrl);

引用自:如何在运行时动态加载 JAR 文件?

并且不要忘记
通过System.load(absPath)或加载 attach.so(或 attach.dll)System.loadLibrary(dllName)

File attachedDLL = new File(folderLibFilePath);
if (attachedDLL.exists()) {
    System.load(attachedDLL.getAbsolutePath()); 
}

我认为我们遇到了同样的问题,这段代码适用于我的情况。

此外,还有另一种方法可以将 tools.jar 添加到类路径中,但实际上它们做了同样的事情:

public void onEnable() throws Exception {
    URLClassPath ucp = (URLClassPath) Reflection.getPrivateField("ucp", this.getClassLoader().getParent()); // reflect the subClass of URLClassLoader
    File getJar = new File(folderLibsPath + File.separator + "tools.jar");
    URL jarUrl = getJar.toURI().toURL();
    ucp.addURL(jarUrl); // or just change its "URLs" field by put your jarURL in its Stack
}

但应该提到的是,Java 将使用这种方式AppClassLoader(SystemClassLoader)来加载 tools.jar(也是调用程序 - 您的应用程序将)。如果您使用CustomClassLoader. (因为根据Java Parent Delegation Model,superClassLoader 无法知道它的 subClassLoader 加载了哪个类)。

因此,如果您在 customClassLoader(系统类加载器的子类)下开发插件,则应在PluginClassLoader分离 VM 后删除 AppClassLoader 中的类路径(这意味着让自定义加载它,或者不加载它的超级路径)。
这里我用反射来完成。

public class Main {
    public void onEnable() throws Exception {

        /** load attach.dll */
        System.loadLibrary("attach");

        /** load tools.jar */
        URLClassPath ucp = (URLClassPath) Reflection.getPrivateField("ucp", this.getClassLoader().getParent());
        File getJar = new File(folderLibsPath + File.separator + "tools.jar");
        URL jarUrl = getJar.toURI().toURL();
        ucp.addURL(jarUrl);
       
        /** attach, load, detach VM */
        VirtualMachine vm;
        vm = VirtualMachine.attach(this.getPid());
        // if the current jar itself is the agent
        vm.loadAgent(new File(this.getClass().getProtectionDomain().getCodeSource().getLocation().toURI().getPath()).getAbsolutePath());
        vm.detach();
        
        /** change the classLoader back to your custom */
        changeClassLoaderBack();

        /** unload native DLL Lib */
        unloadNativeLibs(); // or you can add a condition to unload attach.dll only
    }

    public void changeClassLoaderBack() {
        
        URLClassPath ucp = (URLClassPath) Reflection.getPrivateField("ucp", this.getClassLoader().getParent());
        
        /** reset field path */
        List<?> path = (ArrayList<?>) Reflection.getPrivateField("path", ucp);
        List<URL> newPath = new ArrayList<>();
        path.forEach((v) -> {
            if(!((URL)v).getPath().contains("toos.jar") && !((URL)v).getPath().contains(this.getPlugin().getName())) {
                newPath.add(((URL)v));
            }
        });
        Reflection.setPrivateField("path", ucp, newPath);
        
        /** reset field URLs */
        Reflection.setPrivateField("urls", ucp, new Stack<URL>());
        
        /** reset fields loader and LMAP */
        List<Object> newLoader = new ArrayList<>();
        Map<Object, Object> newLMAP = new HashMap<>();
        ((HashMap<?,?>)Reflection.getPrivateField("lmap", ucp)).forEach((k,v) -> {
            if (!((String)k).contains("tools.jar") && !((String)k).contains(this.getPlugin().getName())) {
                newLMAP.put(k, v);
                newLoader.add(v);
            };
        });
        Reflection.setPrivateField("lmap", ucp, newLMAP); 
        Reflection.setPrivateField("loaders", ucp, newLoader);
        
    }

    private String getPid() {
        RuntimeMXBean bean = ManagementFactory.getRuntimeMXBean();
        String pid = bean.getName();
        if (pid.contains("@")) {
            pid = pid.substring(0, pid.indexOf("@"));
        }
        return pid;
    }

    private void unloadNativeLibs(ClassLoader unloadDLLfromWhichLoader) {
        try {
            ClassLoader classLoader = unloadDLLfromWhichLoader;
            Field field = ClassLoader.class.getDeclaredField("nativeLibraries");
            field.setAccessible(true);
            Vector<?> libs = (Vector<?>) field.get(classLoader);
            Iterator<?> it = libs.iterator();
            Object o;
            while (it.hasNext()) {
                o = it.next();
                Method finalize = o.getClass().getDeclaredMethod("finalize", new Class[0]);
                finalize.setAccessible(true);
                finalize.invoke(o, new Object[0]);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

public class Reflection {
    public static Object getPrivateField(String fieldName, Object object) {
        Field field;
        Object o = null;
        try {
            field = object.getClass().getDeclaredField(fieldName);
            field.setAccessible(true);
            o = field.get(object);
        } 
        catch (NoSuchFieldException e) {
            e.printStackTrace();
        } 
        catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return o;
    }

    public static void setPrivateField(String fieldName, Object object, Object newField) {
        Field field;
        try {
            field = object.getClass().getDeclaredField(fieldName);
            field.setAccessible(true);
            field.set(object, newField);
        } 
        catch (NoSuchFieldException e) {
            e.printStackTrace();
        } 
        catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }
}

希望它可以在某些方面对您有所帮助

于 2020-12-04T08:29:28.620 回答
0

您必须在项目属性中添加该 jar。在eclipse中,将此Jar添加到您的构建路径右键单击项目>构建路径>配置构建路径>选择库选项卡>单击添加外部库>选择Jar文件。

于 2018-08-06T12:40:43.967 回答