1 /* 2 * Copyright (c) 2007, 2011, 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 26 package sun.launcher; 27 28 /* 29 * 30 * <p><b>This is NOT part of any API supported by Sun Microsystems. 31 * If you write code that depends on this, you do so at your own 32 * risk. This code and its internal interfaces are subject to change 33 * or deletion without notice.</b> 34 * 35 */ 36 37 /** 38 * A utility package for the java(1), javaw(1) launchers. 39 * The following are helper methods that the native launcher uses 40 * to perform checks etc. using JNI, see src/share/bin/java.c 41 */ 42 import java.io.File; 43 import java.io.IOException; 44 import java.io.PrintStream; 45 import java.io.UnsupportedEncodingException; 46 import java.lang.reflect.Method; 47 import java.lang.reflect.Modifier; 48 import java.math.BigDecimal; 49 import java.math.RoundingMode; 50 import java.nio.charset.Charset; 51 import java.util.ResourceBundle; 52 import java.text.MessageFormat; 53 import java.util.ArrayList; 54 import java.util.Collections; 55 import java.util.Iterator; 56 import java.util.List; 57 import java.util.Locale; 58 import java.util.Locale.Category; 59 import java.util.Properties; 60 import java.util.Set; 61 import java.util.TreeSet; 62 import java.util.jar.Attributes; 63 import java.util.jar.JarFile; 64 import java.util.jar.Manifest; 65 66 public enum LauncherHelper { 67 INSTANCE; 68 private static final String MAIN_CLASS = "Main-Class"; 69 70 private static StringBuilder outBuf = new StringBuilder(); 71 72 private static ResourceBundle javarb = null; 73 74 private static final String INDENT = " "; 75 private static final String VM_SETTINGS = "VM settings:"; 76 private static final String PROP_SETTINGS = "Property settings:"; 77 private static final String LOCALE_SETTINGS = "Locale settings:"; 78 79 // sync with java.c and sun.misc.VM 80 private static final String diagprop = "sun.java.launcher.diag"; 81 82 private static final String defaultBundleName = 83 "sun.launcher.resources.launcher"; 84 private static class ResourceBundleHolder { 85 private static final ResourceBundle RB = 86 ResourceBundle.getBundle(defaultBundleName); 87 } 88 89 /* 90 * A method called by the launcher to print out the standard settings, 91 * by default -XshowSettings is equivalent to -XshowSettings:all, 92 * Specific information may be gotten by using suboptions with possible 93 * values vm, properties and locale. 94 * 95 * printToStderr: choose between stdout and stderr 96 * 97 * optionFlag: specifies which options to print default is all other 98 * possible values are vm, properties, locale. 99 * 100 * initialHeapSize: in bytes, as set by the launcher, a zero-value indicates 101 * this code should determine this value, using a suitable method or 102 * the line could be omitted. 103 * 104 * maxHeapSize: in bytes, as set by the launcher, a zero-value indicates 105 * this code should determine this value, using a suitable method. 106 * 107 * stackSize: in bytes, as set by the launcher, a zero-value indicates 108 * this code determine this value, using a suitable method or omit the 109 * line entirely. 110 */ 111 static void showSettings(boolean printToStderr, String optionFlag, 112 long initialHeapSize, long maxHeapSize, long stackSize, 113 boolean isServer) { 114 115 PrintStream ostream = (printToStderr) ? System.err : System.out; 116 String opts[] = optionFlag.split(":"); 117 String optStr = (opts.length > 1 && opts[1] != null) 118 ? opts[1].trim() 119 : "all"; 120 switch (optStr) { 121 case "vm": 122 printVmSettings(ostream, initialHeapSize, maxHeapSize, 123 stackSize, isServer); 124 break; 125 case "properties": 126 printProperties(ostream); 127 break; 128 case "locale": 129 printLocale(ostream); 130 break; 131 default: 132 printVmSettings(ostream, initialHeapSize, maxHeapSize, 133 stackSize, isServer); 134 printProperties(ostream); 135 printLocale(ostream); 136 break; 137 } 138 } 139 140 /* 141 * prints the main vm settings subopt/section 142 */ 143 private static void printVmSettings(PrintStream ostream, 144 long initialHeapSize, long maxHeapSize, 145 long stackSize, boolean isServer) { 146 147 ostream.println(VM_SETTINGS); 148 if (stackSize != 0L) { 149 ostream.println(INDENT + "Stack Size: " + 150 SizePrefix.scaleValue(stackSize)); 151 } 152 if (initialHeapSize != 0L) { 153 ostream.println(INDENT + "Min. Heap Size: " + 154 SizePrefix.scaleValue(initialHeapSize)); 155 } 156 if (maxHeapSize != 0L) { 157 ostream.println(INDENT + "Max. Heap Size: " + 158 SizePrefix.scaleValue(maxHeapSize)); 159 } else { 160 ostream.println(INDENT + "Max. Heap Size (Estimated): " 161 + SizePrefix.scaleValue(Runtime.getRuntime().maxMemory())); 162 } 163 ostream.println(INDENT + "Ergonomics Machine Class: " 164 + ((isServer) ? "server" : "client")); 165 ostream.println(INDENT + "Using VM: " 166 + System.getProperty("java.vm.name")); 167 ostream.println(); 168 } 169 170 /* 171 * prints the properties subopt/section 172 */ 173 private static void printProperties(PrintStream ostream) { 174 Properties p = System.getProperties(); 175 ostream.println(PROP_SETTINGS); 176 List<String> sortedPropertyKeys = new ArrayList<>(); 177 sortedPropertyKeys.addAll(p.stringPropertyNames()); 178 Collections.sort(sortedPropertyKeys); 179 for (String x : sortedPropertyKeys) { 180 printPropertyValue(ostream, x, p.getProperty(x)); 181 } 182 ostream.println(); 183 } 184 185 private static boolean isPath(String key) { 186 return key.endsWith(".dirs") || key.endsWith(".path"); 187 } 188 189 private static void printPropertyValue(PrintStream ostream, 190 String key, String value) { 191 ostream.print(INDENT + key + " = "); 192 if (key.equals("line.separator")) { 193 for (byte b : value.getBytes()) { 194 switch (b) { 195 case 0xd: 196 ostream.print("\\r "); 197 break; 198 case 0xa: 199 ostream.print("\\n "); 200 break; 201 default: 202 // print any bizzare line separators in hex, but really 203 // shouldn't happen. 204 ostream.printf("0x%02X", b & 0xff); 205 break; 206 } 207 } 208 ostream.println(); 209 return; 210 } 211 if (!isPath(key)) { 212 ostream.println(value); 213 return; 214 } 215 String[] values = value.split(System.getProperty("path.separator")); 216 boolean first = true; 217 for (String s : values) { 218 if (first) { // first line treated specially 219 ostream.println(s); 220 first = false; 221 } else { // following lines prefix with indents 222 ostream.println(INDENT + INDENT + s); 223 } 224 } 225 } 226 227 /* 228 * prints the locale subopt/section 229 */ 230 private static void printLocale(PrintStream ostream) { 231 Locale locale = Locale.getDefault(); 232 ostream.println(LOCALE_SETTINGS); 233 ostream.println(INDENT + "default locale = " + 234 locale.getDisplayLanguage()); 235 ostream.println(INDENT + "default display locale = " + 236 Locale.getDefault(Category.DISPLAY).getDisplayName()); 237 ostream.println(INDENT + "default format locale = " + 238 Locale.getDefault(Category.FORMAT).getDisplayName()); 239 printLocales(ostream); 240 ostream.println(); 241 } 242 243 private static void printLocales(PrintStream ostream) { 244 Locale[] tlocales = Locale.getAvailableLocales(); 245 final int len = tlocales == null ? 0 : tlocales.length; 246 if (len < 1 ) { 247 return; 248 } 249 // Locale does not implement Comparable so we convert it to String 250 // and sort it for pretty printing. 251 Set<String> sortedSet = new TreeSet<>(); 252 for (Locale l : tlocales) { 253 sortedSet.add(l.toString()); 254 } 255 256 ostream.print(INDENT + "available locales = "); 257 Iterator<String> iter = sortedSet.iterator(); 258 final int last = len - 1; 259 for (int i = 0 ; iter.hasNext() ; i++) { 260 String s = iter.next(); 261 ostream.print(s); 262 if (i != last) { 263 ostream.print(", "); 264 } 265 // print columns of 8 266 if ((i + 1) % 8 == 0) { 267 ostream.println(); 268 ostream.print(INDENT + INDENT); 269 } 270 } 271 } 272 273 private enum SizePrefix { 274 275 KILO(1024, "K"), 276 MEGA(1024 * 1024, "M"), 277 GIGA(1024 * 1024 * 1024, "G"), 278 TERA(1024L * 1024L * 1024L * 1024L, "T"); 279 long size; 280 String abbrev; 281 282 SizePrefix(long size, String abbrev) { 283 this.size = size; 284 this.abbrev = abbrev; 285 } 286 287 private static String scale(long v, SizePrefix prefix) { 288 return BigDecimal.valueOf(v).divide(BigDecimal.valueOf(prefix.size), 289 2, RoundingMode.HALF_EVEN).toPlainString() + prefix.abbrev; 290 } 291 /* 292 * scale the incoming values to a human readable form, represented as 293 * K, M, G and T, see java.c parse_size for the scaled values and 294 * suffixes. The lowest possible scaled value is Kilo. 295 */ 296 static String scaleValue(long v) { 297 if (v < MEGA.size) { 298 return scale(v, KILO); 299 } else if (v < GIGA.size) { 300 return scale(v, MEGA); 301 } else if (v < TERA.size) { 302 return scale(v, GIGA); 303 } else { 304 return scale(v, TERA); 305 } 306 } 307 } 308 309 /** 310 * A private helper method to get a localized message and also 311 * apply any arguments that we might pass. 312 */ 313 private static String getLocalizedMessage(String key, Object... args) { 314 String msg = ResourceBundleHolder.RB.getString(key); 315 return (args != null) ? MessageFormat.format(msg, args) : msg; 316 } 317 318 /** 319 * The java -help message is split into 3 parts, an invariant, followed 320 * by a set of platform dependent variant messages, finally an invariant 321 * set of lines. 322 * This method initializes the help message for the first time, and also 323 * assembles the invariant header part of the message. 324 */ 325 static void initHelpMessage(String progname) { 326 outBuf = outBuf.append(getLocalizedMessage("java.launcher.opt.header", 327 (progname == null) ? "java" : progname )); 328 outBuf = outBuf.append(getLocalizedMessage("java.launcher.opt.datamodel", 329 32)); 330 outBuf = outBuf.append(getLocalizedMessage("java.launcher.opt.datamodel", 331 64)); 332 } 333 334 /** 335 * Appends the vm selection messages to the header, already created. 336 * initHelpSystem must already be called. 337 */ 338 static void appendVmSelectMessage(String vm1, String vm2) { 339 outBuf = outBuf.append(getLocalizedMessage("java.launcher.opt.vmselect", 340 vm1, vm2)); 341 } 342 343 /** 344 * Appends the vm synoym message to the header, already created. 345 * initHelpSystem must be called before using this method. 346 */ 347 static void appendVmSynonymMessage(String vm1, String vm2) { 348 outBuf = outBuf.append(getLocalizedMessage("java.launcher.opt.hotspot", 349 vm1, vm2)); 350 } 351 352 /** 353 * Appends the vm Ergo message to the header, already created. 354 * initHelpSystem must be called before using this method. 355 */ 356 static void appendVmErgoMessage(boolean isServerClass, String vm) { 357 outBuf = outBuf.append(getLocalizedMessage("java.launcher.ergo.message1", 358 vm)); 359 outBuf = (isServerClass) 360 ? outBuf.append(",\n" + 361 getLocalizedMessage("java.launcher.ergo.message2") + "\n\n") 362 : outBuf.append(".\n\n"); 363 } 364 365 /** 366 * Appends the last invariant part to the previously created messages, 367 * and finishes up the printing to the desired output stream. 368 * initHelpSystem must be called before using this method. 369 */ 370 static void printHelpMessage(boolean printToStderr) { 371 PrintStream ostream = (printToStderr) ? System.err : System.out; 372 outBuf = outBuf.append(getLocalizedMessage("java.launcher.opt.footer", 373 File.pathSeparator)); 374 ostream.println(outBuf.toString()); 375 } 376 377 /** 378 * Prints the Xusage text to the desired output stream. 379 */ 380 static void printXUsageMessage(boolean printToStderr) { 381 PrintStream ostream = (printToStderr) ? System.err : System.out; 382 ostream.println(getLocalizedMessage("java.launcher.X.usage", 383 File.pathSeparator)); 384 } 385 386 static String getMainClassFromJar(PrintStream ostream, String jarname) { 387 try { 388 JarFile jarFile = null; 389 try { 390 jarFile = new JarFile(jarname); 391 Manifest manifest = jarFile.getManifest(); 392 if (manifest == null) { 393 abort(ostream, null, "java.launcher.jar.error2", jarname); 394 } 395 Attributes mainAttrs = manifest.getMainAttributes(); 396 if (mainAttrs == null) { 397 abort(ostream, null, "java.launcher.jar.error3", jarname); 398 } 399 return mainAttrs.getValue(MAIN_CLASS).trim(); 400 } finally { 401 if (jarFile != null) { 402 jarFile.close(); 403 } 404 } 405 } catch (IOException ioe) { 406 abort(ostream, ioe, "java.launcher.jar.error1", jarname); 407 } 408 return null; 409 } 410 411 412 // From src/share/bin/java.c: 413 // enum LaunchMode { LM_UNKNOWN = 0, LM_CLASS, LM_JAR }; 414 415 private static final int LM_UNKNOWN = 0; 416 private static final int LM_CLASS = 1; 417 private static final int LM_JAR = 2; 418 419 static void abort(PrintStream ostream, Throwable t, String msgKey, Object... args) { 420 if (msgKey != null) { 421 ostream.println(getLocalizedMessage(msgKey, args)); 422 } 423 if (sun.misc.VM.getSavedProperty(diagprop) != null) { 424 if (t != null) { 425 t.printStackTrace(); 426 } else { 427 Thread.currentThread().dumpStack(); 428 } 429 } 430 System.exit(1); 431 } 432 433 /** 434 * This method does the following: 435 * 1. gets the classname from a Jar's manifest, if necessary 436 * 2. loads the class using the System ClassLoader 437 * 3. ensures the availability and accessibility of the main method, 438 * using signatureDiagnostic method. 439 * a. does the class exist 440 * b. is there a main 441 * c. is the main public 442 * d. is the main static 443 * c. does the main take a String array for args 444 * 4. and off we go...... 445 * 446 * @param printToStderr 447 * @param isJar 448 * @param name 449 * @return 450 */ 451 public static Class<?> checkAndLoadMain(boolean printToStderr, 452 int mode, 453 String what) { 454 final PrintStream ostream = (printToStderr) ? System.err : System.out; 455 final ClassLoader ld = ClassLoader.getSystemClassLoader(); 456 // get the class name 457 String cn = null; 458 switch (mode) { 459 case LM_CLASS: 460 cn = what; 461 break; 462 case LM_JAR: 463 cn = getMainClassFromJar(ostream, what); 464 break; 465 default: 466 // should never happen 467 throw new InternalError("" + mode + ": Unknown launch mode"); 468 } 469 cn = cn.replace('/', '.'); 470 Class<?> c = null; 471 try { 472 c = ld.loadClass(cn); 473 } catch (ClassNotFoundException cnfe) { 474 abort(ostream, cnfe, "java.launcher.cls.error1", cn); 475 } 476 getMainMethod(ostream, c); 477 return c; 478 } 479 480 static Method getMainMethod(PrintStream ostream, Class<?> clazz) { 481 String classname = clazz.getName(); 482 Method method = null; 483 try { 484 method = clazz.getMethod("main", String[].class); 485 } catch (NoSuchMethodException nsme) { 486 abort(ostream, null, "java.launcher.cls.error4", classname); 487 } 488 /* 489 * getMethod (above) will choose the correct method, based 490 * on its name and parameter type, however, we still have to 491 * ensure that the method is static and returns a void. 492 */ 493 int mod = method.getModifiers(); 494 if (!Modifier.isStatic(mod)) { 495 abort(ostream, null, "java.launcher.cls.error2", "static", classname); 496 } 497 if (method.getReturnType() != java.lang.Void.TYPE) { 498 abort(ostream, null, "java.launcher.cls.error3", classname); 499 } 500 return method; 501 } 502 503 private static final String encprop = "sun.jnu.encoding"; 504 private static String encoding = null; 505 private static boolean isCharsetSupported = false; 506 507 /* 508 * converts a c or a byte array to a platform specific string, 509 * previously implemented as a native method in the launcher. 510 */ 511 static String makePlatformString(boolean printToStderr, byte[] inArray) { 512 final PrintStream ostream = (printToStderr) ? System.err : System.out; 513 if (encoding == null) { 514 encoding = System.getProperty(encprop); 515 isCharsetSupported = Charset.isSupported(encoding); 516 } 517 try { 518 String out = isCharsetSupported 519 ? new String(inArray, encoding) 520 : new String(inArray); 521 return out; 522 } catch (UnsupportedEncodingException uee) { 523 abort(ostream, uee, null); 524 } 525 return null; // keep the compiler happy 526 } 527 }