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
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();
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
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 // true if the found modules should return preview versions of resources
89 // (this must only be set for system modules).
90 private final boolean previewMode;
91
92 // for patching modules, can be null
93 private final ModulePatcher patcher;
94
95 // the entries on this module path
96 private final Path[] entries;
97 private int next;
98
99 // map of module name to module reference map for modules already located
100 private final Map<String, ModuleReference> cachedModules = new HashMap<>();
101
102
103 private ModulePath(Runtime.Version version,
104 boolean isLinkPhase,
105 boolean previewMode,
106 ModulePatcher patcher,
107 Path... entries) {
108 this.releaseVersion = version;
109 this.isLinkPhase = isLinkPhase;
110 this.previewMode = previewMode;
111 this.patcher = patcher;
112 this.entries = entries.clone();
113 for (Path entry : this.entries) {
114 Objects.requireNonNull(entry);
115 }
116 }
117
118 /**
119 * Returns a ModuleFinder for an exploded JDK build where {@code moduleDir}
120 * is the $JAVA_HOME/modules directory. The modules may be patched by the
121 * given ModulePatcher.
122 *
123 * <p>Preview mode is only permitted for system modules, and this method
124 * should only be called from {@link SystemModuleFinders#ofSystem()}.
125 */
126 public static ModuleFinder of(ModulePatcher patcher, boolean previewMode, Path moduleDir) {
127 return new ModulePath(JarFile.runtimeVersion(), false, previewMode, patcher, moduleDir);
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. The modules
133 * may be patched by the given ModulePatcher.
134 */
135 public static ModuleFinder of(ModulePatcher patcher, Path... entries) {
136 return new ModulePath(JarFile.runtimeVersion(), false, false, patcher, entries);
137 }
138
139 /**
140 * Returns a ModuleFinder that locates modules on the file system by
141 * searching a sequence of directories and/or packaged modules.
142 */
143 public static ModuleFinder of(Path... entries) {
144 return of((ModulePatcher)null, entries);
145 }
146
147 /**
148 * Returns a ModuleFinder that locates modules on the file system by
149 * searching a sequence of directories and/or packaged modules.
150 *
151 * @param version The release version to use for multi-release JAR files
152 * @param isLinkPhase {@code true} if the link phase to locate JMOD files
153 */
154 public static ModuleFinder of(Runtime.Version version,
155 boolean isLinkPhase,
156 Path... entries) {
157 return new ModulePath(version, isLinkPhase, false, null, entries);
158 }
159
160
161 @Override
162 public Optional<ModuleReference> find(String name) {
163 Objects.requireNonNull(name);
164
165 // try cached modules
166 ModuleReference m = cachedModules.get(name);
167 if (m != null)
168 return Optional.of(m);
169
170 // the module may not have been encountered yet
171 while (hasNextEntry()) {
172 scanNextEntry();
173 m = cachedModules.get(name);
174 if (m != null)
175 return Optional.of(m);
176 }
177 return Optional.empty();
692 }
693 }
694
695 /**
696 * Returns a {@code ModuleReference} to an exploded module on the file
697 * system or {@code null} if {@code module-info.class} not found.
698 *
699 * @throws IOException
700 * @throws InvalidModuleDescriptorException
701 */
702 private ModuleReference readExplodedModule(Path dir) throws IOException {
703 Path mi = dir.resolve(MODULE_INFO);
704 ModuleInfo.Attributes attrs;
705 try (InputStream in = Files.newInputStream(mi)) {
706 attrs = ModuleInfo.read(new BufferedInputStream(in),
707 () -> explodedPackages(dir));
708 } catch (NoSuchFileException e) {
709 // for now
710 return null;
711 }
712 return ModuleReferences.newExplodedModule(attrs, patcher, previewMode, dir);
713 }
714
715 /**
716 * Maps a type name to its package name.
717 */
718 private static String packageName(String cn) {
719 int index = cn.lastIndexOf('.');
720 return (index == -1) ? "" : cn.substring(0, index);
721 }
722
723 /**
724 * Maps the name of an entry in a JAR or ZIP file to a package name.
725 *
726 * @throws InvalidModuleDescriptorException if the name is a class file in
727 * the top-level directory of the JAR/ZIP file (and it's not
728 * module-info.class)
729 */
730 private Optional<String> toPackageName(String name) {
731 assert !name.endsWith("/");
732 int index = name.lastIndexOf("/");
|