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.tools.jlink.internal;
  26 
  27 import java.lang.module.Configuration;
  28 import java.lang.module.ModuleFinder;
  29 import java.lang.reflect.Layer;
  30 import java.nio.ByteOrder;
  31 import java.nio.file.Path;
  32 import java.util.ArrayList;
  33 import java.util.Collections;
  34 import java.util.List;
  35 import java.util.Map;
  36 import java.util.Objects;
  37 import java.util.Set;
  38 
  39 import jdk.internal.module.ModulePath;
  40 import jdk.tools.jlink.plugin.Plugin;
  41 import jdk.tools.jlink.plugin.PluginException;
  42 import jdk.tools.jlink.builder.ImageBuilder;
  43 
  44 /**
  45  * API to call jlink.
  46  */
  47 public final class Jlink {
  48 
  49     /**
  50      * Create a plugin.
  51      *
  52      * @param name Plugin name
  53      * @param configuration Plugin configuration.
  54      * @param pluginsLayer Plugins Layer. null means boot layer.
  55      * @return A new plugin or null if plugin is unknown.
  56      */
  57     public static Plugin newPlugin(String name,
  58             Map<String, String> configuration, Layer pluginsLayer) {
  59         Objects.requireNonNull(name);
  60         Objects.requireNonNull(configuration);
  61         pluginsLayer = pluginsLayer == null ? Layer.boot() : pluginsLayer;
  62         return PluginRepository.newPlugin(configuration, name, pluginsLayer);
  63     }
  64 
  65     /**
  66      * A complete plugin configuration. Instances of this class are used to
  67      * configure jlink.
  68      */
  69     public static final class PluginsConfiguration {
  70 
  71         private final List<Plugin> plugins;
  72         private final ImageBuilder imageBuilder;
  73         private final String lastSorterPluginName;
  74 
  75         /**
  76          * Empty plugins configuration.
  77          */
  78         public PluginsConfiguration() {
  79             this(Collections.emptyList());
  80         }
  81 
  82         /**
  83          * Plugins configuration.
  84          *
  85          * @param plugins List of plugins.
  86          */
  87         public PluginsConfiguration(List<Plugin> plugins) {
  88             this(plugins, null, null);
  89         }
  90 
  91         /**
  92          * Plugins configuration with a last sorter and an ImageBuilder. No
  93          * sorting can occur after the last sorter plugin. The ImageBuilder is
  94          * in charge to layout the image content on disk.
  95          *
  96          * @param plugins List of transformer plugins.
  97          * @param imageBuilder Image builder.
  98          * @param lastSorterPluginName Name of last sorter plugin, no sorting
  99          * can occur after it.
 100          */
 101         public PluginsConfiguration(List<Plugin> plugins,
 102                 ImageBuilder imageBuilder, String lastSorterPluginName) {
 103             this.plugins = plugins == null ? Collections.emptyList()
 104                     : plugins;
 105             this.imageBuilder = imageBuilder;
 106             this.lastSorterPluginName = lastSorterPluginName;
 107         }
 108 
 109         /**
 110          * @return the plugins
 111          */
 112         public List<Plugin> getPlugins() {
 113             return plugins;
 114         }
 115 
 116         /**
 117          * @return the imageBuilder
 118          */
 119         public ImageBuilder getImageBuilder() {
 120             return imageBuilder;
 121         }
 122 
 123         /**
 124          * @return the lastSorterPluginName
 125          */
 126         public String getLastSorterPluginName() {
 127             return lastSorterPluginName;
 128         }
 129 
 130         @Override
 131         public String toString() {
 132             StringBuilder builder = new StringBuilder();
 133             builder.append("imagebuilder=").append(imageBuilder).append("\n");
 134             StringBuilder pluginsBuilder = new StringBuilder();
 135             for (Plugin p : plugins) {
 136                 pluginsBuilder.append(p).append(",");
 137             }
 138             builder.append("plugins=").append(pluginsBuilder).append("\n");
 139             builder.append("lastsorter=").append(lastSorterPluginName).append("\n");
 140 
 141             return builder.toString();
 142         }
 143     }
 144 
 145     /**
 146      * Jlink configuration. Instances of this class are used to configure jlink.
 147      */
 148     public static final class JlinkConfiguration {
 149 
 150         private final List<Path> modulepaths;
 151         private final Path output;
 152         private final Set<String> modules;
 153         private final Set<String> limitmods;
 154         private final ByteOrder endian;
 155         private final ModuleFinder finder;
 156 
 157         /**
 158          * jlink configuration,
 159          *
 160          * @param output Output directory, must not exist.
 161          * @param modulepaths Modules paths
 162          * @param modules Root modules to resolve
 163          * @param limitmods Limit the universe of observable modules
 164          * @param endian Jimage byte order. Native order by default
 165          */
 166         public JlinkConfiguration(Path output,
 167                                   List<Path> modulepaths,
 168                                   Set<String> modules,
 169                                   Set<String> limitmods,
 170                                   ByteOrder endian) {
 171             if (Objects.requireNonNull(modulepaths).isEmpty()) {
 172                 throw new IllegalArgumentException("Empty module path");
 173             }
 174             if (Objects.requireNonNull(modules).isEmpty()) {
 175                 throw new IllegalArgumentException("Empty modules");
 176             }
 177 
 178             this.output = output;
 179             this.modulepaths = modulepaths;
 180             this.modules = modules;
 181             this.limitmods = Objects.requireNonNull(limitmods);
 182             this.endian = Objects.requireNonNull(endian);
 183             this.finder = moduleFinder();
 184         }
 185 
 186         /**
 187          * @return the modulepaths
 188          */
 189         public List<Path> getModulepaths() {
 190             return modulepaths;
 191         }
 192 
 193         /**
 194          * @return the byte ordering
 195          */
 196         public ByteOrder getByteOrder() {
 197             return endian;
 198         }
 199 
 200         /**
 201          * @return the output
 202          */
 203         public Path getOutput() {
 204             return output;
 205         }
 206 
 207         /**
 208          * @return the modules
 209          */
 210         public Set<String> getModules() {
 211             return modules;
 212         }
 213 
 214         /**
 215          * @return the limitmods
 216          */
 217         public Set<String> getLimitmods() {
 218             return limitmods;
 219         }
 220 
 221         /**
 222          * Returns {@link ModuleFinder} that finds all observable modules
 223          * for this jlink configuration.
 224          */
 225         public ModuleFinder finder() {
 226             return finder;
 227         }
 228 
 229         /**
 230          * Returns a {@link Configuration} of the given module path,
 231          * root modules with full service binding.
 232          */
 233         public Configuration resolveAndBind()
 234         {
 235             return Configuration.empty().resolveAndBind(finder,
 236                                                         ModuleFinder.of(),
 237                                                         modules);
 238         }
 239 
 240         /**
 241          * Returns a {@link Configuration} of the given module path,
 242          * root modules with no service binding.
 243          */
 244         public Configuration resolve()
 245         {
 246             return Configuration.empty().resolve(finder,
 247                                                  ModuleFinder.of(),
 248                                                  modules);
 249         }
 250 
 251         private ModuleFinder moduleFinder() {
 252             Path[] entries = modulepaths.toArray(new Path[0]);
 253             ModuleFinder finder = ModulePath.of(Runtime.version(), true, entries);
 254             if (!limitmods.isEmpty()) {
 255                 finder = JlinkTask.limitFinder(finder, limitmods, modules);
 256             }
 257             return finder;
 258         }
 259 
 260         @Override
 261         public String toString() {
 262             StringBuilder builder = new StringBuilder();
 263 
 264             builder.append("output=").append(output).append("\n");
 265             StringBuilder pathsBuilder = new StringBuilder();
 266             for (Path p : modulepaths) {
 267                 pathsBuilder.append(p).append(",");
 268             }
 269             builder.append("modulepaths=").append(pathsBuilder).append("\n");
 270 
 271             StringBuilder modsBuilder = new StringBuilder();
 272             for (String p : modules) {
 273                 modsBuilder.append(p).append(",");
 274             }
 275             builder.append("modules=").append(modsBuilder).append("\n");
 276 
 277             StringBuilder limitsBuilder = new StringBuilder();
 278             for (String p : limitmods) {
 279                 limitsBuilder.append(p).append(",");
 280             }
 281             builder.append("limitmodules=").append(limitsBuilder).append("\n");
 282             builder.append("endian=").append(endian).append("\n");
 283             return builder.toString();
 284         }
 285     }
 286 
 287     /**
 288      * Jlink instance constructor, if a security manager is set, the jlink
 289      * permission is checked.
 290      */
 291     public Jlink() {
 292         if (System.getSecurityManager() != null) {
 293             System.getSecurityManager().
 294                     checkPermission(new JlinkPermission("jlink"));
 295         }
 296     }
 297 
 298     /**
 299      * Build the image.
 300      *
 301      * @param config Jlink config, must not be null.
 302      * @throws PluginException
 303      */
 304     public void build(JlinkConfiguration config) {
 305         build(config, null);
 306     }
 307 
 308     /**
 309      * Build the image with a plugin configuration.
 310      *
 311      * @param config Jlink config, must not be null.
 312      * @param pluginsConfig Plugins config, can be null
 313      * @throws PluginException
 314      */
 315     public void build(JlinkConfiguration config, PluginsConfiguration pluginsConfig) {
 316         Objects.requireNonNull(config);
 317         if (pluginsConfig == null) {
 318             pluginsConfig = new PluginsConfiguration();
 319         }
 320 
 321         // add all auto-enabled plugins from boot layer
 322         pluginsConfig = addAutoEnabledPlugins(pluginsConfig);
 323 
 324         try {
 325             JlinkTask.createImage(config, pluginsConfig);
 326         } catch (Exception ex) {
 327             throw new PluginException(ex);
 328         }
 329     }
 330 
 331     private PluginsConfiguration addAutoEnabledPlugins(PluginsConfiguration pluginsConfig) {
 332         List<Plugin> plugins = new ArrayList<>(pluginsConfig.getPlugins());
 333         List<Plugin> bootPlugins = PluginRepository.getPlugins(Layer.boot());
 334         for (Plugin bp : bootPlugins) {
 335             if (Utils.isAutoEnabled(bp)) {
 336                 try {
 337                     bp.configure(Collections.emptyMap());
 338                 } catch (IllegalArgumentException e) {
 339                     if (JlinkTask.DEBUG) {
 340                         System.err.println("Plugin " + bp.getName() + " threw exception with config: {}");
 341                         e.printStackTrace();
 342                     }
 343                     throw e;
 344                 }
 345                 plugins.add(bp);
 346             }
 347         }
 348         return new PluginsConfiguration(plugins, pluginsConfig.getImageBuilder(),
 349             pluginsConfig.getLastSorterPluginName());
 350     }
 351 
 352     /**
 353      * Post process the image with a plugin configuration.
 354      *
 355      * @param image Existing image.
 356      * @param plugins Plugins cannot be null
 357      */
 358     public void postProcess(ExecutableImage image, List<Plugin> plugins) {
 359         Objects.requireNonNull(image);
 360         Objects.requireNonNull(plugins);
 361         try {
 362             JlinkTask.postProcessImage(image, plugins);
 363         } catch (Exception ex) {
 364             throw new PluginException(ex);
 365         }
 366     }
 367 }