< prev index next >

src/java.base/share/classes/jdk/internal/module/ModulePatcher.java

Print this page




  38 import java.net.URI;
  39 import java.net.URL;
  40 import java.nio.ByteBuffer;
  41 import java.nio.file.Files;
  42 import java.nio.file.Path;
  43 import java.nio.file.Paths;
  44 import java.util.ArrayList;
  45 import java.util.Collections;
  46 import java.util.HashMap;
  47 import java.util.HashSet;
  48 import java.util.List;
  49 import java.util.Map;
  50 import java.util.Optional;
  51 import java.util.Set;
  52 import java.util.jar.JarEntry;
  53 import java.util.jar.JarFile;
  54 import java.util.stream.Collectors;
  55 import java.util.stream.Stream;
  56 
  57 import jdk.internal.loader.Resource;
  58 import jdk.internal.loader.ResourceHelper;
  59 import jdk.internal.misc.JavaLangModuleAccess;
  60 import jdk.internal.misc.SharedSecrets;
  61 import sun.net.www.ParseUtil;
  62 
  63 
  64 /**
  65  * Provides support for patching modules, mostly the boot layer.
  66  */
  67 
  68 public final class ModulePatcher {
  69 
  70     private static final JavaLangModuleAccess JLMA
  71         = SharedSecrets.getJavaLangModuleAccess();
  72 
  73     // module name -> sequence of patches (directories or JAR files)
  74     private final Map<String, List<Path>> map;
  75 
  76     /**
  77      * Initialize the module patcher with the given map. The map key is
  78      * the module name, the value is a list of path strings.


 148         } catch (IOException ioe) {
 149             throw new UncheckedIOException(ioe);
 150         }
 151 
 152         // if there are new packages then we need a new ModuleDescriptor
 153         packages.removeAll(descriptor.packages());
 154         if (!packages.isEmpty()) {
 155             Builder builder = JLMA.newModuleBuilder(descriptor.name(),
 156                                                     /*strict*/ false,
 157                                                     descriptor.modifiers());
 158             if (!descriptor.isAutomatic()) {
 159                 descriptor.requires().forEach(builder::requires);
 160                 descriptor.exports().forEach(builder::exports);
 161                 descriptor.opens().forEach(builder::opens);
 162                 descriptor.uses().forEach(builder::uses);
 163             }
 164             descriptor.provides().forEach(builder::provides);
 165 
 166             descriptor.version().ifPresent(builder::version);
 167             descriptor.mainClass().ifPresent(builder::mainClass);
 168             descriptor.osName().ifPresent(builder::osName);
 169             descriptor.osArch().ifPresent(builder::osArch);
 170             descriptor.osVersion().ifPresent(builder::osVersion);
 171 
 172             // original + new packages
 173             builder.packages(descriptor.packages());
 174             builder.packages(packages);
 175 
 176             descriptor = builder.build();
 177         }
 178 
 179         // return a module reference to the patched module
 180         URI location = mref.location().orElse(null);
 181 

 182         ModuleHashes recordedHashes = null;
 183         ModuleResolution mres = null;
 184         if (mref instanceof ModuleReferenceImpl) {
 185             ModuleReferenceImpl impl = (ModuleReferenceImpl)mref;

 186             recordedHashes = impl.recordedHashes();
 187             mres = impl.moduleResolution();
 188         }
 189 
 190         return new ModuleReferenceImpl(descriptor,
 191                                        location,
 192                                        () -> new PatchedModuleReader(paths, mref),
 193                                        this,

 194                                        recordedHashes,
 195                                        null,
 196                                        mres);
 197 
 198     }
 199 
 200     /**
 201      * Returns true is this module patcher has no patches.
 202      */
 203     public boolean isEmpty() {
 204         return map.isEmpty();
 205     }
 206 
 207     /*
 208      * Returns the names of the patched modules.
 209      */
 210     Set<String> patchedModules() {
 211         return map.keySet();
 212     }
 213 
 214     /**
 215      * A ModuleReader that reads resources from a patched module.
 216      *
 217      * This class is public so as to expose the findResource method to the
 218      * built-in class loaders and avoid locating the resource twice during
 219      * class loading (once to locate the resource, the second to gets the
 220      * URL for the CodeSource).
 221      */
 222     public static class PatchedModuleReader implements ModuleReader {
 223         private final List<ResourceFinder> finders;
 224         private final ModuleReference mref;
 225         private final URL delegateCodeSourceURL;
 226         private volatile ModuleReader delegate;
 227 
 228         /**
 229          * Creates the ModuleReader to reads resources a patched module.
 230          */
 231         PatchedModuleReader(List<Path> patches, ModuleReference mref) {
 232             List<ResourceFinder> finders = new ArrayList<>();
 233             boolean initialized = false;
 234             try {
 235                 for (Path file : patches) {
 236                     if (Files.isRegularFile(file)) {
 237                         finders.add(new JarResourceFinder(file));
 238                     } else {
 239                         finders.add(new ExplodedResourceFinder(file));
 240                     }
 241                 }
 242                 initialized = true;
 243             } catch (IOException ioe) {
 244                 throw new UncheckedIOException(ioe);
 245             } finally {
 246                 // close all ResourceFinder in the event of an error
 247                 if (!initialized) closeAll(finders);
 248             }
 249 


 274         }
 275 
 276         /**
 277          * Returns the ModuleReader to delegate to when the resource is not
 278          * found in a patch location.
 279          */
 280         private ModuleReader delegate() throws IOException {
 281             ModuleReader r = delegate;
 282             if (r == null) {
 283                 synchronized (this) {
 284                     r = delegate;
 285                     if (r == null) {
 286                         delegate = r = mref.open();
 287                     }
 288                 }
 289             }
 290             return r;
 291         }
 292 
 293         /**
 294          * Finds a resources in the patch locations. Returns null if not found.

 295          */
 296         private Resource findResourceInPatch(String name) throws IOException {

 297             for (ResourceFinder finder : finders) {
 298                 Resource r = finder.find(name);
 299                 if (r != null)
 300                     return r;
 301             }

 302             return null;
 303         }
 304 
 305         /**
 306          * Finds a resource of the given name in the patched module.
 307          */
 308         public Resource findResource(String name) throws IOException {
 309 
 310             // patch locations
 311             Resource r = findResourceInPatch(name);
 312             if (r != null)
 313                 return r;
 314 
 315             // original module
 316             ByteBuffer bb = delegate().read(name).orElse(null);
 317             if (bb == null)
 318                 return null;
 319 
 320             return new Resource() {
 321                 private <T> T shouldNotGetHere(Class<T> type) {


 461                 }
 462                 @Override
 463                 public ByteBuffer getByteBuffer() throws IOException {
 464                     byte[] bytes = getInputStream().readAllBytes();
 465                     return ByteBuffer.wrap(bytes);
 466                 }
 467                 @Override
 468                 public InputStream getInputStream() throws IOException {
 469                     return jf.getInputStream(entry);
 470                 }
 471                 @Override
 472                 public int getContentLength() throws IOException {
 473                     long size = entry.getSize();
 474                     return (size > Integer.MAX_VALUE) ? -1 : (int) size;
 475                 }
 476             };
 477         }
 478 
 479         @Override
 480         public Stream<String> list() throws IOException {
 481             return jf.stream()
 482                     .filter(e -> !e.isDirectory())
 483                     .map(JarEntry::getName);
 484         }
 485     }
 486 
 487 
 488     /**
 489      * A ResourceFinder that finds resources on the file system.
 490      */
 491     private static class ExplodedResourceFinder implements ResourceFinder {
 492         private final Path dir;
 493 
 494         ExplodedResourceFinder(Path dir) {
 495             this.dir = dir;
 496         }
 497 
 498         @Override
 499         public void close() { }
 500 
 501         @Override
 502         public Resource find(String name) throws IOException {
 503             Path path = ResourceHelper.toFilePath(name);
 504             if (path != null) {
 505                 Path file = dir.resolve(path);
 506                 if (Files.isRegularFile(file)) {
 507                     return newResource(name, dir, file);
 508                 }
 509             }
 510             return null;
 511         }

 512 
 513         private Resource newResource(String name, Path top, Path file) {
 514             return new Resource() {
 515                 @Override
 516                 public String getName() {
 517                     return name;
 518                 }
 519                 @Override
 520                 public URL getURL() {
 521                     try {
 522                         return file.toUri().toURL();
 523                     } catch (IOException | IOError e) {
 524                         return null;
 525                     }
 526                 }
 527                 @Override
 528                 public URL getCodeSourceURL() {
 529                     try {
 530                         return top.toUri().toURL();
 531                     } catch (IOException | IOError e) {


 533                     }
 534                 }
 535                 @Override
 536                 public ByteBuffer getByteBuffer() throws IOException {
 537                     return ByteBuffer.wrap(Files.readAllBytes(file));
 538                 }
 539                 @Override
 540                 public InputStream getInputStream() throws IOException {
 541                     return Files.newInputStream(file);
 542                 }
 543                 @Override
 544                 public int getContentLength() throws IOException {
 545                     long size = Files.size(file);
 546                     return (size > Integer.MAX_VALUE) ? -1 : (int)size;
 547                 }
 548             };
 549         }
 550 
 551         @Override
 552         public Stream<String> list() throws IOException {
 553             return Files.find(dir, Integer.MAX_VALUE,
 554                               (path, attrs) -> attrs.isRegularFile())
 555                     .map(f -> dir.relativize(f)
 556                                  .toString()
 557                                  .replace(File.separatorChar, '/'));
 558         }
 559     }
 560 
 561 
 562     /**
 563      * Derives a package name from a file path to a .class file.
 564      */
 565     private static String toPackageName(Path top, Path file) {
 566         Path entry = top.relativize(file);
 567         Path parent = entry.getParent();
 568         if (parent == null) {
 569             return warnIfModuleInfo(top, entry.toString());
 570         } else {
 571             return parent.toString().replace(File.separatorChar, '.');
 572         }
 573     }
 574 
 575     /**
 576      * Derives a package name from the name of an entry in a JAR file.
 577      */


  38 import java.net.URI;
  39 import java.net.URL;
  40 import java.nio.ByteBuffer;
  41 import java.nio.file.Files;
  42 import java.nio.file.Path;
  43 import java.nio.file.Paths;
  44 import java.util.ArrayList;
  45 import java.util.Collections;
  46 import java.util.HashMap;
  47 import java.util.HashSet;
  48 import java.util.List;
  49 import java.util.Map;
  50 import java.util.Optional;
  51 import java.util.Set;
  52 import java.util.jar.JarEntry;
  53 import java.util.jar.JarFile;
  54 import java.util.stream.Collectors;
  55 import java.util.stream.Stream;
  56 
  57 import jdk.internal.loader.Resource;

  58 import jdk.internal.misc.JavaLangModuleAccess;
  59 import jdk.internal.misc.SharedSecrets;
  60 import sun.net.www.ParseUtil;
  61 
  62 
  63 /**
  64  * Provides support for patching modules, mostly the boot layer.
  65  */
  66 
  67 public final class ModulePatcher {
  68 
  69     private static final JavaLangModuleAccess JLMA
  70         = SharedSecrets.getJavaLangModuleAccess();
  71 
  72     // module name -> sequence of patches (directories or JAR files)
  73     private final Map<String, List<Path>> map;
  74 
  75     /**
  76      * Initialize the module patcher with the given map. The map key is
  77      * the module name, the value is a list of path strings.


 147         } catch (IOException ioe) {
 148             throw new UncheckedIOException(ioe);
 149         }
 150 
 151         // if there are new packages then we need a new ModuleDescriptor
 152         packages.removeAll(descriptor.packages());
 153         if (!packages.isEmpty()) {
 154             Builder builder = JLMA.newModuleBuilder(descriptor.name(),
 155                                                     /*strict*/ false,
 156                                                     descriptor.modifiers());
 157             if (!descriptor.isAutomatic()) {
 158                 descriptor.requires().forEach(builder::requires);
 159                 descriptor.exports().forEach(builder::exports);
 160                 descriptor.opens().forEach(builder::opens);
 161                 descriptor.uses().forEach(builder::uses);
 162             }
 163             descriptor.provides().forEach(builder::provides);
 164 
 165             descriptor.version().ifPresent(builder::version);
 166             descriptor.mainClass().ifPresent(builder::mainClass);



 167 
 168             // original + new packages
 169             builder.packages(descriptor.packages());
 170             builder.packages(packages);
 171 
 172             descriptor = builder.build();
 173         }
 174 
 175         // return a module reference to the patched module
 176         URI location = mref.location().orElse(null);
 177 
 178         ModuleTarget target = null;
 179         ModuleHashes recordedHashes = null;
 180         ModuleResolution mres = null;
 181         if (mref instanceof ModuleReferenceImpl) {
 182             ModuleReferenceImpl impl = (ModuleReferenceImpl)mref;
 183             target = impl.moduleTarget();
 184             recordedHashes = impl.recordedHashes();
 185             mres = impl.moduleResolution();
 186         }
 187 
 188         return new ModuleReferenceImpl(descriptor,
 189                                        location,
 190                                        () -> new PatchedModuleReader(paths, mref),
 191                                        this,
 192                                        target,
 193                                        recordedHashes,
 194                                        null,
 195                                        mres);
 196 
 197     }
 198 
 199     /**
 200      * Returns true is this module patcher has no patches.
 201      */
 202     public boolean isEmpty() {
 203         return map.isEmpty();
 204     }
 205 
 206     /*
 207      * Returns the names of the patched modules.
 208      */
 209     Set<String> patchedModules() {
 210         return map.keySet();
 211     }
 212 
 213     /**
 214      * A ModuleReader that reads resources from a patched module.
 215      *
 216      * This class is public so as to expose the findResource method to the
 217      * built-in class loaders and avoid locating the resource twice during
 218      * class loading (once to locate the resource, the second to gets the
 219      * URL for the CodeSource).
 220      */
 221     public static class PatchedModuleReader implements ModuleReader {
 222         private final List<ResourceFinder> finders;
 223         private final ModuleReference mref;
 224         private final URL delegateCodeSourceURL;
 225         private volatile ModuleReader delegate;
 226 
 227         /**
 228          * Creates the ModuleReader to reads resources in a patched module.
 229          */
 230         PatchedModuleReader(List<Path> patches, ModuleReference mref) {
 231             List<ResourceFinder> finders = new ArrayList<>();
 232             boolean initialized = false;
 233             try {
 234                 for (Path file : patches) {
 235                     if (Files.isRegularFile(file)) {
 236                         finders.add(new JarResourceFinder(file));
 237                     } else {
 238                         finders.add(new ExplodedResourceFinder(file));
 239                     }
 240                 }
 241                 initialized = true;
 242             } catch (IOException ioe) {
 243                 throw new UncheckedIOException(ioe);
 244             } finally {
 245                 // close all ResourceFinder in the event of an error
 246                 if (!initialized) closeAll(finders);
 247             }
 248 


 273         }
 274 
 275         /**
 276          * Returns the ModuleReader to delegate to when the resource is not
 277          * found in a patch location.
 278          */
 279         private ModuleReader delegate() throws IOException {
 280             ModuleReader r = delegate;
 281             if (r == null) {
 282                 synchronized (this) {
 283                     r = delegate;
 284                     if (r == null) {
 285                         delegate = r = mref.open();
 286                     }
 287                 }
 288             }
 289             return r;
 290         }
 291 
 292         /**
 293          * Finds a resources in the patch locations. Returns null if not found
 294          * or the name is "module-info.class" as that cannot be overridden.
 295          */
 296         private Resource findResourceInPatch(String name) throws IOException {
 297             if (!name.equals("module-info.class")) {
 298                 for (ResourceFinder finder : finders) {
 299                     Resource r = finder.find(name);
 300                     if (r != null)
 301                         return r;
 302                 }
 303             }
 304             return null;
 305         }
 306 
 307         /**
 308          * Finds a resource of the given name in the patched module.
 309          */
 310         public Resource findResource(String name) throws IOException {
 311 
 312             // patch locations
 313             Resource r = findResourceInPatch(name);
 314             if (r != null)
 315                 return r;
 316 
 317             // original module
 318             ByteBuffer bb = delegate().read(name).orElse(null);
 319             if (bb == null)
 320                 return null;
 321 
 322             return new Resource() {
 323                 private <T> T shouldNotGetHere(Class<T> type) {


 463                 }
 464                 @Override
 465                 public ByteBuffer getByteBuffer() throws IOException {
 466                     byte[] bytes = getInputStream().readAllBytes();
 467                     return ByteBuffer.wrap(bytes);
 468                 }
 469                 @Override
 470                 public InputStream getInputStream() throws IOException {
 471                     return jf.getInputStream(entry);
 472                 }
 473                 @Override
 474                 public int getContentLength() throws IOException {
 475                     long size = entry.getSize();
 476                     return (size > Integer.MAX_VALUE) ? -1 : (int) size;
 477                 }
 478             };
 479         }
 480 
 481         @Override
 482         public Stream<String> list() throws IOException {
 483             return jf.stream().map(JarEntry::getName);


 484         }
 485     }
 486 
 487 
 488     /**
 489      * A ResourceFinder that finds resources on the file system.
 490      */
 491     private static class ExplodedResourceFinder implements ResourceFinder {
 492         private final Path dir;
 493 
 494         ExplodedResourceFinder(Path dir) {
 495             this.dir = dir;
 496         }
 497 
 498         @Override
 499         public void close() { }
 500 
 501         @Override
 502         public Resource find(String name) throws IOException {
 503             Path file = Resources.toFilePath(dir, name);
 504             if (file != null) {


 505                 return  newResource(name, dir, file);
 506             } else {

 507                 return null;
 508             }
 509         }
 510 
 511         private Resource newResource(String name, Path top, Path file) {
 512             return new Resource() {
 513                 @Override
 514                 public String getName() {
 515                     return name;
 516                 }
 517                 @Override
 518                 public URL getURL() {
 519                     try {
 520                         return file.toUri().toURL();
 521                     } catch (IOException | IOError e) {
 522                         return null;
 523                     }
 524                 }
 525                 @Override
 526                 public URL getCodeSourceURL() {
 527                     try {
 528                         return top.toUri().toURL();
 529                     } catch (IOException | IOError e) {


 531                     }
 532                 }
 533                 @Override
 534                 public ByteBuffer getByteBuffer() throws IOException {
 535                     return ByteBuffer.wrap(Files.readAllBytes(file));
 536                 }
 537                 @Override
 538                 public InputStream getInputStream() throws IOException {
 539                     return Files.newInputStream(file);
 540                 }
 541                 @Override
 542                 public int getContentLength() throws IOException {
 543                     long size = Files.size(file);
 544                     return (size > Integer.MAX_VALUE) ? -1 : (int)size;
 545                 }
 546             };
 547         }
 548 
 549         @Override
 550         public Stream<String> list() throws IOException {
 551             return Files.walk(dir, Integer.MAX_VALUE)
 552                         .map(f -> Resources.toResourceName(dir, f))
 553                         .filter(s -> s.length() > 0);


 554         }
 555     }
 556 
 557 
 558     /**
 559      * Derives a package name from a file path to a .class file.
 560      */
 561     private static String toPackageName(Path top, Path file) {
 562         Path entry = top.relativize(file);
 563         Path parent = entry.getParent();
 564         if (parent == null) {
 565             return warnIfModuleInfo(top, entry.toString());
 566         } else {
 567             return parent.toString().replace(File.separatorChar, '.');
 568         }
 569     }
 570 
 571     /**
 572      * Derives a package name from the name of an entry in a JAR file.
 573      */
< prev index next >