8225569: jpackage app-image layout JDK-8200758-branch
authorherrick
Fri, 14 Jun 2019 12:04:12 -0400
branchJDK-8200758-branch
changeset 57407 2c14fbeff1dc
parent 57405 539d8b3f9e1e
child 57408 f4a3f1dd2c02
8225569: jpackage app-image layout Reviewed-by: asemenyuk, kcr, almatvee
src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxAppImageBuilder.java
src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxDebBundler.java
src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxRpmBundler.java
src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/LinuxResources.properties
src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/LinuxResources_ja.properties
src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/LinuxResources_zh_CN.properties
src/jdk.jpackage/linux/native/libapplauncher/LinuxPlatform.cpp
src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacAppStoreBundler.java
src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacDmgBundler.java
src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacPkgBundler.java
src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacResources.properties
src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacResources_ja.properties
src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacResources_zh_CN.properties
src/jdk.jpackage/share/classes/jdk/jpackage/internal/AbstractImageBundler.java
src/jdk.jpackage/share/classes/jdk/jpackage/internal/IOUtils.java
src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinAppBundler.java
src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinExeBundler.java
src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinMsiBundler.java
src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WindowsAppImageBuilder.java
src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources.properties
src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources_ja.properties
src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources_zh_CN.properties
src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/template.iss
src/jdk.jpackage/windows/native/libapplauncher/WindowsPlatform.cpp
test/jdk/tools/jpackage/createappimage/JPackageCreateAppImageBase.java
test/jdk/tools/jpackage/createappimage/JPackageCreateAppImageNoNameTest.java
test/jdk/tools/jpackage/createappimage/windows/JPackageCreateAppImageWinConsoleTest.java
test/jdk/tools/jpackage/helpers/JPackagePath.java
--- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxAppImageBuilder.java	Thu Jun 13 19:34:44 2019 -0400
+++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxAppImageBuilder.java	Fri Jun 14 12:04:12 2019 -0400
@@ -58,7 +58,7 @@
     private final Path appDir;
     private final Path appModsDir;
     private final Path runtimeDir;
-    private final Path resourcesDir;
+    private final Path binDir;
     private final Path mdir;
 
     private final Map<String, ? super Object> params;
@@ -89,14 +89,13 @@
         this.appDir = root.resolve("app");
         this.appModsDir = appDir.resolve("mods");
         this.runtimeDir = root.resolve("runtime");
-        this.resourcesDir = root.resolve("resources");
+        this.binDir = root.resolve("bin");
         this.mdir = runtimeDir.resolve("lib");
         this.params = new HashMap<>();
         config.entrySet().stream().forEach(e -> params.put(
                 e.getKey().toString(), e.getValue()));
         Files.createDirectories(appDir);
         Files.createDirectories(runtimeDir);
-        Files.createDirectories(resourcesDir);
     }
 
     public LinuxAppImageBuilder(String appName, Path imageOutDir)
@@ -109,7 +108,7 @@
         this.appDir = null;
         this.appModsDir = null;
         this.runtimeDir = null;
-        this.resourcesDir = null;
+        this.binDir = null;
         this.mdir = null;
         this.params = new HashMap<>();
     }
@@ -151,13 +150,18 @@
         return new File(outDir, APP_NAME.fetchFrom(params));
     }
 
+    public static String getLauncherRelativePath(
+            Map<String, ? super Object> params) {
+        return "bin" + File.separator + APP_NAME.fetchFrom(params);
+    }
+
     public static String getLauncherName(Map<String, ? super Object> params) {
         return APP_NAME.fetchFrom(params);
     }
 
     public static String getLauncherCfgName(
             Map<String, ? super Object> params) {
-        return "app/" + APP_NAME.fetchFrom(params) + ".cfg";
+        return "app" + File.separator + APP_NAME.fetchFrom(params) + ".cfg";
     }
 
     @Override
