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