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 */ 25package jdk.tools.jlink.internal; 26 27import java.lang.module.Configuration; 28import java.lang.module.ModuleFinder; 29import java.nio.ByteOrder; 30import java.nio.file.Path; 31import java.util.ArrayList; 32import java.util.Collections; 33import java.util.List; 34import java.util.Map; 35import java.util.Objects; 36import java.util.Set; 37 38import jdk.internal.module.ModulePath; 39import jdk.tools.jlink.plugin.Plugin; 40import jdk.tools.jlink.plugin.PluginException; 41import jdk.tools.jlink.builder.ImageBuilder; 42 43/** 44 * API to call jlink. 45 */ 46public 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} 364