1 /*
2 * Copyright (c) 2014, 2022, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
53 import java.util.Set;
54 import java.util.jar.Attributes;
55 import java.util.jar.JarEntry;
56 import java.util.jar.JarFile;
57 import java.util.jar.Manifest;
58 import java.util.regex.Matcher;
59 import java.util.regex.Pattern;
60 import java.util.stream.Collectors;
61 import java.util.stream.Stream;
62 import java.util.zip.ZipException;
63 import java.util.zip.ZipFile;
64
65 import sun.nio.cs.UTF_8;
66
67 import jdk.internal.jmod.JmodFile;
68 import jdk.internal.jmod.JmodFile.Section;
69 import jdk.internal.perf.PerfCounter;
70
71 /**
72 * A {@code ModuleFinder} that locates modules on the file system by searching
73 * a sequence of directories or packaged modules. The ModuleFinder can be
74 * created to work in either the run-time or link-time phases. In both cases it
75 * locates modular JAR and exploded modules. When created for link-time then it
76 * additionally locates modules in JMOD files. The ModuleFinder can also
77 * optionally patch any modules that it locates with a ModulePatcher.
78 */
79
80 public class ModulePath implements ModuleFinder {
81 private static final String MODULE_INFO = "module-info.class";
82
83 // the version to use for multi-release modular JARs
84 private final Runtime.Version releaseVersion;
85
86 // true for the link phase (supports modules packaged in JMOD format)
87 private final boolean isLinkPhase;
88
89 // for patching modules, can be null
90 private final ModulePatcher patcher;
91
92 // the entries on this module path
93 private final Path[] entries;
94 private int next;
95
96 // map of module name to module reference map for modules already located
97 private final Map<String, ModuleReference> cachedModules = new HashMap<>();
98
99
100 private ModulePath(Runtime.Version version,
101 boolean isLinkPhase,
102 ModulePatcher patcher,
103 Path... entries) {
104 this.releaseVersion = version;
105 this.isLinkPhase = isLinkPhase;
106 this.patcher = patcher;
107 this.entries = entries.clone();
108 for (Path entry : this.entries) {
109 Objects.requireNonNull(entry);
110 }
111 }
112
113 /**
114 * Returns a ModuleFinder that locates modules on the file system by
115 * searching a sequence of directories and/or packaged modules. The modules
116 * may be patched by the given ModulePatcher.
117 */
118 public static ModuleFinder of(ModulePatcher patcher, Path... entries) {
119 return new ModulePath(JarFile.runtimeVersion(), false, patcher, entries);
120 }
121
122 /**
123 * Returns a ModuleFinder that locates modules on the file system by
124 * searching a sequence of directories and/or packaged modules.
125 */
126 public static ModuleFinder of(Path... entries) {
127 return of((ModulePatcher)null, entries);
128 }
129
130 /**
131 * Returns a ModuleFinder that locates modules on the file system by
132 * searching a sequence of directories and/or packaged modules.
133 *
134 * @param version The release version to use for multi-release JAR files
135 * @param isLinkPhase {@code true} if the link phase to locate JMOD files
136 */
137 public static ModuleFinder of(Runtime.Version version,
138 boolean isLinkPhase,
139 Path... entries) {
140 return new ModulePath(version, isLinkPhase, null, entries);
141 }
142
143
144 @Override
145 public Optional<ModuleReference> find(String name) {
146 Objects.requireNonNull(name);
147
148 // try cached modules
149 ModuleReference m = cachedModules.get(name);
150 if (m != null)
151 return Optional.of(m);
152
153 // the module may not have been encountered yet
154 while (hasNextEntry()) {
155 scanNextEntry();
156 m = cachedModules.get(name);
157 if (m != null)
158 return Optional.of(m);
159 }
160 return Optional.empty();
161 }
162
675 }
676 }
677
678 /**
679 * Returns a {@code ModuleReference} to an exploded module on the file
680 * system or {@code null} if {@code module-info.class} not found.
681 *
682 * @throws IOException
683 * @throws InvalidModuleDescriptorException
684 */
685 private ModuleReference readExplodedModule(Path dir) throws IOException {
686 Path mi = dir.resolve(MODULE_INFO);
687 ModuleInfo.Attributes attrs;
688 try (InputStream in = Files.newInputStream(mi)) {
689 attrs = ModuleInfo.read(new BufferedInputStream(in),
690 () -> explodedPackages(dir));
691 } catch (NoSuchFileException e) {
692 // for now
693 return null;
694 }
695 return ModuleReferences.newExplodedModule(attrs, patcher, dir);
696 }
697
698 /**
699 * Maps a type name to its package name.
700 */
701 private static String packageName(String cn) {
702 int index = cn.lastIndexOf('.');
703 return (index == -1) ? "" : cn.substring(0, index);
704 }
705
706 /**
707 * Maps the name of an entry in a JAR or ZIP file to a package name.
708 *
709 * @throws InvalidModuleDescriptorException if the name is a class file in
710 * the top-level directory of the JAR/ZIP file (and it's not
711 * module-info.class)
712 */
713 private Optional<String> toPackageName(String name) {
714 assert !name.endsWith("/");
715 int index = name.lastIndexOf("/");
|
1 /*
2 * Copyright (c) 2014, 2026, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
53 import java.util.Set;
54 import java.util.jar.Attributes;
55 import java.util.jar.JarEntry;
56 import java.util.jar.JarFile;
57 import java.util.jar.Manifest;
58 import java.util.regex.Matcher;
59 import java.util.regex.Pattern;
60 import java.util.stream.Collectors;
61 import java.util.stream.Stream;
62 import java.util.zip.ZipException;
63 import java.util.zip.ZipFile;
64
65 import sun.nio.cs.UTF_8;
66
67 import jdk.internal.jmod.JmodFile;
68 import jdk.internal.jmod.JmodFile.Section;
69 import jdk.internal.perf.PerfCounter;
70
71 /**
72 * A {@code ModuleFinder} that locates modules on the file system by searching
73 * a sequence of directories or packaged modules.
74 *
75 * <p> A ModulePath can be created to locate modules on a module path at run-time or
76 * link-time. In both cases it supports modular JARs and exploded modules. At link-time
77 * it additionally locates modules in JMOD files. The ModuleFinder can also optionally
78 * patch the modules that it locates with a ModulePatcher.
79 *
80 * <p> A ModulePath can also be created to locate modules in a JDK exploded image. It
81 * can optionally patch any modules that it locates with a ModulePatcher, and it can
82 * support preview classes in META-INF/preview when running with preview features enabled.
83 */
84
85 public class ModulePath implements ModuleFinder {
86 private static final String MODULE_INFO = "module-info.class";
87
88 // the version to use for multi-release modular JARs
89 private final Runtime.Version releaseVersion;
90
91 // true for the link phase (supports modules packaged in JMOD format)
92 private final boolean isLinkPhase;
93
94 // for patching modules, can be null
95 private final ModulePatcher patcher;
96
97 // true for a module path to a JDK exploded image and preview features are enabled
98 private final boolean previewMode;
99
100 // the entries on this module path
101 private final Path[] entries;
102 private int next;
103
104 // map of module name to module reference for modules already located
105 private final Map<String, ModuleReference> cachedModules = new HashMap<>();
106
107
108 private ModulePath(Runtime.Version version,
109 boolean isLinkPhase,
110 ModulePatcher patcher,
111 boolean previewMode,
112 Path... entries) {
113 this.releaseVersion = version;
114 this.isLinkPhase = isLinkPhase;
115 this.patcher = patcher;
116 this.previewMode = previewMode;
117 this.entries = entries.clone();
118 for (Path entry : this.entries) {
119 Objects.requireNonNull(entry);
120 }
121 }
122
123 /**
124 * Returns a ModuleFinder that locates modules on the file system by
125 * searching a sequence of directories and/or packaged modules. The modules
126 * may be patched by the given ModulePatcher.
127 */
128 public static ModuleFinder of(ModulePatcher patcher, Path... entries) {
129 return new ModulePath(JarFile.runtimeVersion(), false, patcher, false, entries);
130 }
131
132 /**
133 * Returns a ModuleFinder that locates modules on the file system by
134 * searching a sequence of directories and/or packaged modules.
135 */
136 public static ModuleFinder of(Path... entries) {
137 return of(null, entries);
138 }
139
140 /**
141 * Returns a ModuleFinder that locates modules on the file system by
142 * searching a sequence of directories and/or packaged modules.
143 *
144 * @param version The release version to use for multi-release JAR files
145 * @param isLinkPhase {@code true} if the link phase to locate JMOD files
146 */
147 public static ModuleFinder of(Runtime.Version version,
148 boolean isLinkPhase,
149 Path... entries) {
150 return new ModulePath(version, isLinkPhase, null, false, entries);
151 }
152
153 /**
154 * Returns a ModuleFinder that locates modules in a JDK exploded image.
155 * @param modulesDir the modules directory ($JAVA_HOME/modules)
156 * @param patcher the ModulePatcher or null
157 * @param previewMode true if preview features are enabled
158 */
159 public static ModuleFinder ofExplodedImage(Path modulesDir,
160 ModulePatcher patcher,
161 boolean previewMode) {
162 return new ModulePath(JarFile.runtimeVersion(), // not used for exploded image
163 false,
164 patcher,
165 previewMode,
166 modulesDir);
167 }
168
169 @Override
170 public Optional<ModuleReference> find(String name) {
171 Objects.requireNonNull(name);
172
173 // try cached modules
174 ModuleReference m = cachedModules.get(name);
175 if (m != null)
176 return Optional.of(m);
177
178 // the module may not have been encountered yet
179 while (hasNextEntry()) {
180 scanNextEntry();
181 m = cachedModules.get(name);
182 if (m != null)
183 return Optional.of(m);
184 }
185 return Optional.empty();
186 }
187
700 }
701 }
702
703 /**
704 * Returns a {@code ModuleReference} to an exploded module on the file
705 * system or {@code null} if {@code module-info.class} not found.
706 *
707 * @throws IOException
708 * @throws InvalidModuleDescriptorException
709 */
710 private ModuleReference readExplodedModule(Path dir) throws IOException {
711 Path mi = dir.resolve(MODULE_INFO);
712 ModuleInfo.Attributes attrs;
713 try (InputStream in = Files.newInputStream(mi)) {
714 attrs = ModuleInfo.read(new BufferedInputStream(in),
715 () -> explodedPackages(dir));
716 } catch (NoSuchFileException e) {
717 // for now
718 return null;
719 }
720 return ModuleReferences.newExplodedModule(attrs, patcher, previewMode, dir);
721 }
722
723 /**
724 * Maps a type name to its package name.
725 */
726 private static String packageName(String cn) {
727 int index = cn.lastIndexOf('.');
728 return (index == -1) ? "" : cn.substring(0, index);
729 }
730
731 /**
732 * Maps the name of an entry in a JAR or ZIP file to a package name.
733 *
734 * @throws InvalidModuleDescriptorException if the name is a class file in
735 * the top-level directory of the JAR/ZIP file (and it's not
736 * module-info.class)
737 */
738 private Optional<String> toPackageName(String name) {
739 assert !name.endsWith("/");
740 int index = name.lastIndexOf("/");
|