< prev index next >

src/java.base/share/classes/jdk/internal/module/ModulePath.java

Print this page

        

@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2017, 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
  * under the terms of the GNU General Public License version 2 only, as
  * published by the Free Software Foundation.  Oracle designates this

@@ -193,25 +193,30 @@
         try {
 
             if (attrs.isDirectory()) {
                 Path mi = entry.resolve(MODULE_INFO);
                 if (!Files.exists(mi)) {
-                    // does not exist or unable to determine so assume a
-                    // directory of modules
+                    // assume a directory of modules
                     return scanDirectory(entry);
                 }
             }
 
             // packaged or exploded module
             ModuleReference mref = readModule(entry, attrs);
             if (mref != null) {
                 String name = mref.descriptor().name();
                 return Collections.singletonMap(name, mref);
+            }
+
+            // not recognized
+            String msg;
+            if (!isLinkPhase && entry.toString().endsWith(".jmod")) {
+                msg = "JMOD format not supported at execution time";
             } else {
-                // skipped
-                return Collections.emptyMap();
+                msg = "Module format not recognized";
             }
+            throw new FindException(msg + ": " + entry);
 
         } catch (IOException ioe) {
             throw new FindException(ioe);
         }
     }

@@ -264,44 +269,33 @@
         return nameToReference;
     }
 
 
     /**
-     * Locates a packaged or exploded module, returning a {@code ModuleReference}
-     * to the module. Returns {@code null} if the entry is skipped because it is
-     * to a directory that does not contain a module-info.class or it's a hidden
-     * file.
+     * Reads a packaged or exploded module, returning a {@code ModuleReference}
+     * to the module. Returns {@code null} if the entry is not recognized.
      *
      * @throws IOException if an I/O error occurs
-     * @throws FindException if the file is not recognized as a module or an
-     *         error occurs parsing its module descriptor
+     * @throws FindException if an error occurs parsing its module descriptor
      */
     private ModuleReference readModule(Path entry, BasicFileAttributes attrs)
         throws IOException
     {
         try {
 
             if (attrs.isDirectory()) {
                 return readExplodedModule(entry); // may return null
-            }
-
+            } else {
             String fn = entry.getFileName().toString();
             if (attrs.isRegularFile()) {
                 if (fn.endsWith(".jar")) {
                     return readJar(entry);
-                } else if (fn.endsWith(".jmod")) {
-                    if (isLinkPhase)
+                    } else if (isLinkPhase && fn.endsWith(".jmod")) {
                         return readJMod(entry);
-                    throw new FindException("JMOD files not supported: " + entry);
                 }
             }
-
-            // skip hidden files
-            if (fn.startsWith(".") || Files.isHidden(entry)) {
                 return null;
-            } else {
-                throw new FindException("Unrecognized module: " + entry);
             }
 
         } catch (InvalidModuleDescriptorException e) {
             throw new FindException("Error reading module: " + entry, e);
         }

@@ -360,11 +354,11 @@
 
     private static final String SERVICES_PREFIX = "META-INF/services/";
 
     /**
      * Returns the service type corresponding to the name of a services
-     * configuration file if it is a valid Java identifier.
+     * configuration file if it is a legal type name.
      *
      * For example, if called with "META-INF/services/p.S" then this method
      * returns a container with the value "p.S".
      */
     private Optional<String> toServiceName(String cf) {

@@ -372,11 +366,11 @@
         int index = cf.lastIndexOf("/") + 1;
         if (index < cf.length()) {
             String prefix = cf.substring(0, index);
             if (prefix.equals(SERVICES_PREFIX)) {
                 String sn = cf.substring(index);
-                if (Checks.isJavaIdentifier(sn))
+                if (Checks.isClassName(sn))
                     return Optional.of(sn);
             }
         }
         return Optional.empty();
     }

@@ -401,15 +395,14 @@
     /**
      * Treat the given JAR file as a module as follows:
      *
      * 1. The module name (and optionally the version) is derived from the file
      *    name of the JAR file
-     * 2. All packages are exported and open
-     * 3. It has no non-exported/non-open packages
-     * 4. The contents of any META-INF/services configuration files are mapped
+     * 2. All packages are derived from the .class files in the JAR file
+     * 3. The contents of any META-INF/services configuration files are mapped
      *    to "provides" declarations
-     * 5. The Main-Class attribute in the main attributes of the JAR manifest
+     * 4. The Main-Class attribute in the main attributes of the JAR manifest
      *    is mapped to the module descriptor mainClass
      */
     private ModuleDescriptor deriveModuleDescriptor(JarFile jf)
         throws IOException
     {

@@ -441,31 +434,34 @@
 
         // finally clean up the module name
         mn = cleanModuleName(mn);
 
         // Builder throws IAE if module name is empty or invalid
-        ModuleDescriptor.Builder builder
-            = ModuleDescriptor.automaticModule(mn)
-                .requires(Set.of(Requires.Modifier.MANDATED), "java.base");
+        ModuleDescriptor.Builder builder = ModuleDescriptor.newAutomaticModule(mn);
         if (vs != null)
             builder.version(vs);
 
         // scan the names of the entries in the JAR file
         Map<Boolean, Set<String>> map = VersionedStream.stream(jf)
                 .filter(e -> !e.isDirectory())
                 .map(JarEntry::getName)
+                .filter(e -> (e.endsWith(".class") ^ e.startsWith(SERVICES_PREFIX)))
                 .collect(Collectors.partitioningBy(e -> e.startsWith(SERVICES_PREFIX),
                                                    Collectors.toSet()));
 
-        Set<String> resources = map.get(Boolean.FALSE);
+        Set<String> classFiles = map.get(Boolean.FALSE);
         Set<String> configFiles = map.get(Boolean.TRUE);
-        // all packages are exported and open
-        resources.stream()
+
+        // the packages containing class files
+        Set<String> packages = classFiles.stream()
                 .map(this::toPackageName)
                 .flatMap(Optional::stream)
                 .distinct()
-                .forEach(pn -> builder.exports(pn).opens(pn));
+                .collect(Collectors.toSet());
+
+        // all packages are exported and open
+        builder.packages(packages);
 
         // map names of service configuration files to service names
         Set<String> serviceNames = configFiles.stream()
                 .map(this::toServiceName)
                 .flatMap(Optional::stream)

@@ -479,10 +475,15 @@
                 BufferedReader reader
                     = new BufferedReader(new InputStreamReader(in, "UTF-8"));
                 String cn;
                 while ((cn = nextLine(reader)) != null) {
                     if (cn.length() > 0) {
+                        String pn = packageName(cn);
+                        if (!packages.contains(pn)) {
+                            String msg = "Provider class " + cn + " not in module";
+                            throw new IOException(msg);
+                        }
                         providerClasses.add(cn);
                     }
                 }
             }
             if (!providerClasses.isEmpty())

@@ -492,12 +493,19 @@
         // Main-Class attribute if it exists
         Manifest man = jf.getManifest();
         if (man != null) {
             Attributes attrs = man.getMainAttributes();
             String mainClass = attrs.getValue(Attributes.Name.MAIN_CLASS);
-            if (mainClass != null)
-                builder.mainClass(mainClass.replace("/", "."));
+            if (mainClass != null) {
+                mainClass = mainClass.replace("/", ".");
+                String pn = packageName(mainClass);
+                if (!packages.contains(pn)) {
+                    String msg = "Main-Class " + mainClass + " not in module";
+                    throw new IOException(msg);
+                }
+                builder.mainClass(mainClass);
+            }
         }
 
         return builder.build();
     }
 

