【问题标题】:Check if tools.jar is available and load it dynamically during runtime检查 tools.jar 是否可用并在运行时动态加载
【发布时间】:2016-03-29 20:26:06
【问题描述】:

我正在开发一个监控应用程序,它使用 Sigar 进行监控以监控不同类型的应用程序。 Sigar 的一个问题是,在监视 Java 应用程序 (JVM) 的堆使用情况时,我只能得到最大堆大小,而不是 JVM 实际使用的堆大小。 因此,我扩展了我的监控应用程序以使用 JMX 连接到 JVM 并检索 CPU 以及堆使用情况。到目前为止,这工作正常,但是 我想尽可能地自动化一切,我不想在激活 JMX 的情况下启动我的所有应用程序,受到监控,而是在需要时使用以下代码动态激活它:

private void connectToJVM(final String pid) throws IOException, AgentLoadException, AgentInitializationException {
    List<VirtualMachineDescriptor> vms = VirtualMachine.list();
    for (VirtualMachineDescriptor desc : vms) {
        if (!desc.id().equals(pid)) {
            continue;
        }
        VirtualMachine vm;
        try {
            vm = VirtualMachine.attach(desc);
        } catch (AttachNotSupportedException e) {
            continue;
        }
        Properties props = vm.getAgentProperties();
        String connectorAddress = props.getProperty(CONNECTOR_ADDRESS);
        if (connectorAddress == null) {
            String agent = vm.getSystemProperties().getProperty("java.home") + File.separator + "lib"
                    + File.separator + "management-agent.jar";
            vm.loadAgent(agent);

            // agent is started, get the connector address
            connectorAddress = vm.getAgentProperties().getProperty(CONNECTOR_ADDRESS);
        }
        vm.detach();
        JMXServiceURL url = new JMXServiceURL(connectorAddress);
        this.jmxConnector = JMXConnectorFactory.connect(url);
    }
}

到目前为止,这工作正常,但问题是我现在依赖于来自 JDK 的 tools.jar。 我现在的问题是我可以在运行时以某种方式检查tools.jar 是否在JAVA_HOME 路径中可用并在它可用时加载它?因为如果它不可用,我只想用 Sigar 进行正常监控,但如果它可用,我想使用 JMX 来监控 Java 应用程序。 我的项目是一个 maven 项目,我正在使用 maven-shade-plugin 创建一个包含所有依赖项的可执行 jar。

目前我正在使用我在互联网上发现的一个肮脏的黑客,它使用反射将tools.jar 动态添加到系统类路径(如果存在)。但我想知道是否也可以以不同的方式进行操作? 提前感谢您的支持。

【问题讨论】:

  • 这是个坏主意,尤其是因为这个 JAR 在 Java 9 中将消失。您应该使用 Compiler API 来访问 javac

标签: java maven jvm jmx tools.jar


【解决方案1】:

我在我的项目中做了类似的事情,look here

这个想法是通过路径中有tools.jar 的不同ClassLoader 加载您的实用程序类。

    File javaHome = new File(System.getProperty("java.home"));
    String toolsPath = javaHome.getName().equalsIgnoreCase("jre") ? "../lib/tools.jar" : "lib/tools.jar";

    URL[] urls = new URL[] {
            getClass().getProtectionDomain().getCodeSource().getLocation(),
            new File(javaHome, toolsPath).getCanonicalFile().toURI().toURL(),
    };

    URLClassLoader loader = new URLClassLoader(urls, null);
    Class<?> utilityClass = loader.loadClass("some.package.MyUtilityClass");

    utilityClass.getMethod("connect").invoke(null);

【讨论】:

  • 感谢您的及时回复。您的方法听起来很有趣,我将尝试使其适应我的项目 :) 使用您的方法,我无法像往常一样调用方法,但始终需要使用符号 class.getMethod("methodName").invoke(parameters)?
  • @tom1991te 不一定。您可以创建该类的实例并将其转换为某个已知接口,例如Runnable utility = (Runnable) cls.newInstance(); 然后正常调用:utility.run();
