Path: blob/master/src/java.base/share/classes/jdk/internal/module/SystemModuleFinders.java
41159 views
/*1* Copyright (c) 2015, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/24package jdk.internal.module;2526import java.io.ByteArrayInputStream;27import java.io.IOException;28import java.io.InputStream;29import java.io.UncheckedIOException;30import java.lang.module.ModuleDescriptor;31import java.lang.module.ModuleFinder;32import java.lang.module.ModuleReader;33import java.lang.module.ModuleReference;34import java.lang.reflect.Constructor;35import java.net.URI;36import java.net.URLConnection;37import java.nio.ByteBuffer;38import java.nio.file.Files;39import java.nio.file.Path;40import java.security.AccessController;41import java.security.PrivilegedAction;42import java.util.ArrayDeque;43import java.util.Collections;44import java.util.Deque;45import java.util.HashMap;46import java.util.HashSet;47import java.util.Iterator;48import java.util.Map;49import java.util.Objects;50import java.util.Optional;51import java.util.Set;52import java.util.Spliterator;53import java.util.function.Consumer;54import java.util.function.Supplier;55import java.util.stream.Stream;56import java.util.stream.StreamSupport;5758import jdk.internal.jimage.ImageLocation;59import jdk.internal.jimage.ImageReader;60import jdk.internal.jimage.ImageReaderFactory;61import jdk.internal.access.JavaNetUriAccess;62import jdk.internal.access.SharedSecrets;63import jdk.internal.util.StaticProperty;64import jdk.internal.module.ModuleHashes.HashSupplier;6566/**67* The factory for SystemModules objects and for creating ModuleFinder objects68* that find modules in the runtime image.69*70* This class supports initializing the module system when the runtime is an71* images build, an exploded build, or an images build with java.base patched72* by an exploded java.base. It also supports a testing mode that re-parses73* the module-info.class resources in the run-time image.74*/7576public final class SystemModuleFinders {77private static final JavaNetUriAccess JNUA = SharedSecrets.getJavaNetUriAccess();7879private static final boolean USE_FAST_PATH;80static {81String value = System.getProperty("jdk.system.module.finder.disableFastPath");82if (value == null) {83USE_FAST_PATH = true;84} else {85USE_FAST_PATH = !value.isEmpty() && !Boolean.parseBoolean(value);86}87}8889// cached ModuleFinder returned from ofSystem90private static volatile ModuleFinder cachedSystemModuleFinder;9192private SystemModuleFinders() { }9394/**95* Returns the SystemModules object to reconstitute all modules. Returns96* null if this is an exploded build or java.base is patched by an exploded97* build.98*/99static SystemModules allSystemModules() {100if (USE_FAST_PATH) {101return SystemModulesMap.allSystemModules();102} else {103return null;104}105}106107/**108* Returns a SystemModules object to reconstitute the modules for the109* given initial module. If the initial module is null then return the110* SystemModules object to reconstitute the default modules.111*112* Return null if there is no SystemModules class for the initial module,113* this is an exploded build, or java.base is patched by an exploded build.114*/115static SystemModules systemModules(String initialModule) {116if (USE_FAST_PATH) {117if (initialModule == null) {118return SystemModulesMap.defaultSystemModules();119}120121String[] initialModules = SystemModulesMap.moduleNames();122for (int i = 0; i < initialModules.length; i++) {123String moduleName = initialModules[i];124if (initialModule.equals(moduleName)) {125String cn = SystemModulesMap.classNames()[i];126try {127// one-arg Class.forName as java.base may not be defined128Constructor<?> ctor = Class.forName(cn).getConstructor();129return (SystemModules) ctor.newInstance();130} catch (Exception e) {131throw new InternalError(e);132}133}134}135}136return null;137}138139/**140* Returns a ModuleFinder that is backed by the given SystemModules object.141*142* @apiNote The returned ModuleFinder is thread safe.143*/144static ModuleFinder of(SystemModules systemModules) {145ModuleDescriptor[] descriptors = systemModules.moduleDescriptors();146ModuleTarget[] targets = systemModules.moduleTargets();147ModuleHashes[] recordedHashes = systemModules.moduleHashes();148ModuleResolution[] moduleResolutions = systemModules.moduleResolutions();149150int moduleCount = descriptors.length;151ModuleReference[] mrefs = new ModuleReference[moduleCount];152@SuppressWarnings(value = {"rawtypes", "unchecked"})153Map.Entry<String, ModuleReference>[] map154= (Map.Entry<String, ModuleReference>[])new Map.Entry[moduleCount];155156Map<String, byte[]> nameToHash = generateNameToHash(recordedHashes);157158for (int i = 0; i < moduleCount; i++) {159String name = descriptors[i].name();160HashSupplier hashSupplier = hashSupplier(nameToHash, name);161ModuleReference mref = toModuleReference(descriptors[i],162targets[i],163recordedHashes[i],164hashSupplier,165moduleResolutions[i]);166mrefs[i] = mref;167map[i] = Map.entry(name, mref);168}169170return new SystemModuleFinder(mrefs, map);171}172173/**174* Returns the ModuleFinder to find all system modules. Supports both175* images and exploded builds.176*177* @apiNote Used by ModuleFinder.ofSystem()178*/179public static ModuleFinder ofSystem() {180ModuleFinder finder = cachedSystemModuleFinder;181if (finder != null) {182return finder;183}184185// probe to see if this is an images build186String home = StaticProperty.javaHome();187Path modules = Path.of(home, "lib", "modules");188if (Files.isRegularFile(modules)) {189if (USE_FAST_PATH) {190SystemModules systemModules = allSystemModules();191if (systemModules != null) {192finder = of(systemModules);193}194}195196// fall back to parsing the module-info.class files in image197if (finder == null) {198finder = ofModuleInfos();199}200201cachedSystemModuleFinder = finder;202return finder;203204}205206// exploded build (do not cache module finder)207Path dir = Path.of(home, "modules");208if (!Files.isDirectory(dir))209throw new InternalError("Unable to detect the run-time image");210ModuleFinder f = ModulePath.of(ModuleBootstrap.patcher(), dir);211return new ModuleFinder() {212@SuppressWarnings("removal")213@Override214public Optional<ModuleReference> find(String name) {215PrivilegedAction<Optional<ModuleReference>> pa = () -> f.find(name);216return AccessController.doPrivileged(pa);217}218@SuppressWarnings("removal")219@Override220public Set<ModuleReference> findAll() {221PrivilegedAction<Set<ModuleReference>> pa = f::findAll;222return AccessController.doPrivileged(pa);223}224};225}226227/**228* Parses the module-info.class of all module in the runtime image and229* returns a ModuleFinder to find the modules.230*231* @apiNote The returned ModuleFinder is thread safe.232*/233private static ModuleFinder ofModuleInfos() {234// parse the module-info.class in every module235Map<String, ModuleInfo.Attributes> nameToAttributes = new HashMap<>();236Map<String, byte[]> nameToHash = new HashMap<>();237ImageReader reader = SystemImage.reader();238for (String mn : reader.getModuleNames()) {239ImageLocation loc = reader.findLocation(mn, "module-info.class");240ModuleInfo.Attributes attrs241= ModuleInfo.read(reader.getResourceBuffer(loc), null);242243nameToAttributes.put(mn, attrs);244ModuleHashes hashes = attrs.recordedHashes();245if (hashes != null) {246for (String name : hashes.names()) {247nameToHash.computeIfAbsent(name, k -> hashes.hashFor(name));248}249}250}251252// create a ModuleReference for each module253Set<ModuleReference> mrefs = new HashSet<>();254Map<String, ModuleReference> nameToModule = new HashMap<>();255for (Map.Entry<String, ModuleInfo.Attributes> e : nameToAttributes.entrySet()) {256String mn = e.getKey();257ModuleInfo.Attributes attrs = e.getValue();258HashSupplier hashSupplier = hashSupplier(nameToHash, mn);259ModuleReference mref = toModuleReference(attrs.descriptor(),260attrs.target(),261attrs.recordedHashes(),262hashSupplier,263attrs.moduleResolution());264mrefs.add(mref);265nameToModule.put(mn, mref);266}267268return new SystemModuleFinder(mrefs, nameToModule);269}270271/**272* A ModuleFinder that finds module in an array or set of modules.273*/274private static class SystemModuleFinder implements ModuleFinder {275final Set<ModuleReference> mrefs;276final Map<String, ModuleReference> nameToModule;277278SystemModuleFinder(ModuleReference[] array,279Map.Entry<String, ModuleReference>[] map) {280this.mrefs = Set.of(array);281this.nameToModule = Map.ofEntries(map);282}283284SystemModuleFinder(Set<ModuleReference> mrefs,285Map<String, ModuleReference> nameToModule) {286this.mrefs = Set.copyOf(mrefs);287this.nameToModule = Map.copyOf(nameToModule);288}289290@Override291public Optional<ModuleReference> find(String name) {292Objects.requireNonNull(name);293return Optional.ofNullable(nameToModule.get(name));294}295296@Override297public Set<ModuleReference> findAll() {298return mrefs;299}300}301302/**303* Creates a ModuleReference to the system module.304*/305static ModuleReference toModuleReference(ModuleDescriptor descriptor,306ModuleTarget target,307ModuleHashes recordedHashes,308HashSupplier hasher,309ModuleResolution mres) {310String mn = descriptor.name();311URI uri = JNUA.create("jrt", "/".concat(mn));312313Supplier<ModuleReader> readerSupplier = new Supplier<>() {314@Override315public ModuleReader get() {316return new SystemModuleReader(mn, uri);317}318};319320ModuleReference mref = new ModuleReferenceImpl(descriptor,321uri,322readerSupplier,323null,324target,325recordedHashes,326hasher,327mres);328329// may need a reference to a patched module if --patch-module specified330mref = ModuleBootstrap.patcher().patchIfNeeded(mref);331332return mref;333}334335/**336* Generates a map of module name to hash value.337*/338static Map<String, byte[]> generateNameToHash(ModuleHashes[] recordedHashes) {339Map<String, byte[]> nameToHash = null;340341boolean secondSeen = false;342// record the hashes to build HashSupplier343for (ModuleHashes mh : recordedHashes) {344if (mh != null) {345// if only one module contain ModuleHashes, use it346if (nameToHash == null) {347nameToHash = mh.hashes();348} else {349if (!secondSeen) {350nameToHash = new HashMap<>(nameToHash);351secondSeen = true;352}353nameToHash.putAll(mh.hashes());354}355}356}357return (nameToHash != null) ? nameToHash : Map.of();358}359360/**361* Returns a HashSupplier that returns the hash of the given module.362*/363static HashSupplier hashSupplier(Map<String, byte[]> nameToHash, String name) {364byte[] hash = nameToHash.get(name);365if (hash != null) {366// avoid lambda here367return new HashSupplier() {368@Override369public byte[] generate(String algorithm) {370return hash;371}372};373} else {374return null;375}376}377378/**379* Holder class for the ImageReader380*381* @apiNote This class must be loaded before a security manager is set.382*/383private static class SystemImage {384static final ImageReader READER = ImageReaderFactory.getImageReader();385static ImageReader reader() {386return READER;387}388}389390/**391* A ModuleReader for reading resources from a module linked into the392* run-time image.393*/394private static class SystemModuleReader implements ModuleReader {395private final String module;396private volatile boolean closed;397398/**399* If there is a security manager set then check permission to400* connect to the run-time image.401*/402private static void checkPermissionToConnect(URI uri) {403@SuppressWarnings("removal")404SecurityManager sm = System.getSecurityManager();405if (sm != null) {406try {407URLConnection uc = uri.toURL().openConnection();408sm.checkPermission(uc.getPermission());409} catch (IOException ioe) {410throw new UncheckedIOException(ioe);411}412}413}414415SystemModuleReader(String module, URI uri) {416checkPermissionToConnect(uri);417this.module = module;418}419420/**421* Returns the ImageLocation for the given resource, {@code null}422* if not found.423*/424private ImageLocation findImageLocation(String name) throws IOException {425Objects.requireNonNull(name);426if (closed)427throw new IOException("ModuleReader is closed");428ImageReader imageReader = SystemImage.reader();429if (imageReader != null) {430return imageReader.findLocation(module, name);431} else {432// not an images build433return null;434}435}436437/**438* Returns {@code true} if the given resource exists, {@code false}439* if not found.440*/441private boolean containsImageLocation(String name) throws IOException {442Objects.requireNonNull(name);443if (closed)444throw new IOException("ModuleReader is closed");445ImageReader imageReader = SystemImage.reader();446if (imageReader != null) {447return imageReader.verifyLocation(module, name);448} else {449// not an images build450return false;451}452}453454@Override455public Optional<URI> find(String name) throws IOException {456if (containsImageLocation(name)) {457URI u = JNUA.create("jrt", "/" + module + "/" + name);458return Optional.of(u);459} else {460return Optional.empty();461}462}463464@Override465public Optional<InputStream> open(String name) throws IOException {466return read(name).map(this::toInputStream);467}468469private InputStream toInputStream(ByteBuffer bb) { // ## -> ByteBuffer?470try {471int rem = bb.remaining();472byte[] bytes = new byte[rem];473bb.get(bytes);474return new ByteArrayInputStream(bytes);475} finally {476release(bb);477}478}479480@Override481public Optional<ByteBuffer> read(String name) throws IOException {482ImageLocation location = findImageLocation(name);483if (location != null) {484return Optional.of(SystemImage.reader().getResourceBuffer(location));485} else {486return Optional.empty();487}488}489490@Override491public void release(ByteBuffer bb) {492Objects.requireNonNull(bb);493ImageReader.releaseByteBuffer(bb);494}495496@Override497public Stream<String> list() throws IOException {498if (closed)499throw new IOException("ModuleReader is closed");500501Spliterator<String> s = new ModuleContentSpliterator(module);502return StreamSupport.stream(s, false);503}504505@Override506public void close() {507// nothing else to do508closed = true;509}510}511512/**513* A Spliterator for traversing the resources of a module linked into the514* run-time image.515*/516private static class ModuleContentSpliterator implements Spliterator<String> {517final String moduleRoot;518final Deque<ImageReader.Node> stack;519Iterator<ImageReader.Node> iterator;520521ModuleContentSpliterator(String module) throws IOException {522moduleRoot = "/modules/" + module;523stack = new ArrayDeque<>();524525// push the root node to the stack to get started526ImageReader.Node dir = SystemImage.reader().findNode(moduleRoot);527if (dir == null || !dir.isDirectory())528throw new IOException(moduleRoot + " not a directory");529stack.push(dir);530iterator = Collections.emptyIterator();531}532533/**534* Returns the name of the next non-directory node or {@code null} if535* there are no remaining nodes to visit.536*/537private String next() throws IOException {538for (;;) {539while (iterator.hasNext()) {540ImageReader.Node node = iterator.next();541String name = node.getName();542if (node.isDirectory()) {543// build node544ImageReader.Node dir = SystemImage.reader().findNode(name);545assert dir.isDirectory();546stack.push(dir);547} else {548// strip /modules/$MODULE/ prefix549return name.substring(moduleRoot.length() + 1);550}551}552553if (stack.isEmpty()) {554return null;555} else {556ImageReader.Node dir = stack.poll();557assert dir.isDirectory();558iterator = dir.getChildren().iterator();559}560}561}562563@Override564public boolean tryAdvance(Consumer<? super String> action) {565String next;566try {567next = next();568} catch (IOException ioe) {569throw new UncheckedIOException(ioe);570}571if (next != null) {572action.accept(next);573return true;574} else {575return false;576}577}578579@Override580public Spliterator<String> trySplit() {581return null;582}583584@Override585public int characteristics() {586return Spliterator.DISTINCT + Spliterator.NONNULL + Spliterator.IMMUTABLE;587}588589@Override590public long estimateSize() {591return Long.MAX_VALUE;592}593}594}595596597