2016-03-29 60 views
0

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

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动态添加到系统类路径(如果存在)。但我想知道是否有可能以不同的方式做到这一点? 预先感谢您的支持。

+0

这是一个坏主意,不是因为这个JAR在Java 9中消失了。你应该使用Compiler API来访问'javac'。 –

回答

2

我在我的项目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); 
+0

感谢您的及时回复。你的方法听起来很有趣我会尝试将它适应到我的项目中:)用你的方法,我不能像通常那样调用方法,但总是需要使用符号'class.getMethod(“methodName”)。invoke(parameters) '? – tom1991te

+0

@ tom1991te不一定。您可以创建该类的一个实例并将其转换为某个已知的接口,例如'Runnable utility =(Runnable)cls.newInstance();'然后通常调用:'utility.run();' – apangin

0

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

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

other answer of mine显示mac的一些1.6,1.7和1.8 JDK的tools.jar和classes.jar文件的位置。

0

我最终使用的代码是:org.gridkit.lab :: JVM附加阿比

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

 

    /** 
    * Copyright 2013 Alexey Ragozin 
    * 
    * Licensed under the Apache License, Version 2.0 (the "License"); 
    * you may not use this file except in compliance with the License. 
    * You may obtain a copy of the License at 
    * 
    *  http://www.apache.org/licenses/LICENSE-2.0 
    * 
    * Unless required by applicable law or agreed to in writing, software 
    * distributed under the License is distributed on an "AS IS" BASIS, 
    * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
    * See the License for the specific language governing permissions and 
    * limitations under the License. 
    */ 
    package org.gridkit.lab.jvm.attach; 

    import java.lang.reflect.Method; 
    import java.net.URL; 
    import java.net.URLClassLoader; 

    /** 
    * @author Alexey Ragozin ([email protected]) 
    */ 
    class AttachAPI { 

     private static final LogStream LOG_ERROR = LogStream.error(); 
     private static boolean started; 

     static { 
      try { 
       String javaHome = System.getProperty("java.home"); 
       String toolsJarURL = "file:" + javaHome + "/../lib/tools.jar"; 

       // Make addURL public 
       Method 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.invoke(sysloader, (Object) new URL(toolsJarURL)); 
        Thread.currentThread().getContextClassLoader().loadClass("com.sun.tools.attach.VirtualMachine"); 
        Thread.currentThread().getContextClassLoader().loadClass("com.sun.tools.attach.AttachNotSupportedException"); 
       } 

      } catch (Exception e) { 
       LOG_ERROR.log("Java home points to " + System.getProperty("java.home") + " make sure it is not a JRE path"); 
       LOG_ERROR.log("Failed to add tools.jar to classpath", e); 
      } 
      started = true; 
     }; 

     public static void ensureToolsJar() { 
      if (!started) { 
       LOG_ERROR.log("Attach API not initialized"); 
      } 
     } 
    } 

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

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

 

    public class MyApp 
    { 
     static { 
      AttachAPI.ensureToolsJar(); 
     } 

     public static void ensureToolsJar() { 
      // do nothing, just ensure call to static initializer 
     } 



    } 

    ... 

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

相关问题