@@ -567,14 +575,14 @@
 
                 // no module-info.class so treat it as automatic module
                 try {
                     ModuleDescriptor md = deriveModuleDescriptor(jf);
                     attrs = new ModuleInfo.Attributes(md, null, null);
-                } catch (IllegalArgumentException iae) {
+                } catch (IllegalArgumentException e) {
                     throw new FindException(
                         "Unable to derive module descriptor for: "
-                        + jf.getName(), iae);
+                        + jf.getName(), e);
                 }
 
             } else {
                 attrs = ModuleInfo.read(jf.getInputStream(entry),
                                         () -> jarPackages(jf));

@@ -619,31 +627,38 @@
         }
         return ModuleReferences.newExplodedModule(attrs, dir);
     }
 
     /**
+     * Maps a type name to its package name.
+     */
+    private static String packageName(String cn) {
+        int index = cn.lastIndexOf('.');
+        return (index == -1) ? "" : cn.substring(0, index);
+    }
+
+    /**
      * Maps the name of an entry in a JAR or ZIP file to a package name.
      *
      * @throws IllegalArgumentException if the name is a class file in
      *         the top-level directory of the JAR/ZIP file (and it's
      *         not module-info.class)
      */
     private Optional<String> toPackageName(String name) {
         assert !name.endsWith("/");
-
         int index = name.lastIndexOf("/");
         if (index == -1) {
             if (name.endsWith(".class") && !name.equals(MODULE_INFO)) {
                 throw new IllegalArgumentException(name
-                        + " found in top-level directory:"
+                        + " found in top-level directory"
                         + " (unnamed package not allowed in module)");
             }
             return Optional.empty();
         }
 
         String pn = name.substring(0, index).replace('/', '.');
-        if (Checks.isJavaIdentifier(pn)) {
+        if (Checks.isPackageName(pn)) {
             return Optional.of(pn);
         } else {
             // not a valid package name
             return Optional.empty();
         }

@@ -662,18 +677,18 @@
         Path parent = file.getParent();
         if (parent == null) {
             String name = file.toString();
             if (name.endsWith(".class") && !name.equals(MODULE_INFO)) {
                 throw new IllegalArgumentException(name
-                        + " found in in top-level directory"
+                        + " found in top-level directory"
                         + " (unnamed package not allowed in module)");
             }
             return Optional.empty();
         }
 
         String pn = parent.toString().replace(File.separatorChar, '.');
-        if (Checks.isJavaIdentifier(pn)) {
+        if (Checks.isPackageName(pn)) {
             return Optional.of(pn);
         } else {
             // not a valid package name
             return Optional.empty();
         }
< prev index next >