--- a/jdk/src/java.base/share/classes/sun/launcher/LauncherHelper.java Tue Mar 15 13:48:26 2016 -0700
+++ b/jdk/src/java.base/share/classes/sun/launcher/LauncherHelper.java Thu Mar 17 19:04:16 2016 +0000
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -43,24 +43,39 @@
import java.io.IOException;
import java.io.PrintStream;
import java.io.UnsupportedEncodingException;
+import java.lang.module.ModuleFinder;
+import java.lang.module.ModuleReference;
+import java.lang.module.ModuleDescriptor;
+import java.lang.module.ModuleDescriptor.Requires;
+import java.lang.module.ModuleDescriptor.Exports;
+import java.lang.module.ModuleDescriptor.Provides;
+import java.lang.reflect.Layer;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
+import java.lang.reflect.Module;
import java.math.BigDecimal;
import java.math.RoundingMode;
+import java.net.URI;
import java.nio.charset.Charset;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.Path;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
import java.text.Normalizer;
-import java.util.ResourceBundle;
import java.text.MessageFormat;
+import java.util.ResourceBundle;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.Collections;
+import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Locale.Category;
+import java.util.Optional;
import java.util.Properties;
+import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.jar.Attributes;
@@ -68,6 +83,7 @@
import java.util.jar.Manifest;
import jdk.internal.misc.VM;
+
public enum LauncherHelper {
INSTANCE;
@@ -98,7 +114,6 @@
ResourceBundle.getBundle(defaultBundleName);
}
private static PrintStream ostream;
- private static final ClassLoader scloader = ClassLoader.getSystemClassLoader();
private static Class<?> appClass; // application class, for GUI/reporting purposes
/*
@@ -439,11 +454,12 @@
}
// From src/share/bin/java.c:
- // enum LaunchMode { LM_UNKNOWN = 0, LM_CLASS, LM_JAR };
+ // enum LaunchMode { LM_UNKNOWN = 0, LM_CLASS, LM_JAR, LM_MODULE }
private static final int LM_UNKNOWN = 0;
private static final int LM_CLASS = 1;
private static final int LM_JAR = 2;
+ private static final int LM_MODULE = 3;
static void abort(Throwable t, String msgKey, Object... args) {
if (msgKey != null) {
@@ -460,33 +476,95 @@
}
/**
- * This method does the following:
- * 1. gets the classname from a Jar's manifest, if necessary
- * 2. loads the class using the System ClassLoader
- * 3. ensures the availability and accessibility of the main method,
- * using signatureDiagnostic method.
- * a. does the class exist
- * b. is there a main
- * c. is the main public
- * d. is the main static
- * e. does the main take a String array for args
- * 4. if no main method and if the class extends FX Application, then call
- * on FXHelper to determine the main class to launch
- * 5. and off we go......
+ * This method:
+ * 1. Loads the main class from the module or class path
+ * 2. Checks the public static void main method.
*
* @param printToStderr if set, all output will be routed to stderr
* @param mode LaunchMode as determined by the arguments passed on the
- * command line
- * @param what either the jar file to launch or the main class when using
- * LM_CLASS mode
+ * command line
+ * @param what the module name[/class], JAR file, or the main class
+ * depending on the mode
+ *
* @return the application's main class
*/
public static Class<?> checkAndLoadMain(boolean printToStderr,
int mode,
String what) {
initOutput(printToStderr);
+
+ Class<?> mainClass = (mode == LM_MODULE) ? loadModuleMainClass(what)
+ : loadMainClass(mode, what);
+
+ validateMainClass(mainClass);
+
+ // record main class if not already set
+ if (appClass == null)
+ appClass = mainClass;
+
+ return mainClass;
+ }
+
+ /**
+ * Returns the main class for a module. The query is either a module name
+ * or module-name/main-class. For the former then the module's main class
+ * is obtained from the module descriptor (MainClass attribute).
+ */
+ private static Class<?> loadModuleMainClass(String what) {
+ int i = what.indexOf('/');
+ String mainModule;
+ String mainClass;
+ if (i == -1) {
+ mainModule = what;
+ mainClass = null;
+ } else {
+ mainModule = what.substring(0, i);
+ mainClass = what.substring(i+1);
+ }
+
+ // main module is in the boot layer
+ Layer layer = Layer.boot();
+ Optional<Module> om = layer.findModule(mainModule);
+ if (!om.isPresent()) {
+ // should not happen
+ throw new InternalError("Module " + mainModule + " not in boot Layer");
+ }
+ Module m = om.get();
+
+ // get main class
+ if (mainClass == null) {
+ Optional<String> omc = m.getDescriptor().mainClass();
+ if (!omc.isPresent()) {
+ abort(null, "java.launcher.module.error1", mainModule);
+ }
+ mainClass = omc.get();
+ }
+
+ // load the class from the module
+ Class<?> c = Class.forName(m, mainClass);
+ if (c == null && System.getProperty("os.name", "").contains("OS X")
+ && Normalizer.isNormalized(mainClass, Normalizer.Form.NFD)) {
+
+ String cn = Normalizer.normalize(mainClass, Normalizer.Form.NFC);
+ c = Class.forName(m, cn);
+
+ }
+ if (c == null) {
+ abort(null, "java.launcher.module.error2", mainClass, mainModule);
+ }
+
+ System.setProperty("jdk.module.main.class", c.getName());
+ return c;
+ }
+
+ /**
+ * Loads the main class from the class path (LM_CLASS or LM_JAR).
+ * If the main class extends FX Application then call on FXHelper to
+ * determine the main class to launch.
+ */
+ private static Class<?> loadMainClass(int mode, String what) {
// get the class name
- String cn = null;
+ String cn;
switch (mode) {
case LM_CLASS:
cn = what;
@@ -498,18 +576,21 @@
// should never happen
throw new InternalError("" + mode + ": Unknown launch mode");
}
+
+ // load the main class
cn = cn.replace('/', '.');
Class<?> mainClass = null;
+ ClassLoader scl = ClassLoader.getSystemClassLoader();
try {
- mainClass = scloader.loadClass(cn);
+ mainClass = scl.loadClass(cn);
} catch (NoClassDefFoundError | ClassNotFoundException cnfe) {
if (System.getProperty("os.name", "").contains("OS X")
- && Normalizer.isNormalized(cn, Normalizer.Form.NFD)) {
+ && Normalizer.isNormalized(cn, Normalizer.Form.NFD)) {
try {
// On Mac OS X since all names with diacretic symbols are given as decomposed it
// is possible that main class name comes incorrectly from the command line
// and we have to re-compose it
- mainClass = scloader.loadClass(Normalizer.normalize(cn, Normalizer.Form.NFC));
+ mainClass = scl.loadClass(Normalizer.normalize(cn, Normalizer.Form.NFC));
} catch (NoClassDefFoundError | ClassNotFoundException cnfe1) {
abort(cnfe, "java.launcher.cls.error1", cn);
}
@@ -517,7 +598,8 @@
abort(cnfe, "java.launcher.cls.error1", cn);
}
}
- // set to mainClass
+
+ // record the main class
appClass = mainClass;
/*
@@ -531,8 +613,6 @@
FXHelper.setFXLaunchParameters(what, mode);
return FXHelper.class;
}
-
- validateMainClass(mainClass);
return mainClass;
}
@@ -693,6 +773,9 @@
static final class FXHelper {
+ private static final String JAVAFX_GRAPHICS_MODULE_NAME =
+ "javafx.graphics";
+
private static final String JAVAFX_LAUNCHER_CLASS_NAME =
"com.sun.javafx.application.LauncherImpl";
@@ -725,9 +808,20 @@
* issue with loading the FX runtime or with the launcher method.
*/
private static void setFXLaunchParameters(String what, int mode) {
- // Check for the FX launcher classes
+
+ // find the module with the FX launcher
+ Optional<Module> om = Layer.boot().findModule(JAVAFX_GRAPHICS_MODULE_NAME);
+ if (!om.isPresent()) {
+ abort(null, "java.launcher.cls.error5");
+ }
+
+ // load the FX launcher class
+ fxLauncherClass = Class.forName(om.get(), JAVAFX_LAUNCHER_CLASS_NAME);
+ if (fxLauncherClass == null) {
+ abort(null, "java.launcher.cls.error5");
+ }
+
try {
- fxLauncherClass = scloader.loadClass(JAVAFX_LAUNCHER_CLASS_NAME);
/*
* signature must be:
* public static void launchApplication(String launchName,
@@ -744,7 +838,7 @@
if (fxLauncherMethod.getReturnType() != java.lang.Void.TYPE) {
abort(null, "java.launcher.javafx.error1");
}
- } catch (ClassNotFoundException | NoSuchMethodException ex) {
+ } catch (NoSuchMethodException ex) {
abort(ex, "java.launcher.cls.error5", ex);
}
@@ -773,4 +867,97 @@
new Object[] {fxLaunchName, fxLaunchMode, args});
}
}
+
+ private static void formatCommaList(PrintStream out,
+ String prefix,
+ Collection<?> list)
+ {
+ if (list.isEmpty())
+ return;
+ out.format("%s", prefix);
+ boolean first = true;
+ for (Object ob : list) {
+ if (first) {
+ out.format(" %s", ob);
+ first = false;
+ } else {
+ out.format(", %s", ob);
+ }
+ }
+ out.format("%n");
+ }
+
+ /**
+ * Called by the launcher to list the observable modules.
+ * If called without any sub-options then the output is a simple list of
+ * the modules. If called with sub-options then the sub-options are the
+ * names of the modules to list (-listmods:java.base,java.desktop for
+ * example).
+ */
+ static void listModules(boolean printToStderr, String optionFlag)
+ throws IOException, ClassNotFoundException
+ {
+ initOutput(printToStderr);
+
+ ModuleFinder finder = jdk.internal.module.ModuleBootstrap.finder();
+
+ int colon = optionFlag.indexOf(':');
+ if (colon == -1) {
+ finder.findAll().stream()
+ .sorted(Comparator.comparing(ModuleReference::descriptor))
+ .forEach(md -> {
+ ostream.println(midAndLocation(md.descriptor(),
+ md.location()));
+ });
+ } else {
+ String[] names = optionFlag.substring(colon+1).split(",");
+ for (String name: names) {
+ ModuleReference mref = finder.find(name).orElse(null);
+ if (mref == null) {
+ // not found
+ continue;
+ }
+
+ ModuleDescriptor md = mref.descriptor();
+ ostream.println(midAndLocation(md, mref.location()));
+
+ for (Requires d : md.requires()) {
+ ostream.format(" requires %s%n", d);
+ }
+ for (String s : md.uses()) {
+ ostream.format(" uses %s%n", s);
+ }
+
+ // sorted exports
+ Set<Exports> exports = new TreeSet<>(Comparator.comparing(Exports::source));
+ exports.addAll(md.exports());
+ for (Exports e : exports) {
+ ostream.format(" exports %s", e.source());
+ if (e.isQualified()) {
+ formatCommaList(ostream, " to", e.targets());
+ } else {
+ ostream.println();
+ }
+ }
+
+ // concealed packages
+ new TreeSet<>(md.conceals())
+ .forEach(p -> ostream.format(" conceals %s%n", p));
+
+ Map<String, Provides> provides = md.provides();
+ for (Provides ps : provides.values()) {
+ for (String impl : ps.providers())
+ ostream.format(" provides %s with %s%n", ps.service(), impl);
+ }
+ }
+ }
+ }
+
+ static String midAndLocation(ModuleDescriptor md, Optional<URI> location ) {
+ URI loc = location.orElse(null);
+ if (loc == null || loc.getScheme().equalsIgnoreCase("jrt"))
+ return md.toNameAndVersion();
+ else
+ return md.toNameAndVersion() + " (" + loc + ")";
+ }
}