< prev index next >

src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ImageFileCreator.java

Print this page

  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 }
< prev index next >