1 /*
2  * Copyright (c) 2015, 2018, 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
23  * questions.
24  */
25 package jdk.internal.module;
26 
27 import java.io.ByteArrayInputStream;
28 import java.io.IOException;
29 import java.io.InputStream;
30 import java.io.UncheckedIOException;
31 import java.lang.module.ModuleDescriptor;
32 import java.lang.module.ModuleFinder;
33 import java.lang.module.ModuleReader;
34 import java.lang.module.ModuleReference;
35 import java.lang.reflect.Constructor;
36 import java.net.URI;
37 import java.net.URLConnection;
38 import java.nio.ByteBuffer;
39 import java.nio.file.Files;
40 import java.nio.file.Path;
41 import java.security.AccessController;
42 import java.security.PrivilegedAction;
43 import java.util.ArrayDeque;
44 import java.util.Collections;
45 import java.util.Deque;
46 import java.util.HashMap;
47 import java.util.HashSet;
48 import java.util.Iterator;
49 import java.util.Map;
50 import java.util.Objects;
51 import java.util.Optional;
52 import java.util.Set;
53 import java.util.Spliterator;
54 import java.util.function.Consumer;
55 import java.util.function.Supplier;
56 import java.util.stream.Stream;
57 import java.util.stream.StreamSupport;
58 
59 import jdk.internal.jimage.ImageLocation;
60 import jdk.internal.jimage.ImageReader;
61 import jdk.internal.jimage.ImageReaderFactory;
62 import jdk.internal.misc.JavaNetUriAccess;
63 import jdk.internal.misc.SharedSecrets;
64 import jdk.internal.util.StaticProperty;
65 import jdk.internal.module.ModuleHashes.HashSupplier;
66 
67 /**
68  * The factory for SystemModules objects and for creating ModuleFinder objects
69  * that find modules in the runtime image.
70  *
71  * This class supports initializing the module system when the runtime is an
72  * images build, an exploded build, or an images build with java.base patched
73  * by an exploded java.base. It also supports a testing mode that re-parses
74  * the module-info.class resources in the run-time image.
75  */
76 
77 public final class SystemModuleFinders {
78     private static final JavaNetUriAccess JNUA = SharedSecrets.getJavaNetUriAccess();
79 
80     private static final boolean USE_FAST_PATH;
81     static {
82         String value = System.getProperty("jdk.system.module.finder.disableFastPath");
83         if (value == null) {
84             USE_FAST_PATH = true;
85         } else {
86             USE_FAST_PATH = !value.isEmpty() && !Boolean.parseBoolean(value);
87         }
88     }
89 
90     // cached ModuleFinder returned from ofSystem
91     private static volatile ModuleFinder cachedSystemModuleFinder;
92 
SystemModuleFinders()93     private SystemModuleFinders() { }
94 
95     /**
96      * Returns the SystemModules object to reconstitute all modules. Returns
97      * null if this is an exploded build or java.base is patched by an exploded
98      * build.
99      */
allSystemModules()100     static SystemModules allSystemModules() {
101         if (USE_FAST_PATH) {
102             return SystemModulesMap.allSystemModules();
103         } else {
104             return null;
105         }
106     }
107 
108     /**
109      * Returns a SystemModules object to reconstitute the modules for the
110      * given initial module. If the initial module is null then return the
111      * SystemModules object to reconstitute the default modules.
112      *
113      * Return null if there is no SystemModules class for the initial module,
114      * this is an exploded build, or java.base is patched by an exploded build.
115      */
systemModules(String initialModule)116     static SystemModules systemModules(String initialModule) {
117         if (USE_FAST_PATH) {
118             if (initialModule == null) {
119                 return SystemModulesMap.defaultSystemModules();
120             }
121 
122             String[] initialModules = SystemModulesMap.moduleNames();
123             for (int i = 0; i < initialModules.length; i++) {
124                 String moduleName = initialModules[i];
125                 if (initialModule.equals(moduleName)) {
126                     String cn = SystemModulesMap.classNames()[i];
127                     try {
128                         // one-arg Class.forName as java.base may not be defined
129                         Constructor<?> ctor = Class.forName(cn).getConstructor();
130                         return (SystemModules) ctor.newInstance();
131                     } catch (Exception e) {
132                         throw new InternalError(e);
133                     }
134                 }
135             }
136         }
137         return null;
138     }
139 
140     /**
141      * Returns a ModuleFinder that is backed by the given SystemModules object.
142      *
143      * @apiNote The returned ModuleFinder is thread safe.
144      */
of(SystemModules systemModules)145     static ModuleFinder of(SystemModules systemModules) {
146         ModuleDescriptor[] descriptors = systemModules.moduleDescriptors();
147         ModuleTarget[] targets = systemModules.moduleTargets();
148         ModuleHashes[] recordedHashes = systemModules.moduleHashes();
149         ModuleResolution[] moduleResolutions = systemModules.moduleResolutions();
150 
151         int moduleCount = descriptors.length;
152         ModuleReference[] mrefs = new ModuleReference[moduleCount];
153         @SuppressWarnings(value = {"rawtypes", "unchecked"})
154         Map.Entry<String, ModuleReference>[] map
155             = (Map.Entry<String, ModuleReference>[])new Map.Entry[moduleCount];
156 
157         Map<String, byte[]> nameToHash = generateNameToHash(recordedHashes);
158 
159         for (int i = 0; i < moduleCount; i++) {
160             String name = descriptors[i].name();
161             HashSupplier hashSupplier = hashSupplier(nameToHash, name);
162             ModuleReference mref = toModuleReference(descriptors[i],
163                                                      targets[i],
164                                                      recordedHashes[i],
165                                                      hashSupplier,
166                                                      moduleResolutions[i]);
167             mrefs[i] = mref;
168             map[i] = Map.entry(name, mref);
169         }
170 
171         return new SystemModuleFinder(mrefs, map);
172     }
173 
174     /**
175      * Returns the ModuleFinder to find all system modules. Supports both
176      * images and exploded builds.
177      *
178      * @apiNote Used by ModuleFinder.ofSystem()
179      */
ofSystem()180     public static ModuleFinder ofSystem() {
181         ModuleFinder finder = cachedSystemModuleFinder;
182         if (finder != null) {
183             return finder;
184         }
185 
186         // probe to see if this is an images build
187         String home = StaticProperty.javaHome();
188         Path modules = Path.of(home, "lib", "modules");
189         if (Files.isRegularFile(modules)) {
190             if (USE_FAST_PATH) {
191                 SystemModules systemModules = allSystemModules();
192                 if (systemModules != null) {
193                     finder = of(systemModules);
194                 }
195             }
196 
197             // fall back to parsing the module-info.class files in image
198             if (finder == null) {
199                 finder = ofModuleInfos();
200             }
201 
202             cachedSystemModuleFinder = finder;
203             return finder;
204 
205         }
206 
207         // exploded build (do not cache module finder)
208         Path dir = Path.of(home, "modules");
209         if (!Files.isDirectory(dir))
210             throw new InternalError("Unable to detect the run-time image");
211         ModuleFinder f = ModulePath.of(ModuleBootstrap.patcher(), dir);
212         return new ModuleFinder() {
213             @Override
214             public Optional<ModuleReference> find(String name) {
215                 PrivilegedAction<Optional<ModuleReference>> pa = () -> f.find(name);
216                 return AccessController.doPrivileged(pa);
217             }
218             @Override
219             public Set<ModuleReference> findAll() {
220                 PrivilegedAction<Set<ModuleReference>> pa = f::findAll;
221                 return AccessController.doPrivileged(pa);
222             }
223         };
224     }
225 
226     /**
227      * Parses the module-info.class of all module in the runtime image and
228      * returns a ModuleFinder to find the modules.
229      *
230      * @apiNote The returned ModuleFinder is thread safe.
231      */
232     private static ModuleFinder ofModuleInfos() {
233         // parse the module-info.class in every module
234         Map<String, ModuleInfo.Attributes> nameToAttributes = new HashMap<>();
235         Map<String, byte[]> nameToHash = new HashMap<>();
236         ImageReader reader = SystemImage.reader();
237         for (String mn : reader.getModuleNames()) {
238             ImageLocation loc = reader.findLocation(mn, "module-info.class");
239             ModuleInfo.Attributes attrs
240                 = ModuleInfo.read(reader.getResourceBuffer(loc), null);
241 
242             nameToAttributes.put(mn, attrs);
243             ModuleHashes hashes = attrs.recordedHashes();
244             if (hashes != null) {
245                 for (String name : hashes.names()) {
246                     nameToHash.computeIfAbsent(name, k -> hashes.hashFor(name));
247                 }
248             }
249         }
250 
251         // create a ModuleReference for each module
252         Set<ModuleReference> mrefs = new HashSet<>();
253         Map<String, ModuleReference> nameToModule = new HashMap<>();
254         for (Map.Entry<String, ModuleInfo.Attributes> e : nameToAttributes.entrySet()) {
255             String mn = e.getKey();
256             ModuleInfo.Attributes attrs = e.getValue();
257             HashSupplier hashSupplier = hashSupplier(nameToHash, mn);
258             ModuleReference mref = toModuleReference(attrs.descriptor(),
259                                                      attrs.target(),
260                                                      attrs.recordedHashes(),
261                                                      hashSupplier,
262                                                      attrs.moduleResolution());
263             mrefs.add(mref);
264             nameToModule.put(mn, mref);
265         }
266 
267         return new SystemModuleFinder(mrefs, nameToModule);
268     }
269 
270     /**
271      * A ModuleFinder that finds module in an array or set of modules.
272      */
273     private static class SystemModuleFinder implements ModuleFinder {
274         final Set<ModuleReference> mrefs;
275         final Map<String, ModuleReference> nameToModule;
276 
277         SystemModuleFinder(ModuleReference[] array,
278                            Map.Entry<String, ModuleReference>[] map) {
279             this.mrefs = Set.of(array);
280             this.nameToModule = Map.ofEntries(map);
281         }
282 
283         SystemModuleFinder(Set<ModuleReference> mrefs,
284                            Map<String, ModuleReference> nameToModule) {
285             this.mrefs = Set.copyOf(mrefs);
286             this.nameToModule = Map.copyOf(nameToModule);
287         }
288 
289         @Override
290         public Optional<ModuleReference> find(String name) {
291             Objects.requireNonNull(name);
292             return Optional.ofNullable(nameToModule.get(name));
293         }
294 
295         @Override
296         public Set<ModuleReference> findAll() {
297             return mrefs;
298         }
299     }
300 
301     /**
302      * Creates a ModuleReference to the system module.
303      */
304     static ModuleReference toModuleReference(ModuleDescriptor descriptor,
305                                              ModuleTarget target,
306                                              ModuleHashes recordedHashes,
307                                              HashSupplier hasher,
308                                              ModuleResolution mres) {
309         String mn = descriptor.name();
310         URI uri = JNUA.create("jrt", "/".concat(mn));
311 
312         Supplier<ModuleReader> readerSupplier = new Supplier<>() {
313             @Override
314             public ModuleReader get() {
315                 return new SystemModuleReader(mn, uri);
316             }
317         };
318 
319         ModuleReference mref = new ModuleReferenceImpl(descriptor,
320                                                        uri,
321                                                        readerSupplier,
322                                                        null,
323                                                        target,
324                                                        recordedHashes,
325                                                        hasher,
326                                                        mres);
327 
328         // may need a reference to a patched module if --patch-module specified
329         mref = ModuleBootstrap.patcher().patchIfNeeded(mref);
330 
331         return mref;
332     }
333 
334     /**
335      * Generates a map of module name to hash value.
336      */
337     static Map<String, byte[]> generateNameToHash(ModuleHashes[] recordedHashes) {
338         Map<String, byte[]> nameToHash = null;
339 
340         boolean secondSeen = false;
341         // record the hashes to build HashSupplier
342         for (ModuleHashes mh : recordedHashes) {
343             if (mh != null) {
344                 // if only one module contain ModuleHashes, use it
345                 if (nameToHash == null) {
346                     nameToHash = mh.hashes();
347                 } else {
348                     if (!secondSeen) {
349                         nameToHash = new HashMap<>(nameToHash);
350                         secondSeen = true;
351                     }
352                     nameToHash.putAll(mh.hashes());
353                 }
354             }
355         }
356         return (nameToHash != null) ? nameToHash : Map.of();
357     }
358 
359     /**
360      * Returns a HashSupplier that returns the hash of the given module.
361      */
362     static HashSupplier hashSupplier(Map<String, byte[]> nameToHash, String name) {
363         byte[] hash = nameToHash.get(name);
364         if (hash != null) {
365             // avoid lambda here
366             return new HashSupplier() {
367                 @Override
368                 public byte[] generate(String algorithm) {
369                     return hash;
370                 }
371             };
372         } else {
373             return null;
374         }
375     }
376 
377     /**
378      * Holder class for the ImageReader
379      *
380      * @apiNote This class must be loaded before a security manager is set.
381      */
382     private static class SystemImage {
383         static final ImageReader READER = ImageReaderFactory.getImageReader();
384         static ImageReader reader() {
385             return READER;
386         }
387     }
388 
389     /**
390      * A ModuleReader for reading resources from a module linked into the
391      * run-time image.
392      */
393     private static class SystemModuleReader implements ModuleReader {
394         private final String module;
395         private volatile boolean closed;
396 
397         /**
398          * If there is a security manager set then check permission to
399          * connect to the run-time image.
400          */
401         private static void checkPermissionToConnect(URI uri) {
402             SecurityManager sm = System.getSecurityManager();
403             if (sm != null) {
404                 try {
405                     URLConnection uc = uri.toURL().openConnection();
406                     sm.checkPermission(uc.getPermission());
407                 } catch (IOException ioe) {
408                     throw new UncheckedIOException(ioe);
409                 }
410             }
411         }
412 
413         SystemModuleReader(String module, URI uri) {
414             checkPermissionToConnect(uri);
415             this.module = module;
416         }
417 
418         /**
419          * Returns the ImageLocation for the given resource, {@code null}
420          * if not found.
421          */
422         private ImageLocation findImageLocation(String name) throws IOException {
423             Objects.requireNonNull(name);
424             if (closed)
425                 throw new IOException("ModuleReader is closed");
426             ImageReader imageReader = SystemImage.reader();
427             if (imageReader != null) {
428                 return imageReader.findLocation(module, name);
429             } else {
430                 // not an images build
431                 return null;
432             }
433         }
434 
435         @Override
436         public Optional<URI> find(String name) throws IOException {
437             ImageLocation location = findImageLocation(name);
438             if (location != null) {
439                 URI u = URI.create("jrt:/" + module + "/" + name);
440                 return Optional.of(u);
441             } else {
442                 return Optional.empty();
443             }
444         }
445 
446         @Override
447         public Optional<InputStream> open(String name) throws IOException {
448             return read(name).map(this::toInputStream);
449         }
450 
451         private InputStream toInputStream(ByteBuffer bb) { // ## -> ByteBuffer?
452             try {
453                 int rem = bb.remaining();
454                 byte[] bytes = new byte[rem];
455                 bb.get(bytes);
456                 return new ByteArrayInputStream(bytes);
457             } finally {
458                 release(bb);
459             }
460         }
461 
462         @Override
463         public Optional<ByteBuffer> read(String name) throws IOException {
464             ImageLocation location = findImageLocation(name);
465             if (location != null) {
466                 return Optional.of(SystemImage.reader().getResourceBuffer(location));
467             } else {
468                 return Optional.empty();
469             }
470         }
471 
472         @Override
473         public void release(ByteBuffer bb) {
474             Objects.requireNonNull(bb);
475             ImageReader.releaseByteBuffer(bb);
476         }
477 
478         @Override
479         public Stream<String> list() throws IOException {
480             if (closed)
481                 throw new IOException("ModuleReader is closed");
482 
483             Spliterator<String> s = new ModuleContentSpliterator(module);
484             return StreamSupport.stream(s, false);
485         }
486 
487         @Override
488         public void close() {
489             // nothing else to do
490             closed = true;
491         }
492     }
493 
494     /**
495      * A Spliterator for traversing the resources of a module linked into the
496      * run-time image.
497      */
498     private static class ModuleContentSpliterator implements Spliterator<String> {
499         final String moduleRoot;
500         final Deque<ImageReader.Node> stack;
501         Iterator<ImageReader.Node> iterator;
502 
503         ModuleContentSpliterator(String module) throws IOException {
504             moduleRoot = "/modules/" + module;
505             stack = new ArrayDeque<>();
506 
507             // push the root node to the stack to get started
508             ImageReader.Node dir = SystemImage.reader().findNode(moduleRoot);
509             if (dir == null || !dir.isDirectory())
510                 throw new IOException(moduleRoot + " not a directory");
511             stack.push(dir);
512             iterator = Collections.emptyIterator();
513         }
514 
515         /**
516          * Returns the name of the next non-directory node or {@code null} if
517          * there are no remaining nodes to visit.
518          */
519         private String next() throws IOException {
520             for (;;) {
521                 while (iterator.hasNext()) {
522                     ImageReader.Node node = iterator.next();
523                     String name = node.getName();
524                     if (node.isDirectory()) {
525                         // build node
526                         ImageReader.Node dir = SystemImage.reader().findNode(name);
527                         assert dir.isDirectory();
528                         stack.push(dir);
529                     } else {
530                         // strip /modules/$MODULE/ prefix
531                         return name.substring(moduleRoot.length() + 1);
532                     }
533                 }
534 
535                 if (stack.isEmpty()) {
536                     return null;
537                 } else {
538                     ImageReader.Node dir = stack.poll();
539                     assert dir.isDirectory();
540                     iterator = dir.getChildren().iterator();
541                 }
542             }
543         }
544 
545         @Override
546         public boolean tryAdvance(Consumer<? super String> action) {
547             String next;
548             try {
549                 next = next();
550             } catch (IOException ioe) {
551                 throw new UncheckedIOException(ioe);
552             }
553             if (next != null) {
554                 action.accept(next);
555                 return true;
556             } else {
557                 return false;
558             }
559         }
560 
561         @Override
562         public Spliterator<String> trySplit() {
563             return null;
564         }
565 
566         @Override
567         public int characteristics() {
568             return Spliterator.DISTINCT + Spliterator.NONNULL + Spliterator.IMMUTABLE;
569         }
570 
571         @Override
572         public long estimateSize() {
573             return Long.MAX_VALUE;
574         }
575     }
576 }
577