【问题标题】:Adding com.sun.tools.jar in classpath of jar在 jar 的类路径中添加 com.sun.tools.jar
【发布时间】:2018-08-06 12:36:44
【问题描述】:

我在使用 jdk1.8.0_121/lib/tools.jar 中的 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吗?

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

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 路径本身。所以,自己在这个方向寻找解决方案。

非常感谢任何帮助。

【问题讨论】:

  • 为什么需要 tools.jar ?
  • 我需要以编程方式附加javaagent
  • 哪种 javaagent ?
  • aspectjweaver - 适用于 AOP LTW
  • 除非您知道自己在做什么并且真的需要这样做,否则不要使用系统范围。

标签: java maven classpath tools.jar


【解决方案1】:

你可以这样试试:

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

或参考以下内容:

The type "com.sun.tools.javac.util.Assert" is not accessible

希望对你有所帮助。

【讨论】:

    【解决方案2】:

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

    【讨论】:

    • 正如我在问题中提到的,我可以通过添加system dependency 在本地运行它。从命令行运行时如何访问服务器上的这个 jar?
    • 正如我所说,我尝试按以下方式将它添加到类路径中Class-Path: $JAVA_HOME/lib/,但它似乎不起作用。对绝对路径进行硬编码可能取决于机器和机器,我正在寻找一种通用的解决方案,它可以在任何地方工作,而无需硬编码/使用机器特定的路径。我尝试使用$JAVA_HOME,因为这在每台机器上都可用。如果你有一个通用的解决方案,它会帮助我。
    • Manifest 条目中无法识别环境变量。
    【解决方案3】:

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

    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);
    

    引用自:How to load JAR files dynamically at Runtime?

    别忘了加载 attach.so(或 attach.dll)
    System.load(absPath)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(系统类加载器的子类)下开发插件,则应在您的 VM 安装后删除 AppClassLoader 中的类路径(这意味着让自定义 PluginClassLoader 加载它,或者不加载它的超级路径)分离。
    这里我用反射来完成。

    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();
            }
        }
    }
    

    希望对你有所帮助

    【讨论】:

      猜你喜欢
      • 2015-03-02
      • 2012-04-22
      • 1970-01-01
      • 2014-09-28
      • 2015-05-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多