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().equals("")) { 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 }