jdk/src/java.base/share/classes/java/lang/module/ModulePath.java
changeset 37779 7c84df693837
parent 36511 9d0388c6b336
child 39050 9de41b79ec7e
equal deleted inserted replaced
37662:ec60c3290242 37779:7c84df693837
    38 import java.nio.file.NoSuchFileException;
    38 import java.nio.file.NoSuchFileException;
    39 import java.nio.file.Path;
    39 import java.nio.file.Path;
    40 import java.nio.file.attribute.BasicFileAttributes;
    40 import java.nio.file.attribute.BasicFileAttributes;
    41 import java.util.Collections;
    41 import java.util.Collections;
    42 import java.util.HashMap;
    42 import java.util.HashMap;
    43 import java.util.HashSet;
    43 import java.util.LinkedHashSet;
    44 import java.util.Map;
    44 import java.util.Map;
    45 import java.util.Objects;
    45 import java.util.Objects;
    46 import java.util.Optional;
    46 import java.util.Optional;
    47 import java.util.Set;
    47 import java.util.Set;
    48 import java.util.jar.Attributes;
    48 import java.util.jar.Attributes;
    50 import java.util.jar.JarFile;
    50 import java.util.jar.JarFile;
    51 import java.util.jar.Manifest;
    51 import java.util.jar.Manifest;
    52 import java.util.regex.Matcher;
    52 import java.util.regex.Matcher;
    53 import java.util.regex.Pattern;
    53 import java.util.regex.Pattern;
    54 import java.util.stream.Collectors;
    54 import java.util.stream.Collectors;
    55 import java.util.stream.Stream;
       
    56 import java.util.zip.ZipEntry;
    55 import java.util.zip.ZipEntry;
    57 import java.util.zip.ZipFile;
    56 import java.util.zip.ZipFile;
    58 
    57 
    59 import jdk.internal.module.Checks;
    58 import jdk.internal.module.Checks;
    60 import jdk.internal.module.ConfigurableModuleFinder;
    59 import jdk.internal.module.ConfigurableModuleFinder;
   188                     // directory of modules
   187                     // directory of modules
   189                     return scanDirectory(entry);
   188                     return scanDirectory(entry);
   190                 }
   189                 }
   191             }
   190             }
   192 
   191 
   193             if (attrs.isRegularFile() || attrs.isDirectory()) {
   192             // packaged or exploded module
   194                 // packaged or exploded module
   193             ModuleReference mref = readModule(entry, attrs);
   195                 ModuleReference mref = readModule(entry, attrs);
   194             if (mref != null) {
   196                 if (mref != null) {
   195                 String name = mref.descriptor().name();
   197                     String name = mref.descriptor().name();
   196                 return Collections.singletonMap(name, mref);
   198                     return Collections.singletonMap(name, mref);
   197             } else {
   199                 }
   198                 // skipped
   200             }
   199                 return Collections.emptyMap();
   201 
   200             }
   202             // not recognized
       
   203             throw new FindException("Unrecognized module: " + entry);
       
   204 
   201 
   205         } catch (IOException ioe) {
   202         } catch (IOException ioe) {
   206             throw new FindException(ioe);
   203             throw new FindException(ioe);
   207         }
   204         }
   208     }
   205     }
   236 
   233 
   237                 ModuleReference mref = readModule(entry, attrs);
   234                 ModuleReference mref = readModule(entry, attrs);
   238 
   235 
   239                 // module found
   236                 // module found
   240                 if (mref != null) {
   237                 if (mref != null) {
   241 
       
   242                     // can have at most one version of a module in the directory
   238                     // can have at most one version of a module in the directory
   243                     String name = mref.descriptor().name();
   239                     String name = mref.descriptor().name();
   244                     if (nameToReference.put(name, mref) != null) {
   240                     if (nameToReference.put(name, mref) != null) {
   245                         throw new FindException("Two versions of module "
   241                         throw new FindException("Two versions of module "
   246                                 + name + " found in " + dir);
   242                                                   + name + " found in " + dir);
   247                     }
   243                     }
   248 
       
   249                 }
   244                 }
   250 
       
   251             }
   245             }
   252         }
   246         }
   253 
   247 
   254         return nameToReference;
   248         return nameToReference;
   255     }
   249     }
   256 
   250 
   257 
   251 
   258     /**
   252     /**
   259      * Locates a packaged or exploded module, returning a {@code ModuleReference}
   253      * Locates a packaged or exploded module, returning a {@code ModuleReference}
   260      * to the module. Returns {@code null} if the module is not recognized
   254      * to the module. Returns {@code null} if the entry is skipped because it is
   261      * as a packaged or exploded module.
   255      * to a directory that does not contain a module-info.class or it's a hidden
       
   256      * file.
   262      *
   257      *
   263      * @throws IOException if an I/O error occurs
   258      * @throws IOException if an I/O error occurs
   264      * @throws FindException if an error occurs parsing the module descriptor
   259      * @throws FindException if the file is not recognized as a module or an
       
   260      *         error occurs parsing its module descriptor
   265      */
   261      */
   266     private ModuleReference readModule(Path entry, BasicFileAttributes attrs)
   262     private ModuleReference readModule(Path entry, BasicFileAttributes attrs)
   267         throws IOException
   263         throws IOException
   268     {
   264     {
   269         try {
   265         try {
   270 
   266 
   271             ModuleReference mref = null;
       
   272             if (attrs.isDirectory()) {
   267             if (attrs.isDirectory()) {
   273                 mref = readExplodedModule(entry);
   268                 return readExplodedModule(entry); // may return null
   274             } if (attrs.isRegularFile()) {
   269             }
   275                 if (entry.toString().endsWith(".jar")) {
   270 
   276                     mref = readJar(entry);
   271             String fn = entry.getFileName().toString();
   277                 } else if (isLinkPhase && entry.toString().endsWith(".jmod")) {
   272             if (attrs.isRegularFile()) {
   278                     mref = readJMod(entry);
   273                 if (fn.endsWith(".jar")) {
       
   274                     return readJar(entry);
       
   275                 } else if (fn.endsWith(".jmod")) {
       
   276                     if (isLinkPhase)
       
   277                         return readJMod(entry);
       
   278                     throw new FindException("JMOD files not supported: " + entry);
   279                 }
   279                 }
   280             }
   280             }
   281             return mref;
   281 
       
   282             // skip hidden files
       
   283             if (fn.startsWith(".") || Files.isHidden(entry)) {
       
   284                 return null;
       
   285             } else {
       
   286                 throw new FindException("Unrecognized module: " + entry);
       
   287             }
   282 
   288 
   283         } catch (InvalidModuleDescriptorException e) {
   289         } catch (InvalidModuleDescriptorException e) {
   284             throw new FindException("Error reading module: " + entry, e);
   290             throw new FindException("Error reading module: " + entry, e);
   285         }
   291         }
   286     }
   292     }
   290 
   296 
   291     private Set<String> jmodPackages(ZipFile zf) {
   297     private Set<String> jmodPackages(ZipFile zf) {
   292         return zf.stream()
   298         return zf.stream()
   293             .filter(e -> e.getName().startsWith("classes/") &&
   299             .filter(e -> e.getName().startsWith("classes/") &&
   294                     e.getName().endsWith(".class"))
   300                     e.getName().endsWith(".class"))
   295             .map(e -> toPackageName(e))
   301             .map(e -> toPackageName(e.getName().substring(8)))
   296             .filter(pkg -> pkg.length() > 0) // module-info
   302             .filter(pkg -> pkg.length() > 0) // module-info
   297             .distinct()
       
   298             .collect(Collectors.toSet());
   303             .collect(Collectors.toSet());
   299     }
   304     }
   300 
   305 
   301     /**
   306     /**
   302      * Returns a {@code ModuleReference} to a module in jmod file on the
   307      * Returns a {@code ModuleReference} to a module in jmod file on the
   303      * file system.
   308      * file system.
       
   309      *
       
   310      * @throws IOException
       
   311      * @throws InvalidModuleDescriptorException
   304      */
   312      */
   305     private ModuleReference readJMod(Path file) throws IOException {
   313     private ModuleReference readJMod(Path file) throws IOException {
   306         try (ZipFile zf = new ZipFile(file.toString())) {
   314         try (ZipFile zf = new ZipFile(file.toString())) {
   307             ZipEntry ze = zf.getEntry("classes/" + MODULE_INFO);
   315             ZipEntry ze = zf.getEntry("classes/" + MODULE_INFO);
   308             if (ze == null) {
   316             if (ze == null) {
   417         if (vs != null)
   425         if (vs != null)
   418             builder.version(vs);
   426             builder.version(vs);
   419 
   427 
   420         // scan the entries in the JAR file to locate the .class and service
   428         // scan the entries in the JAR file to locate the .class and service
   421         // configuration file
   429         // configuration file
   422         Stream<String> stream = jf.stream()
   430         Map<Boolean, Set<String>> map =
   423             .map(e -> e.getName())
   431             jf.stream()
   424             .filter(e -> (e.endsWith(".class") || e.startsWith(SERVICES_PREFIX)))
   432               .map(JarEntry::getName)
   425             .distinct();
   433               .filter(s -> (s.endsWith(".class") ^ s.startsWith(SERVICES_PREFIX)))
   426         Map<Boolean, Set<String>> map
   434               .collect(Collectors.partitioningBy(s -> s.endsWith(".class"),
   427             = stream.collect(Collectors.partitioningBy(s -> s.endsWith(".class"),
   435                                                  Collectors.toSet()));
   428                              Collectors.toSet()));
       
   429         Set<String> classFiles = map.get(Boolean.TRUE);
   436         Set<String> classFiles = map.get(Boolean.TRUE);
   430         Set<String> configFiles = map.get(Boolean.FALSE);
   437         Set<String> configFiles = map.get(Boolean.FALSE);
   431 
   438 
   432         // all packages are exported
   439         // all packages are exported
   433         classFiles.stream()
   440         classFiles.stream()
   434             .map(c -> toPackageName(c))
   441             .map(c -> toPackageName(c))
   435             .distinct()
   442             .distinct()
   436             .forEach(p -> builder.exports(p));
   443             .forEach(builder::exports);
   437 
   444 
   438         // map names of service configuration files to service names
   445         // map names of service configuration files to service names
   439         Set<String> serviceNames = configFiles.stream()
   446         Set<String> serviceNames = configFiles.stream()
   440             .map(this::toServiceName)
   447             .map(this::toServiceName)
   441             .filter(Optional::isPresent)
   448             .flatMap(Optional::stream)
   442             .map(Optional::get)
       
   443             .collect(Collectors.toSet());
   449             .collect(Collectors.toSet());
   444 
   450 
   445         // parse each service configuration file
   451         // parse each service configuration file
   446         for (String sn : serviceNames) {
   452         for (String sn : serviceNames) {
   447             JarEntry entry = jf.getJarEntry(SERVICES_PREFIX + sn);
   453             JarEntry entry = jf.getJarEntry(SERVICES_PREFIX + sn);
   448             Set<String> providerClasses = new HashSet<>();
   454             Set<String> providerClasses = new LinkedHashSet<>();
   449             try (InputStream in = jf.getInputStream(entry)) {
   455             try (InputStream in = jf.getInputStream(entry)) {
   450                 BufferedReader reader
   456                 BufferedReader reader
   451                     = new BufferedReader(new InputStreamReader(in, "UTF-8"));
   457                     = new BufferedReader(new InputStreamReader(in, "UTF-8"));
   452                 String cn;
   458                 String cn;
   453                 while ((cn = nextLine(reader)) != null) {
   459                 while ((cn = nextLine(reader)) != null) {
   473     }
   479     }
   474 
   480 
   475     private Set<String> jarPackages(JarFile jf) {
   481     private Set<String> jarPackages(JarFile jf) {
   476         return jf.stream()
   482         return jf.stream()
   477             .filter(e -> e.getName().endsWith(".class"))
   483             .filter(e -> e.getName().endsWith(".class"))
   478             .map(e -> toPackageName(e))
   484             .map(e -> toPackageName(e.getName()))
   479             .filter(pkg -> pkg.length() > 0)   // module-info
   485             .filter(pkg -> pkg.length() > 0)   // module-info
   480             .distinct()
       
   481             .collect(Collectors.toSet());
   486             .collect(Collectors.toSet());
   482     }
   487     }
   483 
   488 
   484     /**
   489     /**
   485      * Returns a {@code ModuleReference} to a module in modular JAR file on
   490      * Returns a {@code ModuleReference} to a module in modular JAR file on
   486      * the file system.
   491      * the file system.
       
   492      *
       
   493      * @throws IOException
       
   494      * @throws FindException
       
   495      * @throws InvalidModuleDescriptorException
   487      */
   496      */
   488     private ModuleReference readJar(Path file) throws IOException {
   497     private ModuleReference readJar(Path file) throws IOException {
   489         try (JarFile jf = new JarFile(file.toString())) {
   498         try (JarFile jf = new JarFile(file.toFile(),
   490 
   499                                       true,               // verify
       
   500                                       ZipFile.OPEN_READ,
       
   501                                       JarFile.Release.RUNTIME))
       
   502         {
   491             ModuleDescriptor md;
   503             ModuleDescriptor md;
   492             JarEntry entry = jf.getJarEntry(MODULE_INFO);
   504             JarEntry entry = jf.getJarEntry(MODULE_INFO);
   493             if (entry == null) {
   505             if (entry == null) {
   494 
   506 
   495                 // no module-info.class so treat it as automatic module
   507                 // no module-info.class so treat it as automatic module
   518             return Files.find(dir, Integer.MAX_VALUE,
   530             return Files.find(dir, Integer.MAX_VALUE,
   519                               ((path, attrs) -> attrs.isRegularFile() &&
   531                               ((path, attrs) -> attrs.isRegularFile() &&
   520                                path.toString().endsWith(".class")))
   532                                path.toString().endsWith(".class")))
   521                 .map(path -> toPackageName(dir.relativize(path)))
   533                 .map(path -> toPackageName(dir.relativize(path)))
   522                 .filter(pkg -> pkg.length() > 0)   // module-info
   534                 .filter(pkg -> pkg.length() > 0)   // module-info
   523                 .distinct()
       
   524                 .collect(Collectors.toSet());
   535                 .collect(Collectors.toSet());
   525         } catch (IOException x) {
   536         } catch (IOException x) {
   526             throw new UncheckedIOException(x);
   537             throw new UncheckedIOException(x);
   527         }
   538         }
   528     }
   539     }
   529 
   540 
   530     /**
   541     /**
   531      * Returns a {@code ModuleReference} to an exploded module on the file
   542      * Returns a {@code ModuleReference} to an exploded module on the file
   532      * system or {@code null} if {@code module-info.class} not found.
   543      * system or {@code null} if {@code module-info.class} not found.
       
   544      *
       
   545      * @throws IOException
       
   546      * @throws InvalidModuleDescriptorException
   533      */
   547      */
   534     private ModuleReference readExplodedModule(Path dir) throws IOException {
   548     private ModuleReference readExplodedModule(Path dir) throws IOException {
   535         Path mi = dir.resolve(MODULE_INFO);
   549         Path mi = dir.resolve(MODULE_INFO);
   536         ModuleDescriptor md;
   550         ModuleDescriptor md;
   537         try (InputStream in = Files.newInputStream(mi)) {
   551         try (InputStream in = Files.newInputStream(mi)) {
   557         } else {
   571         } else {
   558             return "";
   572             return "";
   559         }
   573         }
   560     }
   574     }
   561 
   575 
   562     private String toPackageName(ZipEntry entry) {
       
   563         String name = entry.getName();
       
   564         assert name.endsWith(".class");
       
   565         // jmod classes in classes/, jar in /
       
   566         int start = name.startsWith("classes/") ? 8 : 0;
       
   567         int index = name.lastIndexOf("/");
       
   568         if (index > start) {
       
   569             return name.substring(start, index).replace('/', '.');
       
   570         } else {
       
   571             return "";
       
   572         }
       
   573     }
       
   574 
       
   575     private String toPackageName(Path path) {
   576     private String toPackageName(Path path) {
   576         String name = path.toString();
   577         String name = path.toString();
   577         assert name.endsWith(".class");
   578         assert name.endsWith(".class");
   578         int index = name.lastIndexOf(File.separatorChar);
   579         int index = name.lastIndexOf(File.separatorChar);
   579         if (index != -1) {
   580         if (index != -1) {