2016-03-29 66 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