2011-09-01 37 views
8

我有一個程序需要動態地(即在運行時)打開一個可用的套接字並在其上啓動一個JMX代理。這個JMX參數是在Java代碼中設置的,而不是通過命令行設置的。這工作正常。此後它被需要監測(即問題JMX命令等)通過Java視覺VM 遠程如何通過java代碼設置JMX遠程端口系統環境參數進行遠程監控?

在程序中的RMI服務器代理上在所述的出盒管理的線: http://download.oracle.com/javase/6/docs/technotes/guides/management/agent.html

問題我已經可以概括爲: 如何這樣的命令行屬性被設置爲系統級 通過Java代碼,使得遠程分析可用於??

-Dcom.sun.management.jmxremote.port=1234 

如果「jmxremote.port」等參數是通過命令行設置, 遠程監控工作正常。我正在嘗試通過Java 而不是通過命令行來找到一種方法。

程序無法通過命令行作爲新的可用端口指定端口具有在運行時計算出來。

流程需要遠程監控和在本地工作正常。 如果未在命令行指定以下參數,則Java Visual VM不會連接到該進程。

-Dcom.sun.management.jmxremote.port=1234 
-Dcom.sun.management.jmxremote.authenticate=false 
-Dcom.sun.management.jmxremote.ssl=false 
-Djava.rmi.server.hostname=10.0.0.128 

我試過了。

System.setProperty("com.sun.management.jmxremote.port",Integer.toString(port)); 

這是啓動JMXConnectorServer之前在程序中完成的第一件事情之一。不幸的是它不被認可。只有運行時屬性(即,通過命令行指定的特性被Java Visual VM識別爲JMX連接)。

對面可以從Java集合類中提取的方式特性也來了,但未能達成如何跟蹤屬性「com.sun.management.jmxremote.port =」

public static void setEnv(Map<String, String> newenv) throws Exception { 
    Class[] classes = Collections.class.getDeclaredClasses(); 
    Map<String, String> env = System.getenv(); 

    for(Class cl : classes) { 

    if("java.util.Collections$UnmodifiableMap".equals(cl.getName())) { 

     Field field = cl.getDeclaredField("m"); 
     field.setAccessible(true); 

     Object obj = field.get(env); 
     Map<String, String> map = (Map<String, String>) obj; 

     //map.clear(); 
     map.putAll(newenv); 
    } 
    } 
} 

任何幫助,將不勝感激!

+0

你應該修改你原來的問題,其中包括代碼示例,您已經在評論被張貼。 – Perception

回答

0

System.setProperty()是相同的-D命令行選項。然而,顯然你必須儘早調用它,以便在讀取屬性之前設置屬性。

+0

Profiler讀取遠程分析的屬性是在JVM啓動時設置的默認屬性。在這種情況下,當java代碼開始時。我在稍後啓動java visual vm profiler並在JMXConnectorServer啓動之前設置屬性。試圖找出如何超越屬性,以便它被反映爲一個jmx系統級別,並可以通過java visual vm檢測到?希望能夠提供任何有關如何「儘早」稱呼它的建議。 – Devesh

+0

運行沒有任何'-Dcom.sun.management.jmxremote'的應用程序,並在運行時設置此env是一個壞主意,因爲JMX代理未加載,因此無法使用。 – kbec

2

如果不指定任何jmxremote ENV作爲運行PARAM然後JMX管理代理未加載。你可以嘗試一下本作動態加載:

public static String loadJMXAgent(int port) throws IOException, AttachNotSupportedException, AgentLoadException, AgentInitializationException { 
    System.setProperty("com.sun.management.jmxremote.port", Integer.toString(port)); 
    String name = ManagementFactory.getRuntimeMXBean().getName(); 
    VirtualMachine vm = VirtualMachine.attach(name.substring(0, name.indexOf('@'))); 

    String lca = vm.getAgentProperties().getProperty("com.sun.management.jmxremote.localConnectorAddress"); 
    if (lca == null) { 
     Path p = Paths.get(System.getProperty("java.home")).normalize(); 
     if (!"jre".equals(p.getName(p.getNameCount()-1).toString().toLowerCase())) p = p.resolve("jre"); 
     File f = p.resolve("lib").resolve("management-agent.jar").toFile(); 
     if (!f.exists()) throw new IOException("Management agent not found"); 

     vm.loadAgent(f.getCanonicalPath(), "com.sun.management.jmxremote"); 
     lca = vm.getAgentProperties().getProperty("com.sun.management.jmxremote.localConnectorAddress"); 
    } 
    vm.detach(); 
    return lca; 
} 

您必須包括jdk/lib/tools.jar

9

kbec的回答顯示的方式,但對我來說沒有工作 - 但是通過this post找我能夠修改並得到一個工作解。

