69 import java.util.logging.Level;
70 import java.util.logging.Logger;
71 import java.util.stream.Collectors;
72 import java.util.stream.Stream;
73 import java.util.zip.ZipEntry;
74 import java.util.zip.ZipInputStream;
75 import jdk.packager.builders.AbstractAppImageBuilder;
76 import jdk.packager.internal.Module;
77
78
79 public class JLinkBundlerHelper {
80
81 private static final ResourceBundle I18N =
82 ResourceBundle.getBundle(JLinkBundlerHelper.class.getName());
83
84 @SuppressWarnings("unchecked")
85 public static final BundlerParamInfo<List<Path>> MODULE_PATH =
86 new StandardBundlerParam<>(
87 I18N.getString("param.module-path.name"),
88 I18N.getString("param.module-path.description"),
89 "modulepath",
90 (Class<List<Path>>) (Object)List.class,
91 p -> new ArrayList(),
92 (s, p) -> Arrays.asList(s.split("[;:]")).stream()
93 .map(ss -> new File(ss).toPath())
94 .collect(Collectors.toList()));
95
96 @SuppressWarnings("unchecked")
97 public static final BundlerParamInfo<String> MAIN_MODULE =
98 new StandardBundlerParam<>(
99 I18N.getString("param.main.module.name"),
100 I18N.getString("param.main.module.description"),
101 "m",
102 String.class,
103 p -> null,
104 (s, p) -> {
105 return String.valueOf(s);
106 });
107
108 @SuppressWarnings("unchecked")
109 public static final BundlerParamInfo<Set<String>> ADD_MODULES =
110 new StandardBundlerParam<>(
111 I18N.getString("param.add-modules.name"),
112 I18N.getString("param.add-modules.description"),
113 "addmods",
114 (Class<Set<String>>) (Object) Set.class,
115 p -> new LinkedHashSet(),
116 (s, p) -> new LinkedHashSet<>(Arrays.asList(s.split("[,;: ]+"))));
117
118 @SuppressWarnings("unchecked")
119 public static final BundlerParamInfo<Set<String>> LIMIT_MODULES =
120 new StandardBundlerParam<>(
121 I18N.getString("param.limit-modules.name"),
122 I18N.getString("param.limit-modules.description"),
123 "limitmods",
124 (Class<Set<String>>) (Object) Set.class,
125 p -> new LinkedHashSet(),
126 (s, p) -> new LinkedHashSet<>(Arrays.asList(s.split("[,;: ]+"))));
127
128 @SuppressWarnings("unchecked")
129 public static final BundlerParamInfo<Boolean> STRIP_NATIVE_COMMANDS =
130 new StandardBundlerParam<>(
131 I18N.getString("param.strip-executables.name"),
132 I18N.getString("param.strip-executables.description"),
133 "strip-native-commands",
134 Boolean.class,
135 p -> Boolean.TRUE,
136 (s, p) -> Boolean.valueOf(s));
137
138 @SuppressWarnings("unchecked")
139 public static final BundlerParamInfo<Boolean> DETECT_MODS =
140 new StandardBundlerParam<>(
141 I18N.getString("param.detect-modules.name"),
142 I18N.getString("param.detect-modules.description"),
143 "Xdetectmods",
144 Boolean.class,
145 p -> Boolean.FALSE,
146 (s, p) -> Boolean.valueOf(s));
147
148 @SuppressWarnings("unchecked")
149 public static final BundlerParamInfo<Map<String, String>> JLINK_OPTIONS =
150 new StandardBundlerParam<>(
151 I18N.getString("param.jlink-options.name"),
152 I18N.getString("param.jlink-options.description"),
153 "jlinkOptions",
154 (Class<Map<String, String>>) (Object) Map.class,
155 p -> Collections.emptyMap(),
156 (s, p) -> {
157 try {
158 Properties props = new Properties();
159 props.load(new StringReader(s));
160 return new LinkedHashMap<>((Map)props);
161 } catch (IOException e) {
162 return new LinkedHashMap<>();
163 }
164 });
165
166 @SuppressWarnings("unchecked")
167 public static final BundlerParamInfo<String> JLINK_BUILDER =
168 new StandardBundlerParam<>(
169 I18N.getString("param.jlink-builder.name"),
170 I18N.getString("param.jlink-builder.description"),
171 "jlink.builder",
172 String.class,
173 null,
174 (s, p) -> s);
175
176 @SuppressWarnings("unchecked")
177 public static final BundlerParamInfo<Integer> DEBUG_PORT =
178 new StandardBundlerParam<>(
179 I18N.getString("param.main.module.name"),
180 I18N.getString("param.main.module.description"),
181 "Xdebug",
182 Integer.class,
183 p -> null,
184 (s, p) -> {
185 return Integer.valueOf(s);
186 });
187
188 public static String ListOfPathToString(List<Path> value) {
189 String result = "";
190
191 for (Path path : value) {
192 if (result.length() > 0) {
193 result += File.pathSeparator;
194 }
195
196 result += path.toString();
197 }
198
199 return result;
200 }
201
215
216 public static File getMainJar(Map<String, ? super Object> params) {
217 File result = null;
218 RelativeFileSet fileset = MAIN_JAR.fetchFrom(params);
219
220 if (fileset != null) {
221 result = new File(fileset.getIncludedFiles().iterator().next());
222 }
223
224 return result;
225 }
226
227 public static String getMainClass(Map<String, ? super Object> params) {
228 String result = "";
229 File mainJar = getMainJar(params);
230
231 if (mainJar != null) {
232 result = MAIN_CLASS.fetchFrom(params);
233 }
234 else {
235 String mainModule = MAIN_MODULE.fetchFrom(params);
236
237 if (mainModule != null) {
238 int index = mainModule.indexOf("/");
239
240 if (index > 0) {
241 result = mainModule.substring(index + 1);
242 }
243 }
244 }
245
246 return result;
247 }
248
249 public static String getMainModule(Map<String, ? super Object> params) {
250 String result = "";
251 String mainModule = MAIN_MODULE.fetchFrom(params);
252
253 if (mainModule != null) {
254 int index = mainModule.indexOf("/");
255
256 if (index > 0) {
257 result = mainModule.substring(0, index);
258 }
259 else {
260 result = mainModule;
261 }
262 }
263
264 return result;
265 }
266
267 public static void execute(Map<String, ? super Object> params, AbstractAppImageBuilder imageBuilder) throws IOException, Exception {
268 List<Path> modulePath = MODULE_PATH.fetchFrom(params);
269 Set<String> addModules = ADD_MODULES.fetchFrom(params);
270 Set<String> limitModules = LIMIT_MODULES.fetchFrom(params);
271 boolean stripNativeCommands = STRIP_NATIVE_COMMANDS.fetchFrom(params);
272 Map<String, String> userArguments = JLINK_OPTIONS.fetchFrom(params);
273 Path outputDir = imageBuilder.getRoot();
274 String excludeFileList = imageBuilder.getExcludeFileList();
275 Set<String> jars = getResourceFileJarList(params, Module.JarType.UnnamedJar);
276 Path jdkModulePath = setupDefaultModulePathIfNecessary(params, modulePath);
277 File mainJar = getMainJar(params);
278 Module.ModuleType mainJarType = Module.ModuleType.Unknown;
279
280 if (mainJar != null) {
281 mainJarType = new Module(mainJar).getModuleType();
282 }
283
284 //--------------------------------------------------------------------
285 // Modules
286
287 boolean detectModules = DETECT_MODS.fetchFrom(params);
288
289 // The default for an unnamed jar is ALL_DEFAULT with the
290 // non-redistributable modules removed.
291 if (mainJarType == Module.ModuleType.UnnamedJar && !detectModules) {
292 addModules.add(ModuleHelper.ALL_RUNTIME);
293 }
294 else if (mainJarType == Module.ModuleType.Unknown || mainJarType == Module.ModuleType.ModularJar) {
295 String mainModule = getMainModule(params);
296 addModules.add(mainModule);
297
298 // Error if any of the srcfiles are modular jars.
299 Set<String> modularJars = getResourceFileJarList(params, Module.JarType.ModularJar);
300
301 if (!modularJars.isEmpty()) {
302 throw new Exception(String.format(I18N.getString("error.srcfiles.contain.modules"), modularJars.toString()));
303 }
304 }
305
306 ModuleHelper moduleHelper = new ModuleHelper(modulePath, addModules, limitModules);
307 addModules.addAll(moduleHelper.modules());
308
309 //--------------------------------------------------------------------
310 // Jars
311
312 // Bundle with minimum dependencies that unnamed jars depend on.
313 if (detectModules && !jars.isEmpty()) {
314 Collection<String> detectedModules = JDepHelper.calculateModules(jars, modulePath);
315
316 if (!detectedModules.isEmpty()) {
317 addModules.addAll(detectedModules);
318 }
319 }
320
321 Log.info(String.format(I18N.getString("message.modules"), addModules.toString()));
322
323 AppRuntimeImageBuilder appRuntimeBuilder = new AppRuntimeImageBuilder();
324 appRuntimeBuilder.setOutputDir(outputDir);
325 appRuntimeBuilder.setModulePath(modulePath);
326 appRuntimeBuilder.setAddModules(addModules);
327 appRuntimeBuilder.setLimitModules(limitModules);
328 appRuntimeBuilder.setExcludeFileList(excludeFileList);
329 appRuntimeBuilder.setStripNativeCommands(stripNativeCommands);
330 appRuntimeBuilder.setUserArguments(userArguments);
331
332 appRuntimeBuilder.build();
333 imageBuilder.prepareApplicationFiles();
334 }
335
336 // Returns the path to the JDK modules in the user defined module path.
337 public static Path findModulePath(List<Path> modulePath, String moduleName) {
338 Path result = null;
339
340 for (Path path : modulePath) {
341 Path moduleNamePath = path.resolve(moduleName);
342
343 if (Files.exists(moduleNamePath)) {
344 result = path;
345 break;
346 }
347 }
348
349 return result;
350 }
351
352 private static Path setupDefaultModulePathIfNecessary(Map<String, ? super Object> params, List<Path> modulePath) {
353 Path result = null;
354 Path userDefinedJdkModulePath = findModulePath(modulePath, "java.base.jmod");
355
356 //TODO Fix JDK-8158977
357
358 // Add the default JDK module path to the module path.
359 if (userDefinedJdkModulePath != null) {
360 result = userDefinedJdkModulePath;
361 }
362 else {
363 Path jdkModulePath = Paths.get(System.getProperty("java.home"), "jmods").toAbsolutePath();
364
365 if (jdkModulePath != null && Files.exists(jdkModulePath)) {
366 result = jdkModulePath;
367 modulePath.add(result);
368 }
369 }
370
371 if (result == null) {
372 Log.info(String.format(I18N.getString("warning.no.jdk.modules.found")));
446 "jdk.dynalink",
447 "jdk.httpserver",
448 "jdk.jfr",
449 "jdk.jsobject",
450 "jdk.management",
451 "jdk.management.cmm",
452 "jdk.management.jfr",
453 "jdk.management.resource",
454 "jdk.net",
455 "jdk.scripting.nashorn",
456 "jdk.sctp",
457 "jdk.security.auth",
458 "jdk.security.jgss",
459 "jdk.unsupported",
460 "jdk.vm.cds",
461 "jdk.xml.dom");
462
463 // The token for "all modules on the module path"
464 private static final String ALL_MODULE_PATH = "ALL-MODULE-PATH";
465
466 public static final String ALL_RUNTIME = "X-ALL-RUNTIME";
467
468 private final Set<String> modules = new HashSet<>();
469
470 public ModuleHelper(List<Path> paths, Set<String> roots, Set<String> limitMods) {
471 boolean found = false;
472
473 for (Iterator<String> iterator = roots.iterator(); iterator.hasNext();) {
474 String module = iterator.next();
475
476 switch (module) {
477 case ALL_MODULE_PATH:
478 iterator.remove();
479
480 if (!found) {
481 modules.addAll(getModuleNamesFromPath(paths));
482 found = true;
483 }
484 break;
485 case ALL_RUNTIME:
486 iterator.remove();
|
69 import java.util.logging.Level;
70 import java.util.logging.Logger;
71 import java.util.stream.Collectors;
72 import java.util.stream.Stream;
73 import java.util.zip.ZipEntry;
74 import java.util.zip.ZipInputStream;
75 import jdk.packager.builders.AbstractAppImageBuilder;
76 import jdk.packager.internal.Module;
77
78
79 public class JLinkBundlerHelper {
80
81 private static final ResourceBundle I18N =
82 ResourceBundle.getBundle(JLinkBundlerHelper.class.getName());
83
84 @SuppressWarnings("unchecked")
85 public static final BundlerParamInfo<List<Path>> MODULE_PATH =
86 new StandardBundlerParam<>(
87 I18N.getString("param.module-path.name"),
88 I18N.getString("param.module-path.description"),
89 "module-path",
90 (Class<List<Path>>) (Object)List.class,
91 p -> new ArrayList(),
92 (s, p) -> Arrays.asList(s.split("[;:]")).stream()
93 .map(ss -> new File(ss).toPath())
94 .collect(Collectors.toList()));
95
96 @SuppressWarnings("unchecked")
97 public static final BundlerParamInfo<String> MODULE =
98 new StandardBundlerParam<>(
99 I18N.getString("param.main.module.name"),
100 I18N.getString("param.main.module.description"),
101 "module",
102 String.class,
103 p -> null,
104 (s, p) -> {
105 return String.valueOf(s);
106 });
107
108 @SuppressWarnings("unchecked")
109 public static final BundlerParamInfo<Set<String>> ADD_MODULES =
110 new StandardBundlerParam<>(
111 I18N.getString("param.add-modules.name"),
112 I18N.getString("param.add-modules.description"),
113 "add-modules",
114 (Class<Set<String>>) (Object) Set.class,
115 p -> new LinkedHashSet(),
116 (s, p) -> new LinkedHashSet<>(Arrays.asList(s.split("[,;: ]+"))));
117
118 @SuppressWarnings("unchecked")
119 public static final BundlerParamInfo<Set<String>> LIMIT_MODULES =
120 new StandardBundlerParam<>(
121 I18N.getString("param.limit-modules.name"),
122 I18N.getString("param.limit-modules.description"),
123 "limit-modules",
124 (Class<Set<String>>) (Object) Set.class,
125 p -> new LinkedHashSet(),
126 (s, p) -> new LinkedHashSet<>(Arrays.asList(s.split("[,;: ]+"))));
127
128 @SuppressWarnings("unchecked")
129 public static final BundlerParamInfo<Boolean> STRIP_NATIVE_COMMANDS =
130 new StandardBundlerParam<>(
131 I18N.getString("param.strip-executables.name"),
132 I18N.getString("param.strip-executables.description"),
133 "strip-native-commands",
134 Boolean.class,
135 p -> Boolean.TRUE,
136 (s, p) -> Boolean.valueOf(s));
137
138 @SuppressWarnings("unchecked")
139 public static final BundlerParamInfo<Boolean> DETECT_MODULES =
140 new StandardBundlerParam<>(
141 I18N.getString("param.detect-modules.name"),
142 I18N.getString("param.detect-modules.description"),
143 "detect-modules",
144 Boolean.class,
145 p -> Boolean.FALSE,
146 (s, p) -> Boolean.valueOf(s));
147
148 @SuppressWarnings("unchecked")
149 public static final BundlerParamInfo<Map<String, String>> JLINK_OPTIONS =
150 new StandardBundlerParam<>(
151 I18N.getString("param.jlink-options.name"),
152 I18N.getString("param.jlink-options.description"),
153 "jlinkOptions",
154 (Class<Map<String, String>>) (Object) Map.class,
155 p -> Collections.emptyMap(),
156 (s, p) -> {
157 try {
158 Properties props = new Properties();
159 props.load(new StringReader(s));
160 return new LinkedHashMap<>((Map)props);
161 } catch (IOException e) {
162 return new LinkedHashMap<>();
163 }
164 });
165
166 @SuppressWarnings("unchecked")
167 public static final BundlerParamInfo<String> JLINK_BUILDER =
168 new StandardBundlerParam<>(
169 I18N.getString("param.jlink-builder.name"),
170 I18N.getString("param.jlink-builder.description"),
171 "jlink.builder",
172 String.class,
173 null,
174 (s, p) -> s);
175
176 @SuppressWarnings("unchecked")
177 public static final BundlerParamInfo<Integer> DEBUG_PORT =
178 new StandardBundlerParam<>(
179 I18N.getString("param.main.module.name"),
180 I18N.getString("param.main.module.description"),
181 "-Xdebug",
182 Integer.class,
183 p -> null,
184 (s, p) -> {
185 return Integer.valueOf(s);
186 });
187
188 public static String ListOfPathToString(List<Path> value) {
189 String result = "";
190
191 for (Path path : value) {
192 if (result.length() > 0) {
193 result += File.pathSeparator;
194 }
195
196 result += path.toString();
197 }
198
199 return result;
200 }
201
215
216 public static File getMainJar(Map<String, ? super Object> params) {
217 File result = null;
218 RelativeFileSet fileset = MAIN_JAR.fetchFrom(params);
219
220 if (fileset != null) {
221 result = new File(fileset.getIncludedFiles().iterator().next());
222 }
223
224 return result;
225 }
226
227 public static String getMainClass(Map<String, ? super Object> params) {
228 String result = "";
229 File mainJar = getMainJar(params);
230
231 if (mainJar != null) {
232 result = MAIN_CLASS.fetchFrom(params);
233 }
234 else {
235 String mainModule = MODULE.fetchFrom(params);
236
237 if (mainModule != null) {
238 int index = mainModule.indexOf("/");
239
240 if (index > 0) {
241 result = mainModule.substring(index + 1);
242 }
243 }
244 }
245
246 return result;
247 }
248
249 public static String getMainModule(Map<String, ? super Object> params) {
250 String result = "";
251 String mainModule = MODULE.fetchFrom(params);
252
253 if (mainModule != null) {
254 int index = mainModule.indexOf("/");
255
256 if (index > 0) {
257 result = mainModule.substring(0, index);
258 }
259 else {
260 result = mainModule;
261 }
262 }
263
264 return result;
265 }
266
267 public static void execute(Map<String, ? super Object> params, AbstractAppImageBuilder imageBuilder) throws IOException, Exception {
268 List<Path> modulePath = MODULE_PATH.fetchFrom(params);
269 Set<String> addModules = ADD_MODULES.fetchFrom(params);
270 Set<String> limitModules = LIMIT_MODULES.fetchFrom(params);
271 boolean stripNativeCommands = STRIP_NATIVE_COMMANDS.fetchFrom(params);
272 Map<String, String> userArguments = JLINK_OPTIONS.fetchFrom(params);
273 Path outputDir = imageBuilder.getRoot();
274 String excludeFileList = imageBuilder.getExcludeFileList();
275 Set<String> jars = getResourceFileJarList(params, Module.JarType.UnnamedJar);
276 setupDefaultModulePathIfNecessary(modulePath);
277 File mainJar = getMainJar(params);
278 Module.ModuleType mainJarType = Module.ModuleType.Unknown;
279
280 if (mainJar != null) {
281 mainJarType = new Module(mainJar).getModuleType();
282 }
283
284 //--------------------------------------------------------------------
285 // Modules
286
287 boolean detectModules = DETECT_MODULES.fetchFrom(params);
288
289 // The default for an unnamed jar is ALL_DEFAULT with the
290 // non-redistributable modules removed.
291 if (mainJarType == Module.ModuleType.UnnamedJar && !detectModules) {
292 addModules.add(ModuleHelper.ALL_RUNTIME);
293 }
294 else if (mainJarType == Module.ModuleType.Unknown || mainJarType == Module.ModuleType.ModularJar) {
295 String mainModule = getMainModule(params);
296 addModules.add(mainModule);
297
298 // Error if any of the srcfiles are modular jars.
299 Set<String> modularJars = getResourceFileJarList(params, Module.JarType.ModularJar);
300
301 if (!modularJars.isEmpty()) {
302 throw new Exception(String.format(I18N.getString("error.srcfiles.contain.modules"), modularJars.toString()));
303 }
304 }
305
306 ModuleHelper moduleHelper = new ModuleHelper(modulePath, addModules, limitModules);
307 addModules.addAll(moduleHelper.modules());
308
309 //--------------------------------------------------------------------
310 // Jars
311
312 // Bundle with minimum dependencies that unnamed jars depend on.
313 if (detectModules && !jars.isEmpty()) {
314 Log.info(String.format(I18N.getString("using.experimental.feature"), "--" + DETECT_MODULES.getID()));
315 Collection<String> detectedModules = JDepHelper.calculateModules(jars, modulePath);
316
317 if (!detectedModules.isEmpty()) {
318 addModules.addAll(detectedModules);
319 }
320 }
321
322 Log.info(String.format(I18N.getString("message.modules"), addModules.toString()));
323
324 AppRuntimeImageBuilder appRuntimeBuilder = new AppRuntimeImageBuilder();
325 appRuntimeBuilder.setOutputDir(outputDir);
326 appRuntimeBuilder.setModulePath(modulePath);
327 appRuntimeBuilder.setAddModules(addModules);
328 appRuntimeBuilder.setLimitModules(limitModules);
329 appRuntimeBuilder.setExcludeFileList(excludeFileList);
330 appRuntimeBuilder.setStripNativeCommands(stripNativeCommands);
331 appRuntimeBuilder.setUserArguments(userArguments);
332
333 appRuntimeBuilder.build();
334 imageBuilder.prepareApplicationFiles();
335 }
336
337 // Returns the path to the JDK modules in the user defined module path.
338 public static Path findModulePath(List<Path> modulePath, String moduleName) {
339 Path result = null;
340
341 for (Path path : modulePath) {
342 Path moduleNamePath = path.resolve(moduleName);
343
344 if (Files.exists(moduleNamePath)) {
345 result = path;
346 break;
347 }
348 }
349
350 return result;
351 }
352
353 private static Path setupDefaultModulePathIfNecessary(List<Path> modulePath) {
354 Path result = null;
355 Path userDefinedJdkModulePath = findModulePath(modulePath, "java.base.jmod");
356
357 //TODO Fix JDK-8158977
358
359 // Add the default JDK module path to the module path.
360 if (userDefinedJdkModulePath != null) {
361 result = userDefinedJdkModulePath;
362 }
363 else {
364 Path jdkModulePath = Paths.get(System.getProperty("java.home"), "jmods").toAbsolutePath();
365
366 if (jdkModulePath != null && Files.exists(jdkModulePath)) {
367 result = jdkModulePath;
368 modulePath.add(result);
369 }
370 }
371
372 if (result == null) {
373 Log.info(String.format(I18N.getString("warning.no.jdk.modules.found")));
447 "jdk.dynalink",
448 "jdk.httpserver",
449 "jdk.jfr",
450 "jdk.jsobject",
451 "jdk.management",
452 "jdk.management.cmm",
453 "jdk.management.jfr",
454 "jdk.management.resource",
455 "jdk.net",
456 "jdk.scripting.nashorn",
457 "jdk.sctp",
458 "jdk.security.auth",
459 "jdk.security.jgss",
460 "jdk.unsupported",
461 "jdk.vm.cds",
462 "jdk.xml.dom");
463
464 // The token for "all modules on the module path"
465 private static final String ALL_MODULE_PATH = "ALL-MODULE-PATH";
466
467 public static final String ALL_RUNTIME = "ALL-RUNTIME";
468
469 private final Set<String> modules = new HashSet<>();
470
471 public ModuleHelper(List<Path> paths, Set<String> roots, Set<String> limitMods) {
472 boolean found = false;
473
474 for (Iterator<String> iterator = roots.iterator(); iterator.hasNext();) {
475 String module = iterator.next();
476
477 switch (module) {
478 case ALL_MODULE_PATH:
479 iterator.remove();
480
481 if (!found) {
482 modules.addAll(getModuleNamesFromPath(paths));
483 found = true;
484 }
485 break;
486 case ALL_RUNTIME:
487 iterator.remove();
|