【问题标题】:How to use a JMX RMI agent in a WebStart process?如何在 WebStart 进程中使用 JMX RMI 代理?
【发布时间】:2012-07-26 13:46:19
【问题描述】:

我正在尝试为通过 WebStart 的应用程序启动器实现基于 JMX 的 RMI 连接器的监视控制。

从命令行或 IDE 启动应用程序时,无论我如何启动它们,连接器都能正常工作。但是,当我通过使用 javaws 调用 JNLP 文件启动它时尝试检查它时,一切都变了。

我尝试了几种方法:

  • 使用内置的 JMX 功能,通过在 JAVAWS_VM_ARGS 中指定以下变量(文档说它应该在 1.6 中工作,但人们报告它没有;在结果过程中似乎根本没有设置它们,如果我使用 Attach API 检查它):

    -Dcom.sun.management.jmxremote
    -Dcom.sun.management.jmxremote.local.only=true
    -Dcom.sun.management.jmxremote.port=SOME_PORT_HERE
    -Dcom.sun.management.jmxremote.authenticate=false
    -Dcom.sun.management.jmxremote.ssl=false
    
  • 使用内置的 JMX 功能,通过在 JNLP 文件中指定以下变量(有人说这不应该工作,而且当我使用 Attach API 检查时它们似乎没有设置;JNLP 是也设置了所有权限):

    <property name="com.sun.management.jmxremote.local.only" value="true" />
    <property name="com.sun.management.jmxremote.port" value="SOME_PORT_HERE" />
    <property name="com.sun.management.jmxremote.authenticate" value="false" />
    <property name="com.sun.management.jmxremote.ssl" value="false" />
    
  • 使用内置的 JMX 功能,通过使用 -J 选项为调用 javaws 程序指定以下变量(如果我使用 Attach 检查它,它们似乎根本没有在结果过程中设置API):

    -J-Dcom.sun.management.jmxremote
    -J-Dcom.sun.management.jmxremote.local.only=true
    -J-Dcom.sun.management.jmxremote.port=SOME_PORT_HERE
    -J-Dcom.sun.management.jmxremote.authenticate=false
    -J-Dcom.sun.management.jmxremote.ssl=false
    
  • 使用带有以下代码的自定义 JMXConnectorServer(连接仍然像以前的尝试一样被拒绝,并显示“连接失败。重试?连接不成功。”):

        try {
            final MBeanServer  mbs     = ManagementFactory.getPlatformMBeanServer();
            final String       jmxPort = System.getProperty("jmx.port");
    
            if (jmxPort != null) {
                final int port = Integer.parseInt(jmxPort);
    
                System.out.println("settings JMX properties");
                System.setProperty("java.rmi.server.randomIDs", "true");
                Map<String, String> env = new HashMap<String, String>();
                env.put("com.sun.management.jmxremote.ssl", "false");
                env.put("com.sun.management.jmxremote.authenticate", "false");
                env.put("com.sun.management.jmxremote.local.only", "true");
                System.out.println("Creating Locate Registry");
                LocateRegistry.createRegistry(port);
                System.out.println("Creating JMX Service URL");
                JMXServiceURL url = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://localhost:" + port + "/server");
                System.out.println("Creating new JMX Connector Server");
                JMXConnectorServer cs = JMXConnectorServerFactory.newJMXConnectorServer(url, env, mbs);
                cs.start();
            }
        } catch (final RemoteException e) {
            e.printStackTrace();
        } catch (final MalformedURLException e) {
            e.printStackTrace();
        } catch (final IOException e) {
            e.printStackTrace();
        }
    

我尝试使用此 JMXServiceURL 连接 JConsole(最终,这将通过另一个程序完成,但我尝试从 JConsole 验证 JMX 服务是否正常运行):

    service:jmx:rmi:///jndi/rmi://localhost:SOME_PORT_HERE/jmxrmi

有没有人知道如何做到这一点,无论是通过编程方式还是如何将这些属性传递给 WebStart-ed 进程?

提前致谢。

附加说明

  • 这适用于 Java 1.6+(不是 1.5)。
  • 这些选项确实在我直接调用我的程序并通过命令行传递它们时起作用。
  • 有人提到直接指定 IP,使用 java.rmi.server.hostname=YOUR_IP。但是,当进程正常启动时,这似乎不需要,所以我认为它与 WebStart 场景无关。
  • 我可以使用 Attach API 的连接通过 PID 与 JConsole 或 JVisualVM 连接到进程,但最终产品不能依赖此,因为它需要 JDK 或捆绑 tools.jar,而我没有不认为有其他公开的替代方案。如果有的话,那实际上会容易得多,所以如果你知道的话,请告诉我。

更新:

实际上,当使用我最后一个带有显式 JMXConnectorServer 的示例时,我在 WebStart 进程的启动时得到一个 NPE,其中:

