6760712: Provide a connector server option that causes it not to prevent the VM from exiting
authorsjiang
Tue, 09 Dec 2008 18:42:13 +0100
changeset 1706 4a382455c04d
parent 1696 aef312419918
child 1707 f6c20f760c59
6760712: Provide a connector server option that causes it not to prevent the VM from exiting Reviewed-by: emcmanus
jdk/src/share/classes/com/sun/jmx/remote/util/EnvHelp.java
jdk/src/share/classes/javax/management/remote/rmi/RMIJRMPServerImpl.java
jdk/test/javax/management/remote/mandatory/connection/DaemonRMIExporterTest.java
--- a/jdk/src/share/classes/com/sun/jmx/remote/util/EnvHelp.java	Fri Dec 05 21:59:09 2008 -0800
+++ b/jdk/src/share/classes/com/sun/jmx/remote/util/EnvHelp.java	Tue Dec 09 18:42:13 2008 +0100
@@ -1,3 +1,4 @@
+
 /*
  * Copyright 2003-2008 Sun Microsystems, Inc.  All Rights Reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
@@ -798,6 +799,24 @@
                 JMXConnectorServer.DELEGATE_TO_EVENT_SERVICE, true, true);
     }
 
+    /**
+     * <p>Name of the attribute that specifies whether a connector server
+     * should not prevent the VM from exiting
+     */
+    public static final String JMX_SERVER_DAEMON = "jmx.remote.x.daemon";
+
+    /**
+     * Returns true if {@value SERVER_DAEMON} is specified in the {@code env}
+     * as a key and its value is a String and it is equal to true ignoring case.
+     *
+     * @param env
+     * @return
+     */
+    public static boolean isServerDaemon(Map env) {
+        return (env != null) &&
+                ("true".equalsIgnoreCase((String)env.get(JMX_SERVER_DAEMON)));
+    }
+
 //    /**
 //     * <p>Name of the attribute that specifies an EventRelay object to use.
 //     */
--- a/jdk/src/share/classes/javax/management/remote/rmi/RMIJRMPServerImpl.java	Fri Dec 05 21:59:09 2008 -0800
+++ b/jdk/src/share/classes/javax/management/remote/rmi/RMIJRMPServerImpl.java	Tue Dec 09 18:42:13 2008 +0100
@@ -38,6 +38,9 @@
 import javax.security.auth.Subject;
 
 import com.sun.jmx.remote.internal.RMIExporter;
