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 */ newPlugin(String name, Map<String, String> configuration, ModuleLayer pluginsLayer)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 */ PluginsConfiguration()79 public PluginsConfiguration() { 80 this(Collections.emptyList()); 81 } 82 83 /** 84 * Plugins configuration. 85 * 86 * @param plugins List of plugins. 87 */ PluginsConfiguration(List<Plugin> plugins)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 */ PluginsConfiguration(List<Plugin> plugins, ImageBuilder imageBuilder, String lastSorterPluginName)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 */ getPlugins()113 public List<Plugin> getPlugins() { 114 return plugins; 115 } 116 117 /** 118 * @return the imageBuilder 119 */ getImageBuilder()120 public ImageBuilder getImageBuilder() { 121 return imageBuilder; 122 } 123 124 /** 125 * @return the lastSorterPluginName 126 */ getLastSorterPluginName()127 public String getLastSorterPluginName() { 128 return lastSorterPluginName; 129 } 130 131 @Override toString()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 */ JlinkConfiguration(Path output, Set<String> modules, ByteOrder endian, ModuleFinder finder)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 */ getByteOrder()177 public ByteOrder getByteOrder() { 178 return endian; 179 } 180 181 /** 182 * @return the output 183 */ getOutput()184 public Path getOutput() { 185 return output; 186 } 187 188 /** 189 * @return the modules 190 */ getModules()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 */ finder()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 */ resolveAndBind()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 */ resolve()218 public Configuration resolve() 219 { 220 return Configuration.empty().resolve(finder, 221 ModuleFinder.of(), 222 modules); 223 } 224 225 @Override toString()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 */ Jlink()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 */ build(JlinkConfiguration config)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 */ build(JlinkConfiguration config, PluginsConfiguration pluginsConfig)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 addAutoEnabledPlugins(PluginsConfiguration pluginsConfig)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 */ postProcess(ExecutableImage image, List<Plugin> plugins)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 } 321