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