jdk/src/java.base/share/classes/sun/launcher/LauncherHelper.java
changeset 36511 9d0388c6b336
parent 34882 ce2a8ec851c1
child 37788 81ce381a91f6
--- 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 + ")";
+    }
 }