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