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.module.InstalledModules;
  48 import jdk.internal.module.ModulePatcher;
  49 import jdk.internal.perf.PerfCounter;
  50 
  51 /**
  52  * A {@code ModuleFinder} that finds modules that are linked into the
  53  * run-time image.
  54  *
  55  * The modules linked into the run-time image are assumed to have the
  56  * ConcealedPackages attribute.
  57  */
  58 
  59 class InstalledModuleFinder implements ModuleFinder {
  60 
  61     private static final PerfCounter initTime
  62         = PerfCounter.newPerfCounter("jdk.module.finder.jimage.initTime");
  63     private static final PerfCounter moduleCount
  64         = PerfCounter.newPerfCounter("jdk.module.finder.jimage.modules");
  65     private static final PerfCounter packageCount
  66         = PerfCounter.newPerfCounter("jdk.module.finder.jimage.packages");
  67     private static final PerfCounter exportsCount
  68         = PerfCounter.newPerfCounter("jdk.module.finder.jimage.exports");
  69     // ImageReader used to access all modules in the image
  70     private static final ImageReader imageReader;
  71 
  72     // the set of modules in the run-time image
  73     private static final Set<ModuleReference> modules;
  74 
  75     // maps module name to module reference
  76     private static final Map<String, ModuleReference> nameToModule;
  77 
  78     /**
  79      * For now, the module references are created eagerly on the assumption
  80      * that service binding will require all modules to be located.
  81      */
  82     static {
  83         long t0 = System.nanoTime();
  84         imageReader = ImageReaderFactory.getImageReader();
  85 
  86         String[] moduleNames = InstalledModules.MODULE_NAMES;
  87         ModuleDescriptor[] descriptors = null;
  88 
  89         boolean fastLoad = System.getProperty("jdk.installed.modules.disable") == null;
  90         if (fastLoad) {
  91             // fast loading of ModuleDescriptor of installed modules
  92             descriptors = InstalledModules.modules();
  93         }
  94 
  95         int n = moduleNames.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             String mn = moduleNames[i];
 103             ModuleDescriptor md;
 104             if (fastLoad) {
 105                 md = descriptors[i];
 106             } else {
 107                 // fallback to read module-info.class
 108                 // if fast loading of ModuleDescriptors is disabled
 109                 ImageLocation location = imageReader.findLocation(mn, "module-info.class");
 110                 md = ModuleDescriptor.read(imageReader.getResourceBuffer(location));
 111             }
 112             if (!md.name().equals(mn))
 113                 throw new InternalError();
 114 
 115             // create the ModuleReference
 116 
 117             URI uri = URI.create("jrt:/" + mn);
 118 
 119             Supplier<ModuleReader> readerSupplier = new Supplier<>() {
 120                 @Override
 121                 public ModuleReader get() {
 122                     return new ImageModuleReader(mn, uri);
 123                 }
 124             };
 125 
 126             ModuleReference mref = new ModuleReference(md, uri, readerSupplier);
 127 
 128             // may need a reference to a patched module if -Xpatch specified
 129             mref = ModulePatcher.interposeIfNeeded(mref);
 130 
 131             mods.add(mref);
 132             map.put(mn, mref);
 133 
 134             // counters
 135             packageCount.add(md.packages().size());
 136             exportsCount.add(md.exports().size());
 137         }
 138 
 139         modules = Collections.unmodifiableSet(mods);
 140         nameToModule = map;
 141 
 142         initTime.addElapsedTimeFrom(t0);
 143     }
 144 
 145     InstalledModuleFinder() { }
 146 
 147     @Override
 148     public Optional<ModuleReference> find(String name) {
 149         Objects.requireNonNull(name);
 150         return Optional.ofNullable(nameToModule.get(name));
 151     }
 152 
 153     @Override
 154     public Set<ModuleReference> findAll() {
 155         return modules;
 156     }
 157 
 158 
 159     /**
 160      * A ModuleReader for reading resources from a module linked into the
 161      * run-time image.
 162      */
 163     static class ImageModuleReader implements ModuleReader {
 164         private final String module;
 165         private volatile boolean closed;
 166 
 167         /**
 168          * If there is a security manager set then check permission to
 169          * connect to the run-time image.
 170          */
 171         private static void checkPermissionToConnect(URI uri) {
 172             SecurityManager sm = System.getSecurityManager();
 173             if (sm != null) {
 174                 try {
 175                     URLConnection uc = uri.toURL().openConnection();
 176                     sm.checkPermission(uc.getPermission());
 177                 } catch (IOException ioe) {
 178                     throw new UncheckedIOException(ioe);
 179                 }
 180             }
 181         }
 182 
 183         ImageModuleReader(String module, URI uri) {
 184             checkPermissionToConnect(uri);
 185             this.module = module;
 186         }
 187 
 188         /**
 189          * Returns the ImageLocation for the given resource, {@code null}
 190          * if not found.
 191          */
 192         private ImageLocation findImageLocation(String name) throws IOException {
 193             if (closed)
 194                 throw new IOException("ModuleReader is closed");
 195 
 196             if (imageReader != null) {
 197                 return imageReader.findLocation(module, name);
 198             } else {
 199                 // not an images build
 200                 return null;
 201             }
 202         }
 203 
 204         @Override
 205         public Optional<URI> find(String name) throws IOException {
 206             ImageLocation location = findImageLocation(name);
 207             if (location != null) {
 208                 URI u = URI.create("jrt:/" + module + "/" + name);
 209                 return Optional.of(u);
 210             } else {
 211                 return Optional.empty();
 212             }
 213         }
 214 
 215         @Override
 216         public Optional<InputStream> open(String name) throws IOException {
 217             return read(name).map(this::toInputStream);
 218         }
 219 
 220         private InputStream toInputStream(ByteBuffer bb) { // ## -> ByteBuffer?
 221             try {
 222                 int rem = bb.remaining();
 223                 byte[] bytes = new byte[rem];
 224                 bb.get(bytes);
 225                 return new ByteArrayInputStream(bytes);
 226             } finally {
 227                 release(bb);
 228             }
 229         }
 230 
 231         @Override
 232         public Optional<ByteBuffer> read(String name) throws IOException {
 233             ImageLocation location = findImageLocation(name);
 234             if (location != null) {
 235                 return Optional.of(imageReader.getResourceBuffer(location));
 236             } else {
 237                 return Optional.empty();
 238             }
 239         }
 240 
 241         @Override
 242         public void release(ByteBuffer bb) {
 243             ImageReader.releaseByteBuffer(bb);
 244         }
 245 
 246         @Override
 247         public void close() {
 248             // nothing else to do
 249             closed = true;
 250         }
 251     }
 252 
 253 }