/* * Copyright (c) 2003, 2024, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ import jdk.test.lib.Utils; import sun.management.jmxremote.ConnectorBootstrap; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.net.BindException; import java.net.InetAddress; import java.nio.file.Path; import java.rmi.server.ExportException; import jdk.internal.agent.AgentConfigurationError; import javax.management.MBeanAttributeInfo; import javax.management.MBeanInfo; import javax.management.MBeanServerConnection; import javax.management.ObjectName; import javax.management.QueryExp; import javax.management.remote.JMXConnector; import javax.management.remote.JMXConnectorFactory; import javax.management.remote.JMXConnectorServer; import javax.management.remote.JMXServiceURL; import java.security.Security; import java.util.ArrayList; import java.util.Enumeration; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Set; /* * @test * @bug 6528083 * @key intermittent * @summary Test RMI Bootstrap * * @library /test/lib * * @run main/othervm/timeout=300 RmiBootstrapTest .*_test.*.in * */ /* * @test * @bug 6528083 * @key intermittent * @summary Test RMI Bootstrap * * @library /test/lib * * @run main/othervm/timeout=300 RmiBootstrapTest .*_ssltest.*.in * */ /** *
This class implements unit test for RMI Bootstrap. * When called with no arguments main() looks in the directory indicated * by the "test.src" system property for files called management*ok.properties * or management*ko.properties. The *ok.properties files are assumed to be * valid Java M&M config files for which the bootstrap should succeed. * The *ko.properties files are assumed to be configurations for which the * bootstrap & connection test will fail.
* *The rmi port number can be specified with the "rmi.port" system property. * If not, this test will use the first available port
* *When called with some argument, the main() will interpret its args to * be Java M&M configuration file names. The filenames are expected to end * with ok.properties or ko.properties - and are interpreted as above.
* *Note that a limitation of the RMI registry (bug 4267864) prevent * this test from succeeding if more than 1 configuration is used. * As long as 4267864 isn't fix, this test must be called as many times * as needed but with a single argument (no arguments, or several arguments * will fail).
* *Debug traces are logged in "sun.management.test"
**/ public class RmiBootstrapTest extends RmiTestBase { static TestLogger log = new TestLogger("RmiBootstrapTest"); // the number of consecutive ports to test for availability private static int MAX_GET_FREE_PORT_TRIES = 10; /** * List all MBeans and their attributes. Used to test communication * with the Java M&M MBean Server. * * @return the number of queried MBeans. */ public static int listMBeans(MBeanServerConnection server) throws IOException { return listMBeans(server, null, null); } /** * List all matching MBeans and their attributes. * Used to test communication with the Java M&M MBean Server. * * @return the number of matching MBeans. */ public static int listMBeans(MBeanServerConnection server, ObjectName pattern, QueryExp query) throws IOException { final Set* This method calls connectAndRead(). **/ public void testCommunication(JMXServiceURL url) throws IOException { final String defaultConf = defaultFileNamePrefix + DefaultValues.CONFIG_FILE_NAME; final String confname = System.getProperty(PropertyNames.CONFIG_FILE_NAME, defaultConf); final Properties props = new Properties(); final File conf = new File(confname); if (conf.exists()) { FileInputStream fin = new FileInputStream(conf); try { props.load(fin); } finally { fin.close(); } } // Do we use authentication? final String useAuthenticationStr = props.getProperty(PropertyNames.USE_AUTHENTICATION, DefaultValues.USE_AUTHENTICATION); final boolean useAuthentication = Boolean.valueOf(useAuthenticationStr).booleanValue(); // Get Password File final String defaultPasswordFileName = Utils.convertPath(defaultFileNamePrefix + DefaultValues.PASSWORD_FILE_NAME); final String passwordFileName = Utils.convertPath(props.getProperty(PropertyNames.PASSWORD_FILE_NAME, defaultPasswordFileName)); // Get Access File final String defaultAccessFileName = Utils.convertPath(defaultFileNamePrefix + DefaultValues.ACCESS_FILE_NAME); final String accessFileName = Utils.convertPath(props.getProperty(PropertyNames.ACCESS_FILE_NAME, defaultAccessFileName)); if (useAuthentication) { System.out.println("PasswordFileName: " + passwordFileName); System.out.println("accessFileName: " + accessFileName); } final Object[] allCredentials; final Object[] noCredentials = {null}; if (useAuthentication) { final ArrayList l = readCredentials(passwordFileName); if (l.size() == 0) { allCredentials = null; } else { allCredentials = l.toArray(); } } else { allCredentials = noCredentials; } int errorCount = 0; if (allCredentials != null) { // Tests that the registered user/passwords are allowed to // connect & read // errorCount += connectAndRead(url, allCredentials, true, true); } else { // Tests that no one is allowed // connect & read // final String[][] someCredentials = {null, {"modify", "R&D"}, {"measure", "QED"}}; errorCount += connectAndRead(url, someCredentials, false, false); } if (useAuthentication && allCredentials != noCredentials) { // Tests that the registered user/passwords are not allowed to // connect & read // final String[][] badCredentials = {{"bad.user", "R&D"}, {"measure", "bad.password"}}; errorCount += connectAndRead(url, badCredentials, false, false); } if (errorCount > 0) { final String err = "Test " + confname + " failed with " + errorCount + " error(s)"; log.debug("testCommunication", err); throw new RuntimeException(err); } } /** * Test the configuration indicated by `file'. * Sets the appropriate System properties for config file and * port and then calls ConnectorBootstrap.initialize(). * eventually cleans up by calling ConnectorBootstrap.terminate(). * * @return null if the test succeeds, an error message otherwise. **/ private String testConfiguration(File file) throws IOException, InterruptedException { final String loopback = InetAddress.getLoopbackAddress().getHostAddress(); for (int i = 0; i < MAX_GET_FREE_PORT_TRIES; i++) { try { int port = jdk.test.lib.Utils.getFreePort(); final String path; try { path = (file == null) ? null : file.getCanonicalPath(); } catch (IOException x) { final String err = "Failed to test configuration " + file + ": " + x; log.trace("testConfiguration", err); log.debug("testConfiguration", x); return err; } final String config = (path == null) ? "Default config file" : path; System.out.println("***"); System.out.println("*** Testing configuration (host= " + loopback + " port=" + port + "): " + path); System.out.println("***"); System.setProperty("com.sun.management.jmxremote.host", loopback); System.setProperty("java.rmi.server.hostname", loopback); System.setProperty("com.sun.management.jmxremote.port", Integer.toString(port)); if (path != null) { System.setProperty("com.sun.management.config.file", path); } else { System.getProperties().remove("com.sun.management.config.file"); } log.trace("testConfiguration", "com.sun.management.jmxremote.port=" + port); if (path != null && log.isDebugOn()) { log.trace("testConfiguration", "com.sun.management.config.file=" + path); } checkSslConfiguration(); final JMXConnectorServer cs; try { cs = ConnectorBootstrap.initialize(); } catch (AgentConfigurationError x) { if (x.getCause() instanceof ExportException) { if (x.getCause().getCause() instanceof BindException) { throw (BindException) x.getCause().getCause(); } } final String err = "Failed to initialize connector:" + "\n\tcom.sun.management.jmxremote.port=" + port + ((path != null) ? "\n\tcom.sun.management.config.file=" + path : "\n\t" + config) + "\n\tError is: " + x; log.trace("testConfiguration", err); log.debug("testConfiguration", x); return err; } catch (Exception x) { log.debug("testConfiguration", x); return x.toString(); } try { JMXServiceURL url = new JMXServiceURL("rmi", null, 0, "/jndi/rmi://localhost:" + port + "/jmxrmi"); try { testCommunication(url); } catch (Exception x) { final String err = "Failed to connect to agent {url=" + url + "}: " + x; log.trace("testConfiguration", err); log.debug("testConfiguration", x); return err; } } catch (Exception x) { final String err = "Failed to test configuration " + config + ": " + x; log.trace("testConfiguration", err); log.debug("testConfiguration", x); return err; } finally { try { cs.stop(); } catch (Exception x) { final String err = "Failed to terminate: " + x; log.trace("testConfiguration", err); log.debug("testConfiguration", x); } } System.out.println("Configuration " + config + " successfully tested"); return null; } catch (BindException ex) { } } System.err.println("Cannot find a free port after " + MAX_GET_FREE_PORT_TRIES + " tries"); return "Failed: cannot find a free port after " + MAX_GET_FREE_PORT_TRIES + " tries"; } /** * Test a configuration file which should make the bootstrap fail. * The test is assumed to have succeeded if the bootstrap fails. * * @return null if the test succeeds, an error message otherwise. **/ private String testConfigurationKo(File conf) throws InterruptedException, IOException { String errStr = testConfiguration(conf); if (errStr == null) { return "Configuration " + conf + " should have failed!"; } System.out.println("Configuration " + conf + " failed as expected"); log.debug("runko", "Error was: " + errStr); return null; } /** * Test a configuration file. Determines whether the bootstrap * should succeed or fail depending on the file name: * *ok.properties: bootstrap should succeed. * *ko.properties: bootstrap or connection should fail. * * @return null if the test succeeds, an error message otherwise. **/ private String testConfigurationFile(String fileName) throws InterruptedException, IOException { File file = new File(fileName); if (fileName.endsWith("ok.properties")) { String errStr = null; errStr = testConfiguration(file); return errStr; } if (fileName.endsWith("ko.properties")) { return testConfigurationKo(file); } return fileName + ": test file suffix must be one of [ko|ok].properties"; } /** * Find all *ko.property files and test them. * (see findConfigurationFilesKo() and testConfigurationKo()) * * @throws RuntimeException if the test fails. **/ public void runko(boolean useSsl) throws InterruptedException, IOException { final File[] conf = RmiTestBase.findConfigurationFilesKo(useSsl); if ((conf == null) || (conf.length == 0)) { throw new RuntimeException("No configuration found"); } String errStr; for (int i = 0; i < conf.length; i++) { errStr = testConfigurationKo(conf[i]); if (errStr != null) { throw new RuntimeException(errStr); } } } /** * Find all *ok.property files and test them. * (see findConfigurationFilesOk() and testConfiguration()) * * @throws RuntimeException if the test fails. **/ public void runok(boolean useSsl) throws InterruptedException, IOException { final File[] conf = RmiTestBase.findConfigurationFilesOk(useSsl); if ((conf == null) || (conf.length == 0)) { throw new RuntimeException("No configuration found"); } String errStr = null; for (int i = 0; i < conf.length; i++) { errStr = testConfiguration(conf[i]); if (errStr != null) { throw new RuntimeException(errStr); } } // FIXME: No jmxremote.password is not installed in JRE by default. // - disable the following test case. // // Test default config // // errStr = testConfiguration(null,port+testPort++); // if (errStr != null) { // throw new RuntimeException(errStr); // } } /** * Finds all configuration files (*ok.properties and *ko.properties) * and tests them. * (see runko() and runok()). * * @throws RuntimeException if the test fails. **/ public void run(boolean useSsl) throws InterruptedException, IOException { runok(useSsl); runko(useSsl); } /** * Tests the specified configuration files. * If args[] is not empty, each element in args[] is expected to be * a filename ending either by ok.properties or ko.properties. * Otherwise, the configuration files will be automatically determined * by looking at all *.properties files located in the directory * indicated by the System property "test.src". * * @throws RuntimeException if the test fails. **/ public void run(String[] args) throws InterruptedException, IOException { if (args.length == 1) { run(args[0].contains("ssl")); } else { for (int i = 1; i < args.length; i++) { final String errStr = testConfigurationFile(args[i]); if (errStr != null) { throw new RuntimeException(errStr); } } } } }