检查tools.jar是否可用,并在运行时动态加载它



我正在开发一个监控应用程序,它使用Sigar进行监控来监控不同类型的应用程序。Sigar的一个问题是,在监视Java应用程序(JVM)的堆使用情况时,我只得到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动态添加到系统类路径中(如果存在的话)。但我想知道是否也可以采取不同的做法?提前感谢您的支持。

我在我的项目中也做了类似的事情,看看这里。

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

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

在文件系统上查找tools.jar比@angin的解决方案稍微棘手一些。不同JDK将tools.jar粘贴在不同的位置,如该方法所示,该方法声称支持Mac上的IBMJDK和HotSpot。

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

我的另一个答案显示了适用于mac的tools.jar和classes.jar文件的位置,分别是1.6、1.7和1.8 JDK。

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

  • https://mvnrepository.com/artifact/org.gridkit.lab/jvm-attach-api/1.2

  • 源代码:http://central.maven.org/maven2/org/gridkit/lab/jvm-attach-api/1.2/

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

/***版权所有2013 Alexey Ragozin**根据Apache许可证2.0版许可("许可证");*除非符合许可证的规定,否则您不得使用此文件。*您可以在以下网址获取许可证副本:**http://www.apache.org/licenses/LICENSE-2.0**除非适用法律要求或书面同意,软件*根据许可证进行的分发是在"按原样"的基础上进行的,*无任何明示或暗示的保证或条件。*有关管理权限的特定语言,请参阅许可证*许可下的限制。*/包org.ridkit.lab.jvm.attach;导入java.lang.reflect.Method;导入java.net.URL;导入java.net.URLClassLoader;/***@作者Alexey Ragozin(alexey.ragozin@gmail.com)*/类AttachAPI{private static final LogStream LOG_ERROR=LogStream.ERROR();私有静态布尔值已启动;静态的{尝试{字符串javaHome=System.getProperty("java.home");字符串toolsJarURL="file:"+javaHome+"/../lib/tools.jar";//公开addURL方法Method=URLClassLoader.class.getDeclaredMethod("addURL",URL.class);method.setAccessible(true);URLClassLoader sysloader=(URLClassLoader)ClassLoader.getSystemClassLoader();if(sysloader.getResourceAsStream(".com/sun/tools/attach/VirtualMachine.class")==null){method.ioke(sysloader,(Object)new URL(toolsJarURL));Thread.currentThread().getContextClassLoader().loadClass("com.sun.tools.attach.VirtualMachine");Thread.currentThread().getContextClassLoader().loadClass("com.sun.tools.attach.AttachNotSupportedException");}}catch(异常e){LOG_ERROR.LOG("Java home指向"+System.getProperty("Java.home")+"确保它不是JRE路径");LOG_ERROR.LOG("未能将tools.jar添加到类路径",e);}started=true;};公共静态void ensureToolsJar(){if(!已启动){LOG_ERROR.LOG("附加API未初始化");}}}

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

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

公共类MyApp{静态的{AttachAPI.ensereToolsJar();}公共静态void ensureToolsJar(){//什么都不做,只需确保调用静态初始值设定项}}。。。

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

相关内容

  • 没有找到相关文章

最新更新