【解决方案2】:

在文件系统上查找 tools.jar 比 @apangin 的解决方案要复杂一些。 不同的JDK把tools.jar贴在不同的地方,如this method所示,声称支持IBM JDK和Mac上的HotSpot。

但即使是我引用的代码看起来也已经过时了。它建议所有 mac JDK 使用 classes.jar,但我的 Mac 1.7 和 1.8 JDK 使用 tools.jar。

这个other answer of mine 显示了一些 1.6、1.7 和 1.8 JDK 的 mac 工具.jar 和 classes.jar 文件的位置。

【讨论】:

    【解决方案3】:

    我最终使用的代码来自:org.gridkit.lab::jvm Attach Api

    从该源代码中,您只需要一个文件:AttachAPI.java

    /** * 版权所有 2013 Alexey Ragozin * * 根据 Apache 许可证 2.0 版(“许可证”)获得许可; * 除非遵守许可,否则您不得使用此文件。 * 您可以在以下网址获取许可证的副本 * * http://www.apache.org/licenses/LICENSE-2.0 * * 除非适用法律要求或书面同意,否则软件 *根据许可分发是在“原样”基础上分发的, * 不提供任何明示或暗示的保证或条件。 * 请参阅许可证以了解特定语言的管理权限和 * 许可证下的限制。 */ 包 org.gridkit.lab.jvm.attach; 导入java.lang.reflect.Method; 导入 java.net.URL; 导入 java.net.URLClassLoader; /** * @作者 Alexey Ragozin (alexey.ragozin@gmail.com) */ 类 AttachAPI { 私有静态最终 LogStream LOG_ERROR = LogStream.error(); 私有静态布尔值开始; 静止的 { 尝试 { String javaHome = System.getProperty("java.home"); 字符串 toolsJarURL = "file:" + javaHome + "/../lib/tools.jar"; // 公开 addURL 方法method = URLClassLoader.class.getDeclaredMethod("addURL", URL.class); 方法.setAccessible(true); URLClassLoader sysloader = (URLClassLoader)ClassLoader.getSystemClassLoader(); if (sysloader.getResourceAsStream("/com/sun/tools/attach/VirtualMachine.class") == null) { method.invoke(sysloader, (Object) new URL(toolsJarURL)); Thread.currentThread().getContextClassLoader().loadClass("com.sun.tools.attach.VirtualMachine"); Thread.currentThread().getContextClassLoader().loadClass("com.sun.tools.attach.AttachNotSupportedException"); } } 捕捉(异常 e){ LOG_ERROR.log("Java home 指向 " + System.getProperty("java.home") + " 确保它不是 JRE 路径"); LOG_ERROR.log("添加 tools.jar 到类路径失败", e); } 开始=真; }; 公共静态无效确保工具Jar(){ 如果(!开始){ LOG_ERROR.log("附加 API 未初始化"); } } }

    要使用此类,请将其放在项目中的某个位置,并确保您相应地更改其包。在下面的示例中,我将文件放在与 MyApp.java 文件相同的文件夹中,但我没有修改 AttachAPI.java 文件的 package 语句以反映这一点,因为我想让它保持原始状态。

    最后,在你的主类中,确保你有一个如下的块:

    公共类 MyApp { 静止的 { AttachAPI.ensureToolsJar(); } 公共静态无效确保工具Jar(){ // 什么都不做,只保证调用静态初始化器 } } ...

    现在您不再需要在命令行中指定 tools.jar 的路径,只需 java -jar MyApp.jar 即可启动您的应用程序

    【讨论】:

      猜你喜欢
      • 2012-04-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-10-15
      • 2016-06-16
      • 1970-01-01
      • 2016-05-23
      相关资源
      最近更新 更多