8246624: Refactor JLinkBundlerHelper and StandardBundlerParam classes

Reviewed-by: herrick, almatvee
This commit is contained in:
Alexey Semenyuk 2020-06-08 09:12:58 -04:00
parent 086c5f1750
commit 045d61b5e0
25 changed files with 434 additions and 998 deletions

@ -147,19 +147,4 @@ public class LinuxAppImageBuilder extends AbstractAppImageBuilder {
iconResource.saveToFile(iconTarget);
}
}
private void copyApplication(Map<String, ? super Object> params)
throws IOException {
for (RelativeFileSet appResources :
APP_RESOURCES_LIST.fetchFrom(params)) {
if (appResources == null) {
throw new RuntimeException("Null app resources?");
}
File srcdir = appResources.getBaseDirectory();
for (String fname : appResources.getIncludedFiles()) {
copyEntry(appLayout.appDirectory(), srcdir, fname);
}
}
}
}

@ -1,5 +1,5 @@
/*
* Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -118,7 +118,7 @@ public class LinuxDebBundler extends LinuxPackageBundler {
private static final BundlerParamInfo<String> MAINTAINER =
new StandardBundlerParam<> (
BundleParams.PARAM_MAINTAINER,
Arguments.CLIOptions.LINUX_DEB_MAINTAINER.getId() + ".internal",
String.class,
params -> VENDOR.fetchFrom(params) + " <"
+ EMAIL.fetchFrom(params) + ">",

@ -218,7 +218,7 @@ public class MacAppImageBuilder extends AbstractAppImageBuilder {
}
// Copy class path entries to Java folder
copyClassPathEntries(appDir, params);
copyApplication(params);
/*********** Take care of "config" files *******/
@ -312,11 +312,7 @@ public class MacAppImageBuilder extends AbstractAppImageBuilder {
}
private static String getLauncherName(Map<String, ? super Object> params) {
if (APP_NAME.fetchFrom(params) != null) {
return APP_NAME.fetchFrom(params);
} else {
return MAIN_CLASS.fetchFrom(params);
}
return APP_NAME.fetchFrom(params);
}
public static String getLauncherCfgName(
@ -324,23 +320,6 @@ public class MacAppImageBuilder extends AbstractAppImageBuilder {
return "Contents/app/" + APP_NAME.fetchFrom(params) + ".cfg";
}
private void copyClassPathEntries(Path javaDirectory,
Map<String, ? super Object> params) throws IOException {
List<RelativeFileSet> resourcesList =
APP_RESOURCES_LIST.fetchFrom(params);
if (resourcesList == null) {
throw new RuntimeException(
I18N.getString("message.null-classpath"));
}
for (RelativeFileSet classPath : resourcesList) {
File srcdir = classPath.getBaseDirectory();
for (String fname : classPath.getIncludedFiles()) {
copyEntry(javaDirectory, srcdir, fname);
}
}
}
private String getBundleName(Map<String, ? super Object> params) {
if (MAC_CF_BUNDLE_NAME.fetchFrom(params) != null) {
String bn = MAC_CF_BUNDLE_NAME.fetchFrom(params);
@ -399,50 +378,6 @@ public class MacAppImageBuilder extends AbstractAppImageBuilder {
data.put("DEPLOY_BUNDLE_SHORT_VERSION", VERSION.fetchFrom(params));
data.put("DEPLOY_BUNDLE_CFBUNDLE_VERSION", VERSION.fetchFrom(params));
boolean hasMainJar = MAIN_JAR.fetchFrom(params) != null;
boolean hasMainModule =
StandardBundlerParam.MODULE.fetchFrom(params) != null;
if (hasMainJar) {
data.put("DEPLOY_MAIN_JAR_NAME", MAIN_JAR.fetchFrom(params).
getIncludedFiles().iterator().next());
}
else if (hasMainModule) {
data.put("DEPLOY_MODULE_NAME",
StandardBundlerParam.MODULE.fetchFrom(params));
}
StringBuilder sb = new StringBuilder();
List<String> jvmOptions = JAVA_OPTIONS.fetchFrom(params);
String newline = ""; //So we don't add extra line after last append
for (String o : jvmOptions) {
sb.append(newline).append(
" <string>").append(o).append("</string>");
newline = "\n";
}
data.put("DEPLOY_JAVA_OPTIONS", sb.toString());
sb = new StringBuilder();
List<String> args = ARGUMENTS.fetchFrom(params);
newline = "";
// So we don't add unneccessary extra line after last append
for (String o : args) {
sb.append(newline).append(" <string>").append(o).append(
"</string>");
newline = "\n";
}
data.put("DEPLOY_ARGUMENTS", sb.toString());
newline = "";
data.put("DEPLOY_LAUNCHER_CLASS", MAIN_CLASS.fetchFrom(params));
data.put("DEPLOY_APP_CLASSPATH",
getCfgClassPath(CLASSPATH.fetchFrom(params)));
StringBuilder bundleDocumentTypes = new StringBuilder();
StringBuilder exportedTypes = new StringBuilder();
for (Map<String, ? super Object>
@ -788,8 +723,9 @@ public class MacAppImageBuilder extends AbstractAppImageBuilder {
}
Path frameworkPath = appLocation.resolve("Contents/Frameworks");
if (Files.isDirectory(frameworkPath)) {
Files.list(frameworkPath)
.forEach(signIdentifiedByPList);
try (var fileList = Files.list(frameworkPath)) {
fileList.forEach(signIdentifiedByPList);
}
ioe = toThrow.get();
if (ioe != null) {

@ -58,7 +58,6 @@ resource.pkg-background-image=pkg background image
message.bundle-name-too-long-warning={0} is set to ''{1}'', which is longer than 16 characters. For a better Mac experience consider shortening it.
message.null-classpath=Null app resources?
message.preparing-info-plist=Preparing Info.plist: {0}.
message.icon-not-icns= The specified icon "{0}" is not an ICNS file and will not be used. The default icon will be used in it's place.
message.version-string-too-many-components=Version sting may have between 1 and 3 numbers: 1, 1.2, 1.2.3.

@ -59,7 +59,6 @@ resource.pkg-background-image=pkg\u80CC\u666F\u30A4\u30E1\u30FC\u30B8
message.bundle-name-too-long-warning={0}\u304C16\u6587\u5B57\u3092\u8D85\u3048\u308B''{1}''\u306B\u8A2D\u5B9A\u3055\u308C\u3066\u3044\u307E\u3059\u3002Mac\u3067\u306E\u64CD\u4F5C\u6027\u3092\u3088\u308A\u826F\u304F\u3059\u308B\u305F\u3081\u306B\u77ED\u304F\u3059\u308B\u3053\u3068\u3092\u691C\u8A0E\u3057\u3066\u304F\u3060\u3055\u3044\u3002
message.null-classpath=Null\u306E\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u30FB\u30EA\u30BD\u30FC\u30B9\u3067\u3059\u304B\u3002
message.preparing-info-plist=Info.plist\u3092\u6E96\u5099\u3057\u3066\u3044\u307E\u3059: {0}\u3002
message.icon-not-icns= \u6307\u5B9A\u3057\u305F\u30A2\u30A4\u30B3\u30F3"{0}"\u306FICNS\u30D5\u30A1\u30A4\u30EB\u3067\u306F\u306A\u304F\u3001\u4F7F\u7528\u3055\u308C\u307E\u305B\u3093\u3002\u30C7\u30D5\u30A9\u30EB\u30C8\u30FB\u30A2\u30A4\u30B3\u30F3\u304C\u305D\u306E\u4F4D\u7F6E\u306B\u4F7F\u7528\u3055\u308C\u307E\u3059\u3002
message.version-string-too-many-components=\u30D0\u30FC\u30B8\u30E7\u30F3\u6587\u5B57\u5217\u306B\u306F\u30011\u30011.2\u30011.2.3\u306A\u30691\u304B\u30893\u306E\u6570\u5B57\u3092\u4F7F\u7528\u3067\u304D\u307E\u3059\u3002

@ -59,7 +59,6 @@ resource.pkg-background-image=pkg \u80CC\u666F\u56FE\u50CF
message.bundle-name-too-long-warning={0}\u5DF2\u8BBE\u7F6E\u4E3A ''{1}'', \u5176\u957F\u5EA6\u8D85\u8FC7\u4E86 16 \u4E2A\u5B57\u7B26\u3002\u4E3A\u4E86\u83B7\u5F97\u66F4\u597D\u7684 Mac \u4F53\u9A8C, \u8BF7\u8003\u8651\u5C06\u5176\u7F29\u77ED\u3002
message.null-classpath=\u662F\u5426\u4E3A\u7A7A\u5E94\u7528\u7A0B\u5E8F\u8D44\u6E90?
message.preparing-info-plist=\u6B63\u5728\u51C6\u5907 Info.plist: {0}\u3002
message.icon-not-icns= \u6307\u5B9A\u7684\u56FE\u6807 "{0}" \u4E0D\u662F ICNS \u6587\u4EF6, \u4E0D\u4F1A\u4F7F\u7528\u3002\u5C06\u4F7F\u7528\u9ED8\u8BA4\u56FE\u6807\u4EE3\u66FF\u3002
message.version-string-too-many-components=\u7248\u672C\u5B57\u7B26\u4E32\u53EF\u4EE5\u5305\u542B 1 \u5230 3 \u4E2A\u6570\u5B57: 1, 1.2, 1.2.3\u3002

@ -89,14 +89,9 @@ public abstract class AbstractAppImageBuilder {
File cfgFileName) throws IOException {
cfgFileName.getParentFile().mkdirs();
cfgFileName.delete();
File mainJar = JLinkBundlerHelper.getMainJar(params);
ModFile.ModType mainJarType = ModFile.ModType.Unknown;
if (mainJar != null) {
mainJarType = new ModFile(mainJar).getModType();
}
String mainModule = StandardBundlerParam.MODULE.fetchFrom(params);
LauncherData launcherData = StandardBundlerParam.LAUNCHER_DATA.fetchFrom(
params);
try (PrintStream out = new PrintStream(cfgFileName)) {
@ -104,27 +99,26 @@ public abstract class AbstractAppImageBuilder {
out.println("app.name=" + APP_NAME.fetchFrom(params));
out.println("app.version=" + VERSION.fetchFrom(params));
out.println("app.runtime=" + getCfgRuntimeDir());
out.println("app.classpath="
+ getCfgClassPath(CLASSPATH.fetchFrom(params)));
for (var path : launcherData.classPath()) {
out.println("app.classpath=" + getCfgAppDir()
+ path.toString().replace("\\", "/"));
}
// The main app is required to be a jar, modular or unnamed.
if (mainModule != null &&
(mainJarType == ModFile.ModType.Unknown ||
mainJarType == ModFile.ModType.ModularJar)) {
out.println("app.mainmodule=" + mainModule);
if (launcherData.isModular()) {
out.println("app.mainmodule=" + launcherData.moduleName() + "/"
+ launcherData.qualifiedClassName());
} else {
String mainClass =
StandardBundlerParam.MAIN_CLASS.fetchFrom(params);
// If the app is contained in an unnamed jar then launch it the
// legacy way and the main class string must be
// of the format com/foo/Main
if (mainJar != null) {
if (launcherData.mainJarName() != null) {
out.println("app.classpath=" + getCfgAppDir()
+ mainJar.toPath().getFileName().toString());
}
if (mainClass != null) {
out.println("app.mainclass=" + mainClass);
+ launcherData.mainJarName().toString());
}
out.println("app.mainclass=" + launcherData.qualifiedClassName());
}
out.println();
@ -149,6 +143,14 @@ public abstract class AbstractAppImageBuilder {
}
}
protected void copyApplication(Map<String, ? super Object> params)
throws IOException {
Path inputPath = StandardBundlerParam.SOURCE_DIR.fetchFrom(params);
if (inputPath != null) {
IOUtils.copyRecursive(SOURCE_DIR.fetchFrom(params), getAppDir());
}
}
File getRuntimeImageDir(File runtimeImageTop) {
return runtimeImageTop;
}

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -27,11 +27,7 @@ package jdk.incubator.jpackage.internal;
import java.text.MessageFormat;
import java.util.Map;
import java.util.ResourceBundle;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.io.File;
import java.io.IOException;
import static jdk.incubator.jpackage.internal.StandardBundlerParam.*;
@ -49,13 +45,12 @@ import static jdk.incubator.jpackage.internal.StandardBundlerParam.*;
*/
public abstract class AbstractImageBundler extends AbstractBundler {
private static final ResourceBundle I18N = ResourceBundle.getBundle(
"jdk.incubator.jpackage.internal.resources.MainResources");
public void imageBundleValidation(Map<String, ? super Object> params)
protected void imageBundleValidation(Map<String, ? super Object> params)
throws ConfigException {
StandardBundlerParam.validateMainClassInfoFromAppResources(params);
if (!params.containsKey(PREDEFINED_APP_IMAGE.getID())
&& !StandardBundlerParam.isRuntimeInstaller(params)) {
StandardBundlerParam.LAUNCHER_DATA.fetchFrom(params);
}
}
protected File createRoot(Map<String, ? super Object> params,
@ -70,11 +65,6 @@ public abstract class AbstractImageBundler extends AbstractBundler {
name, outputDirectory.getAbsolutePath()));
}
// NAME will default to CLASS, so the real problem is no MAIN_CLASS
if (name == null) {
throw new PackagerException("ERR_NoMainClass");
}
// Create directory structure
File rootDirectory = new File(outputDirectory, name);

@ -1,31 +0,0 @@
/*
* Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.incubator.jpackage.internal;
@FunctionalInterface
interface ArgAction {
void execute();
}

@ -344,7 +344,7 @@ public class Arguments {
private final String id;
private final String shortId;
private final OptionCategories category;
private final ArgAction action;
private final Runnable action;
private static Arguments argContext;
private CLIOptions(String id, OptionCategories category) {
@ -357,12 +357,12 @@ public class Arguments {
}
private CLIOptions(String id,
OptionCategories category, ArgAction action) {
OptionCategories category, Runnable action) {
this(id, null, category, action);
}
private CLIOptions(String id, String shortId,
OptionCategories category, ArgAction action) {
OptionCategories category, Runnable action) {
this.id = id;
this.shortId = shortId;
this.action = action;
@ -395,7 +395,7 @@ public class Arguments {
void execute() {
if (action != null) {
action.execute();
action.run();
} else {
defaultAction();
}
@ -471,8 +471,6 @@ public class Arguments {
validateArguments();
addResources(deployParams, input, mainJarPath);
List<Map<String, ? super Object>> launchersAsMap =
new ArrayList<>();
@ -680,35 +678,6 @@ public class Arguments {
}
}
private void addResources(DeployParams deployParams,
String inputdir, String mainJar) throws PackagerException {
if (inputdir == null || inputdir.isEmpty()) {
return;
}
File baseDir = new File(inputdir);
if (!baseDir.isDirectory()) {
throw new PackagerException("ERR_InputNotDirectory", inputdir);
}
if (!baseDir.canRead()) {
throw new PackagerException("ERR_CannotReadInputDir", inputdir);
}
List<String> fileNames;
fileNames = new ArrayList<>();
try (Stream<Path> files = Files.list(baseDir.toPath())) {
files.forEach(file -> fileNames.add(
file.getFileName().toString()));
} catch (IOException e) {
Log.error("Unable to add resources: " + e.getMessage());
}
fileNames.forEach(file -> deployParams.addResource(baseDir, file));
deployParams.setClasspath(mainJar);
}
static CLIOptions toCLIOption(String arg) {
CLIOptions option;
if ((option = argIds.get(arg)) == null) {

@ -1,5 +1,5 @@
/*
* Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -38,60 +38,6 @@ public class BundleParams {
final protected Map<String, ? super Object> params;
// RelativeFileSet
public static final String PARAM_APP_RESOURCES = "appResources";
// String - Icon file name
public static final String PARAM_ICON = "icon";
// String - Name of bundle file and native launcher
public static final String PARAM_NAME = "name";
// String - application vendor, used by most of the bundlers
public static final String PARAM_VENDOR = "vendor";
// String - email name and email, only used for debian */
public static final String PARAM_EMAIL = "email";
// String - vendor <email>, only used for debian */
public static final String PARAM_MAINTAINER = "maintainer";
/* String - Copyright. Used on Mac */
public static final String PARAM_COPYRIGHT = "copyright";
// String - GUID on windows for MSI, CFBundleIdentifier on Mac
// If not compatible with requirements then bundler either do not bundle
// or autogenerate
public static final String PARAM_IDENTIFIER = "identifier";
/* boolean - shortcut preferences */
public static final String PARAM_SHORTCUT = "shortcutHint";
// boolean - menu shortcut preference
public static final String PARAM_MENU = "menuHint";
// String - Application version. Format may differ for different bundlers
public static final String PARAM_VERSION = "appVersion";
// String - Application release. Used on Linux.
public static final String PARAM_RELEASE = "appRelease";
// String - Optional application description. Used by MSI and on Linux
public static final String PARAM_DESCRIPTION = "description";
// String - License type. Needed on Linux (rpm)
public static final String PARAM_LICENSE_TYPE = "licenseType";
// String - File with license. Format is OS/bundler specific
public static final String PARAM_LICENSE_FILE = "licenseFile";
// String Main application class.
// Not used directly but used to derive default values
public static final String PARAM_APPLICATION_CLASS = "applicationClass";
// boolean - Adds a dialog to let the user choose a directory
// where the product will be installed.
public static final String PARAM_INSTALLDIR_CHOOSER = "installdirChooser";
/**
* create a new bundle with all default values
*/
@ -123,11 +69,6 @@ public class BundleParams {
return APP_NAME.fetchFrom(params);
}
public void setAppResourcesList(
List<jdk.incubator.jpackage.internal.RelativeFileSet> rfs) {
putUnlessNull(APP_RESOURCES_LIST.getID(), rfs);
}
private void putUnlessNull(String param, Object value) {
if (value != null) {
params.put(param, value);

@ -50,8 +50,6 @@ import java.util.TreeSet;
*/
public class DeployParams {
final List<RelativeFileSet> resources = new ArrayList<>();
String targetFormat = null; // means default type for this platform
File outdir = null;
@ -98,44 +96,6 @@ public class DeployParams {
return files;
}
public void addResource(File baseDir, String path) {
addResource(baseDir, new File(baseDir, path));
}
public void addResource(File baseDir, File file) {
// normalize initial file
// to strip things like "." in the path
// or it can confuse symlink detection logic
file = file.getAbsoluteFile();
if (baseDir == null) {
baseDir = file.getParentFile();
}
resources.add(new RelativeFileSet(
baseDir, new LinkedHashSet<>(expandFileset(file))));
}
void setClasspath(String mainJarPath) {
String classpath;
// we want main jar first on the classpath
if (mainJarPath != null) {
classpath = mainJarPath + File.pathSeparator;
} else {
classpath = "";
}
for (RelativeFileSet resource : resources) {
for (String file : resource.getIncludedFiles()) {
if (file.endsWith(".jar")) {
if (!file.equals(mainJarPath)) {
classpath += file + File.pathSeparator;
}
}
}
}
addBundleArgument(
StandardBundlerParam.CLASSPATH.getID(), classpath);
}
static void validateName(String s, boolean forApp)
throws PackagerException {
@ -230,14 +190,8 @@ public class DeployParams {
// if bundling non-modular image, or installer without app-image
// then we need some resources and a main class
if (!hasModule && !hasAppImage && !runtimeInstaller) {
if (resources.isEmpty()) {
throw new PackagerException("ERR_MissingAppResources");
}
if (!hasMain) {
throw new PackagerException("ERR_MissingArgument",
"--main-jar");
}
if (!hasModule && !hasAppImage && !runtimeInstaller && !hasMain) {
throw new PackagerException("ERR_MissingArgument", "--main-jar");
}
String name = (String)bundlerArguments.get(
@ -364,9 +318,6 @@ public class DeployParams {
BundleParams getBundleParams() {
BundleParams bundleParams = new BundleParams();
// construct app resources relative to destination folder!
bundleParams.setAppResourcesList(resources);
Map<String, String> unescapedHtmlParams = new TreeMap<>();
Map<String, String> escapedHtmlParams = new TreeMap<>();
@ -386,8 +337,7 @@ public class DeployParams {
@Override
public String toString() {
return "DeployParams {" + "output: " + outdir
+ " resources: {" + resources + "}}";
return "DeployParams {" + "output: " + "}";
}
}

@ -27,28 +27,18 @@ package jdk.incubator.jpackage.internal;
import java.io.File;
import java.io.IOException;
import java.io.StringReader;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.nio.file.Files;
import java.nio.file.Path;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.ResourceBundle;
import java.util.Set;
import java.util.Optional;
import java.util.Arrays;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.regex.Matcher;
@ -64,77 +54,9 @@ import jdk.internal.module.ModulePath;
final class JLinkBundlerHelper {
private static final ResourceBundle I18N = ResourceBundle.getBundle(
"jdk.incubator.jpackage.internal.resources.MainResources");
static final ToolProvider JLINK_TOOL =
private static final ToolProvider JLINK_TOOL =
ToolProvider.findFirst("jlink").orElseThrow();
static File getMainJar(Map<String, ? super Object> params) {
File result = null;
RelativeFileSet fileset =
StandardBundlerParam.MAIN_JAR.fetchFrom(params);
if (fileset != null) {
String filename = fileset.getIncludedFiles().iterator().next();
result = fileset.getBaseDirectory().toPath().
resolve(filename).toFile();
if (result == null || !result.exists()) {
String srcdir =
StandardBundlerParam.SOURCE_DIR.fetchFrom(params);
if (srcdir != null) {
result = new File(srcdir + File.separator + filename);
}
}
}
return result;
}
static String getMainClassFromModule(Map<String, ? super Object> params) {
String mainModule = StandardBundlerParam.MODULE.fetchFrom(params);
if (mainModule != null) {
int index = mainModule.indexOf("/");
if (index > 0) {
return mainModule.substring(index + 1);
} else {
ModuleDescriptor descriptor =
JLinkBundlerHelper.getMainModuleDescription(params);
if (descriptor != null) {
Optional<String> mainClass = descriptor.mainClass();
if (mainClass.isPresent()) {
Log.verbose(MessageFormat.format(I18N.getString(
"message.module-class"),
mainClass.get(),
JLinkBundlerHelper.getMainModule(params)));
return mainClass.get();
}
}
}
}
return null;
}
static String getMainModule(Map<String, ? super Object> params) {
String result = null;
String mainModule = StandardBundlerParam.MODULE.fetchFrom(params);
if (mainModule != null) {
int index = mainModule.indexOf("/");
if (index > 0) {
result = mainModule.substring(0, index);
} else {
result = mainModule;
}
}
return result;
}
static void execute(Map<String, ? super Object> params,
AbstractAppImageBuilder imageBuilder)
throws IOException, Exception {
@ -148,38 +70,23 @@ final class JLinkBundlerHelper {
List<String> options =
StandardBundlerParam.JLINK_OPTIONS.fetchFrom(params);
Path outputDir = imageBuilder.getRuntimeRoot();
File mainJar = getMainJar(params);
ModFile.ModType mainJarType = ModFile.ModType.Unknown;
if (mainJar != null) {
mainJarType = new ModFile(mainJar).getModType();
} else if (StandardBundlerParam.MODULE.fetchFrom(params) == null) {
// user specified only main class, all jars will be on the classpath
mainJarType = ModFile.ModType.UnnamedJar;
}
LauncherData launcherData = StandardBundlerParam.LAUNCHER_DATA.fetchFrom(
params);
boolean bindServices =
StandardBundlerParam.BIND_SERVICES.fetchFrom(params);
// Modules
String mainModule = getMainModule(params);
if (mainModule == null) {
if (mainJarType == ModFile.ModType.UnnamedJar) {
if (addModules.isEmpty()) {
// The default for an unnamed jar is ALL_DEFAULT
addModules.add(ModuleHelper.ALL_DEFAULT);
}
} else if (mainJarType == ModFile.ModType.Unknown ||
mainJarType == ModFile.ModType.ModularJar) {
addModules.add(ModuleHelper.ALL_DEFAULT);
}
if (!launcherData.isModular() && addModules.isEmpty()) {
addModules.add(ModuleHelper.ALL_DEFAULT);
}
Set<String> modules = new ModuleHelper(
modulePath, addModules, limitModules).modules();
if (mainModule != null) {
modules.add(mainModule);
if (launcherData.isModular()) {
modules.add(launcherData.moduleName());
}
runJLink(outputDir, modulePath, modules, limitModules,
@ -188,38 +95,6 @@ final class JLinkBundlerHelper {
imageBuilder.prepareApplicationFiles(params);
}
// Returns the path to the JDK modules in the user defined module path.
static Path findPathOfModule( List<Path> modulePath, String moduleName) {
for (Path path : modulePath) {
Path moduleNamePath = path.resolve(moduleName);
if (Files.exists(moduleNamePath)) {
return path;
}
}
return null;
}
static ModuleDescriptor getMainModuleDescription(Map<String, ? super Object> params) {
boolean hasModule = params.containsKey(StandardBundlerParam.MODULE.getID());
if (hasModule) {
List<Path> modulePath = StandardBundlerParam.MODULE_PATH.fetchFrom(params);
if (!modulePath.isEmpty()) {
ModuleFinder finder = ModuleFinder.of(modulePath.toArray(new Path[0]));
String mainModule = JLinkBundlerHelper.getMainModule(params);
Optional<ModuleReference> omref = finder.find(mainModule);
if (omref.isPresent()) {
return omref.get().descriptor();
}
}
}
return null;
}
/*
* Returns the set of modules that would be visible by default for
* a non-modular-aware application consisting of the given elements.
@ -255,7 +130,7 @@ final class JLinkBundlerHelper {
.anyMatch(e -> !e.isQualified());
}
private static ModuleFinder createModuleFinder(Collection<Path> modulePath) {
static ModuleFinder createModuleFinder(Collection<Path> modulePath) {
return ModuleFinder.compose(
ModulePath.of(JarFile.runtimeVersion(), true,
modulePath.toArray(Path[]::new)),
@ -373,27 +248,14 @@ final class JLinkBundlerHelper {
}
private static String getPathList(List<Path> pathList) {
String ret = null;
for (Path p : pathList) {
String s = Matcher.quoteReplacement(p.toString());
if (ret == null) {
ret = s;
} else {
ret += File.pathSeparator + s;
}
}
return ret;
return pathList.stream()
.map(Path::toString)
.map(Matcher::quoteReplacement)
.collect(Collectors.joining(File.pathSeparator));
}
private static String getStringList(Set<String> strings) {
String ret = null;
for (String s : strings) {
if (ret == null) {
ret = s;
} else {
ret += "," + s;
}
}
return (ret == null) ? null : Matcher.quoteReplacement(ret);
return Matcher.quoteReplacement(strings.stream().collect(
Collectors.joining(",")));
}
}

@ -0,0 +1,281 @@
/*
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.incubator.jpackage.internal;
import java.io.File;
import java.io.IOException;
import java.lang.module.ModuleDescriptor;
import java.nio.file.Files;
import java.nio.file.InvalidPathException;
import java.nio.file.Path;
import java.text.MessageFormat;
import java.util.*;
import java.util.function.Supplier;
import java.util.jar.Attributes;
import java.util.jar.JarFile;
import java.util.jar.Manifest;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* Extracts data needed to run application from parameters.
*/
final class LauncherData {
boolean isModular() {
return moduleDescriptor != null;
}
String qualifiedClassName() {
return qualifiedClassName;
}
String packageName() {
int sepIdx = qualifiedClassName.lastIndexOf('.');
if (sepIdx < 0) {
return "";
}
return qualifiedClassName.substring(sepIdx + 1);
}
String moduleName() {
verifyIsModular(true);
return moduleDescriptor.name();
}
List<Path> modulePath() {
verifyIsModular(true);
return modulePath;
}
Path mainJarName() {
verifyIsModular(false);
return mainJarName;
}
List<Path> classPath() {
return classPath;
}
String getAppVersion() {
if (isModular()) {
ModuleDescriptor.Version ver = moduleDescriptor.version().orElse(null);
if (ver != null) {
return ver.toString();
}
return moduleDescriptor.rawVersion().orElse(null);
}
return null;
}
private LauncherData() {
}
private void verifyIsModular(boolean isModular) {
if ((moduleDescriptor != null) != isModular) {
throw new IllegalStateException();
}
}
static LauncherData create(Map<String, ? super Object> params) throws
ConfigException, IOException {
final String mainModule = getMainModule(params);
if (mainModule == null) {
return createNonModular(params);
}
LauncherData launcherData = new LauncherData();
final int sepIdx = mainModule.indexOf("/");
final String moduleName;
if (sepIdx > 0) {
launcherData.qualifiedClassName = mainModule.substring(sepIdx + 1);
moduleName = mainModule.substring(0, sepIdx);
} else {
moduleName = mainModule;
}
launcherData.modulePath = getModulePath(params);
launcherData.moduleDescriptor = JLinkBundlerHelper.createModuleFinder(
launcherData.modulePath).find(moduleName).orElseThrow(
() -> new ConfigException(MessageFormat.format(I18N.getString(
"error.no-module-in-path"), moduleName), null)).descriptor();
if (launcherData.qualifiedClassName == null) {
launcherData.qualifiedClassName = launcherData.moduleDescriptor.mainClass().orElseThrow(
() -> new ConfigException(I18N.getString("ERR_NoMainClass"),
null));
}
launcherData.initClasspath(params);
return launcherData;
}
private static LauncherData createNonModular(
Map<String, ? super Object> params) throws ConfigException, IOException {
LauncherData launcherData = new LauncherData();
launcherData.qualifiedClassName = getMainClass(params);
launcherData.mainJarName = getMainJarName(params);
if (launcherData.mainJarName == null && launcherData.qualifiedClassName
== null) {
throw new ConfigException(I18N.getString("error.no-main-jar-parameter"),
null);
}
Path mainJarDir = StandardBundlerParam.SOURCE_DIR.fetchFrom(params);
if (mainJarDir == null && launcherData.qualifiedClassName == null) {
throw new ConfigException(I18N.getString("error.no-input-parameter"),
null);
}
final Path mainJarPath;
if (launcherData.mainJarName != null && mainJarDir != null) {
mainJarPath = mainJarDir.resolve(launcherData.mainJarName);
if (!Files.exists(mainJarPath)) {
throw new ConfigException(MessageFormat.format(I18N.getString(
"error.main-jar-does-not-exist"),
launcherData.mainJarName), I18N.getString(
"error.main-jar-does-not-exist.advice"));
}
} else {
mainJarPath = null;
}
if (launcherData.qualifiedClassName == null) {
if (mainJarPath == null) {
throw new ConfigException(I18N.getString("error.no-main-class"),
I18N.getString("error.no-main-class.advice"));
}
try (JarFile jf = new JarFile(mainJarPath.toFile())) {
Manifest m = jf.getManifest();
Attributes attrs = (m != null) ? m.getMainAttributes() : null;
if (attrs != null) {
launcherData.qualifiedClassName = attrs.getValue(
Attributes.Name.MAIN_CLASS);
}
}
}
if (launcherData.qualifiedClassName == null) {
throw new ConfigException(MessageFormat.format(I18N.getString(
"error.no-main-class-with-main-jar"),
launcherData.mainJarName), MessageFormat.format(
I18N.getString(
"error.no-main-class-with-main-jar.advice"),
launcherData.mainJarName));
}
launcherData.initClasspath(params);
return launcherData;
}
private void initClasspath(Map<String, ? super Object> params)
throws IOException {
Path inputDir = StandardBundlerParam.SOURCE_DIR.fetchFrom(params);
if (inputDir == null) {
classPath = Collections.emptyList();
} else {
try (Stream<Path> walk = Files.walk(inputDir, 1)) {
Set<Path> jars = walk.filter(Files::isRegularFile)
.filter(file -> file.toString().endsWith(".jar"))
.map(Path::getFileName)
.collect(Collectors.toSet());
jars.remove(mainJarName);
classPath = jars.stream().sorted().collect(Collectors.toList());
}
}
}
private static String getMainClass(Map<String, ? super Object> params) {
return getStringParam(params, Arguments.CLIOptions.APPCLASS.getId());
}
private static Path getMainJarName(Map<String, ? super Object> params)
throws ConfigException {
return getPathParam(params, Arguments.CLIOptions.MAIN_JAR.getId());
}
private static String getMainModule(Map<String, ? super Object> params) {
return getStringParam(params, Arguments.CLIOptions.MODULE.getId());
}
private static String getStringParam(Map<String, ? super Object> params,
String paramName) {
Optional<Object> value = Optional.ofNullable(params.get(paramName));
if (value.isPresent()) {
return value.get().toString();
}
return null;
}
private static <T> T getPathParam(Map<String, ? super Object> params,
String paramName, Supplier<T> func) throws ConfigException {
try {
return func.get();
} catch (InvalidPathException ex) {
throw new ConfigException(MessageFormat.format(I18N.getString(
"error.not-path-parameter"), paramName,
ex.getLocalizedMessage()), null, ex);
}
}
private static Path getPathParam(Map<String, ? super Object> params,
String paramName) throws ConfigException {
return getPathParam(params, paramName, () -> {
String value = getStringParam(params, paramName);
Path result = null;
if (value != null) {
result = Path.of(value);
}
return result;
});
}
private static List<Path> getModulePath(Map<String, ? super Object> params)
throws ConfigException {
return getPathListParameter(Arguments.CLIOptions.MODULE_PATH.getId(),
params);
}
private static List<Path> getPathListParameter(String paramName,
Map<String, ? super Object> params) throws ConfigException {
return getPathParam(params, paramName, () -> {
String value = params.getOrDefault(paramName, "").toString();
return List.of(value.split(File.pathSeparator)).stream()
.map(Path::of)
.collect(Collectors.toUnmodifiableList());
});
}
private String qualifiedClassName;
private Path mainJarName;
private List<Path> classPath;
private List<Path> modulePath;
private ModuleDescriptor moduleDescriptor;
}

@ -1,120 +0,0 @@
/*
* Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.incubator.jpackage.internal;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
final class ModFile {
private final String filename;
private final ModType moduleType;
enum JarType {All, UnnamedJar, ModularJar}
enum ModType {
Unknown, UnnamedJar, ModularJar, Jmod, ExplodedModule}
ModFile(File aFile) {
super();
filename = aFile.getPath();
moduleType = getModType(aFile);
}
String getModName() {
File file = new File(getFileName());
// do not try to remove extension for directories
return moduleType == ModType.ExplodedModule ?
file.getName() : getFileWithoutExtension(file.getName());
}
String getFileName() {
return filename;
}
ModType getModType() {
return moduleType;
}
private static ModType getModType(File aFile) {
ModType result = ModType.Unknown;
String filename = aFile.getAbsolutePath();
if (aFile.isFile()) {
if (filename.endsWith(".jmod")) {
result = ModType.Jmod;
}
else if (filename.endsWith(".jar")) {
JarType status = isModularJar(filename);
if (status == JarType.ModularJar) {
result = ModType.ModularJar;
}
else if (status == JarType.UnnamedJar) {
result = ModType.UnnamedJar;
}
}
}
else if (aFile.isDirectory()) {
File moduleInfo = new File(
filename + File.separator + "module-info.class");
if (moduleInfo.exists()) {
result = ModType.ExplodedModule;
}
}
return result;
}
private static JarType isModularJar(String FileName) {
JarType result = JarType.All;
try (ZipInputStream zip =
new ZipInputStream(new FileInputStream(FileName))) {
result = JarType.UnnamedJar;
for (ZipEntry entry = zip.getNextEntry(); entry != null;
entry = zip.getNextEntry()) {
if (entry.getName().matches("module-info.class")) {
result = JarType.ModularJar;
break;
}
}
} catch (IOException ex) {
}
return result;
}
private static String getFileWithoutExtension(String FileName) {
return FileName.replaceFirst("[.][^.]+$", "");
}
}

@ -1,80 +0,0 @@
/*
* Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.incubator.jpackage.internal;
import java.io.File;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.Set;
/**
* RelativeFileSet
*
* A class encapsulating a directory and a set of files within it.
*/
class RelativeFileSet {
private File basedir;
private Set<String> files = new LinkedHashSet<>();
RelativeFileSet(File base, Collection<File> files) {
basedir = base;
String baseAbsolute = basedir.getAbsolutePath();
for (File f: files) {
String absolute = f.getAbsolutePath();
if (!absolute.startsWith(baseAbsolute)) {
throw new RuntimeException("File " + f.getAbsolutePath() +
" does not belong to " + baseAbsolute);
}
if (!absolute.equals(baseAbsolute)) {
// possible in jpackage case
this.files.add(absolute.substring(baseAbsolute.length()+1));
}
}
}
RelativeFileSet(File base, Set<File> files) {
this(base, (Collection<File>) files);
}
File getBaseDirectory() {
return basedir;
}
Set<String> getIncludedFiles() {
return files;
}
@Override
public String toString() {
if (files.size() == 1) {
return "" + basedir + File.pathSeparator + files;
}
return "RelativeFileSet {basedir:" + basedir
+ ", files: {" + files + "}";
}
}

@ -65,8 +65,6 @@ import java.util.stream.Stream;
*/
class StandardBundlerParam<T> extends BundlerParamInfo<T> {
private static final ResourceBundle I18N = ResourceBundle.getBundle(
"jdk.incubator.jpackage.internal.resources.MainResources");
private static final String JAVABASEJMOD = "java.base.jmod";
private final static String DEFAULT_VERSION = "1.0";
private final static String DEFAULT_RELEASE = "1";
@ -86,67 +84,36 @@ class StandardBundlerParam<T> extends BundlerParamInfo<T> {
this.stringConverter = stringConverter;
}
static final StandardBundlerParam<RelativeFileSet> APP_RESOURCES =
static final StandardBundlerParam<LauncherData> LAUNCHER_DATA =
new StandardBundlerParam<>(
BundleParams.PARAM_APP_RESOURCES,
RelativeFileSet.class,
null, // no default. Required parameter
null // no string translation,
// tool must provide complex type
"launcherData",
LauncherData.class,
params -> {
try {
return LauncherData.create(params);
} catch (ConfigException | IOException ex) {
throw new RuntimeException(ex);
}
},
null
);
@SuppressWarnings("unchecked")
static final
StandardBundlerParam<List<RelativeFileSet>> APP_RESOURCES_LIST =
new StandardBundlerParam<>(
BundleParams.PARAM_APP_RESOURCES + "List",
(Class<List<RelativeFileSet>>) (Object) List.class,
// Default is appResources, as a single item list
p -> new ArrayList<>(Collections.singletonList(
APP_RESOURCES.fetchFrom(p))),
StandardBundlerParam::createAppResourcesListFromString
);
static final StandardBundlerParam<String> SOURCE_DIR =
static final StandardBundlerParam<Path> SOURCE_DIR =
new StandardBundlerParam<>(
Arguments.CLIOptions.INPUT.getId(),
String.class,
Path.class,
p -> null,
(s, p) -> {
String value = String.valueOf(s);
if (value.charAt(value.length() - 1) ==
File.separatorChar) {
return value.substring(0, value.length() - 1);
}
else {
return value;
}
}
(s, p) -> Path.of(s)
);
// note that each bundler is likely to replace this one with
// their own converter
static final StandardBundlerParam<RelativeFileSet> MAIN_JAR =
static final StandardBundlerParam<Path> MAIN_JAR =
new StandardBundlerParam<>(
Arguments.CLIOptions.MAIN_JAR.getId(),
RelativeFileSet.class,
params -> {
extractMainClassInfoFromAppResources(params);
return (RelativeFileSet) params.get("mainJar");
},
(s, p) -> getMainJar(s, p)
);
static final StandardBundlerParam<String> CLASSPATH =
new StandardBundlerParam<>(
"classpath",
String.class,
params -> {
extractMainClassInfoFromAppResources(params);
String cp = (String) params.get("classpath");
return cp == null ? "" : cp;
},
(s, p) -> s
Path.class,
params -> LAUNCHER_DATA.fetchFrom(params).mainJarName(),
null
);
static final StandardBundlerParam<String> MAIN_CLASS =
@ -157,14 +124,7 @@ class StandardBundlerParam<T> extends BundlerParamInfo<T> {
if (isRuntimeInstaller(params)) {
return null;
}
extractMainClassInfoFromAppResources(params);
String s = (String) params.get(
BundleParams.PARAM_APPLICATION_CLASS);
if (s == null) {
s = JLinkBundlerHelper.getMainClassFromModule(
params);
}
return s;
return LAUNCHER_DATA.fetchFrom(params).qualifiedClassName();
},
(s, p) -> s
);
@ -253,13 +213,11 @@ class StandardBundlerParam<T> extends BundlerParamInfo<T> {
(s, p) -> Arrays.asList(s.split("\n\n"))
);
// note that each bundler is likely to replace this one with
// their own converter
static final StandardBundlerParam<String> VERSION =
new StandardBundlerParam<>(
Arguments.CLIOptions.VERSION.getId(),
String.class,
params -> getDefaultAppVersion(params),
StandardBundlerParam::getDefaultAppVersion,
(s, p) -> s
);
@ -414,19 +372,12 @@ class StandardBundlerParam<T> extends BundlerParamInfo<T> {
new StandardBundlerParam<>(
Arguments.CLIOptions.MODULE_PATH.getId(),
(Class<List<Path>>) (Object)List.class,
p -> { return getDefaultModulePath(); },
p -> getDefaultModulePath(),
(s, p) -> {
List<Path> modulePath = Arrays.asList(s
.split(File.pathSeparator)).stream()
.map(ss -> new File(ss).toPath())
List<Path> modulePath = Stream.of(s.split(File.pathSeparator))
.map(Path::of)
.collect(Collectors.toList());
Path javaBasePath = null;
if (modulePath != null) {
javaBasePath = JLinkBundlerHelper
.findPathOfModule(modulePath, JAVABASEJMOD);
} else {
modulePath = new ArrayList<Path>();
}
Path javaBasePath = findPathOfModule(modulePath, JAVABASEJMOD);
// Add the default JDK module path to the module path.
if (javaBasePath == null) {
@ -434,9 +385,7 @@ class StandardBundlerParam<T> extends BundlerParamInfo<T> {
if (jdkModulePath != null) {
modulePath.addAll(jdkModulePath);
javaBasePath =
JLinkBundlerHelper.findPathOfModule(
modulePath, JAVABASEJMOD);
javaBasePath = findPathOfModule(modulePath, JAVABASEJMOD);
}
}
@ -449,6 +398,20 @@ class StandardBundlerParam<T> extends BundlerParamInfo<T> {
return modulePath;
});
// Returns the path to the JDK modules in the user defined module path.
private static Path findPathOfModule( List<Path> modulePath, String moduleName) {
for (Path path : modulePath) {
Path moduleNamePath = path.resolve(moduleName);
if (Files.exists(moduleNamePath)) {
return path;
}
}
return null;
}
static final BundlerParamInfo<String> MODULE =
new StandardBundlerParam<>(
Arguments.CLIOptions.MODULE.getId(),
@ -496,16 +459,13 @@ class StandardBundlerParam<T> extends BundlerParamInfo<T> {
}
static File getPredefinedAppImage(Map<String, ? super Object> params) {
File applicationImage = null;
if (PREDEFINED_APP_IMAGE.fetchFrom(params) != null) {
applicationImage = PREDEFINED_APP_IMAGE.fetchFrom(params);
if (!applicationImage.exists()) {
throw new RuntimeException(
MessageFormat.format(I18N.getString(
"message.app-image-dir-does-not-exist"),
PREDEFINED_APP_IMAGE.getID(),
applicationImage.toString()));
}
File applicationImage = PREDEFINED_APP_IMAGE.fetchFrom(params);
if (applicationImage != null && !applicationImage.exists()) {
throw new RuntimeException(
MessageFormat.format(I18N.getString(
"message.app-image-dir-does-not-exist"),
PREDEFINED_APP_IMAGE.getID(),
applicationImage.toString()));
}
return applicationImage;
}
@ -548,236 +508,36 @@ class StandardBundlerParam<T> extends BundlerParamInfo<T> {
appBuilder.prepareApplicationFiles(params);
}
static void extractMainClassInfoFromAppResources(
Map<String, ? super Object> params) {
boolean hasMainClass = params.containsKey(MAIN_CLASS.getID());
boolean hasMainJar = params.containsKey(MAIN_JAR.getID());
boolean hasMainJarClassPath = params.containsKey(CLASSPATH.getID());
boolean hasModule = params.containsKey(MODULE.getID());
if (hasMainClass && hasMainJar && hasMainJarClassPath || hasModule ||
isRuntimeInstaller(params)) {
return;
}
// it's a pair.
// The [0] is the srcdir [1] is the file relative to sourcedir
List<String[]> filesToCheck = new ArrayList<>();
if (hasMainJar) {
RelativeFileSet rfs = MAIN_JAR.fetchFrom(params);
for (String s : rfs.getIncludedFiles()) {
filesToCheck.add(
new String[] {rfs.getBaseDirectory().toString(), s});
}
} else if (hasMainJarClassPath) {
for (String s : CLASSPATH.fetchFrom(params).split("\\s+")) {
if (APP_RESOURCES.fetchFrom(params) != null) {
filesToCheck.add(
new String[] {APP_RESOURCES.fetchFrom(params)
.getBaseDirectory().toString(), s});
}
}
} else {
List<RelativeFileSet> rfsl = APP_RESOURCES_LIST.fetchFrom(params);
if (rfsl == null || rfsl.isEmpty()) {
return;
}
for (RelativeFileSet rfs : rfsl) {
if (rfs == null) continue;
for (String s : rfs.getIncludedFiles()) {
filesToCheck.add(
new String[]{rfs.getBaseDirectory().toString(), s});
}
}
}
// presume the set iterates in-order
for (String[] fnames : filesToCheck) {
try {
// only sniff jars
if (!fnames[1].toLowerCase().endsWith(".jar")) continue;
File file = new File(fnames[0], fnames[1]);
// that actually exist
if (!file.exists()) continue;
try (JarFile jf = new JarFile(file)) {
Manifest m = jf.getManifest();
Attributes attrs = (m != null) ?
m.getMainAttributes() : null;
if (attrs != null) {
if (!hasMainJar) {
if (fnames[0] == null) {
fnames[0] = file.getParentFile().toString();
}
params.put(MAIN_JAR.getID(), new RelativeFileSet(
new File(fnames[0]),
new LinkedHashSet<>(Collections
.singletonList(file))));
}
if (!hasMainJarClassPath) {
String cp =
attrs.getValue(Attributes.Name.CLASS_PATH);
params.put(CLASSPATH.getID(),
cp == null ? "" : cp);
}
break;
}
}
} catch (IOException ignore) {
ignore.printStackTrace();
}
}
private static List<Path> getDefaultModulePath() {
return List.of(
Path.of(System.getProperty("java.home"), "jmods").toAbsolutePath());
}
static void validateMainClassInfoFromAppResources(
Map<String, ? super Object> params) throws ConfigException {
boolean hasMainClass = params.containsKey(MAIN_CLASS.getID());
boolean hasMainJar = params.containsKey(MAIN_JAR.getID());
boolean hasMainJarClassPath = params.containsKey(CLASSPATH.getID());
boolean hasModule = params.containsKey(MODULE.getID());
boolean hasAppImage = params.containsKey(PREDEFINED_APP_IMAGE.getID());
if (hasMainClass && hasMainJar && hasMainJarClassPath ||
hasAppImage || isRuntimeInstaller(params)) {
return;
}
if (hasModule) {
if (JLinkBundlerHelper.getMainClassFromModule(params) == null) {
throw new ConfigException(
I18N.getString("ERR_NoMainClass"), null);
}
} else {
extractMainClassInfoFromAppResources(params);
if (!params.containsKey(MAIN_CLASS.getID())) {
if (hasMainJar) {
throw new ConfigException(
MessageFormat.format(I18N.getString(
"error.no-main-class-with-main-jar"),
MAIN_JAR.fetchFrom(params)),
MessageFormat.format(I18N.getString(
"error.no-main-class-with-main-jar.advice"),
MAIN_JAR.fetchFrom(params)));
} else {
throw new ConfigException(
I18N.getString("error.no-main-class"),
I18N.getString("error.no-main-class.advice"));
}
}
}
}
private static List<RelativeFileSet>
createAppResourcesListFromString(String s,
Map<String, ? super Object> objectObjectMap) {
List<RelativeFileSet> result = new ArrayList<>();
for (String path : s.split("[:;]")) {
File f = new File(path);
if (f.getName().equals("*") || path.endsWith("/") ||
path.endsWith("\\")) {
if (f.getName().equals("*")) {
f = f.getParentFile();
}
Set<File> theFiles = new HashSet<>();
try {
try (Stream<Path> stream = Files.walk(f.toPath())) {
stream.filter(Files::isRegularFile)
.forEach(p -> theFiles.add(p.toFile()));
}
} catch (IOException e) {
e.printStackTrace();
}
result.add(new RelativeFileSet(f, theFiles));
} else {
result.add(new RelativeFileSet(f.getParentFile(),
Collections.singleton(f)));
}
}
return result;
}
private static RelativeFileSet getMainJar(
String mainJarValue, Map<String, ? super Object> params) {
for (RelativeFileSet rfs : APP_RESOURCES_LIST.fetchFrom(params)) {
File appResourcesRoot = rfs.getBaseDirectory();
File mainJarFile = new File(appResourcesRoot, mainJarValue);
if (mainJarFile.exists()) {
return new RelativeFileSet(appResourcesRoot,
new LinkedHashSet<>(Collections.singletonList(
mainJarFile)));
}
mainJarFile = new File(mainJarValue);
if (mainJarFile.exists()) {
// absolute path for main-jar may fail is not legal
// below contains explicit error message.
} else {
List<Path> modulePath = MODULE_PATH.fetchFrom(params);
modulePath.removeAll(getDefaultModulePath());
if (!modulePath.isEmpty()) {
Path modularJarPath = JLinkBundlerHelper.findPathOfModule(
modulePath, mainJarValue);
if (modularJarPath != null &&
Files.exists(modularJarPath)) {
return new RelativeFileSet(appResourcesRoot,
new LinkedHashSet<>(Collections.singletonList(
modularJarPath.toFile())));
}
}
}
}
throw new IllegalArgumentException(
new ConfigException(MessageFormat.format(I18N.getString(
"error.main-jar-does-not-exist"),
mainJarValue), I18N.getString(
"error.main-jar-does-not-exist.advice")));
}
static List<Path> getDefaultModulePath() {
List<Path> result = new ArrayList<Path>();
Path jdkModulePath = Paths.get(
System.getProperty("java.home"), "jmods").toAbsolutePath();
if (jdkModulePath != null && Files.exists(jdkModulePath)) {
result.add(jdkModulePath);
}
else {
// On a developer build the JDK Home isn't where we expect it
// relative to the jmods directory. Do some extra
// processing to find it.
Map<String, String> env = System.getenv();
if (env.containsKey("JDK_HOME")) {
jdkModulePath = Paths.get(env.get("JDK_HOME"),
".." + File.separator + "images"
+ File.separator + "jmods").toAbsolutePath();
if (jdkModulePath != null && Files.exists(jdkModulePath)) {
result.add(jdkModulePath);
}
}
}
return result;
}
static String getDefaultAppVersion(Map<String, ? super Object> params) {
private static String getDefaultAppVersion(Map<String, ? super Object> params) {
String appVersion = DEFAULT_VERSION;
ModuleDescriptor descriptor = JLinkBundlerHelper.getMainModuleDescription(params);
if (descriptor != null) {
Optional<Version> oversion = descriptor.version();
if (oversion.isPresent()) {
if (isRuntimeInstaller(params)) {
return appVersion;
}
LauncherData launcherData = null;
try {
launcherData = LAUNCHER_DATA.fetchFrom(params);
} catch (RuntimeException ex) {
if (ex.getCause() instanceof ConfigException) {
return appVersion;
}
throw ex;
}
if (launcherData.isModular()) {
String moduleVersion = launcherData.getAppVersion();
if (moduleVersion != null) {
Log.verbose(MessageFormat.format(I18N.getString(
"message.module-version"),
oversion.get().toString(),
JLinkBundlerHelper.getMainModule(params)));
appVersion = oversion.get().toString();
moduleVersion,
launcherData.moduleName()));
appVersion = moduleVersion;
}
}

@ -56,6 +56,10 @@ error.no-main-class=A main class was not specified nor was one found in the supp
error.no-main-class.advice=Please specify a application class or ensure that the appResources has a jar containing one in the manifest
error.main-jar-does-not-exist=The configured main jar does not exist {0} in the input directory
error.main-jar-does-not-exist.advice=The main jar must be specified relative to the input directory (not an absolute path), and must exist within that directory
error.no-module-in-path="Failed to find {0} module in module path"
error.not-path-parameter="Invalid value of {0} parameter: {1}"
error.no-input-parameter="Missing --input parameter for non modular application"
error.no-main-jar-parameter="Missing --main-jar parameter for non modular application"
error.no-content-types-for-file-association=No MIME types were specified for File Association number {0}
error.no-content-types-for-file-association.advice=Specify MIME type for File Association number {0}
@ -69,7 +73,6 @@ error.tool-old-version.advice=Please install {0} {1} or newer
error.jlink.failed=jlink failed with: {0}
error.blocked.option=jlink option [{0}] is not permitted in --jlink-options
warning.module.does.not.exist=Module [{0}] does not exist
warning.no.jdk.modules.found=Warning: No JDK Modules found
warning.deprecation=Warning: Option "{0}" is deprecated and may be removed in a future release

@ -56,6 +56,10 @@ error.no-main-class=\u30E1\u30A4\u30F3\u30FB\u30AF\u30E9\u30B9\u304C\u6307\u5B9A
error.no-main-class.advice=\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u30FB\u30AF\u30E9\u30B9\u3092\u6307\u5B9A\u3059\u308B\u304B\u3001\u30DE\u30CB\u30D5\u30A7\u30B9\u30C8\u3067appResources\u306B\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u30FB\u30AF\u30E9\u30B9\u3092\u542B\u3080jar\u304C\u3042\u308B\u3053\u3068\u3092\u78BA\u8A8D\u3057\u3066\u304F\u3060\u3055\u3044
error.main-jar-does-not-exist=\u5165\u529B\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u3067\u3001\u69CB\u6210\u3055\u308C\u305F\u30E1\u30A4\u30F3jar\u304C{0}\u306B\u5B58\u5728\u3057\u307E\u305B\u3093
error.main-jar-does-not-exist.advice=\u5165\u529B\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u306B\u5BFE\u3057\u3066\u76F8\u5BFE\u7684\u306B(\u7D76\u5BFE\u30D1\u30B9\u3067\u306F\u306A\u3044)\u30E1\u30A4\u30F3jar\u3092\u6307\u5B9A\u3059\u308B\u5FC5\u8981\u304C\u3042\u308A\u3001\u305D\u306E\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u5185\u306B\u5B58\u5728\u3059\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059
error.no-module-in-path="Failed to find {0} module in module path"
error.not-path-parameter="Invalid value of {0} parameter: {1}"
error.no-input-parameter="Missing --input parameter for non modular application"
error.no-main-jar-parameter="Missing --main-jar parameter for non modular application"
error.no-content-types-for-file-association=No MIME types were specified for File Association number {0}
error.no-content-types-for-file-association.advice=Specify MIME type for File Association number {0}
@ -69,7 +73,6 @@ error.tool-old-version.advice={0} {1}\u4EE5\u964D\u3092\u30A4\u30F3\u30B9\u30C8\
error.jlink.failed=jlink\u304C\u6B21\u3067\u5931\u6557\u3057\u307E\u3057\u305F: {0}
error.blocked.option=jlink option [{0}] is not permitted in --jlink-options
warning.module.does.not.exist=\u30E2\u30B8\u30E5\u30FC\u30EB[{0}]\u306F\u5B58\u5728\u3057\u307E\u305B\u3093
warning.no.jdk.modules.found=\u8B66\u544A: JDK\u30E2\u30B8\u30E5\u30FC\u30EB\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093
warning.deprecation=Warning: Option "{0}" is deprecated and may be removed in a future release

@ -56,6 +56,10 @@ error.no-main-class=\u672A\u6307\u5B9A\u4E3B\u7C7B\uFF0C\u5728\u63D0\u4F9B\u7684
error.no-main-class.advice=\u8BF7\u6307\u5B9A\u5E94\u7528\u7A0B\u5E8F\u7C7B\uFF0C\u6216\u8005\u786E\u4FDD appResources \u4E2D\u6709\u4E00\u4E2A jar \u5728\u6E05\u5355\u4E2D\u5305\u542B\u5E94\u7528\u7A0B\u5E8F\u7C7B\u3002
error.main-jar-does-not-exist=\u914D\u7F6E\u7684\u4E3B jar \u5728\u8F93\u5165\u76EE\u5F55\u4E2D\u4E0D\u5B58\u5728 {0}
error.main-jar-does-not-exist.advice=\u5FC5\u987B\u4F7F\u7528\u76F8\u5BF9\u4E8E\u8F93\u5165\u76EE\u5F55\u7684\u8DEF\u5F84\uFF08\u4E0D\u4F7F\u7528\u7EDD\u5BF9\u8DEF\u5F84\uFF09\u6307\u5B9A\u4E3B jar \uFF0C\u5E76\u4E14\u8BE5\u76EE\u5F55\u4E2D\u5B58\u5728\u4E3B jar
error.no-module-in-path="Failed to find {0} module in module path"
error.not-path-parameter="Invalid value of {0} parameter: {1}"
error.no-input-parameter="Missing --input parameter for non modular application"
error.no-main-jar-parameter="Missing --main-jar parameter for non modular application"
error.no-content-types-for-file-association=No MIME types were specified for File Association number {0}
error.no-content-types-for-file-association.advice=Specify MIME type for File Association number {0}
@ -69,7 +73,6 @@ error.tool-old-version.advice=\u8BF7\u5B89\u88C5 {0} {1}\u6216\u66F4\u65B0\u7248
error.jlink.failed=jlink \u5931\u8D25\uFF0C\u51FA\u73B0 {0}
error.blocked.option=jlink option [{0}] is not permitted in --jlink-options
warning.module.does.not.exist=\u6A21\u5757 [{0}] \u4E0D\u5B58\u5728
warning.no.jdk.modules.found=\u8B66\u544A: \u672A\u627E\u5230 JDK \u6A21\u5757
warning.deprecation=Warning: Option "{0}" is deprecated and may be removed in a future release

@ -1,5 +1,5 @@
/*
* Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -226,6 +226,8 @@ public class WinMsiBundler extends AbstractBundler {
public boolean validate(Map<String, ? super Object> params)
throws ConfigException {
try {
APP_BUNDLER.fetchFrom(params).validate(params);
if (wixToolset == null) {
wixToolset = WixTool.toolset();
}

@ -192,22 +192,4 @@ public class WindowsAppImageBuilder extends AbstractAppImageBuilder {
executableFile.toFile().setExecutable(true);
executableFile.toFile().setReadOnly();
}
private void copyApplication(Map<String, ? super Object> params)
throws IOException {
List<RelativeFileSet> appResourcesList =
APP_RESOURCES_LIST.fetchFrom(params);
if (appResourcesList == null) {
throw new RuntimeException("Null app resources?");
}
for (RelativeFileSet appResources : appResourcesList) {
if (appResources == null) {
throw new RuntimeException("Null app resources?");
}
File srcdir = appResources.getBaseDirectory();
for (String fname : appResources.getIncludedFiles()) {
copyEntry(appDir, srcdir, fname);
}
}
}
}

@ -1,5 +1,5 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -45,9 +45,9 @@ public class AppImageFileTest {
@Test
public void testIdentity() throws IOException {
Map<String, ? super Object> params = new LinkedHashMap<>();
params.put("name", "Foo");
params.put("app-version", "2.3");
params.put("description", "Duck is the King");
params.put(Arguments.CLIOptions.NAME.getId(), "Foo");
params.put(Arguments.CLIOptions.VERSION.getId(), "2.3");
params.put(Arguments.CLIOptions.DESCRIPTION.getId(), "Duck is the King");
AppImageFile aif = create(params);
Assert.assertEquals("Foo", aif.getLauncherName());
@ -61,11 +61,13 @@ public class AppImageFileTest {
// We should be ready to handle curious minds.
Map<String, ? super Object> params = new LinkedHashMap<>();
params.put("invalidParamName", "randomStringValue");
params.put(Arguments.CLIOptions.APPCLASS.getId(), "TestClass");
params.put(Arguments.CLIOptions.MAIN_JAR.getId(), "test.jar");
create(params);
params = new LinkedHashMap<>();
params.put("name", "foo");
params.put("app-version", "");
params.put(Arguments.CLIOptions.NAME.getId(), "foo");
params.put(Arguments.CLIOptions.VERSION.getId(), "");
create(params);
}

@ -1,5 +1,5 @@
/*
* Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -119,7 +119,6 @@ public class DeployParamsTest {
params = new DeployParams();
params.setOutput(testRoot);
params.addResource(testRoot, new File(testRoot, "test.jar"));
params.addBundleArgument(Arguments.CLIOptions.APPCLASS.getId(),
"TestClass");
params.addBundleArgument(Arguments.CLIOptions.MAIN_JAR.getId(),

@ -1,5 +1,5 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -123,7 +123,7 @@ public final class ModulePathTest {
expectedErrorMessage = "Error: Missing argument: --runtime-image or --module-path";
} else {
expectedErrorMessage = String.format(
"Error: Module %s not found", appDesc.moduleName());
"Failed to find %s module in module path", appDesc.moduleName());
}
List<String> output = cmd