< prev index next >

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

Print this page


   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 package jdk.internal.module;
  26 
  27 import java.io.Closeable;
  28 import java.io.File;
  29 import java.io.IOError;
  30 import java.io.IOException;
  31 import java.io.InputStream;
  32 import java.io.UncheckedIOException;
  33 import java.lang.module.ModuleDescriptor;

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

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


  91         }
  92     }
  93 
  94     /**
  95      * Returns a module reference that interposes on the given module if
  96      * needed. If there are no patches for the given module then the module
  97      * reference is simply returned. Otherwise the patches for the module
  98      * are scanned (to find any new packages) and a new module reference is
  99      * returned.
 100      *
 101      * @throws UncheckedIOException if an I/O error is detected
 102      */
 103     public ModuleReference patchIfNeeded(ModuleReference mref) {
 104         // if there are no patches for the module then nothing to do
 105         ModuleDescriptor descriptor = mref.descriptor();
 106         String mn = descriptor.name();
 107         List<Path> paths = map.get(mn);
 108         if (paths == null)
 109             return mref;
 110 
 111         // scan the JAR file or directory tree to get the set of packages


 112         Set<String> packages = new HashSet<>();

 113         try {
 114             for (Path file : paths) {
 115                 if (Files.isRegularFile(file)) {
 116 
 117                     // JAR file - do not open as a multi-release JAR as this
 118                     // is not supported by the boot class loader
 119                     try (JarFile jf = new JarFile(file.toFile())) {
 120                         jf.stream()


 121                           .map(e -> toPackageName(file, e))
 122                           .filter(Checks::isJavaIdentifier)
 123                           .forEach(packages::add);
 124                     }
 125 
 126                 } else if (Files.isDirectory(file)) {
 127 
 128                     // exploded directory without following sym links
 129                     Path top = file;
 130                     Files.find(top, Integer.MAX_VALUE,
 131                                ((path, attrs) -> attrs.isRegularFile()))


 132                             .map(path -> toPackageName(top, path))
 133                             .filter(Checks::isJavaIdentifier)
 134                             .forEach(packages::add);
 135 
 136                 }
 137             }
 138 
 139         } catch (IOException ioe) {
 140             throw new UncheckedIOException(ioe);
 141         }
 142 
 143         // if there are new packages then we need a new ModuleDescriptor
 144         Set<String> original = descriptor.packages();
 145         packages.addAll(original);
 146         if (packages.size() > original.size()) {
 147             descriptor = JLMA.newModuleDescriptor(descriptor, packages);




















 148         }
 149 
 150         // return a module reference to the patched module
 151         URI location = mref.location().orElse(null);
 152 
 153         ModuleHashes recordedHashes = null;
 154         ModuleResolution mres = null;
 155         if (mref instanceof ModuleReferenceImpl) {
 156             ModuleReferenceImpl impl = (ModuleReferenceImpl)mref;
 157             recordedHashes = impl.recordedHashes();
 158             mres = impl.moduleResolution();
 159         }
 160 
 161         return new ModuleReferenceImpl(descriptor,
 162                                        location,
 163                                        () -> new PatchedModuleReader(paths, mref),
 164                                        this,
 165                                        recordedHashes,
 166                                        null,
 167                                        mres);


 454                     .map(JarEntry::getName);
 455         }
 456     }
 457 
 458 
 459     /**
 460      * A ResourceFinder that finds resources on the file system.
 461      */
 462     private static class ExplodedResourceFinder implements ResourceFinder {
 463         private final Path dir;
 464 
 465         ExplodedResourceFinder(Path dir) {
 466             this.dir = dir;
 467         }
 468 
 469         @Override
 470         public void close() { }
 471 
 472         @Override
 473         public Resource find(String name) throws IOException {
 474             Path file = Paths.get(name.replace('/', File.separatorChar));
 475             if (file.getRoot() == null) {
 476                 file = dir.resolve(file);
 477             } else {
 478                 // drop the root component so that the resource is
 479                 // located relative to the module directory
 480                 int n = file.getNameCount();
 481                 if (n == 0)
 482                     return null;
 483                 file = dir.resolve(file.subpath(0, n));
 484             }
 485 
 486             if (Files.isRegularFile(file)) {
 487                 return newResource(name, dir, file);
 488             } else {
 489                 return null;
 490             }
 491         }


 492 
 493         private Resource newResource(String name, Path top, Path file) {
 494             return new Resource() {
 495                 @Override
 496                 public String getName() {
 497                     return name;
 498                 }
 499                 @Override
 500                 public URL getURL() {
 501                     try {
 502                         return file.toUri().toURL();
 503                     } catch (IOException | IOError e) {
 504                         return null;
 505                     }
 506                 }
 507                 @Override
 508                 public URL getCodeSourceURL() {
 509                     try {
 510                         return top.toUri().toURL();
 511                     } catch (IOException | IOError e) {


   1 /*
   2  * Copyright (c) 2015, 2017, 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 package jdk.internal.module;
  26 
  27 import java.io.Closeable;
  28 import java.io.File;
  29 import java.io.IOError;
  30 import java.io.IOException;
  31 import java.io.InputStream;
  32 import java.io.UncheckedIOException;
  33 import java.lang.module.ModuleDescriptor;
  34 import java.lang.module.ModuleDescriptor.Builder;
  35 import java.lang.module.ModuleReader;
  36 import java.lang.module.ModuleReference;
  37 import java.net.MalformedURLException;
  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.


  93         }
  94     }
  95 
  96     /**
  97      * Returns a module reference that interposes on the given module if
  98      * needed. If there are no patches for the given module then the module
  99      * reference is simply returned. Otherwise the patches for the module
 100      * are scanned (to find any new packages) and a new module reference is
 101      * returned.
 102      *
 103      * @throws UncheckedIOException if an I/O error is detected
 104      */
 105     public ModuleReference patchIfNeeded(ModuleReference mref) {
 106         // if there are no patches for the module then nothing to do
 107         ModuleDescriptor descriptor = mref.descriptor();
 108         String mn = descriptor.name();
 109         List<Path> paths = map.get(mn);
 110         if (paths == null)
 111             return mref;
 112 
 113         // Scan the JAR file or directory tree to get the set of packages.
 114         // For automatic modules then packages that do not contain class files
 115         // must be ignored.
 116         Set<String> packages = new HashSet<>();
 117         boolean isAutomatic = descriptor.isAutomatic();
 118         try {
 119             for (Path file : paths) {
 120                 if (Files.isRegularFile(file)) {
 121 
 122                     // JAR file - do not open as a multi-release JAR as this
 123                     // is not supported by the boot class loader
 124                     try (JarFile jf = new JarFile(file.toFile())) {
 125                         jf.stream()
 126                           .filter(e -> !e.isDirectory()
 127                                   && (!isAutomatic || e.getName().endsWith(".class")))
 128                           .map(e -> toPackageName(file, e))
 129                           .filter(Checks::isPackageName)
 130                           .forEach(packages::add);
 131                     }
 132 
 133                 } else if (Files.isDirectory(file)) {
 134 
 135                     // exploded directory without following sym links
 136                     Path top = file;
 137                     Files.find(top, Integer.MAX_VALUE,
 138                                ((path, attrs) -> attrs.isRegularFile()))
 139                             .filter(path -> !isAutomatic
 140                                     || path.toString().endsWith(".class"))
 141                             .map(path -> toPackageName(top, path))
 142                             .filter(Checks::isPackageName)
 143                             .forEach(packages::add);
 144 
 145                 }
 146             }
 147 
 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);


 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) {


< prev index next >