jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ModulePoolImpl.java
changeset 39934 9c84ee88dd3a
parent 39933 c0dd0f198453
parent 39922 e613affb88d1
child 39935 6016bd47edc9
equal deleted inserted replaced
39933:c0dd0f198453 39934:9c84ee88dd3a
     1 /*
       
     2  * Copyright (c) 2015, 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.tools.jlink.internal;
       
    26 
       
    27 import java.lang.module.ModuleDescriptor;
       
    28 import java.nio.ByteBuffer;
       
    29 import java.nio.ByteOrder;
       
    30 import java.util.Collections;
       
    31 import java.util.HashMap;
       
    32 import java.util.HashSet;
       
    33 import java.util.LinkedHashMap;
       
    34 import java.util.Map;
       
    35 import java.util.Objects;
       
    36 import java.util.Optional;
       
    37 import java.util.Set;
       
    38 import java.util.function.Function;
       
    39 import java.util.stream.Stream;
       
    40 import jdk.internal.jimage.decompressor.CompressedResourceHeader;
       
    41 import jdk.tools.jlink.plugin.ModulePool;
       
    42 import jdk.tools.jlink.plugin.LinkModule;
       
    43 import jdk.tools.jlink.plugin.ModuleEntry;
       
    44 import jdk.tools.jlink.plugin.PluginException;
       
    45 import jdk.tools.jlink.internal.plugins.FileCopierPlugin;
       
    46 
       
    47 /**
       
    48  * Pool of module data.
       
    49  */
       
    50 public class ModulePoolImpl implements ModulePool {
       
    51 
       
    52     private class ModuleImpl implements LinkModule {
       
    53 
       
    54         final Map<String, ModuleEntry> moduleContent = new LinkedHashMap<>();
       
    55         private ModuleDescriptor descriptor;
       
    56         final String name;
       
    57 
       
    58         private ModuleImpl(String name) {
       
    59             this.name = name;
       
    60         }
       
    61 
       
    62         @Override
       
    63         public String getName() {
       
    64             return name;
       
    65         }
       
    66 
       
    67         @Override
       
    68         public Optional<ModuleEntry> findEntry(String path) {
       
    69             if (!path.startsWith("/")) {
       
    70                 path = "/" + path;
       
    71             }
       
    72             if (!path.startsWith("/" + name)) {
       
    73                 path = "/" + name + path;
       
    74             }
       
    75             return Optional.ofNullable(moduleContent.get(path));
       
    76         }
       
    77 
       
    78         @Override
       
    79         public ModuleDescriptor getDescriptor() {
       
    80             if (descriptor == null) {
       
    81                 String p = "/" + name + "/module-info.class";
       
    82                 Optional<ModuleEntry> content = findEntry(p);
       
    83                 if (!content.isPresent()) {
       
    84                     throw new PluginException("No module-info for " + name
       
    85                             + " module");
       
    86                 }
       
    87                 ByteBuffer bb = ByteBuffer.wrap(content.get().getBytes());
       
    88                 descriptor = ModuleDescriptor.read(bb);
       
    89             }
       
    90             return descriptor;
       
    91         }
       
    92 
       
    93         @Override
       
    94         public void add(ModuleEntry data) {
       
    95             if (isReadOnly()) {
       
    96                 throw new PluginException("ModulePool is readonly");
       
    97             }
       
    98             Objects.requireNonNull(data);
       
    99             if (!data.getModule().equals(name)) {
       
   100                 throw new PluginException("Can't add resource " + data.getPath()
       
   101                         + " to module " + name);
       
   102             }
       
   103             ModulePoolImpl.this.add(data);
       
   104         }
       
   105 
       
   106         @Override
       
   107         public Set<String> getAllPackages() {
       
   108             Set<String> pkgs = new HashSet<>();
       
   109             moduleContent.values().stream().filter(m -> m.getType().
       
   110                     equals(ModuleEntry.Type.CLASS_OR_RESOURCE)).forEach(res -> {
       
   111                 // Module metadata only contains packages with .class files
       
   112                 if (ImageFileCreator.isClassPackage(res.getPath())) {
       
   113                     String[] split = ImageFileCreator.splitPath(res.getPath());
       
   114                     String pkg = split[1];
       
   115                     if (pkg != null && !pkg.isEmpty()) {
       
   116                         pkgs.add(pkg);
       
   117                     }
       
   118                 }
       
   119             });
       
   120             return pkgs;
       
   121         }
       
   122 
       
   123         @Override
       
   124         public String toString() {
       
   125             return getName();
       
   126         }
       
   127 
       
   128         @Override
       
   129         public Stream<? extends ModuleEntry> entries() {
       
   130             return moduleContent.values().stream();
       
   131         }
       
   132 
       
   133         @Override
       
   134         public int getEntryCount() {
       
   135             return moduleContent.values().size();
       
   136         }
       
   137     }
       
   138 
       
   139     private final Map<String, ModuleEntry> resources = new LinkedHashMap<>();
       
   140     private final Map<String, ModuleImpl> modules = new LinkedHashMap<>();
       
   141     private final ModuleImpl fileCopierModule = new ModuleImpl(FileCopierPlugin.FAKE_MODULE);
       
   142     private Map<String, String> releaseProps = new HashMap<>();
       
   143 
       
   144     private final ByteOrder order;
       
   145 
       
   146     private boolean isReadOnly;
       
   147     private final StringTable table;
       
   148 
       
   149     public ModulePoolImpl() {
       
   150         this(ByteOrder.nativeOrder());
       
   151     }
       
   152 
       
   153     public ModulePoolImpl(ByteOrder order) {
       
   154         this(order, new StringTable() {
       
   155 
       
   156             @Override
       
   157             public int addString(String str) {
       
   158                 return -1;
       
   159             }
       
   160 
       
   161             @Override
       
   162             public String getString(int id) {
       
   163                 return null;
       
   164             }
       
   165         });
       
   166     }
       
   167 
       
   168     public ModulePoolImpl(ByteOrder order, StringTable table) {
       
   169         this.order = Objects.requireNonNull(order);
       
   170         this.table = Objects.requireNonNull(table);
       
   171     }
       
   172 
       
   173     /**
       
   174      * Add a ModuleEntry.
       
   175      *
       
   176      * @param data The ModuleEntry to add.
       
   177      */
       
   178     @Override
       
   179     public void add(ModuleEntry data) {
       
   180         if (isReadOnly()) {
       
   181             throw new PluginException("ModulePool is readonly");
       
   182         }
       
   183         Objects.requireNonNull(data);
       
   184         if (resources.get(data.getPath()) != null) {
       
   185             throw new PluginException("Resource " + data.getPath()
       
   186                     + " already present");
       
   187         }
       
   188         String modulename = data.getModule();
       
   189         ModuleImpl m = modules.get(modulename);
       
   190         // ## TODO: FileCopierPlugin should not add content to a module
       
   191         // FAKE_MODULE is not really a module to be added in the image
       
   192         if (FileCopierPlugin.FAKE_MODULE.equals(modulename)) {
       
   193             m = fileCopierModule;
       
   194         }
       
   195         if (m == null) {
       
   196             m = new ModuleImpl(modulename);
       
   197             modules.put(modulename, m);
       
   198         }
       
   199         resources.put(data.getPath(), data);
       
   200         m.moduleContent.put(data.getPath(), data);
       
   201     }
       
   202 
       
   203     /**
       
   204      * Retrieves the module for the provided name.
       
   205      *
       
   206      * @param name The module name
       
   207      * @return the module of matching name, if found
       
   208      */
       
   209     @Override
       
   210     public Optional<LinkModule> findModule(String name) {
       
   211         Objects.requireNonNull(name);
       
   212         return Optional.ofNullable(modules.get(name));
       
   213     }
       
   214 
       
   215     /**
       
   216      * The stream of modules contained in this ModulePool.
       
   217      *
       
   218      * @return The stream of modules.
       
   219      */
       
   220     @Override
       
   221     public Stream<? extends LinkModule> modules() {
       
   222         return modules.values().stream();
       
   223     }
       
   224 
       
   225     /**
       
   226      * Return the number of LinkModule count in this ModulePool.
       
   227      *
       
   228      * @return the module count.
       
   229      */
       
   230     @Override
       
   231     public int getModuleCount() {
       
   232         return modules.size();
       
   233     }
       
   234 
       
   235     /**
       
   236      * Get all ModuleEntry contained in this ModulePool instance.
       
   237      *
       
   238      * @return The stream of LinkModuleEntries.
       
   239      */
       
   240     @Override
       
   241     public Stream<? extends ModuleEntry> entries() {
       
   242         return resources.values().stream();
       
   243     }
       
   244 
       
   245     /**
       
   246      * Return the number of ModuleEntry count in this ModulePool.
       
   247      *
       
   248      * @return the entry count.
       
   249      */
       
   250     @Override
       
   251     public int getEntryCount() {
       
   252         return resources.values().size();
       
   253     }
       
   254 
       
   255     /**
       
   256      * Get the ModuleEntry for the passed path.
       
   257      *
       
   258      * @param path A data path
       
   259      * @return A ModuleEntry instance or null if the data is not found
       
   260      */
       
   261     @Override
       
   262     public Optional<ModuleEntry> findEntry(String path) {
       
   263         Objects.requireNonNull(path);
       
   264         return Optional.ofNullable(resources.get(path));
       
   265     }
       
   266 
       
   267     /**
       
   268      * Get the ModuleEntry for the passed path restricted to supplied context.
       
   269      *
       
   270      * @param path A data path
       
   271      * @param context A context of the search
       
   272      * @return A ModuleEntry instance or null if the data is not found
       
   273      */
       
   274     @Override
       
   275     public Optional<ModuleEntry> findEntryInContext(String path, ModuleEntry context) {
       
   276         Objects.requireNonNull(path);
       
   277         Objects.requireNonNull(context);
       
   278         LinkModule module = modules.get(context.getModule());
       
   279         Objects.requireNonNull(module);
       
   280         Optional<ModuleEntry> entry = module.findEntry(path);
       
   281         // Navigating other modules via requires and exports is problematic
       
   282         // since we cannot construct the runtime model of loaders and layers.
       
   283         return entry;
       
   284      }
       
   285 
       
   286     /**
       
   287      * Check if the ModulePool contains the given ModuleEntry.
       
   288      *
       
   289      * @param data The module data to check existence for.
       
   290      * @return The module data or null if not found.
       
   291      */
       
   292     @Override
       
   293     public boolean contains(ModuleEntry data) {
       
   294         Objects.requireNonNull(data);
       
   295         return findEntry(data.getPath()).isPresent();
       
   296     }
       
   297 
       
   298     /**
       
   299      * Check if the ModulePool contains some content at all.
       
   300      *
       
   301      * @return True, no content, false otherwise.
       
   302      */
       
   303     @Override
       
   304     public boolean isEmpty() {
       
   305         return resources.isEmpty();
       
   306     }
       
   307 
       
   308     /**
       
   309      * Visit each ModuleEntry in this ModulePool to transform it and
       
   310      * copy the transformed ModuleEntry to the output ModulePool.
       
   311      *
       
   312      * @param transform The function called for each ModuleEntry found in
       
   313      * the ModulePool. The transform function should return a
       
   314      * ModuleEntry instance which will be added to the output or it should
       
   315      * return null if the passed ModuleEntry is to be ignored for the
       
   316      * output.
       
   317      *
       
   318      * @param output The ModulePool to be filled with Visitor returned
       
   319      * ModuleEntry.
       
   320      */
       
   321     @Override
       
   322     public void transformAndCopy(Function<ModuleEntry, ModuleEntry> transform,
       
   323             ModulePool output) {
       
   324         entries().forEach(resource -> {
       
   325             ModuleEntry res = transform.apply(resource);
       
   326             if (res != null) {
       
   327                 output.add(res);
       
   328             }
       
   329         });
       
   330     }
       
   331 
       
   332     /**
       
   333      * The ByteOrder currently in use when generating the jimage file.
       
   334      *
       
   335      * @return The ByteOrder.
       
   336      */
       
   337     @Override
       
   338     public ByteOrder getByteOrder() {
       
   339         return order;
       
   340     }
       
   341 
       
   342     @Override
       
   343     public Map<String, String> getReleaseProperties() {
       
   344         return isReadOnly()? Collections.unmodifiableMap(releaseProps) : releaseProps;
       
   345     }
       
   346 
       
   347     public StringTable getStringTable() {
       
   348         return table;
       
   349     }
       
   350 
       
   351     /**
       
   352      * Make this Resources instance read-only. No resource can be added.
       
   353      */
       
   354     public void setReadOnly() {
       
   355         isReadOnly = true;
       
   356     }
       
   357 
       
   358     /**
       
   359      * Read only state.
       
   360      *
       
   361      * @return true if readonly false otherwise.
       
   362      */
       
   363     @Override
       
   364     public boolean isReadOnly() {
       
   365         return isReadOnly;
       
   366     }
       
   367 
       
   368     /**
       
   369      * A resource that has been compressed.
       
   370      */
       
   371     public static final class CompressedModuleData extends ByteArrayModuleEntry {
       
   372 
       
   373         final long uncompressed_size;
       
   374 
       
   375         private CompressedModuleData(String module, String path,
       
   376                 byte[] content, long uncompressed_size) {
       
   377             super(module, path, ModuleEntry.Type.CLASS_OR_RESOURCE, content);
       
   378             this.uncompressed_size = uncompressed_size;
       
   379         }
       
   380 
       
   381         public long getUncompressedSize() {
       
   382             return uncompressed_size;
       
   383         }
       
   384 
       
   385         @Override
       
   386         public boolean equals(Object other) {
       
   387             if (!(other instanceof CompressedModuleData)) {
       
   388                 return false;
       
   389             }
       
   390             CompressedModuleData f = (CompressedModuleData) other;
       
   391             return f.getPath().equals(getPath());
       
   392         }
       
   393 
       
   394         @Override
       
   395         public int hashCode() {
       
   396             return super.hashCode();
       
   397         }
       
   398     }
       
   399 
       
   400     public static CompressedModuleData newCompressedResource(ModuleEntry original,
       
   401             ByteBuffer compressed,
       
   402             String plugin, String pluginConfig, StringTable strings,
       
   403             ByteOrder order) {
       
   404         Objects.requireNonNull(original);
       
   405         Objects.requireNonNull(compressed);
       
   406         Objects.requireNonNull(plugin);
       
   407 
       
   408         boolean isTerminal = !(original instanceof CompressedModuleData);
       
   409         long uncompressed_size = original.getLength();
       
   410         if (original instanceof CompressedModuleData) {
       
   411             CompressedModuleData comp = (CompressedModuleData) original;
       
   412             uncompressed_size = comp.getUncompressedSize();
       
   413         }
       
   414         int nameOffset = strings.addString(plugin);
       
   415         int configOffset = -1;
       
   416         if (pluginConfig != null) {
       
   417             configOffset = strings.addString(plugin);
       
   418         }
       
   419         CompressedResourceHeader rh
       
   420                 = new CompressedResourceHeader(compressed.limit(), original.getLength(),
       
   421                         nameOffset, configOffset, isTerminal);
       
   422         // Merge header with content;
       
   423         byte[] h = rh.getBytes(order);
       
   424         ByteBuffer bb = ByteBuffer.allocate(compressed.limit() + h.length);
       
   425         bb.order(order);
       
   426         bb.put(h);
       
   427         bb.put(compressed);
       
   428         byte[] contentWithHeader = bb.array();
       
   429 
       
   430         CompressedModuleData compressedResource
       
   431                 = new CompressedModuleData(original.getModule(), original.getPath(),
       
   432                         contentWithHeader, uncompressed_size);
       
   433         return compressedResource;
       
   434     }
       
   435 
       
   436 }