@@ -174,12 +178,19 @@
     public void prepareApplicationFiles() throws IOException {
         Map<String, ? super Object> originalParams = new HashMap<>(params);
 
+        try {
+            IOUtils.writableOutputDir(root);
+            IOUtils.writableOutputDir(binDir);
+        } catch (PackagerException pe) {
+            throw new RuntimeException(pe);
+        }
+
         // create the primary launcher
         createLauncherForEntryPoint(params);
 
         // Copy library to the launcher folder
         try (InputStream is_lib = getResourceAsStream(LIBRARY_NAME)) {
-            writeEntry(is_lib, root.resolve(LIBRARY_NAME));
+            writeEntry(is_lib, binDir.resolve(LIBRARY_NAME));
         }
 
         // create the additional launchers, if any
@@ -203,7 +214,7 @@
     private void createLauncherForEntryPoint(
             Map<String, ? super Object> params) throws IOException {
         // Copy executable to Linux folder
-        Path executableFile = root.resolve(getLauncherName(params));
+        Path executableFile = binDir.resolve(getLauncherName(params));
         try (InputStream is_launcher =
                 getResourceAsStream("jpackageapplauncher")) {
             writeEntry(is_launcher, executableFile);
@@ -219,7 +230,7 @@
     private void copyIcon() throws IOException {
         File icon = ICON_PNG.fetchFrom(params);
         if (icon != null) {
-            File iconTarget = new File(resourcesDir.toFile(),
+            File iconTarget = new File(binDir.toFile(),
                     APP_NAME.fetchFrom(params) + ".png");
             IOUtils.copyFile(icon, iconTarget);
         }
--- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxDebBundler.java	Thu Jun 13 19:34:44 2019 -0400
+++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxDebBundler.java	Fri Jun 14 12:04:12 2019 -0400
@@ -303,14 +303,8 @@
 
     public File bundle(Map<String, ? super Object> params,
             File outdir) throws PackagerException {
-        if (!outdir.isDirectory() && !outdir.mkdirs()) {
-            throw new PackagerException ("error.cannot-create-output-dir",
-                    outdir.getAbsolutePath());
-        }
-        if (!outdir.canWrite()) {
-            throw new PackagerException("error.cannot-write-to-output-dir",
-                    outdir.getAbsolutePath());
-        }
+
+        IOUtils.writableOutputDir(outdir.toPath());
 
         // we want to create following structure
         //   <package-name>
@@ -393,8 +387,9 @@
         Map<String, String> data = createReplacementData(params);
         File rootDir = LinuxAppBundler.getRootDir(APP_IMAGE_ROOT.fetchFrom(
                 params), params);
+        File binDir = new File(rootDir, "bin");
 
-        File iconTarget = getConfig_IconFile(rootDir, params);
+        File iconTarget = getConfig_IconFile(binDir, params);
         File icon = ICON_PNG.fetchFrom(params);
         if (!StandardBundlerParam.isRuntimeInstaller(params)) {
             // prepare installer icon
@@ -429,9 +424,9 @@
                 // prepare desktop shortcut
                 try (Writer w = Files.newBufferedWriter(
                         getConfig_DesktopShortcutFile(
-                                rootDir, addLauncher).toPath())) {
+                                binDir, addLauncher).toPath())) {
                     String content = preprocessTextResource(
-                            getConfig_DesktopShortcutFile(rootDir,
+                            getConfig_DesktopShortcutFile(binDir,
                             addLauncher).getName(),
                             I18N.getString("resource.menu-shortcut-descriptor"),
                             DEFAULT_DESKTOP_FILE_TEMPLATE,
@@ -443,7 +438,7 @@
             }
 
             // prepare installer icon
-            iconTarget = getConfig_IconFile(rootDir, addLauncher);
+            iconTarget = getConfig_IconFile(binDir, addLauncher);
             icon = ICON_PNG.fetchFrom(addLauncher);
             if (icon == null || !icon.exists()) {
                 fetchResource(iconTarget.getName(),
@@ -572,7 +567,7 @@
                     int size = getSquareSizeOfImage(faIcon);
 
                     if (size > 0) {
-                        File target = new File(rootDir,
+                        File target = new File(binDir,
                                 APP_NAME.fetchFrom(params)
                                 + "_fa_" + faIcon.getName());
                         IOUtils.copyFile(faIcon, target);
@@ -617,7 +612,7 @@
 
             if (addedEntry) {
                 try (Writer w = Files.newBufferedWriter(
-                        new File(rootDir, mimeInfoFile).toPath())) {
+                        new File(binDir, mimeInfoFile).toPath())) {
                     w.write(mimeInfo.toString());
                 }
                 data.put("FILE_ASSOCIATION_INSTALL", registrations.toString());
@@ -629,10 +624,10 @@
         if (!StandardBundlerParam.isRuntimeInstaller(params)) {
             //prepare desktop shortcut
             try (Writer w = Files.newBufferedWriter(
-                    getConfig_DesktopShortcutFile(rootDir, params).toPath())) {
+                    getConfig_DesktopShortcutFile(binDir, params).toPath())) {
                 String content = preprocessTextResource(
                         getConfig_DesktopShortcutFile(
-                        rootDir, params).getName(),
+                        binDir, params).getName(),
                         I18N.getString("resource.menu-shortcut-descriptor"),
                         DEFAULT_DESKTOP_FILE_TEMPLATE,
                         data,
@@ -724,6 +719,7 @@
     private Map<String, String> createReplacementData(
             Map<String, ? super Object> params) {
         Map<String, String> data = new HashMap<>();
+        String launcher = LinuxAppImageBuilder.getLauncherRelativePath(params);
 
         data.put("APPLICATION_NAME", APP_NAME.fetchFrom(params));
         data.put("APPLICATION_FS_NAME", APP_NAME.fetchFrom(params));
@@ -731,7 +727,7 @@
         data.put("APPLICATION_VENDOR", VENDOR.fetchFrom(params));
         data.put("APPLICATION_MAINTAINER", MAINTAINER.fetchFrom(params));
         data.put("APPLICATION_VERSION", VERSION.fetchFrom(params));
-        data.put("APPLICATION_LAUNCHER_FILENAME", APP_NAME.fetchFrom(params));
+        data.put("APPLICATION_LAUNCHER_FILENAME", launcher);
         data.put("INSTALLATION_DIRECTORY", LINUX_INSTALL_DIR.fetchFrom(params));
         data.put("XDG_PREFIX", XDG_FILE_PREFIX.fetchFrom(params));
         data.put("DEPLOY_BUNDLE_CATEGORY", MENU_GROUP.fetchFrom(params));
--- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxRpmBundler.java	Thu Jun 13 19:34:44 2019 -0400
+++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxRpmBundler.java	Fri Jun 14 12:04:12 2019 -0400
@@ -253,16 +253,8 @@
 
     public File bundle(Map<String, ? super Object> params,
             File outdir) throws PackagerException {
-        if (!outdir.isDirectory() && !outdir.mkdirs()) {
-            throw new PackagerException(
-                    "error.cannot-create-output-dir",
-                    outdir.getAbsolutePath());
-        }
-        if (!outdir.canWrite()) {
-            throw new PackagerException(
-                    "error.cannot-write-to-output-dir",
-                    outdir.getAbsolutePath());
-        }
+
+        IOUtils.writableOutputDir(outdir.toPath());
 
         File imageDir = RPM_IMAGE_DIR.fetchFrom(params);
         try {
@@ -309,9 +301,10 @@
         Map<String, String> data = createReplacementData(params);
         File rootDir =
             LinuxAppBundler.getRootDir(RPM_IMAGE_DIR.fetchFrom(params), params);
+        File binDir = new File(rootDir, "bin");
 
         // prepare installer icon
-        File iconTarget = getConfig_IconFile(rootDir, params);
+        File iconTarget = getConfig_IconFile(binDir, params);
         File icon = LinuxAppBundler.ICON_PNG.fetchFrom(params);
         if (!StandardBundlerParam.isRuntimeInstaller(params)) {
             if (icon == null || !icon.exists()) {
@@ -343,10 +336,10 @@
 
             // prepare desktop shortcut
             try (Writer w = Files.newBufferedWriter(
-                    getConfig_DesktopShortcutFile(rootDir,
+                    getConfig_DesktopShortcutFile(binDir,
                             addLauncher).toPath())) {
                 String content = preprocessTextResource(
-                        getConfig_DesktopShortcutFile(rootDir,
+                        getConfig_DesktopShortcutFile(binDir,
                         addLauncher).getName(),
                         I18N.getString("resource.menu-shortcut-descriptor"),
                         DEFAULT_DESKTOP_FILE_TEMPLATE, addLauncherData,
@@ -356,7 +349,7 @@
             }
 
             // prepare installer icon
-            iconTarget = getConfig_IconFile(rootDir, addLauncher);
+            iconTarget = getConfig_IconFile(binDir, addLauncher);
             icon = LinuxAppBundler.ICON_PNG.fetchFrom(addLauncher);
             if (icon == null || !icon.exists()) {
                 fetchResource(iconTarget.getName(),
@@ -488,7 +481,7 @@
                     int size = getSquareSizeOfImage(faIcon);
 
                     if (size > 0) {
-                        File target = new File(rootDir,
+                        File target = new File(binDir,
                                 APP_NAME.fetchFrom(params)
                                 + "_fa_" + faIcon.getName());
                         IOUtils.copyFile(faIcon, target);
@@ -533,7 +526,7 @@
 
             if (addedEntry) {
                 try (Writer w = Files.newBufferedWriter(
-                        new File(rootDir, mimeInfoFile).toPath())) {
+                        new File(binDir, mimeInfoFile).toPath())) {
                     w.write(mimeInfo.toString());
                 }
                 data.put("FILE_ASSOCIATION_INSTALL", registrations.toString());
@@ -545,9 +538,9 @@
         if (!StandardBundlerParam.isRuntimeInstaller(params)) {
             //prepare desktop shortcut
             try (Writer w = Files.newBufferedWriter(
-                    getConfig_DesktopShortcutFile(rootDir, params).toPath())) {
+                    getConfig_DesktopShortcutFile(binDir, params).toPath())) {
                 String content = preprocessTextResource(
-                        getConfig_DesktopShortcutFile(rootDir,
+                        getConfig_DesktopShortcutFile(binDir,
                                                       params).getName(),
                         I18N.getString("resource.menu-shortcut-descriptor"),
                         DEFAULT_DESKTOP_FILE_TEMPLATE, data,
@@ -575,13 +568,14 @@
     private Map<String, String> createReplacementData(
             Map<String, ? super Object> params) throws IOException {
         Map<String, String> data = new HashMap<>();
+        String launcher = LinuxAppImageBuilder.getLauncherRelativePath(params);
 
         data.put("APPLICATION_NAME", APP_NAME.fetchFrom(params));
         data.put("APPLICATION_FS_NAME", APP_NAME.fetchFrom(params));
         data.put("APPLICATION_PACKAGE", BUNDLE_NAME.fetchFrom(params));
         data.put("APPLICATION_VENDOR", VENDOR.fetchFrom(params));
         data.put("APPLICATION_VERSION", VERSION.fetchFrom(params));
-        data.put("APPLICATION_LAUNCHER_FILENAME", APP_NAME.fetchFrom(params));
+        data.put("APPLICATION_LAUNCHER_FILENAME", launcher);
         data.put("INSTALLATION_DIRECTORY", LINUX_INSTALL_DIR.fetchFrom(params));
         data.put("XDG_PREFIX", XDG_FILE_PREFIX.fetchFrom(params));
         data.put("DEPLOY_BUNDLE_CATEGORY", MENU_GROUP.fetchFrom(params));
--- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/LinuxResources.properties	Thu Jun 13 19:34:44 2019 -0400
+++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/LinuxResources.properties	Fri Jun 14 12:04:12 2019 -0400
@@ -47,8 +47,6 @@
 error.parameters-null.advice=Pass in a non-null parameters map.
 error.tool-not-found=Can not find {0}.
 error.tool-not-found.advice=Please install required packages.
-error.cannot-create-output-dir=Output directory {0} cannot be created.
-error.cannot-write-to-output-dir=Output directory {0} is not writable.
 error.no-content-types-for-file-association=No MIME types were specified for File Association number {0}.
 error.too-many-content-types-for-file-association=More than one MIME types was specified for File Association number {0}.
 error.invalid-value-for-package-name=Invalid value "{0}" for the package name.
--- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/LinuxResources_ja.properties	Thu Jun 13 19:34:44 2019 -0400
+++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/LinuxResources_ja.properties	Fri Jun 14 12:04:12 2019 -0400
@@ -47,8 +47,6 @@
 error.parameters-null.advice=Pass in a non-null parameters map.
 error.tool-not-found=Can not find {0}.
 error.tool-not-found.advice=Please install required packages.
-error.cannot-create-output-dir=Output directory {0} cannot be created.
-error.cannot-write-to-output-dir=Output directory {0} is not writable.
 error.no-content-types-for-file-association=No MIME types were specified for File Association number {0}.
 error.too-many-content-types-for-file-association=More than one MIME types was specified for File Association number {0}.
 error.invalid-value-for-package-name=Invalid value "{0}" for the package name.
--- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/LinuxResources_zh_CN.properties	Thu Jun 13 19:34:44 2019 -0400
+++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/LinuxResources_zh_CN.properties	Fri Jun 14 12:04:12 2019 -0400
@@ -47,8 +47,6 @@
 error.parameters-null.advice=Pass in a non-null parameters map.
 error.tool-not-found=Can not find {0}.
 error.tool-not-found.advice=Please install required packages.
-error.cannot-create-output-dir=Output directory {0} cannot be created.
-error.cannot-write-to-output-dir=Output directory {0} is not writable.
 error.no-content-types-for-file-association=No MIME types were specified for File Association number {0}.
 error.too-many-content-types-for-file-association=More than one MIME types was specified for File Association number {0}.
 error.invalid-value-for-package-name=Invalid value "{0}" for the package name.
--- a/src/jdk.jpackage/linux/native/libapplauncher/LinuxPlatform.cpp	Thu Jun 13 19:34:44 2019 -0400
+++ b/src/jdk.jpackage/linux/native/libapplauncher/LinuxPlatform.cpp	Fri Jun 14 12:04:12 2019 -0400
@@ -76,7 +76,8 @@
 }
 
 TString LinuxPlatform::GetPackageLauncherDirectory() {
-    return GetPackageRootDirectory();
+    return FilePath::IncludeTrailingSeparator(
+            GetPackageRootDirectory()) + _T("bin");
 }
 
 TString LinuxPlatform::GetPackageRuntimeBinDirectory() {
@@ -130,8 +131,16 @@
 }
 
 TString LinuxPlatform::GetPackageRootDirectory() {
+    TString result;
     TString filename = GetModuleFileName();
-    return FilePath::ExtractFilePath(filename);
+    TString binPath = FilePath::ExtractFilePath(filename);
+
+    size_t slash = binPath.find_last_of(TRAILING_PATHSEPARATOR);
+    if (slash != TString::npos) {
+        result = binPath.substr(0, slash);
+    }
+
+    return result;
 }
 
 TString LinuxPlatform::GetAppDataDirectory() {
--- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacAppStoreBundler.java	Thu Jun 13 19:34:44 2019 -0400
+++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacAppStoreBundler.java	Fri Jun 14 12:04:12 2019 -0400
@@ -121,16 +121,8 @@
             File outdir) throws PackagerException {
         Log.verbose(MessageFormat.format(I18N.getString(
                 "message.building-bundle"), APP_NAME.fetchFrom(params)));
-        if (!outdir.isDirectory() && !outdir.mkdirs()) {
-            throw new PackagerException(
-                    "error.cannot-create-output-dir",
-                     outdir.getAbsolutePath());
-        }
-        if (!outdir.canWrite()) {
-            throw new PackagerException(
-                    "error.cannot-write-to-output-dir",
-                    outdir.getAbsolutePath());
-        }
+
+        IOUtils.writableOutputDir(outdir.toPath());
 
         // first, load in some overrides
         // icns needs @2 versions, so load in the @2 default
--- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacDmgBundler.java	Thu Jun 13 19:34:44 2019 -0400
+++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacDmgBundler.java	Fri Jun 14 12:04:12 2019 -0400
@@ -54,16 +54,8 @@
             File outdir) throws PackagerException {
         Log.verbose(MessageFormat.format(I18N.getString("message.building-dmg"),
                 APP_NAME.fetchFrom(params)));
-        if (!outdir.isDirectory() && !outdir.mkdirs()) {
-            throw new PackagerException(
-                    "error.cannot-create-output-dir",
-                    outdir.getAbsolutePath());
-        }
-        if (!outdir.canWrite()) {
-            throw new PackagerException(
-                    "error.cannot-write-to-output-dir",
-                    outdir.getAbsolutePath());
-        }
+
+        IOUtils.writableOutputDir(outdir.toPath());
 
         File appImageDir = APP_IMAGE_TEMP_ROOT.fetchFrom(params);
         try {
--- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacPkgBundler.java	Thu Jun 13 19:34:44 2019 -0400
+++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacPkgBundler.java	Fri Jun 14 12:04:12 2019 -0400
@@ -135,16 +135,8 @@
             File outdir) throws PackagerException {
         Log.verbose(MessageFormat.format(I18N.getString("message.building-pkg"),
                 APP_NAME.fetchFrom(params)));
-        if (!outdir.isDirectory() && !outdir.mkdirs()) {
-            throw new PackagerException(
-                    "error.cannot-create-output-dir",
-                    outdir.getAbsolutePath());
-        }
-        if (!outdir.canWrite()) {
-            throw new PackagerException(
-                    "error.cannot-write-to-output-dir",
-                    outdir.getAbsolutePath());
-        }
+
+        IOUtils.writableOutputDir(outdir.toPath());
 
         File appImageDir = null;
         try {
--- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacResources.properties	Thu Jun 13 19:34:44 2019 -0400
+++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacResources.properties	Fri Jun 14 12:04:12 2019 -0400
@@ -39,8 +39,6 @@
 error.explicit-sign-no-cert.advice=Either specify a valid cert in 'mac.signing-key-developer-id-app' or unset 'signBundle' or set 'signBundle' to false.
 error.parameters-null=Parameters map is null.
 error.parameters-null.advice=Pass in a non-null parameters map.
-error.cannot-create-output-dir=Output directory {0} cannot be created.
-error.cannot-write-to-output-dir=Output directory {0} is not writable.
 error.must-sign-app-store=Mac App Store apps must be signed, and signing has been disabled by bundler configuration.
 error.must-sign-app-store.advice=Either unset 'signBundle' or set 'signBundle' to true.
 error.no-app-signing-key=No Mac App Store App Signing Key
--- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacResources_ja.properties	Thu Jun 13 19:34:44 2019 -0400
+++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacResources_ja.properties	Fri Jun 14 12:04:12 2019 -0400
@@ -39,8 +39,6 @@
 error.explicit-sign-no-cert.advice=Either specify a valid cert in 'mac.signing-key-developer-id-app' or unset 'signBundle' or set 'signBundle' to false.
 error.parameters-null=Parameters map is null.
 error.parameters-null.advice=Pass in a non-null parameters map.
-error.cannot-create-output-dir=Output directory {0} cannot be created.
-error.cannot-write-to-output-dir=Output directory {0} is not writable.
 error.must-sign-app-store=Mac App Store apps must be signed, and signing has been disabled by bundler configuration.
 error.must-sign-app-store.advice=Either unset 'signBundle' or set 'signBundle' to true.
 error.no-app-signing-key=No Mac App Store App Signing Key
--- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacResources_zh_CN.properties	Thu Jun 13 19:34:44 2019 -0400
+++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacResources_zh_CN.properties	Fri Jun 14 12:04:12 2019 -0400
@@ -39,8 +39,6 @@
 error.explicit-sign-no-cert.advice=Either specify a valid cert in 'mac.signing-key-developer-id-app' or unset 'signBundle' or set 'signBundle' to false.
 error.parameters-null=Parameters map is null.
 error.parameters-null.advice=Pass in a non-null parameters map.
-error.cannot-create-output-dir=Output directory {0} cannot be created.
-error.cannot-write-to-output-dir=Output directory {0} is not writable.
 error.must-sign-app-store=Mac App Store apps must be signed, and signing has been disabled by bundler configuration.
 error.must-sign-app-store.advice=Either unset 'signBundle' or set 'signBundle' to true.
 error.no-app-signing-key=No Mac App Store App Signing Key
--- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/AbstractImageBundler.java	Thu Jun 13 19:34:44 2019 -0400
+++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/AbstractImageBundler.java	Fri Jun 14 12:04:12 2019 -0400
@@ -61,16 +61,9 @@
     protected File createRoot(Map<String, ? super Object> params,
             File outputDirectory, boolean dependentTask, String name)
             throws PackagerException {
-        if (!outputDirectory.isDirectory() && !outputDirectory.mkdirs()) {
-            throw new RuntimeException(MessageFormat.format(
-                    I18N.getString("error.cannot-create-output-dir"),
-                    outputDirectory.getAbsolutePath()));
-        }
-        if (!outputDirectory.canWrite()) {
-            throw new RuntimeException(MessageFormat.format(
-                    I18N.getString("error.cannot-write-to-output-dir"),
-                    outputDirectory.getAbsolutePath()));
-        }
+
+        IOUtils.writableOutputDir(outputDirectory.toPath());
+
         if (!dependentTask) {
             Log.verbose(MessageFormat.format(
                     I18N.getString("message.creating-app-bundle"),
--- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/IOUtils.java	Thu Jun 13 19:34:44 2019 -0400
+++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/IOUtils.java	Fri Jun 14 12:04:12 2019 -0400
@@ -280,4 +280,17 @@
 
         return ret;
     }
+
+    static void writableOutputDir(Path outdir) throws PackagerException {
+        File file = outdir.toFile();
+
+        if (!file.isDirectory() && !file.mkdirs()) {
+            throw new PackagerException("error.cannot-create-output-dir",
+                    file.getAbsolutePath());
+        }
+        if (!file.canWrite()) {
+            throw new PackagerException("error.cannot-write-to-output-dir",
+                    file.getAbsolutePath());
+        }
+    }
 }
--- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinAppBundler.java	Thu Jun 13 19:34:44 2019 -0400
+++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinAppBundler.java	Fri Jun 14 12:04:12 2019 -0400
@@ -151,12 +151,17 @@
         return APP_NAME.fetchFrom(p);
     }
 
+    public static String getLauncherRelativePath(
+            Map<String, ? super Object> p) {
+        return "bin" + File.separator + getAppName(p) + ".exe";
+    }
+
     public static String getLauncherName(Map<String, ? super Object> p) {
         return getAppName(p) + ".exe";
     }
 
     public static String getLauncherCfgName(Map<String, ? super Object> p) {
-        return "app\\" + getAppName(p) +".cfg";
+        return "app" + File.separator + getAppName(p) +".cfg";
     }
 
     public boolean bundle(Map<String, ? super Object> p, File outputDirectory)
--- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinExeBundler.java	Thu Jun 13 19:34:44 2019 -0400
+++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinExeBundler.java	Fri Jun 14 12:04:12 2019 -0400
@@ -342,14 +342,8 @@
 
     public File bundle(Map<String, ? super Object> params, File outdir)
             throws PackagerException {
-        if (!outdir.isDirectory() && !outdir.mkdirs()) {
-            throw new PackagerException("error.cannot-create-output-dir",
-                    outdir.getAbsolutePath());
-        }
-        if (!outdir.canWrite()) {
-            throw new PackagerException("error.cannot-write-to-output-dir",
-                    outdir.getAbsolutePath());
-        }
+
+        IOUtils.writableOutputDir(outdir.toPath());
 
         String tempDirectory = WindowsDefender.getUserTempDirectory();
         if (Arguments.CLIOptions.context().userProvidedBuildRoot) {
--- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinMsiBundler.java	Thu Jun 13 19:34:44 2019 -0400
+++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinMsiBundler.java	Fri Jun 14 12:04:12 2019 -0400
@@ -486,14 +486,8 @@
 
     public File bundle(Map<String, ? super Object> params, File outdir)
             throws PackagerException {
-        if (!outdir.isDirectory() && !outdir.mkdirs()) {
-            throw new PackagerException("error.cannot-create-output-dir",
-                    outdir.getAbsolutePath());
-        }
-        if (!outdir.canWrite()) {
-            throw new PackagerException("error.cannot-write-to-output-dir",
-                    outdir.getAbsolutePath());
-        }
+
+        IOUtils.writableOutputDir(outdir.toPath());
 
         // validate we have valid tools before continuing
         String light = TOOL_LIGHT_EXECUTABLE.fetchFrom(params);
@@ -587,7 +581,8 @@
             xml.writeStartDocument();
             xml.writeStartElement("Include");
 
-            File launcher = new File(imageRootDir, WinAppBundler.getLauncherName(params));
+            File launcher = new File(imageRootDir,
+                    WinAppBundler.getLauncherRelativePath(params));
             if (launcher.exists()) {
                 String iconPath = launcher.getAbsolutePath().replace(".exe", ".ico");
                 if (MENU_HINT.fetchFrom(params)) {
@@ -608,7 +603,7 @@
                 Map<String, ? super Object> sl = addLaunchers.get(i);
                 if (SHORTCUT_HINT.fetchFrom(sl) || MENU_HINT.fetchFrom(sl)) {
                     File addLauncher = new File(imageRootDir,
-                            WinAppBundler.getLauncherName(sl));
+                            WinAppBundler.getLauncherRelativePath(sl));
                     String addLauncherPath
                             = relativePath(imageRootDir, addLauncher);
                     String addLauncherIconPath
@@ -741,8 +736,8 @@
 
         boolean needRegistryKey = !MSI_SYSTEM_WIDE.fetchFrom(params);
         File imageRootDir = WIN_APP_IMAGE.fetchFrom(params);
-        File launcherFile =
-                new File(imageRootDir, WinAppBundler.getLauncherName(params));
+        File launcherFile = new File(imageRootDir,
+                WinAppBundler.getLauncherRelativePath(params));
 
         // Find out if we need to use registry. We need it if
         //  - we doing user level install as file can not serve as KeyPath
@@ -814,7 +809,7 @@
             for (int i = 0; i < addLaunchers.size(); i++) {
                 Map<String, ? super Object> sl = addLaunchers.get(i);
                 File addLauncherFile = new File(imageRootDir,
-                        WinAppBundler.getLauncherName(sl));
+                        WinAppBundler.getLauncherRelativePath(sl));
                 if (f.equals(addLauncherFile)) {
                     if (SHORTCUT_HINT.fetchFrom(sl)) {
                         out.println(prefix
--- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WindowsAppImageBuilder.java	Thu Jun 13 19:34:44 2019 -0400
+++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WindowsAppImageBuilder.java	Fri Jun 14 12:04:12 2019 -0400
@@ -76,6 +76,7 @@
     private final Path appModsDir;
     private final Path runtimeDir;
     private final Path mdir;
+    private final Path binDir;
 
     private final Map<String, ? super Object> params;
 
@@ -125,6 +126,7 @@
         this.appModsDir = appDir.resolve("mods");
         this.runtimeDir = root.resolve("runtime");
         this.mdir = runtimeDir.resolve("lib");
+        this.binDir = root.resolve("bin");
         Files.createDirectories(appDir);
         Files.createDirectories(runtimeDir);
     }
@@ -141,6 +143,7 @@
         this.appModsDir = null;
         this.runtimeDir = root;
         this.mdir = runtimeDir.resolve("lib");
+        this.binDir = null;
         Files.createDirectories(runtimeDir);
     }
 
@@ -229,16 +232,14 @@
     @Override
     public void prepareApplicationFiles() throws IOException {
         Map<String, ? super Object> originalParams = new HashMap<>(params);
-        File rootFile = root.toFile();
-        if (!rootFile.isDirectory() && !rootFile.mkdirs()) {
-            throw new RuntimeException(MessageFormat.format(I18N.getString(
-                "error.cannot-create-output-dir"), rootFile.getAbsolutePath()));
+
+        try {
+            IOUtils.writableOutputDir(root);
+            IOUtils.writableOutputDir(binDir);
+        } catch (PackagerException pe) {
+            throw new RuntimeException(pe);
         }
-        if (!rootFile.canWrite()) {
-            throw new RuntimeException(MessageFormat.format(
-                    I18N.getString("error.cannot-write-to-output-dir"),
-                    rootFile.getAbsolutePath()));
-        }
+
         // create the .exe launchers
         createLauncherForEntryPoint(params);
 
@@ -247,7 +248,7 @@
 
         // copy in the needed libraries
         try (InputStream is_lib = getResourceAsStream(LIBRARY_NAME)) {
-            Files.copy(is_lib, root.resolve(LIBRARY_NAME));
+            Files.copy(is_lib, binDir.resolve(LIBRARY_NAME));
         }
 
         copyMSVCDLLs();
@@ -272,7 +273,7 @@
                     p.toFile().getName().toLowerCase()))
                  .forEach(p -> {
                     try {
-                        Files.copy(p, root.resolve((p.toFile().getName())));
+                        Files.copy(p, binDir.resolve((p.toFile().getName())));
                     } catch (IOException e) {
                         ioe.set(e);
                     }
@@ -294,10 +295,10 @@
         if (REDIST_MSVCR_URL != null && REDIST_MSVCP_URL != null) {
             Files.copy(
                     REDIST_MSVCR_URL,
-                    root.resolve(REDIST_MSVCR.replaceAll("VS_VER", VS_VER)));
+                    binDir.resolve(REDIST_MSVCR.replaceAll("VS_VER", VS_VER)));
             Files.copy(
                     REDIST_MSVCP_URL,
-                    root.resolve(REDIST_MSVCP.replaceAll("VS_VER", VS_VER)));
+                    binDir.resolve(REDIST_MSVCP.replaceAll("VS_VER", VS_VER)));
             return true;
         }
 
@@ -369,8 +370,9 @@
 
         prepareExecutableProperties(params);
 
-        // Copy executable root folder
-        Path executableFile = root.resolve(getLauncherName(params));
+        // Copy executable to bin folder
+        Path executableFile = binDir.resolve(getLauncherName(params));
+
         try (InputStream is_launcher =
                 getResourceAsStream(getLauncherResourceName(params))) {
             writeEntry(is_launcher, executableFile);
@@ -418,7 +420,7 @@
         }
 
         Files.copy(iconTarget.toPath(),
-                root.resolve(APP_NAME.fetchFrom(params) + ".ico"));
+                binDir.resolve(APP_NAME.fetchFrom(params) + ".ico"));
     }
 
     private void copyApplication(Map<String, ? super Object> params)
--- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources.properties	Thu Jun 13 19:34:44 2019 -0400
+++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources.properties	Fri Jun 14 12:04:12 2019 -0400
@@ -44,8 +44,6 @@
 error.no-windows-resources=This copy of the JDK does not support Windows.
 error.no-windows-resources.advice=Please use the Oracle JDK for Windows.
 error.cannot-find-launcher=Cannot find cfg file in predefined app image directory {0}.
-error.cannot-create-output-dir=Output directory {0} cannot be created.
-error.cannot-write-to-output-dir=Output directory {0} is not writable.
 error.iscc-not-found=Can not find Inno Setup Compiler (iscc.exe).
 error.iscc-not-found.advice=Download Inno Setup 5 or later from http://www.jrsoftware.org and add it to the PATH.
 error.copyright-is-too-long=The copyright string is too long for InnoSetup.
--- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources_ja.properties	Thu Jun 13 19:34:44 2019 -0400
+++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources_ja.properties	Fri Jun 14 12:04:12 2019 -0400
@@ -37,15 +37,13 @@
 resource.inno-setup-project-file=Inno Setup project file
 resource.setup-icon=setup dialog icon
 resource.post-install-script=script to run after application image is populated
-resource.wxl-file-name=MsiInstallerStrings_ja.wxl
+resource.wxl-file-name=MsiInstallerStrings_en.wxl
 
 error.parameters-null=Parameters map is null.
 error.parameters-null.advice=Pass in a non-null parameters map.
 error.no-windows-resources=This copy of the JDK does not support Windows.
 error.no-windows-resources.advice=Please use the Oracle JDK for Windows.
 error.cannot-find-launcher=Cannot find cfg file in predefined app image directory {0}.
-error.cannot-create-output-dir=Output directory {0} cannot be created.
-error.cannot-write-to-output-dir=Output directory {0} is not writable.
 error.iscc-not-found=Can not find Inno Setup Compiler (iscc.exe).
 error.iscc-not-found.advice=Download Inno Setup 5 or later from http://www.jrsoftware.org and add it to the PATH.
 error.copyright-is-too-long=The copyright string is too long for InnoSetup.
--- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources_zh_CN.properties	Thu Jun 13 19:34:44 2019 -0400
+++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources_zh_CN.properties	Fri Jun 14 12:04:12 2019 -0400
@@ -37,15 +37,13 @@
 resource.inno-setup-project-file=Inno Setup project file
 resource.setup-icon=setup dialog icon
 resource.post-install-script=script to run after application image is populated
-resource.wxl-file-name=MsiInstallerStrings_zh_CN.wxl
+resource.wxl-file-name=MsiInstallerStrings_en.wxl
 
 error.parameters-null=Parameters map is null.
 error.parameters-null.advice=Pass in a non-null parameters map.
 error.no-windows-resources=This copy of the JDK does not support Windows.
 error.no-windows-resources.advice=Please use the Oracle JDK for Windows.
 error.cannot-find-launcher=Cannot find cfg file in predefined app image directory {0}.
-error.cannot-create-output-dir=Output directory {0} cannot be created.
-error.cannot-write-to-output-dir=Output directory {0} is not writable.
 error.iscc-not-found=Can not find Inno Setup Compiler (iscc.exe).
 error.iscc-not-found.advice=Download Inno Setup 5 or later from http://www.jrsoftware.org and add it to the PATH.
 error.copyright-is-too-long=The copyright string is too long for InnoSetup.
--- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/template.iss	Thu Jun 13 19:34:44 2019 -0400
+++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/template.iss	Fri Jun 14 12:04:12 2019 -0400
@@ -26,7 +26,7 @@
 Compression=lzma
 SolidCompression=yes
 PrivilegesRequired=APPLICATION_INSTALL_PRIVILEGE
-SetupIconFile=INSTALLER_NAME\LAUNCHER_NAME.ico
+SetupIconFile=INSTALLER_NAME\bin\LAUNCHER_NAME.ico
 UninstallDisplayIcon={app}\LAUNCHER_NAME.ico
 UninstallDisplayName=INSTALLER_NAME
 WizardImageStretch=No
@@ -38,21 +38,21 @@
 Name: "english"; MessagesFile: "compiler:Default.isl"
 
 [Files]
-Source: "INSTALLER_NAME\LAUNCHER_NAME.exe"; DestDir: "{app}"; Flags: ignoreversion
+Source: "INSTALLER_NAME\bin\LAUNCHER_NAME.exe"; DestDir: "{app}"; Flags: ignoreversion
 Source: "INSTALLER_NAME\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs
 
 [Icons]
-Name: "{group}\INSTALLER_NAME"; Filename: "{app}\LAUNCHER_NAME.exe"; IconFilename: "{app}\LAUNCHER_NAME.ico"; Check: APPLICATION_MENU_SHORTCUT()
-Name: "{commondesktop}\INSTALLER_NAME"; Filename: "{app}\LAUNCHER_NAME.exe";  IconFilename: "{app}\LAUNCHER_NAME.ico"; Check: APPLICATION_DESKTOP_SHORTCUT()
+Name: "{group}\INSTALLER_NAME"; Filename: "{app}\bin\LAUNCHER_NAME.exe"; IconFilename: "{app}\bin\LAUNCHER_NAME.ico"; Check: APPLICATION_MENU_SHORTCUT()
+Name: "{commondesktop}\INSTALLER_NAME"; Filename: "{app}\bin\LAUNCHER_NAME.exe";  IconFilename: "{app}\bin\LAUNCHER_NAME.ico"; Check: APPLICATION_DESKTOP_SHORTCUT()
 ADD_LAUNCHERS
 
 [Run]
-Filename: "{app}\RUN_FILENAME.exe"; Parameters: "-Xappcds:generatecache"; Check: APPLICATION_APP_CDS_INSTALL()
-Filename: "{app}\RUN_FILENAME.exe"; Description: "{cm:LaunchProgram,INSTALLER_NAME}"; Flags: nowait postinstall skipifsilent; Check: APPLICATION_NOT_SERVICE()
-Filename: "{app}\RUN_FILENAME.exe"; Parameters: "-install -svcName ""INSTALLER_NAME"" -svcDesc ""APPLICATION_DESCRIPTION"" -mainExe ""APPLICATION_LAUNCHER_FILENAME"" START_ON_INSTALL RUN_AT_STARTUP"; Check: APPLICATION_SERVICE()
+Filename: "{app}\bin\RUN_FILENAME.exe"; Parameters: "-Xappcds:generatecache"; Check: APPLICATION_APP_CDS_INSTALL()
+Filename: "{app}\bin\RUN_FILENAME.exe"; Description: "{cm:LaunchProgram,INSTALLER_NAME}"; Flags: nowait postinstall skipifsilent; Check: APPLICATION_NOT_SERVICE()
+Filename: "{app}\bin\RUN_FILENAME.exe"; Parameters: "-install -svcName ""INSTALLER_NAME"" -svcDesc ""APPLICATION_DESCRIPTION"" -mainExe ""APPLICATION_LAUNCHER_FILENAME"" START_ON_INSTALL RUN_AT_STARTUP"; Check: APPLICATION_SERVICE()
 
 [UninstallRun]
-Filename: "{app}\RUN_FILENAME.exe "; Parameters: "-uninstall -svcName INSTALLER_NAME STOP_ON_UNINSTALL"; Check: APPLICATION_SERVICE()
+Filename: "{app}\bin\RUN_FILENAME.exe "; Parameters: "-uninstall -svcName INSTALLER_NAME STOP_ON_UNINSTALL"; Check: APPLICATION_SERVICE()
 
 [Code]
 function returnTrue(): Boolean;
--- a/src/jdk.jpackage/windows/native/libapplauncher/WindowsPlatform.cpp	Thu Jun 13 19:34:44 2019 -0400
+++ b/src/jdk.jpackage/windows/native/libapplauncher/WindowsPlatform.cpp	Fri Jun 14 12:04:12 2019 -0400
@@ -151,7 +151,8 @@
 }
 
 TString WindowsPlatform::GetPackageLauncherDirectory() {
-    return GetPackageRootDirectory();
+    return  FilePath::IncludeTrailingSeparator(
+            GetPackageRootDirectory()) + _T("bin");
 }
 
 TString WindowsPlatform::GetPackageRuntimeBinDirectory() {
@@ -175,8 +176,16 @@
 }
 
 TString WindowsPlatform::GetPackageRootDirectory() {
+    TString result;
     TString filename = GetModuleFileName();
-    return FilePath::ExtractFilePath(filename);
+    TString binPath = FilePath::ExtractFilePath(filename);
+
+    size_t slash = binPath.find_last_of(TRAILING_PATHSEPARATOR);
+    if (slash != TString::npos) {
+        result = binPath.substr(0, slash);
+    }
+
+    return result;
 }
 
 TString WindowsPlatform::GetAppDataDirectory() {
--- a/test/jdk/tools/jpackage/createappimage/JPackageCreateAppImageBase.java	Thu Jun 13 19:34:44 2019 -0400
+++ b/test/jdk/tools/jpackage/createappimage/JPackageCreateAppImageBase.java	Fri Jun 14 12:04:12 2019 -0400
@@ -23,6 +23,7 @@
 
 import java.io.File;
 import java.nio.file.Files;
+import java.nio.file.Path;
 
 public abstract class JPackageCreateAppImageBase {
     private static final String appOutput = JPackagePath.getAppOutputFile();
@@ -44,18 +45,27 @@
     }
 
     public static void validate(String app) throws Exception {
-        int retVal = JPackageHelper.execute(null, app);
+        Path outPath = Path.of(appWorkingDir, appOutput);
+        int retVal = JPackageHelper.execute(outPath.toFile(), app);
+
+        if (outPath.toFile().exists()) {
+             System.out.println("output contents: ");
+             System.out.println(Files.readString(outPath) + "\n");
+        } else {
+             System.out.println("no output file: " + outPath
+                   + " from command: " + app);
+        }
+
         if (retVal != 0) {
             throw new AssertionError(
-                   "Test application exited with error: " + retVal);
+                "Test application (" + app + ") exited with error: " + retVal);
         }
 
-        File outfile = new File(appWorkingDir + File.separator + appOutput);
-        if (!outfile.exists()) {
+        if (!outPath.toFile().exists()) {
             throw new AssertionError(appOutput + " was not created");
         }
 
-        String output = Files.readString(outfile.toPath());
+        String output = Files.readString(outPath);
         String[] result = output.split("\n");
         validateResult(result);
     }
--- a/test/jdk/tools/jpackage/createappimage/JPackageCreateAppImageNoNameTest.java	Thu Jun 13 19:34:44 2019 -0400
+++ b/test/jdk/tools/jpackage/createappimage/JPackageCreateAppImageNoNameTest.java	Fri Jun 14 12:04:12 2019 -0400
@@ -35,9 +35,10 @@
  */
 public class JPackageCreateAppImageNoNameTest {
     private static final String OUTPUT = "output";
-    private static final String app = JPackagePath.getAppNoName();
+    private static final String app = JPackagePath.getApp("Hello");
     private static final String appOutput = JPackagePath.getAppOutputFile();
-    private static final String appWorkingDir = JPackagePath.getAppWorkingDirNoName();
+    private static final String appWorkingDir =
+            JPackagePath.getAppWorkingDir("Hello");
 
     private static final String[] CMD = {
         "create-app-image",
--- a/test/jdk/tools/jpackage/createappimage/windows/JPackageCreateAppImageWinConsoleTest.java	Thu Jun 13 19:34:44 2019 -0400
+++ b/test/jdk/tools/jpackage/createappimage/windows/JPackageCreateAppImageWinConsoleTest.java	Fri Jun 14 12:04:12 2019 -0400
@@ -22,7 +22,7 @@
  */
 
 import java.io.IOException;
-import java.io.File;
+import java.nio.file.Path;
 import java.io.InputStream;
 import java.io.FileInputStream;
 
@@ -67,15 +67,13 @@
     };
 
     private static void checkSubsystem(boolean console) throws Exception {
-        String file = console ? OUTPUT_WIN_CONSOLE : OUTPUT;
-        file += File.separator;
-        file += NAME;
-        file += File.separator;
-        file += NAME + ".exe";
+        Path path = Path.of(console ? OUTPUT_WIN_CONSOLE : OUTPUT,
+                NAME, "bin", NAME + ".exe");
 
-        JPackageCreateAppImageBase.validate(file);
+        System.out.println("validate path: " + path.toString());
+        JPackageCreateAppImageBase.validate(path.toString());
 
-        try (InputStream inputStream = new FileInputStream(file)) {
+        try (InputStream inputStream = new FileInputStream(path.toString())) {
             byte [] bytes = new byte[BUFFER_SIZE];
             if (inputStream.read(bytes) != BUFFER_SIZE) {
                 throw new AssertionError("Wrong number of bytes read");
@@ -86,19 +84,23 @@
             for (int i = 0;  i < (bytes.length - 4); i++) {
                 if (bytes[i] == 0x50 && bytes[i + 1] == 0x45 &&
                         bytes[i + 2] == 0x0 && bytes[i + 3] == 0x0) {
-                    i = i + 4 + 20 + 68; // Signature, File Header and subsystem offset.
+
+                    // Signature, File Header and subsystem offset.
+                    i = i + 4 + 20 + 68;
                     byte subsystem = bytes[i];
                     if (console) {
                         if (subsystem != CONSOLE_SUBSYSTEM) {
-                            throw new AssertionError("Unexpected subsystem: " + subsystem);
+                            throw new AssertionError("Unexpected subsystem: "
+                                    + subsystem);
                         } else {
-                            return; // done
+                            return;
                         }
                     } else {
                         if (subsystem != GUI_SUBSYSTEM) {
-                            throw new AssertionError("Unexpected subsystem: " + subsystem);
+                            throw new AssertionError("Unexpected subsystem: "
+                                    + subsystem);
                         } else {
-                            return; // done
+                            return;
                         }
                     }
                 }
@@ -117,7 +119,8 @@
         JPackageHelper.executeCLI(true, cmd);
     }
 
-    private static void testCreateAppImageToolProvider(String [] cmd) throws Exception {
+    private static void testCreateAppImageToolProvider(String [] cmd)
+                throws Exception {
         JPackageHelper.executeToolProvider(true, cmd);
     }
 
--- a/test/jdk/tools/jpackage/helpers/JPackagePath.java	Thu Jun 13 19:34:44 2019 -0400
+++ b/test/jdk/tools/jpackage/helpers/JPackagePath.java	Fri Jun 14 12:04:12 2019 -0400
@@ -22,9 +22,11 @@
  */
 
 import java.io.File;
+import java.nio.file.Path;
 
 /**
- * Helper class which contains functions to get different system dependent paths used by tests
+ * Helper class which contains functions to get different system
+ * dependent paths used by tests
  */
 public class JPackagePath {
 
@@ -33,10 +35,12 @@
     private static final String WIN_PROGRAM_FILES = "C:\\Program Files";
 
     // Path to Windows Start menu items
-    private static final String WIN_START_MENU = "C:\\ProgramData\\Microsoft\\Windows\\Start Menu\\Programs";
+    private static final String WIN_START_MENU =
+            "C:\\ProgramData\\Microsoft\\Windows\\Start Menu\\Programs";
 
     // Path to Windows public desktop location
-    private static final String WIN_PUBLIC_DESKTOP = "C:\\Users\\Public\\Desktop";
+    private static final String WIN_PUBLIC_DESKTOP =
+            "C:\\Users\\Public\\Desktop";
 
     // Return path to test src adjusted to location of caller
     public static String getTestSrcRoot() {
@@ -55,14 +59,12 @@
 
     public static String getApp(String name) {
         if (JPackageHelper.isWindows()) {
-            return "output" + File.separator + name
-                    + File.separator + name + ".exe";
+            return Path.of("output", name, "bin", name + ".exe").toString();
         } else if (JPackageHelper.isOSX()) {
-            return "output" + File.separator + name + ".app"
-                    + File.separator + "Contents"
-                    + File.separator + "MacOS" + File.separator + name;
+            return Path.of("output", name + ".app",
+                    "Contents", "MacOS", name).toString();
         } else if (JPackageHelper.isLinux()) {
-            return "output" + File.separator + name + File.separator + name;
+            return Path.of("output", name, "bin", name).toString();
         } else {
             throw new AssertionError("Cannot detect platform");
         }
@@ -75,63 +77,49 @@
 
     public static String getAppIcon(String name) {
         if (JPackageHelper.isWindows()) {
-            return "output" + File.separator + name + File.separator
-                    + name + ".ico";
+            return Path.of("output", name, "bin", name + ".ico").toString();
         } else if (JPackageHelper.isOSX()) {
-            return "output" + File.separator + name + ".app"
-                    + File.separator + "Contents" + File.separator
-                    + "Resources" + File.separator + name + ".icns";
+            return Path.of("output", name + ".app",
+                    "Contents", "Resources", name + ".icns").toString();
         } else if (JPackageHelper.isLinux()) {
-            return "output" + File.separator + name + File.separator
-                    + "resources"+ File.separator + name + ".png";
+            return Path.of("output", name, "bin", name + ".png").toString();
         } else {
             throw new AssertionError("Cannot detect platform");
         }
     }
 
-    // Returns path to generate test application without --name argument
-    public static String getAppNoName() {
+    // Returns path to generate secondary launcher of given application
+    public static String getAppSL(String sl) {
+        return getAppSL("test", sl);
+    }
+
+    public static String getAppSL(String app, String sl) {
         if (JPackageHelper.isWindows()) {
-            return "output" + File.separator + "Hello" + File.separator + "Hello.exe";
+            return Path.of("output", app, "bin", sl + ".exe").toString();
         } else if (JPackageHelper.isOSX()) {
-            return "output" + File.separator + "Hello.app" + File.separator + "Contents"
-                    + File.separator + "MacOS" + File.separator + "Hello";
+            return Path.of("output", app + ".app",
+                    "Contents", "MacOS", sl).toString();
         } else if (JPackageHelper.isLinux()) {
-            return "output" + File.separator + "Hello" + File.separator + "Hello";
+            return Path.of("output", app, "bin", sl).toString();
         } else {
             throw new AssertionError("Cannot detect platform");
         }
     }
 
-    // Returns path to generate secondary launcher of test application
-    public static String getAppSL(String name) {
-        if (JPackageHelper.isWindows()) {
-            return "output" + File.separator + "test" + File.separator
-                    + name + ".exe";
-        } else if (JPackageHelper.isOSX()) {
-            return "output" + File.separator + "test.app" + File.separator
-                    + "Contents" + File.separator + "MacOS"
-                    + File.separator + name;
-        } else if (JPackageHelper.isLinux()) {
-            return "output" + File.separator + "test" + File.separator + name;
-        } else {
-            throw new AssertionError("Cannot detect platform");
-        }
-    }
-
-    // Returns path to app working directory (where test application generates its output)
+    // Returns path to app working directory
+    // (where test application generates its output)
     public static String getAppWorkingDir() {
         return getAppWorkingDir("test");
     }
 
     public static String getAppWorkingDir(String name) {
          if (JPackageHelper.isWindows()) {
-            return "output" + File.separator + name + File.separator + "app";
+            return Path.of("output", name, "app").toString();
         } else if (JPackageHelper.isOSX()) {
-            return "output" + File.separator + name + ".app"
-                    + File.separator + "Contents" + File.separator + "Java";
+            return Path.of("output", name + ".app",
+                    "Contents", "Java").toString();
         } else if (JPackageHelper.isLinux()) {
-            return "output" + File.separator + name + File.separator + "app";
+            return Path.of("output", name, "app").toString();
         } else {
             throw new AssertionError("Cannot detect platform");
         }
@@ -139,29 +127,17 @@
 
     // Returns path to test application cfg file
     public static String getAppCfg() {
-         if (JPackageHelper.isWindows()) {
-            return "output" + File.separator + "test" + File.separator + "app" + File.separator
-                    + "test.cfg";
-        } else if (JPackageHelper.isOSX()) {
-            return "output" + File.separator + "test.app" + File.separator + "Contents"
-                    + File.separator + "Java" + File.separator + "test.cfg";
-        } else if (JPackageHelper.isLinux()) {
-            return "output" + File.separator + "test" + File.separator + "app" + File.separator
-                    + "test.cfg";
-        } else {
-            throw new AssertionError("Cannot detect platform");
-        }
+        return getAppCfg("test");
     }
 
-    // Returns path to app working directory without --name (where test application generates its output)
-    public static String getAppWorkingDirNoName() {
+    public static String getAppCfg(String name) {
          if (JPackageHelper.isWindows()) {
-            return "output" + File.separator + "Hello" + File.separator + "app";
+            return Path.of("output", name, "app", name + ".cfg").toString();
         } else if (JPackageHelper.isOSX()) {
-            return "output" + File.separator + "Hello.app" + File.separator + "Contents"
-                    + File.separator + "Java";
+            return Path.of("output", name + ".app",
+                    "Contents", "Java", name + ".cfg").toString();
         } else if (JPackageHelper.isLinux()) {
-            return "output" + File.separator + "Hello" + File.separator + "app";
+            return Path.of("output", name, "app", name + ".cfg").toString();
         } else {
             throw new AssertionError("Cannot detect platform");
         }
@@ -169,19 +145,19 @@
 
     // Returns path including executable to java in image runtime folder
     public static String getRuntimeJava() {
+        return getRuntimeJava("test");
+    }
+
+    public static String getRuntimeJava(String name) {
         if (JPackageHelper.isWindows()) {
-            return "output" + File.separator + "test"
-                    + File.separator + "runtime" + File.separator
-                    + "bin" + File.separator + "java.exe";
+            return Path.of("output", name,
+                    "runtime", "bin", "java.exe").toString();
         } else if (JPackageHelper.isOSX()) {
-            return "output" + File.separator + "test.app" + File.separator
-                    + "Contents" + File.separator
-                    + "runtime" + File.separator + "Contents" + File.separator
-                    + "Home" + File.separator + "bin" + File.separator + "java";
+            return Path.of("output", name + ".app", "Contents",
+                    "runtime", "Contents", "Home", "bin", "java").toString();
         } else if (JPackageHelper.isLinux()) {
-            return "output" + File.separator + "test"
-                    + File.separator + "runtime" + File.separator
-                    + "bin" + File.separator + "java";
+            return Path.of("output", name,
+                    "runtime", "bin", "java").toString();
         } else {
             throw new AssertionError("Cannot detect platform");
         }
@@ -194,18 +170,18 @@
 
     // Returns path to bin folder in image runtime
     public static String getRuntimeBin() {
+        return getRuntimeBin("test");
+    }
+
+    public static String getRuntimeBin(String name) {
         if (JPackageHelper.isWindows()) {
-            return "output" + File.separator + "test"
-                    + File.separator + "runtime" + File.separator + "bin";
+            return Path.of("output", name, "runtime", "bin").toString();
         } else if (JPackageHelper.isOSX()) {
-            return "output" + File.separator + "test.app"
-                    + File.separator + "Contents"
-                    + File.separator + "runtime"
-                    + File.separator + "Contents"
-                    + File.separator + "Home" + File.separator + "bin";
+            return Path.of("output", name + ".app",
+                    "Contents", "runtime",
+                    "Contents", "Home", "bin").toString();
         } else if (JPackageHelper.isLinux()) {
-            return "output" + File.separator + "test"
-                    + File.separator + "runtime" + File.separator + "bin";
+            return Path.of("output", name, "runtime", "bin").toString();
         } else {
             throw new AssertionError("Cannot detect platform");
         }
@@ -216,8 +192,8 @@
     }
 
     public static String getWinUserLocal() {
-        return System.getProperty("user.home") + File.separator + "AppData"
-                 + File.separator + "Local";
+        return Path.of(System.getProperty("user.home"),
+                "AppData", "Local").toString();
     }
 
     public static String getWinStartMenu() {
@@ -229,65 +205,77 @@
     }
 
     public static String getWinUserLocalStartMenu() {
-        return System.getProperty("user.home") + File.separator + "AppData"
-                + File.separator + "Roaming" + File.separator + "Microsoft"
-                + File.separator + "Windows" + File.separator + "Start Menu"
-                + File.separator + "Programs";
-
+        return Path.of(System.getProperty("user.home"), "AppData", "Roaming",
+                "Microsoft", "Windows", "Start Menu", "Programs").toString();
     }
 
     public static String getWinInstalledApp(String testName) {
-        return getWinProgramFiles() + File.separator + testName + File.separator
-                + testName + ".exe";
+        return Path.of(getWinProgramFiles(), testName,
+                testName + ".exe").toString();
     }
 
-    public static String getWinInstalledApp(String installDir, String testName) {
-        return getWinProgramFiles() + File.separator + installDir + File.separator
-                + testName + ".exe";
+    public static String getWinInstalledApp(String installDir,
+            String testName) {
+        return Path.of(getWinProgramFiles(), installDir, "bin",
+                testName + ".exe").toString();
     }
 
     public static String getOSXInstalledApp(String testName) {
-        return File.separator + "Applications" + File.separator + testName
-                + ".app" + File.separator + "Contents" + File.separator
-                + "MacOS" + File.separator + testName;
+        return File.separator + "Applications"
+                + File.separator + testName + ".app"
+                + File.separator + "Contents"
+                + File.separator + "MacOS"
+                + File.separator + testName;
     }
 
     public static String getLinuxInstalledApp(String testName) {
-        return File.separator + "opt" + File.separator + testName +
-                File.separator + testName;
+        return File.separator + "opt"
+                + File.separator + testName
+                + File.separator + testName;
     }
 
     public static String getOSXInstalledApp(String subDir, String testName) {
-        return File.separator + "Applications" + File.separator + subDir
-                + File.separator + testName + ".app" + File.separator
-                + "Contents" + File.separator + "MacOS" + File.separator
-                + testName;
+        return File.separator + "Applications"
+                + File.separator + subDir
+                + File.separator + testName + ".app"
+                + File.separator + "Contents"
+                + File.separator + "MacOS"
+                + File.separator + testName;
     }
 
     public static String getLinuxInstalledApp(String subDir, String testName) {
-        return File.separator + "opt" + File.separator + subDir + File.separator
-                + testName + File.separator + testName;
+        return File.separator + "opt"
+                + File.separator + subDir
+                + File.separator + testName
+                + File.separator + testName;
     }
 
     public static String getWinInstallFolder(String testName) {
-        return getWinProgramFiles() + File.separator + testName;
+        return getWinProgramFiles()
+                + File.separator + testName;
     }
 
     public static String getLinuxInstallFolder(String testName) {
-        return File.separator + "opt" + File.separator + testName;
+        return File.separator + "opt"
+                + File.separator + testName;
     }
 
     public static String getLinuxInstallFolder(String subDir, String testName) {
         if (testName == null) {
-            return File.separator + "opt" + File.separator + subDir;
+            return File.separator + "opt"
+                    + File.separator + subDir;
         } else {
-            return File.separator + "opt" + File.separator + subDir
+            return File.separator + "opt"
+                    + File.separator + subDir
                     + File.separator + testName;
         }
     }
 
     public static String getWinUserLocalInstalledApp(String testName) {
-        return getWinUserLocal() + File.separator + testName + File.separator + testName + ".exe";
+        return getWinUserLocal()
+                + File.separator + testName
+                + File.separator + "bin"
+                + File.separator + testName + ".exe";
     }
 
     public static String getWinUserLocalInstallFolder(String testName) {
@@ -296,7 +284,8 @@
 
     // Returs path to test license file
     public static String getLicenseFilePath() {
-        String path = JPackagePath.getTestSrcRoot() + File.separator + "resources"
+        String path = JPackagePath.getTestSrcRoot()
+                + File.separator + "resources"
                 + File.separator + "license.txt";
 
         return path;
@@ -304,7 +293,8 @@
 
     // Returns path to app folder of installed application
     public static String getWinInstalledAppFolder(String testName) {
-        return getWinProgramFiles() + File.separator + testName + File.separator
-                + "app";
+        return getWinProgramFiles()
+                + File.separator + testName
+                + File.separator + "app";
     }
 }