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