如何自动关闭JMXConnectorServer

时间:2012-09-28 18:06:46

标签: java jmx

我正在尝试启动JMXConnectorServer以进行管理和调试。但是我不希望这个服务阻止应用程序在最后一个非守护程序线程终止时正常退出。

换句话说,我希望以下程序立即终止:

public class Main {
    public static void main(final String[] args) throws IOException {
        MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
        JMXServiceURL jmxUrl = new JMXServiceURL("rmi", null, 0);
        JMXConnectorServer connectorServer =
            JMXConnectorServerFactory.newJMXConnectorServer(jmxUrl, null, mbs);
        connectorServer.start();
    }
}

5 个答案:

答案 0 :(得分:1)

是的,在某些时候你需要connectorServer.stop();

修改

在阅读你的评论时,听起来你应该这样做:

connectorServer.start();
try {
    // create thread-pool
    ExecutorService threadPool = Executors...
    // submit jobs to the thread-pool
    ...
    threadPool.shutdown();
    // wait for the submitted jobs to finish
    threadPool.awaitTermination(Long.MAX_LONG, TimeUnit.SECONDS);
} finally {
    connectorServer.stop();
}

@Nicholas关于挂钩的想法很好。但是,通常情况下,我的main线程等待某种从shutdown() JMX操作设置的变量。类似的东西:

public CountDownLatch shutdownLatch = new CountDownLatch(1);
...

// in main
connectorServer.start();
try {
    // do the main-thread stuff
    shutdownLatch.await();
} finally {
    connectorServer.stop();
}

// in some JMX exposed operation
public void shutdown() {
    Main.shutdownLatch.countDown();
}

另外,您可以使用我的SimpleJMX包来管理您的JMX服务器。

JmxServer jmxServer = new JmxServer(8000);
jmxServer.start();
try {
    // register our lookupCache object defined below
    jmxServer.register(lookupCache);
    jmxServer.register(someOtherObject);
} finally {
    jmxServer.stop();
}

答案 1 :(得分:1)

我玩类似的问题并编写了这个类:

public final class HardDaemonizer extends Thread {

    private final Runnable target;
    private final String newThreadName;

    public HardDaemonizer(Runnable target, String name, String newThreadName) {
        super(name == null ? "Daemonizer" : name);
        setDaemon(true);
        this.target = target;
        this.newThreadName = newThreadName;
    }

    @Override
    public void run() {
        try {
            List<Thread> tb = getSubThreads();
            target.run();
            List<Thread> ta = new java.util.ArrayList<>(getSubThreads());
            ta.removeAll(tb);
            for (Thread thread : ta) {
                thread.setName(newThreadName);
            }
            Thread.sleep(Long.MAX_VALUE);
        } catch (InterruptedException ex) {
            Logger.getLogger(HardDaemonizer.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    public static Thread daemonize(String daemonizerName, String newThreadName, Runnable target) {
        HardDaemonizer daemonizer = new HardDaemonizer(target, daemonizerName, newThreadName);
        daemonizer.start();
        return daemonizer;
    }

    private static List<Thread> getSubThreads() {
        ThreadGroup group = Thread.currentThread().getThreadGroup().getParent();
        Thread[] threads = new Thread[group.activeCount()];
        group.enumerate(threads);
        return java.util.Arrays.asList(threads);
    }
}

您可以这样使用它:

HardDaemonizer.daemonize(null, "ConnectorServer", new Runnable(){
    @Override
    public void run() {
        try {
            connectorServer.start();
        } catch (IOException ex) {
            Logger.getLogger(Ralph.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
});

小心 - 这很棘手!


修改

唉......这不适合你。它仅硬连接守护连接器线程,当jvm停止时,该线程将被终止。另外,您可以自定义此主题的名称。

或者,您可以添加标记completed并以daemonize方法循环进入,直到连接器服务器启动。


<强>简化

这是简化的守护进程,没有棘手的线程重命名:

public abstract class Daemonizer<T> extends Thread {

    private final T target;
    private boolean completed = false;
    private Exception cause = null;

    public Daemonizer(T target) {
        super(Daemonizer.class.getSimpleName());
        setDaemon(true);
        this.target = target;
    }

    @Override
    public void run() {
        try {
            act(target);
        } catch (Exception ex) {
            cause = ex;
        }
        completed = true;
        try {
            Thread.sleep(Long.MAX_VALUE);
        } catch (InterruptedException ex) {
        java.util.logging.Logger.getLogger(Daemonizer.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        }
    }

    public abstract void act(final T target) throws Exception;

    public static void daemonize(Daemonizer daemonizer) throws Exception {
        daemonizer.start();
        while (!daemonizer.completed) {
            Thread.sleep(50);
        }
        if (daemonizer.cause != null) {
            throw daemonizer.cause;
        }
    }
}

用法:

Daemonizer.daemonize(new Daemonizer<JMXConnectorServer>(server) {
    @Override
    public void act(JMXConnectorServer server) throws Exception {
        server.start();
    }
});

答案 2 :(得分:0)

您可以添加JVM Shutdown Hook来停止连接器服务器。

=====更新=====

不确定为什么你的关机钩子不起作用。也许您可以提供示例代码。这是一个例子:

public static void main(String[] args) {        
    try {
        log("Creating Connector Server");
        final JMXConnectorServer jcs = JMXConnectorServerFactory.newJMXConnectorServer(new JMXServiceURL("rmi", "localhost", 12387), null, ManagementFactory.getPlatformMBeanServer());
        Thread jcsStopper = new Thread("JCS-Stopper") {
            public void run() {
                if(jcs.isActive()) {
                    try {
                        jcs.stop();
                        log("Connector Server Stopped");
                    } catch (Exception e) {
                        log("Failed to stop JCS");
                        e.printStackTrace();
                    }                       
                }
            }
        };
        jcsStopper.setDaemon(false);
        Runtime.getRuntime().addShutdownHook(jcsStopper);
        log("Registered Server Stop Task");
        jcs.start();
        log("Server Started");
        Thread.sleep(3000);
        System.exit(0);

    } catch (Exception ex) {
        ex.printStackTrace(System.err);
    }
}

输出是:

[main]:Creating Connector Server
[main]:Registered Server Stop Task
[main]:Server Started
[JCS-Stopper]:Connector Server Stopped

答案 3 :(得分:0)

String port = getProperty("com.sun.management.jmxremote.port");
if (port == null) {
    port = String.valueOf(getAvailablePort());
    System.setProperty("com.sun.management.jmxremote.port", port);
    System.setProperty("com.sun.management.jmxremote.ssl", "false");
    System.setProperty("com.sun.management.jmxremote.authenticate", "false");

    sun.management.Agent.startAgent();
}
log.info(InetAddress.getLocalHost().getCanonicalHostName() + ":" + port);

答案 4 :(得分:0)

根据我的经验,JMXConnectorServer仅在您明确创建时才在用户线程中运行。

如果您通过系统属性为平台MBean服务器配置RMI访问权限,则隐式创建的JMX连接器服务器将作为守护程序进程运行,而不会阻止JMV关闭。为此,您的代码将缩小为以下

public class Main {
    public static void main(final String[] args) throws IOException {
        MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
    }
}

但您需要设置以下系统属性:

-Dcom.sun.management.jmxremote.port=1919
-Dcom.sun.management.jmxremote.authenticate=false
-Dcom.sun.management.jmxremote.ssl=false