1 /*
2 * Copyright (c) 2014, 2024, 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
32 import java.io.DataOutputStream;
33 import java.io.IOException;
34 import java.io.OutputStream;
35 import java.io.UncheckedIOException;
36 import java.nio.ByteOrder;
37 import java.nio.charset.StandardCharsets;
38 import java.nio.file.Files;
39 import java.nio.file.Path;
40 import java.util.ArrayList;
41 import java.util.Collections;
42 import java.util.HashMap;
43 import java.util.HashSet;
44 import java.util.List;
45 import java.util.Map;
46 import java.util.Objects;
47 import java.util.Optional;
48 import java.util.Set;
49 import java.util.stream.Collectors;
50 import java.util.stream.Stream;
51
52 import jdk.tools.jlink.internal.Archive.Entry;
53 import jdk.tools.jlink.internal.Archive.Entry.EntryType;
54 import jdk.tools.jlink.internal.JRTArchive.ResourceFileEntry;
55 import jdk.tools.jlink.internal.ResourcePoolManager.CompressedModuleData;
56 import jdk.tools.jlink.internal.runtimelink.JimageDiffGenerator;
57 import jdk.tools.jlink.internal.runtimelink.JimageDiffGenerator.ImageResource;
58 import jdk.tools.jlink.internal.runtimelink.ResourceDiff;
59 import jdk.tools.jlink.internal.runtimelink.ResourcePoolReader;
60 import jdk.tools.jlink.plugin.PluginException;
61 import jdk.tools.jlink.plugin.ResourcePool;
62 import jdk.tools.jlink.plugin.ResourcePoolBuilder;
63 import jdk.tools.jlink.plugin.ResourcePoolEntry;
64 import jdk.tools.jlink.plugin.ResourcePoolModule;
65
66 /**
67 * An image (native endian.)
68 * <pre>{@code
69 * {
70 * u4 magic;
71 * u2 major_version;
210 * Create a jimage based on content of the given ResourcePoolManager,
211 * optionally creating a runtime that can be used for linking from the
212 * run-time image
213 *
214 * @param allContent The content that needs to get added to the resulting
215 * lib/modules (jimage) file.
216 * @param writer The writer for the jimage file.
217 * @param pluginSupport The stack of all plugins to apply.
218 * @param out The output stream to write the jimage to.
219 * @param generateRuntimeImage if a runtime suitable for linking from the
220 * run-time image should get created.
221 * @return A pool of the actual result resources.
222 * @throws IOException
223 */
224 private static ResourcePool generateJImage(ResourcePoolManager allContent,
225 BasicImageWriter writer,
226 ImagePluginStack pluginSupport,
227 DataOutputStream out,
228 boolean generateRuntimeImage
229 ) throws IOException {
230 ResourcePool resultResources;
231 try {
232 resultResources = pluginSupport.visitResources(allContent);
233 if (generateRuntimeImage) {
234 // Keep track of non-modules resources for linking from a run-time image
235 resultResources = addNonClassResourcesTrackFiles(resultResources,
236 writer);
237 // Generate the diff between the input resources from packaged
238 // modules in 'allContent' to the plugin- or otherwise
239 // generated-content in 'resultResources'
240 resultResources = addResourceDiffFiles(allContent.resourcePool(),
241 resultResources,
242 writer);
243 }
244 } catch (PluginException pe) {
245 if (JlinkTask.DEBUG) {
246 pe.printStackTrace();
247 }
248 throw pe;
249 } catch (Exception ex) {
250 if (JlinkTask.DEBUG) {
251 ex.printStackTrace();
252 }
253 throw new IOException(ex);
254 }
255 Set<String> duplicates = new HashSet<>();
256 long[] offset = new long[1];
257
258 List<ResourcePoolEntry> content = new ArrayList<>();
259 List<String> paths = new ArrayList<>();
260 // the order of traversing the resources and the order of
261 // the module content being written must be the same
262 resultResources.entries().forEach(res -> {
263 if (res.type().equals(ResourcePoolEntry.Type.CLASS_OR_RESOURCE)) {
264 String path = res.path();
265 content.add(res);
266 long uncompressedSize = res.contentLength();
267 long compressedSize = 0;
268 if (res instanceof CompressedModuleData) {
269 CompressedModuleData comp
270 = (CompressedModuleData) res;
271 compressedSize = res.contentLength();
272 uncompressedSize = comp.getUncompressedSize();
273 }
274 long onFileSize = res.contentLength();
275
276 if (duplicates.contains(path)) {
277 System.err.format("duplicate resource \"%s\", skipping%n",
278 path);
279 // TODO Need to hang bytes on resource and write
280 // from resource not zip.
281 // Skipping resource throws off writing from zip.
282 offset[0] += onFileSize;
283 return;
284 }
285 duplicates.add(path);
286 writer.addLocation(path, offset[0], compressedSize, uncompressedSize);
287 paths.add(path);
288 offset[0] += onFileSize;
289 }
290 });
291
292 ImageResourcesTree tree = new ImageResourcesTree(offset[0], writer, paths);
293
294 // write header and indices
295 byte[] bytes = writer.getBytes();
296 out.write(bytes, 0, bytes.length);
297
298 // write module content
299 content.forEach((res) -> {
300 res.write(out);
301 });
302
303 tree.addContent(out);
304
305 out.close();
306
307 return resultResources;
308 }
309
310 /**
311 * Support for creating a runtime suitable for linking from the run-time
312 * image.
313 *
314 * Generates differences between the packaged modules "view" in
315 * {@code jmodContent} to the optimized image in {@code resultContent} and
316 * adds the result to the returned resource pool.
317 *
318 * @param jmodContent The resource pool view of packaged modules
319 * @param resultContent The optimized result generated from the jmodContent
320 * input by applying the plugin stack.
321 * @param writer The image writer.
322 * @return The resource pool with the difference file resources added to
323 * the {@code resultContent}
324 */
325 @SuppressWarnings("try")
326 private static ResourcePool addResourceDiffFiles(ResourcePool jmodContent,
327 ResourcePool resultContent,
328 BasicImageWriter writer) {
329 JimageDiffGenerator generator = new JimageDiffGenerator();
541
542 /**
543 * Creates a ResourcePoolManager from existing resources so that more
544 * resources can be appended.
545 *
546 * @param resultResources The existing resources to initially add.
547 * @param writer The basic image writer.
548 * @return An appendable ResourcePoolManager.
549 */
550 private static ResourcePoolManager createPoolManager(ResourcePool resultResources,
551 BasicImageWriter writer) {
552 ResourcePoolManager resources = createBasicResourcePoolManager(resultResources.byteOrder(),
553 writer);
554 // Note that resources are already sorted in the correct order.
555 // The underlying ResourcePoolManager keeps track of entries via
556 // LinkedHashMap, which keeps values in insertion order. Therefore
557 // adding resources here, preserving that same order is OK.
558 resultResources.entries().forEach(resources::add);
559 return resources;
560 }
561
562 /**
563 * Helper method that splits a Resource path onto 3 items: module, parent
564 * and resource name.
565 *
566 * @param path
567 * @return An array containing module, parent and name.
568 */
569 public static String[] splitPath(String path) {
570 Objects.requireNonNull(path);
571 String noRoot = path.substring(1);
572 int pkgStart = noRoot.indexOf("/");
573 String module = noRoot.substring(0, pkgStart);
574 List<String> result = new ArrayList<>();
575 result.add(module);
576 String pkg = noRoot.substring(pkgStart + 1);
577 String resName;
578 int pkgEnd = pkg.lastIndexOf("/");
579 if (pkgEnd == -1) { // No package.
580 resName = pkg;
581 } else {
582 resName = pkg.substring(pkgEnd + 1);
583 }
584
585 pkg = toPackage(pkg, false);
586 result.add(pkg);
587 result.add(resName);
588
589 String[] array = new String[result.size()];
590 return result.toArray(array);
591 }
592
593 /**
594 * Returns the path of the resource.
595 */
596 public static String resourceName(String path) {
597 Objects.requireNonNull(path);
598 String s = path.substring(1);
599 int index = s.indexOf("/");
600 return s.substring(index + 1);
601 }
602
603 public static String toPackage(String name) {
604 return toPackage(name, false);
605 }
606
607 private static String toPackage(String name, boolean log) {
608 int index = name.lastIndexOf('/');
609 if (index > 0) {
610 return name.substring(0, index).replace('/', '.');
611 } else {
612 // ## unnamed package
613 if (log) {
614 System.err.format("Warning: %s in unnamed package%n", name);
615 }
616 return "";
617 }
618 }
619 }
|
1 /*
2 * Copyright (c) 2014, 2025, 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
32 import java.io.DataOutputStream;
33 import java.io.IOException;
34 import java.io.OutputStream;
35 import java.io.UncheckedIOException;
36 import java.nio.ByteOrder;
37 import java.nio.charset.StandardCharsets;
38 import java.nio.file.Files;
39 import java.nio.file.Path;
40 import java.util.ArrayList;
41 import java.util.Collections;
42 import java.util.HashMap;
43 import java.util.HashSet;
44 import java.util.List;
45 import java.util.Map;
46 import java.util.Objects;
47 import java.util.Optional;
48 import java.util.Set;
49 import java.util.stream.Collectors;
50 import java.util.stream.Stream;
51
52 import jdk.internal.jimage.ImageLocation;
53 import jdk.tools.jlink.internal.Archive.Entry;
54 import jdk.tools.jlink.internal.Archive.Entry.EntryType;
55 import jdk.tools.jlink.internal.JRTArchive.ResourceFileEntry;
56 import jdk.tools.jlink.internal.ResourcePoolManager.CompressedModuleData;
57 import jdk.tools.jlink.internal.runtimelink.JimageDiffGenerator;
58 import jdk.tools.jlink.internal.runtimelink.JimageDiffGenerator.ImageResource;
59 import jdk.tools.jlink.internal.runtimelink.ResourceDiff;
60 import jdk.tools.jlink.internal.runtimelink.ResourcePoolReader;
61 import jdk.tools.jlink.plugin.PluginException;
62 import jdk.tools.jlink.plugin.ResourcePool;
63 import jdk.tools.jlink.plugin.ResourcePoolBuilder;
64 import jdk.tools.jlink.plugin.ResourcePoolEntry;
65 import jdk.tools.jlink.plugin.ResourcePoolModule;
66
67 /**
68 * An image (native endian.)
69 * <pre>{@code
70 * {
71 * u4 magic;
72 * u2 major_version;
211 * Create a jimage based on content of the given ResourcePoolManager,
212 * optionally creating a runtime that can be used for linking from the
213 * run-time image
214 *
215 * @param allContent The content that needs to get added to the resulting
216 * lib/modules (jimage) file.
217 * @param writer The writer for the jimage file.
218 * @param pluginSupport The stack of all plugins to apply.
219 * @param out The output stream to write the jimage to.
220 * @param generateRuntimeImage if a runtime suitable for linking from the
221 * run-time image should get created.
222 * @return A pool of the actual result resources.
223 * @throws IOException
224 */
225 private static ResourcePool generateJImage(ResourcePoolManager allContent,
226 BasicImageWriter writer,
227 ImagePluginStack pluginSupport,
228 DataOutputStream out,
229 boolean generateRuntimeImage
230 ) throws IOException {
231 ResourcePool resultResources =
232 getResourcePool(allContent, writer, pluginSupport, generateRuntimeImage);
233 Set<String> duplicates = new HashSet<>();
234 long[] offset = new long[1];
235
236 List<ResourcePoolEntry> content = new ArrayList<>();
237 List<String> paths = new ArrayList<>();
238 // the order of traversing the resources and the order of
239 // the module content being written must be the same
240 resultResources.entries().forEach(res -> {
241 if (res.type().equals(ResourcePoolEntry.Type.CLASS_OR_RESOURCE)) {
242 String path = res.path();
243 content.add(res);
244 long uncompressedSize = res.contentLength();
245 long compressedSize = 0;
246 if (res instanceof CompressedModuleData) {
247 CompressedModuleData comp
248 = (CompressedModuleData) res;
249 compressedSize = res.contentLength();
250 uncompressedSize = comp.getUncompressedSize();
251 }
252 long onFileSize = res.contentLength();
253
254 if (duplicates.contains(path)) {
255 System.err.format("duplicate resource \"%s\", skipping%n",
256 path);
257 // TODO Need to hang bytes on resource and write
258 // from resource not zip.
259 // Skipping resource throws off writing from zip.
260 offset[0] += onFileSize;
261 return;
262 }
263 int locFlags = ImageLocation.getFlags(
264 res.path(), p -> resultResources.findEntry(p).isPresent());
265 duplicates.add(path);
266 writer.addLocation(path, offset[0], compressedSize, uncompressedSize, locFlags);
267 paths.add(path);
268 offset[0] += onFileSize;
269 }
270 });
271
272 ImageResourcesTree tree = new ImageResourcesTree(offset[0], writer, paths);
273
274 // write header and indices
275 byte[] bytes = writer.getBytes();
276 out.write(bytes, 0, bytes.length);
277
278 // write module content
279 content.forEach((res) -> {
280 res.write(out);
281 });
282
283 tree.addContent(out);
284
285 out.close();
286
287 return resultResources;
288 }
289
290 private static ResourcePool getResourcePool(
291 ResourcePoolManager allContent,
292 BasicImageWriter writer,
293 ImagePluginStack pluginSupport,
294 boolean generateRuntimeImage)
295 throws IOException {
296 ResourcePool resultResources;
297 try {
298 resultResources = pluginSupport.visitResources(allContent);
299 if (generateRuntimeImage) {
300 // Keep track of non-modules resources for linking from a run-time image
301 resultResources = addNonClassResourcesTrackFiles(resultResources,
302 writer);
303 // Generate the diff between the input resources from packaged
304 // modules in 'allContent' to the plugin- or otherwise
305 // generated-content in 'resultResources'
306 resultResources = addResourceDiffFiles(allContent.resourcePool(),
307 resultResources,
308 writer);
309 }
310 } catch (PluginException pe) {
311 if (JlinkTask.DEBUG) {
312 pe.printStackTrace();
313 }
314 throw pe;
315 } catch (Exception ex) {
316 if (JlinkTask.DEBUG) {
317 ex.printStackTrace();
318 }
319 throw new IOException(ex);
320 }
321 return resultResources;
322 }
323
324 /**
325 * Support for creating a runtime suitable for linking from the run-time
326 * image.
327 *
328 * Generates differences between the packaged modules "view" in
329 * {@code jmodContent} to the optimized image in {@code resultContent} and
330 * adds the result to the returned resource pool.
331 *
332 * @param jmodContent The resource pool view of packaged modules
333 * @param resultContent The optimized result generated from the jmodContent
334 * input by applying the plugin stack.
335 * @param writer The image writer.
336 * @return The resource pool with the difference file resources added to
337 * the {@code resultContent}
338 */
339 @SuppressWarnings("try")
340 private static ResourcePool addResourceDiffFiles(ResourcePool jmodContent,
341 ResourcePool resultContent,
342 BasicImageWriter writer) {
343 JimageDiffGenerator generator = new JimageDiffGenerator();
555
556 /**
557 * Creates a ResourcePoolManager from existing resources so that more
558 * resources can be appended.
559 *
560 * @param resultResources The existing resources to initially add.
561 * @param writer The basic image writer.
562 * @return An appendable ResourcePoolManager.
563 */
564 private static ResourcePoolManager createPoolManager(ResourcePool resultResources,
565 BasicImageWriter writer) {
566 ResourcePoolManager resources = createBasicResourcePoolManager(resultResources.byteOrder(),
567 writer);
568 // Note that resources are already sorted in the correct order.
569 // The underlying ResourcePoolManager keeps track of entries via
570 // LinkedHashMap, which keeps values in insertion order. Therefore
571 // adding resources here, preserving that same order is OK.
572 resultResources.entries().forEach(resources::add);
573 return resources;
574 }
575 }
|