public static String loadJMXAgent(int port) throws IOException, 
     AttachNotSupportedException, AgentLoadException, 
     AgentInitializationException { 
    String name = ManagementFactory.getRuntimeMXBean().getName(); 
    VirtualMachine vm = VirtualMachine.attach(name.substring(0, 
      name.indexOf('@'))); 

    String lca = vm.getAgentProperties().getProperty(
      "com.sun.management.jmxremote.localConnectorAddress"); 
    if (lca == null) { 
     Path p = Paths.get(System.getProperty("java.home")).normalize(); 
     if (!"jre".equals(p.getName(p.getNameCount() - 1).toString() 
       .toLowerCase())) { 
      p = p.resolve("jre"); 
     } 
     File f = p.resolve("lib").resolve("management-agent.jar").toFile(); 
     if (!f.exists()) { 
      throw new IOException("Management agent not found"); 
     } 
     String options = String.format("com.sun.management.jmxremote.port=%d, " + 
       "com.sun.management.jmxremote.authenticate=false, " + 
       "com.sun.management.jmxremote.ssl=false", port); 
     vm.loadAgent(f.getCanonicalPath(), options); 
     lca = vm.getAgentProperties().getProperty(
       "com.sun.management.jmxremote.localConnectorAddress"); 
    } 
    vm.detach(); 
    return lca; 
} 

這個工程在Eclipse中卻得到它在命令行的工作是不同的事情 - 有關於這個這裏Why does using the Java Attach API fail on linux? (even though maven build completes) 一些討論,但我發現加入$ JAVA_HOME/lib中/工具。jar到我的classpath解決了這個問題。

1

我嘗試了一些方法來指定從Java代碼jmxremote端口有一個特定的端口上的連接,並已找出以下幾點:

如果jmxremote arg的規定: 的平臺MBean服務器是在我的代碼修改必要的jmxremote System.properties之前由JVM啓動。每個mbean服務器都有一個自己的bean註冊表。平臺和JVM mbeans無法以其他方式註冊自己的bean。

您可以在設置jmx端口屬性後創建備用mbean服務器。這將監聽您指定的正確的jmx端口。

這樣你選擇平臺服務器:

MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); 

這樣自己

System.setProperty("com.sun.management.jmxremote.port","9991"); 
//... 
MBeanServer mbsCustom=MBeanServerFactory.createMBeanServer(); 

還認爲,Linux已經是環回接口所以你應該指定正確的主機名明確地收聽。

不建議使用的其他的MBeanServer比平臺的,根據手冊,但我可以想像某些情況下的命令行選項都沒有,你可以啓動服務器的方式。

0

這是對我有用的東西。參考文獻Oracle JMX Tutorial。我假設你已經知道如何在下面的例子中使用正確的SimpleMXBean。

package sample; 

import java.io.IOException; 
import java.lang.management.ManagementFactory; 
import java.rmi.registry.LocateRegistry; 
import java.util.HashMap; 
import java.util.Map; 

import javax.management.MBeanServer; 
import javax.management.ObjectName; 
import javax.management.remote.JMXConnectorServer; 
import javax.management.remote.JMXConnectorServerFactory; 
import javax.management.remote.JMXServiceURL; 

public class MBServerTest { 
    public static void loadJMXAgent(int port, MBeanServer mbs) throws IOException { 
     LocateRegistry.createRegistry(port); 
     System.out.println("Initialize the environment map"); 
     Map<String,Object> env = new HashMap<String,Object>(); 
     env.put("com.sun.management.jmxremote.authenticate", "false"); 
     env.put("com.sun.management.jmxremote.ssl", "false"); 
     System.out.println("Create an RMI connector server"); 
     JMXServiceURL url = 
      new JMXServiceURL("service:jmx:rmi:///jndi/rmi://:"+port+"/jmxrmi"); 
     JMXConnectorServer cs = 
      JMXConnectorServerFactory.newJMXConnectorServer(url, env, mbs); 

     // Start the RMI connector server. 
     // 
     System.out.println("Start the RMI connector server"); 
     cs.start(); 

    } 

    public static void main(String[] args) throws Exception { 
     MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); 
     loadJMXAgent(1199,mbs); 

     SimpleStandard cache = new SimpleStandard(); 

     ObjectName name = new ObjectName(
       "org.javalobby.tnt.jmx:type=ApplicationCacheMBean"); 
     mbs.registerMBean(cache, name); 
     imitateActivity(cache); 
    } 

    private static void imitateActivity(SimpleStandard cache) { 
     while (true) { 
      try { 
       cache.cacheObject("hello"); 
       Thread.sleep(1000); 
      } catch (InterruptedException e) { 
      } 
     } 
    } 
} 
+0

小心解釋反對票? –

4

你這樣做的方式有點錯誤。在你的代碼被調用的時候,你已經錯過了這些屬性產生任何效果的機會。

你需要創建一個RMIREGISTRY,然後創建一個鏈接到平臺的JMXConnectorServer的MBeanServer這樣的:

private void createJmxConnectorServer() throws IOException { 
    LocateRegistry.createRegistry(1234); 
    MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); 
    JMXServiceURL url = new JMXServiceURL("service:jmx:rmi://localhost/jndi/rmi://localhost:1234/jmxrmi"); 
    JMXConnectorServer svr = JMXConnectorServerFactory.newJMXConnectorServer(url, null, mbs); 
    svr.start(); 
}