1 /*
   2  * Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 import sun.management.jmxremote.ConnectorBootstrap;
  24 
  25 import java.io.File;
  26 import java.io.FileInputStream;
  27 import java.io.FilenameFilter;
  28 import java.io.IOException;
  29 import java.net.BindException;
  30 import java.rmi.server.ExportException;
  31 
  32 import java.util.Properties;
  33 import java.util.Iterator;
  34 import java.util.Set;
  35 import java.util.ArrayList;
  36 import java.util.HashMap;
  37 import java.util.Map;
  38 import java.util.Enumeration;
  39 
  40 import javax.management.remote.*;
  41 import javax.management.*;
  42 
  43 import jdk.internal.agent.AgentConfigurationError;
  44 
  45 import java.security.Security;
  46 
  47 /**
  48  * <p>This class implements unit test for RMI Bootstrap.
  49  * When called with no arguments main() looks in the directory indicated
  50  * by the "test.src" system property for files called management*ok.properties
  51  * or management*ko.properties. The *ok.properties files are assumed to be
  52  * valid Java M&M config files for which the bootstrap should succeed.
  53  * The *ko.properties files are assumed to be configurations for which the
  54  * bootstrap & connection test will fail.</p>
  55  *
  56  * <p>The rmi port number can be specified with the "rmi.port" system property.
  57  * If not, this test will use the first available port</p>
  58  *
  59  * <p>When called with some argument, the main() will interprete its args to
  60  * be Java M&M configuration file names. The filenames are expected to end
  61  * with ok.properties or ko.properties - and are interpreted as above.</p>
  62  *
  63  * <p>Note that a limitation of the RMI registry (bug 4267864) prevent
  64  * this test from succeeding if more than 1 configuration is used.
  65  * As long as 4267864 isn't fix, this test must be called as many times
  66  * as needed but with a single argument (no arguments, or several arguments
  67  * will fail).</p>
  68  *
  69  * <p>Debug traces are logged in "sun.management.test"</p>
  70  **/
  71 public class RmiBootstrapTest {
  72     // the number of consecutive ports to test for availability
  73     private static int MAX_GET_FREE_PORT_TRIES = 10;
  74     static TestLogger log =
  75         new TestLogger("RmiBootstrapTest");
  76 
  77     /**
  78      * Default values for RMI configuration properties.
  79      **/
  80     public static interface DefaultValues {
  81         public static final String PORT="0";
  82         public static final String CONFIG_FILE_NAME="management.properties";
  83         public static final String USE_SSL="true";
  84         public static final String USE_AUTHENTICATION="true";
  85         public static final String PASSWORD_FILE_NAME="jmxremote.password";
  86         public static final String ACCESS_FILE_NAME="jmxremote.access";
  87         public static final String KEYSTORE="keystore";
  88         public static final String KEYSTORE_PASSWD="password";
  89         public static final String TRUSTSTORE="truststore";
  90         public static final String TRUSTSTORE_PASSWD="trustword";
  91         public static final String SSL_NEED_CLIENT_AUTH="false";
  92     }
  93 
  94     /**
  95      * Names of RMI configuration properties.
  96      **/
  97     public static interface PropertyNames {
  98         public static final String PORT=
  99             "com.sun.management.jmxremote.port";
 100         public static final String CONFIG_FILE_NAME=
 101             "com.sun.management.config.file";
 102         public static final String USE_SSL=
 103             "com.sun.management.jmxremote.ssl";
 104         public static final String USE_AUTHENTICATION=
 105             "com.sun.management.jmxremote.authenticate";
 106         public static final String PASSWORD_FILE_NAME=
 107             "com.sun.management.jmxremote.password.file";
 108         public static final String ACCESS_FILE_NAME=
 109             "com.sun.management.jmxremote.access.file";
 110         public static final String INSTRUMENT_ALL=
 111             "com.sun.management.instrumentall";
 112         public static final String CREDENTIALS =
 113             "jmx.remote.credentials";
 114         public static final String KEYSTORE=
 115             "javax.net.ssl.keyStore";
 116         public static final String KEYSTORE_PASSWD=
 117             "javax.net.ssl.keyStorePassword";
 118         public static final String TRUSTSTORE=
 119             "javax.net.ssl.trustStore";
 120         public static final String TRUSTSTORE_PASSWD=
 121             "javax.net.ssl.trustStorePassword";
 122         public static final String SSL_ENABLED_CIPHER_SUITES =
 123             "com.sun.management.jmxremote.ssl.enabled.cipher.suites";
 124         public static final String SSL_ENABLED_PROTOCOLS =
 125             "com.sun.management.jmxremote.ssl.enabled.protocols";
 126         public static final String SSL_NEED_CLIENT_AUTH =
 127             "com.sun.management.jmxremote.ssl.need.client.auth";
 128         public static final String SSL_CLIENT_ENABLED_CIPHER_SUITES =
 129             "javax.rmi.ssl.client.enabledCipherSuites";
 130     }
 131 
 132     /**
 133      * A filter to find all filenames who match <prefix>*<suffix>.
 134      * Note that <prefix> and <suffix> can overlap.
 135      **/
 136     private static class ConfigFilenameFilter implements FilenameFilter {
 137         final String suffix;
 138         final String prefix;
 139         ConfigFilenameFilter(String prefix, String suffix) {
 140             this.suffix=suffix;
 141             this.prefix=prefix;
 142         }
 143         public boolean accept(File dir, String name) {
 144             return (name.startsWith(prefix) && name.endsWith(suffix));
 145         }
 146     }
 147 
 148     /**
 149      * Get all "management*ok.properties" files in the directory
 150      * indicated by the "test.src" management property.
 151      **/
 152     private static File[] findConfigurationFilesOk() {
 153         final String testSrc = System.getProperty("test.src");
 154         final File dir = new File(testSrc);
 155         final FilenameFilter filter =
 156             new ConfigFilenameFilter("management_test","ok.properties");
 157         return dir.listFiles(filter);
 158     }
 159 
 160     /**
 161      * Get all "management*ko.properties" files in the directory
 162      * indicated by the "test.src" management property.
 163      **/
 164     private static File[] findConfigurationFilesKo() {
 165         final String testSrc = System.getProperty("test.src");
 166         final File dir = new File(testSrc);
 167         final FilenameFilter filter =
 168             new ConfigFilenameFilter("management_test","ko.properties");
 169         return dir.listFiles(filter);
 170     }
 171 
 172     /**
 173      * List all MBeans and their attributes. Used to test communication
 174      * with the Java M&M MBean Server.
 175      * @return the number of queried MBeans.
 176      */
 177     public static int listMBeans(MBeanServerConnection server)
 178         throws IOException {
 179         return listMBeans(server,null,null);
 180     }
 181 
 182     /**
 183      * List all matching MBeans and their attributes.
 184      * Used to test communication with the Java M&M MBean Server.
 185      * @return the number of matching MBeans.
 186      */
 187     public static int listMBeans(MBeanServerConnection server,
 188                                   ObjectName pattern, QueryExp query)
 189         throws IOException {
 190 
 191         final Set names = server.queryNames(pattern,query);
 192         for (final Iterator i=names.iterator(); i.hasNext(); ) {
 193             ObjectName name = (ObjectName)i.next();
 194             log.trace("listMBeans","Got MBean: "+name);
 195             try {
 196                 MBeanInfo info =
 197                     server.getMBeanInfo((ObjectName)name);
 198                 MBeanAttributeInfo[] attrs = info.getAttributes();
 199                 if (attrs == null) continue;
 200                 for (int j=0; j<attrs.length; j++) {
 201                     if (attrs[j].isReadable()) {
 202                         try {
 203                             Object o =
 204                                 server.getAttribute(name,attrs[j].getName());
 205                             if (log.isDebugOn())
 206                                 log.debug("listMBeans","\t\t" +
 207                                           attrs[j].getName() +
 208                                           " = "+o);
 209                         } catch (Exception x) {
 210                             log.trace("listMBeans","JmxClient failed to get " +
 211                                       attrs[j].getName() + ": " + x);
 212                             final IOException io =
 213                                 new IOException("JmxClient failed to get " +
 214                                                 attrs[j].getName());
 215                             io.initCause(x);
 216                             throw io;
 217                         }
 218                     }
 219                 }
 220             } catch (Exception x) {
 221                log.trace("listMBeans",
 222                          "JmxClient failed to get MBeanInfo: "  + x);
 223                 final IOException io =
 224                     new IOException("JmxClient failed to get MBeanInfo: "+x);
 225                 io.initCause(x);
 226                 throw io;
 227             }
 228         }
 229         return names.size();
 230     }
 231 
 232     /**
 233      * Compute the full path name for a default file.
 234      * @param basename basename (with extension) of the default file.
 235      * @return ${JRE}/conf/management/${basename}
 236      **/
 237     private static String getDefaultFileName(String basename) {
 238         final String fileSeparator = File.separator;
 239         final StringBuffer defaultFileName =
 240             new StringBuffer(System.getProperty("java.home")).
 241             append(fileSeparator).append("conf").append(fileSeparator).
 242             append("management").append(fileSeparator).
 243             append(basename);
 244         return defaultFileName.toString();
 245     }
 246 
 247     /**
 248      * Compute the full path name for a default file.
 249      * @param basename basename (with extension) of the default file.
 250      * @return ${JRE}/conf/management/${basename}
 251      **/
 252     private static String getDefaultStoreName(String basename) {
 253         final String fileSeparator = File.separator;
 254         final StringBuffer defaultFileName =
 255             new StringBuffer(System.getProperty("test.src")).
 256             append(fileSeparator).append("ssl").append(fileSeparator).
 257             append(basename);
 258         return defaultFileName.toString();
 259     }
 260 
 261 
 262     /**
 263      * Parses the password file to read the credentials.
 264      * Returns an ArrayList of arrays of 2 string:
 265      * {<subject>, <password>}.
 266      * If the password file does not exists, return an empty list.
 267      * (File not found = empty file).
 268      **/
 269     private ArrayList readCredentials(String passwordFileName)
 270         throws IOException {
 271         final Properties pws = new Properties();
 272         final ArrayList  result = new ArrayList();
 273         final File f = new File(passwordFileName);
 274         if (!f.exists()) return result;
 275         FileInputStream fin = new FileInputStream(passwordFileName);
 276         try {pws.load(fin);}finally{fin.close();}
 277         for (Enumeration en=pws.propertyNames();en.hasMoreElements();) {
 278             final String[] cred = new String[2];
 279             cred[0]=(String)en.nextElement();
 280             cred[1]=pws.getProperty(cred[0]);
 281             result.add(cred);
 282         }
 283         return result;
 284     }
 285 
 286 
 287     /**
 288      * Connect with the given url, using all given credentials in turn.
 289      * A null entry in the useCredentials arrays indicate a connection
 290      * where no credentials are used.
 291      * @param url JMXServiceURL of the server.
 292      * @param useCredentials An array of credentials (a credential
 293      *        is a two String array, so this is an array of arrays
 294      *        of strings:
 295      *           useCredentials[i][0]=subject
 296      *           useCredentials[i][1]=password
 297      *        if useCredentials[i] == null means no credentials.
 298      * @param expectConnectOk true if connection is expected to succeed
 299      *    Note: if expectConnectOk=false and the test fails to connect
 300      *          the number of failure is not incremented. Conversely,
 301      *          if expectConnectOk=false and the test does not fail to
 302      *          connect the number of failure is incremented.
 303      * @param expectReadOk true if communication (listMBeans) is expected
 304      *        to succeed.
 305      *    Note: if expectReadOk=false and the test fails to read MBeans
 306      *          the number of failure is not incremented. Conversely,
 307      *          if expectReadOk=false and the test does not fail to
 308      *          read MBeans the number of failure is incremented.
 309      * @return number of failure.
 310      **/
 311     public int connectAndRead(JMXServiceURL url,
 312                               Object[] useCredentials,
 313                               boolean  expectConnectOk,
 314                               boolean  expectReadOk)
 315         throws IOException {
 316 
 317         int errorCount = 0;
 318 
 319         for (int i=0 ; i<useCredentials.length ; i++) {
 320             final Map m = new HashMap();
 321             final String[] credentials = (String[])useCredentials[i];
 322             final String   crinfo;
 323             if (credentials != null) {
 324                 crinfo = "{"+credentials[0] + ", " + credentials[1] + "}";
 325                 m.put(PropertyNames.CREDENTIALS,credentials);
 326             } else {
 327                 crinfo="no credentials";
 328             }
 329             log.trace("testCommunication","using credentials: " + crinfo);
 330 
 331             final JMXConnector c;
 332             try {
 333                 c = JMXConnectorFactory.connect(url,m);
 334             } catch (IOException x ) {
 335                 if (expectConnectOk) {
 336                     final String err = "Connection failed for " + crinfo +
 337                         ": " + x;
 338                     System.out.println(err);
 339                     log.trace("testCommunication",err);
 340                     log.debug("testCommunication",x);
 341                     errorCount++;
 342                     continue;
 343                 } else {
 344                     System.out.println("Connection failed as expected for " +
 345                                        crinfo + ": " + x);
 346                     continue;
 347                 }
 348             } catch (RuntimeException x ) {
 349                 if (expectConnectOk) {
 350                     final String err = "Connection failed for " + crinfo +
 351                         ": " + x;
 352                     System.out.println(err);
 353                     log.trace("testCommunication",err);
 354                     log.debug("testCommunication",x);
 355                     errorCount++;
 356                     continue;
 357                 } else {
 358                     System.out.println("Connection failed as expected for " +
 359                                        crinfo + ": " + x);
 360                     continue;
 361                 }
 362             }
 363             try {
 364                 MBeanServerConnection conn =
 365                     c.getMBeanServerConnection();
 366                 if (log.isDebugOn()) {
 367                     log.debug("testCommunication","Connection is:" + conn);
 368                     log.debug("testCommunication","Server domain is: " +
 369                               conn.getDefaultDomain());
 370                 }
 371                 final ObjectName pattern =
 372                     new ObjectName("java.lang:type=Memory,*");
 373                 final int count = listMBeans(conn,pattern,null);
 374                 if (count == 0)
 375                     throw new Exception("Expected at least one matching "+
 376                                         "MBean for "+pattern);
 377                 if (expectReadOk) {
 378                     System.out.println("Communication succeeded " +
 379                                        "as expected for "+
 380                                        crinfo + ": found " + count
 381                                        + ((count<2)?"MBean":"MBeans"));
 382                 } else {
 383                     final String err = "Expected failure didn't occur for " +
 384                         crinfo;
 385                     System.out.println(err);
 386                     errorCount++;
 387                 }
 388             } catch (IOException x ) {
 389                 if (expectReadOk) {
 390                     final String err = "Communication failed with " + crinfo +
 391                         ": " + x;
 392                     System.out.println(err);
 393                     log.trace("testCommunication",err);
 394                     log.debug("testCommunication",x);
 395                     errorCount++;
 396                     continue;
 397                 } else {
 398                     System.out.println("Communication failed as expected for "+
 399                                        crinfo + ": " + x);
 400                     continue;
 401                 }
 402             } catch (RuntimeException x ) {
 403                 if (expectReadOk) {
 404                     final String err = "Communication failed with " + crinfo +
 405                         ": " + x;
 406                     System.out.println(err);
 407                     log.trace("testCommunication",err);
 408                     log.debug("testCommunication",x);
 409                     errorCount++;
 410                     continue;
 411                 } else {
 412                     System.out.println("Communication failed as expected for "+
 413                                        crinfo + ": " + x);
 414                 }
 415             } catch (Exception x) {
 416                 final String err = "Failed to read MBeans with " + crinfo +
 417                     ": " + x;
 418                 System.out.println(err);
 419                 log.trace("testCommunication",err);
 420                 log.debug("testCommunication",x);
 421                 errorCount++;
 422                 continue;
 423             } finally {
 424                 c.close();
 425             }
 426         }
 427         return errorCount;
 428     }
 429 
 430 
 431     private void setSslProperties(String clientEnabledCipherSuites) {
 432         final String defaultKeyStore =
 433             getDefaultStoreName(DefaultValues.KEYSTORE);
 434         final String defaultTrustStore =
 435             getDefaultStoreName(DefaultValues.TRUSTSTORE);
 436 
 437         final String keyStore =
 438             System.getProperty(PropertyNames.KEYSTORE, defaultKeyStore);
 439         System.setProperty(PropertyNames.KEYSTORE,keyStore);
 440         log.trace("setSslProperties",PropertyNames.KEYSTORE+"="+keyStore);
 441 
 442         final String password =
 443             System.getProperty(PropertyNames.KEYSTORE_PASSWD,
 444                                DefaultValues.KEYSTORE_PASSWD);
 445         System.setProperty(PropertyNames.KEYSTORE_PASSWD,password);
 446         log.trace("setSslProperties",
 447                   PropertyNames.KEYSTORE_PASSWD+"="+password);
 448 
 449         final String trustStore =
 450             System.getProperty(PropertyNames.TRUSTSTORE,
 451                                defaultTrustStore);
 452         System.setProperty(PropertyNames.TRUSTSTORE,trustStore);
 453         log.trace("setSslProperties",
 454                   PropertyNames.TRUSTSTORE+"="+trustStore);
 455 
 456         final String trustword =
 457             System.getProperty(PropertyNames.TRUSTSTORE_PASSWD,
 458                                DefaultValues.TRUSTSTORE_PASSWD);
 459         System.setProperty(PropertyNames.TRUSTSTORE_PASSWD,trustword);
 460         log.trace("setSslProperties",
 461                   PropertyNames.TRUSTSTORE_PASSWD+"="+trustword);
 462 
 463         if (clientEnabledCipherSuites != null) {
 464             System.setProperty("javax.rmi.ssl.client.enabledCipherSuites",
 465                     clientEnabledCipherSuites);
 466         } else {
 467             System.clearProperty("javax.rmi.ssl.client.enabledCipherSuites");
 468         }
 469     }
 470 
 471     private void checkSslConfiguration() {
 472         try {
 473             final String defaultConf =
 474                 getDefaultFileName(DefaultValues.CONFIG_FILE_NAME);
 475             final String confname =
 476                 System.getProperty(PropertyNames.CONFIG_FILE_NAME,defaultConf);
 477 
 478             final Properties props = new Properties();
 479             final File conf = new File(confname);
 480             if (conf.exists()) {
 481                 FileInputStream fin = new FileInputStream(conf);
 482                 try {props.load(fin);} finally {fin.close();}
 483             }
 484 
 485             // Do we use SSL?
 486             final String  useSslStr =
 487                 props.getProperty(PropertyNames.USE_SSL,
 488                                   DefaultValues.USE_SSL);
 489             final boolean useSsl =
 490                 Boolean.valueOf(useSslStr).booleanValue();
 491 
 492             log.debug("checkSslConfiguration",
 493                       PropertyNames.USE_SSL+"="+useSsl+
 494                       ": setting SSL");
 495             // Do we use SSL client authentication?
 496             final String  useSslClientAuthStr =
 497                 props.getProperty(PropertyNames.SSL_NEED_CLIENT_AUTH,
 498                                   DefaultValues.SSL_NEED_CLIENT_AUTH);
 499             final boolean useSslClientAuth =
 500                 Boolean.valueOf(useSslClientAuthStr).booleanValue();
 501 
 502             log.debug("checkSslConfiguration",
 503                       PropertyNames.SSL_NEED_CLIENT_AUTH+"="+useSslClientAuth);
 504 
 505             // Do we use customized SSL cipher suites?
 506             final String sslCipherSuites =
 507                 props.getProperty(PropertyNames.SSL_ENABLED_CIPHER_SUITES);
 508 
 509             log.debug("checkSslConfiguration",
 510                       PropertyNames.SSL_ENABLED_CIPHER_SUITES + "=" +
 511                       sslCipherSuites);
 512 
 513             // Do we use customized SSL protocols?
 514             final String sslProtocols =
 515                 props.getProperty(PropertyNames.SSL_ENABLED_PROTOCOLS);
 516 
 517             log.debug("checkSslConfiguration",
 518                       PropertyNames.SSL_ENABLED_PROTOCOLS + "=" +
 519                       sslProtocols);
 520 
 521             if (useSsl) {
 522                 setSslProperties(props.getProperty(
 523                         PropertyNames.SSL_CLIENT_ENABLED_CIPHER_SUITES));
 524             }
 525         } catch (Exception x) {
 526             System.out.println("Failed to setup SSL configuration: " + x);
 527             log.debug("checkSslConfiguration",x);
 528         }
 529     }
 530 
 531     /**
 532      * Tests the server bootstraped at the given URL.
 533      * Uses the system properties to determine which config file is used.
 534      * Loads the config file to determine which password file is used.
 535      * Loads the password file to find out wich credentials to use.
 536      * Also checks that unregistered user/passwords are not allowed to
 537      * connect when a password file is used.
 538      *
 539      * This method calls connectAndRead().
 540      **/
 541     public void testCommunication(JMXServiceURL url)
 542         throws IOException {
 543 
 544         final String defaultConf =
 545             getDefaultFileName(DefaultValues.CONFIG_FILE_NAME);
 546         final String confname =
 547             System.getProperty(PropertyNames.CONFIG_FILE_NAME,defaultConf);
 548 
 549         final Properties props = new Properties();
 550         final File conf = new File(confname);
 551         if (conf.exists()) {
 552             FileInputStream fin = new FileInputStream(conf);
 553             try {props.load(fin);} finally {fin.close();}
 554         }
 555 
 556         // Do we use authentication?
 557         final String  useAuthenticationStr =
 558             props.getProperty(PropertyNames.USE_AUTHENTICATION,
 559                               DefaultValues.USE_AUTHENTICATION);
 560         final boolean useAuthentication =
 561             Boolean.valueOf(useAuthenticationStr).booleanValue();
 562 
 563         // Get Password File
 564         final String defaultPasswordFileName = Utils.convertPath(
 565             getDefaultFileName(DefaultValues.PASSWORD_FILE_NAME));
 566         final String passwordFileName = Utils.convertPath(
 567             props.getProperty(PropertyNames.PASSWORD_FILE_NAME,
 568                               defaultPasswordFileName));
 569 
 570         // Get Access File
 571         final String defaultAccessFileName = Utils.convertPath(
 572             getDefaultFileName(DefaultValues.ACCESS_FILE_NAME));
 573         final String accessFileName = Utils.convertPath(
 574             props.getProperty(PropertyNames.ACCESS_FILE_NAME,
 575                               defaultAccessFileName));
 576 
 577         if (useAuthentication) {
 578             System.out.println("PasswordFileName: " + passwordFileName);
 579             System.out.println("accessFileName: " + accessFileName);
 580         }
 581 
 582         final Object[] allCredentials;
 583         final Object[] noCredentials = { null };
 584         if (useAuthentication) {
 585             final ArrayList l = readCredentials(passwordFileName);
 586             if (l.size() == 0) allCredentials = null;
 587             else allCredentials = l.toArray();
 588         } else allCredentials = noCredentials;
 589 
 590         int errorCount = 0;
 591         if (allCredentials!=null) {
 592             // Tests that the registered user/passwords are allowed to
 593             // connect & read
 594             //
 595             errorCount += connectAndRead(url,allCredentials,true,true);
 596         } else {
 597             // Tests that no one is allowed
 598             // connect & read
 599             //
 600             final String[][] someCredentials = {
 601                 null,
 602                 { "modify", "R&D" },
 603                 { "measure", "QED" }
 604             };
 605             errorCount += connectAndRead(url,someCredentials,false,false);
 606         }
 607 
 608         if (useAuthentication && allCredentials != noCredentials) {
 609             // Tests that the registered user/passwords are not allowed to
 610             // connect & read
 611             //
 612             final String[][] badCredentials = {
 613                 { "bad.user", "R&D" },
 614                 { "measure", "bad.password" }
 615             };
 616             errorCount += connectAndRead(url,badCredentials,false,false);
 617         }
 618         if (errorCount > 0) {
 619             final String err = "Test " + confname + " failed with " +
 620                 errorCount + " error(s)";
 621             log.debug("testCommunication",err);
 622             throw new RuntimeException(err);
 623         }
 624     }
 625 
 626 
 627     /**
 628      * Test the configuration indicated by `file'.
 629      * Sets the appropriate System properties for config file and
 630      * port and then calls ConnectorBootstrap.initialize().
 631      * eventually cleans up by calling ConnectorBootstrap.terminate().
 632      * @return null if the test succeeds, an error message otherwise.
 633      **/
 634     private String testConfiguration(File file) throws IOException, InterruptedException {
 635 
 636         for (int i = 0; i < MAX_GET_FREE_PORT_TRIES; i++) {
 637             try {
 638                 int port = jdk.testlibrary.Utils.getFreePort();
 639                 final String path;
 640                 try {
 641                     path=(file==null)?null:file.getCanonicalPath();
 642                 } catch(IOException x) {
 643                     final String err = "Failed to test configuration " + file +
 644                         ": " + x;
 645                     log.trace("testConfiguration",err);
 646                     log.debug("testConfiguration",x);
 647                     return err;
 648                 }
 649                 final String config = (path==null)?"Default config file":path;
 650 
 651                 System.out.println("***");
 652                 System.out.println("*** Testing configuration (port=" + port + "): "
 653                                    + path);
 654                 System.out.println("***");
 655 
 656                 System.setProperty("com.sun.management.jmxremote.port",
 657                                     Integer.toString(port));
 658                 if (path != null)
 659                     System.setProperty("com.sun.management.config.file", path);
 660                 else
 661                     System.getProperties().remove("com.sun.management.config.file");
 662 
 663                 log.trace("testConfiguration","com.sun.management.jmxremote.port="+port);
 664                 if (path != null && log.isDebugOn())
 665                     log.trace("testConfiguration",
 666                               "com.sun.management.config.file="+path);
 667 
 668                 checkSslConfiguration();
 669 
 670                 final JMXConnectorServer cs;
 671                 try {
 672                     cs = ConnectorBootstrap.initialize();
 673                 } catch (AgentConfigurationError x) {
 674                     if (x.getCause() instanceof ExportException) {
 675                         if (x.getCause().getCause() instanceof BindException) {
 676                             throw (BindException)x.getCause().getCause();
 677                         }
 678                     }
 679                     final String err = "Failed to initialize connector:" +
 680                         "\n\tcom.sun.management.jmxremote.port=" + port +
 681                         ((path!=null)?"\n\tcom.sun.management.config.file="+path:
 682                          "\n\t"+config) +
 683                         "\n\tError is: " + x;
 684                     log.trace("testConfiguration",err);
 685                     log.debug("testConfiguration",x);
 686                     return err;
 687                 } catch (Exception x) {
 688                     log.debug("testConfiguration",x);
 689                     return x.toString();
 690                 }
 691 
 692                 try {
 693                     JMXServiceURL url =
 694                         new JMXServiceURL("rmi",null,0,"/jndi/rmi://localhost:"+
 695                                           port+"/jmxrmi");
 696 
 697                     try {
 698                         testCommunication(url);
 699                     } catch (Exception x) {
 700                         final String err = "Failed to connect to agent {url="+url+
 701                             "}: " + x;
 702                         log.trace("testConfiguration",err);
 703                         log.debug("testConfiguration",x);
 704                         return err;
 705                     }
 706                 } catch (Exception x) {
 707                     final String err = "Failed to test configuration "+config+
 708                         ": "+x;
 709                     log.trace("testConfiguration",err);
 710                     log.debug("testConfiguration",x);
 711                     return err;
 712                 } finally {
 713                     try {
 714                         cs.stop();
 715                     } catch (Exception x) {
 716                         final String err = "Failed to terminate: "+x;
 717                         log.trace("testConfiguration",err);
 718                         log.debug("testConfiguration",x);
 719                     }
 720                 }
 721                 System.out.println("Configuration " + config + " successfully tested");
 722                 return null;
 723             } catch(BindException ex) {
 724             }
 725         }
 726         System.err.println("Cannot find a free port after " + MAX_GET_FREE_PORT_TRIES + " tries");
 727         return "Failed: cannot find a free port after " + MAX_GET_FREE_PORT_TRIES + " tries";
 728     }
 729 
 730     /**
 731      * Test a configuration file which should make the bootstrap fail.
 732      * The test is assumed to have succeeded if the bootstrap fails.
 733      * @return null if the test succeeds, an error message otherwise.
 734      **/
 735     private String testConfigurationKo(File conf) throws InterruptedException, IOException {
 736         String errStr = null;
 737         errStr = testConfiguration(conf);
 738         if (errStr == null) {
 739             return "Configuration " +
 740                 conf + " should have failed!";
 741         }
 742         System.out.println("Configuration " +
 743                            conf + " failed as expected");
 744         log.debug("runko", "Error was: " + errStr);
 745         return null;
 746     }
 747 
 748     /**
 749      * Test a configuration file. Determines whether the bootstrap
 750      * should succeed or fail depending on the file name:
 751      *     *ok.properties: bootstrap should succeed.
 752      *     *ko.properties: bootstrap or connection should fail.
 753      * @return null if the test succeeds, an error message otherwise.
 754      **/
 755     private String testConfigurationFile(String fileName) throws InterruptedException, IOException {
 756         File file = new File(fileName);
 757 
 758         if (fileName.endsWith("ok.properties")) {
 759             String errStr = null;
 760             errStr = testConfiguration(file);
 761             return errStr;
 762         }
 763         if (fileName.endsWith("ko.properties")) {
 764             return testConfigurationKo(file);
 765         }
 766         return fileName +
 767             ": test file suffix must be one of [ko|ok].properties";
 768     }
 769 
 770     /**
 771      * Find all *ko.property files and test them.
 772      * (see findConfigurationFilesKo() and testConfigurationKo())
 773      * @throws RuntimeException if the test fails.
 774      **/
 775     public void runko() throws InterruptedException, IOException {
 776         final File[] conf = findConfigurationFilesKo();
 777         if ((conf == null)||(conf.length == 0))
 778             throw new RuntimeException("No configuration found");
 779 
 780         String errStr;
 781         for (int i=0;i<conf.length;i++) {
 782             errStr = testConfigurationKo(conf[i]);
 783             if (errStr != null) {
 784                 throw new RuntimeException(errStr);
 785             }
 786         }
 787 
 788     }
 789 
 790     /**
 791      * Find all *ok.property files and test them.
 792      * (see findConfigurationFilesOk() and testConfiguration())
 793      * @throws RuntimeException if the test fails.
 794      **/
 795     public void runok() throws InterruptedException, IOException {
 796         final File[] conf = findConfigurationFilesOk();
 797         if ((conf == null)||(conf.length == 0))
 798             throw new RuntimeException("No configuration found");
 799 
 800         String errStr = null;
 801         for (int i=0;i<conf.length;i++) {
 802             errStr = testConfiguration(conf[i]);
 803             if (errStr != null) {
 804                 throw new RuntimeException(errStr);
 805             }
 806         }
 807 
 808         // FIXME: No jmxremote.password is not installed in JRE by default.
 809         // - disable the following test case.
 810         //
 811         // Test default config
 812         //
 813         // errStr = testConfiguration(null,port+testPort++);
 814         // if (errStr != null) {
 815         //    throw new RuntimeException(errStr);
 816         // }
 817     }
 818 
 819     /**
 820      * Finds all configuration files (*ok.properties and *ko.properties)
 821      * and tests them.
 822      * (see runko() and runok()).
 823      * @throws RuntimeException if the test fails.
 824      **/
 825     public void run() throws InterruptedException, IOException {
 826         runok();
 827         runko();
 828     }
 829 
 830     /**
 831      * Tests the specified configuration files.
 832      * If args[] is not empty, each element in args[] is expected to be
 833      * a filename ending either by ok.properties or ko.properties.
 834      * Otherwise, the configuration files will be automatically determined
 835      * by looking at all *.properties files located in the directory
 836      * indicated by the System property "test.src".
 837      * @throws RuntimeException if the test fails.
 838      **/
 839     public void run(String args[]) throws InterruptedException, IOException {
 840         if (args.length == 0) {
 841             run() ; return;
 842         }
 843         for (int i=0; i<args.length; i++) {
 844             final String errStr =testConfigurationFile(args[i]);
 845             if (errStr != null) {
 846                 throw new RuntimeException(errStr);
 847             }
 848         }
 849     }
 850 
 851     /**
 852      * Calls run(args[]).
 853      * exit(1) if the test fails.
 854      **/
 855     public static void main(String args[]) throws Exception {
 856         Security.setProperty("jdk.tls.disabledAlgorithms", "");
 857 
 858         try {
 859             MAX_GET_FREE_PORT_TRIES = Integer.parseInt(System.getProperty("test.getfreeport.max.tries", "10"));
 860         } catch (NumberFormatException ex) {
 861         }
 862 
 863         RmiBootstrapTest manager = new RmiBootstrapTest();
 864         try {
 865             manager.run(args);
 866         } catch (RuntimeException r) {
 867             System.out.println("Test Failed: "+ r.getMessage());
 868             System.exit(1);
 869         } catch (Throwable t) {
 870             System.out.println("Test Failed: "+ t);
 871             t.printStackTrace();
 872             System.exit(2);
 873         }
 874         System.out.println("**** Test  RmiBootstrap Passed ****");
 875     }
 876 
 877 }