52 import java.nio.file.Files;
53 import java.nio.file.Path;
54 import java.text.Normalizer;
55 import java.util.ResourceBundle;
56 import java.text.MessageFormat;
57 import java.util.ArrayList;
58 import java.util.Collections;
59 import java.util.Iterator;
60 import java.util.List;
61 import java.util.Locale;
62 import java.util.Locale.Category;
63 import java.util.Properties;
64 import java.util.Set;
65 import java.util.TreeSet;
66 import java.util.jar.Attributes;
67 import java.util.jar.JarFile;
68 import java.util.jar.Manifest;
69
70 public enum LauncherHelper {
71 INSTANCE;
72 private static final String MAIN_CLASS = "Main-Class";
73
74 private static StringBuilder outBuf = new StringBuilder();
75
76 private static final String INDENT = " ";
77 private static final String VM_SETTINGS = "VM settings:";
78 private static final String PROP_SETTINGS = "Property settings:";
79 private static final String LOCALE_SETTINGS = "Locale settings:";
80
81 // sync with java.c and sun.misc.VM
82 private static final String diagprop = "sun.java.launcher.diag";
83 final static boolean trace = sun.misc.VM.getSavedProperty(diagprop) != null;
84
85 private static final String defaultBundleName =
86 "sun.launcher.resources.launcher";
87 private static class ResourceBundleHolder {
88 private static final ResourceBundle RB =
89 ResourceBundle.getBundle(defaultBundleName);
90 }
91 private static PrintStream ostream;
401 try (JarFile jarFile = new JarFile(jarname)) {
402 Manifest manifest = jarFile.getManifest();
403 if (manifest == null) {
404 abort(null, "java.launcher.jar.error2", jarname);
405 }
406 Attributes mainAttrs = manifest.getMainAttributes();
407 if (mainAttrs == null) {
408 abort(null, "java.launcher.jar.error3", jarname);
409 }
410 mainValue = mainAttrs.getValue(MAIN_CLASS);
411 if (mainValue == null) {
412 abort(null, "java.launcher.jar.error3", jarname);
413 }
414
415 /*
416 * Hand off to FXHelper if it detects a JavaFX application
417 * This must be done after ensuring a Main-Class entry
418 * exists to enforce compliance with the jar specification
419 */
420 if (mainAttrs.containsKey(
421 new Attributes.Name(FXHelper.JAVAFX_APPLICATION_MARKER))) {
422 return FXHelper.class.getName();
423 }
424
425 return mainValue.trim();
426 } catch (IOException ioe) {
427 abort(ioe, "java.launcher.jar.error1", jarname);
428 }
429 return null;
430 }
431
432 // From src/share/bin/java.c:
433 // enum LaunchMode { LM_UNKNOWN = 0, LM_CLASS, LM_JAR };
434
435 private static final int LM_UNKNOWN = 0;
436 private static final int LM_CLASS = 1;
437 private static final int LM_JAR = 2;
438
439 static void abort(Throwable t, String msgKey, Object... args) {
440 if (msgKey != null) {
441 ostream.println(getLocalizedMessage(msgKey, args));
499 try {
500 // On Mac OS X since all names with diacretic symbols are given as decomposed it
501 // is possible that main class name comes incorrectly from the command line
502 // and we have to re-compose it
503 mainClass = scloader.loadClass(Normalizer.normalize(cn, Normalizer.Form.NFC));
504 } catch (NoClassDefFoundError | ClassNotFoundException cnfe1) {
505 abort(cnfe, "java.launcher.cls.error1", cn);
506 }
507 } else {
508 abort(cnfe, "java.launcher.cls.error1", cn);
509 }
510 }
511 // set to mainClass
512 appClass = mainClass;
513
514 /*
515 * Check if FXHelper can launch it using the FX launcher. In an FX app,
516 * the main class may or may not have a main method, so do this before
517 * validating the main class.
518 */
519 if (mainClass.equals(FXHelper.class) ||
520 FXHelper.doesExtendFXApplication(mainClass)) {
521 // Will abort() if there are problems with the FX runtime
522 FXHelper.setFXLaunchParameters(what, mode);
523 return FXHelper.class;
524 }
525
526 validateMainClass(mainClass);
527 return mainClass;
528 }
529
530 /*
531 * Accessor method called by the launcher after getting the main class via
532 * checkAndLoadMain(). The "application class" is the class that is finally
533 * executed to start the application and in this case is used to report
534 * the correct application name, typically for UI purposes.
535 */
536 public static Class<?> getApplicationClass() {
537 return appClass;
538 }
539
540 // Check the existence and signature of main and abort if incorrect
541 static void validateMainClass(Class<?> mainClass) {
542 Method mainMethod;
543 try {
544 mainMethod = mainClass.getMethod("main", String[].class);
545 } catch (NoSuchMethodException nsme) {
546 // invalid main or not FX application, abort with an error
547 abort(null, "java.launcher.cls.error4", mainClass.getName(),
548 FXHelper.JAVAFX_APPLICATION_CLASS_NAME);
549 return; // Avoid compiler issues
550 }
551
552 /*
553 * getMethod (above) will choose the correct method, based
554 * on its name and parameter type, however, we still have to
555 * ensure that the method is static and returns a void.
556 */
557 int mod = mainMethod.getModifiers();
558 if (!Modifier.isStatic(mod)) {
559 abort(null, "java.launcher.cls.error2", "static",
560 mainMethod.getDeclaringClass().getName());
561 }
562 if (mainMethod.getReturnType() != java.lang.Void.TYPE) {
563 abort(null, "java.launcher.cls.error3",
564 mainMethod.getDeclaringClass().getName());
565 }
566 }
567
568 private static final String encprop = "sun.jnu.encoding";
569 private static String encoding = null;
570 private static boolean isCharsetSupported = false;
571
572 /*
573 * converts a c or a byte array to a platform specific string,
574 * previously implemented as a native method in the launcher.
575 */
576 static String makePlatformString(boolean printToStderr, byte[] inArray) {
577 initOutput(printToStderr);
578 if (encoding == null) {
579 encoding = System.getProperty(encprop);
580 isCharsetSupported = Charset.isSupported(encoding);
581 }
582 try {
583 String out = isCharsetSupported
584 ? new String(inArray, encoding)
585 : new String(inArray);
651 private static class StdArg {
652 final String arg;
653 final boolean needsExpansion;
654 StdArg(String arg, boolean expand) {
655 this.arg = arg;
656 this.needsExpansion = expand;
657 }
658 // protocol: first char indicates whether expansion is required
659 // 'T' = true ; needs expansion
660 // 'F' = false; needs no expansion
661 StdArg(String in) {
662 this.arg = in.substring(1);
663 needsExpansion = in.charAt(0) == 'T';
664 }
665 public String toString() {
666 return "StdArg{" + "arg=" + arg + ", needsExpansion=" + needsExpansion + '}';
667 }
668 }
669
670 static final class FXHelper {
671 // Marker entry in jar manifest that designates a JavaFX application jar
672 private static final String JAVAFX_APPLICATION_MARKER =
673 "JavaFX-Application-Class";
674 private static final String JAVAFX_APPLICATION_CLASS_NAME =
675 "javafx.application.Application";
676 private static final String JAVAFX_LAUNCHER_CLASS_NAME =
677 "com.sun.javafx.application.LauncherImpl";
678
679 /*
680 * The launch method used to invoke the JavaFX launcher. These must
681 * match the strings used in the launchApplication method.
682 *
683 * Command line JavaFX-App-Class Launch mode FX Launch mode
684 * java -cp fxapp.jar FXClass N/A LM_CLASS "LM_CLASS"
685 * java -cp somedir FXClass N/A LM_CLASS "LM_CLASS"
686 * java -jar fxapp.jar Present LM_JAR "LM_JAR"
687 * java -jar fxapp.jar Not Present LM_JAR "LM_JAR"
688 */
689 private static final String JAVAFX_LAUNCH_MODE_CLASS = "LM_CLASS";
690 private static final String JAVAFX_LAUNCH_MODE_JAR = "LM_JAR";
691
692 /*
693 * FX application launcher and launch method, so we can launch
694 * applications with no main method.
695 */
725 abort(null, "java.launcher.javafx.error1");
726 }
727 } catch (ClassNotFoundException | NoSuchMethodException ex) {
728 abort(ex, "java.launcher.cls.error5", ex);
729 }
730
731 fxLaunchName = what;
732 switch (mode) {
733 case LM_CLASS:
734 fxLaunchMode = JAVAFX_LAUNCH_MODE_CLASS;
735 break;
736 case LM_JAR:
737 fxLaunchMode = JAVAFX_LAUNCH_MODE_JAR;
738 break;
739 default:
740 // should not have gotten this far...
741 throw new InternalError(mode + ": Unknown launch mode");
742 }
743 }
744
745 /*
746 * Check if the given class is a JavaFX Application class. This is done
747 * in a way that does not cause the Application class to load or throw
748 * ClassNotFoundException if the JavaFX runtime is not available.
749 */
750 private static boolean doesExtendFXApplication(Class<?> mainClass) {
751 for (Class<?> sc = mainClass.getSuperclass(); sc != null;
752 sc = sc.getSuperclass()) {
753 if (sc.getName().equals(JAVAFX_APPLICATION_CLASS_NAME)) {
754 return true;
755 }
756 }
757 return false;
758 }
759
760 public static void main(String... args) throws Exception {
761 if (fxLauncherMethod == null
762 || fxLaunchMode == null
763 || fxLaunchName == null) {
764 throw new RuntimeException("Invalid JavaFX launch parameters");
765 }
766 // launch appClass via fxLauncherMethod
767 fxLauncherMethod.invoke(null,
768 new Object[] {fxLaunchName, fxLaunchMode, args});
769 }
770 }
771 }
|
52 import java.nio.file.Files;
53 import java.nio.file.Path;
54 import java.text.Normalizer;
55 import java.util.ResourceBundle;
56 import java.text.MessageFormat;
57 import java.util.ArrayList;
58 import java.util.Collections;
59 import java.util.Iterator;
60 import java.util.List;
61 import java.util.Locale;
62 import java.util.Locale.Category;
63 import java.util.Properties;
64 import java.util.Set;
65 import java.util.TreeSet;
66 import java.util.jar.Attributes;
67 import java.util.jar.JarFile;
68 import java.util.jar.Manifest;
69
70 public enum LauncherHelper {
71 INSTANCE;
72
73 // used to identify JavaFX applications
74 private static final String JAVAFX_APPLICATION_MARKER =
75 "JavaFX-Application-Class";
76 private static final String JAVAFX_APPLICATION_CLASS_NAME =
77 "javafx.application.Application";
78 private static final String JAVAFX_FXHELPER_CLASS_NAME_SUFFIX =
79 "LauncherHelper$FXHelper";
80 private static final String MAIN_CLASS = "Main-Class";
81
82 private static StringBuilder outBuf = new StringBuilder();
83
84 private static final String INDENT = " ";
85 private static final String VM_SETTINGS = "VM settings:";
86 private static final String PROP_SETTINGS = "Property settings:";
87 private static final String LOCALE_SETTINGS = "Locale settings:";
88
89 // sync with java.c and sun.misc.VM
90 private static final String diagprop = "sun.java.launcher.diag";
91 final static boolean trace = sun.misc.VM.getSavedProperty(diagprop) != null;
92
93 private static final String defaultBundleName =
94 "sun.launcher.resources.launcher";
95 private static class ResourceBundleHolder {
96 private static final ResourceBundle RB =
97 ResourceBundle.getBundle(defaultBundleName);
98 }
99 private static PrintStream ostream;
409 try (JarFile jarFile = new JarFile(jarname)) {
410 Manifest manifest = jarFile.getManifest();
411 if (manifest == null) {
412 abort(null, "java.launcher.jar.error2", jarname);
413 }
414 Attributes mainAttrs = manifest.getMainAttributes();
415 if (mainAttrs == null) {
416 abort(null, "java.launcher.jar.error3", jarname);
417 }
418 mainValue = mainAttrs.getValue(MAIN_CLASS);
419 if (mainValue == null) {
420 abort(null, "java.launcher.jar.error3", jarname);
421 }
422
423 /*
424 * Hand off to FXHelper if it detects a JavaFX application
425 * This must be done after ensuring a Main-Class entry
426 * exists to enforce compliance with the jar specification
427 */
428 if (mainAttrs.containsKey(
429 new Attributes.Name(JAVAFX_APPLICATION_MARKER))) {
430 FXHelper.setFXLaunchParameters(jarname, LM_JAR);
431 return FXHelper.class.getName();
432 }
433
434 return mainValue.trim();
435 } catch (IOException ioe) {
436 abort(ioe, "java.launcher.jar.error1", jarname);
437 }
438 return null;
439 }
440
441 // From src/share/bin/java.c:
442 // enum LaunchMode { LM_UNKNOWN = 0, LM_CLASS, LM_JAR };
443
444 private static final int LM_UNKNOWN = 0;
445 private static final int LM_CLASS = 1;
446 private static final int LM_JAR = 2;
447
448 static void abort(Throwable t, String msgKey, Object... args) {
449 if (msgKey != null) {
450 ostream.println(getLocalizedMessage(msgKey, args));
508 try {
509 // On Mac OS X since all names with diacretic symbols are given as decomposed it
510 // is possible that main class name comes incorrectly from the command line
511 // and we have to re-compose it
512 mainClass = scloader.loadClass(Normalizer.normalize(cn, Normalizer.Form.NFC));
513 } catch (NoClassDefFoundError | ClassNotFoundException cnfe1) {
514 abort(cnfe, "java.launcher.cls.error1", cn);
515 }
516 } else {
517 abort(cnfe, "java.launcher.cls.error1", cn);
518 }
519 }
520 // set to mainClass
521 appClass = mainClass;
522
523 /*
524 * Check if FXHelper can launch it using the FX launcher. In an FX app,
525 * the main class may or may not have a main method, so do this before
526 * validating the main class.
527 */
528 if ( mainClass.getName().contains(JAVAFX_FXHELPER_CLASS_NAME_SUFFIX) ||
529 doesExtendFXApplication(mainClass)) {
530 // Will abort() if there are problems with FX runtime
531 FXHelper.setFXLaunchParameters(what, mode);
532 return FXHelper.class;
533 }
534
535 validateMainClass(mainClass);
536 return mainClass;
537 }
538
539 /*
540 * Accessor method called by the launcher after getting the main class via
541 * checkAndLoadMain(). The "application class" is the class that is finally
542 * executed to start the application and in this case is used to report
543 * the correct application name, typically for UI purposes.
544 */
545 public static Class<?> getApplicationClass() {
546 return appClass;
547 }
548
549 /*
550 * Check if the given class is a JavaFX Application class. This is done
551 * in a way that does not cause the Application class to load or throw
552 * ClassNotFoundException if the JavaFX runtime is not available.
553 */
554 private static boolean doesExtendFXApplication(Class<?> mainClass) {
555 for (Class<?> sc = mainClass.getSuperclass(); sc != null;
556 sc = sc.getSuperclass()) {
557 if (sc.getName().equals(JAVAFX_APPLICATION_CLASS_NAME)) {
558 return true;
559 }
560 }
561 return false;
562 }
563
564 // Check the existence and signature of main and abort if incorrect
565 static void validateMainClass(Class<?> mainClass) {
566 Method mainMethod;
567 try {
568 mainMethod = mainClass.getMethod("main", String[].class);
569 } catch (NoSuchMethodException nsme) {
570 // invalid main or not FX application, abort with an error
571 abort(null, "java.launcher.cls.error4", mainClass.getName(),
572 JAVAFX_APPLICATION_CLASS_NAME);
573 return; // Avoid compiler issues
574 }
575
576 /*
577 * getMethod (above) will choose the correct method, based
578 * on its name and parameter type, however, we still have to
579 * ensure that the method is static and returns a void.
580 */
581 int mod = mainMethod.getModifiers();
582 if (!Modifier.isStatic(mod)) {
583 abort(null, "java.launcher.cls.error2", "static",
584 mainMethod.getDeclaringClass().getName());
585 }
586 if (mainMethod.getReturnType() != java.lang.Void.TYPE) {
587 abort(null, "java.launcher.cls.error3",
588 mainMethod.getDeclaringClass().getName());
589 }
590 return;
591 }
592
593 private static final String encprop = "sun.jnu.encoding";
594 private static String encoding = null;
595 private static boolean isCharsetSupported = false;
596
597 /*
598 * converts a c or a byte array to a platform specific string,
599 * previously implemented as a native method in the launcher.
600 */
601 static String makePlatformString(boolean printToStderr, byte[] inArray) {
602 initOutput(printToStderr);
603 if (encoding == null) {
604 encoding = System.getProperty(encprop);
605 isCharsetSupported = Charset.isSupported(encoding);
606 }
607 try {
608 String out = isCharsetSupported
609 ? new String(inArray, encoding)
610 : new String(inArray);
676 private static class StdArg {
677 final String arg;
678 final boolean needsExpansion;
679 StdArg(String arg, boolean expand) {
680 this.arg = arg;
681 this.needsExpansion = expand;
682 }
683 // protocol: first char indicates whether expansion is required
684 // 'T' = true ; needs expansion
685 // 'F' = false; needs no expansion
686 StdArg(String in) {
687 this.arg = in.substring(1);
688 needsExpansion = in.charAt(0) == 'T';
689 }
690 public String toString() {
691 return "StdArg{" + "arg=" + arg + ", needsExpansion=" + needsExpansion + '}';
692 }
693 }
694
695 static final class FXHelper {
696
697 private static final String JAVAFX_LAUNCHER_CLASS_NAME =
698 "com.sun.javafx.application.LauncherImpl";
699
700 /*
701 * The launch method used to invoke the JavaFX launcher. These must
702 * match the strings used in the launchApplication method.
703 *
704 * Command line JavaFX-App-Class Launch mode FX Launch mode
705 * java -cp fxapp.jar FXClass N/A LM_CLASS "LM_CLASS"
706 * java -cp somedir FXClass N/A LM_CLASS "LM_CLASS"
707 * java -jar fxapp.jar Present LM_JAR "LM_JAR"
708 * java -jar fxapp.jar Not Present LM_JAR "LM_JAR"
709 */
710 private static final String JAVAFX_LAUNCH_MODE_CLASS = "LM_CLASS";
711 private static final String JAVAFX_LAUNCH_MODE_JAR = "LM_JAR";
712
713 /*
714 * FX application launcher and launch method, so we can launch
715 * applications with no main method.
716 */
746 abort(null, "java.launcher.javafx.error1");
747 }
748 } catch (ClassNotFoundException | NoSuchMethodException ex) {
749 abort(ex, "java.launcher.cls.error5", ex);
750 }
751
752 fxLaunchName = what;
753 switch (mode) {
754 case LM_CLASS:
755 fxLaunchMode = JAVAFX_LAUNCH_MODE_CLASS;
756 break;
757 case LM_JAR:
758 fxLaunchMode = JAVAFX_LAUNCH_MODE_JAR;
759 break;
760 default:
761 // should not have gotten this far...
762 throw new InternalError(mode + ": Unknown launch mode");
763 }
764 }
765
766 public static void main(String... args) throws Exception {
767 if (fxLauncherMethod == null
768 || fxLaunchMode == null
769 || fxLaunchName == null) {
770 throw new RuntimeException("Invalid JavaFX launch parameters");
771 }
772 // launch appClass via fxLauncherMethod
773 fxLauncherMethod.invoke(null,
774 new Object[] {fxLaunchName, fxLaunchMode, args});
775 }
776 }
777 }
|