+import com.sun.jmx.remote.util.EnvHelp;
+import sun.rmi.server.UnicastServerRef;
+import sun.rmi.server.UnicastServerRef2;
 
 /**
  * <p>An {@link RMIServer} object that is exported through JRMP and that
@@ -93,12 +96,27 @@
     }
 
     private void export(Remote obj) throws RemoteException {
-        RMIExporter exporter =
+        final RMIExporter exporter =
             (RMIExporter) env.get(RMIExporter.EXPORTER_ATTRIBUTE);
-        if (exporter == null)
+        final boolean daemon = EnvHelp.isServerDaemon(env);
+
+        if (daemon && exporter != null) {
+            throw new IllegalArgumentException("If "+EnvHelp.JMX_SERVER_DAEMON+
+                    " is specified as true, "+RMIExporter.EXPORTER_ATTRIBUTE+
+                    " cannot be used to specify an exporter!");
+        }
+
+        if (daemon) {
+            if (csf == null && ssf == null) {
+                new UnicastServerRef(port).exportObject(obj, null, true);
+            } else {
+                new UnicastServerRef2(port, csf, ssf).exportObject(obj, null, true);
+            }
+        } else if (exporter != null) {
+            exporter.exportObject(obj, port, csf, ssf);
+        } else {
             UnicastRemoteObject.exportObject(obj, port, csf, ssf);
-        else
-            exporter.exportObject(obj, port, csf, ssf);
+        }
     }
 
     private void unexport(Remote obj, boolean force)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/management/remote/mandatory/connection/DaemonRMIExporterTest.java	Tue Dec 09 18:42:13 2008 +0100
@@ -0,0 +1,123 @@
+/*
+ * Copyright 2003-2008 Sun Microsystems, Inc.  All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+
+/*
+ * @test
+ * @bug 6760712
+ * @summary test the connector server option that causes it not to prevent the
+ * VM from exiting
+ * @author Shanliang JIANG, Eamonn McManus
+ * @run main/othervm DaemonRMIExporterTest
+ */
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import javax.management.MBeanServerFactory;
+import javax.management.remote.JMXConnector;
+import javax.management.remote.JMXConnectorFactory;
+import javax.management.remote.JMXServiceURL;
+import javax.management.remote.JMXConnectorServer;
+import javax.management.remote.JMXConnectorServerFactory;
+
+// Test the connector server option that causes it not to prevent the VM
+// from exiting.  It's tricky to test exactly that, though possible.  If
+// we're being run from within jtreg, then jtreg has threads of its own
+// that will prevent the VM from exiting.  What's more it will kill all
+// threads that the test created as soon as the main method returns,
+// including the ones that would prevent the VM from exiting without the
+// special option.
+// Here we check that the test code does not create
+// any permanent non-daemon threads, by recording the initial set of
+// non-daemon threads (including at least one from jtreg), doing our stuff,
+// then waiting for there to be no non-daemon threads that were not in
+// the initial set.
+public class DaemonRMIExporterTest {
+    public static void main(String[] args) throws Exception {
+        Set<Thread> initialNonDaemonThreads = getNonDaemonThreads();
+
+        JMXServiceURL addr = new JMXServiceURL("rmi", null, 0);
+        System.out.println("DaemonRMIExporterTest: Creating a RMIConnectorServer on " + addr);
+        Map<String, ?> env =
+            Collections.singletonMap("jmx.remote.x.daemon", "true");
+        JMXConnectorServer server =
+                JMXConnectorServerFactory.newJMXConnectorServer(addr,
+                env,
+                MBeanServerFactory.createMBeanServer());
+        server.start();
+        System.out.println("DaemonRMIExporterTest: Started the server on " + server.getAddress());
+
+        System.out.println("DaemonRMIExporterTest: Connecting a client to the server ...");
+        final JMXConnector conn = JMXConnectorFactory.connect(server.getAddress());
+        conn.getMBeanServerConnection().getDefaultDomain();
+
+        System.out.println("DaemonRMIExporterTest: Closing the client ...");
+        conn.close();
+
+        System.out.println("DaemonRMIExporterTest No more user code to execute, the VM should " +
+                "exit normally, otherwise will be blocked forever if the bug is not fixed.");
+
+        long deadline = System.currentTimeMillis() + 10000;
+        ok: {
+            while (System.currentTimeMillis() < deadline) {
+                Set<Thread> nonDaemonThreads = getNonDaemonThreads();
+                nonDaemonThreads.removeAll(initialNonDaemonThreads);
+                if (nonDaemonThreads.isEmpty())
+                    break ok;
+                System.out.println("Non-daemon threads: " + nonDaemonThreads);
+                try {
+                    Thread.sleep(500);
+                } catch (InterruptedException e) {
+                    throw new AssertionError(e);
+                }
+            }
+            throw new Exception("TEST FAILED: non-daemon threads remain");
+        }
+
+        System.out.println("TEST PASSED");
+    }
+
+    private static Set<Thread> getNonDaemonThreads() {
+        ThreadGroup tg = Thread.currentThread().getThreadGroup();
+        while (tg.getParent() != null)
+            tg = tg.getParent();
+        Thread[] threads = null;
+        for (int size = 10; size < 10000; size *= 2) {
+            threads = new Thread[size];
+            int n = tg.enumerate(threads, true);
+            if (n < size) {
+                threads = Arrays.copyOf(threads, n);
+                break;
+            }
+        }
+        Set<Thread> ndThreads = new HashSet<Thread>();
+        for (Thread t : threads) {
+            if (!t.isDaemon())
+                ndThreads.add(t);
+        }
+        return ndThreads;
+    }
+}