12:39:28,743 WARN  [App] [trce] java.lang.NullPointerException
 at org.jboss.security.jndi.LoginInitialContextFactory.getInitialContext(LoginInitialContextFactory.java:81)
 at javax.naming.spi.NamingManager.getInitialContext(NamingManager.java:667)
 at javax.naming.InitialContext.getDefaultInitCtx(InitialContext.java:288)
 at javax.naming.InitialContext.init(InitialContext.java:223)
 at javax.naming.InitialContext.<init>(InitialContext.java:197)
 at javax.management.remote.rmi.RMIConnectorServer.bind(RMIConnectorServer.java:619)
 at javax.management.remote.rmi.RMIConnectorServer.start(RMIConnectorServer.java:412)
 // line invoking .start() on the JMXConnectorServer's instance

【问题讨论】:

  • 我知道您不想要“您是否尝试过”cmets,但对于最后一个示例,您是否尝试使用类似以下网址:"service:jmx:rmi://localhost:" + port + "/jndi/rmi://localhost:" + port + "/server"。另外,我假设您的 webstart 应用程序被授予所有权限?
  • @jtahlborn:也不起作用。我已经在 J​​NLp 中授予了所有权限,是的(见第 2 点)。
  • 我认为最后一个解决方案可能有效,问题似乎是您配置的默认初始上下文是 jboss 上下文。您需要制作不使用 jboss 上下文的 rmi 东西。可能需要传入诸如“com.sun.jndi.rmi.registry.RegistryContextFactory”之类的初始上下文工厂,请参阅docs.oracle.com/javase/1.5.0/docs/guide/jndi/jndi-rmi.html
  • @jtahlborn:是的,这看起来确实是个问题。不幸的是,我需要设置这个 InitialContext。但是,这让我想起了我不久前遇到的一个类似问题(完全不同且不相关的上下文),我确实设法交换了初始上下文。这可能是在这里尝试的东西。如果我对此线程感兴趣,我会看看它的进展并回来:) 感谢您的帮助。
  • 漂亮,我很高兴你让它工作了。我相信其他人会欣赏这些细节。

标签: java jmx jnlp java-web-start


【解决方案1】:

正如在问题评论中与jtahlborn 讨论的那样,我们发现这确实是特定于应用程序设置的。

要让 JMX 为基于 WebStart 的应用程序工作,需要解决一些缺陷,并且对使用多个 JNDI 上下文工厂也有一些影响。

在这种特殊情况下,应用程序在启动和填充配置设置时读取了jndi.properties,因此 JBoss JNDI 上下文工厂甚至已经在 WebStart-ed 应用程序入口点的静态块的调用点 /主类。

由于这些 JNDI 属性对于程序的其余部分是必需的,因此解决方案就是在以编程方式创建 JMXServerConnector 时覆盖这些属性。

这是一个例子:

    @SuppressWarnings("serial")
    private static Properties   initProperties() {
        return (new Properties() {{
            // overrides for the JNDI factory
            // mostly changing the INITIAL_CONTEXT_FACTORY from:
            //   "org.jboss.security.jndi.JndiLoginInitialContextFactory"
            // to:
            put(Context.INITIAL_CONTEXT_FACTORY,
                "org.jnp.interfaces.NamingContextFactory");
            put(Context.PROVIDER_URL, "jnp://localhost/");

            // Required JMX properties and other things ...
            //  YMMV of course, these were for my current settings
            put("java.rmi.server.randomIDs", "true");
            put("java.rmi.server.hostname", host); // host needs to be an IP
            put("com.sun.management.jmxremote.ssl", "false");
            put("com.sun.management.jmxremote.authenticate", "false");
            put("com.sun.management.jmxremote.local.only", "true");
            put("com.sun.management.jmxremote.port", String.valueOf(port));
        }});
    }

和:

    // error handling left out for clarity
    private static void startJMXAgent() throws TheWorld {
        Registry           reg  = LocateRegistry.createRegistry(port);
        JMXConnectorServer cs   =
            JMXConnectorServerFactory.newJMXConnectorServer(
                new JMXServiceURL(
                    "service:jmx:rmi://" + host + ":" + port + "/" +
                    "jndi/rmi://" + host + ":" + port + "/" +
                    servicename
                ),
                initProperties(),
                ManagementFactory.getPlatformMBeanServer()
            );
        cs.start();
    }

注意:我假设您可以反过来做,并在命令行或 JNLP 文件中覆盖这些属性,然后将它们重置为以后上下文所需的任何内容。但是,这不适合这里的用例,所以我没有真正研究它。

【讨论】:

    【解决方案2】:

    尝试在 windows 的命令行中设置以下参数

    设置 JAVAWS_VM_ARGS=-Dcom.sun.management.jmxremote.port=9400 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false

    然后从同一命令提示符启动 jnlp 文件。它对我有用。

    【讨论】:

    • 谢谢。但是,请重新阅读我的问题:这种方法已经过尝试,在某些情况下并不可靠。 using the built-in JMX features, by specifying the following variables in JAVAWS_VM_ARGS (docs say it should work in 1.6, but people report it doesn't; they don't appear to be set at all in the resulting process, if I inspect it using the Attach API): [...]。其中一个问题是,在某些环境中,WS 启动器会分叉,而这些不会传递给分叉的孩子。您也可以尝试指定 nofork 选项,但这取决于系统,WS 可能会忽略它。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-08-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-03
    相关资源
    最近更新 更多