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