1 /*
   2  * Copyright (c) 2003, 2018, 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.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 package jdk.internal.agent;
  26 
  27 import java.io.BufferedInputStream;
  28 import java.io.File;
  29 import java.io.FileInputStream;
  30 import java.io.FileNotFoundException;
  31 import java.io.IOException;
  32 import java.io.InputStream;
  33 import java.lang.management.ManagementFactory;
  34 import java.lang.reflect.Method;
  35 import java.net.InetAddress;
  36 import java.net.MalformedURLException;
  37 import java.net.UnknownHostException;
  38 import java.security.AccessController;
  39 import java.security.PrivilegedAction;
  40 import java.text.MessageFormat;
  41 import java.util.HashMap;
  42 import java.util.Map;
  43 import java.util.MissingResourceException;
  44 import java.util.Properties;
  45 import java.util.ResourceBundle;
  46 import java.util.ServiceLoader;
  47 import java.util.function.Function;
  48 import java.util.function.Predicate;
  49 
  50 import javax.management.remote.JMXConnectorServer;
  51 import javax.management.remote.JMXServiceURL;
  52 
  53 import static jdk.internal.agent.AgentConfigurationError.*;
  54 import jdk.internal.agent.spi.AgentProvider;
  55 import jdk.internal.vm.VMSupport;
  56 import sun.management.jdp.JdpController;
  57 import sun.management.jdp.JdpException;
  58 import sun.management.jmxremote.ConnectorBootstrap;
  59 
  60 /**
  61  * This class provides the methods to start the management agent.
  62  * 1. {@link #startAgent} method is invoked by the VM if {@code -Dcom.sun.management.*} is set
  63  * 2. {@link #startLocalManagementAgent} or {@link #startRemoteManagementAgent}
  64  *    is invoked to start the management agent after the VM starts
  65  *    via jcmd ManagementAgent.start and start_local command.
  66  */
  67 public class Agent {
  68     /**
  69      * Agent status collector strategy class
  70      */
  71     private static abstract class StatusCollector {
  72         protected static final Map<String, String> DEFAULT_PROPS = new HashMap<>();
  73 
  74         static {
  75             DEFAULT_PROPS.put(ConnectorBootstrap.PropertyNames.PORT,
  76                               ConnectorBootstrap.DefaultValues.PORT);
  77             DEFAULT_PROPS.put(ConnectorBootstrap.PropertyNames.USE_LOCAL_ONLY,
  78                               ConnectorBootstrap.DefaultValues.USE_LOCAL_ONLY);
  79             DEFAULT_PROPS.put(ConnectorBootstrap.PropertyNames.USE_AUTHENTICATION,
  80                               ConnectorBootstrap.DefaultValues.USE_AUTHENTICATION);
  81             DEFAULT_PROPS.put(ConnectorBootstrap.PropertyNames.USE_SSL,
  82                               ConnectorBootstrap.DefaultValues.USE_SSL);
  83             DEFAULT_PROPS.put(ConnectorBootstrap.PropertyNames.USE_REGISTRY_SSL,
  84                               ConnectorBootstrap.DefaultValues.USE_REGISTRY_SSL);
  85             DEFAULT_PROPS.put(ConnectorBootstrap.PropertyNames.SSL_NEED_CLIENT_AUTH,
  86                               ConnectorBootstrap.DefaultValues.SSL_NEED_CLIENT_AUTH);
  87             DEFAULT_PROPS.put(ConnectorBootstrap.PropertyNames.CONFIG_FILE_NAME,
  88                               ConnectorBootstrap.DefaultValues.CONFIG_FILE_NAME);
  89             DEFAULT_PROPS.put(ConnectorBootstrap.PropertyNames.PASSWORD_FILE_NAME,
  90                               ConnectorBootstrap.DefaultValues.PASSWORD_FILE_NAME);
  91             DEFAULT_PROPS.put(ConnectorBootstrap.PropertyNames.ACCESS_FILE_NAME,
  92                               ConnectorBootstrap.DefaultValues.ACCESS_FILE_NAME);
  93 
  94         }
  95 
  96         final protected StringBuilder sb = new StringBuilder();
  97         final public String collect() {
  98             Properties agentProps = VMSupport.getAgentProperties();
  99             String localConnAddr = (String)agentProps.get(LOCAL_CONNECTOR_ADDRESS_PROP);
 100             if (localConnAddr != null || jmxServer != null) {
 101                 addAgentStatus(true);
 102                 appendConnections(localConnAddr);
 103             } else {
 104                 addAgentStatus(false);
 105             }
 106             return sb.toString();
 107         }
 108 
 109         private void appendConnections(String localConnAddr) {
 110             appendConnectionsHeader();
 111             if (localConnAddr != null) {
 112                 try {
 113                     JMXServiceURL u = new JMXServiceURL(localConnAddr);
 114                     addConnection(false, u);
 115                 } catch (MalformedURLException e) {
 116                     // will never happen
 117                 }
 118 
 119             }
 120             if (jmxServer != null) {
 121                 addConnection(true, jmxServer.getAddress());
 122             }
 123             appendConnectionsFooter();
 124         }
 125 
 126         private void addConnection(boolean remote, JMXServiceURL u) {
 127             appendConnectionHeader(remote);
 128             addConnectionDetails(u);
 129             addConfigProperties();
 130             appendConnectionFooter(remote);
 131         }
 132 
 133         private void addConfigProperties() {
 134             appendConfigPropsHeader();
 135 
 136             Properties remoteProps = configProps != null ?
 137                                         configProps : getManagementProperties();
 138             Map<Object, Object> props = new HashMap<>(DEFAULT_PROPS);
 139 
 140             if (remoteProps == null) {
 141                 // local connector only
 142                 String loc_only = System.getProperty(
 143                     ConnectorBootstrap.PropertyNames.USE_LOCAL_ONLY
 144                 );
 145 
 146                 if (loc_only != null &&
 147                     !ConnectorBootstrap.DefaultValues.USE_LOCAL_ONLY.equals(loc_only)) {
 148                     props.put(
 149                         ConnectorBootstrap.PropertyNames.USE_LOCAL_ONLY,
 150                         loc_only
 151                     );
 152                 }
 153             } else {
 154                 props.putAll(remoteProps);
 155             }
 156 
 157             props.entrySet().stream()
 158                 .filter(preprocess(Map.Entry::getKey, StatusCollector::isManagementProp))
 159                 .forEach(this::addConfigProp);
 160 
 161             appendConfigPropsFooter();
 162         }
 163 
 164         private static boolean isManagementProp(Object pName) {
 165             return pName != null && pName.toString().startsWith("com.sun.management.");
 166         }
 167 
 168         private static <T, V> Predicate<T> preprocess(Function<T, V> f, Predicate<V> p) {
 169             return (T t) -> p.test(f.apply(t));
 170         }
 171 
 172         abstract protected void addAgentStatus(boolean enabled);
 173         abstract protected void appendConnectionsHeader();
 174         abstract protected void appendConnectionsFooter();
 175         abstract protected void addConnectionDetails(JMXServiceURL u);
 176         abstract protected void appendConnectionHeader(boolean remote);
 177         abstract protected void appendConnectionFooter(boolean remote);
 178         abstract protected void appendConfigPropsHeader();
 179         abstract protected void appendConfigPropsFooter();
 180         abstract protected void addConfigProp(Map.Entry<?, ?> prop);
 181     }
 182 
 183     /**
 184      * Free-text status collector strategy implementation
 185      */
 186     final private static class TextStatusCollector extends StatusCollector {
 187 
 188         @Override
 189         protected void addAgentStatus(boolean enabled) {
 190             sb.append("Agent: ").append(enabled ? "enabled" : "disabled").append('\n');
 191         }
 192 
 193         @Override
 194         protected void appendConnectionsHeader() {
 195             sb.append('\n');
 196         }
 197 
 198         @Override
 199         protected void addConnectionDetails(JMXServiceURL u) {
 200             sb.append("Protocol       : ").append(u.getProtocol()).append('\n')
 201               .append("Host           : ").append(u.getHost()).append('\n')
 202               .append("URL            : ").append(u).append('\n');
 203         }
 204 
 205         @Override
 206         protected void appendConnectionHeader(boolean remote) {
 207             sb.append("Connection Type: ").append(remote ? "remote" : "local").append('\n');
 208         }
 209 
 210         @Override
 211         protected void appendConfigPropsHeader() {
 212             sb.append("Properties     :\n");
 213         }
 214 
 215         @Override
 216         protected void addConfigProp(Map.Entry<?, ?> prop) {
 217             sb.append("  ").append(prop.getKey()).append(" = ")
 218               .append(prop.getValue());
 219             Object defVal = DEFAULT_PROPS.get(prop.getKey());
 220             if (defVal != null && defVal.equals(prop.getValue())) {
 221                 sb.append(" [default]");
 222             }
 223             sb.append("\n");
 224         }
 225 
 226         @Override
 227         protected void appendConnectionsFooter() {}
 228 
 229         @Override
 230         protected void appendConnectionFooter(boolean remote) {
 231             sb.append('\n');
 232         }
 233 
 234         @Override
 235         protected void appendConfigPropsFooter() {}
 236     }
 237 
 238     // management properties
 239 
 240     private static Properties mgmtProps;
 241     private static ResourceBundle messageRB;
 242     private static final String CONFIG_FILE =
 243             "com.sun.management.config.file";
 244     private static final String JMXREMOTE =
 245             "com.sun.management.jmxremote";
 246     private static final String JMXREMOTE_PORT =
 247             "com.sun.management.jmxremote.port";
 248     private static final String RMI_PORT =
 249             "com.sun.management.jmxremote.rmi.port";
 250     private static final String ENABLE_THREAD_CONTENTION_MONITORING =
 251             "com.sun.management.enableThreadContentionMonitoring";
 252     private static final String LOCAL_CONNECTOR_ADDRESS_PROP =
 253             "com.sun.management.jmxremote.localConnectorAddress";
 254 
 255     private static final String JDP_DEFAULT_ADDRESS = "224.0.23.178";
 256     private static final int JDP_DEFAULT_PORT = 7095;
 257 
 258     // The only active agent allowed
 259     private static JMXConnectorServer jmxServer = null;
 260     // The properties used to configure the server
 261     private static Properties configProps = null;
 262 
 263     // Parse string com.sun.management.prop=xxx,com.sun.management.prop=yyyy
 264     // and return property set if args is null or empty
 265     // return empty property set
 266     private static Properties parseString(String args) {
 267         Properties argProps = new Properties();
 268         if (args != null && !args.trim().isEmpty()) {
 269             for (String option : args.split(",")) {
 270                 String s[] = option.split("=", 2);
 271                 String name = s[0].trim();
 272                 String value = (s.length > 1) ? s[1].trim() : "";
 273 
 274                 if (!name.startsWith("com.sun.management.")) {
 275                     error(INVALID_OPTION, name);
 276                 }
 277 
 278                 argProps.setProperty(name, value);
 279             }
 280         }
 281 
 282         return argProps;
 283     }
 284 
 285     // invoked by -javaagent or -Dcom.sun.management.agent.class
 286     public static void premain(String args) throws Exception {
 287         agentmain(args);
 288     }
 289 
 290     // invoked by attach mechanism
 291     public static void agentmain(String args) throws Exception {
 292         if (args == null || args.length() == 0) {
 293             args = JMXREMOTE;           // default to local management
 294         }
 295 
 296         Properties arg_props = parseString(args);
 297 
 298         // Read properties from the config file
 299         Properties config_props = new Properties();
 300         String fname = arg_props.getProperty(CONFIG_FILE);
 301         readConfiguration(fname, config_props);
 302 
 303         // Arguments override config file
 304         config_props.putAll(arg_props);
 305         startAgent(config_props);
 306     }
 307 
 308     /*
 309      * Starts the local management agent.
 310      * This method is invoked by either startAgent method or
 311      * by the VM directly via jcmd ManagementAgent.start_local command.
 312      */
 313     private static synchronized void startLocalManagementAgent() {
 314         Properties agentProps = VMSupport.getAgentProperties();
 315 
 316         // start local connector if not started
 317         if (agentProps.get(LOCAL_CONNECTOR_ADDRESS_PROP) == null) {
 318             JMXConnectorServer cs = ConnectorBootstrap.startLocalConnectorServer();
 319             String address = cs.getAddress().toString();
 320             // Add the local connector address to the agent properties
 321             agentProps.put(LOCAL_CONNECTOR_ADDRESS_PROP, address);
 322 
 323             try {
 324                 // export the address to the instrumentation buffer
 325                 ConnectorAddressLink.export(address);
 326             } catch (Exception x) {
 327                 // Connector server started but unable to export address
 328                 // to instrumentation buffer - non-fatal error.
 329                 warning(EXPORT_ADDRESS_FAILED, x.getMessage());
 330             }
 331         }
 332     }
 333 
 334     /*
 335      * This method is invoked by the VM to start the remote management agent
 336      * via jcmd ManagementAgent.start command.
 337      */
 338     private static synchronized void startRemoteManagementAgent(String args) throws Exception {
 339         if (jmxServer != null) {
 340             throw new RuntimeException(getText(INVALID_STATE, "Agent already started"));
 341         }
 342 
 343         try {
 344             Properties argProps = parseString(args);
 345             configProps = new Properties();
 346 
 347             // Load the management properties from the config file
 348             // if config file is not specified readConfiguration implicitly
 349             // reads <java.home>/conf/management/management.properties
 350 
 351             String fname = System.getProperty(CONFIG_FILE);
 352             readConfiguration(fname, configProps);
 353 
 354             // management properties can be overridden by system properties
 355             // which take precedence
 356             Properties sysProps = System.getProperties();
 357             synchronized (sysProps) {
 358                 configProps.putAll(sysProps);
 359             }
 360 
 361             // if user specifies config file into command line for either
 362             // jcmd utilities or attach command it overrides properties set in
 363             // command line at the time of VM start
 364             String fnameUser = argProps.getProperty(CONFIG_FILE);
 365             if (fnameUser != null) {
 366                 readConfiguration(fnameUser, configProps);
 367             }
 368 
 369             // arguments specified in command line of jcmd utilities
 370             // override both system properties and one set by config file
 371             // specified in jcmd command line
 372             configProps.putAll(argProps);
 373 
 374             // jcmd doesn't allow to change ThreadContentionMonitoring, but user
 375             // can specify this property inside config file, so enable optional
 376             // monitoring functionality if this property is set
 377             final String enableThreadContentionMonitoring =
 378                     configProps.getProperty(ENABLE_THREAD_CONTENTION_MONITORING);
 379 
 380             if (enableThreadContentionMonitoring != null) {
 381                 ManagementFactory.getThreadMXBean().
 382                         setThreadContentionMonitoringEnabled(true);
 383             }
 384 
 385             String jmxremotePort = configProps.getProperty(JMXREMOTE_PORT);
 386             if (jmxremotePort != null) {
 387                 jmxServer = ConnectorBootstrap.
 388                         startRemoteConnectorServer(jmxremotePort, configProps);
 389 
 390                 startDiscoveryService(configProps);
 391             } else {
 392                 throw new AgentConfigurationError(INVALID_JMXREMOTE_PORT, "No port specified");
 393             }
 394         } catch (JdpException e) {
 395             error(e);
 396         } catch (AgentConfigurationError err) {
 397             error(err);
 398         }
 399     }
 400 
 401     private static synchronized void stopRemoteManagementAgent() throws Exception {
 402 
 403         JdpController.stopDiscoveryService();
 404 
 405         if (jmxServer != null) {
 406             ConnectorBootstrap.unexportRegistry();
 407             ConnectorAddressLink.unexportRemote();
 408 
 409             // Attempt to stop already stopped agent
 410             // Don't cause any errors.
 411             jmxServer.stop();
 412             jmxServer = null;
 413             configProps = null;
 414         }
 415     }
 416 
 417     private static synchronized String getManagementAgentStatus() throws Exception {
 418         return new TextStatusCollector().collect();
 419     }
 420 
 421     private static void startAgent(Properties props) throws Exception {
 422         String jmxremote = props.getProperty(JMXREMOTE);
 423         String jmxremotePort = props.getProperty(JMXREMOTE_PORT);
 424 
 425         // Enable optional monitoring functionality if requested
 426         final String enableThreadContentionMonitoring =
 427                 props.getProperty(ENABLE_THREAD_CONTENTION_MONITORING);
 428         if (enableThreadContentionMonitoring != null) {
 429             ManagementFactory.getThreadMXBean().
 430                     setThreadContentionMonitoringEnabled(true);
 431         }
 432 
 433         try {
 434 
 435             /*
 436              * If the jmxremote.port property is set then we start the
 437              * RMIConnectorServer for remote M&M.
 438              *
 439              * If the jmxremote or jmxremote.port properties are set then
 440              * we start a RMIConnectorServer for local M&M. The address
 441              * of this "local" server is exported as a counter to the jstat
 442              * instrumentation buffer.
 443              */
 444             if (jmxremote != null || jmxremotePort != null) {
 445                 if (jmxremotePort != null) {
 446                     jmxServer = ConnectorBootstrap.
 447                             startRemoteConnectorServer(jmxremotePort, props);
 448                     startDiscoveryService(props);
 449                 }
 450                 startLocalManagementAgent();
 451             }
 452 
 453         } catch (AgentConfigurationError e) {
 454             error(e);
 455         } catch (Exception e) {
 456             error(e);
 457         }
 458     }
 459 
 460     private static void startDiscoveryService(Properties props)
 461             throws IOException, JdpException {
 462         // Start discovery service if requested
 463         String discoveryPort = props.getProperty("com.sun.management.jdp.port");
 464         String discoveryAddress = props.getProperty("com.sun.management.jdp.address");
 465         String discoveryShouldStart = props.getProperty("com.sun.management.jmxremote.autodiscovery");
 466 
 467         // Decide whether we should start autodicovery service.
 468         // To start autodiscovery following conditions should be met:
 469         // autodiscovery==true OR (autodicovery==null AND jdp.port != NULL)
 470 
 471         boolean shouldStart = false;
 472         if (discoveryShouldStart == null){
 473             shouldStart = (discoveryPort != null);
 474         }
 475         else{
 476             try{
 477                shouldStart = Boolean.parseBoolean(discoveryShouldStart);
 478             } catch (NumberFormatException e) {
 479                 throw new AgentConfigurationError(AGENT_EXCEPTION, "Couldn't parse autodiscovery argument");
 480             }
 481         }
 482 
 483         if (shouldStart) {
 484             // port and address are required arguments and have no default values
 485             InetAddress address;
 486             try {
 487                 address = (discoveryAddress == null) ?
 488                         InetAddress.getByName(JDP_DEFAULT_ADDRESS) : InetAddress.getByName(discoveryAddress);
 489             } catch (UnknownHostException e) {
 490                 throw new AgentConfigurationError(AGENT_EXCEPTION, e, "Unable to broadcast to requested address");
 491             }
 492 
 493             int port = JDP_DEFAULT_PORT;
 494             if (discoveryPort != null) {
 495                try {
 496                   port = Integer.parseInt(discoveryPort);
 497                } catch (NumberFormatException e) {
 498                  throw new AgentConfigurationError(AGENT_EXCEPTION, "Couldn't parse JDP port argument");
 499                }
 500             }
 501 
 502             // Get service URL to broadcast it
 503             Map<String,String> remoteProps = ConnectorAddressLink.importRemoteFrom(0);
 504             String jmxUrlStr = remoteProps.get("sun.management.JMXConnectorServer.0.remoteAddress");
 505 
 506             String instanceName = props.getProperty("com.sun.management.jdp.name");
 507 
 508             JdpController.startDiscoveryService(address, port, instanceName, jmxUrlStr);
 509         }
 510     }
 511 
 512     public static Properties loadManagementProperties() {
 513         Properties props = new Properties();
 514 
 515         // Load the management properties from the config file
 516 
 517         String fname = System.getProperty(CONFIG_FILE);
 518         readConfiguration(fname, props);
 519 
 520         // management properties can be overridden by system properties
 521         // which take precedence
 522         Properties sysProps = System.getProperties();
 523         synchronized (sysProps) {
 524             props.putAll(sysProps);
 525         }
 526 
 527         return props;
 528     }
 529 
 530     public static synchronized Properties getManagementProperties() {
 531         if (mgmtProps == null) {
 532             String configFile = System.getProperty(CONFIG_FILE);
 533             String jmxremote = System.getProperty(JMXREMOTE);
 534             String jmxremotePort = System.getProperty(JMXREMOTE_PORT);
 535 
 536             if (configFile == null && jmxremote == null && jmxremotePort == null) {
 537                 // return if out-of-the-management option is not specified
 538                 return null;
 539             }
 540             mgmtProps = loadManagementProperties();
 541         }
 542         return mgmtProps;
 543     }
 544 
 545     // read config file and initialize the properties
 546     private static void readConfiguration(String fname, Properties p) {
 547         if (fname == null) {
 548             String home = System.getProperty("java.home");
 549             if (home == null) {
 550                 throw new Error("Can't find java.home ??");
 551             }
 552             StringBuilder defaultFileName = new StringBuilder(home);
 553             defaultFileName.append(File.separator).append("conf");
 554             defaultFileName.append(File.separator).append("management");
 555             defaultFileName.append(File.separator).append("management.properties");
 556             // Set file name
 557             fname = defaultFileName.toString();
 558         }
 559         final File configFile = new File(fname);
 560         if (!configFile.exists()) {
 561             error(CONFIG_FILE_NOT_FOUND, fname);
 562         }
 563 
 564         InputStream in = null;
 565         try {
 566             in = new FileInputStream(configFile);
 567             BufferedInputStream bin = new BufferedInputStream(in);
 568             p.load(bin);
 569         } catch (FileNotFoundException e) {
 570             error(CONFIG_FILE_OPEN_FAILED, e.getMessage());
 571         } catch (IOException e) {
 572             error(CONFIG_FILE_OPEN_FAILED, e.getMessage());
 573         } catch (SecurityException e) {
 574             error(CONFIG_FILE_ACCESS_DENIED, fname);
 575         } finally {
 576             if (in != null) {
 577                 try {
 578                     in.close();
 579                 } catch (IOException e) {
 580                     error(CONFIG_FILE_CLOSE_FAILED, fname);
 581                 }
 582             }
 583         }
 584     }
 585 
 586     /**
 587      * This method is invoked by the VM to start the management agent
 588      * when -Dcom.sun.management.* is set during startup.
 589      */
 590     public static void startAgent() throws Exception {
 591         String prop = System.getProperty("com.sun.management.agent.class");
 592 
 593         // -Dcom.sun.management.agent.class not set so read management
 594         // properties and start agent
 595         if (prop == null) {
 596             // initialize management properties
 597             Properties props = getManagementProperties();
 598             if (props != null) {
 599                 startAgent(props);
 600             }
 601             return;
 602         }
 603 
 604         // -Dcom.sun.management.agent.class=<agent classname>:<agent args>
 605         String[] values = prop.split(":");
 606         if (values.length < 1 || values.length > 2) {
 607             error(AGENT_CLASS_INVALID, "\"" + prop + "\"");
 608         }
 609         String cname = values[0];
 610         String args = (values.length == 2 ? values[1] : null);
 611 
 612         if (cname == null || cname.length() == 0) {
 613             error(AGENT_CLASS_INVALID, "\"" + prop + "\"");
 614         }
 615 
 616         if (cname != null) {
 617             try {
 618                 // Instantiate the named class.
 619                 // invoke the premain(String args) method
 620                 Class<?> clz = ClassLoader.getSystemClassLoader().loadClass(cname);
 621                 Method premain = clz.getMethod("premain",
 622                         new Class<?>[]{String.class});
 623                 premain.invoke(null, /* static */
 624                         new Object[]{args});
 625             } catch (ClassNotFoundException ex) {
 626                 error(AGENT_CLASS_NOT_FOUND, "\"" + cname + "\"");
 627             } catch (NoSuchMethodException ex) {
 628                 error(AGENT_CLASS_PREMAIN_NOT_FOUND, "\"" + cname + "\"");
 629             } catch (SecurityException ex) {
 630                 error(AGENT_CLASS_ACCESS_DENIED);
 631             } catch (Exception ex) {
 632                 String msg = (ex.getCause() == null
 633                         ? ex.getMessage()
 634                         : ex.getCause().getMessage());
 635                 error(AGENT_CLASS_FAILED, msg);
 636             }
 637         }
 638     }
 639 
 640     public static void error(String key) {
 641         String keyText = getText(key);
 642         System.err.print(getText("agent.err.error") + ": " + keyText);
 643         throw new RuntimeException(keyText);
 644     }
 645 
 646     public static void error(String key, String message) {
 647         String keyText = getText(key);
 648         System.err.print(getText("agent.err.error") + ": " + keyText);
 649         System.err.println(": " + message);
 650         throw new RuntimeException(keyText + ": " + message);
 651     }
 652 
 653     public static void error(Exception e) {
 654         e.printStackTrace();
 655         System.err.println(getText(AGENT_EXCEPTION) + ": " + e.toString());
 656         throw new RuntimeException(e);
 657     }
 658 
 659     public static void error(AgentConfigurationError e) {
 660         String keyText = getText(e.getError());
 661         String[] params = e.getParams();
 662 
 663         System.err.print(getText("agent.err.error") + ": " + keyText);
 664 
 665         if (params != null && params.length != 0) {
 666            StringBuffer message = new StringBuffer(params[0]);
 667            for (int i = 1; i < params.length; i++) {
 668                message.append(" " + params[i]);
 669            }
 670            System.err.println(": " + message);
 671         }
 672         e.printStackTrace();
 673         throw new RuntimeException(e);
 674     }
 675 
 676     public static void warning(String key, String message) {
 677         System.err.print(getText("agent.err.warning") + ": " + getText(key));
 678         System.err.println(": " + message);
 679     }
 680 
 681     private static void initResource() {
 682         try {
 683             messageRB =
 684                 ResourceBundle.getBundle("jdk.internal.agent.resources.agent");
 685         } catch (MissingResourceException e) {
 686             throw new Error("Fatal: Resource for management agent is missing");
 687         }
 688     }
 689 
 690     public static String getText(String key) {
 691         if (messageRB == null) {
 692             initResource();
 693         }
 694         try {
 695             return messageRB.getString(key);
 696         } catch (MissingResourceException e) {
 697             return "Missing management agent resource bundle: key = \"" + key + "\"";
 698         }
 699     }
 700 
 701     public static String getText(String key, String... args) {
 702         if (messageRB == null) {
 703             initResource();
 704         }
 705         String format = messageRB.getString(key);
 706         if (format == null) {
 707             format = "missing resource key: key = \"" + key + "\", "
 708                     + "arguments = \"{0}\", \"{1}\", \"{2}\"";
 709         }
 710         return MessageFormat.format(format, (Object[]) args);
 711     }
 712 }