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 jdk.internal.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.lang.module.ModuleDescriptor; 33 import java.lang.module.ModuleFinder; 34 import java.lang.module.ModuleReader; 35 import java.lang.module.ModuleReference; 36 import java.net.URI; 37 import java.net.URLConnection; 38 import java.nio.ByteBuffer; 39 import java.util.ArrayDeque; 40 import java.util.Collections; 41 import java.util.Deque; 42 import java.util.HashMap; 43 import java.util.Iterator; 44 import java.util.Map; 45 import java.util.Map.Entry; 46 import java.util.Objects; 47 import java.util.Optional; 48 import java.util.Set; 49 import java.util.Spliterator; 50 import java.util.function.Consumer; 51 import java.util.function.Supplier; 52 import java.util.stream.Stream; 53 import java.util.stream.StreamSupport; 54 55 import jdk.internal.jimage.ImageLocation; 56 import jdk.internal.jimage.ImageReader; 57 import jdk.internal.jimage.ImageReaderFactory; 58 import jdk.internal.misc.JavaNetUriAccess; 59 import jdk.internal.misc.SharedSecrets; 60 import jdk.internal.module.ModuleHashes.HashSupplier; 61 import jdk.internal.perf.PerfCounter; 62 63 /** 64 * A {@code ModuleFinder} that finds modules that are linked into the 65 * run-time image. 66 * 67 * The modules linked into the run-time image are assumed to have the 68 * Packages attribute. 69 */ 70 71 public class SystemModuleFinder implements ModuleFinder { 72 73 private static final JavaNetUriAccess JNUA = SharedSecrets.getJavaNetUriAccess(); 74 75 private static final PerfCounter initTime 76 = PerfCounter.newPerfCounter("jdk.module.finder.jimage.initTime"); 77 private static final PerfCounter moduleCount 78 = PerfCounter.newPerfCounter("jdk.module.finder.jimage.modules"); 79 private static final PerfCounter packageCount 80 = PerfCounter.newPerfCounter("jdk.module.finder.jimage.packages"); 81 private static final PerfCounter exportsCount 82 = PerfCounter.newPerfCounter("jdk.module.finder.jimage.exports"); 83 // ImageReader used to access all modules in the image 84 private static final ImageReader imageReader; 85 86 // singleton finder to find modules in the run-time images 87 private static final SystemModuleFinder INSTANCE; 88 89 public static SystemModuleFinder getInstance() { 90 return INSTANCE; 91 } 92 93 /** 94 * For now, the module references are created eagerly on the assumption 95 * that service binding will require all modules to be located. 96 */ 97 static { 98 long t0 = System.nanoTime(); 99 imageReader = ImageReaderFactory.getImageReader(); 100 101 INSTANCE = new SystemModuleFinder(); 102 103 initTime.addElapsedTimeFrom(t0); 104 } 105 106 private static boolean isFastPathSupported() { 107 return SystemModules.MODULE_NAMES.length > 0; 108 } 109 110 private static String[] moduleNames() { 111 if (isFastPathSupported()) 112 // module names recorded at link time 113 return SystemModules.MODULE_NAMES; 114 115 // this happens when java.base is patched with java.base 116 // from an exploded image 117 return imageReader.getModuleNames(); 118 } 119 120 // the set of modules in the run-time image 121 private final Set<ModuleReference> modules; 122 123 // maps module name to module reference 124 private final Map<String, ModuleReference> nameToModule; 125 126 // module name to hashes 127 private final Map<String, byte[]> hashes; 128 129 private SystemModuleFinder() { 130 String[] names = moduleNames(); 131 int n = names.length; 132 moduleCount.add(n); 133 134 // fastpath is enabled by default. 135 // It can be disabled for troubleshooting purpose. 136 boolean disabled = 137 System.getProperty("jdk.system.module.finder.disabledFastPath") != null; 138 139 ModuleDescriptor[] descriptors; 140 ModuleHashes[] recordedHashes; 141 ModuleResolution[] moduleResolutions; 142 143 // fast loading of ModuleDescriptor of system modules 144 if (isFastPathSupported() && !disabled) { 145 descriptors = SystemModules.descriptors(); 146 recordedHashes = SystemModules.hashes(); 147 moduleResolutions = SystemModules.moduleResolutions(); 148 } else { 149 // if fast loading of ModuleDescriptors is disabled 150 // fallback to read module-info.class 151 descriptors = new ModuleDescriptor[n]; 152 recordedHashes = new ModuleHashes[n]; 153 moduleResolutions = new ModuleResolution[n]; 154 for (int i = 0; i < names.length; i++) { 155 String mn = names[i]; 156 ImageLocation loc = imageReader.findLocation(mn, "module-info.class"); 157 ModuleInfo.Attributes attrs = 158 ModuleInfo.read(imageReader.getResourceBuffer(loc), null); 159 descriptors[i] = attrs.descriptor(); 160 recordedHashes[i] = attrs.recordedHashes(); 161 moduleResolutions[i] = attrs.moduleResolution(); 162 } 163 } 164 165 Map<String, byte[]> hashes = null; 166 boolean secondSeen = false; 167 // record the hashes to build HashSupplier 168 for (ModuleHashes mh : recordedHashes) { 169 if (mh != null) { 170 // if only one module contain ModuleHashes, use it 171 if (hashes == null) { 172 hashes = mh.hashes(); 173 } else { 174 if (!secondSeen) { 175 hashes = new HashMap<>(hashes); 176 secondSeen = true; 177 } 178 hashes.putAll(mh.hashes()); 179 } 180 } 181 } 182 this.hashes = (hashes == null) ? Map.of() : hashes; 183 184 ModuleReference[] mods = new ModuleReference[n]; 185 186 @SuppressWarnings(value = {"rawtypes", "unchecked"}) 187 Entry<String, ModuleReference>[] map 188 = (Entry<String, ModuleReference>[])new Entry[n]; 189 190 for (int i = 0; i < n; i++) { 191 ModuleDescriptor md = descriptors[i]; 192 193 // create the ModuleReference 194 ModuleReference mref = toModuleReference(md, 195 recordedHashes[i], 196 hashSupplier(names[i]), 197 moduleResolutions[i]); 198 mods[i] = mref; 199 map[i] = Map.entry(names[i], mref); 200 201 // counters 202 packageCount.add(md.packages().size()); 203 exportsCount.add(md.exports().size()); 204 } 205 206 modules = Set.of(mods); 207 nameToModule = Map.ofEntries(map); 208 } 209 210 @Override 211 public Optional<ModuleReference> find(String name) { 212 Objects.requireNonNull(name); 213 return Optional.ofNullable(nameToModule.get(name)); 214 } 215 216 @Override 217 public Set<ModuleReference> findAll() { 218 return modules; 219 } 220 221 private ModuleReference toModuleReference(ModuleDescriptor md, 222 ModuleHashes recordedHashes, 223 HashSupplier hasher, 224 ModuleResolution mres) { 225 String mn = md.name(); 226 URI uri = JNUA.create("jrt", "/".concat(mn)); 227 228 Supplier<ModuleReader> readerSupplier = new Supplier<>() { 229 @Override 230 public ModuleReader get() { 231 return new ImageModuleReader(mn, uri); 232 } 233 }; 234 235 ModuleReference mref = 236 new ModuleReferenceImpl(md, uri, readerSupplier, null, 237 recordedHashes, hasher, mres); 238 239 // may need a reference to a patched module if --patch-module specified 240 mref = ModuleBootstrap.patcher().patchIfNeeded(mref); 241 242 return mref; 243 } 244 245 private HashSupplier hashSupplier(String name) { 246 if (!hashes.containsKey(name)) 247 return null; 248 249 return new HashSupplier() { 250 @Override 251 public byte[] generate(String algorithm) { 252 return hashes.get(name); 253 } 254 }; 255 } 256 257 /** 258 * A ModuleReader for reading resources from a module linked into the 259 * run-time image. 260 */ 261 static class ImageModuleReader implements ModuleReader { 262 private final String module; 263 private volatile boolean closed; 264 265 /** 266 * If there is a security manager set then check permission to 267 * connect to the run-time image. 268 */ 269 private static void checkPermissionToConnect(URI uri) { 270 SecurityManager sm = System.getSecurityManager(); 271 if (sm != null) { 272 try { 273 URLConnection uc = uri.toURL().openConnection(); 274 sm.checkPermission(uc.getPermission()); 275 } catch (IOException ioe) { 276 throw new UncheckedIOException(ioe); 277 } 278 } 279 } 280 281 ImageModuleReader(String module, URI uri) { 282 checkPermissionToConnect(uri); 283 this.module = module; 284 } 285 286 /** 287 * Returns the ImageLocation for the given resource, {@code null} 288 * if not found. 289 */ 290 private ImageLocation findImageLocation(String name) throws IOException { 291 Objects.requireNonNull(name); 292 if (closed) 293 throw new IOException("ModuleReader is closed"); 294 if (imageReader != null) { 295 return imageReader.findLocation(module, name); 296 } else { 297 // not an images build 298 return null; 299 } 300 } 301 302 @Override 303 public Optional<URI> find(String name) throws IOException { 304 ImageLocation location = findImageLocation(name); 305 if (location != null) { 306 URI u = URI.create("jrt:/" + module + "/" + name); 307 return Optional.of(u); 308 } else { 309 return Optional.empty(); 310 } 311 } 312 313 @Override 314 public Optional<InputStream> open(String name) throws IOException { 315 return read(name).map(this::toInputStream); 316 } 317 318 private InputStream toInputStream(ByteBuffer bb) { // ## -> ByteBuffer? 319 try { 320 int rem = bb.remaining(); 321 byte[] bytes = new byte[rem]; 322 bb.get(bytes); 323 return new ByteArrayInputStream(bytes); 324 } finally { 325 release(bb); 326 } 327 } 328 329 @Override 330 public Optional<ByteBuffer> read(String name) throws IOException { 331 ImageLocation location = findImageLocation(name); 332 if (location != null) { 333 return Optional.of(imageReader.getResourceBuffer(location)); 334 } else { 335 return Optional.empty(); 336 } 337 } 338 339 @Override 340 public void release(ByteBuffer bb) { 341 Objects.requireNonNull(bb); 342 ImageReader.releaseByteBuffer(bb); 343 } 344 345 @Override 346 public Stream<String> list() throws IOException { 347 if (closed) 348 throw new IOException("ModuleReader is closed"); 349 350 Spliterator<String> s = new ModuleContentSpliterator(module); 351 return StreamSupport.stream(s, false); 352 } 353 354 @Override 355 public void close() { 356 // nothing else to do 357 closed = true; 358 } 359 } 360 361 /** 362 * A Spliterator for traversing the resources of a module linked into the 363 * run-time image. 364 */ 365 static class ModuleContentSpliterator implements Spliterator<String> { 366 final String moduleRoot; 367 final Deque<ImageReader.Node> stack; 368 Iterator<ImageReader.Node> iterator; 369 370 ModuleContentSpliterator(String module) throws IOException { 371 moduleRoot = "/modules/" + module; 372 stack = new ArrayDeque<>(); 373 374 // push the root node to the stack to get started 375 ImageReader.Node dir = imageReader.findNode(moduleRoot); 376 if (dir == null || !dir.isDirectory()) 377 throw new IOException(moduleRoot + " not a directory"); 378 stack.push(dir); 379 iterator = Collections.emptyIterator(); 380 } 381 382 /** 383 * Returns the name of the next non-directory node or {@code null} if 384 * there are no remaining nodes to visit. 385 */ 386 private String next() throws IOException { 387 for (;;) { 388 while (iterator.hasNext()) { 389 ImageReader.Node node = iterator.next(); 390 String name = node.getName(); 391 if (node.isDirectory()) { 392 // build node 393 ImageReader.Node dir = imageReader.findNode(name); 394 assert dir.isDirectory(); 395 stack.push(dir); 396 } else { 397 // strip /modules/$MODULE/ prefix 398 return name.substring(moduleRoot.length() + 1); 399 } 400 } 401 402 if (stack.isEmpty()) { 403 return null; 404 } else { 405 ImageReader.Node dir = stack.poll(); 406 assert dir.isDirectory(); 407 iterator = dir.getChildren().iterator(); 408 } 409 } 410 } 411 412 @Override 413 public boolean tryAdvance(Consumer<? super String> action) { 414 String next; 415 try { 416 next = next(); 417 } catch (IOException ioe) { 418 throw new UncheckedIOException(ioe); 419 } 420 if (next != null) { 421 action.accept(next); 422 return true; 423 } else { 424 return false; 425 } 426 } 427 428 @Override 429 public Spliterator<String> trySplit() { 430 return null; 431 } 432 433 @Override 434 public int characteristics() { 435 return Spliterator.DISTINCT + Spliterator.NONNULL + Spliterator.IMMUTABLE; 436 } 437 438 @Override 439 public long estimateSize() { 440 return Long.MAX_VALUE; 441 } 442 } 443 }