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