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