1 /*
   2  * Copyright (c) 2015, 2016, 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 
  26 package java.lang.module;
  27 
  28 import java.io.ByteArrayInputStream;
  29 import java.io.IOException;
  30 import java.io.InputStream;
  31 import java.io.UncheckedIOException;
  32 import java.net.URI;
  33 import java.net.URLConnection;
  34 import java.nio.ByteBuffer;
  35 import java.util.Collections;
  36 import java.util.HashMap;
  37 import java.util.HashSet;
  38 import java.util.Map;
  39 import java.util.Objects;
  40 import java.util.Optional;
  41 import java.util.Set;
  42 import java.util.function.Supplier;
  43 
  44 import jdk.internal.jimage.ImageLocation;
  45 import jdk.internal.jimage.ImageReader;
  46 import jdk.internal.jimage.ImageReaderFactory;
  47 import jdk.internal.misc.JavaNetAccess;
  48 import jdk.internal.misc.SharedSecrets;
  49 import jdk.internal.module.ModuleHashes;
  50 import jdk.internal.module.ModuleHashes.HashSupplier;
  51 import jdk.internal.module.SystemModules;
  52 import jdk.internal.module.ModulePatcher;
  53 import jdk.internal.perf.PerfCounter;
  54 
  55 /**
  56  * A {@code ModuleFinder} that finds modules that are linked into the
  57  * run-time image.
  58  *
  59  * The modules linked into the run-time image are assumed to have the
  60  * ConcealedPackages attribute.
  61  */
  62 
  63 class SystemModuleFinder implements ModuleFinder {
  64 
  65     private static final PerfCounter initTime
  66         = PerfCounter.newPerfCounter("jdk.module.finder.jimage.initTime");
  67     private static final PerfCounter moduleCount
  68         = PerfCounter.newPerfCounter("jdk.module.finder.jimage.modules");
  69     private static final PerfCounter packageCount
  70         = PerfCounter.newPerfCounter("jdk.module.finder.jimage.packages");
  71     private static final PerfCounter exportsCount
  72         = PerfCounter.newPerfCounter("jdk.module.finder.jimage.exports");
  73     // ImageReader used to access all modules in the image
  74     private static final ImageReader imageReader;
  75 
  76     private static final JavaNetAccess jna = SharedSecrets.getJavaNetAccess();
  77 
  78     // the set of modules in the run-time image
  79     private static final Set<ModuleReference> modules;
  80 
  81     // maps module name to module reference
  82     private static final Map<String, ModuleReference> nameToModule;
  83 
  84     /**
  85      * For now, the module references are created eagerly on the assumption
  86      * that service binding will require all modules to be located.
  87      */
  88     static {
  89         long t0 = System.nanoTime();
  90         imageReader = ImageReaderFactory.getImageReader();
  91 
  92         String[] names = moduleNames();
  93         ModuleDescriptor[] descriptors = descriptors(names);
  94 
  95         int n = names.length;
  96         moduleCount.add(n);
  97 
  98         Set<ModuleReference> mods = new HashSet<>(n);
  99         Map<String, ModuleReference> map = new HashMap<>(n);
 100 
 101         for (int i = 0; i < n; i++) {
 102             ModuleDescriptor md = descriptors[i];
 103 
 104             // create the ModuleReference
 105             ModuleReference mref = toModuleReference(md, hashSupplier(i, names[i]));
 106 
 107             mods.add(mref);
 108             map.put(names[i], mref);
 109 
 110             // counters
 111             packageCount.add(md.packages().size());
 112             exportsCount.add(md.exports().size());
 113         }
 114 
 115         modules = Collections.unmodifiableSet(mods);
 116         nameToModule = map;
 117 
 118         initTime.addElapsedTimeFrom(t0);
 119     }
 120 
 121     /*
 122      * Returns an array of ModuleDescriptor of the given module names.
 123      *
 124      * This obtains ModuleDescriptors from SystemModules class that is generated
 125      * from the jlink system-modules plugin.  ModuleDescriptors have already
 126      * been validated at link time.
 127      *
 128      * If java.base is patched, or fastpath is disabled for troubleshooting
 129      * purpose, it will fall back to find system modules via jrt file system.
 130      */
 131     private static ModuleDescriptor[] descriptors(String[] names) {
 132         // fastpath is enabled by default.
 133         // It can be disabled for troubleshooting purpose.
 134         boolean disabled =
 135             System.getProperty("jdk.system.module.finder.disabledFastPath") != null;
 136 
 137         // fast loading of ModuleDescriptor of system modules
 138         if (isFastPathSupported() && !disabled)
 139             return SystemModules.modules();
 140 
 141         // if fast loading of ModuleDescriptors is disabled
 142         // fallback to read module-info.class
 143         ModuleDescriptor[] descriptors = new ModuleDescriptor[names.length];
 144         for (int i = 0; i < names.length; i++) {
 145             String mn = names[i];
 146             ImageLocation loc = imageReader.findLocation(mn, "module-info.class");
 147             descriptors[i] = ModuleDescriptor.read(imageReader.getResourceBuffer(loc));
 148 
 149             // add the recorded hashes of tied modules
 150             Hashes.add(descriptors[i]);
 151         }
 152         return descriptors;
 153     }
 154 
 155     private static boolean isFastPathSupported() {
 156        return SystemModules.MODULE_NAMES.length > 0;
 157     }
 158 
 159     private static String[] moduleNames() {
 160         if (isFastPathSupported())
 161             // module names recorded at link time
 162             return SystemModules.MODULE_NAMES;
 163 
 164         // this happens when java.base is patched with java.base
 165         // from an exploded image
 166         return imageReader.getModuleNames();
 167     }
 168 
 169     private static ModuleReference toModuleReference(ModuleDescriptor md,
 170                                                      HashSupplier hash)
 171     {
 172         String mn = md.name();
 173 
 174         URI uri = jna.createURI("jrt", "/".concat(mn));
 175 
 176         Supplier<ModuleReader> readerSupplier = new Supplier<>() {
 177             @Override
 178             public ModuleReader get() {
 179                 return new ImageModuleReader(mn, uri);
 180             }
 181         };
 182 
 183         ModuleReference mref =
 184             new ModuleReference(md, uri, readerSupplier, hash);
 185 
 186         // may need a reference to a patched module if --patch-module specified
 187         mref = ModulePatcher.interposeIfNeeded(mref);
 188 
 189         return mref;
 190     }
 191 
 192     private static HashSupplier hashSupplier(int index, String name) {
 193         if (isFastPathSupported()) {
 194             return new HashSupplier() {
 195                 @Override
 196                 public String generate(String algorithm) {
 197                     return SystemModules.MODULES_TO_HASH[index];
 198                 }
 199             };
 200         } else {
 201             return Hashes.hashFor(name);
 202         }
 203     }
 204 
 205     /*
 206      * This helper class is only used when SystemModules is patched.
 207      * It will get the recorded hashes from module-info.class.
 208      */
 209     private static class Hashes {
 210         static Map<String, String> hashes = new HashMap<>();
 211 
 212         static void add(ModuleDescriptor descriptor) {
 213             Optional<ModuleHashes> ohashes = descriptor.hashes();
 214             if (ohashes.isPresent()) {
 215                 hashes.putAll(ohashes.get().hashes());
 216             }
 217         }
 218 
 219         static HashSupplier hashFor(String name) {
 220             if (!hashes.containsKey(name))
 221                 return null;
 222 
 223             return new HashSupplier() {
 224                 @Override
 225                 public String generate(String algorithm) {
 226                     return hashes.get(name);
 227                 }
 228             };
 229         }
 230     }
 231 
 232     SystemModuleFinder() { }
 233 
 234     @Override
 235     public Optional<ModuleReference> find(String name) {
 236         Objects.requireNonNull(name);
 237         return Optional.ofNullable(nameToModule.get(name));
 238     }
 239 
 240     @Override
 241     public Set<ModuleReference> findAll() {
 242         return modules;
 243     }
 244 
 245 
 246     /**
 247      * A ModuleReader for reading resources from a module linked into the
 248      * run-time image.
 249      */
 250     static class ImageModuleReader implements ModuleReader {
 251         private final String module;
 252         private volatile boolean closed;
 253 
 254         /**
 255          * If there is a security manager set then check permission to
 256          * connect to the run-time image.
 257          */
 258         private static void checkPermissionToConnect(URI uri) {
 259             SecurityManager sm = System.getSecurityManager();
 260             if (sm != null) {
 261                 try {
 262                     URLConnection uc = uri.toURL().openConnection();
 263                     sm.checkPermission(uc.getPermission());
 264                 } catch (IOException ioe) {
 265                     throw new UncheckedIOException(ioe);
 266                 }
 267             }
 268         }
 269 
 270         ImageModuleReader(String module, URI uri) {
 271             checkPermissionToConnect(uri);
 272             this.module = module;
 273         }
 274 
 275         /**
 276          * Returns the ImageLocation for the given resource, {@code null}
 277          * if not found.
 278          */
 279         private ImageLocation findImageLocation(String name) throws IOException {
 280             Objects.requireNonNull(name);
 281             if (closed)
 282                 throw new IOException("ModuleReader is closed");
 283             if (imageReader != null) {
 284                 return imageReader.findLocation(module, name);
 285             } else {
 286                 // not an images build
 287                 return null;
 288             }
 289         }
 290 
 291         @Override
 292         public Optional<URI> find(String name) throws IOException {
 293             ImageLocation location = findImageLocation(name);
 294             if (location != null) {
 295                 URI u = URI.create("jrt:/" + module + "/" + name);
 296                 return Optional.of(u);
 297             } else {
 298                 return Optional.empty();
 299             }
 300         }
 301 
 302         @Override
 303         public Optional<InputStream> open(String name) throws IOException {
 304             return read(name).map(this::toInputStream);
 305         }
 306 
 307         private InputStream toInputStream(ByteBuffer bb) { // ## -> ByteBuffer?
 308             try {
 309                 int rem = bb.remaining();
 310                 byte[] bytes = new byte[rem];
 311                 bb.get(bytes);
 312                 return new ByteArrayInputStream(bytes);
 313             } finally {
 314                 release(bb);
 315             }
 316         }
 317 
 318         @Override
 319         public Optional<ByteBuffer> read(String name) throws IOException {
 320             ImageLocation location = findImageLocation(name);
 321             if (location != null) {
 322                 return Optional.of(imageReader.getResourceBuffer(location));
 323             } else {
 324                 return Optional.empty();
 325             }
 326         }
 327 
 328         @Override
 329         public void release(ByteBuffer bb) {
 330             Objects.requireNonNull(bb);
 331             ImageReader.releaseByteBuffer(bb);
 332         }
 333 
 334         @Override
 335         public void close() {
 336             // nothing else to do
 337             closed = true;
 338         }
 339     }
 340 
 341 }