--- a/make/CompileDemos.gmk Thu Jun 27 22:03:19 2019 +0200
+++ b/make/CompileDemos.gmk Thu Jun 27 19:14:42 2019 -0400
@@ -219,6 +219,11 @@
MAIN_CLASS := transparentruler.Ruler, \
))
+$(eval $(call SetupBuildDemo, JNLPConverter, \
+ DEMO_SUBDIR := jpackage, \
+ MAIN_CLASS := jnlp.converter.Main, \
+))
+
################################################################################
# Copy html and README files.
--- a/make/CompileJavaModules.gmk Thu Jun 27 22:03:19 2019 +0200
+++ b/make/CompileJavaModules.gmk Thu Jun 27 19:14:42 2019 -0400
@@ -380,6 +380,13 @@
################################################################################
+jdk.jpackage_COPY += .gif .png .txt .spec .script .prerm .preinst .postrm .postinst .list \
+ .desktop .copyright .control .plist .template .icns .scpt .entitlements .wxs .wxl .iss .ico .bmp
+
+jdk.jpackage_CLEAN += .properties
+
+################################################################################
+
jdk.jconsole_COPY += .gif .png
jdk.jconsole_CLEAN_FILES += $(wildcard \
--- a/make/common/Modules.gmk Thu Jun 27 22:03:19 2019 +0200
+++ b/make/common/Modules.gmk Thu Jun 27 19:14:42 2019 -0400
@@ -1,5 +1,5 @@
#
-# Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2014, 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
@@ -128,6 +128,7 @@
JRE_TOOL_MODULES += \
jdk.jdwp.agent \
jdk.pack \
+ jdk.jpackage \
jdk.scripting.nashorn.shell \
#
@@ -168,6 +169,7 @@
jdk.naming.rmi \
jdk.net \
jdk.pack \
+ jdk.jpackage \
jdk.rmic \
jdk.scripting.nashorn \
jdk.sctp \
@@ -227,6 +229,13 @@
endif
################################################################################
+# jpackage is only on windows, macosx, and linux
+
+ifeq ($(call isTargetOs, windows macosx linux), false)
+ MODULES_FILTER += jdk.jpackage
+endif
+
+################################################################################
# Module list macros
# Use append so that the custom extension may add to these variables
--- a/make/common/NativeCompilation.gmk Thu Jun 27 22:03:19 2019 +0200
+++ b/make/common/NativeCompilation.gmk Thu Jun 27 19:14:42 2019 -0400
@@ -397,6 +397,7 @@
# ARFLAGS the archiver flags to be used
# OBJECT_DIR the directory where we store the object files
# OUTPUT_DIR the directory where the resulting binary is put
+# SYMBOLS_DIR the directory where the debug symbols are put, defaults to OUTPUT_DIR
# INCLUDES only pick source from these directories
# EXCLUDES do not pick source from these directories
# INCLUDE_FILES only compile exactly these files!
@@ -511,8 +512,6 @@
$$(call SetIfEmpty, $1_SYSROOT_CFLAGS, $$($$($1_TOOLCHAIN)_SYSROOT_CFLAGS))
$$(call SetIfEmpty, $1_SYSROOT_LDFLAGS, $$($$($1_TOOLCHAIN)_SYSROOT_LDFLAGS))
- # Make sure the dirs exist.
- $$(call MakeDir, $$($1_OBJECT_DIR) $$($1_OUTPUT_DIR))
$$(foreach d, $$($1_SRC), $$(if $$(wildcard $$d), , \
$$(error SRC specified to SetupNativeCompilation $1 contains missing directory $$d)))
@@ -883,30 +882,31 @@
ifeq ($$($1_COPY_DEBUG_SYMBOLS), true)
ifneq ($$($1_DEBUG_SYMBOLS), false)
+ $$(call SetIfEmpty, $1_SYMBOLS_DIR, $$($1_OUTPUT_DIR))
# Only copy debug symbols for dynamic libraries and programs.
ifneq ($$($1_TYPE), STATIC_LIBRARY)
# Generate debuginfo files.
ifeq ($(call isTargetOs, windows), true)
- $1_EXTRA_LDFLAGS += -debug "-pdb:$$($1_OUTPUT_DIR)/$$($1_NOSUFFIX).pdb" \
- "-map:$$($1_OUTPUT_DIR)/$$($1_NOSUFFIX).map"
- $1_DEBUGINFO_FILES := $$($1_OUTPUT_DIR)/$$($1_NOSUFFIX).pdb \
- $$($1_OUTPUT_DIR)/$$($1_NOSUFFIX).map
+ $1_EXTRA_LDFLAGS += -debug "-pdb:$$($1_SYMBOLS_DIR)/$$($1_NOSUFFIX).pdb" \
+ "-map:$$($1_SYMBOLS_DIR)/$$($1_NOSUFFIX).map"
+ $1_DEBUGINFO_FILES := $$($1_SYMBOLS_DIR)/$$($1_NOSUFFIX).pdb \
+ $$($1_SYMBOLS_DIR)/$$($1_NOSUFFIX).map
else ifeq ($(call isTargetOs, linux solaris), true)
- $1_DEBUGINFO_FILES := $$($1_OUTPUT_DIR)/$$($1_NOSUFFIX).debuginfo
+ $1_DEBUGINFO_FILES := $$($1_SYMBOLS_DIR)/$$($1_NOSUFFIX).debuginfo
# Setup the command line creating debuginfo files, to be run after linking.
# It cannot be run separately since it updates the original target file
$1_CREATE_DEBUGINFO_CMDS := \
$$($1_OBJCOPY) --only-keep-debug $$($1_TARGET) $$($1_DEBUGINFO_FILES) $$(NEWLINE) \
- $(CD) $$($1_OUTPUT_DIR) && \
+ $(CD) $$($1_SYMBOLS_DIR) && \
$$($1_OBJCOPY) --add-gnu-debuglink=$$($1_DEBUGINFO_FILES) $$($1_TARGET)
else ifeq ($(call isTargetOs, macosx), true)
$1_DEBUGINFO_FILES := \
- $$($1_OUTPUT_DIR)/$$($1_BASENAME).dSYM/Contents/Info.plist \
- $$($1_OUTPUT_DIR)/$$($1_BASENAME).dSYM/Contents/Resources/DWARF/$$($1_BASENAME)
+ $$($1_SYMBOLS_DIR)/$$($1_BASENAME).dSYM/Contents/Info.plist \
+ $$($1_SYMBOLS_DIR)/$$($1_BASENAME).dSYM/Contents/Resources/DWARF/$$($1_BASENAME)
$1_CREATE_DEBUGINFO_CMDS := \
- $(DSYMUTIL) --out $$($1_OUTPUT_DIR)/$$($1_BASENAME).dSYM $$($1_TARGET)
+ $(DSYMUTIL) --out $$($1_SYMBOLS_DIR)/$$($1_BASENAME).dSYM $$($1_TARGET)
endif
# Since the link rule creates more than one file that we want to track,
@@ -928,14 +928,14 @@
$1 += $$($1_DEBUGINFO_FILES)
ifeq ($$($1_ZIP_EXTERNAL_DEBUG_SYMBOLS), true)
- $1_DEBUGINFO_ZIP := $$($1_OUTPUT_DIR)/$$($1_NOSUFFIX).diz
+ $1_DEBUGINFO_ZIP := $$($1_SYMBOLS_DIR)/$$($1_NOSUFFIX).diz
$1 += $$($1_DEBUGINFO_ZIP)
# The dependency on TARGET is needed for debuginfo files
# to be rebuilt properly.
$$($1_DEBUGINFO_ZIP): $$($1_DEBUGINFO_FILES) $$($1_TARGET)
- $(CD) $$($1_OUTPUT_DIR) && \
- $(ZIPEXE) -q -r $$@ $$(subst $$($1_OUTPUT_DIR)/,, $$($1_DEBUGINFO_FILES))
+ $(CD) $$($1_SYMBOLS_DIR) && \
+ $(ZIPEXE) -q -r $$@ $$(subst $$($1_SYMBOLS_DIR)/,, $$($1_DEBUGINFO_FILES))
endif
endif # !STATIC_LIBRARY
@@ -971,6 +971,7 @@
$$($1_TARGET): $$($1_TARGET_DEPS)
$$(call LogInfo, Building static library $$($1_BASENAME))
+ $$(call MakeDir, $$($1_OUTPUT_DIR) $$($1_SYMBOLS_DIR))
$$(call ExecuteWithLog, $$($1_OBJECT_DIR)/$$($1_SAFE_NAME)_link, \
$$($1_AR) $$($1_ARFLAGS) $(AR_OUT_OPTION)$$($1_TARGET) $$($1_ALL_OBJS) \
$$($1_RES))
@@ -1072,7 +1073,9 @@
# Keep as much as possible on one execution line for best performance
# on Windows
$$(call LogInfo, Linking $$($1_BASENAME))
+ $$(call MakeDir, $$($1_OUTPUT_DIR) $$($1_SYMBOLS_DIR))
ifeq ($(call isTargetOs, windows), true)
+
$$(call ExecuteWithLog, $$($1_OBJECT_DIR)/$$($1_SAFE_NAME)_link, \
$$($1_LD) $$($1_LDFLAGS) $$($1_EXTRA_LDFLAGS) $$($1_SYSROOT_LDFLAGS) \
$(LD_OUT_OPTION)$$($1_TARGET) $$($1_LD_OBJ_ARG) $$($1_RES) $$(GLOBAL_LIBS) \
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/make/launcher/Launcher-jdk.jpackage.gmk Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,78 @@
+#
+# 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.
+#
+
+include LauncherCommon.gmk
+
+
+################################################################################
+
+$(eval $(call SetupBuildLauncher, jpackage, \
+ MAIN_CLASS := jdk.jpackage.main.Main, \
+))
+
+################################################################################
+
+JPACKAGE_APPLAUNCHEREXE_SRC := \
+ $(TOPDIR)/src/jdk.jpackage/$(OPENJDK_TARGET_OS)/native/jpackageapplauncher
+
+# Output app launcher executable in resources dir, and symbols in the object dir
+$(eval $(call SetupJdkExecutable, BUILD_JPACKAGE_APPLAUNCHEREXE, \
+ NAME := jpackageapplauncher, \
+ OUTPUT_DIR := $(JDK_OUTPUTDIR)/modules/$(MODULE)/jdk/jpackage/internal/resources, \
+ SYMBOLS_DIR := $(SUPPORT_OUTPUTDIR)/native/$(MODULE)/jpackageapplauncher, \
+ SRC := $(JPACKAGE_APPLAUNCHEREXE_SRC), \
+ TOOLCHAIN := TOOLCHAIN_LINK_CXX, \
+ OPTIMIZATION := LOW, \
+ CFLAGS := $(CXXFLAGS_JDKEXE), \
+ CFLAGS_windows := -EHsc -DLAUNCHERC -DUNICODE -D_UNICODE, \
+ LDFLAGS := $(LDFLAGS_JDKEXE), \
+ LIBS_macosx := -framework Cocoa, \
+ LIBS := $(LIBCXX), \
+ LIBS_linux := -ldl, \
+ LIBS_windows := user32.lib shell32.lib advapi32.lib, \
+))
+
+TARGETS += $(BUILD_JPACKAGE_APPLAUNCHEREXE)
+
+# Build non-console version of launcher
+ifeq ($(call isTargetOs, windows), true)
+
+ $(eval $(call SetupJdkExecutable, BUILD_JPACKAGE_APPLAUNCHERWEXE, \
+ NAME := jpackageapplauncherw, \
+ OUTPUT_DIR := $(JDK_OUTPUTDIR)/modules/$(MODULE)/jdk/jpackage/internal/resources, \
+ SYMBOLS_DIR := $(SUPPORT_OUTPUTDIR)/native/$(MODULE)/jpackageapplauncherw, \
+ SRC := $(JPACKAGE_APPLAUNCHEREXE_SRC), \
+ TOOLCHAIN := TOOLCHAIN_LINK_CXX, \
+ OPTIMIZATION := LOW, \
+ CFLAGS := $(CXXFLAGS_JDKEXE), \
+ CFLAGS_windows := -EHsc -DUNICODE -D_UNICODE, \
+ LDFLAGS := $(LDFLAGS_JDKEXE), \
+ LIBS := $(LIBCXX), \
+ LIBS_windows := user32.lib shell32.lib advapi32.lib, \
+ ))
+
+ TARGETS += $(BUILD_JPACKAGE_APPLAUNCHERWEXE)
+endif
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/make/lib/Lib-jdk.jpackage.gmk Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,99 @@
+#
+# 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.
+#
+
+include LibCommon.gmk
+
+################################################################################
+
+# Output app launcher library in resources dir, and symbols in the object dir
+$(eval $(call SetupJdkLibrary, BUILD_LIB_APPLAUNCHER, \
+ NAME := applauncher, \
+ OUTPUT_DIR := $(JDK_OUTPUTDIR)/modules/$(MODULE)/jdk/jpackage/internal/resources, \
+ SYMBOLS_DIR := $(SUPPORT_OUTPUTDIR)/native/$(MODULE)/libapplauncher, \
+ TOOLCHAIN := TOOLCHAIN_LINK_CXX, \
+ OPTIMIZATION := LOW, \
+ CFLAGS := $(CXXFLAGS_JDKLIB), \
+ CFLAGS_windows := -EHsc -DUNICODE -D_UNICODE, \
+ LDFLAGS := $(LDFLAGS_JDKLIB) $(LDFLAGS_CXX_JDK) \
+ $(call SET_SHARED_LIBRARY_ORIGIN), \
+ LIBS := $(LIBCXX), \
+ LIBS_windows := user32.lib shell32.lib advapi32.lib ole32.lib, \
+ LIBS_linux := -ldl -lpthread, \
+ LIBS_macosx := -ldl -framework Cocoa, \
+))
+
+$(BUILD_LIB_APPLAUNCHER): $(call FindLib, java.base, java)
+
+TARGETS += $(BUILD_LIB_APPLAUNCHER)
+
+################################################################################
+
+ifeq ($(call isTargetOs, windows), true)
+
+ $(eval $(call SetupJdkLibrary, BUILD_LIB_JPACKAGE, \
+ NAME := jpackage, \
+ OPTIMIZATION := LOW, \
+ CFLAGS := $(CXXFLAGS_JDKLIB), \
+ CFLAGS_windows := -EHsc -DUNICODE -D_UNICODE, \
+ LDFLAGS := $(LDFLAGS_JDKLIB) $(LDFLAGS_CXX_JDK) \
+ $(call SET_SHARED_LIBRARY_ORIGIN), \
+ LIBS := $(LIBCXX), \
+ LIBS_windows := user32.lib shell32.lib advapi32.lib ole32.lib, \
+ ))
+
+ TARGETS += $(BUILD_LIB_JPACKAGE)
+
+ # Build Wix custom action helper
+ # Output library in resources dir, and symbols in the object dir
+ $(eval $(call SetupJdkLibrary, BUILD_LIB_WIXHELPER, \
+ NAME := wixhelper, \
+ OUTPUT_DIR := $(JDK_OUTPUTDIR)/modules/$(MODULE)/jdk/jpackage/internal/resources, \
+ SYMBOLS_DIR := $(SUPPORT_OUTPUTDIR)/native/$(MODULE)/libwixhelper, \
+ OPTIMIZATION := LOW, \
+ CFLAGS := $(CXXFLAGS_JDKLIB), \
+ CFLAGS_windows := -EHsc -DUNICODE -D_UNICODE, \
+ LDFLAGS := $(LDFLAGS_JDKLIB) $(LDFLAGS_CXX_JDK), \
+ LIBS := $(LIBCXX), \
+ LIBS_windows := msi.lib Shlwapi.lib User32.lib, \
+ ))
+
+ TARGETS += $(BUILD_LIB_WIXHELPER)
+
+ $(eval $(call SetupJdkExecutable, BUILD_JPACKAGE_MSIWRAPPER, \
+ NAME := msiwrapper, \
+ OUTPUT_DIR := $(JDK_OUTPUTDIR)/modules/$(MODULE)/jdk/jpackage/internal/resources, \
+ SYMBOLS_DIR := $(SUPPORT_OUTPUTDIR)/native/$(MODULE)/msiwrapper, \
+ SRC := $(TOPDIR)/src/jdk.jpackage/$(OPENJDK_TARGET_OS)/native/msiwrapper, \
+ EXTRA_FILES := $(addprefix $(TOPDIR)/src/jdk.jpackage/$(OPENJDK_TARGET_OS)/native/libjpackage/, \
+ FileUtils.cpp Log.cpp WinSysInfo.cpp tstrings.cpp WinErrorHandling.cpp ErrorHandling.cpp), \
+ CFLAGS := $(CXXFLAGS_JDKEXE) \
+ $(addprefix -I$(TOPDIR)/src/jdk.jpackage/$(OPENJDK_TARGET_OS)/native/, msiwrapper libjpackage), \
+ CFLAGS_windows := -EHsc -DUNICODE -D_UNICODE, \
+ LDFLAGS := $(LDFLAGS_JDKEXE), \
+ LIBS := $(LIBCXX), \
+ ))
+
+ TARGETS += $(BUILD_JPACKAGE_MSIWRAPPER)
+endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/demo/share/jpackage/JNLPConverter/README.txt Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,23 @@
+About JNLPConverter
+===================
+
+JNLPConverter is a standalone tool which uses jpackage to create bundles from
+Java Web Start(TM) Applications and helps to migrate from JNLP to jpackage.
+JNLPConverter will locate and use the jpackage tool from the same JDK as used
+to run JNLPConverter. JNLPConverter supports HTTP/HTTPS and FILE protocol.
+
+Running JNLPConverter
+=====================
+
+To run the JNLPConverter:
+
+ java -jar JNLPConverter.jar <mode> <options>
+
+To get help on JNLPConverter options:
+
+ java -jar JNLPConverter.jar --help
+
+These instructions assume that this installation's version of the java command
+is in your path. If it isn't, then you should either specify the complete path
+to the java command or update your PATH environment variable as described
+in the installation instructions for the Java(TM) SE Development Kit.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/demo/share/jpackage/JNLPConverter/src/jnlp/converter/HTTPHelper.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,193 @@
+/*
+ * 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.
+ *
+ * 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 jnlp.converter;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.HttpURLConnection;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URL;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import jnlp.converter.parser.GeneralUtil;
+
+public class HTTPHelper {
+
+ public static final int BUFFER_SIZE = 4096;
+
+ public static String downloadFile(String url, String destFolder, String destFileName) throws MalformedURLException, IOException {
+ HttpURLConnection connection = null;
+ String destFile = null;
+
+ try {
+ if (url.contains(" ")) {
+ url = url.replace(" ", "%20");
+ }
+ if (url.contains("\\")) {
+ url = url.replace("\\", "/");
+ }
+
+ URL resource = new URL(url);
+ connection = (HttpURLConnection) resource.openConnection();
+
+ int responseCode = connection.getResponseCode();
+ if (responseCode == HttpURLConnection.HTTP_OK) {
+ destFile = destFolder + File.separator + destFileName;
+ Log.verbose("Downloading " + url + " to " + destFile);
+
+ try (InputStream inputStream = connection.getInputStream();
+ OutputStream outputStream = new FileOutputStream(destFile)) {
+ byte[] buffer = new byte[BUFFER_SIZE];
+
+ int length;
+ do {
+ length = inputStream.read(buffer);
+ if (length > 0) {
+ outputStream.write(buffer, 0, length);
+ }
+ } while (length > 0);
+ }
+ } else {
+ HTTPHelperException e = new HTTPHelperException("Error: Cannot download " + url + ". Server response code: " + responseCode);
+ e.setResponseCode(responseCode);
+ throw e;
+ }
+ } catch (IOException e) {
+ if (e instanceof HTTPHelperException) {
+ throw e;
+ } else {
+ throw new HTTPHelperException("Error: Cannot download " + url + ". " + e.getClass().getSimpleName() + ": " + e.getMessage());
+ }
+ } finally {
+ if (connection != null) {
+ connection.disconnect();
+ }
+ }
+
+ return destFile;
+ }
+
+ public static String copyFile(String url, String destFolder, String destFileName) throws Exception {
+ if (url.contains(" ")) {
+ url = url.replace(" ", "%20");
+ }
+
+ URI sourceURI = new URI(url);
+
+ String sourceFile = sourceURI.getPath();
+ File file = new File(sourceFile);
+ if (!file.exists()) {
+ throw new FileNotFoundException("Error: " + sourceFile + " does not exist.");
+ }
+
+ String destFile = destFolder + File.separator + destFileName;
+ file = new File(destFile);
+ if (file.exists()) {
+ file.delete();
+ }
+
+ Path sourcePath = Paths.get(sourceURI);
+ Path destPath = Paths.get(destFile);
+ Log.verbose("Copying " + url + " to " + destFile);
+ Files.copy(sourcePath, destPath);
+
+ return destFile;
+ }
+
+ public static boolean isHTTPUrl(String url) {
+ return (url.startsWith("http://") || url.startsWith("https://"));
+ }
+
+ public static byte[] getJNLPBits(String versionedJNLP, String jnlp) throws Exception {
+ String jnlpFilePath = null;
+ byte[] bits = null;
+
+ if (isHTTPUrl(jnlp)) {
+ try {
+ jnlpFilePath = downloadFile(versionedJNLP, JNLPConverter.getJnlpDownloadFolderStatic(), getFileNameFromURL(jnlp));
+ } catch (HTTPHelperException ex) {
+ if (ex.getResponseCode() == HttpURLConnection.HTTP_NOT_FOUND &&
+ !versionedJNLP.equals(jnlp)) {
+ Log.warning("Downloading versioned JNLP from " + versionedJNLP + " failed.");
+ Log.warning(ex.getMessage());
+ Log.warning("Downloading " + jnlp + " instead.");
+ jnlpFilePath = downloadFile(jnlp, JNLPConverter.getJnlpDownloadFolderStatic(), getFileNameFromURL(jnlp));
+ } else {
+ throw ex;
+ }
+ }
+ JNLPConverter.markFileToDelete(jnlpFilePath);
+ } else {
+ try {
+ jnlpFilePath = copyFile(versionedJNLP, JNLPConverter.getJnlpDownloadFolderStatic(), getFileNameFromURL(jnlp));
+ } catch (FileNotFoundException ex) {
+ System.out.println("Error copying versioned JNLP from " + versionedJNLP);
+ System.out.println(ex.getMessage());
+ System.out.println("Copying " + jnlp + " instead.");
+ jnlpFilePath = HTTPHelper.copyFile(jnlp, JNLPConverter.getJnlpDownloadFolderStatic(), getFileNameFromURL(jnlp));
+ }
+ JNLPConverter.markFileToDelete(jnlpFilePath);
+ }
+
+ File jnlpFile = new File(jnlpFilePath);
+ if (jnlpFile.exists()) {
+ bits = GeneralUtil.readBytes(new FileInputStream(jnlpFile), jnlpFile.length());
+ }
+
+ return bits;
+ }
+
+ public static String getFileNameFromURL(String url) throws IOException {
+ int index;
+ int index1 = url.lastIndexOf('/');
+ int index2 = url.lastIndexOf('\\');
+
+ if (index1 >= index2) {
+ index = index1;
+ } else {
+ index = index2;
+ }
+
+ if (index != -1) {
+ String name = url.substring(index + 1, url.length());
+ name = name.replace("%20", " ");
+ if (name.endsWith(".jnlp") || name.endsWith(".jar")) { // JNLP or JAR
+ return name;
+ } else if (name.endsWith(".ico")) { // Icons
+ return name;
+ } else {
+ throw new IOException("Error: Unsupported file extension for " + url);
+ }
+ } else {
+ throw new IOException("Error: URL (" + url + ") should end with file name.");
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/demo/share/jpackage/JNLPConverter/src/jnlp/converter/HTTPHelperException.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ *
+ * 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 jnlp.converter;
+
+import java.io.IOException;
+
+public class HTTPHelperException extends IOException {
+ private int responseCode = -1;
+
+ public HTTPHelperException(String msg) {
+ super(msg);
+ }
+
+ public void setResponseCode(int code) {
+ responseCode = code;
+ }
+
+ public int getResponseCode() {
+ return responseCode;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/demo/share/jpackage/JNLPConverter/src/jnlp/converter/JNLPConverter.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,841 @@
+/*
+ * 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.
+ *
+ * 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 jnlp.converter;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PrintWriter;
+import java.net.HttpURLConnection;
+import java.net.URI;
+import java.net.URL;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+import jnlp.converter.parser.JNLPDesc;
+import jnlp.converter.parser.JNLPDesc.AssociationDesc;
+import jnlp.converter.parser.JNLPDesc.IconDesc;
+import jnlp.converter.parser.ResourcesDesc.JARDesc;
+import jnlp.converter.parser.XMLFormat;
+
+public class JNLPConverter {
+
+ private final Options options;
+ private JNLPDesc jnlpd = null;
+ private final List<String> launchArgs = new ArrayList<>();
+
+ private String downloadFolder = null;
+ private String jnlpDownloadFolder = null;
+ private static String jnlpDownloadFolderStatic;
+ private String jarDownloadFolder = null;
+ private String iconDownloadFolder = null;
+ private String propDownloadFolder = null;
+
+ private static String jpackagePath = null;
+
+ private static boolean markFileToDelete = false;
+
+ private static final String FA_EXTENSIONS = "extension";
+ private static final String FA_CONTENT_TYPE = "mime-type";
+ private static final String FA_DESCRIPTION = "description";
+ private static final String FA_ICON = "icon";
+
+ public JNLPConverter(Options options) {
+ this.options = options;
+ jnlpDownloadFolderStatic = getJnlpDownloadFolder();
+ markFileToDelete = (options.keep() == null);
+ }
+
+ public String [] getLaunchArgs() {
+ return launchArgs.toArray(new String[0]);
+ }
+
+ public void convert() {
+ try {
+ loadJNLPDesc();
+ downloadResources();
+ validate();
+ buildLaunchArgs();
+ saveLaunchArgs();
+ runJPackage();
+ } catch (Exception ex) {
+ Log.error(ex.getMessage());
+ }
+ }
+
+ private JNLPDesc getJNLPD(String jnlp) throws Exception {
+ URL codebase = getCodeBase(jnlp);
+ byte[] bits = HTTPHelper.getJNLPBits(jnlp, jnlp);
+ return XMLFormat.parse(bits, codebase, jnlp);
+ }
+
+ private void loadJNLPDesc() throws Exception {
+ String jnlp = options.getJNLP();
+ jnlpd = getJNLPD(jnlp);
+
+ // Check for required options in case of FX
+ if (jnlpd.isFXApp()) {
+ if (!options.isRuntimeImageSet()) {
+ throw new Exception("This is a JavaFX Web-Start application which requires a runtime image capable of running JavaFX applications, which can be specified by the jpackage option --runtime-image (using --jpackage-options).");
+ }
+ }
+
+ // Check href. It can be same as URL we provided or new one
+ // if JNLP has different href or codebase. We assume that
+ // XMLFormat.parse() will handle any errors in href and codebase
+ // correctly.
+ String href = jnlpd.getHref();
+ if (href != null && !href.equalsIgnoreCase(jnlp)) {
+ if (href.startsWith("file:")) {
+ URI hrefURI = new URI(href);
+ URI jnlpURI = new URI(jnlp);
+
+ String hrefPath = hrefURI.getPath();
+ String jnlpPath = jnlpURI.getPath();
+
+ if (!hrefPath.equalsIgnoreCase(jnlpPath)) {
+ jnlp = href;
+ jnlpd = getJNLPD(jnlp);
+ }
+ } else {
+ jnlp = href;
+ jnlpd = getJNLPD(jnlp);
+ }
+ }
+
+ if (jnlpd.getName() == null) {
+ jnlpd.setName(getNameFromURL(jnlp));
+ }
+ }
+
+ private static String getNameFromURL(String url) throws IOException {
+ int index;
+ int index1 = url.lastIndexOf('/');
+ int index2 = url.lastIndexOf('\\');
+
+ if (index1 >= index2) {
+ index = index1;
+ } else {
+ index = index2;
+ }
+
+ if (index != -1) {
+ String name = url.substring(index + 1, url.length());
+ if (name.endsWith(".jnlp")) {
+ return name.substring(0, name.length() - 5);
+ }
+ }
+
+ return null;
+ }
+
+ private URL getCodeBase(String jnlp) throws Exception {
+ int index = jnlp.lastIndexOf('/');
+ if (index != -1) {
+ if (HTTPHelper.isHTTPUrl(jnlp)) {
+ return new URL(jnlp.substring(0, index + 1));
+ } else {
+ String codeBasePath = jnlp.substring(0, index);
+ if (!codeBasePath.endsWith("/")) {
+ codeBasePath += "/";
+ }
+ return new URI(codeBasePath).toURL();
+ }
+ }
+
+ return null;
+ }
+
+ public static void markFileToDelete(String file) {
+ if (file == null || file.isEmpty()) {
+ return;
+ }
+
+ if (markFileToDelete) {
+ try {
+ File f = new File(file);
+ f.deleteOnExit();
+ } catch (Exception e) {
+ // Print exception, but do not fail conversion.
+ Log.warning(e.getLocalizedMessage());
+ }
+ }
+ }
+
+ public static void deleteFile(String file) {
+ try {
+ File f = new File(file);
+ f.delete();
+ } catch (Exception e) {
+ Log.warning(e.getLocalizedMessage());
+ }
+ }
+
+ private void downloadResources() throws Exception {
+ List<JARDesc> jars = jnlpd.getResources();
+ for (JARDesc jar : jars) {
+ if (jar.getVersion() != null) {
+ if (!jnlpd.isVersionEnabled()) {
+ throw new Exception("Error: Version based download protocol is not supported without -Djnlp.versionEnabled=true.");
+ }
+ }
+
+ String destFile = null;
+ if (HTTPHelper.isHTTPUrl(jar.getLocation().toString())) {
+ if (jar.getVersion() != null) {
+ try {
+ destFile = HTTPHelper.downloadFile(jar.getVersionLocation().toString(), getJarDownloadFolder(), HTTPHelper.getFileNameFromURL(jar.getLocation().toString()));
+ } catch (HTTPHelperException ex) {
+ if (ex.getResponseCode() == HttpURLConnection.HTTP_NOT_FOUND) {
+ System.out.println("Error downloading versioned JAR from " + jar.getVersionLocation());
+ System.out.println(ex.getMessage());
+ System.out.println("Downloading " + jar.getLocation() + " instead.");
+ destFile = HTTPHelper.downloadFile(jar.getLocation().toString(), getJarDownloadFolder(), HTTPHelper.getFileNameFromURL(jar.getLocation().toString()));
+ } else {
+ throw ex;
+ }
+ }
+ } else {
+ destFile = HTTPHelper.downloadFile(jar.getLocation().toString(), getJarDownloadFolder(), HTTPHelper.getFileNameFromURL(jar.getLocation().toString()));
+ }
+ markFileToDelete(destFile);
+ } else {
+ if (jar.getVersion() != null) {
+ try {
+ destFile = HTTPHelper.copyFile(jar.getVersionLocation().toString(), getJarDownloadFolder(), HTTPHelper.getFileNameFromURL(jar.getLocation().toString()));
+ } catch (FileNotFoundException ex) {
+ System.out.println("Error copying versioned JAR from " + jar.getVersionLocation());
+ System.out.println(ex.getMessage());
+ System.out.println("Copying " + jar.getLocation() + " instead.");
+ destFile = HTTPHelper.copyFile(jar.getLocation().toString(), getJarDownloadFolder(), HTTPHelper.getFileNameFromURL(jar.getLocation().toString()));
+ }
+ } else {
+ destFile = HTTPHelper.copyFile(jar.getLocation().toString(), getJarDownloadFolder(), HTTPHelper.getFileNameFromURL(jar.getLocation().toString()));
+ }
+ markFileToDelete(destFile);
+ }
+
+ if (jar.isNativeLib()) {
+ unpackNativeLib(destFile);
+ deleteFile(destFile);
+ } else {
+ jnlpd.addFile(jar.getName());
+ }
+ }
+
+ IconDesc icon = jnlpd.getIcon();
+ if (icon != null) {
+ String destFile;
+
+ if (HTTPHelper.isHTTPUrl(icon.getLocation())) {
+ destFile = HTTPHelper.downloadFile(icon.getLocation(), getIconDownloadFolder(), HTTPHelper.getFileNameFromURL(icon.getLocation()));
+ } else {
+ destFile = HTTPHelper.copyFile(icon.getLocation(), getIconDownloadFolder(), HTTPHelper.getFileNameFromURL(icon.getLocation()));
+ }
+
+ markFileToDelete(destFile);
+ icon.setLocalLocation(destFile);
+ }
+
+ AssociationDesc [] associations = jnlpd.getAssociations();
+ if (associations != null) {
+ for (AssociationDesc association : associations) {
+ if (association.getIconUrl() != null) {
+ String destFile;
+ if (HTTPHelper.isHTTPUrl(association.getIconUrl())) {
+ destFile = HTTPHelper.downloadFile(association.getIconUrl(), getIconDownloadFolder(), HTTPHelper.getFileNameFromURL(association.getIconUrl()));
+ } else {
+ destFile = HTTPHelper.copyFile(association.getIconUrl(), getIconDownloadFolder(), HTTPHelper.getFileNameFromURL(association.getIconUrl()));
+ }
+
+ markFileToDelete(destFile);
+ association.setIconLocalLocation(destFile);
+ }
+ }
+ }
+ }
+
+ public void unpackNativeLib(String file) throws IOException {
+ try (JarFile jarFile = new JarFile(file)) {
+ Enumeration entries = jarFile.entries();
+
+ while (entries.hasMoreElements()) {
+ JarEntry entry = (JarEntry) entries.nextElement();
+
+ // Skip directories
+ if (entry.isDirectory()) {
+ continue;
+ }
+
+ String entryName = entry.getName();
+ // Skip anything in sub-directories
+ if (entryName.contains("\\") || entryName.contains("/")) {
+ continue;
+ }
+
+ // Skip anything not ending with .dll, .dylib or .so
+ if (!entryName.endsWith(".dll") && !entryName.endsWith(".dylib") && !entryName.endsWith(".so")) {
+ continue;
+ }
+
+ File destFile = new File(getJarDownloadFolder(), entryName);
+ if (destFile.exists()) {
+ Log.warning(destFile.getAbsolutePath() + " already exist and will not be overwriten by native library from " + file + ".");
+ continue;
+ }
+
+ InputStream inputStream = jarFile.getInputStream(entry);
+ FileOutputStream outputStream = new FileOutputStream(destFile);
+
+ byte[] buffer = new byte[HTTPHelper.BUFFER_SIZE];
+ int length;
+ do {
+ length = inputStream.read(buffer);
+ if (length > 0) {
+ outputStream.write(buffer, 0, length);
+ }
+ } while (length > 0);
+
+ jnlpd.addFile(entryName);
+ }
+ }
+ }
+
+ private void validate() {
+ if (jnlpd.getMainJar() == null) {
+ Log.error("Cannot find main jar");
+ }
+
+ if (jnlpd.getMainClass() == null) {
+ Log.error("Cannot find main class");
+ }
+ }
+
+ private void addLaunchArg(String arg, List<String> launchArgs) {
+ if (arg != null && !arg.isEmpty()) {
+ if (!options.isOptionPresent(arg)){
+ launchArgs.add(arg);
+ } else {
+ Log.info(arg + " generated by JNLPConverter is dropped, since it is overwriten via --jpackage-options");
+ }
+ }
+ }
+
+ private void addLaunchArg(String arg, String value, List<String> launchArgs) {
+ if (arg != null && !arg.isEmpty() && value != null && !value.isEmpty()) {
+ if (!options.isOptionPresent(arg)){
+ launchArgs.add(arg);
+ launchArgs.add(value);
+ } else {
+ Log.info(arg + "=" + value +" generated by JNLPConverter is dropped, since it is overwriten via --jpackage-options");
+ }
+ }
+ }
+
+ private void displayLaunchArgs() {
+ if (Log.isVerbose()) {
+ System.out.println();
+ System.out.println("jpackage launch arguments (each argument starts on new line):");
+ launchArgs.forEach((arg) -> {
+ System.out.println(arg);
+ });
+ }
+ }
+
+ private static int fileAssociationsCount = 0;
+ private String getFileAssociationsFile() {
+ String file = getPropDownloadFolder();
+ file += File.separator;
+ file += "fileAssociation";
+ file += String.valueOf(fileAssociationsCount);
+ file += ".properties";
+
+ fileAssociationsCount++;
+
+ return file;
+ }
+
+ private void buildLaunchArgs() {
+ if (options.createAppImage()) {
+ addLaunchArg("create-app-image", launchArgs);
+ } else if (options.createInstaller()) {
+ if (options.getInstallerType() == null) {
+ addLaunchArg("create-installer", launchArgs);
+ } else {
+ addLaunchArg("create-installer", launchArgs);
+ if (options.getInstallerType() != null) {
+ addLaunchArg("--installer-type", options.getInstallerType(), launchArgs);
+ }
+ }
+ }
+
+ // Set verbose for jpackage if it is set for us.
+ if (options.verbose()) {
+ addLaunchArg("--verbose", launchArgs);
+ }
+
+ addLaunchArg("--input", getJarDownloadFolder(), launchArgs);
+ addLaunchArg("--output", options.getOutput(), launchArgs);
+ addLaunchArg("--name", jnlpd.getName(), launchArgs);
+ addLaunchArg("--app-version", jnlpd.getVersion(), launchArgs);
+ addLaunchArg("--vendor", jnlpd.getVendor(), launchArgs);
+ addLaunchArg("--description", jnlpd.getDescription(), launchArgs);
+ addLaunchArg("--icon", jnlpd.getIconLocation(), launchArgs);
+ addLaunchArg("--main-jar", jnlpd.getMainJar(), launchArgs);
+ addLaunchArg("--main-class", jnlpd.getMainClass(), launchArgs);
+
+ addArguments(launchArgs);
+ addJVMArgs(launchArgs);
+
+ if (options.createInstaller()) {
+ if (jnlpd.isDesktopHint()) {
+ if (Platform.isWindows()) {
+ addLaunchArg("--win-shortcut", launchArgs);
+ } else {
+ Log.warning("Ignoring shortcut hint, since it is not supported on current platform.");
+ }
+ }
+
+ if (jnlpd.isMenuHint()) {
+ if (Platform.isWindows()) {
+ addLaunchArg("--win-menu", launchArgs);
+ addLaunchArg("--win-menu-group", jnlpd.getSubMenu(), launchArgs);
+ } else {
+ Log.warning("Ignoring menu hint, since it is not supported on current platform.");
+ }
+ }
+
+ AssociationDesc[] associations = jnlpd.getAssociations();
+ if (associations != null) {
+ for (AssociationDesc association : associations) {
+ String file = getFileAssociationsFile();
+ markFileToDelete(file);
+
+ try (PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter(file)))) {
+ if (association.getExtensions() != null && association.getMimeType() != null) {
+ out.println(FA_EXTENSIONS + "=" + quote(association.getExtensions()));
+ out.println(FA_CONTENT_TYPE + "=" + quote(association.getMimeType()));
+
+ if (association.getMimeDescription() != null) {
+ out.println(FA_DESCRIPTION + "=" + association.getMimeDescription());
+ }
+
+ if (association.getIconLocalLocation() != null) {
+ out.println(FA_ICON + "=" + quote(association.getIconLocalLocation()));
+ }
+
+ addLaunchArg("--file-associations", file, launchArgs);
+ }
+ } catch (Exception ex) {
+ Log.warning(ex.toString());
+ if (association.getExtensions() != null) {
+ Log.warning("File assoication for " + association.getExtensions() + " will be ignored due to exception above.");
+ }
+ }
+ }
+ }
+ }
+
+ // Add options from --jpackage-options
+ List<String> jpackageOptions = options.getJPackageOptions();
+ jpackageOptions.forEach((option) -> {
+ launchArgs.add(option);
+ });
+
+ displayLaunchArgs();
+ }
+
+ private String getCommandFileName() {
+ Platform platform = Platform.getPlatform();
+ switch (platform) {
+ case WINDOWS:
+ return "run_jpackage.bat";
+ case LINUX:
+ return "run_jpackage.sh";
+ case MAC:
+ return "run_jpackage.sh";
+ default:
+ Log.error("Cannot determine platform type.");
+ return "";
+ }
+ }
+
+ private void saveLaunchArgs() {
+ if (options.keep() != null) {
+ File keepFolder = new File(options.keep());
+ String cmdFile = keepFolder.getAbsolutePath() + File.separator + getCommandFileName();
+ try (PrintWriter out = new PrintWriter(cmdFile)) {
+ out.print(getJPackagePath());
+ launchArgs.forEach((arg) -> {
+ out.print(" ");
+
+ if (arg.contains(" ")) {
+ int len = arg.length();
+ if (len >= 1) {
+ if (arg.charAt(0) != '"' && arg.charAt(len - 1) != '"') {
+ out.print("\"" + arg + "\"");
+ } else {
+ if (Platform.isWindows()) {
+ out.print(arg);
+ } else {
+ arg = escapeQuote(arg);
+ out.print("\"" + arg + "\"");
+ }
+ }
+ }
+ } else {
+ out.print(arg);
+ }
+ });
+ } catch (FileNotFoundException ex) {
+ Log.error("Cannot save file with command line: " + ex.getLocalizedMessage());
+ }
+ }
+ }
+
+ private void runJPackage() {
+ List<String> command = new ArrayList<>();
+ command.add(getJPackagePath());
+ command.addAll(launchArgs);
+
+ ProcessBuilder builder = new ProcessBuilder();
+ builder.inheritIO();
+ builder.command(command);
+
+ try {
+ Process process = builder.start();
+ int exitCode = process.waitFor();
+ if (exitCode != 0) {
+ Log.warning("jpackage retrun non zero code: " + exitCode);
+ }
+ } catch (IOException | InterruptedException ex) {
+ Log.error(ex.getMessage());
+ }
+ }
+
+ private void addArguments(List<String> launchArgs) {
+ List<String> arguments = jnlpd.getArguments();
+ if (arguments.isEmpty()) {
+ return;
+ }
+
+ String argsStr = "";
+ for (int i = 0; i < arguments.size(); i++) {
+ String arg = arguments.get(i);
+ argsStr += quote(arg);
+ if ((i + 1) != arguments.size()) {
+ argsStr += " ";
+ }
+ }
+
+ launchArgs.add("--arguments");
+ if (Platform.isWindows()) {
+ if (argsStr.contains(" ")) {
+ if (argsStr.contains("\"")) {
+ argsStr = escapeQuote(argsStr);
+ }
+ argsStr = "\"" + argsStr + "\"";
+ }
+ }
+ launchArgs.add(argsStr);
+ }
+
+ private void addJVMArgs(List<String> launchArgs) {
+ List<String> jvmArgs = jnlpd.getVMArgs();
+ if (jvmArgs.isEmpty()) {
+ return;
+ }
+
+ String jvmArgsStr = "";
+ for (int i = 0; i < jvmArgs.size(); i++) {
+ String arg = jvmArgs.get(i);
+ jvmArgsStr += quote(arg);
+ if ((i + 1) != jvmArgs.size()) {
+ jvmArgsStr += " ";
+ }
+ }
+
+ launchArgs.add("--java-options");
+ if (Platform.isWindows()) {
+ if (jvmArgsStr.contains(" ")) {
+ if (jvmArgsStr.contains("\"")) {
+ jvmArgsStr = escapeQuote(jvmArgsStr);
+ }
+ jvmArgsStr = "\"" + jvmArgsStr + "\"";
+ }
+ }
+ launchArgs.add(jvmArgsStr);
+ }
+
+ private String quote(String in) {
+ if (in == null) {
+ return null;
+ }
+
+ if (in.isEmpty()) {
+ return "";
+ }
+
+ if (!in.contains("=")) {
+ // Not a property
+ if (in.contains(" ")) {
+ in = escapeQuote(in);
+ return "\"" + in + "\"";
+ }
+ return in;
+ }
+
+ if (!in.contains(" ")) {
+ return in; // No need to quote
+ }
+
+ int paramIndex = in.indexOf("=");
+ if (paramIndex <= 0) {
+ return in; // Something wrong, just skip quoting
+ }
+
+ String param = in.substring(0, paramIndex);
+ String value = in.substring(paramIndex + 1);
+
+ if (value.length() == 0) {
+ return in; // No need to quote
+ }
+
+ value = escapeQuote(value);
+
+ return param + "=" + "\"" + value + "\"";
+ }
+
+ private String escapeQuote(String in) {
+ if (in == null) {
+ return null;
+ }
+
+ if (in.isEmpty()) {
+ return "";
+ }
+
+ if (in.contains("\"")) {
+ // Use code points to preserve non-ASCII chars
+ StringBuilder sb = new StringBuilder();
+ int codeLen = in.codePointCount(0, in.length());
+ for (int i = 0; i < codeLen; i++) {
+ int code = in.codePointAt(i);
+ // Note: No need to escape '\' on Linux or OS X.
+ // jpackage expects us to pass arguments and properties with quotes and spaces as a map
+ // with quotes being escaped with additional \ for internal quotes.
+ // So if we want two properties below:
+ // -Djnlp.Prop1=Some "Value" 1
+ // -Djnlp.Prop2=Some Value 2
+ // jpackage will need:
+ // "-Djnlp.Prop1=\"Some \\"Value\\" 1\" -Djnlp.Prop2=\"Some Value 2\""
+ // but since we using ProcessBuilder to run jpackage we will need to escape
+ // our escape symbols as well, so we will need to pass string below to ProcessBuilder:
+ // "-Djnlp.Prop1=\\\"Some \\\\\\\"Value\\\\\\\" 1\\\" -Djnlp.Prop2=\\\"Some Value 2\\\""
+ switch (code) {
+ case '"':
+ // " -> \" -> \\\"
+ if (i == 0 || in.codePointAt(i - 1) != '\\') {
+ sb.appendCodePoint('\\');
+ sb.appendCodePoint(code);
+ }
+ break;
+ case '\\':
+ // We need to escape already escaped symbols as well
+ if ((i + 1) < codeLen) {
+ int nextCode = in.codePointAt(i + 1);
+ if (nextCode == '"') {
+ // \" -> \\\"
+ sb.appendCodePoint('\\');
+ sb.appendCodePoint('\\');
+ sb.appendCodePoint('\\');
+ sb.appendCodePoint(nextCode);
+ } else {
+ sb.appendCodePoint('\\');
+ sb.appendCodePoint(code);
+ }
+ } else {
+ sb.appendCodePoint(code);
+ }
+ break;
+ default:
+ sb.appendCodePoint(code);
+ break;
+ }
+ }
+ return sb.toString();
+ }
+
+ return in;
+ }
+
+ public synchronized String getDownloadFolder() {
+ if (downloadFolder == null) {
+ try {
+ File file;
+ if (options.keep() == null) {
+ Path path = Files.createTempDirectory("JNLPConverter");
+ file = path.toFile();
+ file.deleteOnExit();
+ } else {
+ file = new File(options.keep());
+ if (!file.exists()) {
+ file.mkdir();
+ }
+ }
+
+ downloadFolder = file.getAbsolutePath();
+ } catch (IOException e) {
+ Log.error(e.getMessage());
+ }
+ }
+
+ return downloadFolder;
+ }
+
+ public final synchronized String getJnlpDownloadFolder() {
+ if (jnlpDownloadFolder == null) {
+ File file = new File(getDownloadFolder() + File.separator + "jnlp");
+ file.mkdir();
+ markFileToDelete(getDownloadFolder() + File.separator + "jnlp");
+ jnlpDownloadFolder = file.getAbsolutePath();
+ }
+
+ return jnlpDownloadFolder;
+ }
+
+ public static String getJnlpDownloadFolderStatic() {
+ return jnlpDownloadFolderStatic;
+ }
+
+ public synchronized String getJarDownloadFolder() {
+ if (jarDownloadFolder == null) {
+ File file = new File(getDownloadFolder() + File.separator + "jar");
+ file.mkdir();
+ markFileToDelete(getDownloadFolder() + File.separator + "jar");
+ jarDownloadFolder = file.getAbsolutePath();
+ }
+
+ return jarDownloadFolder;
+ }
+
+ public synchronized String getIconDownloadFolder() {
+ if (iconDownloadFolder == null) {
+ File file = new File(getDownloadFolder() + File.separator + "icon");
+ file.mkdir();
+ markFileToDelete(getDownloadFolder() + File.separator + "icon");
+ iconDownloadFolder = file.getAbsolutePath();
+ }
+
+ return iconDownloadFolder;
+ }
+
+ public synchronized String getPropDownloadFolder() {
+ if (propDownloadFolder == null) {
+ File file = new File(getDownloadFolder() + File.separator + "prop");
+ file.mkdir();
+ markFileToDelete(getDownloadFolder() + File.separator + "prop");
+ propDownloadFolder = file.getAbsolutePath();
+ }
+
+ return propDownloadFolder;
+ }
+
+ public synchronized static String getJPackagePath() {
+ if (jpackagePath == null) {
+ jpackagePath = System.getProperty("java.home");
+ jpackagePath += File.separator;
+ jpackagePath += "bin";
+ jpackagePath += File.separator;
+
+ Platform platform = Platform.getPlatform();
+ switch (platform) {
+ case WINDOWS:
+ jpackagePath += "jpackage.exe";
+ break;
+ case LINUX:
+ jpackagePath += "jpackage";
+ break;
+ case MAC:
+ jpackagePath += "jpackage";
+ break;
+ default:
+ Log.error("Cannot determine platform type.");
+ break;
+ }
+
+ Log.verbose("jpackage: " + jpackagePath);
+ }
+
+ return jpackagePath;
+ }
+
+ public static String getIconFormat(String icon) {
+ // GIF, JPEG, ICO, or PNG
+ if (icon.toLowerCase().endsWith(".gif")) {
+ return "GIF";
+ } else if (icon.toLowerCase().endsWith(".jpg")) {
+ return "JPEG";
+ } else if (icon.toLowerCase().endsWith(".ico")) {
+ return "ICO";
+ } else if (icon.toLowerCase().endsWith(".png")) {
+ return "PNG";
+ }
+
+ return "UNKNOWN";
+ }
+
+ public static boolean isIconSupported(String icon) {
+ Platform platform = Platform.getPlatform();
+ switch (platform) {
+ case WINDOWS:
+ if (icon.endsWith(".ico")) {
+ return true;
+ } else {
+ Log.warning("Icon file format (" + getIconFormat(icon) + ") is not supported on Windows for file " + icon + ".");
+ return false;
+ }
+ case LINUX:
+ if (icon.endsWith(".png")) {
+ return true;
+ } else {
+ Log.warning("Icon file format (" + getIconFormat(icon) + ") is not supported on Linux for file " + icon + ".");
+ return false;
+ }
+ case MAC:
+ Log.warning("Icon file format (" + getIconFormat(icon) + ") is not supported on OS X for file " + icon + ".");
+ return false;
+ }
+
+ return false;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/demo/share/jpackage/JNLPConverter/src/jnlp/converter/Log.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved.
+ * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
+ */
+package jnlp.converter;
+
+public class Log {
+ private static boolean verbose = false;
+
+ public static void setVerbose(boolean verbose) {
+ Log.verbose = verbose;
+ }
+
+ public static boolean isVerbose() {
+ return verbose;
+ }
+
+ public static void verbose(String msg) {
+ if (verbose) {
+ System.out.println(msg);
+ }
+ }
+
+ public static void info(String msg) {
+ System.out.println("Info: " + msg);
+ }
+
+ public static void warning(String msg) {
+ System.err.println("Warning: " + msg);
+ }
+
+ public static void error(String msg) {
+ System.err.println("Error: " + msg);
+ System.exit(1);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/demo/share/jpackage/JNLPConverter/src/jnlp/converter/Main.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,71 @@
+/*
+ * 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.
+ *
+ * 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 jnlp.converter;
+
+import java.io.File;
+
+public class Main {
+
+ private static void showHelp() {
+ Options.showHelp();
+ }
+
+ private static void showVersion() {
+ System.out.println("Version: 1.0");
+ }
+
+ private static void createBundle(Options options) {
+ Log.verbose("Creating bundle for JNLP: " + options.getJNLP());
+ Log.verbose("Output folder: " + options.getOutput());
+
+ JNLPConverter converter = new JNLPConverter(options);
+ converter.convert();
+ }
+
+ private static void validateJDK() {
+ String jpackagePath = JNLPConverter.getJPackagePath();
+ File file = new File(jpackagePath);
+ if (!file.exists()) {
+ Log.error("Cannot find " + jpackagePath + ". Make sure you running JNLPConverter with supported JDK version.");
+ }
+ }
+
+ public static void main(String[] args) {
+ Options options = Options.parseArgs(args); // Only valid options will be returned
+
+ Log.setVerbose(options.verbose());
+
+ validateJDK();
+
+ if (options.help()) {
+ showHelp();
+ } else if (options.version()) {
+ showVersion();
+ } else {
+ createBundle(options);
+ }
+
+ System.exit(0);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/demo/share/jpackage/JNLPConverter/src/jnlp/converter/Options.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,373 @@
+/*
+ * 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.
+ *
+ * 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 jnlp.converter;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+public class Options {
+
+ private boolean createAppImage = false;
+ private boolean createInstaller = false;
+ private String installerType = null;
+ private String jnlp = null;
+ private String output = null;
+ private String keep = null;
+ private boolean help = false;
+ private boolean verbose = false;
+ private boolean version = false;
+ private final List<String> jpackageOptions = new ArrayList<>();
+ private boolean isRuntimeImageSet = false;
+
+ private static final String JNLP_OPTION_PREFIX = "--jnlp=";
+ private static final String OUTPUT_OPTION_PREFIX = "--output=";
+ private static final String KEEP_OPTION_PREFIX = "--keep=";
+ private static final String JNLP_OPTION_SHORT_PREFIX = "-j";
+ private static final String OUTPUT_OPTION_SHORT_PREFIX = "-o";
+ private static final String KEEP_OPTION_SHORT_PREFIX = "-k";
+
+ private static final String [] INSTALLER_TYPES = {"msi", "exe", "dmg", "pkg",
+ "rpm", "deb"};
+
+ // --output, -o, --input, -i, --main-jar, --main-class
+ private static final String [] BLOCKED_JPACKAGE_OPTIONS = {"--output", "-o", "--input", "-i",
+ "--main-jar", "--main-class"};
+
+ private static final String RUNTIME_IMAGE_OPTION = "--runtime-image";
+
+ private static final String ERR_UNKNOWN_OPTION = "Unknown option: ";
+ private static final String ERR_MISSING_VALUE = "Value is required for option ";
+ private static final String ERR_MISSING_MODE = "Error: create-app-image or create-installer mode is required";
+ private static final String ERR_MISSING_JNLP = "Error: --jnlp is required";
+ private static final String ERR_MISSING_OUTPUT = "Error: --output is required";
+ private static final String ERR_OUTPUT_EXISTS = "Error: output folder already exists";
+ private static final String ERR_KEEP_EXISTS = "Error: folder for --keep argument already exists";
+ private static final String ERR_INVALID_PROTOCOL_JNLP = "Error: Invalid protocol for JNLP file. Only HTTP, HTTPS and FILE protocols are supported.";
+
+ public boolean createAppImage() {
+ return createAppImage;
+ }
+
+ public boolean createInstaller() {
+ return createInstaller;
+ }
+
+ public String getInstallerType() {
+ return installerType;
+ }
+
+ public String getJNLP() {
+ return jnlp;
+ }
+
+ public String getOutput() {
+ return output;
+ }
+
+ public String keep() {
+ return keep;
+ }
+
+ public boolean help() {
+ return help;
+ }
+
+ public boolean verbose() {
+ return verbose;
+ }
+
+ public boolean version() {
+ return version;
+ }
+
+ public List<String> getJPackageOptions() {
+ return jpackageOptions;
+ }
+
+ public boolean isRuntimeImageSet() {
+ return isRuntimeImageSet;
+ }
+
+ // Helper method to dump all options
+ private void display() {
+ System.out.println("Options:");
+ System.out.println("createAppImage: " + createAppImage);
+ System.out.println("createInstaller: " + createInstaller);
+ System.out.println("installerType: " + installerType);
+ System.out.println("jnlp: " + jnlp);
+ System.out.println("output: " + output);
+ System.out.println("keep: " + keep);
+ System.out.println("help: " + help);
+ System.out.println("verbose: " + verbose);
+ System.out.println("version: " + version);
+ for (int i = 0; i < jpackageOptions.size(); i++) {
+ System.out.println("jpackageOptions[" + i + "]: " + jpackageOptions.get(i));
+ }
+ }
+
+ private void validate() {
+ if (help || version) {
+ return;
+ }
+
+ if (!createAppImage && !createInstaller) {
+ optionError(ERR_MISSING_MODE);
+ }
+
+ if (jnlp == null) {
+ optionError(ERR_MISSING_JNLP);
+ } else {
+ int index = jnlp.indexOf(":");
+ if (index == -1 || index == 0) {
+ optionError(ERR_INVALID_PROTOCOL_JNLP);
+ } else {
+ String protocol = jnlp.substring(0, index);
+ if (!protocol.equalsIgnoreCase("http") &&
+ !protocol.equalsIgnoreCase("https") &&
+ !protocol.equalsIgnoreCase("file")) {
+ optionError(ERR_INVALID_PROTOCOL_JNLP);
+ }
+ }
+ }
+
+ if (output == null) {
+ optionError(ERR_MISSING_OUTPUT);
+ } else {
+ File file = new File(output);
+ if (file.exists()) {
+ optionErrorNoHelp(ERR_OUTPUT_EXISTS);
+ }
+ }
+
+ if (keep != null) {
+ File file = new File(keep);
+ if (file.exists()) {
+ optionErrorNoHelp(ERR_KEEP_EXISTS);
+ }
+ }
+
+ jpackageOptions.forEach((option) -> {
+ if (isBlockedOption(option)) {
+ Log.error(option + " is not allowed via --jpackage-options, since it will conflict with "
+ + "same option generated by JNLPConverter.");
+ }
+ });
+ }
+
+ public boolean isOptionPresent(String option) {
+ for (String jpackageOption : jpackageOptions) {
+ if (jpackageOption.equalsIgnoreCase(option)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private boolean isBlockedOption(String option) {
+ for (String blockedOption : BLOCKED_JPACKAGE_OPTIONS) {
+ if (blockedOption.equalsIgnoreCase(option)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ public static void showHelp() {
+// System.out.println("********* Help should not be longer then 80 characters as per JEP-293 *********");
+ System.out.println("Usage: java -jar JNLPConverter.jar <mode> <options>");
+ System.out.println("");
+ System.out.println("where mode is one of:");
+ System.out.println(" create-app-image");
+ System.out.println(" Generates a platform-specific application image.");
+ System.out.println(" create-installer");
+ System.out.println(" Generates a platform-specific installer for the application.");
+ System.out.println("");
+ System.out.println("Possible options include:");
+ System.out.println(" -j, --jnlp <path>");
+ System.out.println(" Full path to JNLP file. Supported protocols are HTTP/HTTPS/FILE.");
+ System.out.println(" -o, --output <path>");
+ System.out.println(" Name of the directory where generated output files are placed.");
+ System.out.println(" -k, --keep <path>");
+ System.out.println(" Keep JNLP, JARs and command line arguments for jpackage");
+ System.out.println(" in directory provided.");
+ System.out.println(" --jpackage-options <options>");
+ System.out.println(" Specify additional jpackage options or overwrite provided by JNLPConverter.");
+ System.out.println(" All jpackage options can be specified except: --output -o, --input -i,");
+ System.out.println(" --main-jar -j and --class -c.");
+ System.out.println(" --installer-type <type>");
+ System.out.println(" The type of the installer to create");
+ System.out.println(" Valid values are: {\"exe\", \"msi\", \"rpm\", \"deb\", \"pkg\", \"dmg\"}");
+ System.out.println(" If this option is not specified (in create-installer mode) all");
+ System.out.println(" supported types of installable packages for the current");
+ System.out.println(" platform will be created.");
+ System.out.println(" -h, --help, -?");
+ System.out.println(" Print this help message");
+ System.out.println(" -v, --verbose");
+ System.out.println(" Enable verbose output.");
+ System.out.println(" --version");
+ System.out.println(" Version information.");
+ System.out.println("To specify an argument for a long option, you can use --<name>=<value> or");
+ System.out.println("--<name> <value>.");
+ System.out.println("To specify proxy server use standard Java properties http.proxyHost and http.proxyPort.");
+ }
+
+ private static boolean isInstallerType(String type) {
+ for (String installerType : INSTALLER_TYPES) {
+ if (installerType.equals(type)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ public static Options parseArgs(String[] args) {
+ Options options = new Options();
+
+ int index = 0;
+ if (args.length >= 1) {
+ switch (args[0]) {
+ case "create-app-image":
+ options.createAppImage = true;
+ index = 1;
+ break;
+ case "create-installer":
+ options.createInstaller = true;
+ index = 1;
+ break;
+ case "-h":
+ case "--help":
+ case "-?":
+ case "--version":
+ break;
+ default:
+ optionError(Options.ERR_MISSING_MODE);
+ break;
+ }
+ }
+
+ for (int i = index; i < args.length; i++) {
+ String arg = args[i];
+
+ if (arg.equals("--jnlp")) {
+ if (++i >= args.length) {
+ optionError(Options.ERR_MISSING_VALUE, "--jnlp");
+ }
+ options.jnlp = args[i];
+ } else if (arg.startsWith(JNLP_OPTION_PREFIX)) {
+ options.jnlp = arg.substring(JNLP_OPTION_PREFIX.length());
+ } else if (arg.equals("--output")) {
+ if (++i >= args.length) {
+ optionError(Options.ERR_MISSING_VALUE, "--output");
+ }
+ options.output = args[i];
+ } else if (arg.startsWith(OUTPUT_OPTION_PREFIX)) {
+ options.output = arg.substring(OUTPUT_OPTION_PREFIX.length());
+ } else if (arg.equals("--keep")) {
+ if (++i >= args.length) {
+ optionError(Options.ERR_MISSING_VALUE, "--keep");
+ }
+ options.keep = args[i];
+ } else if (arg.startsWith(KEEP_OPTION_PREFIX)) {
+ options.keep = arg.substring(KEEP_OPTION_PREFIX.length());
+ } else if (arg.equals("--help")) {
+ options.help = true;
+ } else if (arg.equals("--verbose")) {
+ options.verbose = true;
+ } else if (arg.equals("--version")) {
+ options.version = true;
+ } else if (arg.equals("-j")) { // short options
+ if (++i >= args.length) {
+ optionError(Options.ERR_MISSING_VALUE, "-j");
+ }
+ options.jnlp = args[i];
+ } else if (arg.startsWith(JNLP_OPTION_SHORT_PREFIX)) {
+ options.jnlp = arg.substring(JNLP_OPTION_SHORT_PREFIX.length());
+ } else if (arg.equals("-o")) {
+ if (++i >= args.length) {
+ optionError(Options.ERR_MISSING_VALUE, "-o");
+ }
+ options.output = args[i];
+ } else if (arg.startsWith(OUTPUT_OPTION_SHORT_PREFIX)) {
+ options.output = arg.substring(OUTPUT_OPTION_SHORT_PREFIX.length());
+ } else if (arg.equals("-k")) {
+ if (++i >= args.length) {
+ optionError(Options.ERR_MISSING_VALUE, "-k");
+ }
+ options.keep = args[i];
+ } else if (arg.startsWith(KEEP_OPTION_SHORT_PREFIX)) {
+ options.keep = arg.substring(KEEP_OPTION_SHORT_PREFIX.length());
+ } else if (arg.equals("-h") || arg.equals("-?")) {
+ options.help = true;
+ } else if (arg.equals("-v")) {
+ options.verbose = true;
+ } else if (arg.equals("--jpackage-options")) {
+ for (i = (i + 1); i < args.length; i++) {
+ if (!options.isRuntimeImageSet) {
+ if (args[i].equals(RUNTIME_IMAGE_OPTION)) {
+ options.isRuntimeImageSet = true;
+ }
+ }
+ options.jpackageOptions.add(args[i]);
+ }
+ } else if (arg.equals("--installer-type")) {
+ if ((i + 1) < args.length) {
+ if (isInstallerType(args[i + 1])) {
+ options.installerType = args[i + 1];
+ i++;
+ }
+ }
+ } else {
+ optionError(ERR_UNKNOWN_OPTION, arg);
+ }
+ }
+
+ //options.display(); // For testing only
+ options.validate();
+
+ return options;
+ }
+
+ private static void optionErrorNoHelp(String msg) {
+ System.out.println(msg);
+ System.exit(1);
+ }
+
+ private static void optionError(String msg) {
+ System.out.println(msg);
+ System.out.println();
+ showHelp();
+ System.exit(1);
+ }
+
+ private static void optionError(String msg, String option) {
+ System.out.println(msg + option);
+ System.out.println();
+ showHelp();
+ System.exit(1);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/demo/share/jpackage/JNLPConverter/src/jnlp/converter/Platform.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,82 @@
+/*
+ * 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.
+ *
+ * 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 jnlp.converter;
+
+import java.util.regex.Pattern;
+
+public enum Platform {
+ UNKNOWN, WINDOWS, LINUX, MAC;
+ private static final Platform platform;
+ private static final int majorVersion;
+ private static final int minorVersion;
+
+ static {
+ String os = System.getProperty("os.name").toLowerCase();
+
+ if (os.contains("win")) {
+ platform = Platform.WINDOWS;
+ } else if (os.contains("nix") || os.contains("nux")) {
+ platform = Platform.LINUX;
+ } else if (os.contains("mac")) {
+ platform = Platform.MAC;
+ } else {
+ platform = Platform.UNKNOWN;
+ }
+
+ String version = System.getProperty("os.version");
+ String[] parts = version.split(Pattern.quote("."));
+
+ if (parts.length > 0) {
+ majorVersion = Integer.parseInt(parts[0]);
+
+ if (parts.length > 1) {
+ minorVersion = Integer.parseInt(parts[0]);
+ } else {
+ minorVersion = -1;
+ }
+ } else {
+ majorVersion = -1;
+ minorVersion = -1;
+ }
+ }
+
+ private Platform() {
+ }
+
+ public static Platform getPlatform() {
+ return platform;
+ }
+
+ public static boolean isWindows() {
+ return (platform == Platform.WINDOWS);
+ }
+
+ public static int getMajorVersion() {
+ return majorVersion;
+ }
+
+ public static int getMinorVersion() {
+ return minorVersion;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/demo/share/jpackage/JNLPConverter/src/jnlp/converter/parser/GeneralUtil.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,415 @@
+/*
+ * Copyright (c) 2006, 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.
+ *
+ * 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 jnlp.converter.parser;
+
+import java.util.Locale;
+import java.util.ArrayList;
+import java.util.StringTokenizer;
+import java.io.BufferedInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+
+/**
+ * Handy class to add some utility methods for dealing with property matching
+ * etc.
+ */
+public class GeneralUtil {
+
+ public static boolean prefixMatchStringList(String[] prefixList, String target) {
+ // No prefixes matches everything
+ if (prefixList == null) {
+ return true;
+ }
+ // No target, but a prefix list does not match anything
+ if (target == null) {
+ return false;
+ }
+ for (String prefix : prefixList) {
+ if (target.startsWith(prefix)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private static String getOSArch() {
+ return System.getProperty("os.arch");
+ }
+
+ public static boolean prefixMatchArch(String[] prefixList) {
+ // No prefixes matches everything
+ if (prefixList == null) {
+ return true;
+ }
+
+ // check for the current arch
+ String arch = getOSArch();
+ for (String prefix : prefixList) {
+ if (arch.startsWith(prefix)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Converts a space delimited string to a list of strings
+ */
+ public static String[] getStringList(String str) {
+ if (str == null) {
+ return null;
+ }
+ ArrayList<String> list = new ArrayList<>();
+ int i = 0;
+ int length = str.length();
+ StringBuffer sb = null;
+ while (i < length) {
+ char ch = str.charAt(i);
+ switch (ch) {
+ case ' ':
+ // A space was hit. Add string to list
+ if (sb != null) {
+ list.add(sb.toString());
+ sb = null;
+ }
+ break;
+ case '\\':
+ // It is a delimiter. Add next character
+ if (i + 1 < length) {
+ ch = str.charAt(++i);
+ if (sb == null) {
+ sb = new StringBuffer();
+ }
+ sb.append(ch);
+ }
+ break;
+ default:
+ if (sb == null) {
+ sb = new StringBuffer();
+ } sb.append(ch);
+ break;
+ }
+ i++; // Next character
+ }
+ // Make sure to add the last part to the list too
+ if (sb != null) {
+ list.add(sb.toString());
+ }
+ if (list.isEmpty()) {
+ return null;
+ }
+ String[] results = new String[list.size()];
+ return list.toArray(results);
+ }
+
+ /**
+ * Checks if string list matches default locale
+ */
+ public static boolean matchLocale(String[] localeList, Locale locale) {
+ // No locale specified matches everything
+ if (localeList == null) {
+ return true;
+ }
+ for (String localeList1 : localeList) {
+ if (matchLocale(localeList1, locale)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Checks if string matches default locale
+ */
+ public static boolean matchLocale(String localeStr, Locale locale) {
+ if (localeStr == null || localeStr.length() == 0) {
+ return true;
+ }
+
+ // Compare against default locale
+ String language;
+ String country;
+ String variant;
+
+ // The locale string is of the form language_country_variant
+ StringTokenizer st = new StringTokenizer(localeStr, "_", false);
+ if (st.hasMoreElements() && locale.getLanguage().length() > 0) {
+ language = st.nextToken();
+ if (!language.equalsIgnoreCase(locale.getLanguage())) {
+ return false;
+ }
+ }
+ if (st.hasMoreElements() && locale.getCountry().length() > 0) {
+ country = st.nextToken();
+ if (!country.equalsIgnoreCase(locale.getCountry())) {
+ return false;
+ }
+ }
+ if (st.hasMoreElements() && locale.getVariant().length() > 0) {
+ variant = st.nextToken();
+ if (!variant.equalsIgnoreCase(locale.getVariant())) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ public static long heapValToLong(String heapValue) {
+ if (heapValue == null) {
+ return -1;
+ }
+ long multiplier = 1;
+ if (heapValue.toLowerCase().lastIndexOf('m') != -1) {
+ // units are megabytes, 1 megabyte = 1024 * 1024 bytes
+ multiplier = 1024 * 1024;
+ heapValue = heapValue.substring(0, heapValue.length() - 1);
+ } else if (heapValue.toLowerCase().lastIndexOf('k') != -1) {
+ // units are kilobytes, 1 kilobyte = 1024 bytes
+ multiplier = 1024;
+ heapValue = heapValue.substring(0, heapValue.length() - 1);
+ }
+ long theValue;
+ try {
+ theValue = Long.parseLong(heapValue);
+ theValue = theValue * multiplier;
+ } catch (NumberFormatException e) {
+ theValue = -1;
+ }
+ return theValue;
+ }
+
+ public static byte[] readBytes(InputStream is, long size) throws IOException {
+ // Sanity on file size (restrict to 1M)
+ if (size > 1024 * 1024) {
+ throw new IOException("File too large");
+ }
+
+ BufferedInputStream bis;
+ if (is instanceof BufferedInputStream) {
+ bis = (BufferedInputStream) is;
+ } else {
+ bis = new BufferedInputStream(is);
+ }
+
+ if (size <= 0) {
+ size = 10 * 1024; // Default to 10K
+ }
+ byte[] b = new byte[(int) size];
+ int n;
+ int bytesRead = 0;
+ n = bis.read(b, bytesRead, b.length - bytesRead);
+ while (n != -1) {
+ bytesRead += n;
+ // Still room in array
+ if (b.length == bytesRead) {
+ byte[] bb = new byte[b.length * 2];
+ System.arraycopy(b, 0, bb, 0, b.length);
+ b = bb;
+ }
+ // Read next line
+ n = bis.read(b, bytesRead, b.length - bytesRead);
+ }
+ bis.close();
+ is.close();
+
+ if (bytesRead != b.length) {
+ byte[] bb = new byte[bytesRead];
+ System.arraycopy(b, 0, bb, 0, bytesRead);
+ b = bb;
+ }
+ return b;
+ }
+
+ public static String getOSFullName() {
+ return System.getProperty("os.name");
+ }
+
+ /**
+ * Makes sure a URL is a path URL, i.e., ends with '/'
+ */
+ public static URL asPathURL(URL url) {
+ if (url == null) {
+ return null;
+ }
+
+ String path = url.getFile();
+ if (path != null && !path.endsWith("/")) {
+ try {
+ return new URL(url.getProtocol(),
+ url.getHost(),
+ url.getPort(),
+ url.getFile() + "/");
+ } catch (MalformedURLException mue) {
+ // Should not happen
+ }
+ }
+ // Just return same URl
+ return url;
+ }
+
+ public static Locale getDefaultLocale() {
+ return Locale.getDefault();
+ }
+
+ public static String toNormalizedString(URL u) {
+ if (u == null) {
+ return "";
+ }
+
+ try {
+ if (u.getPort() == u.getDefaultPort()) {
+ u = new URL(u.getProtocol().toLowerCase(),
+ u.getHost().toLowerCase(), -1, u.getFile());
+ } else {
+ u = new URL(u.getProtocol().toLowerCase(),
+ u.getHost().toLowerCase(), u.getPort(), u.getFile());
+ }
+ } catch (MalformedURLException ex) {
+ }
+ return u.toExternalForm();
+ }
+
+ public static boolean sameURLs(URL u1, URL u2) {
+ if (u1 == null || u2 == null || (u1 == u2)) {
+ return (u1 == u2);
+ }
+ //NB: do not use URL.sameFile() as it will do DNS lookup
+ // Also, do quick check before slow string comparisons
+ String f1 = u1.getFile();
+ String f2 = u2.getFile();
+ return (f1.length() == f2.length()) && sameBase(u1, u2)
+ && f1.equalsIgnoreCase(f2);
+ }
+
+ public static boolean sameBase(URL u1, URL u2) {
+ return u1 != null && u2 != null &&
+ sameHost(u1, u2) && samePort(u1, u2) && sameProtocol(u1, u2);
+ }
+
+ private static boolean sameProtocol(URL u1, URL u2) {
+ //protocols are known to be lowercase
+ return u1.getProtocol().equals(u2.getProtocol());
+ }
+
+ private static boolean sameHost(URL u1, URL u2) {
+ String host = u1.getHost();
+ String otherHost = u2.getHost();
+ if (host == null || otherHost == null) {
+ return (host == null && otherHost == null);
+ } else {
+ //avoid slow comparison for strings of different length
+ return ((host.length() == otherHost.length())
+ && host.equalsIgnoreCase(otherHost));
+ }
+ }
+
+ private static boolean samePort(URL u1, URL u2) {
+ return getPort(u1) == getPort(u2);
+ }
+
+ public static int getPort(URL u) {
+ if (u.getPort() != -1) {
+ return u.getPort();
+ } else {
+ return u.getDefaultPort();
+ }
+ }
+
+ public static URL getBase(URL url) {
+ if (url == null) return null;
+ String file = url.getFile();
+ if (file != null) {
+ int idx = file.lastIndexOf('/');
+ if (idx != -1 ) {
+ file = file.substring(0, idx + 1);
+ }
+ try {
+ return new URL(
+ url.getProtocol(),
+ url.getHost(),
+ url.getPort(),
+ file);
+ } catch(MalformedURLException mue) {
+ System.err.println(mue.getMessage());
+ }
+ }
+ // Just return same URL
+ return url;
+ }
+
+ private static String getEmbeddedVersionPath(String path, String version) {
+ int index = path.lastIndexOf("/");
+ String filename = path.substring(index + 1);
+ path = path.substring(0, index + 1);
+
+ String ext = null;
+ index = filename.lastIndexOf(".");
+ if (index != -1) {
+ ext = filename.substring(index + 1);
+ filename = filename.substring(0, index);
+ }
+
+ StringBuilder filenameSB = new StringBuilder(filename);
+ if (version != null) {
+ filenameSB.append("__V");
+ filenameSB.append(version);
+ }
+ if (ext != null) {
+ filenameSB.append(".");
+ filenameSB.append(ext);
+ }
+
+ path += filenameSB.toString();
+ return path;
+ }
+
+ public static URL getEmbeddedVersionURL(URL u, String version) throws Exception {
+ if (u == null) {
+ return null;
+ }
+
+ if (version == null || version.indexOf("*") != -1
+ || version.indexOf("+") != -1) {
+ // Do not support * or + in version string
+ return u;
+ }
+
+ URL versionURL = null;
+
+ String protocol = u.getProtocol();
+ String host = u.getHost();
+ int port = u.getPort();
+ String path = u.getPath();
+
+ path = getEmbeddedVersionPath(path, version);
+
+ versionURL = new URL(protocol, host, port, path);
+
+ return versionURL;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/demo/share/jpackage/JNLPConverter/src/jnlp/converter/parser/JNLPDesc.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,617 @@
+/*
+ * 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.
+ *
+ * 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 jnlp.converter.parser;
+
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.Properties;
+import jnlp.converter.Log;
+
+import jnlp.converter.parser.ResourcesDesc.JARDesc;
+import jnlp.converter.parser.ResourcesDesc.JREDesc;
+
+public class JNLPDesc {
+ private String specVersion = null;
+ private String codebase = null;
+ private String version = null;
+ private String href = null;
+ private String name = null;
+ private String title = null;
+ private String vendor = null;
+ private String mainJar = null;
+ private String [] descriptions = null;
+ private IconDesc [] icons = null;
+ private ShortcutDesc shortcuts = null;
+ private AssociationDesc [] associations = null;
+ private String mainClass = null;
+ private final List<String> arguments = new ArrayList<>();
+ private final List<String> files = new ArrayList<>();
+ private final List<JARDesc> resources = new ArrayList<>();
+ private final List<String> vmArgs = new ArrayList<>();
+ private boolean isLibrary = false;
+ private boolean isInstaller = false;
+ private boolean isJRESet = false;
+ private ResourcesDesc resourcesDesc;
+ private boolean isVersionEnabled = false;
+ private boolean isSandbox = true;
+ private boolean isFXApp = false;
+
+ public void setSpecVersion(String specVersion) {
+ this.specVersion = specVersion;
+
+ // Valid values are 1.0, 1.5, 6.0, 6.0.10, 6.0.18, 7.0, 8.20, 9 or a wildcard such as 1.0+.
+ if (!specVersion.startsWith("1.0") &&
+ !specVersion.startsWith("1.5") &&
+ !specVersion.startsWith("6.0") &&
+ !specVersion.startsWith("6.0.10") &&
+ !specVersion.startsWith("6.0.18") &&
+ !specVersion.startsWith("7.0") &&
+ !specVersion.startsWith("8.20") &&
+ !specVersion.startsWith("9")) {
+ System.out.println("Warning: Invalid version of the JNLP specification found: "
+ + specVersion + ". Valid values are 1.0, 1.5, 6.0, 6.0.10, 6.0.18, 7.0,"
+ + " 8.20, 9 or a wildcard such as 1.0+.");
+ }
+ }
+
+ public String getSpecVersion() {
+ return specVersion;
+ }
+
+ public void setCodebase(String codebase) {
+ this.codebase = codebase;
+ }
+
+ public String getCodebase() {
+ return codebase;
+ }
+
+ public void setVersion(String version) {
+ this.version = version;
+ }
+
+ public String getVersion() {
+ return version;
+ }
+
+ public void setHref(String href) {
+ this.href = href;
+ }
+
+ public String getHref() {
+ return href;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setTitle(String title) {
+ this.title = title;
+ }
+
+ public String getTitle() {
+ return title;
+ }
+
+ public void setVendor(String vendor) {
+ this.vendor = vendor;
+ }
+
+ public String getVendor() {
+ return vendor;
+ }
+
+ public void setMainJar(String mainJar) {
+ if (this.mainJar == null) {
+ this.mainJar = mainJar;
+ } else {
+ Log.warning("Main jar already set to '" + this.mainJar + "'. "
+ + "Attempt to set main jar to '" + mainJar + "' will be ignored.");
+ }
+ }
+
+ public String getMainJar() {
+ return mainJar;
+ }
+
+ public void setDescriptions(String [] descriptions) {
+ this.descriptions = descriptions;
+ }
+
+ public String getDescription() {
+ String description = null;
+ if (descriptions != null) {
+ if (descriptions[InformationDesc.DESC_DEFAULT] != null) {
+ description = descriptions[InformationDesc.DESC_DEFAULT];
+ } else if (descriptions[InformationDesc.DESC_SHORT] != null) {
+ description = descriptions[InformationDesc.DESC_SHORT];
+ } else if (descriptions[InformationDesc.DESC_ONELINE] != null) {
+ description = descriptions[InformationDesc.DESC_ONELINE];
+ } else if (descriptions[InformationDesc.DESC_TOOLTIP] != null) {
+ description = descriptions[InformationDesc.DESC_TOOLTIP];
+ }
+
+ if (description != null) {
+ if (description.contains("\r") || description.contains("\n")) {
+ Log.warning("Multiple lines of text in description is not supported and description will be converted to single line by replacing new lines with spaces.");
+ Log.warning("Original description:");
+ Log.warning(description);
+ String descs[] = description.split("\n");
+ description = "";
+ for (String desc : descs) {
+ desc = desc.trim();
+ if (desc.endsWith("\r")) { // In case new line was \r\n
+ if (desc.length() != 1) {
+ desc = desc.substring(0, desc.length() - 1);
+ } else {
+ continue;
+ }
+ }
+
+ if (desc.isEmpty()) {
+ continue;
+ }
+
+ if (!description.isEmpty()) {
+ description += " ";
+ }
+
+ description += desc;
+ }
+ Log.warning("Converted description:");
+ Log.warning(description);
+ }
+ }
+ }
+
+ return description;
+ }
+
+ public void setIcons(IconDesc [] icons) {
+ this.icons = icons;
+ }
+
+ public IconDesc getIcon() {
+ for (IconDesc icon : icons) {
+ if (icon.getKind() == IconDesc.ICON_KIND_DEFAULT) {
+ return icon;
+ }
+ }
+
+ for (IconDesc icon : icons) {
+ if (icon.getKind() == IconDesc.ICON_KIND_SHORTCUT) {
+ return icon;
+ }
+ }
+
+ return null;
+ }
+
+ public String getIconLocation() {
+ IconDesc icon = getIcon();
+ if (icon != null) {
+ return icon.getLocalLocation();
+ }
+
+ return null;
+ }
+
+ public void setShortcuts(ShortcutDesc shortcuts) {
+ this.shortcuts = shortcuts;
+ }
+
+ public boolean isDesktopHint() {
+ if (shortcuts != null) {
+ return shortcuts.getDesktop();
+ }
+
+ return false;
+ }
+
+ public boolean isMenuHint() {
+ if (shortcuts != null) {
+ return shortcuts.getMenu();
+ }
+
+ return false;
+ }
+
+ public String getSubMenu() {
+ if (shortcuts != null) {
+ return shortcuts.getSubmenu();
+ }
+
+ return null;
+ }
+
+ public void setAssociations(AssociationDesc [] associations) {
+ this.associations = associations;
+ }
+
+ public AssociationDesc [] getAssociations() {
+ return associations;
+ }
+
+ public void setMainClass(String mainClass, boolean isJavafxDesc) {
+ if (isJavafxDesc) {
+ this.mainClass = mainClass;
+ } else if (this.mainClass == null) {
+ this.mainClass = mainClass;
+ }
+ }
+
+ public String getMainClass() {
+ return mainClass;
+ }
+
+ public void addArguments(String argument) {
+ if (argument != null && !argument.isEmpty()) {
+ arguments.add(argument);
+ }
+ }
+
+ public List<String> getArguments() {
+ return arguments;
+ }
+
+ public void setProperty(String name, String value) {
+ if (name.equalsIgnoreCase("jnlp.versionEnabled") && value.equalsIgnoreCase("true")) {
+ isVersionEnabled = true;
+ return;
+ }
+
+ addVMArg("-D" + name + "=" + value);
+ }
+
+ public boolean isVersionEnabled() {
+ return isVersionEnabled;
+ }
+
+ public boolean isSandbox() {
+ return isSandbox;
+ }
+
+ public void setIsSandbox(boolean value) {
+ isSandbox = value;
+ }
+
+ public boolean isFXApp() {
+ return isFXApp;
+ }
+
+ public void setIsFXApp(boolean value) {
+ isFXApp = value;
+ }
+
+ public void addFile(String file) {
+ if (file != null) {
+ files.add(file);
+ }
+ }
+
+ public List<String> getFiles() {
+ return files;
+ }
+
+ private boolean isResourceExists(JARDesc resource) {
+ for (JARDesc r : resources) {
+ if (r.getLocation().equals(resource.getLocation())) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ public void addResource(JARDesc resource) {
+ if (resource != null) {
+ if (isResourceExists(resource)) {
+ Log.warning("Ignoring repeated resource " + resource.getLocation());
+ return;
+ }
+ resources.add(resource);
+ }
+ }
+
+ public List<JARDesc> getResources() {
+ return resources;
+ }
+
+ public void addVMArg(String arg) {
+ if (arg != null) {
+ vmArgs.add(arg);
+ }
+ }
+
+ public List<String> getVMArgs() {
+ return vmArgs;
+ }
+
+ public void setIsLibrary(boolean isLibrary) {
+ this.isLibrary = isLibrary;
+ }
+
+ public boolean isLibrary() {
+ return isLibrary;
+ }
+
+ public void setIsInstaller(boolean isInstaller) {
+ this.isInstaller = isInstaller;
+ }
+
+ public boolean isInstaller() {
+ return isInstaller;
+ }
+
+ public void setIsJRESet(boolean isJRESet) {
+ this.isJRESet = isJRESet;
+ }
+
+ public boolean isJRESet() {
+ return isJRESet;
+ }
+
+ public void setResourcesDesc(ResourcesDesc resourcesDesc) {
+ this.resourcesDesc = resourcesDesc;
+ }
+
+ public ResourcesDesc getResourcesDesc() {
+ return resourcesDesc;
+ }
+
+ public void parseResourceDesc() throws Exception {
+ if (resourcesDesc != null && !resourcesDesc.isEmpty()) {
+ setMainJar(resourcesDesc.getMainJar().getName());
+
+ JARDesc[] jars = resourcesDesc.getAllJarDescs();
+ for (JARDesc jar : jars) {
+ addResource(jar);
+ }
+
+ JREDesc jreDesc = resourcesDesc.getJreDesc();
+ if (jreDesc != null) {
+ String [] args = jreDesc.getVmArgsList();
+ if (args != null) {
+ for (String arg : args) {
+ addVMArg(arg);
+ }
+ }
+
+ if (jreDesc.getMinHeap() != -1) {
+ addVMArg("-Xms" + jreDesc.getMinHeap());
+ }
+
+ if (jreDesc.getMaxHeap() != -1) {
+ addVMArg("-Xmx" + jreDesc.getMaxHeap());
+ }
+ }
+
+ Properties props = resourcesDesc.getResourceProperties();
+ Enumeration e = props.propertyNames();
+ while (e.hasMoreElements()) {
+ String key = (String) e.nextElement();
+ String value = props.getProperty(key);
+ setProperty(key, value);
+ }
+ }
+ }
+
+ public static class InformationDesc {
+
+ private final String _title;
+ private final String _vendor;
+ private final String[] _descriptions;
+ private final IconDesc[] _icons;
+ private ShortcutDesc _shortcutHints;
+ private AssociationDesc[] _associations;
+
+ public InformationDesc(String title, String vendor,
+ String[] descriptions,
+ IconDesc[] icons,
+ ShortcutDesc shortcutHints,
+ AssociationDesc[] associations) {
+ _title = (title == null) ? "" : title;
+ _vendor = (vendor == null) ? "" : vendor;
+ if (descriptions == null) {
+ descriptions = new String[NOF_DESC];
+ }
+ _descriptions = descriptions;
+ _icons = icons;
+ _shortcutHints = shortcutHints;
+ _associations = associations;
+ }
+
+ /**
+ * Constants for the getInfoDescription
+ */
+ final public static int DESC_DEFAULT = 0;
+ final public static int DESC_SHORT = 1;
+ final public static int DESC_ONELINE = 2;
+ final public static int DESC_TOOLTIP = 3;
+ final public static int NOF_DESC = 4;
+
+ /**
+ * Information
+ */
+ public String getTitle() {
+ return _title;
+ }
+
+ public String getVendor() {
+ return _vendor;
+ }
+
+ public IconDesc[] getIcons() {
+ return _icons;
+ }
+
+ public ShortcutDesc getShortcut() {
+ if (_shortcutHints == null) {
+ return null;
+ }
+ return new ShortcutDesc(_shortcutHints.getDesktop(), _shortcutHints.getMenu(), _shortcutHints.getSubmenu());
+ }
+
+ public AssociationDesc[] getAssociations() {
+ return _associations;
+ }
+
+ /**
+ * Sets new shortcut hints.
+ *
+ * @param shortcutDesc the new shortcut hints to set
+ */
+ public void setShortcut(ShortcutDesc shortcut) {
+ _shortcutHints = shortcut;
+ }
+
+ /**
+ * Sets new associations.
+ *
+ * @param assoc the association to set
+ */
+ public void setAssociation(AssociationDesc assoc) {
+ if (assoc == null) {
+ _associations = null;
+ } else {
+ _associations = new AssociationDesc[]{assoc};
+ }
+ }
+
+ /**
+ * Returns the description of the given kind. will return null if none
+ * there
+ */
+ public String getDescription(int kind) {
+ return _descriptions[kind];
+ }
+
+ public String[] getDescription() {
+ return _descriptions;
+ }
+ }
+
+ public static class IconDesc {
+
+ private final String _location;
+ private String _localLocation;
+ private final int _kind;
+
+ final public static int ICON_KIND_DEFAULT = 0;
+ final public static int ICON_KIND_SHORTCUT = 5;
+
+ public IconDesc(URL location, int kind) {
+ _location = location.toExternalForm();
+ _kind = kind;
+ }
+
+ public String getLocation() {
+ return _location;
+ }
+
+ public void setLocalLocation(String localLocation) {
+ _localLocation = localLocation;
+ }
+
+ public String getLocalLocation() {
+ return _localLocation;
+ }
+
+ public int getKind() {
+ return _kind;
+ }
+ }
+
+ public static class ShortcutDesc {
+
+ private final boolean _desktop;
+ private final boolean _menu;
+ private final String _submenu;
+
+ public ShortcutDesc(boolean desktop, boolean menu, String submenu) {
+ _desktop = desktop;
+ _menu = menu;
+ _submenu = submenu;
+ }
+
+ public boolean getDesktop() {
+ return _desktop;
+ }
+
+ public boolean getMenu() {
+ return _menu;
+ }
+
+ public String getSubmenu() {
+ return _submenu;
+ }
+ }
+
+ public static class AssociationDesc {
+
+ private final String _extensions;
+ private final String _mimeType;
+ private final String _description;
+ private final String _icon;
+ private String _iconLocalLocation;
+
+ public AssociationDesc(String extensions, String mimeType, String description, URL icon) {
+ _extensions = extensions;
+ _mimeType = mimeType;
+ _description = description;
+ _icon = (icon != null) ? icon.toExternalForm() : null;
+ }
+
+ public void setIconLocalLocation(String localLocation) {
+ _iconLocalLocation = localLocation;
+ }
+
+ public String getIconLocalLocation() {
+ return _iconLocalLocation;
+ }
+
+ public String getExtensions() {
+ return _extensions;
+ }
+
+ public String getMimeType() {
+ return _mimeType;
+ }
+
+ public String getMimeDescription() {
+ return _description;
+ }
+
+ public String getIconUrl() {
+ return _icon;
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/demo/share/jpackage/JNLPConverter/src/jnlp/converter/parser/ResourceType.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2006, 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.
+ *
+ * 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 jnlp.converter.parser;
+
+/*
+ * Public super class for all resource entries
+ */
+interface ResourceType {
+ /** Visit this specific type */
+ void visit(ResourceVisitor visitor) throws Exception;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/demo/share/jpackage/JNLPConverter/src/jnlp/converter/parser/ResourceVisitor.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2006, 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.
+ *
+ * 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 jnlp.converter.parser;
+
+import jnlp.converter.parser.ResourcesDesc.JARDesc;
+import jnlp.converter.parser.ResourcesDesc.PropertyDesc;
+import jnlp.converter.parser.ResourcesDesc.JREDesc;
+import jnlp.converter.parser.ResourcesDesc.ExtensionDesc;
+
+/**
+ * A visitor class for the various ResourceType objects
+ * with dummy visit methods for all types.
+ */
+public class ResourceVisitor {
+
+ public void visitJARDesc(JARDesc jad) {
+ }
+
+ public void visitPropertyDesc(PropertyDesc prd) {
+ }
+
+ public void visitExtensionDesc(ExtensionDesc ed) throws Exception {
+ }
+
+ public void visitJREDesc(JREDesc jrd) {
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/demo/share/jpackage/JNLPConverter/src/jnlp/converter/parser/ResourcesDesc.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,665 @@
+/*
+ * Copyright (c) 2006, 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.
+ *
+ * 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 jnlp.converter.parser;
+
+import java.net.URL;
+import java.util.Properties;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Enumeration;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+import jnlp.converter.HTTPHelper;
+
+/**
+ * This class contains information about the codebase and properties, i.e., how
+ * to locate the classes and optional-packages
+ */
+public class ResourcesDesc implements ResourceType {
+
+ private final List<ResourceType> _list;
+ private volatile JNLPDesc _parent = null;
+
+ /**
+ * Create empty resource list
+ */
+ public ResourcesDesc() {
+ _list = new CopyOnWriteArrayList<>();
+ }
+
+ public JNLPDesc getParent() {
+ return _parent;
+ }
+
+ void setParent(JNLPDesc parent) {
+ _parent = parent;
+ for (int i = 0; i < _list.size(); i++) {
+ Object o = _list.get(i);
+ if (o instanceof JREDesc) {
+ JREDesc jredesc = (JREDesc) o;
+ if (jredesc.getNestedResources() != null) {
+ jredesc.getNestedResources().setParent(parent);
+ }
+ }
+ }
+ }
+
+ public void addResource(ResourceType rd) {
+ if (rd != null) {
+ _list.add(rd);
+ }
+ }
+
+ boolean isEmpty() {
+ return _list.isEmpty();
+ }
+
+ public JARDesc[] getLocalJarDescs() {
+ ArrayList<JARDesc> jds = new ArrayList<>(_list.size());
+ for (ResourceType rt : _list) {
+ if (rt instanceof JARDesc) {
+ jds.add((JARDesc) rt);
+ }
+ }
+ return jds.toArray(new JARDesc[jds.size()]);
+ }
+
+ public JREDesc getJreDesc() {
+ for (ResourceType rt : _list) {
+ if (rt instanceof JREDesc) {
+ return (JREDesc)rt;
+ }
+ }
+
+ return null;
+ }
+
+ public ExtensionDesc[] getExtensionDescs() throws Exception {
+ final ArrayList<ExtensionDesc> extList = new ArrayList<>();
+ visit(new ResourceVisitor() {
+ @Override
+ public void visitExtensionDesc(ExtensionDesc ed) throws Exception {
+ // add all extensiondesc recursively
+ addExtToList(extList);
+ }
+ });
+ return extList.toArray(new ExtensionDesc[extList.size()]);
+ }
+
+ public JARDesc[] getAllJarDescs() throws Exception {
+ List<JARDesc> jarList = new ArrayList<>();
+ addJarsToList(jarList);
+ return jarList.toArray(new JARDesc[jarList.size()]);
+ }
+
+ /**
+ * Add to a list of all the ExtensionDesc. This method goes recusivly through
+ * all ExtensionDesc
+ */
+ private void addExtToList(final List<ExtensionDesc> list) throws Exception {
+ // Iterate through list an add ext jnlp to the list.
+ visit(new ResourceVisitor() {
+ @Override
+ public void visitExtensionDesc(ExtensionDesc ed) throws Exception {
+ if (ed.getExtensionDesc() != null) {
+ ed.getExtensionDesc().getMainJar();
+ ResourcesDesc rd = ed.getExtensionDesc().getResourcesDesc();
+ if (rd != null) {
+ rd.addExtToList(list);
+ }
+ }
+ list.add(ed);
+ }
+ });
+ }
+
+ private void addJarsToList(final List<JARDesc> list) throws Exception {
+
+ // Iterate through list an add resources to the list.
+ // The ordering of resources are preserved
+ visit(new ResourceVisitor() {
+ @Override
+ public void visitJARDesc(JARDesc jd) {
+ list.add(jd);
+ }
+
+ @Override
+ public void visitExtensionDesc(ExtensionDesc ed) throws Exception {
+ if (ed.getExtensionDesc() != null) {
+ ResourcesDesc rd = ed.getExtensionDesc().getResourcesDesc();
+ if (rd != null) {
+ rd.addJarsToList(list);
+ }
+ }
+ }
+ });
+ }
+
+ /**
+ * Get all the resources needed when a specific resource is requested.
+ * Returns null if no resource was found
+ */
+ public JARDesc[] getResource(final URL location) throws Exception {
+ final JARDesc[] resources = new JARDesc[1];
+ // Find the given resource
+ visit(new ResourceVisitor() {
+ @Override
+ public void visitJARDesc(JARDesc jd) {
+ if (GeneralUtil.sameURLs(jd.getLocation(), location)) {
+ resources[0] = jd;
+ }
+ }
+ });
+
+ // Found no resource?
+ if (resources[0] == null) {
+ return null;
+ }
+
+ // No part, so just one resource
+ return resources;
+ }
+
+ /* Returns the Expected Main Jar
+ * first jar with attribute main="true"
+ * else first jar if none has that attribute
+ * will look in extensions, and nested resource blocks if matching
+ */
+ protected JARDesc getMainJar() throws Exception {
+ // Normal trick to get around final arguments to inner classes
+ final JARDesc[] results = new JARDesc[2];
+
+ visit(new ResourceVisitor() {
+ @Override
+ public void visitJARDesc(JARDesc jd) {
+ if (jd.isJavaFile()) {
+ // Keep track of first Java File
+ if (results[0] == null || results[0].isNativeLib()) {
+ results[0] = jd;
+ }
+ // Keep tack of Java File marked main
+ if (jd.isMainJarFile()) {
+ results[1] = jd;
+ }
+ } else if (jd.isNativeLib()) {
+ // if jnlp extension has only native lib
+ if (results[0] == null) {
+ results[0] = jd;
+ }
+ }
+ }
+
+ @Override
+ public void visitExtensionDesc(ExtensionDesc ed) throws Exception {
+ // only check if no main yet and it is not an installer
+ if (results[1] == null && !ed.isInstaller()) {
+ JNLPDesc extLd = ed.getExtensionDesc();
+ if (extLd != null && extLd.isLibrary()) {
+ ResourcesDesc rd = extLd.getResourcesDesc();
+ if (rd != null) {
+ // look for main jar in extension resource
+ rd.visit(this);
+ }
+ }
+ }
+ }
+ });
+
+ // Default is the first, if none is specified as main. This might
+ // return NULL if there is no JAR resources.
+ JARDesc first = results[0];
+ JARDesc main = results[1];
+
+ // if main is null then return first;
+ // libraries have no such thing as a main jar, so return first;
+ // otherwise return main
+ // only returns null if there are no jars.
+ return (main == null) ? first : main;
+ }
+
+ /*
+ * Get the properties defined for this object
+ */
+ public Properties getResourceProperties() throws Exception {
+ final Properties props = new Properties();
+ visit(new ResourceVisitor() {
+ @Override
+ public void visitPropertyDesc(PropertyDesc pd) {
+ props.setProperty(pd.getKey(), pd.getValue());
+ }
+
+ @Override
+ public void visitExtensionDesc(ExtensionDesc ed) throws Exception {
+ JNLPDesc jnlpd = ed.getExtensionDesc();
+ ResourcesDesc rd = jnlpd.getResourcesDesc();
+ if (rd != null) {
+ Properties extProps = rd.getResourceProperties();
+ Enumeration e = extProps.propertyNames();
+ while (e.hasMoreElements()) {
+ String key = (String) e.nextElement();
+ String value = extProps.getProperty(key);
+ props.setProperty(key, value);
+ }
+ }
+ }
+ });
+ return props;
+ }
+
+ /*
+ * Get the properties defined for this object, in the right order.
+ */
+ public List<Property> getResourcePropertyList() throws Exception {
+ final LinkedList<Property> propList = new LinkedList<>();
+ visit(new ResourceVisitor() {
+ @Override
+ public void visitPropertyDesc(PropertyDesc pd) {
+ propList.add(new Property(pd.getKey(), pd.getValue()));
+ }
+ });
+ return propList;
+ }
+
+ /**
+ * visitor dispatch
+ */
+ @Override
+ public void visit(ResourceVisitor rv) throws Exception {
+ for (int i = 0; i < _list.size(); i++) {
+ ResourceType rt = _list.get(i);
+ rt.visit(rv);
+ }
+ }
+
+ public void addNested(ResourcesDesc nested) throws Exception {
+ if (nested != null) {
+ nested.visit(new ResourceVisitor() {
+ @Override
+ public void visitJARDesc(JARDesc jd) {
+ _list.add(jd);
+ }
+
+ @Override
+ public void visitPropertyDesc(PropertyDesc pd) {
+ _list.add(pd);
+ }
+
+ @Override
+ public void visitExtensionDesc(ExtensionDesc ed) {
+ _list.add(ed);
+ }
+ });
+ }
+
+ }
+
+ public static class JARDesc implements ResourceType {
+
+ private URL _location;
+ private String _locationString;
+ private String _version;
+ private boolean _isNativeLib;
+ private boolean _isMainFile; // Only used for Java JAR files (a main JAR file is implicitly eager)
+ private ResourcesDesc _parent; // Back-pointer to the Resources that contains this JAR
+
+ public JARDesc(URL location, String version, boolean isMainFile, boolean isNativeLib, ResourcesDesc parent) {
+ _location = location;
+ _locationString = GeneralUtil.toNormalizedString(location);
+ _version = version;
+ _isMainFile = isMainFile;
+ _isNativeLib = isNativeLib;
+ _parent = parent;
+ }
+
+ /**
+ * Type of JAR resource
+ */
+ public boolean isNativeLib() {
+ return _isNativeLib;
+ }
+
+ public boolean isJavaFile() {
+ return !_isNativeLib;
+ }
+
+ /**
+ * Returns URL/version for JAR file
+ */
+ public URL getVersionLocation() throws Exception {
+ if (getVersion() == null) {
+ return _location;
+ } else {
+ return GeneralUtil.getEmbeddedVersionURL(getLocation(), getVersion());
+ }
+ }
+
+ public URL getLocation() {
+ return _location;
+ }
+
+ public String getVersion() {
+ return _version;
+ }
+
+ public String getName() {
+ // File can be separated by '/' or '\\'
+ int index;
+ int index1 = _locationString.lastIndexOf('/');
+ int index2 = _locationString.lastIndexOf('\\');
+
+ if (index1 >= index2) {
+ index = index1;
+ } else {
+ index = index2;
+ }
+
+ if (index != -1) {
+ return _locationString.substring(index + 1, _locationString.length());
+ }
+
+ return null;
+ }
+
+ /**
+ * Returns if this is the main JAR file
+ */
+ public boolean isMainJarFile() {
+ return _isMainFile;
+ }
+
+ /**
+ * Get parent LaunchDesc
+ */
+ public ResourcesDesc getParent() {
+ return _parent;
+ }
+
+ /**
+ * Visitor dispatch
+ */
+ public void visit(ResourceVisitor rv) {
+ rv.visitJARDesc(this);
+ }
+ }
+
+ public static class PropertyDesc implements ResourceType {
+
+ private String _key;
+ private String _value;
+
+ public PropertyDesc(String key, String value) {
+ _key = key;
+ _value = value;
+ }
+
+ // Accessors
+ public String getKey() {
+ return _key;
+ }
+
+ public String getValue() {
+ return _value;
+ }
+
+ /**
+ * Visitor dispatch
+ */
+ public void visit(ResourceVisitor rv) {
+ rv.visitPropertyDesc(this);
+ }
+
+ }
+
+ public static class JREDesc implements ResourceType {
+
+ private String _version;
+ private long _maxHeap;
+ private long _minHeap;
+ private String _vmargs;
+ private ResourcesDesc _resourceDesc;
+ private JNLPDesc _extensioDesc;
+ private String _archList;
+
+ /*
+ * Constructor to create new instance based on the requirements from JNLP file.
+ */
+ public JREDesc(String version, long minHeap, long maxHeap, String vmargs,
+ ResourcesDesc resourcesDesc, String archList) {
+
+ _version = version;
+ _maxHeap = maxHeap;
+ _minHeap = minHeap;
+ _vmargs = vmargs;
+ _resourceDesc = resourcesDesc;
+ _extensioDesc = null;
+ _archList = archList;
+ }
+
+ public String[] getArchList() {
+ return GeneralUtil.getStringList(_archList);
+ }
+
+ public String getVersion() {
+ return _version;
+ }
+
+ public long getMinHeap() {
+ return _minHeap;
+ }
+
+ public long getMaxHeap() {
+ return _maxHeap;
+ }
+
+ public String getVmArgs() {
+ return _vmargs;
+ }
+
+ public String[] getVmArgsList() {
+ return GeneralUtil.getStringList(_vmargs);
+ }
+
+ public ResourcesDesc getNestedResources() {
+ return _resourceDesc;
+ }
+
+ public JNLPDesc getExtensionDesc() {
+ return _extensioDesc;
+ }
+
+ public void setExtensionDesc(JNLPDesc ld) {
+ _extensioDesc = ld;
+ }
+
+ /* visitor dispatch */
+ public void visit(ResourceVisitor rv) {
+ rv.visitJREDesc(this);
+ }
+ }
+
+ public static class Property implements Cloneable {
+
+ public static final String JNLP_VERSION_ENABLED = "jnlp.versionEnabled";
+
+ String key;
+ String value;
+
+ public Property(String spec) {
+ spec = spec.trim();
+ if (!spec.startsWith("-D") || spec.length() < 3) {
+ throw new IllegalArgumentException("Property invalid");
+ }
+
+ int endKey = spec.indexOf("=");
+ if (endKey < 0) {
+ // it's legal to have no assignment
+ this.key = spec.substring(2); // skip "-D"
+ this.value = "";
+ } else {
+ this.key = spec.substring(2, endKey);
+ this.value = spec.substring(endKey + 1);
+ }
+ }
+
+ public static Property createProperty(String spec) {
+ Property prop = null;
+ try {
+ prop = new Property(spec);
+ } catch (IllegalArgumentException iae) {
+ }
+ return prop;
+ }
+
+ public Property(String key, String value) {
+ this.key = key;
+ if (value != null) {
+ this.value = value;
+ } else {
+ this.value = "";
+ }
+ }
+
+ public String getKey() {
+ return key;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ // @return String representation, unquoted, unified presentation
+ public String toString() {
+ if (value.length() == 0) {
+ return "-D" + key;
+ }
+ return "-D" + key + "=" + value;
+ }
+
+ public void addTo(Properties props) {
+ props.setProperty(key, value);
+ }
+
+ // Hash Object
+ public boolean equals(Object o) {
+ if (!(o instanceof Property)) {
+ return false;
+ }
+ Property op = (Property) o;
+ int hashTheirs = op.hashCode();
+ int hashThis = hashCode();
+ return hashTheirs == hashThis;
+ }
+
+ public int hashCode() {
+ return key.hashCode();
+ }
+
+ private static List<Object> jnlpProps = Arrays.asList(new Object[]{
+ JNLP_VERSION_ENABLED
+ });
+
+ public static boolean isJnlpProperty(String spec) {
+ try {
+ Property p = new Property(spec);
+ return isJnlpPropertyKey(p.getKey());
+ } catch (Exception e) {
+ return false;
+ }
+ }
+
+ public static boolean isJnlpPropertyKey(String key) {
+ return key != null && jnlpProps.contains(key);
+ }
+ }
+
+ public static class ExtensionDesc implements ResourceType {
+ // Tag elements
+
+ private final URL _location;
+ private final String _locationString;
+ private final String _version;
+ private final URL _codebase;
+
+ // Link to launchDesc
+ private JNLPDesc _extensionLd; // Link to launchDesc for extension
+
+ public ExtensionDesc(URL location, String version) {
+ _location = location;
+ _locationString = GeneralUtil.toNormalizedString(location);
+ _version = version;
+ _codebase = GeneralUtil.asPathURL(GeneralUtil.getBase(location));
+ _extensionLd = null;
+ }
+
+ public boolean isInstaller() throws Exception {
+ if (getExtensionDesc() != null) {
+ return _extensionLd.isInstaller();
+ }
+ return false;
+ }
+
+ public URL getLocation() {
+ return _location;
+ }
+
+ public String getVersionLocation() throws Exception {
+ if (getVersion() == null) {
+ return _locationString;
+ } else {
+ return GeneralUtil.toNormalizedString(GeneralUtil.getEmbeddedVersionURL(getLocation(), getVersion()));
+ }
+ }
+
+ public String getVersion() {
+ return _version;
+ }
+
+ public URL getCodebase() {
+ return _codebase;
+ }
+
+ /*
+ * Information about the resources
+ */
+ public JNLPDesc getExtensionDesc() throws Exception {
+ if (_extensionLd == null) {
+ byte[] bits = HTTPHelper.getJNLPBits(getVersionLocation(), _locationString);
+ _extensionLd = XMLFormat.parse(bits, getCodebase(), getVersionLocation());
+ }
+ return _extensionLd;
+ }
+
+ public void setExtensionDesc(JNLPDesc desc) {
+ _extensionLd = desc;
+ }
+
+ /**
+ * Visitor dispatch
+ */
+ public void visit(ResourceVisitor rv) throws Exception {
+ rv.visitExtensionDesc(this);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/demo/share/jpackage/JNLPConverter/src/jnlp/converter/parser/VersionID.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,313 @@
+/*
+ * Copyright (c) 2006, 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.
+ *
+ * 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 jnlp.converter.parser;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+
+/**
+ * VersionID contains a JNLP version ID.
+ *
+ * The VersionID also contains a prefix indicator that can
+ * be used when stored with a VersionString
+ *
+ */
+public class VersionID implements Comparable<VersionID> {
+ private final String[] _tuple; // Array of Integer or String objects
+ private final boolean _usePrefixMatch; // star (*) prefix
+ private final boolean _useGreaterThan; // plus (+) greather-than
+ private final boolean _isCompound; // and (&) operator
+ private final VersionID _rest; // remaining part after the &
+
+ /**
+ * Creates a VersionID object from a given <code>String</code>.
+ * @param str version string to parse
+ */
+ public VersionID(String str) {
+ if (str == null || str.length() == 0) {
+ _tuple = new String[0];
+ _useGreaterThan = false;
+ _usePrefixMatch = false;
+ _isCompound = false;
+ _rest = null;
+ return;
+ }
+
+ // Check for compound
+ int amp = str.indexOf("&");
+ if (amp >= 0) {
+ _isCompound = true;
+ VersionID firstPart = new VersionID(str.substring(0, amp));
+ _rest = new VersionID(str.substring(amp+1));
+ _tuple = firstPart._tuple;
+ _usePrefixMatch = firstPart._usePrefixMatch;
+ _useGreaterThan = firstPart._useGreaterThan;
+ } else {
+ _isCompound = false;
+ _rest = null;
+ // Check for postfix
+ if (str.endsWith("+")) {
+ _useGreaterThan = true;
+ _usePrefixMatch = false;
+ str = str.substring(0, str.length() - 1);
+ } else if (str.endsWith("*")) {
+ _useGreaterThan = false;
+ _usePrefixMatch = true;
+ str = str.substring(0, str.length() - 1);
+ } else {
+ _useGreaterThan = false;
+ _usePrefixMatch = false;
+ }
+
+ ArrayList<String> list = new ArrayList<>();
+ int start = 0;
+ for (int i = 0; i < str.length(); i++) {
+ // Split at each separator character
+ if (".-_".indexOf(str.charAt(i)) != -1) {
+ if (start < i) {
+ String value = str.substring(start, i);
+ list.add(value);
+ }
+ start = i + 1;
+ }
+ }
+ if (start < str.length()) {
+ list.add(str.substring(start, str.length()));
+ }
+ _tuple = list.toArray(new String[0]);
+ }
+ }
+
+ /** @return true if no flags are set */
+ public boolean isSimpleVersion() {
+ return !_useGreaterThan && !_usePrefixMatch && !_isCompound;
+ }
+
+ /** Match 'this' versionID against vid.
+ * The _usePrefixMatch/_useGreaterThan flag is used to determine if a
+ * prefix match of an exact match should be performed
+ * if _isCompound, must match _rest also.
+ */
+ public boolean match(VersionID vid) {
+ if (_isCompound) {
+ if (!_rest.match(vid)) {
+ return false;
+ }
+ }
+ return (_usePrefixMatch) ? this.isPrefixMatchTuple(vid) :
+ (_useGreaterThan) ? vid.isGreaterThanOrEqualTuple(this) :
+ matchTuple(vid);
+ }
+
+ /** Compares if two version IDs are equal */
+ @Override
+ public boolean equals(Object o) {
+ if (matchTuple(o)) {
+ VersionID ov = (VersionID) o;
+ if (_rest == null || _rest.equals(ov._rest)) {
+ if ((_useGreaterThan == ov._useGreaterThan) &&
+ (_usePrefixMatch == ov._usePrefixMatch)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /** Computes a hash code for a VersionID */
+ @Override
+ public int hashCode() {
+ boolean first = true;
+ int hashCode = 0;
+ for (String tuple : _tuple) {
+ if (first) {
+ first = false;
+ hashCode = tuple.hashCode();
+ } else {
+ hashCode = hashCode ^ tuple.hashCode();
+ }
+ }
+ return hashCode;
+ }
+
+ /** Compares if two version IDs are equal */
+ private boolean matchTuple(Object o) {
+ // Check for null and type
+ if (o == null || !(o instanceof VersionID)) {
+ return false;
+ }
+ VersionID vid = (VersionID) o;
+
+ // Normalize arrays
+ String[] t1 = normalize(_tuple, vid._tuple.length);
+ String[] t2 = normalize(vid._tuple, _tuple.length);
+
+ // Check contents
+ for (int i = 0; i < t1.length; i++) {
+ Object o1 = getValueAsObject(t1[i]);
+ Object o2 = getValueAsObject(t2[i]);
+ if (!o1.equals(o2)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private Object getValueAsObject(String value) {
+ if (value.length() > 0 && value.charAt(0) != '-') {
+ try {
+ return Integer.valueOf(value);
+ } catch (NumberFormatException nfe) {
+ /* fall through */
+ }
+ }
+ return value;
+ }
+
+ public boolean isGreaterThan(VersionID vid) {
+ if (vid == null) {
+ return false;
+ }
+ return isGreaterThanOrEqualHelper(vid, false, true);
+ }
+
+ public boolean isGreaterThanOrEqual(VersionID vid) {
+ if (vid == null) {
+ return false;
+ }
+ return isGreaterThanOrEqualHelper(vid, true, true);
+ }
+
+ boolean isGreaterThanOrEqualTuple(VersionID vid) {
+ return isGreaterThanOrEqualHelper(vid, true, false);
+ }
+
+ /** Compares if 'this' is greater than vid */
+ private boolean isGreaterThanOrEqualHelper(VersionID vid,
+ boolean allowEqual, boolean useRest) {
+
+ if (useRest && _isCompound) {
+ if (!_rest.isGreaterThanOrEqualHelper(vid, allowEqual, true)) {
+ return false;
+ }
+ }
+ // Normalize the two strings
+ String[] t1 = normalize(_tuple, vid._tuple.length);
+ String[] t2 = normalize(vid._tuple, _tuple.length);
+
+ for (int i = 0; i < t1.length; i++) {
+ // Compare current element
+ Object e1 = getValueAsObject(t1[i]);
+ Object e2 = getValueAsObject(t2[i]);
+ if (e1.equals(e2)) {
+ // So far so good
+ } else {
+ if (e1 instanceof Integer && e2 instanceof Integer) {
+ // if both can be parsed as ints, compare ints
+ return ((Integer)e1).intValue() > ((Integer)e2).intValue();
+ } else {
+ if (e1 instanceof Integer) {
+ return false; // e1 (numeric) < e2 (non-numeric)
+ } else if (e2 instanceof Integer) {
+ return true; // e1 (non-numeric) > e2 (numeric)
+ }
+
+ String s1 = t1[i];
+ String s2 = t2[i];
+
+ return s1.compareTo(s2) > 0;
+ }
+
+ }
+ }
+ // If we get here, they are equal
+ return allowEqual;
+ }
+
+ /** Checks if 'this' is a prefix of vid */
+ private boolean isPrefixMatchTuple(VersionID vid) {
+
+ // Make sure that vid is at least as long as the prefix
+ String[] t2 = normalize(vid._tuple, _tuple.length);
+
+ for (int i = 0; i < _tuple.length; i++) {
+ Object e1 = _tuple[i];
+ Object e2 = t2[i];
+ if (e1.equals(e2)) {
+ // So far so good
+ } else {
+ // Not a prefix
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /** Normalize an array to a certain lengh */
+ private String[] normalize(String[] list, int minlength) {
+ if (list.length < minlength) {
+ // Need to do padding
+ String[] newlist = new String[minlength];
+ System.arraycopy(list, 0, newlist, 0, list.length);
+ Arrays.fill(newlist, list.length, newlist.length, "0");
+ return newlist;
+ } else {
+ return list;
+ }
+ }
+
+ @Override
+ public int compareTo(VersionID o) {
+ if (o == null || !(o instanceof VersionID)) {
+ return -1;
+ }
+ VersionID vid = o;
+ return equals(vid) ? 0 : (isGreaterThanOrEqual(vid) ? 1 : -1);
+ }
+
+ /** Show it as a string */
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < _tuple.length - 1; i++) {
+ sb.append(_tuple[i]);
+ sb.append('.');
+ }
+ if (_tuple.length > 0) {
+ sb.append(_tuple[_tuple.length - 1]);
+ }
+ if (_useGreaterThan) {
+ sb.append('+');
+ }
+ if (_usePrefixMatch) {
+ sb.append('*');
+ }
+ if (_isCompound) {
+ sb.append("&");
+ sb.append(_rest);
+ }
+ return sb.toString();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/demo/share/jpackage/JNLPConverter/src/jnlp/converter/parser/VersionString.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) 2006, 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.
+ *
+ * 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 jnlp.converter.parser;
+
+import java.util.List;
+import java.util.ArrayList;
+import java.util.StringTokenizer;
+
+/*
+ * Utility class that knows to handle version strings
+ * A version string is of the form:
+ *
+ * (version-id ('+'?) ' ') *
+ *
+ */
+public class VersionString {
+ private final ArrayList<VersionID> _versionIds;
+
+ /** Constructs a VersionString object from string */
+ public VersionString(String vs) {
+ _versionIds = new ArrayList<>();
+ if (vs != null) {
+ StringTokenizer st = new StringTokenizer(vs, " ", false);
+ while (st.hasMoreElements()) {
+ // Note: The VersionID class takes care of a postfixed '+'
+ _versionIds.add(new VersionID(st.nextToken()));
+ }
+ }
+ }
+
+ public VersionString(VersionID id) {
+ _versionIds = new ArrayList<>();
+ if (id != null) {
+ _versionIds.add(id);
+ }
+ }
+
+ public boolean isSimpleVersion() {
+ if (_versionIds.size() == 1) {
+ return _versionIds.get(0).isSimpleVersion();
+ }
+ return false;
+ }
+
+ /** Check if this VersionString object contains the VersionID m */
+ public boolean contains(VersionID m) {
+ for (int i = 0; i < _versionIds.size(); i++) {
+ VersionID vi = _versionIds.get(i);
+ boolean check = vi.match(m);
+ if (check) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /** Check if this VersionString object contains the VersionID m, given as a string */
+ public boolean contains(String versionid) {
+ return contains(new VersionID(versionid));
+ }
+
+ /** Check if this VersionString object contains anything greater than m */
+ public boolean containsGreaterThan(VersionID m) {
+ for (int i = 0; i < _versionIds.size(); i++) {
+ VersionID vi = _versionIds.get(i);
+ boolean check = vi.isGreaterThan(m);
+ if (check) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /** Check if this VersionString object contains anything greater than the VersionID m, given as a string */
+ public boolean containsGreaterThan(String versionid) {
+ return containsGreaterThan(new VersionID(versionid));
+ }
+
+ /** Check if the versionString 'vs' contains the VersionID 'vi' */
+ public static boolean contains(String vs, String vi) {
+ return (new VersionString(vs)).contains(vi);
+ }
+
+ /** Pretty-print object */
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < _versionIds.size(); i++) {
+ sb.append(_versionIds.get(i).toString());
+ sb.append(' ');
+ }
+ return sb.toString();
+ }
+
+ public List<VersionID> getAllVersionIDs() {
+ return _versionIds;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/demo/share/jpackage/JNLPConverter/src/jnlp/converter/parser/XMLFormat.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,659 @@
+/*
+ * Copyright (c) 2006, 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.
+ *
+ * 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 jnlp.converter.parser;
+
+import java.net.URL;
+import java.util.Arrays;
+import java.util.ArrayList;
+import jnlp.converter.JNLPConverter;
+import jnlp.converter.parser.exception.MissingFieldException;
+import jnlp.converter.parser.exception.BadFieldException;
+import jnlp.converter.parser.exception.JNLParseException;
+import jnlp.converter.parser.xml.XMLEncoding;
+import jnlp.converter.parser.xml.XMLParser;
+import jnlp.converter.parser.xml.XMLNode;
+import jnlp.converter.parser.JNLPDesc.AssociationDesc;
+import jnlp.converter.parser.JNLPDesc.IconDesc;
+import jnlp.converter.parser.JNLPDesc.InformationDesc;
+import jnlp.converter.parser.JNLPDesc.ShortcutDesc;
+import jnlp.converter.parser.ResourcesDesc.JARDesc;
+import jnlp.converter.parser.ResourcesDesc.JREDesc;
+import jnlp.converter.Log;
+import jnlp.converter.parser.ResourcesDesc.ExtensionDesc;
+import jnlp.converter.parser.ResourcesDesc.PropertyDesc;
+import org.xml.sax.SAXParseException;
+
+public class XMLFormat {
+
+ public static XMLNode parseBits(byte[] bits) throws JNLParseException {
+ return parse(decode(bits));
+ }
+
+ private static String decode(byte[] bits) throws JNLParseException {
+ try {
+ return XMLEncoding.decodeXML(bits);
+ } catch (Exception e) {
+ throw new JNLParseException(e,
+ "exception determining encoding of jnlp file", 0);
+ }
+ }
+
+ private static XMLNode parse(String source) throws JNLParseException {
+ try {
+ return (new XMLParser(source).parse());
+ } catch (SAXParseException spe) {
+ throw new JNLParseException(spe,
+ "exception parsing jnlp file", spe.getLineNumber());
+ } catch (Exception e) {
+ throw new JNLParseException(e,
+ "exception parsing jnlp file", 0);
+ }
+ }
+
+ /**
+ * thisCodebase, if set, is used to determine the codebase,
+ * if JNLP codebase is not absolute.
+ *
+ * @param thisCodebase base URL of this JNLPDesc location
+ */
+ public static JNLPDesc parse(byte[] bits, URL thisCodebase, String jnlp)
+ throws Exception {
+
+ JNLPDesc jnlpd = new JNLPDesc();
+ String source = decode(bits).trim();
+ XMLNode root = parse(source);
+
+ if (root == null || root.getName() == null) {
+ throw new JNLParseException(null, null, 0);
+ }
+
+ // Check that root element is a <jnlp> tag
+ if (!root.getName().equals("jnlp")) {
+ throw (new MissingFieldException(source, "<jnlp>"));
+ }
+
+ // Read <jnlp> attributes (path is empty, i.e., "")
+ // (spec, version, codebase, href)
+ String specVersion = XMLUtils.getAttribute(root, "", "spec", "1.0+");
+ jnlpd.setSpecVersion(specVersion);
+ String version = XMLUtils.getAttribute(root, "", "version");
+ jnlpd.setVersion(version);
+
+ // Make sure the codebase URL ends with a '/'.
+ //
+ // Regarding the JNLP spec,
+ // the thisCodebase is used to determine the codebase.
+ // codebase = new URL(thisCodebase, codebase)
+ URL codebase = GeneralUtil.asPathURL(XMLUtils.getAttributeURL(source,
+ thisCodebase, root, "", "codebase"));
+ if (codebase == null && thisCodebase != null) {
+ codebase = thisCodebase;
+ }
+ jnlpd.setCodebase(codebase.toExternalForm());
+
+ // Get href for JNLP file
+ URL href = XMLUtils.getAttributeURL(source, codebase, root, "", "href");
+ jnlpd.setHref(href.toExternalForm());
+
+ // Read <security> attributes
+ if (XMLUtils.isElementPath(root, "<security><all-permissions>")) {
+ jnlpd.setIsSandbox(false);
+ } else if (XMLUtils.isElementPath(root,
+ "<security><j2ee-application-client-permissions>")) {
+ jnlpd.setIsSandbox(false);
+ }
+
+ // We can be fxapp, and also be applet, or application, or neither
+ boolean isFXApp = false;
+ boolean isApplet = false;
+ if (XMLUtils.isElementPath(root, "<javafx-desc>")) {
+ // no new type for javafx-desc - needs one of the others
+ buildFXAppDesc(source, root, "<javafx-desc>", jnlpd);
+ jnlpd.setIsFXApp(true);
+ isFXApp = true;
+ }
+
+ /*
+ * Note - the jnlp specification says there must be exactly one of
+ * the descriptor types. This code has always violated (or at least
+ * not checked for) that condition.
+ * Instead it uses precedent order app, component, installer, applet
+ * and ignores any other descriptors given.
+ */
+ if (XMLUtils.isElementPath(root, "<application-desc>")) {
+ buildApplicationDesc(source, root, jnlpd);
+ } else if (XMLUtils.isElementPath(root, "<component-desc>")) {
+ jnlpd.setIsLibrary(true);
+ } else if (XMLUtils.isElementPath(root, "<installer-desc>")) {
+ Log.warning("<installer-desc> is not supported and will be ignored in " + jnlp);
+ jnlpd.setIsInstaller(true);
+ } else if (XMLUtils.isElementPath(root, "<applet-desc>")) {
+ isApplet = true;
+ } else {
+ if (!isFXApp) {
+ throw (new MissingFieldException(source,
+ "<jnlp>(<application-desc>|<applet-desc>|" +
+ "<installer-desc>|<component-desc>)"));
+ }
+ }
+
+ if (isApplet && !isFXApp) {
+ Log.error("Applet based applications deployed with <applet-desc> element are not supported.");
+ }
+
+ if (!jnlpd.isLibrary() && !jnlpd.isInstaller()) {
+ buildInformationDesc(source, codebase, root, jnlpd);
+ }
+
+ if (!jnlpd.isInstaller()) {
+ buildResourcesDesc(source, codebase, root, false, jnlpd);
+ }
+
+ if (!jnlpd.isLibrary() && !jnlpd.isInstaller()) {
+ jnlpd.parseResourceDesc();
+ }
+
+ if (!jnlpd.isInstaller()) {
+ if (jnlpd.isSandbox()) {
+ if (jnlpd.isLibrary()) {
+ Log.warning(jnlp + " is sandbox extension. JNLPConverter does not support sandbox environment and converted application will run without security manager.");
+ } else {
+ Log.warning("This is sandbox Web-Start application. JNLPConverter does not support sandbox environment and converted application will run without security manager.");
+ }
+ }
+ }
+
+ return jnlpd;
+ }
+
+ /**
+ * Create a combine informationDesc in the two informationDesc.
+ * The information present in id1 overwrite the information present in id2
+ */
+ private static InformationDesc combineInformationDesc(
+ InformationDesc id1, InformationDesc id2) {
+ if (id1 == null) {
+ return id2;
+ }
+ if (id2 == null) {
+ return id1;
+ }
+
+ String t1 = id1.getTitle();
+ String title = (t1 != null && t1.length() > 0) ?
+ t1 : id2.getTitle();
+ String v1 = id1.getVendor();
+ String vendor = (v1 != null && v1.length() > 0) ?
+ v1 : id2.getVendor();
+
+ /** Copy descriptions */
+ String[] descriptions = new String[InformationDesc.NOF_DESC];
+ for (int i = 0; i < descriptions.length; i++) {
+ descriptions[i] = (id1.getDescription(i) != null)
+ ? id1.getDescription(i) : id2.getDescription(i);
+ }
+
+ /** Icons */
+ ArrayList<IconDesc> iconList = new ArrayList<>();
+ if (id2.getIcons() != null) {
+ iconList.addAll(Arrays.asList(id2.getIcons()));
+ }
+ if (id1.getIcons() != null) {
+ iconList.addAll(Arrays.asList(id1.getIcons()));
+ }
+ IconDesc[] icons = new IconDesc[iconList.size()];
+ icons = iconList.toArray(icons);
+
+ ShortcutDesc hints = (id1.getShortcut() != null) ?
+ id1.getShortcut() : id2.getShortcut();
+
+ AssociationDesc[] asd = ( AssociationDesc[] ) addArrays(
+ (Object[])id1.getAssociations(), (Object[])id2.getAssociations());
+
+ return new InformationDesc(title,
+ vendor,
+ descriptions,
+ icons,
+ hints,
+ asd);
+ }
+
+ /** Extract data from <information> tag */
+ private static void buildInformationDesc(final String source, final URL codebase, XMLNode root, JNLPDesc jnlpd)
+ throws MissingFieldException, BadFieldException {
+ final ArrayList<InformationDesc> list = new ArrayList<>();
+
+ // Iterates over all <information> nodes ignoring the type
+ XMLUtils.visitElements(root,
+ "<information>", new XMLUtils.ElementVisitor() {
+ @Override
+ public void visitElement(XMLNode e) throws
+ BadFieldException, MissingFieldException {
+
+ // Check for right os, arch, and locale
+ String[] os = GeneralUtil.getStringList(
+ XMLUtils.getAttribute(e, "", "os", null));
+ String[] arch = GeneralUtil.getStringList(
+ XMLUtils.getAttribute(e, "", "arch", null));
+ String[] locale = GeneralUtil.getStringList(
+ XMLUtils.getAttribute(e, "", "locale", null));
+ if (GeneralUtil.prefixMatchStringList(
+ os, GeneralUtil.getOSFullName()) &&
+ GeneralUtil.prefixMatchArch(arch) &&
+ matchDefaultLocale(locale))
+ {
+ // Title, vendor
+ String title = XMLUtils.getElementContents(e, "<title>");
+ String vendor = XMLUtils.getElementContents(e, "<vendor>");
+
+ // Descriptions
+ String[] descriptions =
+ new String[InformationDesc.NOF_DESC];
+ descriptions[InformationDesc.DESC_DEFAULT] =
+ XMLUtils.getElementContentsWithAttribute(
+ e, "<description>", "kind", "", null);
+ descriptions[InformationDesc.DESC_ONELINE] =
+ XMLUtils.getElementContentsWithAttribute(
+ e, "<description>", "kind", "one-line", null);
+ descriptions[InformationDesc.DESC_SHORT] =
+ XMLUtils.getElementContentsWithAttribute(
+ e, "<description>", "kind", "short", null);
+ descriptions[InformationDesc.DESC_TOOLTIP] =
+ XMLUtils.getElementContentsWithAttribute(
+ e, "<description>", "kind", "tooltip", null);
+
+ // Icons
+ IconDesc[] icons = getIconDescs(source, codebase, e);
+
+ // Shortcut hints
+ ShortcutDesc shortcuts = getShortcutDesc(e);
+
+ // Association hints
+ AssociationDesc[] associations = getAssociationDesc(
+ source, codebase, e);
+
+ list.add(new InformationDesc(
+ title, vendor, descriptions, icons,
+ shortcuts, associations));
+ }
+ }
+ });
+
+ /* Combine all information desc. information in a single one for
+ * the current locale using the following priorities:
+ * 1. locale == language_country_variant
+ * 2. locale == lauguage_country
+ * 3. locale == lauguage
+ * 4. no or empty locale
+ */
+ InformationDesc normId = new InformationDesc(null, null, null, null, null, null);
+ for (InformationDesc id : list) {
+ normId = combineInformationDesc(id, normId);
+ }
+
+ jnlpd.setTitle(normId.getTitle());
+ jnlpd.setVendor(normId.getVendor());
+ jnlpd.setDescriptions(normId.getDescription());
+ jnlpd.setIcons(normId.getIcons());
+ jnlpd.setShortcuts(normId.getShortcut());
+ jnlpd.setAssociations(normId.getAssociations());
+ }
+
+ private static Object[] addArrays (Object[] a1, Object[] a2) {
+ if (a1 == null) {
+ return a2;
+ }
+ if (a2 == null) {
+ return a1;
+ }
+ ArrayList<Object> list = new ArrayList<>();
+ int i;
+ for (i=0; i<a1.length; list.add(a1[i++]));
+ for (i=0; i<a2.length; list.add(a2[i++]));
+ return list.toArray(a1);
+ }
+
+ public static boolean matchDefaultLocale(String[] localeStr) {
+ return GeneralUtil.matchLocale(localeStr, GeneralUtil.getDefaultLocale());
+ }
+
+ /** Extract data from <resources> tag. There is only one. */
+ static void buildResourcesDesc(final String source,
+ final URL codebase, XMLNode root, final boolean ignoreJres, JNLPDesc jnlpd)
+ throws MissingFieldException, BadFieldException {
+ // Extract classpath directives
+ final ResourcesDesc rdesc = new ResourcesDesc();
+
+ // Iterate over all entries
+ XMLUtils.visitElements(root, "<resources>",
+ new XMLUtils.ElementVisitor() {
+ @Override
+ public void visitElement(XMLNode e)
+ throws MissingFieldException, BadFieldException {
+ // Check for right os, archictecture, and locale
+ String[] os = GeneralUtil.getStringList(
+ XMLUtils.getAttribute(e, "", "os", null));
+ final String arch = XMLUtils.getAttribute(e, "", "arch", null);
+ String[] locale = GeneralUtil.getStringList(
+ XMLUtils.getAttribute(e, "", "locale", null));
+ if (GeneralUtil.prefixMatchStringList(
+ os, GeneralUtil.getOSFullName())
+ && matchDefaultLocale(locale)) {
+ // Now visit all children in this node
+ XMLUtils.visitChildrenElements(e,
+ new XMLUtils.ElementVisitor() {
+ @Override
+ public void visitElement(XMLNode e2)
+ throws MissingFieldException, BadFieldException {
+ handleResourceElement(source, codebase,
+ e2, rdesc, ignoreJres, arch, jnlpd);
+ }
+ });
+ }
+ }
+ });
+
+ if (!rdesc.isEmpty()) {
+ jnlpd.setResourcesDesc(rdesc);
+ }
+ }
+
+ private static IconDesc[] getIconDescs(final String source,
+ final URL codebase, XMLNode e)
+ throws MissingFieldException, BadFieldException {
+ final ArrayList<IconDesc> answer = new ArrayList<>();
+ XMLUtils.visitElements(e, "<icon>", new XMLUtils.ElementVisitor() {
+ @Override
+ public void visitElement(XMLNode icon) throws
+ MissingFieldException, BadFieldException {
+ String kindStr = XMLUtils.getAttribute(icon, "", "kind", "");
+ URL href = XMLUtils.getRequiredURL(source, codebase, icon, "", "href");
+
+ if (href != null) {
+ if (!JNLPConverter.isIconSupported(href.toExternalForm())) {
+ return;
+ }
+ }
+
+ int kind;
+ if (kindStr == null || kindStr.isEmpty() || kindStr.equals("default")) {
+ kind = IconDesc.ICON_KIND_DEFAULT;
+ } else if (kindStr.equals("shortcut")) {
+ kind = IconDesc.ICON_KIND_SHORTCUT;
+ } else {
+ Log.warning("Ignoring unsupported icon \"" + href + "\" with kind \"" + kindStr + "\".");
+ return;
+ }
+
+ answer.add(new IconDesc(href, kind));
+ }
+ });
+ return answer.toArray(new IconDesc[answer.size()]);
+ }
+
+ private static ShortcutDesc getShortcutDesc(XMLNode e)
+ throws MissingFieldException, BadFieldException {
+ final ArrayList<ShortcutDesc> shortcuts = new ArrayList<>();
+
+ XMLUtils.visitElements(e, "<shortcut>", new XMLUtils.ElementVisitor() {
+ @Override
+ public void visitElement(XMLNode shortcutNode)
+ throws MissingFieldException, BadFieldException {
+ boolean desktopHinted =
+ XMLUtils.isElementPath(shortcutNode, "<desktop>");
+ boolean menuHinted =
+ XMLUtils.isElementPath(shortcutNode, "<menu>");
+ String submenuHinted =
+ XMLUtils.getAttribute(shortcutNode, "<menu>", "submenu");
+ shortcuts.add(new ShortcutDesc(desktopHinted, menuHinted, submenuHinted));
+ }
+ });
+
+ if (shortcuts.size() > 0) {
+ return shortcuts.get(0);
+ }
+ return null;
+ }
+
+ private static AssociationDesc[] getAssociationDesc(final String source,
+ final URL codebase, XMLNode e)
+ throws MissingFieldException, BadFieldException {
+ final ArrayList<AssociationDesc> answer = new ArrayList<>();
+ XMLUtils.visitElements(e, "<association>",
+ new XMLUtils.ElementVisitor() {
+ @Override
+ public void visitElement(XMLNode node)
+ throws MissingFieldException, BadFieldException {
+
+ String extensions = XMLUtils.getAttribute(
+ node, "", "extensions");
+
+ String mimeType = XMLUtils.getAttribute(
+ node, "", "mime-type");
+ String description = XMLUtils.getElementContents(
+ node, "<description>");
+
+ URL icon = XMLUtils.getAttributeURL(
+ source, codebase, node, "<icon>", "href");
+
+ if (!JNLPConverter.isIconSupported(icon.toExternalForm())) {
+ icon = null;
+ }
+
+ if (extensions == null && mimeType == null) {
+ throw new MissingFieldException(source,
+ "<association>(<extensions><mime-type>)");
+ } else if (extensions == null) {
+ throw new MissingFieldException(source,
+ "<association><extensions>");
+ } else if (mimeType == null) {
+ throw new MissingFieldException(source,
+ "<association><mime-type>");
+ }
+
+ // don't support uppercase extension and mime-type on gnome.
+ if ("gnome".equals(System.getProperty("sun.desktop"))) {
+ extensions = extensions.toLowerCase();
+ mimeType = mimeType.toLowerCase();
+ }
+
+ answer.add(new AssociationDesc(extensions, mimeType,
+ description, icon));
+ }
+ });
+ return answer.toArray(
+ new AssociationDesc[answer.size()]);
+ }
+
+ /** Handle the individual entries in a resource desc */
+ private static void handleResourceElement(String source, URL codebase,
+ XMLNode e, ResourcesDesc rdesc, boolean ignoreJres, String arch, JNLPDesc jnlpd)
+ throws MissingFieldException, BadFieldException {
+
+ String tag = e.getName();
+
+ boolean matchArch = GeneralUtil.prefixMatchArch(
+ GeneralUtil.getStringList(arch));
+
+
+ if (matchArch && (tag.equals("jar") || tag.equals("nativelib"))) {
+ /*
+ * jar/nativelib elements
+ */
+ URL href = XMLUtils.getRequiredURL(source, codebase, e, "", "href");
+ String version = XMLUtils.getAttribute(e, "", "version", null);
+
+ String mainStr = XMLUtils.getAttribute(e, "", "main");
+ boolean isNativeLib = tag.equals("nativelib");
+
+ boolean isMain = "true".equalsIgnoreCase(mainStr);
+
+ JARDesc jd = new JARDesc(href, version, isMain, isNativeLib, rdesc);
+ rdesc.addResource(jd);
+ } else if (matchArch && tag.equals("property")) {
+ /*
+ * property tag
+ */
+ String name = XMLUtils.getRequiredAttribute(source, e, "", "name");
+ String value = XMLUtils.getRequiredAttributeEmptyOK(
+ source, e, "", "value");
+
+ rdesc.addResource(new PropertyDesc(name, value));
+ } else if (matchArch && tag.equals("extension")) {
+ URL href = XMLUtils.getRequiredURL(source, codebase, e, "", "href");
+ String version = XMLUtils.getAttribute(e, "", "version", null);
+ rdesc.addResource(new ExtensionDesc(href, version));
+ } else if ((tag.equals("java") || tag.equals("j2se")) && !ignoreJres) {
+ /*
+ * j2se element
+ */
+ String version =
+ XMLUtils.getRequiredAttribute(source, e, "", "version");
+ String minheapstr =
+ XMLUtils.getAttribute(e, "", "initial-heap-size");
+ String maxheapstr =
+ XMLUtils.getAttribute(e, "", "max-heap-size");
+
+ String vmargs =
+ XMLUtils.getAttribute(e, "", "java-vm-args");
+
+ if (jnlpd.isJRESet()) {
+ if (vmargs == null) {
+ vmargs = "none";
+ }
+ Log.warning("Ignoring repeated element <" + tag + "> with version " + version +
+ " and java-vm-args: " + vmargs);
+ return;
+ }
+
+ long minheap = GeneralUtil.heapValToLong(minheapstr);
+ long maxheap = GeneralUtil.heapValToLong(maxheapstr);
+
+ ResourcesDesc cbs = null;
+ buildResourcesDesc(source, codebase, e, true, null);
+
+ // JRE
+ JREDesc jreDesc = new JREDesc(
+ version,
+ minheap,
+ maxheap,
+ vmargs,
+ cbs,
+ arch);
+
+ rdesc.addResource(jreDesc);
+
+ jnlpd.setIsJRESet(true);
+ }
+ }
+
+ /** Extract data from the application-desc tag */
+ private static void buildApplicationDesc(final String source,
+ XMLNode root, JNLPDesc jnlpd) throws MissingFieldException, BadFieldException {
+
+ String mainclass = XMLUtils.getClassName(source, root,
+ "<application-desc>", "main-class", false);
+ String appType = XMLUtils.getAttribute(root, "<application-desc>",
+ "type", "Java");
+ String progressclass = XMLUtils.getClassName(source, root,
+ "<application-desc>", "progress-class", false);
+ if (progressclass != null && !progressclass.isEmpty()) {
+ Log.warning("JNLPConverter does not support progress indication. \"" + progressclass + "\" will not be loaded and will be ignored.");
+ }
+
+ if (!("Java".equalsIgnoreCase(appType) ||
+ "JavaFx".equalsIgnoreCase(appType))) {
+ throw new BadFieldException(source, XMLUtils.getPathString(root) +
+ "<application-desc>type", appType);
+ }
+
+ if ("JavaFx".equalsIgnoreCase(appType)) {
+ jnlpd.setIsFXApp(true);
+ }
+
+ XMLUtils.visitElements(root, "<application-desc><argument>", new XMLUtils.ElementVisitor() {
+ @Override
+ public void visitElement(XMLNode e) throws MissingFieldException, BadFieldException {
+ String arg = XMLUtils.getElementContents(e, "", null);
+ if (arg == null) {
+ throw new BadFieldException(source, XMLUtils.getPathString(e), "");
+ }
+ jnlpd.addArguments(arg);
+ }
+ });
+
+ XMLUtils.visitElements(root, "<application-desc><param>",
+ new XMLUtils.ElementVisitor() {
+ @Override
+ public void visitElement(XMLNode e) throws MissingFieldException,
+ BadFieldException {
+ String pn = XMLUtils.getRequiredAttribute(
+ source, e, "", "name");
+ String pv = XMLUtils.getRequiredAttributeEmptyOK(
+ source, e, "", "value");
+ jnlpd.setProperty(pn, pv);
+ }
+ });
+ jnlpd.setMainClass(mainclass, false);
+ }
+
+ /** Extract data from the javafx-desc tag */
+ private static void buildFXAppDesc(final String source,
+ XMLNode root, String element, JNLPDesc jnlpd)
+ throws MissingFieldException, BadFieldException {
+ String mainclass = XMLUtils.getClassName(source, root, element,
+ "main-class", true);
+ String name = XMLUtils.getRequiredAttribute(source, root,
+ "<javafx-desc>", "name");
+
+ /* extract arguments */
+ XMLUtils.visitElements(root, "<javafx-desc><argument>", new XMLUtils.ElementVisitor() {
+ @Override
+ public void visitElement(XMLNode e) throws MissingFieldException, BadFieldException {
+ String arg = XMLUtils.getElementContents(e, "", null);
+ if (arg == null) {
+ throw new BadFieldException(source, XMLUtils.getPathString(e), "");
+ }
+ jnlpd.addArguments(arg);
+ }
+ });
+
+ /* extract parameters */
+ XMLUtils.visitElements(root, "<javafx-desc><param>",
+ new XMLUtils.ElementVisitor() {
+ @Override
+ public void visitElement(XMLNode e) throws MissingFieldException,
+ BadFieldException {
+ String pn = XMLUtils.getRequiredAttribute(
+ source, e, "", "name");
+ String pv = XMLUtils.getRequiredAttributeEmptyOK(
+ source, e, "", "value");
+ jnlpd.setProperty(pn, pv);
+ }
+ });
+
+ jnlpd.setMainClass(mainclass, true);
+ jnlpd.setName(name);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/demo/share/jpackage/JNLPConverter/src/jnlp/converter/parser/XMLUtils.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,328 @@
+/*
+ * Copyright (c) 2006, 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.
+ *
+ * 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 jnlp.converter.parser;
+
+import java.net.URL;
+import java.net.MalformedURLException;
+
+import jnlp.converter.parser.exception.BadFieldException;
+import jnlp.converter.parser.exception.MissingFieldException;
+import jnlp.converter.parser.xml.XMLNode;
+
+/** Contains handy methods for looking up information
+ * stored in XMLNodes.
+ */
+public class XMLUtils {
+
+ /** Returns the value of an integer attribute */
+ public static int getIntAttribute(String source, XMLNode root, String path, String name, int defaultvalue)
+ throws BadFieldException {
+ String value = getAttribute(root, path, name);
+ if (value == null) {
+ return defaultvalue;
+ }
+
+ try {
+ return Integer.parseInt(value);
+ } catch (NumberFormatException nfe) {
+ throw new BadFieldException(source, getPathString(root) + path + name, value);
+ }
+ }
+
+ /** Returns the value of a given attribute, or null if not set */
+ public static String getAttribute(XMLNode root, String path, String name)
+ throws BadFieldException {
+ return getAttribute(root, path, name, null);
+ }
+
+ /** Returns the value of a given attribute */
+ public static String getRequiredAttributeEmptyOK(String source,
+ XMLNode root, String path, String name) throws MissingFieldException {
+ String value = null;
+ XMLNode elem = findElementPath(root, path);
+ if (elem != null) {
+ value = elem.getAttribute(name);
+ }
+ if (value == null) {
+ throw new MissingFieldException(source,
+ getPathString(root)+ path + name);
+ }
+ return value;
+ }
+
+ /*** Returns the value of an attribute, which must be a valid class name */
+ public static String getClassName(String source, XMLNode root,
+ String path, String name, boolean required)
+ throws BadFieldException, MissingFieldException {
+
+ String className;
+ if (required) {
+ className = getRequiredAttribute(source, root, path, name);
+ } else {
+ className = getAttribute(root, path, name);
+ }
+ if (className != null && className.endsWith(".class")) {
+ int i = className.lastIndexOf(".class");
+ String cname = className.substring(0, i);
+ return cname;
+ }
+ return className;
+ }
+
+ /** Returns the value of a given attribute, or null if not set */
+ public static String getRequiredAttribute(String source, XMLNode root,
+ String path, String name) throws MissingFieldException, BadFieldException {
+ String s = getAttribute(root, path, name, null);
+ if (s == null) {
+ throw new MissingFieldException(source, getPathString(root) + path + name);
+ }
+ s = s.trim();
+ return (s.length() == 0) ? null : s;
+ }
+
+ /** Returns the value of a given attribute, or the default value 'def' if not set */
+ public static String getAttribute(XMLNode root, String path, String name,
+ String def) throws BadFieldException {
+ XMLNode elem = findElementPath(root, path);
+ if (elem == null) {
+ return def;
+ }
+ String value = elem.getAttribute(name);
+ return (value == null || value.length() == 0) ? def : value;
+ }
+
+ /** Expands a URL into an absolute URL from a relative URL */
+ public static URL getAttributeURL(String source, URL base, XMLNode root, String path, String name) throws BadFieldException {
+ String value = getAttribute(root, path, name);
+ if (value == null) return null;
+ try {
+ if (value.startsWith("jar:")) {
+ int bang = value.indexOf("!/");
+ if (bang > 0) {
+ String entry = value.substring(bang);
+ String urlString = value.substring(4, bang);
+ URL url = (base == null) ?
+ new URL(urlString) : new URL(base, urlString);
+ return new URL("jar:" + url.toString() + entry);
+ }
+ }
+ return (base == null) ? new URL(value) : new URL(base, value);
+ } catch(MalformedURLException mue) {
+ if (mue.getMessage().contains("https")) {
+ throw new BadFieldException(source, "<jnlp>", "https");
+ }
+ throw new BadFieldException(source, getPathString(root) + path + name, value);
+ }
+ }
+
+ /** Returns the value of an attribute as a URL or null if not set */
+ public static URL getAttributeURL(String source, XMLNode root, String path, String name) throws BadFieldException {
+ return getAttributeURL(source, null, root, path, name);
+ }
+
+ public static URL getRequiredURL(String source, URL base, XMLNode root, String path, String name) throws BadFieldException, MissingFieldException {
+ URL url = getAttributeURL(source, base, root, path, name);
+ if (url == null) {
+ throw new MissingFieldException(source, getPathString(root) + path + name);
+ }
+ return url;
+ }
+
+ /** Returns the value of an attribute as a URL. Throws a MissingFieldException if the
+ * attribute is not defined
+ */
+ public static URL getRequiredURL(String source, XMLNode root, String path, String name) throws BadFieldException, MissingFieldException {
+ return getRequiredURL(source, null, root, path, name);
+ }
+
+ /** Returns true if the path exists in the document, otherwise false */
+ public static boolean isElementPath(XMLNode root, String path) {
+ return findElementPath(root, path) != null;
+ }
+
+ public static URL getElementURL(String source, XMLNode root, String path) throws BadFieldException {
+ String value = getElementContents(root, path);
+ try {
+ return new URL(value);
+ } catch(MalformedURLException mue) {
+ throw new BadFieldException(source, getPathString(root) + path, value);
+ }
+ }
+
+ /** Returns a string describing the current location in the DOM */
+ public static String getPathString(XMLNode e) {
+ return (e == null || !(e.isElement())) ? "" : getPathString(e.getParent()) + "<" + e.getName() + ">";
+ }
+
+ /** Returns the contents of an element with the given path and an attribute matching a specific value. Returns
+ * NULL if not found
+ */
+ public static String getElementContentsWithAttribute(XMLNode root, String path, String attr, String val, String defaultvalue)
+ throws BadFieldException, MissingFieldException {
+ XMLNode e = getElementWithAttribute(root, path, attr, val);
+ if (e == null) {
+ return defaultvalue;
+ }
+ return getElementContents(e, "", defaultvalue);
+ }
+
+ public static URL getAttributeURLWithAttribute(String source, XMLNode root, String path, String attrcond, String val,
+ String name, URL defaultvalue)
+ throws BadFieldException, MissingFieldException {
+ XMLNode e = getElementWithAttribute(root, path, attrcond, val);
+ if (e == null) {
+ return defaultvalue;
+ }
+ URL url = getAttributeURL(source, e, "", name);
+ if (url == null) {
+ return defaultvalue;
+ }
+ return url;
+ }
+
+ /** Returns an element with the given path and an attribute matching a specific value. Returns
+ * NULL if not found
+ */
+ public static XMLNode getElementWithAttribute(XMLNode root, String path, final String attr, final String val)
+ throws BadFieldException, MissingFieldException {
+ final XMLNode[] result = {null};
+ visitElements(root, path, new ElementVisitor() {
+ public void visitElement(XMLNode e) throws BadFieldException, MissingFieldException {
+ if (result[0] == null && e.getAttribute(attr).equals(val)) {
+ result[0] = e;
+ }
+ }
+ });
+ return result[0];
+ }
+
+ /** Like getElementContents(...) but with a defaultValue of null */
+ public static String getElementContents(XMLNode root, String path) {
+ return getElementContents(root, path, null);
+ }
+
+ /** Returns the value of the last element tag in the path, e.g., <..><tag>value</tag>. The DOM is assumes
+ * to be normalized. If no value is found, the defaultvalue is returned
+ */
+ public static String getElementContents(XMLNode root, String path, String defaultvalue) {
+ XMLNode e = findElementPath(root, path);
+ if (e == null) {
+ return defaultvalue;
+ }
+ XMLNode n = e.getNested();
+ if (n != null && !n.isElement()) {
+ return n.getName();
+ }
+ return defaultvalue;
+ }
+
+ /** Parses a path string of the form <tag1><tag2><tag3> and returns the specific Element
+ * node for that tag, or null if it does not exist. If multiple elements exists with same
+ * path the first is returned
+ */
+ public static XMLNode findElementPath(XMLNode elem, String path) {
+ // End condition. Root null -> path does not exist
+ if (elem == null) {
+ return null;
+ }
+
+ // End condition. String empty, return current root
+ if (path == null || path.length() == 0) {
+ return elem;
+ }
+
+ // Strip of first tag
+ int idx = path.indexOf('>');
+ if (!(path.charAt(0) == '<')) {
+ throw new IllegalArgumentException("bad path. Missing begin tag");
+ }
+ if (idx == -1) {
+ throw new IllegalArgumentException("bad path. Missing end tag");
+ }
+ String head = path.substring(1, idx);
+ String tail = path.substring(idx + 1);
+ return findElementPath(findChildElement(elem, head), tail);
+ }
+
+ /** Returns an child element with the current tag name or null. */
+ public static XMLNode findChildElement(XMLNode elem, String tag) {
+ XMLNode n = elem.getNested();
+ while (n != null) {
+ if (n.isElement() && n.getName().equals(tag)) {
+ return n;
+ }
+ n = n.getNext();
+ }
+ return null;
+ }
+
+ /** Iterator class */
+ public abstract static class ElementVisitor {
+ abstract public void visitElement(XMLNode e) throws BadFieldException, MissingFieldException;
+ }
+
+ /** Visits all elements which matches the <path>. The iteration is only
+ * done on the last element in the path.
+ */
+ public static void visitElements(XMLNode root, String path, ElementVisitor ev)
+ throws BadFieldException, MissingFieldException {
+ // Get last element in path
+ int idx = path.lastIndexOf('<');
+ if (idx == -1) {
+ throw new IllegalArgumentException(
+ "bad path. Must contain atleast one tag");
+ }
+ if (path.length() == 0 || path.charAt(path.length() - 1) != '>') {
+ throw new IllegalArgumentException("bad path. Must end with a >");
+ }
+ String head = path.substring(0, idx);
+ String tag = path.substring(idx + 1, path.length() - 1);
+
+ XMLNode elem = findElementPath(root, head);
+ if (elem == null) {
+ return;
+ }
+
+ // Iterate through all child nodes
+ XMLNode n = elem.getNested();
+ while (n != null) {
+ if (n.isElement() && n.getName().equals(tag)) {
+ ev.visitElement(n);
+ }
+ n = n.getNext();
+ }
+ }
+
+ public static void visitChildrenElements(XMLNode elem, ElementVisitor ev)
+ throws BadFieldException, MissingFieldException {
+ // Iterate through all child nodes
+ XMLNode n = elem.getNested();
+ while (n != null) {
+ if (n.isElement()) {
+ ev.visitElement(n);
+ }
+ n = n.getNext();
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/demo/share/jpackage/JNLPConverter/src/jnlp/converter/parser/exception/BadFieldException.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2006, 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.
+ *
+ * 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 jnlp.converter.parser.exception;
+
+public class BadFieldException extends Exception {
+
+ private final String _field;
+ private final String _value;
+
+ public BadFieldException(String source, String field, String value) {
+ super();
+ _value = value;
+ _field = field;
+ }
+
+ /**
+ * Returns the name of the offending field
+ */
+ public String getField() {
+ return _field;
+ }
+
+ /**
+ * Returns the value of the offending field
+ */
+ public String getValue() {
+ return _value;
+ }
+
+ /**
+ * toString implementation
+ */
+ @Override
+ public String toString() {
+ return "BadFieldException[ " + getField() + "," + getValue() + "]";
+ }
+
+ @Override
+ public String getMessage(){
+ return toString();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/demo/share/jpackage/JNLPConverter/src/jnlp/converter/parser/exception/JNLParseException.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2006, 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.
+ *
+ * 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 jnlp.converter.parser.exception;
+
+/**
+ * Exception thrown if a parse error occurred when interpreting
+ * the launch descriptor
+ */
+
+public class JNLParseException extends Exception {
+
+ private final String _msg;
+ private final int _line;
+
+ public JNLParseException(Exception exception, String msg, int line) {
+ super(exception);
+ _msg = msg;
+ _line = line;
+ }
+
+ @Override
+ public String getMessage() {
+ return _msg;
+ }
+
+ private int getLine() {
+ return _line;
+ }
+
+ @Override
+ public String toString() {
+ return "JNLParseException[ " + getMessage() + "] at " + getLine();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/demo/share/jpackage/JNLPConverter/src/jnlp/converter/parser/exception/MissingFieldException.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2006, 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.
+ *
+ * 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 jnlp.converter.parser.exception;
+
+public class MissingFieldException extends Exception {
+
+ private final String _field;
+
+ public MissingFieldException(String source, String field) {
+ super();
+ _field = field;
+ }
+
+ /**
+ * Returns the name of the offending field
+ */
+ private String getField() {
+ return _field;
+ }
+
+ /**
+ * toString implementation
+ */
+ @Override
+ public String toString() {
+ return "MissingFieldException[ " + getField() + "]";
+ }
+
+ @Override
+ public String getMessage(){
+ return toString();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/demo/share/jpackage/JNLPConverter/src/jnlp/converter/parser/xml/XMLAttribute.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2006, 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.
+ *
+ * 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 jnlp.converter.parser.xml;
+
+/**
+ * Class that contains information about a specific attribute
+ */
+public class XMLAttribute {
+
+ private final String _name;
+ private final String _value;
+ private XMLAttribute _next;
+
+ public XMLAttribute(String name, String value) {
+ _name = XMLNode.stripNameSpace(name);
+ _value = value;
+ }
+
+ public String getName() {
+ return _name;
+ }
+
+ public String getValue() {
+ return _value;
+ }
+
+ public XMLAttribute getNext() {
+ return _next;
+ }
+
+ public void setNext(XMLAttribute next) {
+ _next = next;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/demo/share/jpackage/JNLPConverter/src/jnlp/converter/parser/xml/XMLEncoding.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,342 @@
+/*
+ * Copyright (c) 2006, 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.
+ *
+ * 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 jnlp.converter.parser.xml;
+
+import java.io.ByteArrayInputStream;
+import java.io.EOFException;
+import java.io.InputStreamReader;
+import java.io.IOException;
+import java.io.Reader;
+import java.io.UnsupportedEncodingException;
+
+public class XMLEncoding {
+ /**
+ * Decodes a byte stream into a String by testing for a Byte Order Mark
+ * (BOM) or an XML declaration.
+ * <br />
+ * Detection begins by examining the first four octets of the stream for a
+ * BOM. If a BOM is not found, then an encoding declaration is looked for
+ * at the beginning of the stream. If the encoding still can not be
+ * determined at this point, then UTF-8 is assumed.
+ *
+ * @param data an array of bytes containing an encoded XML document.
+ *
+ * @return A string containing the decoded XML document.
+ */
+ public static String decodeXML(byte [] data) throws IOException {
+ int start = 0;
+ String encoding;
+
+ if (data.length < BOM_LENGTH) {
+ throw (new EOFException("encoding.error.not.xml"));
+ }
+ // no else required; successfully read stream
+ int firstFour = ((0xff000000 & ((int) data[0] << 24)) |
+ (0x00ff0000 & ((int) data[1] << 16)) |
+ (0x0000ff00 & ((int) data[2] << 8)) |
+ (0x000000ff & (int) data[3]));
+
+ // start by examining the first four bytes for a BOM
+ switch (firstFour) {
+ case EBCDIC:
+ // examine the encoding declaration
+ encoding = examineEncodingDeclaration(data, IBM037_ENC);
+ break;
+
+ case XML_DECLARATION:
+ // assume UTF-8, but examine the encoding declaration
+ encoding = examineEncodingDeclaration(data, UTF_8_ENC);
+ break;
+
+ case UTF_16BE:
+ encoding = UTF_16BE_ENC;
+ break;
+
+ case UTF_16LE:
+ encoding = UTF_16LE_ENC;
+ break;
+
+ case UNUSUAL_OCTET_1:
+ case UNUSUAL_OCTET_2:
+ throw (new UnsupportedEncodingException("encoding.error.unusual.octet"));
+
+ case UTF_32_BE_BOM:
+ case UTF_32_LE_BOM:
+ encoding = UTF_32_ENC;
+ break;
+
+ default:
+ int firstThree = firstFour & 0xffffff00;
+
+ switch (firstThree) {
+ case UTF_8_BOM:
+ // the InputStreamReader class doen't properly handle
+ // the Byte Order Mark (BOM) in UTF-8 streams, so don't
+ // putback those 3 bytes.
+ start = 3;
+ encoding = UTF_8_ENC;
+ break;
+
+ default:
+ int firstTwo = firstFour & 0xffff0000;
+
+ switch (firstTwo) {
+ case UTF_16_BE_BOM:
+ case UTF_16_LE_BOM:
+ encoding = UTF_16_ENC;
+ break;
+
+ default:
+ // this is probably UTF-8 without the encoding
+ // declaration
+ encoding = UTF_8_ENC;
+ break;
+ }
+ break;
+ }
+ break;
+ }
+
+ return (new String(data, start, data.length - start, encoding));
+ }
+
+ /**
+ * [3] S ::= ( #x20 | #x09 | #x0d | #x0a )
+ * [23] XMLDecl ::= '<?xml' VersionInfo EncodingDecl? SDDecl? S? '?>'
+ * [24] VersionInfo ::= S 'version' Eq ( '"' VersionNum '"' |
+ * "'" VersionNum "'" )
+ * [25] Eq ::= S? '=' S?
+ * [26] VersionNum ::= ([a-zA-Z0-9_.:] | '-')+
+ * [80] EncodingDecl ::= S 'encoding' Eq ( '"' EncName '"' |
+ * "'" EncName "'" )
+ * [81] EncName ::= [a-zA-Z] ([a-zA-Z0-9_.] | '-')*
+ */
+ private static String examineEncodingDeclaration(byte [] data,
+ String encoding) throws IOException {
+ boolean loop = false;
+ boolean recognized = false;
+ boolean almost = false;
+ boolean question = false;
+ boolean done = false;
+ boolean found = false;
+ int pos = 0;
+ int ch = -1;
+ Reader reader = null;
+ String result = ((encoding != null) ? encoding : UTF_8_ENC);
+
+ reader = new InputStreamReader(new ByteArrayInputStream(data), result);
+ ch = reader.read();
+
+ // if this is an XML declaration, it will start with the text '<?xml'
+ for (int i = 0; ((i < XML_DECL_START.length()) && (done == false)); i++) {
+ if (ch != XML_DECL_START.charAt(i)) {
+ // This doesn't look like an XML declaration. This method
+ // should only be called if the stream contains an XML
+ // declaration in the encoding that is passed into the method.
+ done = true;
+ break;
+ }
+ // no else required; still matches
+ ch = reader.read();
+ }
+
+ // there must be at least one whitespace character next.
+ loop = true;
+ while ((loop == true) && (done == false)) {
+ switch (ch) {
+ case SPACE:
+ case TAB: // intentional
+ case LINEFEED: // fall
+ case RETURN: // through
+ ch = reader.read();
+ break;
+
+ case -1:
+ // unexpected EOF
+ done = true;
+ break;
+
+ default:
+ // non-whitespace
+ loop = false;
+ break;
+ }
+ }
+
+ // now look for the text 'encoding', but if the end of the XML
+ // declaration (signified by the text '?>') comes first, then
+ // assume the encoding is UTF-8
+ loop = true;
+ while ((loop == true) && (done == false)) {
+ if (ch == -1) {
+ // unexpected EOF
+ done = true;
+ break;
+ } else if (recognized == true) {
+ // this is the encoding declaration as long as the next few
+ // characters are whitespace and/or the equals ('=') sign
+ switch (ch) {
+ case SPACE: // intentional
+ case TAB: // fall
+ case LINEFEED: // through
+ case RETURN:
+ // don't need to do anything
+ break;
+
+ case EQUAL:
+ if (almost == false) {
+ // got the equal, now find a quote
+ almost = true;
+ } else {
+ // this is not valid XML, so punt
+ recognized = false;
+ done = true;
+ }
+ break;
+
+ case DOUBLE_QUOTE: // intentional
+ case SINGLE_QUOTE: // fall through
+ if (almost == true) {
+ // got the quote, so move on to get the value
+ loop = false;
+ } else {
+ // got a quote before the equal; this is not valid
+ // XML, so punt
+ recognized = false;
+ done = true;
+ }
+ break;
+
+ default:
+ // non-whitespace
+ recognized = false;
+ if (almost == true) {
+ // this is not valid XML, so punt
+ done = true;
+ }
+ // no else required; this wasn't the encoding
+ // declaration
+ break;
+ }
+
+ if (recognized == false) {
+ // this isn't the encoding declaration, so go back to the
+ // top without reading the next character
+ pos = 0;
+ continue;
+ }
+ // no else required; still looking good
+ } else if (ch == ENCODING_DECL.charAt(pos++)) {
+ if (ENCODING_DECL.length() == pos) {
+ // this looks like the encoding declaration
+ recognized = true;
+ }
+ // no else required; this might be the encoding declaration
+ } else if (ch == '?') {
+ question = true;
+ pos = 0;
+ } else if ((ch == '>') && (question == true)) {
+ // there is no encoding declaration, so assume that the initial
+ // encoding guess was correct
+ done = true;
+ continue;
+ } else {
+ // still searching for the encoding declaration
+ pos = 0;
+ }
+
+ ch = reader.read();
+ }
+
+ if (done == false) {
+ StringBuilder buffer = new StringBuilder(MAX_ENC_NAME);
+
+ if (((ch >= 'a') && (ch <= 'z')) |
+ ((ch >= 'A') && (ch <= 'Z'))) {
+ // add the character to the result
+ buffer.append((char) ch);
+
+ loop = true;
+ while ((loop == true) && (done == false)) {
+ ch = reader.read();
+
+ if (((ch >= 'a') && (ch <= 'z')) ||
+ ((ch >= 'A') && (ch <= 'Z')) ||
+ ((ch >= '0') && (ch <= '9')) ||
+ (ch == '_') || (ch == '.') || (ch == '-')) {
+ // add the character to the result
+ buffer.append((char) ch);
+ } else if ((ch == DOUBLE_QUOTE) || (ch == SINGLE_QUOTE)) {
+ // finished!
+ found = true;
+ done = true;
+ result = buffer.toString();
+ } else {
+ // this is not a valid encoding name, so punt
+ done = true;
+ }
+ }
+ } else {
+ // this is not a valid encoding name, so punt
+ done = true;
+ }
+ }
+ // no else required; already failed to find the encoding somewhere else
+
+ return (result);
+ }
+
+ private static final int BOM_LENGTH = 4;
+ private static final int MAX_ENC_NAME = 512;
+
+ private static final int SPACE = 0x00000020;
+ private static final int TAB = 0x00000009;
+ private static final int LINEFEED = 0x0000000a;
+ private static final int RETURN = 0x0000000d;
+ private static final int EQUAL = '=';
+ private static final int DOUBLE_QUOTE = '\"';
+ private static final int SINGLE_QUOTE = '\'';
+
+ private static final int UTF_32_BE_BOM = 0x0000feff;
+ private static final int UTF_32_LE_BOM = 0xfffe0000;
+ private static final int UTF_16_BE_BOM = 0xfeff0000;
+ private static final int UTF_16_LE_BOM = 0xfffe0000;
+ private static final int UTF_8_BOM = 0xefbbbf00;
+ private static final int UNUSUAL_OCTET_1 = 0x00003c00;
+ private static final int UNUSUAL_OCTET_2 = 0x003c0000;
+ private static final int UTF_16BE = 0x003c003f;
+ private static final int UTF_16LE = 0x3c003f00;
+ private static final int EBCDIC = 0x4c6fa794;
+ private static final int XML_DECLARATION = 0x3c3f786d;
+
+ private static final String UTF_32_ENC = "UTF-32";
+ private static final String UTF_16_ENC = "UTF-16";
+ private static final String UTF_16BE_ENC = "UTF-16BE";
+ private static final String UTF_16LE_ENC = "UTF-16LE";
+ private static final String UTF_8_ENC = "UTF-8";
+ private static final String IBM037_ENC = "IBM037";
+
+ private static final String XML_DECL_START = "<?xml";
+ private static final String ENCODING_DECL = "encoding";
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/demo/share/jpackage/JNLPConverter/src/jnlp/converter/parser/xml/XMLNode.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,201 @@
+/*
+ * Copyright (c) 2006, 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.
+ *
+ * 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 jnlp.converter.parser.xml;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+/**
+ * Class that contains information about an XML Node
+ */
+public class XMLNode {
+ private final boolean _isElement; // Element/PCTEXT
+ private final String _name;
+ private final XMLAttribute _attr;
+ private XMLNode _parent; // Parent Node
+ private XMLNode _nested; // Nested XML tags
+ private XMLNode _next; // Following XML tag on the same level
+
+ public final static String WILDCARD = "*";
+
+ /** Creates a PCTEXT node */
+ public XMLNode(String name) {
+ _isElement = false;
+ _name = name;
+ _attr = null;
+ _nested = null;
+ _next = null;
+ _parent = null;
+ }
+
+ /** Creates a ELEMENT node */
+ public XMLNode(String name, XMLAttribute attr) {
+ _isElement = true;
+ _name = stripNameSpace(name);
+ _attr = attr;
+ _nested = null;
+ _next = null;
+ _parent = null;
+ }
+
+ public String getName() {
+ return _name;
+ }
+
+ public XMLAttribute getAttributes() {
+ return _attr;
+ }
+
+ public XMLNode getNested() {
+ return _nested;
+ }
+
+ public XMLNode getNext() {
+ return _next;
+ }
+
+ public boolean isElement() {
+ return _isElement;
+ }
+
+ public void setParent(XMLNode parent) {
+ _parent = parent;
+ }
+
+ public XMLNode getParent() {
+ return _parent;
+ }
+
+ public void setNext(XMLNode next) {
+ _next = next;
+ }
+
+ public void setNested(XMLNode nested) {
+ _nested = nested;
+ }
+
+ public static String stripNameSpace(String name) {
+ if (name != null && !name.startsWith("xmlns:")) {
+ int i = name.lastIndexOf(":");
+ if (i >= 0 && i < name.length()) {
+ return name.substring(i+1);
+ }
+ }
+ return name;
+ }
+
+ @Override
+ public int hashCode() {
+ int hash = 3;
+ hash = 83 * hash + (this._name != null ? this._name.hashCode() : 0);
+ hash = 83 * hash + (this._attr != null ? this._attr.hashCode() : 0);
+ hash = 83 * hash + (this._nested != null ? this._nested.hashCode() : 0);
+ hash = 83 * hash + (this._next != null ? this._next.hashCode() : 0);
+ return hash;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == null || !(o instanceof XMLNode)) return false;
+ XMLNode other = (XMLNode)o;
+ boolean result =
+ match(_name, other._name) &&
+ match(_attr, other._attr) &&
+ match(_nested, other._nested) &&
+ match(_next, other._next);
+ return result;
+ }
+
+ public String getAttribute(String name) {
+ XMLAttribute cur = _attr;
+ while(cur != null) {
+ if (name.equals(cur.getName())) return cur.getValue();
+ cur = cur.getNext();
+ }
+ return "";
+ }
+
+ private static boolean match(Object o1, Object o2) {
+ if (o1 == null) {
+ return (o2 == null);
+ }
+ return o1.equals(o2);
+ }
+
+ public void printToStream(PrintWriter out) {
+ printToStream(out, false);
+ }
+
+ public void printToStream(PrintWriter out, boolean trim) {
+ printToStream(out, 0, trim);
+ }
+
+ public void printToStream(PrintWriter out, int n, boolean trim) {
+ if (!isElement()) {
+ String value = _name; // value node (where name is data of parent)
+ if (trim && value.length() > 512) {
+ value = "...";
+ }
+ out.print(value);
+ } else {
+ if (_nested == null) {
+ String attrString = (_attr == null) ? "" : (" " + _attr.toString());
+ lineln(out, n, "<" + _name + attrString + "/>");
+ } else {
+ String attrString = (_attr == null) ? "" : (" " + _attr.toString());
+ lineln(out, n, "<" + _name + attrString + ">");
+ _nested.printToStream(out, n + 1, trim);
+ if (_nested.isElement()) {
+ lineln(out, n, "</" + _name + ">");
+ } else {
+ out.print("</" + _name + ">");
+ }
+ }
+ }
+ if (_next != null) {
+ _next.printToStream(out, n, trim);
+ }
+ }
+
+ private static void lineln(PrintWriter out, int indent, String s) {
+ out.println("");
+ for(int i = 0; i < indent; i++) {
+ out.print(" ");
+ }
+ out.print(s);
+ }
+
+ @Override
+ public String toString() {
+ return toString(false);
+ }
+
+ public String toString(boolean hideLongElementValue) {
+ StringWriter sw = new StringWriter(1000);
+ PrintWriter pw = new PrintWriter(sw);
+ printToStream(pw, hideLongElementValue);
+ pw.close();
+ return sw.toString();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/demo/share/jpackage/JNLPConverter/src/jnlp/converter/parser/xml/XMLParser.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,186 @@
+/*
+ * Copyright (c) 2014, 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.
+ *
+ * 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 jnlp.converter.parser.xml;
+
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+
+import org.xml.sax.InputSource;
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.DefaultHandler;
+
+import java.io.StringReader;
+import java.io.IOException;
+import java.util.Stack;
+
+public class XMLParser extends DefaultHandler {
+
+ private XMLNode _root;
+ private final String _source;
+ private Stack<XMLNode> _inProgress;
+ private String _characters;
+
+ // although defined in com.sun.org.apache.xerces.internal.impl.Constants,
+ // we should not be able to access that, so defined here
+ private final static String DTD_DOWNLOAD =
+ "http://apache.org/xml/features/nonvalidating/load-external-dtd";
+
+ private static final SAXParserFactory SPF = SAXParserFactory.newInstance();
+
+ /*
+ * Construct an <code>XMLParser</code>.
+ *
+ * @param source - the source text to parse.
+ */
+ public XMLParser(String source) {
+ _source = source.trim();
+ }
+
+ public XMLNode parse() throws SAXException {
+ // normally we parse without validating, but leave option to parse
+ // with validation, possibly controlled by config option.
+ return parse(false);
+ }
+
+ public XMLNode parse(boolean validating) throws SAXException {
+ _root = null;
+ _inProgress = new Stack<>();
+
+ try {
+ InputSource is = new InputSource(new StringReader(_source));
+ SPF.setValidating(validating);
+ // only download dtd file from DOCTYPE if we are doing validation
+ try {
+ SPF.setFeature(DTD_DOWNLOAD, validating);
+ } catch (Exception e) {
+ }
+ SAXParser sp = SPF.newSAXParser();
+ sp.parse(is, this);
+ } catch (ParserConfigurationException | IOException pce) {
+ throw new SAXException(pce);
+ }
+
+ return _root;
+ }
+
+ @Override
+ public void startElement(String uri, String localeName, String qName,
+ Attributes attributes) throws SAXException {
+
+ XMLAttribute first = null;
+ XMLAttribute last = null;
+ int len = attributes.getLength();
+
+ for (int i = 0; i < len; i++) {
+ XMLAttribute att = new XMLAttribute(
+ // in old implementation attribute names and values were trimmed
+ attributes.getQName(i).trim(), attributes.getValue(i).trim());
+ if (first == null) {
+ first = att;
+ }
+ if (last != null) {
+ last.setNext(att);
+ }
+ last = att;
+ }
+ _inProgress.push(new XMLNode(qName, first));
+ _characters = null;
+ }
+
+ @Override
+ public void endElement(String uri, String localeName, String elementName)
+ throws SAXException {
+ XMLNode node = _inProgress.pop();
+ // <information>
+ // <title>Title</title>
+ // <vendor>Vendor</vendor> "Some whitespaces"
+ // </information>
+ // In example above when we receive end of <information> we will
+ // have _characters set to whitespace and new line and it will be
+ // added as child node to <information>. This will break our cache code
+ // which will think that JNLP file changed on server even if it is not
+ // and thus we might not load properly.
+ //
+ // <application-desc name="HelloWorld" main-class="HelloWorld">
+ // <argument> test with whitespaces </argument>
+ // </application-desc>
+ // From example above we want to include whitespaces for <argument>.
+ //
+ // <node1>
+ // <node2>abc</node2>
+ // xyz (might be whitespaces)
+ // </node1>
+ // In JNLP spec we do not have cases when node have nested nodes as
+ // well as text which is whitespaces only.
+ //
+ // So to fix it lets check if ending node have nested nodes, then do
+ // not add whitespaces only node.
+ if (node != null && node.getNested() != null && _characters != null) {
+ String trimCharacters = _characters.trim();
+ if ((trimCharacters == null) || (trimCharacters.length() == 0)) {
+ _characters = null; // No need to add whitespaces only
+ }
+ }
+ if ((_characters != null) && (_characters.trim().length() > 0)) {
+ addChild(node, new XMLNode(_characters));
+ }
+
+ if (_inProgress.isEmpty()) {
+ _root = node;
+ } else {
+ addChild(_inProgress.peek(), node);
+ }
+ _characters = null;
+ }
+
+ @Override
+ public void characters(char[] chars, int start, int length)
+ throws SAXException {
+ String s = new String(chars, start, length);
+ _characters = ((_characters == null) ? s : _characters + s);
+ }
+
+ @Override
+ public void ignorableWhitespace(char[] chars, int start, int length)
+ throws SAXException {
+ String s = new String(chars, start, length);
+ _characters = ((_characters == null) ? s : _characters + s);
+ }
+
+ private void addChild(XMLNode parent, XMLNode child) {
+ child.setParent(parent);
+
+ XMLNode sibling = parent.getNested();
+ if (sibling == null) {
+ parent.setNested(child); // set us as only child
+ } else {
+ while (sibling.getNext() != null) {
+ sibling = sibling.getNext();
+ }
+ sibling.setNext(child); // sets us as youngest child
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/demo/share/nbproject/jpackage/JNLPConverter/build.xml Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,80 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- You may freely edit this file. See commented blocks below for -->
+<!-- some examples of how to customize the build. -->
+<!-- (If you delete it and reopen the project it will be recreated.) -->
+<!-- By default, only the Clean and Build commands use this build script. -->
+<!-- Commands such as Run, Debug, and Test only use this build script if -->
+<!-- the Compile on Save feature is turned off for the project. -->
+<!-- You can turn off the Compile on Save (or Deploy on Save) setting -->
+<!-- in the project's Project Properties dialog box.-->
+<project name="JNLPConverter" default="default" basedir=".">
+ <description>Builds, tests, and runs the project JNLPConverter.</description>
+ <import file="nbproject/build-impl.xml"/>
+ <!--
+
+ There exist several targets which are by default empty and which can be
+ used for execution of your tasks. These targets are usually executed
+ before and after some main targets. They are:
+
+ -pre-init: called before initialization of project properties
+ -post-init: called after initialization of project properties
+ -pre-compile: called before javac compilation
+ -post-compile: called after javac compilation
+ -pre-compile-single: called before javac compilation of single file
+ -post-compile-single: called after javac compilation of single file
+ -pre-compile-test: called before javac compilation of JUnit tests
+ -post-compile-test: called after javac compilation of JUnit tests
+ -pre-compile-test-single: called before javac compilation of single JUnit test
+ -post-compile-test-single: called after javac compilation of single JUunit test
+ -pre-jar: called before JAR building
+ -post-jar: called after JAR building
+ -post-clean: called after cleaning build products
+
+ (Targets beginning with '-' are not intended to be called on their own.)
+
+ Example of inserting an obfuscator after compilation could look like this:
+
+ <target name="-post-compile">
+ <obfuscate>
+ <fileset dir="${build.classes.dir}"/>
+ </obfuscate>
+ </target>
+
+ For list of available properties check the imported
+ nbproject/build-impl.xml file.
+
+
+ Another way to customize the build is by overriding existing main targets.
+ The targets of interest are:
+
+ -init-macrodef-javac: defines macro for javac compilation
+ -init-macrodef-junit: defines macro for junit execution
+ -init-macrodef-debug: defines macro for class debugging
+ -init-macrodef-java: defines macro for class execution
+ -do-jar: JAR building
+ run: execution of project
+ -javadoc-build: Javadoc generation
+ test-report: JUnit report generation
+
+ An example of overriding the target for project execution could look like this:
+
+ <target name="run" depends="JNLPConverter-impl.jar">
+ <exec dir="bin" executable="launcher.exe">
+ <arg file="${dist.jar}"/>
+ </exec>
+ </target>
+
+ Notice that the overridden target depends on the jar target and not only on
+ the compile target as the regular run target does. Again, for a list of available
+ properties which you can use, check the target you are overriding in the
+ nbproject/build-impl.xml file.
+
+ -->
+ <target name="-post-jar">
+ <copy file="${dist.jar}" todir="../../../jpackage/jnlpconverter"/>
+ </target>
+
+ <target name="-post-clean">
+ <delete file="../../../jpackage/jnlpconverter/JNLPConverter.jar"/>
+ </target>
+</project>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/demo/share/nbproject/jpackage/JNLPConverter/manifest.mf Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,3 @@
+Manifest-Version: 1.0
+X-COMMENT: Main-Class will be added automatically by build
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/demo/share/nbproject/jpackage/JNLPConverter/nbproject/build-impl.xml Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,1403 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+*** GENERATED FROM project.xml - DO NOT EDIT ***
+*** EDIT ../build.xml INSTEAD ***
+
+For the purpose of easier reading the script
+is divided into following sections:
+
+ - initialization
+ - compilation
+ - jar
+ - execution
+ - debugging
+ - javadoc
+ - test compilation
+ - test execution
+ - test debugging
+ - applet
+ - cleanup
+
+ -->
+<project xmlns:j2seproject1="http://www.netbeans.org/ns/j2se-project/1" xmlns:j2seproject3="http://www.netbeans.org/ns/j2se-project/3" xmlns:jaxrpc="http://www.netbeans.org/ns/j2se-project/jax-rpc" basedir=".." default="default" name="JNLPConverter-impl">
+ <fail message="Please build using Ant 1.8.0 or higher.">
+ <condition>
+ <not>
+ <antversion atleast="1.8.0"/>
+ </not>
+ </condition>
+ </fail>
+ <target depends="test,jar,javadoc" description="Build and test whole project." name="default"/>
+ <!--
+ ======================
+ INITIALIZATION SECTION
+ ======================
+ -->
+ <target name="-pre-init">
+ <!-- Empty placeholder for easier customization. -->
+ <!-- You can override this target in the ../build.xml file. -->
+ </target>
+ <target depends="-pre-init" name="-init-private">
+ <property file="nbproject/private/config.properties"/>
+ <property file="nbproject/private/configs/${config}.properties"/>
+ <property file="nbproject/private/private.properties"/>
+ </target>
+ <target depends="-pre-init,-init-private" name="-init-user">
+ <property file="${user.properties.file}"/>
+ <!-- The two properties below are usually overridden -->
+ <!-- by the active platform. Just a fallback. -->
+ <property name="default.javac.source" value="1.6"/>
+ <property name="default.javac.target" value="1.6"/>
+ </target>
+ <target depends="-pre-init,-init-private,-init-user" name="-init-project">
+ <property file="nbproject/configs/${config}.properties"/>
+ <property file="nbproject/project.properties"/>
+ </target>
+ <target depends="-pre-init,-init-private,-init-user,-init-project,-init-macrodef-property" name="-do-init">
+ <property name="platform.java" value="${java.home}/bin/java"/>
+ <available file="${manifest.file}" property="manifest.available"/>
+ <condition property="splashscreen.available">
+ <and>
+ <not>
+ <equals arg1="${application.splash}" arg2="" trim="true"/>
+ </not>
+ <available file="${application.splash}"/>
+ </and>
+ </condition>
+ <condition property="main.class.available">
+ <and>
+ <isset property="main.class"/>
+ <not>
+ <equals arg1="${main.class}" arg2="" trim="true"/>
+ </not>
+ </and>
+ </condition>
+ <condition property="profile.available">
+ <and>
+ <isset property="javac.profile"/>
+ <length length="0" string="${javac.profile}" when="greater"/>
+ <matches pattern="((1\.[89])|9)(\..*)?" string="${javac.source}"/>
+ </and>
+ </condition>
+ <condition property="do.archive">
+ <or>
+ <not>
+ <istrue value="${jar.archive.disabled}"/>
+ </not>
+ <istrue value="${not.archive.disabled}"/>
+ </or>
+ </condition>
+ <condition property="do.mkdist">
+ <and>
+ <isset property="do.archive"/>
+ <isset property="libs.CopyLibs.classpath"/>
+ <not>
+ <istrue value="${mkdist.disabled}"/>
+ </not>
+ </and>
+ </condition>
+ <condition property="do.archive+manifest.available">
+ <and>
+ <isset property="manifest.available"/>
+ <istrue value="${do.archive}"/>
+ </and>
+ </condition>
+ <condition property="do.archive+main.class.available">
+ <and>
+ <isset property="main.class.available"/>
+ <istrue value="${do.archive}"/>
+ </and>
+ </condition>
+ <condition property="do.archive+splashscreen.available">
+ <and>
+ <isset property="splashscreen.available"/>
+ <istrue value="${do.archive}"/>
+ </and>
+ </condition>
+ <condition property="do.archive+profile.available">
+ <and>
+ <isset property="profile.available"/>
+ <istrue value="${do.archive}"/>
+ </and>
+ </condition>
+ <condition property="have.tests">
+ <or/>
+ </condition>
+ <condition property="have.sources">
+ <or>
+ <available file="${src.src.dir}"/>
+ </or>
+ </condition>
+ <condition property="netbeans.home+have.tests">
+ <and>
+ <isset property="netbeans.home"/>
+ <isset property="have.tests"/>
+ </and>
+ </condition>
+ <condition property="no.javadoc.preview">
+ <and>
+ <isset property="javadoc.preview"/>
+ <isfalse value="${javadoc.preview}"/>
+ </and>
+ </condition>
+ <property name="run.jvmargs" value=""/>
+ <property name="run.jvmargs.ide" value=""/>
+ <property name="javac.compilerargs" value=""/>
+ <property name="work.dir" value="${basedir}"/>
+ <condition property="no.deps">
+ <and>
+ <istrue value="${no.dependencies}"/>
+ </and>
+ </condition>
+ <property name="javac.debug" value="true"/>
+ <property name="javadoc.preview" value="true"/>
+ <property name="application.args" value=""/>
+ <property name="source.encoding" value="${file.encoding}"/>
+ <property name="runtime.encoding" value="${source.encoding}"/>
+ <property name="manifest.encoding" value="${source.encoding}"/>
+ <condition property="javadoc.encoding.used" value="${javadoc.encoding}">
+ <and>
+ <isset property="javadoc.encoding"/>
+ <not>
+ <equals arg1="${javadoc.encoding}" arg2=""/>
+ </not>
+ </and>
+ </condition>
+ <property name="javadoc.encoding.used" value="${source.encoding}"/>
+ <property name="includes" value="**"/>
+ <property name="excludes" value=""/>
+ <property name="do.depend" value="false"/>
+ <condition property="do.depend.true">
+ <istrue value="${do.depend}"/>
+ </condition>
+ <path id="endorsed.classpath.path" path="${endorsed.classpath}"/>
+ <condition else="" property="endorsed.classpath.cmd.line.arg" value="-Xbootclasspath/p:'${toString:endorsed.classpath.path}'">
+ <and>
+ <isset property="endorsed.classpath"/>
+ <not>
+ <equals arg1="${endorsed.classpath}" arg2="" trim="true"/>
+ </not>
+ </and>
+ </condition>
+ <condition else="" property="javac.profile.cmd.line.arg" value="-profile ${javac.profile}">
+ <isset property="profile.available"/>
+ </condition>
+ <condition else="false" property="jdkBug6558476">
+ <and>
+ <matches pattern="1\.[56]" string="${java.specification.version}"/>
+ <not>
+ <os family="unix"/>
+ </not>
+ </and>
+ </condition>
+ <condition else="false" property="javac.fork">
+ <or>
+ <istrue value="${jdkBug6558476}"/>
+ <istrue value="${javac.external.vm}"/>
+ </or>
+ </condition>
+ <property name="jar.index" value="false"/>
+ <property name="jar.index.metainf" value="${jar.index}"/>
+ <property name="copylibs.rebase" value="true"/>
+ <available file="${meta.inf.dir}/persistence.xml" property="has.persistence.xml"/>
+ <condition property="junit.available">
+ <or>
+ <available classname="org.junit.Test" classpath="${run.test.classpath}"/>
+ <available classname="junit.framework.Test" classpath="${run.test.classpath}"/>
+ </or>
+ </condition>
+ <condition property="testng.available">
+ <available classname="org.testng.annotations.Test" classpath="${run.test.classpath}"/>
+ </condition>
+ <condition property="junit+testng.available">
+ <and>
+ <istrue value="${junit.available}"/>
+ <istrue value="${testng.available}"/>
+ </and>
+ </condition>
+ <condition else="testng" property="testng.mode" value="mixed">
+ <istrue value="${junit+testng.available}"/>
+ </condition>
+ <condition else="" property="testng.debug.mode" value="-mixed">
+ <istrue value="${junit+testng.available}"/>
+ </condition>
+ <property name="java.failonerror" value="true"/>
+ </target>
+ <target name="-post-init">
+ <!-- Empty placeholder for easier customization. -->
+ <!-- You can override this target in the ../build.xml file. -->
+ </target>
+ <target depends="-pre-init,-init-private,-init-user,-init-project,-do-init" name="-init-check">
+ <fail unless="src.src.dir">Must set src.src.dir</fail>
+ <fail unless="build.dir">Must set build.dir</fail>
+ <fail unless="dist.dir">Must set dist.dir</fail>
+ <fail unless="build.classes.dir">Must set build.classes.dir</fail>
+ <fail unless="dist.javadoc.dir">Must set dist.javadoc.dir</fail>
+ <fail unless="build.test.classes.dir">Must set build.test.classes.dir</fail>
+ <fail unless="build.test.results.dir">Must set build.test.results.dir</fail>
+ <fail unless="build.classes.excludes">Must set build.classes.excludes</fail>
+ <fail unless="dist.jar">Must set dist.jar</fail>
+ </target>
+ <target name="-init-macrodef-property">
+ <macrodef name="property" uri="http://www.netbeans.org/ns/j2se-project/1">
+ <attribute name="name"/>
+ <attribute name="value"/>
+ <sequential>
+ <property name="@{name}" value="${@{value}}"/>
+ </sequential>
+ </macrodef>
+ </target>
+ <target depends="-init-ap-cmdline-properties" if="ap.supported.internal" name="-init-macrodef-javac-with-processors">
+ <macrodef name="javac" uri="http://www.netbeans.org/ns/j2se-project/3">
+ <attribute default="${src.src.dir}" name="srcdir"/>
+ <attribute default="${build.classes.dir}" name="destdir"/>
+ <attribute default="${javac.classpath}" name="classpath"/>
+ <attribute default="${javac.processorpath}" name="processorpath"/>
+ <attribute default="${build.generated.sources.dir}/ap-source-output" name="apgeneratedsrcdir"/>
+ <attribute default="${includes}" name="includes"/>
+ <attribute default="${excludes}" name="excludes"/>
+ <attribute default="${javac.debug}" name="debug"/>
+ <attribute default="${empty.dir}" name="sourcepath"/>
+ <attribute default="${empty.dir}" name="gensrcdir"/>
+ <element name="customize" optional="true"/>
+ <sequential>
+ <property location="${build.dir}/empty" name="empty.dir"/>
+ <mkdir dir="${empty.dir}"/>
+ <mkdir dir="@{apgeneratedsrcdir}"/>
+ <javac debug="@{debug}" deprecation="${javac.deprecation}" destdir="@{destdir}" encoding="${source.encoding}" excludes="@{excludes}" fork="${javac.fork}" includeantruntime="false" includes="@{includes}" source="${javac.source}" sourcepath="@{sourcepath}" srcdir="@{srcdir}" target="${javac.target}" tempdir="${java.io.tmpdir}">
+ <src>
+ <dirset dir="@{gensrcdir}" erroronmissingdir="false">
+ <include name="*"/>
+ </dirset>
+ </src>
+ <classpath>
+ <path path="@{classpath}"/>
+ </classpath>
+ <compilerarg line="${endorsed.classpath.cmd.line.arg}"/>
+ <compilerarg line="${javac.profile.cmd.line.arg}"/>
+ <compilerarg line="${javac.compilerargs}"/>
+ <compilerarg value="-processorpath"/>
+ <compilerarg path="@{processorpath}:${empty.dir}"/>
+ <compilerarg line="${ap.processors.internal}"/>
+ <compilerarg line="${annotation.processing.processor.options}"/>
+ <compilerarg value="-s"/>
+ <compilerarg path="@{apgeneratedsrcdir}"/>
+ <compilerarg line="${ap.proc.none.internal}"/>
+ <customize/>
+ </javac>
+ </sequential>
+ </macrodef>
+ </target>
+ <target depends="-init-ap-cmdline-properties" name="-init-macrodef-javac-without-processors" unless="ap.supported.internal">
+ <macrodef name="javac" uri="http://www.netbeans.org/ns/j2se-project/3">
+ <attribute default="${src.src.dir}" name="srcdir"/>
+ <attribute default="${build.classes.dir}" name="destdir"/>
+ <attribute default="${javac.classpath}" name="classpath"/>
+ <attribute default="${javac.processorpath}" name="processorpath"/>
+ <attribute default="${build.generated.sources.dir}/ap-source-output" name="apgeneratedsrcdir"/>
+ <attribute default="${includes}" name="includes"/>
+ <attribute default="${excludes}" name="excludes"/>
+ <attribute default="${javac.debug}" name="debug"/>
+ <attribute default="${empty.dir}" name="sourcepath"/>
+ <attribute default="${empty.dir}" name="gensrcdir"/>
+ <element name="customize" optional="true"/>
+ <sequential>
+ <property location="${build.dir}/empty" name="empty.dir"/>
+ <mkdir dir="${empty.dir}"/>
+ <javac debug="@{debug}" deprecation="${javac.deprecation}" destdir="@{destdir}" encoding="${source.encoding}" excludes="@{excludes}" fork="${javac.fork}" includeantruntime="false" includes="@{includes}" source="${javac.source}" sourcepath="@{sourcepath}" srcdir="@{srcdir}" target="${javac.target}" tempdir="${java.io.tmpdir}">
+ <src>
+ <dirset dir="@{gensrcdir}" erroronmissingdir="false">
+ <include name="*"/>
+ </dirset>
+ </src>
+ <classpath>
+ <path path="@{classpath}"/>
+ </classpath>
+ <compilerarg line="${endorsed.classpath.cmd.line.arg}"/>
+ <compilerarg line="${javac.profile.cmd.line.arg}"/>
+ <compilerarg line="${javac.compilerargs}"/>
+ <customize/>
+ </javac>
+ </sequential>
+ </macrodef>
+ </target>
+ <target depends="-init-macrodef-javac-with-processors,-init-macrodef-javac-without-processors" name="-init-macrodef-javac">
+ <macrodef name="depend" uri="http://www.netbeans.org/ns/j2se-project/3">
+ <attribute default="${src.src.dir}" name="srcdir"/>
+ <attribute default="${build.classes.dir}" name="destdir"/>
+ <attribute default="${javac.classpath}" name="classpath"/>
+ <sequential>
+ <depend cache="${build.dir}/depcache" destdir="@{destdir}" excludes="${excludes}" includes="${includes}" srcdir="@{srcdir}">
+ <classpath>
+ <path path="@{classpath}"/>
+ </classpath>
+ </depend>
+ </sequential>
+ </macrodef>
+ <macrodef name="force-recompile" uri="http://www.netbeans.org/ns/j2se-project/3">
+ <attribute default="${build.classes.dir}" name="destdir"/>
+ <sequential>
+ <fail unless="javac.includes">Must set javac.includes</fail>
+ <pathconvert pathsep="${line.separator}" property="javac.includes.binary">
+ <path>
+ <filelist dir="@{destdir}" files="${javac.includes}"/>
+ </path>
+ <globmapper from="*.java" to="*.class"/>
+ </pathconvert>
+ <tempfile deleteonexit="true" property="javac.includesfile.binary"/>
+ <echo file="${javac.includesfile.binary}" message="${javac.includes.binary}"/>
+ <delete>
+ <files includesfile="${javac.includesfile.binary}"/>
+ </delete>
+ <delete>
+ <fileset file="${javac.includesfile.binary}"/>
+ </delete>
+ </sequential>
+ </macrodef>
+ </target>
+ <target if="${junit.available}" name="-init-macrodef-junit-init">
+ <condition else="false" property="nb.junit.batch" value="true">
+ <and>
+ <istrue value="${junit.available}"/>
+ <not>
+ <isset property="test.method"/>
+ </not>
+ </and>
+ </condition>
+ <condition else="false" property="nb.junit.single" value="true">
+ <and>
+ <istrue value="${junit.available}"/>
+ <isset property="test.method"/>
+ </and>
+ </condition>
+ </target>
+ <target name="-init-test-properties">
+ <property name="test.binaryincludes" value="<nothing>"/>
+ <property name="test.binarytestincludes" value=""/>
+ <property name="test.binaryexcludes" value=""/>
+ </target>
+ <target if="${nb.junit.single}" name="-init-macrodef-junit-single" unless="${nb.junit.batch}">
+ <macrodef name="junit" uri="http://www.netbeans.org/ns/j2se-project/3">
+ <attribute default="${includes}" name="includes"/>
+ <attribute default="${excludes}" name="excludes"/>
+ <attribute default="**" name="testincludes"/>
+ <attribute default="" name="testmethods"/>
+ <element name="customize" optional="true"/>
+ <sequential>
+ <property name="junit.forkmode" value="perTest"/>
+ <junit dir="${work.dir}" errorproperty="tests.failed" failureproperty="tests.failed" fork="true" forkmode="${junit.forkmode}" showoutput="true" tempdir="${build.dir}">
+ <test methods="@{testmethods}" name="@{testincludes}" todir="${build.test.results.dir}"/>
+ <syspropertyset>
+ <propertyref prefix="test-sys-prop."/>
+ <mapper from="test-sys-prop.*" to="*" type="glob"/>
+ </syspropertyset>
+ <formatter type="brief" usefile="false"/>
+ <formatter type="xml"/>
+ <jvmarg value="-ea"/>
+ <customize/>
+ </junit>
+ </sequential>
+ </macrodef>
+ </target>
+ <target depends="-init-test-properties" if="${nb.junit.batch}" name="-init-macrodef-junit-batch" unless="${nb.junit.single}">
+ <macrodef name="junit" uri="http://www.netbeans.org/ns/j2se-project/3">
+ <attribute default="${includes}" name="includes"/>
+ <attribute default="${excludes}" name="excludes"/>
+ <attribute default="**" name="testincludes"/>
+ <attribute default="" name="testmethods"/>
+ <element name="customize" optional="true"/>
+ <sequential>
+ <property name="junit.forkmode" value="perTest"/>
+ <junit dir="${work.dir}" errorproperty="tests.failed" failureproperty="tests.failed" fork="true" forkmode="${junit.forkmode}" showoutput="true" tempdir="${build.dir}">
+ <batchtest todir="${build.test.results.dir}">
+ <fileset dir="${build.test.classes.dir}" excludes="@{excludes},${excludes},${test.binaryexcludes}" includes="${test.binaryincludes}">
+ <filename name="${test.binarytestincludes}"/>
+ </fileset>
+ </batchtest>
+ <syspropertyset>
+ <propertyref prefix="test-sys-prop."/>
+ <mapper from="test-sys-prop.*" to="*" type="glob"/>
+ </syspropertyset>
+ <formatter type="brief" usefile="false"/>
+ <formatter type="xml"/>
+ <jvmarg value="-ea"/>
+ <customize/>
+ </junit>
+ </sequential>
+ </macrodef>
+ </target>
+ <target depends="-init-macrodef-junit-init,-init-macrodef-junit-single, -init-macrodef-junit-batch" if="${junit.available}" name="-init-macrodef-junit"/>
+ <target if="${testng.available}" name="-init-macrodef-testng">
+ <macrodef name="testng" uri="http://www.netbeans.org/ns/j2se-project/3">
+ <attribute default="${includes}" name="includes"/>
+ <attribute default="${excludes}" name="excludes"/>
+ <attribute default="**" name="testincludes"/>
+ <attribute default="" name="testmethods"/>
+ <element name="customize" optional="true"/>
+ <sequential>
+ <condition else="" property="testng.methods.arg" value="@{testincludes}.@{testmethods}">
+ <isset property="test.method"/>
+ </condition>
+ <union id="test.set"/>
+ <taskdef classname="org.testng.TestNGAntTask" classpath="${run.test.classpath}" name="testng"/>
+ <testng classfilesetref="test.set" failureProperty="tests.failed" listeners="org.testng.reporters.VerboseReporter" methods="${testng.methods.arg}" mode="${testng.mode}" outputdir="${build.test.results.dir}" suitename="JNLPConverter" testname="TestNG tests" workingDir="${work.dir}">
+ <xmlfileset dir="${build.test.classes.dir}" includes="@{testincludes}"/>
+ <propertyset>
+ <propertyref prefix="test-sys-prop."/>
+ <mapper from="test-sys-prop.*" to="*" type="glob"/>
+ </propertyset>
+ <customize/>
+ </testng>
+ </sequential>
+ </macrodef>
+ </target>
+ <target name="-init-macrodef-test-impl">
+ <macrodef name="test-impl" uri="http://www.netbeans.org/ns/j2se-project/3">
+ <attribute default="${includes}" name="includes"/>
+ <attribute default="${excludes}" name="excludes"/>
+ <attribute default="**" name="testincludes"/>
+ <attribute default="" name="testmethods"/>
+ <element implicit="true" name="customize" optional="true"/>
+ <sequential>
+ <echo>No tests executed.</echo>
+ </sequential>
+ </macrodef>
+ </target>
+ <target depends="-init-macrodef-junit" if="${junit.available}" name="-init-macrodef-junit-impl">
+ <macrodef name="test-impl" uri="http://www.netbeans.org/ns/j2se-project/3">
+ <attribute default="${includes}" name="includes"/>
+ <attribute default="${excludes}" name="excludes"/>
+ <attribute default="**" name="testincludes"/>
+ <attribute default="" name="testmethods"/>
+ <element implicit="true" name="customize" optional="true"/>
+ <sequential>
+ <j2seproject3:junit excludes="@{excludes}" includes="@{includes}" testincludes="@{testincludes}" testmethods="@{testmethods}">
+ <customize/>
+ </j2seproject3:junit>
+ </sequential>
+ </macrodef>
+ </target>
+ <target depends="-init-macrodef-testng" if="${testng.available}" name="-init-macrodef-testng-impl">
+ <macrodef name="test-impl" uri="http://www.netbeans.org/ns/j2se-project/3">
+ <attribute default="${includes}" name="includes"/>
+ <attribute default="${excludes}" name="excludes"/>
+ <attribute default="**" name="testincludes"/>
+ <attribute default="" name="testmethods"/>
+ <element implicit="true" name="customize" optional="true"/>
+ <sequential>
+ <j2seproject3:testng excludes="@{excludes}" includes="@{includes}" testincludes="@{testincludes}" testmethods="@{testmethods}">
+ <customize/>
+ </j2seproject3:testng>
+ </sequential>
+ </macrodef>
+ </target>
+ <target depends="-init-macrodef-test-impl,-init-macrodef-junit-impl,-init-macrodef-testng-impl" name="-init-macrodef-test">
+ <macrodef name="test" uri="http://www.netbeans.org/ns/j2se-project/3">
+ <attribute default="${includes}" name="includes"/>
+ <attribute default="${excludes}" name="excludes"/>
+ <attribute default="**" name="testincludes"/>
+ <attribute default="" name="testmethods"/>
+ <sequential>
+ <j2seproject3:test-impl excludes="@{excludes}" includes="@{includes}" testincludes="@{testincludes}" testmethods="@{testmethods}">
+ <customize>
+ <classpath>
+ <path path="${run.test.classpath}"/>
+ </classpath>
+ <jvmarg line="${endorsed.classpath.cmd.line.arg}"/>
+ <jvmarg line="${run.jvmargs}"/>
+ <jvmarg line="${run.jvmargs.ide}"/>
+ </customize>
+ </j2seproject3:test-impl>
+ </sequential>
+ </macrodef>
+ </target>
+ <target if="${junit.available}" name="-init-macrodef-junit-debug" unless="${nb.junit.batch}">
+ <macrodef name="junit-debug" uri="http://www.netbeans.org/ns/j2se-project/3">
+ <attribute default="${includes}" name="includes"/>
+ <attribute default="${excludes}" name="excludes"/>
+ <attribute default="**" name="testincludes"/>
+ <attribute default="" name="testmethods"/>
+ <element name="customize" optional="true"/>
+ <sequential>
+ <property name="junit.forkmode" value="perTest"/>
+ <junit dir="${work.dir}" errorproperty="tests.failed" failureproperty="tests.failed" fork="true" forkmode="${junit.forkmode}" showoutput="true" tempdir="${build.dir}">
+ <test methods="@{testmethods}" name="@{testincludes}" todir="${build.test.results.dir}"/>
+ <syspropertyset>
+ <propertyref prefix="test-sys-prop."/>
+ <mapper from="test-sys-prop.*" to="*" type="glob"/>
+ </syspropertyset>
+ <formatter type="brief" usefile="false"/>
+ <formatter type="xml"/>
+ <jvmarg value="-ea"/>
+ <jvmarg line="${debug-args-line}"/>
+ <jvmarg value="-Xrunjdwp:transport=${debug-transport},address=${jpda.address}"/>
+ <customize/>
+ </junit>
+ </sequential>
+ </macrodef>
+ </target>
+ <target depends="-init-test-properties" if="${nb.junit.batch}" name="-init-macrodef-junit-debug-batch">
+ <macrodef name="junit-debug" uri="http://www.netbeans.org/ns/j2se-project/3">
+ <attribute default="${includes}" name="includes"/>
+ <attribute default="${excludes}" name="excludes"/>
+ <attribute default="**" name="testincludes"/>
+ <attribute default="" name="testmethods"/>
+ <element name="customize" optional="true"/>
+ <sequential>
+ <property name="junit.forkmode" value="perTest"/>
+ <junit dir="${work.dir}" errorproperty="tests.failed" failureproperty="tests.failed" fork="true" forkmode="${junit.forkmode}" showoutput="true" tempdir="${build.dir}">
+ <batchtest todir="${build.test.results.dir}">
+ <fileset dir="${build.test.classes.dir}" excludes="@{excludes},${excludes},${test.binaryexcludes}" includes="${test.binaryincludes}">
+ <filename name="${test.binarytestincludes}"/>
+ </fileset>
+ </batchtest>
+ <syspropertyset>
+ <propertyref prefix="test-sys-prop."/>
+ <mapper from="test-sys-prop.*" to="*" type="glob"/>
+ </syspropertyset>
+ <formatter type="brief" usefile="false"/>
+ <formatter type="xml"/>
+ <jvmarg value="-ea"/>
+ <jvmarg line="${debug-args-line}"/>
+ <jvmarg value="-Xrunjdwp:transport=${debug-transport},address=${jpda.address}"/>
+ <customize/>
+ </junit>
+ </sequential>
+ </macrodef>
+ </target>
+ <target depends="-init-macrodef-junit-debug,-init-macrodef-junit-debug-batch" if="${junit.available}" name="-init-macrodef-junit-debug-impl">
+ <macrodef name="test-debug-impl" uri="http://www.netbeans.org/ns/j2se-project/3">
+ <attribute default="${includes}" name="includes"/>
+ <attribute default="${excludes}" name="excludes"/>
+ <attribute default="**" name="testincludes"/>
+ <attribute default="" name="testmethods"/>
+ <element implicit="true" name="customize" optional="true"/>
+ <sequential>
+ <j2seproject3:junit-debug excludes="@{excludes}" includes="@{includes}" testincludes="@{testincludes}" testmethods="@{testmethods}">
+ <customize/>
+ </j2seproject3:junit-debug>
+ </sequential>
+ </macrodef>
+ </target>
+ <target if="${testng.available}" name="-init-macrodef-testng-debug">
+ <macrodef name="testng-debug" uri="http://www.netbeans.org/ns/j2se-project/3">
+ <attribute default="${main.class}" name="testClass"/>
+ <attribute default="" name="testMethod"/>
+ <element name="customize2" optional="true"/>
+ <sequential>
+ <condition else="-testclass @{testClass}" property="test.class.or.method" value="-methods @{testClass}.@{testMethod}">
+ <isset property="test.method"/>
+ </condition>
+ <condition else="-suitename JNLPConverter -testname @{testClass} ${test.class.or.method}" property="testng.cmd.args" value="@{testClass}">
+ <matches pattern=".*\.xml" string="@{testClass}"/>
+ </condition>
+ <delete dir="${build.test.results.dir}" quiet="true"/>
+ <mkdir dir="${build.test.results.dir}"/>
+ <j2seproject3:debug classname="org.testng.TestNG" classpath="${debug.test.classpath}">
+ <customize>
+ <customize2/>
+ <jvmarg value="-ea"/>
+ <arg line="${testng.debug.mode}"/>
+ <arg line="-d ${build.test.results.dir}"/>
+ <arg line="-listener org.testng.reporters.VerboseReporter"/>
+ <arg line="${testng.cmd.args}"/>
+ </customize>
+ </j2seproject3:debug>
+ </sequential>
+ </macrodef>
+ </target>
+ <target depends="-init-macrodef-testng-debug" if="${testng.available}" name="-init-macrodef-testng-debug-impl">
+ <macrodef name="testng-debug-impl" uri="http://www.netbeans.org/ns/j2se-project/3">
+ <attribute default="${main.class}" name="testClass"/>
+ <attribute default="" name="testMethod"/>
+ <element implicit="true" name="customize2" optional="true"/>
+ <sequential>
+ <j2seproject3:testng-debug testClass="@{testClass}" testMethod="@{testMethod}">
+ <customize2/>
+ </j2seproject3:testng-debug>
+ </sequential>
+ </macrodef>
+ </target>
+ <target depends="-init-macrodef-junit-debug-impl" if="${junit.available}" name="-init-macrodef-test-debug-junit">
+ <macrodef name="test-debug" uri="http://www.netbeans.org/ns/j2se-project/3">
+ <attribute default="${includes}" name="includes"/>
+ <attribute default="${excludes}" name="excludes"/>
+ <attribute default="**" name="testincludes"/>
+ <attribute default="" name="testmethods"/>
+ <attribute default="${main.class}" name="testClass"/>
+ <attribute default="" name="testMethod"/>
+ <sequential>
+ <j2seproject3:test-debug-impl excludes="@{excludes}" includes="@{includes}" testincludes="@{testincludes}" testmethods="@{testmethods}">
+ <customize>
+ <classpath>
+ <path path="${run.test.classpath}"/>
+ </classpath>
+ <jvmarg line="${endorsed.classpath.cmd.line.arg}"/>
+ <jvmarg line="${run.jvmargs}"/>
+ <jvmarg line="${run.jvmargs.ide}"/>
+ </customize>
+ </j2seproject3:test-debug-impl>
+ </sequential>
+ </macrodef>
+ </target>
+ <target depends="-init-macrodef-testng-debug-impl" if="${testng.available}" name="-init-macrodef-test-debug-testng">
+ <macrodef name="test-debug" uri="http://www.netbeans.org/ns/j2se-project/3">
+ <attribute default="${includes}" name="includes"/>
+ <attribute default="${excludes}" name="excludes"/>
+ <attribute default="**" name="testincludes"/>
+ <attribute default="" name="testmethods"/>
+ <attribute default="${main.class}" name="testClass"/>
+ <attribute default="" name="testMethod"/>
+ <sequential>
+ <j2seproject3:testng-debug-impl testClass="@{testClass}" testMethod="@{testMethod}">
+ <customize2>
+ <syspropertyset>
+ <propertyref prefix="test-sys-prop."/>
+ <mapper from="test-sys-prop.*" to="*" type="glob"/>
+ </syspropertyset>
+ </customize2>
+ </j2seproject3:testng-debug-impl>
+ </sequential>
+ </macrodef>
+ </target>
+ <target depends="-init-macrodef-test-debug-junit,-init-macrodef-test-debug-testng" name="-init-macrodef-test-debug"/>
+ <!--
+ pre NB7.2 profiling section; consider it deprecated
+ -->
+ <target depends="-profile-pre-init, init, -profile-post-init, -profile-init-macrodef-profile, -profile-init-check" if="profiler.info.jvmargs.agent" name="profile-init"/>
+ <target if="profiler.info.jvmargs.agent" name="-profile-pre-init">
+ <!-- Empty placeholder for easier customization. -->
+ <!-- You can override this target in the ../build.xml file. -->
+ </target>
+ <target if="profiler.info.jvmargs.agent" name="-profile-post-init">
+ <!-- Empty placeholder for easier customization. -->
+ <!-- You can override this target in the ../build.xml file. -->
+ </target>
+ <target if="profiler.info.jvmargs.agent" name="-profile-init-macrodef-profile">
+ <macrodef name="resolve">
+ <attribute name="name"/>
+ <attribute name="value"/>
+ <sequential>
+ <property name="@{name}" value="${env.@{value}}"/>
+ </sequential>
+ </macrodef>
+ <macrodef name="profile">
+ <attribute default="${main.class}" name="classname"/>
+ <element name="customize" optional="true"/>
+ <sequential>
+ <property environment="env"/>
+ <resolve name="profiler.current.path" value="${profiler.info.pathvar}"/>
+ <java classname="@{classname}" dir="${profiler.info.dir}" failonerror="${java.failonerror}" fork="true" jvm="${profiler.info.jvm}">
+ <jvmarg line="${endorsed.classpath.cmd.line.arg}"/>
+ <jvmarg value="${profiler.info.jvmargs.agent}"/>
+ <jvmarg line="${profiler.info.jvmargs}"/>
+ <env key="${profiler.info.pathvar}" path="${profiler.info.agentpath}:${profiler.current.path}"/>
+ <arg line="${application.args}"/>
+ <classpath>
+ <path path="${run.classpath}"/>
+ </classpath>
+ <syspropertyset>
+ <propertyref prefix="run-sys-prop."/>
+ <mapper from="run-sys-prop.*" to="*" type="glob"/>
+ </syspropertyset>
+ <customize/>
+ </java>
+ </sequential>
+ </macrodef>
+ </target>
+ <target depends="-profile-pre-init, init, -profile-post-init, -profile-init-macrodef-profile" if="profiler.info.jvmargs.agent" name="-profile-init-check">
+ <fail unless="profiler.info.jvm">Must set JVM to use for profiling in profiler.info.jvm</fail>
+ <fail unless="profiler.info.jvmargs.agent">Must set profiler agent JVM arguments in profiler.info.jvmargs.agent</fail>
+ </target>
+ <!--
+ end of pre NB7.2 profiling section
+ -->
+ <target depends="-init-debug-args" name="-init-macrodef-nbjpda">
+ <macrodef name="nbjpdastart" uri="http://www.netbeans.org/ns/j2se-project/1">
+ <attribute default="${main.class}" name="name"/>
+ <attribute default="${debug.classpath}" name="classpath"/>
+ <attribute default="" name="stopclassname"/>
+ <sequential>
+ <nbjpdastart addressproperty="jpda.address" name="@{name}" stopclassname="@{stopclassname}" transport="${debug-transport}">
+ <classpath>
+ <path path="@{classpath}"/>
+ </classpath>
+ </nbjpdastart>
+ </sequential>
+ </macrodef>
+ <macrodef name="nbjpdareload" uri="http://www.netbeans.org/ns/j2se-project/1">
+ <attribute default="${build.classes.dir}" name="dir"/>
+ <sequential>
+ <nbjpdareload>
+ <fileset dir="@{dir}" includes="${fix.classes}">
+ <include name="${fix.includes}*.class"/>
+ </fileset>
+ </nbjpdareload>
+ </sequential>
+ </macrodef>
+ </target>
+ <target name="-init-debug-args">
+ <property name="version-output" value="java version "${ant.java.version}"/>
+ <condition property="have-jdk-older-than-1.4">
+ <or>
+ <contains string="${version-output}" substring="java version "1.0"/>
+ <contains string="${version-output}" substring="java version "1.1"/>
+ <contains string="${version-output}" substring="java version "1.2"/>
+ <contains string="${version-output}" substring="java version "1.3"/>
+ </or>
+ </condition>
+ <condition else="-Xdebug" property="debug-args-line" value="-Xdebug -Xnoagent -Djava.compiler=none">
+ <istrue value="${have-jdk-older-than-1.4}"/>
+ </condition>
+ <condition else="dt_socket" property="debug-transport-by-os" value="dt_shmem">
+ <os family="windows"/>
+ </condition>
+ <condition else="${debug-transport-by-os}" property="debug-transport" value="${debug.transport}">
+ <isset property="debug.transport"/>
+ </condition>
+ </target>
+ <target depends="-init-debug-args" name="-init-macrodef-debug">
+ <macrodef name="debug" uri="http://www.netbeans.org/ns/j2se-project/3">
+ <attribute default="${main.class}" name="classname"/>
+ <attribute default="${debug.classpath}" name="classpath"/>
+ <element name="customize" optional="true"/>
+ <sequential>
+ <java classname="@{classname}" dir="${work.dir}" failonerror="${java.failonerror}" fork="true">
+ <jvmarg line="${endorsed.classpath.cmd.line.arg}"/>
+ <jvmarg line="${debug-args-line}"/>
+ <jvmarg value="-Xrunjdwp:transport=${debug-transport},address=${jpda.address}"/>
+ <jvmarg value="-Dfile.encoding=${runtime.encoding}"/>
+ <redirector errorencoding="${runtime.encoding}" inputencoding="${runtime.encoding}" outputencoding="${runtime.encoding}"/>
+ <jvmarg line="${run.jvmargs}"/>
+ <jvmarg line="${run.jvmargs.ide}"/>
+ <classpath>
+ <path path="@{classpath}"/>
+ </classpath>
+ <syspropertyset>
+ <propertyref prefix="run-sys-prop."/>
+ <mapper from="run-sys-prop.*" to="*" type="glob"/>
+ </syspropertyset>
+ <customize/>
+ </java>
+ </sequential>
+ </macrodef>
+ </target>
+ <target name="-init-macrodef-java">
+ <macrodef name="java" uri="http://www.netbeans.org/ns/j2se-project/1">
+ <attribute default="${main.class}" name="classname"/>
+ <attribute default="${run.classpath}" name="classpath"/>
+ <attribute default="jvm" name="jvm"/>
+ <element name="customize" optional="true"/>
+ <sequential>
+ <java classname="@{classname}" dir="${work.dir}" failonerror="${java.failonerror}" fork="true">
+ <jvmarg line="${endorsed.classpath.cmd.line.arg}"/>
+ <jvmarg value="-Dfile.encoding=${runtime.encoding}"/>
+ <redirector errorencoding="${runtime.encoding}" inputencoding="${runtime.encoding}" outputencoding="${runtime.encoding}"/>
+ <jvmarg line="${run.jvmargs}"/>
+ <jvmarg line="${run.jvmargs.ide}"/>
+ <classpath>
+ <path path="@{classpath}"/>
+ </classpath>
+ <syspropertyset>
+ <propertyref prefix="run-sys-prop."/>
+ <mapper from="run-sys-prop.*" to="*" type="glob"/>
+ </syspropertyset>
+ <customize/>
+ </java>
+ </sequential>
+ </macrodef>
+ </target>
+ <target name="-init-macrodef-copylibs">
+ <macrodef name="copylibs" uri="http://www.netbeans.org/ns/j2se-project/3">
+ <attribute default="${manifest.file}" name="manifest"/>
+ <element name="customize" optional="true"/>
+ <sequential>
+ <property location="${build.classes.dir}" name="build.classes.dir.resolved"/>
+ <pathconvert property="run.classpath.without.build.classes.dir">
+ <path path="${run.classpath}"/>
+ <map from="${build.classes.dir.resolved}" to=""/>
+ </pathconvert>
+ <pathconvert pathsep=" " property="jar.classpath">
+ <path path="${run.classpath.without.build.classes.dir}"/>
+ <chainedmapper>
+ <flattenmapper/>
+ <filtermapper>
+ <replacestring from=" " to="%20"/>
+ </filtermapper>
+ <globmapper from="*" to="lib/*"/>
+ </chainedmapper>
+ </pathconvert>
+ <taskdef classname="org.netbeans.modules.java.j2seproject.copylibstask.CopyLibs" classpath="${libs.CopyLibs.classpath}" name="copylibs"/>
+ <copylibs compress="${jar.compress}" excludeFromCopy="${copylibs.excludes}" index="${jar.index}" indexMetaInf="${jar.index.metainf}" jarfile="${dist.jar}" manifest="@{manifest}" manifestencoding="UTF-8" rebase="${copylibs.rebase}" runtimeclasspath="${run.classpath.without.build.classes.dir}">
+ <fileset dir="${build.classes.dir}" excludes="${dist.archive.excludes}"/>
+ <manifest>
+ <attribute name="Class-Path" value="${jar.classpath}"/>
+ <customize/>
+ </manifest>
+ </copylibs>
+ </sequential>
+ </macrodef>
+ </target>
+ <target name="-init-presetdef-jar">
+ <presetdef name="jar" uri="http://www.netbeans.org/ns/j2se-project/1">
+ <jar compress="${jar.compress}" index="${jar.index}" jarfile="${dist.jar}" manifestencoding="UTF-8">
+ <j2seproject1:fileset dir="${build.classes.dir}" excludes="${dist.archive.excludes}"/>
+ </jar>
+ </presetdef>
+ </target>
+ <target name="-init-ap-cmdline-properties">
+ <property name="annotation.processing.enabled" value="true"/>
+ <property name="annotation.processing.processors.list" value=""/>
+ <property name="annotation.processing.processor.options" value=""/>
+ <property name="annotation.processing.run.all.processors" value="true"/>
+ <property name="javac.processorpath" value="${javac.classpath}"/>
+ <property name="javac.test.processorpath" value="${javac.test.classpath}"/>
+ <condition property="ap.supported.internal" value="true">
+ <not>
+ <matches pattern="1\.[0-5](\..*)?" string="${javac.source}"/>
+ </not>
+ </condition>
+ </target>
+ <target depends="-init-ap-cmdline-properties" if="ap.supported.internal" name="-init-ap-cmdline-supported">
+ <condition else="" property="ap.processors.internal" value="-processor ${annotation.processing.processors.list}">
+ <isfalse value="${annotation.processing.run.all.processors}"/>
+ </condition>
+ <condition else="" property="ap.proc.none.internal" value="-proc:none">
+ <isfalse value="${annotation.processing.enabled}"/>
+ </condition>
+ </target>
+ <target depends="-init-ap-cmdline-properties,-init-ap-cmdline-supported" name="-init-ap-cmdline">
+ <property name="ap.cmd.line.internal" value=""/>
+ </target>
+ <target depends="-pre-init,-init-private,-init-user,-init-project,-do-init,-post-init,-init-check,-init-macrodef-property,-init-macrodef-javac,-init-macrodef-test,-init-macrodef-test-debug,-init-macrodef-nbjpda,-init-macrodef-debug,-init-macrodef-java,-init-presetdef-jar,-init-ap-cmdline" name="init"/>
+ <!--
+ ===================
+ COMPILATION SECTION
+ ===================
+ -->
+ <target name="-deps-jar-init" unless="built-jar.properties">
+ <property location="${build.dir}/built-jar.properties" name="built-jar.properties"/>
+ <delete file="${built-jar.properties}" quiet="true"/>
+ </target>
+ <target if="already.built.jar.${basedir}" name="-warn-already-built-jar">
+ <echo level="warn" message="Cycle detected: JNLPConverter was already built"/>
+ </target>
+ <target depends="init,-deps-jar-init" name="deps-jar" unless="no.deps">
+ <mkdir dir="${build.dir}"/>
+ <touch file="${built-jar.properties}" verbose="false"/>
+ <property file="${built-jar.properties}" prefix="already.built.jar."/>
+ <antcall target="-warn-already-built-jar"/>
+ <propertyfile file="${built-jar.properties}">
+ <entry key="${basedir}" value=""/>
+ </propertyfile>
+ </target>
+ <target depends="init,-check-automatic-build,-clean-after-automatic-build" name="-verify-automatic-build"/>
+ <target depends="init" name="-check-automatic-build">
+ <available file="${build.classes.dir}/.netbeans_automatic_build" property="netbeans.automatic.build"/>
+ </target>
+ <target depends="init" if="netbeans.automatic.build" name="-clean-after-automatic-build">
+ <antcall target="clean"/>
+ </target>
+ <target depends="init,deps-jar" name="-pre-pre-compile">
+ <mkdir dir="${build.classes.dir}"/>
+ </target>
+ <target name="-pre-compile">
+ <!-- Empty placeholder for easier customization. -->
+ <!-- You can override this target in the ../build.xml file. -->
+ </target>
+ <target if="do.depend.true" name="-compile-depend">
+ <pathconvert property="build.generated.subdirs">
+ <dirset dir="${build.generated.sources.dir}" erroronmissingdir="false">
+ <include name="*"/>
+ </dirset>
+ </pathconvert>
+ <j2seproject3:depend srcdir="${src.src.dir}:${build.generated.subdirs}"/>
+ </target>
+ <target depends="init,deps-jar,-pre-pre-compile,-pre-compile, -copy-persistence-xml,-compile-depend" if="have.sources" name="-do-compile">
+ <j2seproject3:javac gensrcdir="${build.generated.sources.dir}"/>
+ <copy todir="${build.classes.dir}">
+ <fileset dir="${src.src.dir}" excludes="${build.classes.excludes},${excludes}" includes="${includes}"/>
+ </copy>
+ </target>
+ <target if="has.persistence.xml" name="-copy-persistence-xml">
+ <mkdir dir="${build.classes.dir}/META-INF"/>
+ <copy todir="${build.classes.dir}/META-INF">
+ <fileset dir="${meta.inf.dir}" includes="persistence.xml orm.xml"/>
+ </copy>
+ </target>
+ <target name="-post-compile">
+ <!-- Empty placeholder for easier customization. -->
+ <!-- You can override this target in the ../build.xml file. -->
+ </target>
+ <target depends="init,deps-jar,-verify-automatic-build,-pre-pre-compile,-pre-compile,-do-compile,-post-compile" description="Compile project." name="compile"/>
+ <target name="-pre-compile-single">
+ <!-- Empty placeholder for easier customization. -->
+ <!-- You can override this target in the ../build.xml file. -->
+ </target>
+ <target depends="init,deps-jar,-pre-pre-compile" name="-do-compile-single">
+ <fail unless="javac.includes">Must select some files in the IDE or set javac.includes</fail>
+ <j2seproject3:force-recompile/>
+ <j2seproject3:javac excludes="" gensrcdir="${build.generated.sources.dir}" includes="${javac.includes}" sourcepath="${src.src.dir}"/>
+ </target>
+ <target name="-post-compile-single">
+ <!-- Empty placeholder for easier customization. -->
+ <!-- You can override this target in the ../build.xml file. -->
+ </target>
+ <target depends="init,deps-jar,-verify-automatic-build,-pre-pre-compile,-pre-compile-single,-do-compile-single,-post-compile-single" name="compile-single"/>
+ <!--
+ ====================
+ JAR BUILDING SECTION
+ ====================
+ -->
+ <target depends="init" name="-pre-pre-jar">
+ <dirname file="${dist.jar}" property="dist.jar.dir"/>
+ <mkdir dir="${dist.jar.dir}"/>
+ </target>
+ <target name="-pre-jar">
+ <!-- Empty placeholder for easier customization. -->
+ <!-- You can override this target in the ../build.xml file. -->
+ </target>
+ <target depends="init" if="do.archive" name="-do-jar-create-manifest" unless="manifest.available">
+ <tempfile deleteonexit="true" destdir="${build.dir}" property="tmp.manifest.file"/>
+ <touch file="${tmp.manifest.file}" verbose="false"/>
+ </target>
+ <target depends="init" if="do.archive+manifest.available" name="-do-jar-copy-manifest">
+ <tempfile deleteonexit="true" destdir="${build.dir}" property="tmp.manifest.file"/>
+ <copy encoding="${manifest.encoding}" file="${manifest.file}" outputencoding="UTF-8" tofile="${tmp.manifest.file}"/>
+ </target>
+ <target depends="init,-do-jar-create-manifest,-do-jar-copy-manifest" if="do.archive+main.class.available" name="-do-jar-set-mainclass">
+ <manifest encoding="UTF-8" file="${tmp.manifest.file}" mode="update">
+ <attribute name="Main-Class" value="${main.class}"/>
+ </manifest>
+ </target>
+ <target depends="init,-do-jar-create-manifest,-do-jar-copy-manifest" if="do.archive+profile.available" name="-do-jar-set-profile">
+ <manifest encoding="UTF-8" file="${tmp.manifest.file}" mode="update">
+ <attribute name="Profile" value="${javac.profile}"/>
+ </manifest>
+ </target>
+ <target depends="init,-do-jar-create-manifest,-do-jar-copy-manifest" if="do.archive+splashscreen.available" name="-do-jar-set-splashscreen">
+ <basename file="${application.splash}" property="splashscreen.basename"/>
+ <mkdir dir="${build.classes.dir}/META-INF"/>
+ <copy failonerror="false" file="${application.splash}" todir="${build.classes.dir}/META-INF"/>
+ <manifest encoding="UTF-8" file="${tmp.manifest.file}" mode="update">
+ <attribute name="SplashScreen-Image" value="META-INF/${splashscreen.basename}"/>
+ </manifest>
+ </target>
+ <target depends="init,-init-macrodef-copylibs,compile,-pre-pre-jar,-pre-jar,-do-jar-create-manifest,-do-jar-copy-manifest,-do-jar-set-mainclass,-do-jar-set-profile,-do-jar-set-splashscreen" if="do.mkdist" name="-do-jar-copylibs">
+ <j2seproject3:copylibs manifest="${tmp.manifest.file}"/>
+ <echo level="info">To run this application from the command line without Ant, try:</echo>
+ <property location="${dist.jar}" name="dist.jar.resolved"/>
+ <echo level="info">java -jar "${dist.jar.resolved}"</echo>
+ </target>
+ <target depends="init,compile,-pre-pre-jar,-pre-jar,-do-jar-create-manifest,-do-jar-copy-manifest,-do-jar-set-mainclass,-do-jar-set-profile,-do-jar-set-splashscreen" if="do.archive" name="-do-jar-jar" unless="do.mkdist">
+ <j2seproject1:jar manifest="${tmp.manifest.file}"/>
+ <property location="${build.classes.dir}" name="build.classes.dir.resolved"/>
+ <property location="${dist.jar}" name="dist.jar.resolved"/>
+ <pathconvert property="run.classpath.with.dist.jar">
+ <path path="${run.classpath}"/>
+ <map from="${build.classes.dir.resolved}" to="${dist.jar.resolved}"/>
+ </pathconvert>
+ <condition else="" property="jar.usage.message" value="To run this application from the command line without Ant, try:${line.separator}${platform.java} -cp ${run.classpath.with.dist.jar} ${main.class}">
+ <isset property="main.class.available"/>
+ </condition>
+ <condition else="debug" property="jar.usage.level" value="info">
+ <isset property="main.class.available"/>
+ </condition>
+ <echo level="${jar.usage.level}" message="${jar.usage.message}"/>
+ </target>
+ <target depends="-do-jar-copylibs" if="do.archive" name="-do-jar-delete-manifest">
+ <delete>
+ <fileset file="${tmp.manifest.file}"/>
+ </delete>
+ </target>
+ <target depends="init,compile,-pre-pre-jar,-pre-jar,-do-jar-create-manifest,-do-jar-copy-manifest,-do-jar-set-mainclass,-do-jar-set-profile,-do-jar-set-splashscreen,-do-jar-jar,-do-jar-delete-manifest" name="-do-jar-without-libraries"/>
+ <target depends="init,compile,-pre-pre-jar,-pre-jar,-do-jar-create-manifest,-do-jar-copy-manifest,-do-jar-set-mainclass,-do-jar-set-profile,-do-jar-set-splashscreen,-do-jar-copylibs,-do-jar-delete-manifest" name="-do-jar-with-libraries"/>
+ <target name="-post-jar">
+ <!-- Empty placeholder for easier customization. -->
+ <!-- You can override this target in the ../build.xml file. -->
+ </target>
+ <target depends="init,compile,-pre-jar,-do-jar-without-libraries,-do-jar-with-libraries,-post-jar" name="-do-jar"/>
+ <target depends="init,compile,-pre-jar,-do-jar,-post-jar" description="Build JAR." name="jar"/>
+ <!--
+ =================
+ EXECUTION SECTION
+ =================
+ -->
+ <target depends="init,compile" description="Run a main class." name="run">
+ <j2seproject1:java>
+ <customize>
+ <arg line="${application.args}"/>
+ </customize>
+ </j2seproject1:java>
+ </target>
+ <target name="-do-not-recompile">
+ <property name="javac.includes.binary" value=""/>
+ </target>
+ <target depends="init,compile-single" name="run-single">
+ <fail unless="run.class">Must select one file in the IDE or set run.class</fail>
+ <j2seproject1:java classname="${run.class}"/>
+ </target>
+ <target depends="init,compile-test-single" name="run-test-with-main">
+ <fail unless="run.class">Must select one file in the IDE or set run.class</fail>
+ <j2seproject1:java classname="${run.class}" classpath="${run.test.classpath}"/>
+ </target>
+ <!--
+ =================
+ DEBUGGING SECTION
+ =================
+ -->
+ <target depends="init" if="netbeans.home" name="-debug-start-debugger">
+ <j2seproject1:nbjpdastart name="${debug.class}"/>
+ </target>
+ <target depends="init" if="netbeans.home" name="-debug-start-debugger-main-test">
+ <j2seproject1:nbjpdastart classpath="${debug.test.classpath}" name="${debug.class}"/>
+ </target>
+ <target depends="init,compile" name="-debug-start-debuggee">
+ <j2seproject3:debug>
+ <customize>
+ <arg line="${application.args}"/>
+ </customize>
+ </j2seproject3:debug>
+ </target>
+ <target depends="init,compile,-debug-start-debugger,-debug-start-debuggee" description="Debug project in IDE." if="netbeans.home" name="debug"/>
+ <target depends="init" if="netbeans.home" name="-debug-start-debugger-stepinto">
+ <j2seproject1:nbjpdastart stopclassname="${main.class}"/>
+ </target>
+ <target depends="init,compile,-debug-start-debugger-stepinto,-debug-start-debuggee" if="netbeans.home" name="debug-stepinto"/>
+ <target depends="init,compile-single" if="netbeans.home" name="-debug-start-debuggee-single">
+ <fail unless="debug.class">Must select one file in the IDE or set debug.class</fail>
+ <j2seproject3:debug classname="${debug.class}"/>
+ </target>
+ <target depends="init,compile-single,-debug-start-debugger,-debug-start-debuggee-single" if="netbeans.home" name="debug-single"/>
+ <target depends="init,compile-test-single" if="netbeans.home" name="-debug-start-debuggee-main-test">
+ <fail unless="debug.class">Must select one file in the IDE or set debug.class</fail>
+ <j2seproject3:debug classname="${debug.class}" classpath="${debug.test.classpath}"/>
+ </target>
+ <target depends="init,compile-test-single,-debug-start-debugger-main-test,-debug-start-debuggee-main-test" if="netbeans.home" name="debug-test-with-main"/>
+ <target depends="init" name="-pre-debug-fix">
+ <fail unless="fix.includes">Must set fix.includes</fail>
+ <property name="javac.includes" value="${fix.includes}.java"/>
+ </target>
+ <target depends="init,-pre-debug-fix,compile-single" if="netbeans.home" name="-do-debug-fix">
+ <j2seproject1:nbjpdareload/>
+ </target>
+ <target depends="init,-pre-debug-fix,-do-debug-fix" if="netbeans.home" name="debug-fix"/>
+ <!--
+ =================
+ PROFILING SECTION
+ =================
+ -->
+ <!--
+ pre NB7.2 profiler integration
+ -->
+ <target depends="profile-init,compile" description="Profile a project in the IDE." if="profiler.info.jvmargs.agent" name="-profile-pre72">
+ <fail unless="netbeans.home">This target only works when run from inside the NetBeans IDE.</fail>
+ <nbprofiledirect>
+ <classpath>
+ <path path="${run.classpath}"/>
+ </classpath>
+ </nbprofiledirect>
+ <profile/>
+ </target>
+ <target depends="profile-init,compile-single" description="Profile a selected class in the IDE." if="profiler.info.jvmargs.agent" name="-profile-single-pre72">
+ <fail unless="profile.class">Must select one file in the IDE or set profile.class</fail>
+ <fail unless="netbeans.home">This target only works when run from inside the NetBeans IDE.</fail>
+ <nbprofiledirect>
+ <classpath>
+ <path path="${run.classpath}"/>
+ </classpath>
+ </nbprofiledirect>
+ <profile classname="${profile.class}"/>
+ </target>
+ <target depends="profile-init,compile-single" if="profiler.info.jvmargs.agent" name="-profile-applet-pre72">
+ <fail unless="netbeans.home">This target only works when run from inside the NetBeans IDE.</fail>
+ <nbprofiledirect>
+ <classpath>
+ <path path="${run.classpath}"/>
+ </classpath>
+ </nbprofiledirect>
+ <profile classname="sun.applet.AppletViewer">
+ <customize>
+ <arg value="${applet.url}"/>
+ </customize>
+ </profile>
+ </target>
+ <target depends="profile-init,compile-test-single" if="profiler.info.jvmargs.agent" name="-profile-test-single-pre72">
+ <fail unless="netbeans.home">This target only works when run from inside the NetBeans IDE.</fail>
+ <nbprofiledirect>
+ <classpath>
+ <path path="${run.test.classpath}"/>
+ </classpath>
+ </nbprofiledirect>
+ <junit dir="${profiler.info.dir}" errorproperty="tests.failed" failureproperty="tests.failed" fork="true" jvm="${profiler.info.jvm}" showoutput="true">
+ <env key="${profiler.info.pathvar}" path="${profiler.info.agentpath}:${profiler.current.path}"/>
+ <jvmarg value="${profiler.info.jvmargs.agent}"/>
+ <jvmarg line="${profiler.info.jvmargs}"/>
+ <test name="${profile.class}"/>
+ <classpath>
+ <path path="${run.test.classpath}"/>
+ </classpath>
+ <syspropertyset>
+ <propertyref prefix="test-sys-prop."/>
+ <mapper from="test-sys-prop.*" to="*" type="glob"/>
+ </syspropertyset>
+ <formatter type="brief" usefile="false"/>
+ <formatter type="xml"/>
+ </junit>
+ </target>
+ <!--
+ end of pre NB72 profiling section
+ -->
+ <target if="netbeans.home" name="-profile-check">
+ <condition property="profiler.configured">
+ <or>
+ <contains casesensitive="true" string="${run.jvmargs.ide}" substring="-agentpath:"/>
+ <contains casesensitive="true" string="${run.jvmargs.ide}" substring="-javaagent:"/>
+ </or>
+ </condition>
+ </target>
+ <target depends="-profile-check,-profile-pre72" description="Profile a project in the IDE." if="profiler.configured" name="profile" unless="profiler.info.jvmargs.agent">
+ <startprofiler/>
+ <antcall target="run"/>
+ </target>
+ <target depends="-profile-check,-profile-single-pre72" description="Profile a selected class in the IDE." if="profiler.configured" name="profile-single" unless="profiler.info.jvmargs.agent">
+ <fail unless="run.class">Must select one file in the IDE or set run.class</fail>
+ <startprofiler/>
+ <antcall target="run-single"/>
+ </target>
+ <target depends="-profile-test-single-pre72" description="Profile a selected test in the IDE." name="profile-test-single"/>
+ <target depends="-profile-check" description="Profile a selected test in the IDE." if="profiler.configured" name="profile-test" unless="profiler.info.jvmargs">
+ <fail unless="test.includes">Must select some files in the IDE or set test.includes</fail>
+ <startprofiler/>
+ <antcall target="test-single"/>
+ </target>
+ <target depends="-profile-check" description="Profile a selected class in the IDE." if="profiler.configured" name="profile-test-with-main">
+ <fail unless="run.class">Must select one file in the IDE or set run.class</fail>
+ <startprofiler/>
+ <antcall target="run-test-with-main"/>
+ </target>
+ <target depends="-profile-check,-profile-applet-pre72" if="profiler.configured" name="profile-applet" unless="profiler.info.jvmargs.agent">
+ <fail unless="applet.url">Must select one file in the IDE or set applet.url</fail>
+ <startprofiler/>
+ <antcall target="run-applet"/>
+ </target>
+ <!--
+ ===============
+ JAVADOC SECTION
+ ===============
+ -->
+ <target depends="init" if="have.sources" name="-javadoc-build">
+ <mkdir dir="${dist.javadoc.dir}"/>
+ <condition else="" property="javadoc.endorsed.classpath.cmd.line.arg" value="-J${endorsed.classpath.cmd.line.arg}">
+ <and>
+ <isset property="endorsed.classpath.cmd.line.arg"/>
+ <not>
+ <equals arg1="${endorsed.classpath.cmd.line.arg}" arg2=""/>
+ </not>
+ </and>
+ </condition>
+ <condition else="" property="bug5101868workaround" value="*.java">
+ <matches pattern="1\.[56](\..*)?" string="${java.version}"/>
+ </condition>
+ <javadoc additionalparam="-J-Dfile.encoding=${file.encoding} ${javadoc.additionalparam}" author="${javadoc.author}" charset="UTF-8" destdir="${dist.javadoc.dir}" docencoding="UTF-8" encoding="${javadoc.encoding.used}" failonerror="true" noindex="${javadoc.noindex}" nonavbar="${javadoc.nonavbar}" notree="${javadoc.notree}" private="${javadoc.private}" source="${javac.source}" splitindex="${javadoc.splitindex}" use="${javadoc.use}" useexternalfile="true" version="${javadoc.version}" windowtitle="${javadoc.windowtitle}">
+ <classpath>
+ <path path="${javac.classpath}"/>
+ </classpath>
+ <fileset dir="${src.src.dir}" excludes="${bug5101868workaround},${excludes}" includes="${includes}">
+ <filename name="**/*.java"/>
+ </fileset>
+ <fileset dir="${build.generated.sources.dir}" erroronmissingdir="false">
+ <include name="**/*.java"/>
+ <exclude name="*.java"/>
+ </fileset>
+ <arg line="${javadoc.endorsed.classpath.cmd.line.arg}"/>
+ </javadoc>
+ <copy todir="${dist.javadoc.dir}">
+ <fileset dir="${src.src.dir}" excludes="${excludes}" includes="${includes}">
+ <filename name="**/doc-files/**"/>
+ </fileset>
+ <fileset dir="${build.generated.sources.dir}" erroronmissingdir="false">
+ <include name="**/doc-files/**"/>
+ </fileset>
+ </copy>
+ </target>
+ <target depends="init,-javadoc-build" if="netbeans.home" name="-javadoc-browse" unless="no.javadoc.preview">
+ <nbbrowse file="${dist.javadoc.dir}/index.html"/>
+ </target>
+ <target depends="init,-javadoc-build,-javadoc-browse" description="Build Javadoc." name="javadoc"/>
+ <!--
+ =========================
+ TEST COMPILATION SECTION
+ =========================
+ -->
+ <target depends="init,compile" if="have.tests" name="-pre-pre-compile-test">
+ <mkdir dir="${build.test.classes.dir}"/>
+ </target>
+ <target name="-pre-compile-test">
+ <!-- Empty placeholder for easier customization. -->
+ <!-- You can override this target in the ../build.xml file. -->
+ </target>
+ <target if="do.depend.true" name="-compile-test-depend">
+ <j2seproject3:depend classpath="${javac.test.classpath}" destdir="${build.test.classes.dir}" srcdir=""/>
+ </target>
+ <target depends="init,deps-jar,compile,-pre-pre-compile-test,-pre-compile-test,-compile-test-depend" if="have.tests" name="-do-compile-test">
+ <j2seproject3:javac apgeneratedsrcdir="${build.test.classes.dir}" classpath="${javac.test.classpath}" debug="true" destdir="${build.test.classes.dir}" processorpath="${javac.test.processorpath}" srcdir=""/>
+ <copy todir="${build.test.classes.dir}"/>
+ </target>
+ <target name="-post-compile-test">
+ <!-- Empty placeholder for easier customization. -->
+ <!-- You can override this target in the ../build.xml file. -->
+ </target>
+ <target depends="init,compile,-pre-pre-compile-test,-pre-compile-test,-do-compile-test,-post-compile-test" name="compile-test"/>
+ <target name="-pre-compile-test-single">
+ <!-- Empty placeholder for easier customization. -->
+ <!-- You can override this target in the ../build.xml file. -->
+ </target>
+ <target depends="init,deps-jar,compile,-pre-pre-compile-test,-pre-compile-test-single" if="have.tests" name="-do-compile-test-single">
+ <fail unless="javac.includes">Must select some files in the IDE or set javac.includes</fail>
+ <j2seproject3:force-recompile destdir="${build.test.classes.dir}"/>
+ <j2seproject3:javac apgeneratedsrcdir="${build.test.classes.dir}" classpath="${javac.test.classpath}" debug="true" destdir="${build.test.classes.dir}" excludes="" includes="${javac.includes}" processorpath="${javac.test.processorpath}" sourcepath="" srcdir=""/>
+ <copy todir="${build.test.classes.dir}"/>
+ </target>
+ <target name="-post-compile-test-single">
+ <!-- Empty placeholder for easier customization. -->
+ <!-- You can override this target in the ../build.xml file. -->
+ </target>
+ <target depends="init,compile,-pre-pre-compile-test,-pre-compile-test-single,-do-compile-test-single,-post-compile-test-single" name="compile-test-single"/>
+ <!--
+ =======================
+ TEST EXECUTION SECTION
+ =======================
+ -->
+ <target depends="init" if="have.tests" name="-pre-test-run">
+ <mkdir dir="${build.test.results.dir}"/>
+ </target>
+ <target depends="init,compile-test,-pre-test-run" if="have.tests" name="-do-test-run">
+ <j2seproject3:test includes="${includes}" testincludes="**/*Test.java"/>
+ </target>
+ <target depends="init,compile-test,-pre-test-run,-do-test-run" if="have.tests" name="-post-test-run">
+ <fail if="tests.failed" unless="ignore.failing.tests">Some tests failed; see details above.</fail>
+ </target>
+ <target depends="init" if="have.tests" name="test-report"/>
+ <target depends="init" if="netbeans.home+have.tests" name="-test-browse"/>
+ <target depends="init,compile-test,-pre-test-run,-do-test-run,test-report,-post-test-run,-test-browse" description="Run unit tests." name="test"/>
+ <target depends="init" if="have.tests" name="-pre-test-run-single">
+ <mkdir dir="${build.test.results.dir}"/>
+ </target>
+ <target depends="init,compile-test-single,-pre-test-run-single" if="have.tests" name="-do-test-run-single">
+ <fail unless="test.includes">Must select some files in the IDE or set test.includes</fail>
+ <j2seproject3:test excludes="" includes="${test.includes}" testincludes="${test.includes}"/>
+ </target>
+ <target depends="init,compile-test-single,-pre-test-run-single,-do-test-run-single" if="have.tests" name="-post-test-run-single">
+ <fail if="tests.failed" unless="ignore.failing.tests">Some tests failed; see details above.</fail>
+ </target>
+ <target depends="init,compile-test-single,-pre-test-run-single,-do-test-run-single,-post-test-run-single" description="Run single unit test." name="test-single"/>
+ <target depends="init,compile-test-single,-pre-test-run-single" if="have.tests" name="-do-test-run-single-method">
+ <fail unless="test.class">Must select some files in the IDE or set test.class</fail>
+ <fail unless="test.method">Must select some method in the IDE or set test.method</fail>
+ <j2seproject3:test excludes="" includes="${javac.includes}" testincludes="${test.class}" testmethods="${test.method}"/>
+ </target>
+ <target depends="init,compile-test-single,-pre-test-run-single,-do-test-run-single-method" if="have.tests" name="-post-test-run-single-method">
+ <fail if="tests.failed" unless="ignore.failing.tests">Some tests failed; see details above.</fail>
+ </target>
+ <target depends="init,compile-test-single,-pre-test-run-single,-do-test-run-single-method,-post-test-run-single-method" description="Run single unit test." name="test-single-method"/>
+ <!--
+ =======================
+ TEST DEBUGGING SECTION
+ =======================
+ -->
+ <target depends="init,compile-test-single,-pre-test-run-single" if="have.tests" name="-debug-start-debuggee-test">
+ <fail unless="test.class">Must select one file in the IDE or set test.class</fail>
+ <j2seproject3:test-debug excludes="" includes="${javac.includes}" testClass="${test.class}" testincludes="${javac.includes}"/>
+ </target>
+ <target depends="init,compile-test-single,-pre-test-run-single" if="have.tests" name="-debug-start-debuggee-test-method">
+ <fail unless="test.class">Must select one file in the IDE or set test.class</fail>
+ <fail unless="test.method">Must select some method in the IDE or set test.method</fail>
+ <j2seproject3:test-debug excludes="" includes="${javac.includes}" testClass="${test.class}" testMethod="${test.method}" testincludes="${test.class}" testmethods="${test.method}"/>
+ </target>
+ <target depends="init,compile-test" if="netbeans.home+have.tests" name="-debug-start-debugger-test">
+ <j2seproject1:nbjpdastart classpath="${debug.test.classpath}" name="${test.class}"/>
+ </target>
+ <target depends="init,compile-test-single,-debug-start-debugger-test,-debug-start-debuggee-test" name="debug-test"/>
+ <target depends="init,compile-test-single,-debug-start-debugger-test,-debug-start-debuggee-test-method" name="debug-test-method"/>
+ <target depends="init,-pre-debug-fix,compile-test-single" if="netbeans.home" name="-do-debug-fix-test">
+ <j2seproject1:nbjpdareload dir="${build.test.classes.dir}"/>
+ </target>
+ <target depends="init,-pre-debug-fix,-do-debug-fix-test" if="netbeans.home" name="debug-fix-test"/>
+ <!--
+ =========================
+ APPLET EXECUTION SECTION
+ =========================
+ -->
+ <target depends="init,compile-single" name="run-applet">
+ <fail unless="applet.url">Must select one file in the IDE or set applet.url</fail>
+ <j2seproject1:java classname="sun.applet.AppletViewer">
+ <customize>
+ <arg value="${applet.url}"/>
+ </customize>
+ </j2seproject1:java>
+ </target>
+ <!--
+ =========================
+ APPLET DEBUGGING SECTION
+ =========================
+ -->
+ <target depends="init,compile-single" if="netbeans.home" name="-debug-start-debuggee-applet">
+ <fail unless="applet.url">Must select one file in the IDE or set applet.url</fail>
+ <j2seproject3:debug classname="sun.applet.AppletViewer">
+ <customize>
+ <arg value="${applet.url}"/>
+ </customize>
+ </j2seproject3:debug>
+ </target>
+ <target depends="init,compile-single,-debug-start-debugger,-debug-start-debuggee-applet" if="netbeans.home" name="debug-applet"/>
+ <!--
+ ===============
+ CLEANUP SECTION
+ ===============
+ -->
+ <target name="-deps-clean-init" unless="built-clean.properties">
+ <property location="${build.dir}/built-clean.properties" name="built-clean.properties"/>
+ <delete file="${built-clean.properties}" quiet="true"/>
+ </target>
+ <target if="already.built.clean.${basedir}" name="-warn-already-built-clean">
+ <echo level="warn" message="Cycle detected: JNLPConverter was already built"/>
+ </target>
+ <target depends="init,-deps-clean-init" name="deps-clean" unless="no.deps">
+ <mkdir dir="${build.dir}"/>
+ <touch file="${built-clean.properties}" verbose="false"/>
+ <property file="${built-clean.properties}" prefix="already.built.clean."/>
+ <antcall target="-warn-already-built-clean"/>
+ <propertyfile file="${built-clean.properties}">
+ <entry key="${basedir}" value=""/>
+ </propertyfile>
+ </target>
+ <target depends="init" name="-do-clean">
+ <delete dir="${build.dir}"/>
+ <delete dir="${dist.dir}" followsymlinks="false" includeemptydirs="true"/>
+ </target>
+ <target name="-post-clean">
+ <!-- Empty placeholder for easier customization. -->
+ <!-- You can override this target in the ../build.xml file. -->
+ </target>
+ <target depends="init,deps-clean,-do-clean,-post-clean" description="Clean build products." name="clean"/>
+ <target name="-check-call-dep">
+ <property file="${call.built.properties}" prefix="already.built."/>
+ <condition property="should.call.dep">
+ <and>
+ <not>
+ <isset property="already.built.${call.subproject}"/>
+ </not>
+ <available file="${call.script}"/>
+ </and>
+ </condition>
+ </target>
+ <target depends="-check-call-dep" if="should.call.dep" name="-maybe-call-dep">
+ <ant antfile="${call.script}" inheritall="false" target="${call.target}">
+ <propertyset>
+ <propertyref prefix="transfer."/>
+ <mapper from="transfer.*" to="*" type="glob"/>
+ </propertyset>
+ </ant>
+ </target>
+</project>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/demo/share/nbproject/jpackage/JNLPConverter/nbproject/genfiles.properties Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,8 @@
+build.xml.data.CRC32=9017e41e
+build.xml.script.CRC32=5cf818d6
+build.xml.stylesheet.CRC32=8064a381@1.80.1.48
+# This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml.
+# Do not edit this file. You may delete it but then the IDE will never regenerate such files for you.
+nbproject/build-impl.xml.data.CRC32=9017e41e
+nbproject/build-impl.xml.script.CRC32=d0290d6d
+nbproject/build-impl.xml.stylesheet.CRC32=830a3534@1.80.1.48
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/demo/share/nbproject/jpackage/JNLPConverter/nbproject/project.properties Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,96 @@
+annotation.processing.enabled=true
+annotation.processing.enabled.in.editor=false
+annotation.processing.processors.list=
+annotation.processing.run.all.processors=true
+annotation.processing.source.output=${build.generated.sources.dir}/ap-source-output
+application.title=JNLPConverter
+application.vendor=
+auxiliary.org-netbeans-spi-editor-hints-projects.perProjectHintSettingsFile=nbproject/cfg_hints.xml
+build.classes.dir=${build.dir}/classes
+build.classes.excludes=**/*.java,**/*.form
+# This directory is removed when the project is cleaned:
+build.dir=build
+build.generated.dir=${build.dir}/generated
+build.generated.sources.dir=${build.dir}/generated-sources
+# Only compile against the classpath explicitly listed here:
+build.sysclasspath=ignore
+build.test.classes.dir=${build.dir}/test/classes
+build.test.results.dir=${build.dir}/test/results
+# Uncomment to specify the preferred debugger connection transport:
+#debug.transport=dt_socket
+debug.classpath=\
+ ${run.classpath}
+debug.test.classpath=\
+ ${run.test.classpath}
+# Files in build.classes.dir which should be excluded from distribution jar
+dist.archive.excludes=
+# This directory is removed when the project is cleaned:
+dist.dir=dist
+dist.jar=${dist.dir}/JNLPConverter.jar
+dist.javadoc.dir=${dist.dir}/javadoc
+endorsed.classpath=
+excludes=
+file.reference.jnlpconverter-src=../../../jpackage/jnlpconverter/src
+includes=**
+jar.archive.disabled=${jnlp.enabled}
+jar.compress=false
+jar.index=${jnlp.enabled}
+javac.classpath=
+# Space-separated list of extra javac options
+javac.compilerargs=
+javac.deprecation=false
+javac.external.vm=true
+javac.processorpath=\
+ ${javac.classpath}
+javac.source=1.8
+javac.target=1.8
+javac.test.classpath=\
+ ${javac.classpath}:\
+ ${build.classes.dir}
+javac.test.processorpath=\
+ ${javac.test.classpath}
+javadoc.additionalparam=
+javadoc.author=false
+javadoc.encoding=${source.encoding}
+javadoc.noindex=false
+javadoc.nonavbar=false
+javadoc.notree=false
+javadoc.private=false
+javadoc.splitindex=true
+javadoc.use=true
+javadoc.version=false
+javadoc.windowtitle=
+jnlp.codebase.type=no.codebase
+jnlp.descriptor=application
+jnlp.enabled=false
+jnlp.mixed.code=default
+jnlp.offline-allowed=false
+jnlp.signed=false
+jnlp.signing=
+jnlp.signing.alias=
+jnlp.signing.keystore=
+main.class=jnlp.converter.Main
+# Optional override of default Application-Library-Allowable-Codebase attribute identifying the locations where your signed RIA is expected to be found.
+manifest.custom.application.library.allowable.codebase=
+# Optional override of default Caller-Allowable-Codebase attribute identifying the domains from which JavaScript code can make calls to your RIA without security prompts.
+manifest.custom.caller.allowable.codebase=
+# Optional override of default Codebase manifest attribute, use to prevent RIAs from being repurposed
+manifest.custom.codebase=
+# Optional override of default Permissions manifest attribute (supported values: sandbox, all-permissions)
+manifest.custom.permissions=
+manifest.file=manifest.mf
+meta.inf.dir=${src.dir}/META-INF
+mkdist.disabled=false
+platform.active=default_platform
+run.classpath=\
+ ${javac.classpath}:\
+ ${build.classes.dir}
+# Space-separated list of JVM arguments used when running the project.
+# You may also define separate properties like run-sys-prop.name=value instead of -Dname=value.
+# To set system properties for unit tests define test-sys-prop.name=value:
+run.jvmargs=
+run.test.classpath=\
+ ${javac.test.classpath}:\
+ ${build.test.classes.dir}
+source.encoding=UTF-8
+src.src.dir=${file.reference.jnlpconverter-src}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/demo/share/nbproject/jpackage/JNLPConverter/nbproject/project.xml Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://www.netbeans.org/ns/project/1">
+ <type>org.netbeans.modules.java.j2seproject</type>
+ <configuration>
+ <data xmlns="http://www.netbeans.org/ns/j2se-project/3">
+ <name>JNLPConverter</name>
+ <source-roots>
+ <root id="src.src.dir" name="Source Packages"/>
+ </source-roots>
+ <test-roots/>
+ </data>
+ </configuration>
+</project>
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/packager/AppRuntimeImageBuilder.java Thu Jun 27 22:03:19 2019 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,146 +0,0 @@
-/*
- * Copyright (c) 2016, 2017, 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.tools.jlink.internal.packager;
-
-
-import jdk.tools.jlink.builder.DefaultImageBuilder;
-import jdk.tools.jlink.internal.Jlink;
-import jdk.tools.jlink.internal.JlinkTask;
-import jdk.tools.jlink.plugin.Plugin;
-
-import java.io.File;
-import java.io.IOException;
-import java.lang.module.ModuleFinder;
-import java.nio.ByteOrder;
-import java.nio.file.Path;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * AppRuntimeImageBuilder is a private API used only by the Java Packager to generate
- * a Java runtime image using jlink. AppRuntimeImageBuilder encapsulates the
- * arguments that jlink requires to generate this image. To create the image call the
- * build() method.
- */
-public final class AppRuntimeImageBuilder {
- private Path outputDir = null;
- private Map<String, String> launchers = Collections.emptyMap();
- private List<Path> modulePath = null;
- private Set<String> addModules = null;
- private Set<String> limitModules = null;
- private String excludeFileList = null;
- private Map<String, String> userArguments = null;
- private Boolean stripNativeCommands = null;
-
- public AppRuntimeImageBuilder() {}
-
- public void setOutputDir(Path value) {
- outputDir = value;
- }
-
- public void setLaunchers(Map<String, String> value) {
- launchers = value;
- }
-
- public void setModulePath(List<Path> value) {
- modulePath = value;
- }
-
- public void setAddModules(Set<String> value) {
- addModules = value;
- }
-
- public void setLimitModules(Set<String> value) {
- limitModules = value;
- }
-
- public void setExcludeFileList(String value) {
- excludeFileList = value;
- }
-
- public void setStripNativeCommands(boolean value) {
- stripNativeCommands = value;
- }
-
- public void setUserArguments(Map<String, String> value) {
- userArguments = value;
- }
-
- public void build() throws IOException {
- // jlink main arguments
- Jlink.JlinkConfiguration jlinkConfig =
- new Jlink.JlinkConfiguration(new File("").toPath(), // Unused
- addModules,
- ByteOrder.nativeOrder(),
- moduleFinder(modulePath,
- limitModules, addModules));
-
- // plugin configuration
- List<Plugin> plugins = new ArrayList<Plugin>();
-
- if (stripNativeCommands) {
- plugins.add(Jlink.newPlugin(
- "strip-native-commands",
- Collections.singletonMap("strip-native-commands", "on"),
- null));
- }
-
- if (excludeFileList != null && !excludeFileList.isEmpty()) {
- plugins.add(Jlink.newPlugin(
- "exclude-files",
- Collections.singletonMap("exclude-files", excludeFileList),
- null));
- }
-
- // add user supplied jlink arguments
- for (Map.Entry<String, String> entry : userArguments.entrySet()) {
- String key = entry.getKey();
- String value = entry.getValue();
- plugins.add(Jlink.newPlugin(key,
- Collections.singletonMap(key, value),
- null));
- }
-
- // build the image
- Jlink.PluginsConfiguration pluginConfig = new Jlink.PluginsConfiguration(
- plugins, new DefaultImageBuilder(outputDir, launchers), null);
- Jlink jlink = new Jlink();
- jlink.build(jlinkConfig, pluginConfig);
- }
-
- /*
- * Returns a ModuleFinder that limits observability to the given root
- * modules, their transitive dependences, plus a set of other modules.
- */
- public static ModuleFinder moduleFinder(List<Path> modulepaths,
- Set<String> roots,
- Set<String> otherModules) {
- return JlinkTask.newModuleFinder(modulepaths, roots, otherModules);
- }
-}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxAppBundler.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,175 @@
+/*
+ * 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.jpackage.internal;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.text.MessageFormat;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Map;
+import java.util.ResourceBundle;
+
+import static jdk.jpackage.internal.StandardBundlerParam.*;
+
+public class LinuxAppBundler extends AbstractImageBundler {
+
+ private static final ResourceBundle I18N = ResourceBundle.getBundle(
+ "jdk.jpackage.internal.resources.LinuxResources");
+
+ static final BundlerParamInfo<File> ICON_PNG =
+ new StandardBundlerParam<>(
+ "icon.png",
+ File.class,
+ params -> {
+ File f = ICON.fetchFrom(params);
+ if (f != null && !f.getName().toLowerCase().endsWith(".png")) {
+ Log.error(MessageFormat.format(
+ I18N.getString("message.icon-not-png"), f));
+ return null;
+ }
+ return f;
+ },
+ (s, p) -> new File(s));
+
+ static final BundlerParamInfo<String> LINUX_INSTALL_DIR =
+ new StandardBundlerParam<>(
+ "linux-install-dir",
+ String.class,
+ params -> {
+ String dir = INSTALL_DIR.fetchFrom(params);
+ if (dir != null) {
+ if (dir.endsWith("/")) {
+ dir = dir.substring(0, dir.length()-1);
+ }
+ return dir;
+ }
+ return "/opt";
+ },
+ (s, p) -> s
+ );
+
+ static final BundlerParamInfo<String> LINUX_PACKAGE_DEPENDENCIES =
+ new StandardBundlerParam<>(
+ Arguments.CLIOptions.LINUX_PACKAGE_DEPENDENCIES.getId(),
+ String.class,
+ params -> {
+ return "";
+ },
+ (s, p) -> s
+ );
+
+ @Override
+ public boolean validate(Map<String, ? super Object> params)
+ throws ConfigException {
+ try {
+ if (params == null) throw new ConfigException(
+ I18N.getString("error.parameters-null"),
+ I18N.getString("error.parameters-null.advice"));
+
+ return doValidate(params);
+ } catch (RuntimeException re) {
+ if (re.getCause() instanceof ConfigException) {
+ throw (ConfigException) re.getCause();
+ } else {
+ throw new ConfigException(re);
+ }
+ }
+ }
+
+ private boolean doValidate(Map<String, ? super Object> params)
+ throws ConfigException {
+
+ imageBundleValidation(params);
+
+ return true;
+ }
+
+ // it is static for the sake of sharing with "installer" bundlers
+ // that may skip calls to validate/bundle in this class!
+ static File getRootDir(File outDir, Map<String, ? super Object> params) {
+ return new File(outDir, APP_NAME.fetchFrom(params));
+ }
+
+ File doBundle(Map<String, ? super Object> params, File outputDirectory,
+ boolean dependentTask) throws PackagerException {
+ if (StandardBundlerParam.isRuntimeInstaller(params)) {
+ return PREDEFINED_RUNTIME_IMAGE.fetchFrom(params);
+ } else {
+ return doAppBundle(params, outputDirectory, dependentTask);
+ }
+ }
+
+ private File doAppBundle(Map<String, ? super Object> params,
+ File outputDirectory, boolean dependentTask) throws PackagerException {
+ try {
+ File rootDirectory = createRoot(params, outputDirectory,
+ dependentTask, APP_NAME.fetchFrom(params));
+ AbstractAppImageBuilder appBuilder = new LinuxAppImageBuilder(
+ params, outputDirectory.toPath());
+ if (PREDEFINED_RUNTIME_IMAGE.fetchFrom(params) == null ) {
+ JLinkBundlerHelper.execute(params, appBuilder);
+ } else {
+ StandardBundlerParam.copyPredefinedRuntimeImage(
+ params, appBuilder);
+ }
+ return rootDirectory;
+ } catch (PackagerException pe) {
+ throw pe;
+ } catch (Exception ex) {
+ Log.verbose(ex);
+ throw new PackagerException(ex);
+ }
+ }
+
+ @Override
+ public String getName() {
+ return I18N.getString("app.bundler.name");
+ }
+
+ @Override
+ public String getID() {
+ return "linux.app";
+ }
+
+ @Override
+ public String getBundleType() {
+ return "IMAGE";
+ }
+
+ @Override
+ public File execute(Map<String, ? super Object> params,
+ File outputParentDir) throws PackagerException {
+ return doBundle(params, outputParentDir, false);
+ }
+
+ @Override
+ public boolean supported(boolean runtimeInstaller) {
+ return true;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxAppImageBuilder.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,227 @@
+/*
+ * Copyright (c) 2015, 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.jpackage.internal;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.UncheckedIOException;
+import java.io.Writer;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.attribute.PosixFilePermission;
+import java.text.MessageFormat;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.ResourceBundle;
+import java.util.Set;
+
+import static jdk.jpackage.internal.StandardBundlerParam.*;
+
+public class LinuxAppImageBuilder extends AbstractAppImageBuilder {
+
+ private static final ResourceBundle I18N = ResourceBundle.getBundle(
+ "jdk.jpackage.internal.resources.LinuxResources");
+
+ private static final String LIBRARY_NAME = "libapplauncher.so";
+
+ private final Path root;
+ private final Path appDir;
+ private final Path appModsDir;
+ private final Path runtimeDir;
+ private final Path binDir;
+ private final Path mdir;
+
+ private final Map<String, ? super Object> params;
+
+ public static final BundlerParamInfo<File> ICON_PNG =
+ new StandardBundlerParam<>(
+ "icon.png",
+ File.class,
+ params -> {
+ File f = ICON.fetchFrom(params);
+ if (f != null && !f.getName().toLowerCase().endsWith(".png")) {
+ Log.error(MessageFormat.format(I18N.getString(
+ "message.icon-not-png"), f));
+ return null;
+ }
+ return f;
+ },
+ (s, p) -> new File(s));
+
+ public LinuxAppImageBuilder(Map<String, Object> config, Path imageOutDir)
+ throws IOException {
+ super(config,
+ imageOutDir.resolve(APP_NAME.fetchFrom(config) + "/runtime"));
+
+ Objects.requireNonNull(imageOutDir);
+
+ this.root = imageOutDir.resolve(APP_NAME.fetchFrom(config));
+ this.appDir = root.resolve("app");
+ this.appModsDir = appDir.resolve("mods");
+ this.runtimeDir = root.resolve("runtime");
+ 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);
+ }
+
+ public LinuxAppImageBuilder(String appName, Path imageOutDir)
+ throws IOException {
+ super(null, imageOutDir.resolve(appName));
+
+ Objects.requireNonNull(imageOutDir);
+
+ this.root = imageOutDir.resolve(appName);
+ this.appDir = null;
+ this.appModsDir = null;
+ this.runtimeDir = null;
+ this.binDir = null;
+ this.mdir = null;
+ this.params = new HashMap<>();
+ }
+
+ private void writeEntry(InputStream in, Path dstFile) throws IOException {
+ Files.createDirectories(dstFile.getParent());
+ Files.copy(in, dstFile);
+ }
+
+ // it is static for the sake of sharing with "installer" bundlers
+ // that may skip calls to validate/bundle in this class!
+ public static File getRootDir(File outDir,
+ Map<String, ? super Object> params) {
+ 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);
+ }
+
+ private static String getLauncherName(Map<String, ? super Object> params) {
+ return APP_NAME.fetchFrom(params);
+ }
+
+ public static String getLauncherCfgName(
+ Map<String, ? super Object> params) {
+ return "app" + File.separator + APP_NAME.fetchFrom(params) + ".cfg";
+ }
+
+ @Override
+ public Path getAppDir() {
+ return appDir;
+ }
+
+ @Override
+ public Path getAppModsDir() {
+ return appModsDir;
+ }
+
+ @Override
+ 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, binDir.resolve(LIBRARY_NAME));
+ }
+
+ // create the additional launchers, if any
+ List<Map<String, ? super Object>> entryPoints
+ = StandardBundlerParam.ADD_LAUNCHERS.fetchFrom(params);
+ for (Map<String, ? super Object> entryPoint : entryPoints) {
+ createLauncherForEntryPoint(
+ AddLauncherArguments.merge(originalParams, entryPoint));
+ }
+
+ // Copy class path entries to Java folder
+ copyApplication();
+
+ // Copy icon to Resources folder
+ copyIcon();
+ }
+
+ @Override
+ public void prepareJreFiles() throws IOException {}
+
+ private void createLauncherForEntryPoint(
+ Map<String, ? super Object> params) throws IOException {
+ // Copy executable to Linux folder
+ Path executableFile = binDir.resolve(getLauncherName(params));
+ try (InputStream is_launcher =
+ getResourceAsStream("jpackageapplauncher")) {
+ writeEntry(is_launcher, executableFile);
+ }
+
+ executableFile.toFile().setExecutable(true, false);
+ executableFile.toFile().setWritable(true, true);
+
+ writeCfgFile(params, root.resolve(getLauncherCfgName(params)).toFile(),
+ "$APPDIR/runtime");
+ }
+
+ private void copyIcon() throws IOException {
+ File icon = ICON_PNG.fetchFrom(params);
+ if (icon != null) {
+ File iconTarget = new File(binDir.toFile(),
+ APP_NAME.fetchFrom(params) + ".png");
+ IOUtils.copyFile(icon, iconTarget);
+ }
+ }
+
+ private void copyApplication() 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(appDir, srcdir, fname);
+ }
+ }
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxDebBundler.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,854 @@
+/*
+ * 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.jpackage.internal;
+
+import javax.imageio.ImageIO;
+import java.awt.image.BufferedImage;
+import java.io.*;
+import java.nio.file.Files;
+import java.nio.file.attribute.PosixFilePermission;
+import java.nio.file.attribute.PosixFilePermissions;
+import java.text.MessageFormat;
+import java.util.*;
+import java.util.regex.Pattern;
+
+import static jdk.jpackage.internal.StandardBundlerParam.*;
+import static jdk.jpackage.internal.LinuxAppBundler.ICON_PNG;
+import static jdk.jpackage.internal.LinuxAppBundler.LINUX_INSTALL_DIR;
+import static jdk.jpackage.internal.LinuxAppBundler.LINUX_PACKAGE_DEPENDENCIES;
+
+public class LinuxDebBundler extends AbstractBundler {
+
+ private static final ResourceBundle I18N = ResourceBundle.getBundle(
+ "jdk.jpackage.internal.resources.LinuxResources");
+
+ public static final BundlerParamInfo<LinuxAppBundler> APP_BUNDLER =
+ new StandardBundlerParam<>(
+ "linux.app.bundler",
+ LinuxAppBundler.class,
+ params -> new LinuxAppBundler(),
+ (s, p) -> null);
+
+ // Debian rules for package naming are used here
+ // https://www.debian.org/doc/debian-policy/ch-controlfields.html#s-f-Source
+ //
+ // Package names must consist only of lower case letters (a-z),
+ // digits (0-9), plus (+) and minus (-) signs, and periods (.).
+ // They must be at least two characters long and
+ // must start with an alphanumeric character.
+ //
+ private static final Pattern DEB_BUNDLE_NAME_PATTERN =
+ Pattern.compile("^[a-z][a-z\\d\\+\\-\\.]+");
+
+ public static final BundlerParamInfo<String> BUNDLE_NAME =
+ new StandardBundlerParam<> (
+ Arguments.CLIOptions.LINUX_BUNDLE_NAME.getId(),
+ String.class,
+ params -> {
+ String nm = APP_NAME.fetchFrom(params);
+
+ if (nm == null) return null;
+
+ // make sure to lower case and spaces/underscores become dashes
+ nm = nm.toLowerCase().replaceAll("[ _]", "-");
+ return nm;
+ },
+ (s, p) -> {
+ if (!DEB_BUNDLE_NAME_PATTERN.matcher(s).matches()) {
+ throw new IllegalArgumentException(new ConfigException(
+ MessageFormat.format(I18N.getString(
+ "error.invalid-value-for-package-name"), s),
+ I18N.getString(
+ "error.invalid-value-for-package-name.advice")));
+ }
+
+ return s;
+ });
+
+ public static final BundlerParamInfo<String> FULL_PACKAGE_NAME =
+ new StandardBundlerParam<> (
+ "linux.deb.fullPackageName",
+ String.class,
+ params -> BUNDLE_NAME.fetchFrom(params) + "-"
+ + VERSION.fetchFrom(params),
+ (s, p) -> s);
+
+ public static final BundlerParamInfo<File> DEB_IMAGE_DIR =
+ new StandardBundlerParam<>(
+ "linux.deb.imageDir",
+ File.class,
+ params -> {
+ File imagesRoot = IMAGES_ROOT.fetchFrom(params);
+ if (!imagesRoot.exists()) imagesRoot.mkdirs();
+ return new File(new File(imagesRoot, "linux-deb.image"),
+ FULL_PACKAGE_NAME.fetchFrom(params));
+ },
+ (s, p) -> new File(s));
+
+ public static final BundlerParamInfo<File> APP_IMAGE_ROOT =
+ new StandardBundlerParam<>(
+ "linux.deb.imageRoot",
+ File.class,
+ params -> {
+ File imageDir = DEB_IMAGE_DIR.fetchFrom(params);
+ return new File(imageDir, LINUX_INSTALL_DIR.fetchFrom(params));
+ },
+ (s, p) -> new File(s));
+
+ public static final BundlerParamInfo<File> CONFIG_DIR =
+ new StandardBundlerParam<>(
+ "linux.deb.configDir",
+ File.class,
+ params -> new File(DEB_IMAGE_DIR.fetchFrom(params), "DEBIAN"),
+ (s, p) -> new File(s));
+
+ public static final BundlerParamInfo<String> EMAIL =
+ new StandardBundlerParam<> (
+ Arguments.CLIOptions.LINUX_DEB_MAINTAINER.getId(),
+ String.class,
+ params -> "Unknown",
+ (s, p) -> s);
+
+ public static final BundlerParamInfo<String> MAINTAINER =
+ new StandardBundlerParam<> (
+ BundleParams.PARAM_MAINTAINER,
+ String.class,
+ params -> VENDOR.fetchFrom(params) + " <"
+ + EMAIL.fetchFrom(params) + ">",
+ (s, p) -> s);
+
+ public static final BundlerParamInfo<String> LICENSE_TEXT =
+ new StandardBundlerParam<> (
+ "linux.deb.licenseText",
+ String.class,
+ params -> {
+ try {
+ String licenseFile = LICENSE_FILE.fetchFrom(params);
+ if (licenseFile != null) {
+ return Files.readString(new File(licenseFile).toPath());
+ }
+ } catch (Exception e) {
+ Log.verbose(e);
+ }
+ return "Unknown";
+ },
+ (s, p) -> s);
+
+ public static final BundlerParamInfo<String> XDG_FILE_PREFIX =
+ new StandardBundlerParam<> (
+ "linux.xdg-prefix",
+ String.class,
+ params -> {
+ try {
+ String vendor;
+ if (params.containsKey(VENDOR.getID())) {
+ vendor = VENDOR.fetchFrom(params);
+ } else {
+ vendor = "jpackage";
+ }
+ String appName = APP_NAME.fetchFrom(params);
+
+ return (appName + "-" + vendor).replaceAll("\\s", "");
+ } catch (Exception e) {
+ Log.verbose(e);
+ }
+ return "unknown-MimeInfo.xml";
+ },
+ (s, p) -> s);
+
+ public static final BundlerParamInfo<String> MENU_GROUP =
+ new StandardBundlerParam<>(
+ Arguments.CLIOptions.LINUX_MENU_GROUP.getId(),
+ String.class,
+ params -> I18N.getString("param.menu-group.default"),
+ (s, p) -> s
+ );
+
+ private final static String DEFAULT_ICON = "javalogo_white_32.png";
+ private final static String DEFAULT_CONTROL_TEMPLATE = "template.control";
+ private final static String DEFAULT_PRERM_TEMPLATE = "template.prerm";
+ private final static String DEFAULT_PREINSTALL_TEMPLATE =
+ "template.preinst";
+ private final static String DEFAULT_POSTRM_TEMPLATE = "template.postrm";
+ private final static String DEFAULT_POSTINSTALL_TEMPLATE =
+ "template.postinst";
+ private final static String DEFAULT_COPYRIGHT_TEMPLATE =
+ "template.copyright";
+ private final static String DEFAULT_DESKTOP_FILE_TEMPLATE =
+ "template.desktop";
+
+ public final static String TOOL_DPKG = "dpkg-deb";
+
+ public static boolean testTool(String toolName, String minVersion) {
+ try {
+ ProcessBuilder pb = new ProcessBuilder(
+ toolName,
+ "--version");
+ // not interested in the output
+ IOUtils.exec(pb, true, null);
+ } catch (Exception e) {
+ Log.verbose(MessageFormat.format(I18N.getString(
+ "message.test-for-tool"), toolName, e.getMessage()));
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public boolean validate(Map<String, ? super Object> params)
+ throws ConfigException {
+ try {
+ if (params == null) throw new ConfigException(
+ I18N.getString("error.parameters-null"),
+ I18N.getString("error.parameters-null.advice"));
+
+ //run basic validation to ensure requirements are met
+ //we are not interested in return code, only possible exception
+ APP_BUNDLER.fetchFrom(params).validate(params);
+
+ // NOTE: Can we validate that the required tools are available
+ // before we start?
+ if (!testTool(TOOL_DPKG, "1")){
+ throw new ConfigException(MessageFormat.format(
+ I18N.getString("error.tool-not-found"), TOOL_DPKG),
+ I18N.getString("error.tool-not-found.advice"));
+ }
+
+
+ // Show warning is license file is missing
+ String licenseFile = LICENSE_FILE.fetchFrom(params);
+ if (licenseFile == null) {
+ Log.verbose(I18N.getString("message.debs-like-licenses"));
+ }
+
+ // only one mime type per association, at least one file extention
+ List<Map<String, ? super Object>> associations =
+ FILE_ASSOCIATIONS.fetchFrom(params);
+ if (associations != null) {
+ for (int i = 0; i < associations.size(); i++) {
+ Map<String, ? super Object> assoc = associations.get(i);
+ List<String> mimes = FA_CONTENT_TYPE.fetchFrom(assoc);
+ if (mimes == null || mimes.isEmpty()) {
+ String msgKey =
+ "error.no-content-types-for-file-association";
+ throw new ConfigException(
+ MessageFormat.format(I18N.getString(msgKey), i),
+ I18N.getString(msgKey + ".advise"));
+
+ } else if (mimes.size() > 1) {
+ String msgKey =
+ "error.too-many-content-types-for-file-association";
+ throw new ConfigException(
+ MessageFormat.format(I18N.getString(msgKey), i),
+ I18N.getString(msgKey + ".advise"));
+ }
+ }
+ }
+
+ // bundle name has some restrictions
+ // the string converter will throw an exception if invalid
+ BUNDLE_NAME.getStringConverter().apply(
+ BUNDLE_NAME.fetchFrom(params), params);
+
+ return true;
+ } catch (RuntimeException re) {
+ if (re.getCause() instanceof ConfigException) {
+ throw (ConfigException) re.getCause();
+ } else {
+ throw new ConfigException(re);
+ }
+ }
+ }
+
+ private boolean prepareProto(Map<String, ? super Object> params)
+ throws PackagerException, IOException {
+ File appImage = StandardBundlerParam.getPredefinedAppImage(params);
+ File appDir = null;
+
+ // we either have an application image or need to build one
+ if (appImage != null) {
+ appDir = new File(APP_IMAGE_ROOT.fetchFrom(params),
+ APP_NAME.fetchFrom(params));
+ // copy everything from appImage dir into appDir/name
+ IOUtils.copyRecursive(appImage.toPath(), appDir.toPath());
+ } else {
+ appDir = APP_BUNDLER.fetchFrom(params).doBundle(params,
+ APP_IMAGE_ROOT.fetchFrom(params), true);
+ }
+ return appDir != null;
+ }
+
+ public File bundle(Map<String, ? super Object> params,
+ File outdir) throws PackagerException {
+
+ IOUtils.writableOutputDir(outdir.toPath());
+
+ // we want to create following structure
+ // <package-name>
+ // DEBIAN
+ // control (file with main package details)
+ // menu (request to create menu)
+ // ... other control files if needed ....
+ // opt (by default)
+ // AppFolder (this is where app image goes)
+ // launcher executable
+ // app
+ // runtime
+
+ File imageDir = DEB_IMAGE_DIR.fetchFrom(params);
+ File configDir = CONFIG_DIR.fetchFrom(params);
+
+ try {
+
+ imageDir.mkdirs();
+ configDir.mkdirs();
+ if (prepareProto(params) && prepareProjectConfig(params)) {
+ return buildDeb(params, outdir);
+ }
+ return null;
+ } catch (IOException ex) {
+ Log.verbose(ex);
+ throw new PackagerException(ex);
+ }
+ }
+
+ /*
+ * set permissions with a string like "rwxr-xr-x"
+ *
+ * This cannot be directly backport to 22u which is built with 1.6
+ */
+ private void setPermissions(File file, String permissions) {
+ Set<PosixFilePermission> filePermissions =
+ PosixFilePermissions.fromString(permissions);
+ try {
+ if (file.exists()) {
+ Files.setPosixFilePermissions(file.toPath(), filePermissions);
+ }
+ } catch (IOException ex) {
+ Log.error(ex.getMessage());
+ Log.verbose(ex);
+ }
+
+ }
+
+ private String getArch() {
+ String arch = System.getProperty("os.arch");
+ if ("i386".equals(arch))
+ return "i386";
+ else
+ return "amd64";
+ }
+
+ private long getInstalledSizeKB(Map<String, ? super Object> params) {
+ return getInstalledSizeKB(APP_IMAGE_ROOT.fetchFrom(params)) >> 10;
+ }
+
+ private long getInstalledSizeKB(File dir) {
+ long count = 0;
+ File[] children = dir.listFiles();
+ if (children != null) {
+ for (File file : children) {
+ if (file.isFile()) {
+ count += file.length();
+ }
+ else if (file.isDirectory()) {
+ count += getInstalledSizeKB(file);
+ }
+ }
+ }
+ return count;
+ }
+
+ private boolean prepareProjectConfig(Map<String, ? super Object> params)
+ throws IOException {
+ 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(binDir, params);
+ File icon = ICON_PNG.fetchFrom(params);
+ if (!StandardBundlerParam.isRuntimeInstaller(params)) {
+ // prepare installer icon
+ if (icon == null || !icon.exists()) {
+ fetchResource(iconTarget.getName(),
+ I18N.getString("resource.menu-icon"),
+ DEFAULT_ICON,
+ iconTarget,
+ VERBOSE.fetchFrom(params),
+ RESOURCE_DIR.fetchFrom(params));
+ } else {
+ fetchResource(iconTarget.getName(),
+ I18N.getString("resource.menu-icon"),
+ icon,
+ iconTarget,
+ VERBOSE.fetchFrom(params),
+ RESOURCE_DIR.fetchFrom(params));
+ }
+ }
+
+ StringBuilder installScripts = new StringBuilder();
+ StringBuilder removeScripts = new StringBuilder();
+ for (Map<String, ? super Object> addLauncher :
+ ADD_LAUNCHERS.fetchFrom(params)) {
+ Map<String, String> addLauncherData =
+ createReplacementData(addLauncher);
+ addLauncherData.put("APPLICATION_FS_NAME",
+ data.get("APPLICATION_FS_NAME"));
+ addLauncherData.put("DESKTOP_MIMES", "");
+
+ if (!StandardBundlerParam.isRuntimeInstaller(params)) {
+ // prepare desktop shortcut
+ try (Writer w = Files.newBufferedWriter(
+ getConfig_DesktopShortcutFile(
+ binDir, addLauncher).toPath())) {
+ String content = preprocessTextResource(
+ getConfig_DesktopShortcutFile(binDir,
+ addLauncher).getName(),
+ I18N.getString("resource.menu-shortcut-descriptor"),
+ DEFAULT_DESKTOP_FILE_TEMPLATE,
+ addLauncherData,
+ VERBOSE.fetchFrom(params),
+ RESOURCE_DIR.fetchFrom(params));
+ w.write(content);
+ }
+ }
+
+ // prepare installer icon
+ iconTarget = getConfig_IconFile(binDir, addLauncher);
+ icon = ICON_PNG.fetchFrom(addLauncher);
+ if (icon == null || !icon.exists()) {
+ fetchResource(iconTarget.getName(),
+ I18N.getString("resource.menu-icon"),
+ DEFAULT_ICON,
+ iconTarget,
+ VERBOSE.fetchFrom(params),
+ RESOURCE_DIR.fetchFrom(params));
+ } else {
+ fetchResource(iconTarget.getName(),
+ I18N.getString("resource.menu-icon"),
+ icon,
+ iconTarget,
+ VERBOSE.fetchFrom(params),
+ RESOURCE_DIR.fetchFrom(params));
+ }
+
+ // postinst copying of desktop icon
+ installScripts.append(
+ " xdg-desktop-menu install --novendor ");
+ installScripts.append(LINUX_INSTALL_DIR.fetchFrom(params));
+ installScripts.append("/");
+ installScripts.append(data.get("APPLICATION_FS_NAME"));
+ installScripts.append("/");
+ installScripts.append(
+ addLauncherData.get("APPLICATION_LAUNCHER_FILENAME"));
+ installScripts.append(".desktop\n");
+
+ // postrm cleanup of desktop icon
+ removeScripts.append(
+ " xdg-desktop-menu uninstall --novendor ");
+ removeScripts.append(LINUX_INSTALL_DIR.fetchFrom(params));
+ removeScripts.append("/");
+ removeScripts.append(data.get("APPLICATION_FS_NAME"));
+ removeScripts.append("/");
+ removeScripts.append(
+ addLauncherData.get("APPLICATION_LAUNCHER_FILENAME"));
+ removeScripts.append(".desktop\n");
+ }
+ data.put("ADD_LAUNCHERS_INSTALL", installScripts.toString());
+ data.put("ADD_LAUNCHERS_REMOVE", removeScripts.toString());
+
+ List<Map<String, ? super Object>> associations =
+ FILE_ASSOCIATIONS.fetchFrom(params);
+ data.put("FILE_ASSOCIATION_INSTALL", "");
+ data.put("FILE_ASSOCIATION_REMOVE", "");
+ data.put("DESKTOP_MIMES", "");
+ if (associations != null) {
+ String mimeInfoFile = XDG_FILE_PREFIX.fetchFrom(params)
+ + "-MimeInfo.xml";
+ StringBuilder mimeInfo = new StringBuilder(
+ "<?xml version=\"1.0\"?>\n<mime-info xmlns="
+ + "'http://www.freedesktop.org/standards/shared-mime-info'>\n");
+ StringBuilder registrations = new StringBuilder();
+ StringBuilder deregistrations = new StringBuilder();
+ StringBuilder desktopMimes = new StringBuilder("MimeType=");
+ boolean addedEntry = false;
+
+ for (Map<String, ? super Object> assoc : associations) {
+ // <mime-type type="application/x-vnd.awesome">
+ // <comment>Awesome document</comment>
+ // <glob pattern="*.awesome"/>
+ // <glob pattern="*.awe"/>
+ // </mime-type>
+
+ if (assoc == null) {
+ continue;
+ }
+
+ String description = FA_DESCRIPTION.fetchFrom(assoc);
+ File faIcon = FA_ICON.fetchFrom(assoc);
+ List<String> extensions = FA_EXTENSIONS.fetchFrom(assoc);
+ if (extensions == null) {
+ Log.error(I18N.getString(
+ "message.creating-association-with-null-extension"));
+ }
+
+ List<String> mimes = FA_CONTENT_TYPE.fetchFrom(assoc);
+ if (mimes == null || mimes.isEmpty()) {
+ continue;
+ }
+ String thisMime = mimes.get(0);
+ String dashMime = thisMime.replace('/', '-');
+
+ mimeInfo.append(" <mime-type type='")
+ .append(thisMime)
+ .append("'>\n");
+ if (description != null && !description.isEmpty()) {
+ mimeInfo.append(" <comment>")
+ .append(description)
+ .append("</comment>\n");
+ }
+
+ if (extensions != null) {
+ for (String ext : extensions) {
+ mimeInfo.append(" <glob pattern='*.")
+ .append(ext)
+ .append("'/>\n");
+ }
+ }
+
+ mimeInfo.append(" </mime-type>\n");
+ if (!addedEntry) {
+ registrations.append(" xdg-mime install ")
+ .append(LINUX_INSTALL_DIR.fetchFrom(params))
+ .append("/")
+ .append(data.get("APPLICATION_FS_NAME"))
+ .append("/")
+ .append(mimeInfoFile)
+ .append("\n");
+
+ deregistrations.append(" xdg-mime uninstall ")
+ .append(LINUX_INSTALL_DIR.fetchFrom(params))
+ .append("/")
+ .append(data.get("APPLICATION_FS_NAME"))
+ .append("/")
+ .append(mimeInfoFile)
+ .append("\n");
+ addedEntry = true;
+ } else {
+ desktopMimes.append(";");
+ }
+ desktopMimes.append(thisMime);
+
+ if (faIcon != null && faIcon.exists()) {
+ int size = getSquareSizeOfImage(faIcon);
+
+ if (size > 0) {
+ File target = new File(binDir,
+ APP_NAME.fetchFrom(params)
+ + "_fa_" + faIcon.getName());
+ IOUtils.copyFile(faIcon, target);
+
+ // xdg-icon-resource install --context mimetypes
+ // --size 64 awesomeapp_fa_1.png
+ // application-x.vnd-awesome
+ registrations.append(
+ " xdg-icon-resource install "
+ + "--context mimetypes --size ")
+ .append(size)
+ .append(" ")
+ .append(LINUX_INSTALL_DIR.fetchFrom(params))
+ .append("/")
+ .append(data.get("APPLICATION_FS_NAME"))
+ .append("/")
+ .append(target.getName())
+ .append(" ")
+ .append(dashMime)
+ .append("\n");
+
+ // x dg-icon-resource uninstall --context mimetypes
+ // --size 64 awesomeapp_fa_1.png
+ // application-x.vnd-awesome
+ deregistrations.append(
+ " xdg-icon-resource uninstall "
+ + "--context mimetypes --size ")
+ .append(size)
+ .append(" ")
+ .append(LINUX_INSTALL_DIR.fetchFrom(params))
+ .append("/")
+ .append(data.get("APPLICATION_FS_NAME"))
+ .append("/")
+ .append(target.getName())
+ .append(" ")
+ .append(dashMime)
+ .append("\n");
+ }
+ }
+ }
+ mimeInfo.append("</mime-info>");
+
+ if (addedEntry) {
+ try (Writer w = Files.newBufferedWriter(
+ new File(binDir, mimeInfoFile).toPath())) {
+ w.write(mimeInfo.toString());
+ }
+ data.put("FILE_ASSOCIATION_INSTALL", registrations.toString());
+ data.put("FILE_ASSOCIATION_REMOVE", deregistrations.toString());
+ data.put("DESKTOP_MIMES", desktopMimes.toString());
+ }
+ }
+
+ if (!StandardBundlerParam.isRuntimeInstaller(params)) {
+ //prepare desktop shortcut
+ try (Writer w = Files.newBufferedWriter(
+ getConfig_DesktopShortcutFile(binDir, params).toPath())) {
+ String content = preprocessTextResource(
+ getConfig_DesktopShortcutFile(
+ binDir, params).getName(),
+ I18N.getString("resource.menu-shortcut-descriptor"),
+ DEFAULT_DESKTOP_FILE_TEMPLATE,
+ data,
+ VERBOSE.fetchFrom(params),
+ RESOURCE_DIR.fetchFrom(params));
+ w.write(content);
+ }
+ }
+ // prepare control file
+ try (Writer w = Files.newBufferedWriter(
+ getConfig_ControlFile(params).toPath())) {
+ String content = preprocessTextResource(
+ getConfig_ControlFile(params).getName(),
+ I18N.getString("resource.deb-control-file"),
+ DEFAULT_CONTROL_TEMPLATE,
+ data,
+ VERBOSE.fetchFrom(params),
+ RESOURCE_DIR.fetchFrom(params));
+ w.write(content);
+ }
+
+ try (Writer w = Files.newBufferedWriter(
+ getConfig_PreinstallFile(params).toPath())) {
+ String content = preprocessTextResource(
+ getConfig_PreinstallFile(params).getName(),
+ I18N.getString("resource.deb-preinstall-script"),
+ DEFAULT_PREINSTALL_TEMPLATE,
+ data,
+ VERBOSE.fetchFrom(params),
+ RESOURCE_DIR.fetchFrom(params));
+ w.write(content);
+ }
+ setPermissions(getConfig_PreinstallFile(params), "rwxr-xr-x");
+
+ try (Writer w = Files.newBufferedWriter(
+ getConfig_PrermFile(params).toPath())) {
+ String content = preprocessTextResource(
+ getConfig_PrermFile(params).getName(),
+ I18N.getString("resource.deb-prerm-script"),
+ DEFAULT_PRERM_TEMPLATE,
+ data,
+ VERBOSE.fetchFrom(params),
+ RESOURCE_DIR.fetchFrom(params));
+ w.write(content);
+ }
+ setPermissions(getConfig_PrermFile(params), "rwxr-xr-x");
+
+ try (Writer w = Files.newBufferedWriter(
+ getConfig_PostinstallFile(params).toPath())) {
+ String content = preprocessTextResource(
+ getConfig_PostinstallFile(params).getName(),
+ I18N.getString("resource.deb-postinstall-script"),
+ DEFAULT_POSTINSTALL_TEMPLATE,
+ data,
+ VERBOSE.fetchFrom(params),
+ RESOURCE_DIR.fetchFrom(params));
+ w.write(content);
+ }
+ setPermissions(getConfig_PostinstallFile(params), "rwxr-xr-x");
+
+ try (Writer w = Files.newBufferedWriter(
+ getConfig_PostrmFile(params).toPath())) {
+ String content = preprocessTextResource(
+ getConfig_PostrmFile(params).getName(),
+ I18N.getString("resource.deb-postrm-script"),
+ DEFAULT_POSTRM_TEMPLATE,
+ data,
+ VERBOSE.fetchFrom(params),
+ RESOURCE_DIR.fetchFrom(params));
+ w.write(content);
+ }
+ setPermissions(getConfig_PostrmFile(params), "rwxr-xr-x");
+
+ try (Writer w = Files.newBufferedWriter(
+ getConfig_CopyrightFile(params).toPath())) {
+ String content = preprocessTextResource(
+ getConfig_CopyrightFile(params).getName(),
+ I18N.getString("resource.deb-copyright-file"),
+ DEFAULT_COPYRIGHT_TEMPLATE,
+ data,
+ VERBOSE.fetchFrom(params),
+ RESOURCE_DIR.fetchFrom(params));
+ w.write(content);
+ }
+
+ return true;
+ }
+
+ 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));
+ data.put("APPLICATION_PACKAGE", BUNDLE_NAME.fetchFrom(params));
+ 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", 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));
+ data.put("APPLICATION_DESCRIPTION", DESCRIPTION.fetchFrom(params));
+ data.put("APPLICATION_COPYRIGHT", COPYRIGHT.fetchFrom(params));
+ data.put("APPLICATION_LICENSE_TEXT", LICENSE_TEXT.fetchFrom(params));
+ data.put("APPLICATION_ARCH", getArch());
+ data.put("APPLICATION_INSTALLED_SIZE",
+ Long.toString(getInstalledSizeKB(params)));
+ String deps = LINUX_PACKAGE_DEPENDENCIES.fetchFrom(params);
+ data.put("PACKAGE_DEPENDENCIES",
+ deps.isEmpty() ? "" : "Depends: " + deps);
+ data.put("RUNTIME_INSTALLER", "" +
+ StandardBundlerParam.isRuntimeInstaller(params));
+
+ return data;
+ }
+
+ private File getConfig_DesktopShortcutFile(File rootDir,
+ Map<String, ? super Object> params) {
+ return new File(rootDir, APP_NAME.fetchFrom(params) + ".desktop");
+ }
+
+ private File getConfig_IconFile(File rootDir,
+ Map<String, ? super Object> params) {
+ return new File(rootDir, APP_NAME.fetchFrom(params) + ".png");
+ }
+
+ private File getConfig_ControlFile(Map<String, ? super Object> params) {
+ return new File(CONFIG_DIR.fetchFrom(params), "control");
+ }
+
+ private File getConfig_PreinstallFile(Map<String, ? super Object> params) {
+ return new File(CONFIG_DIR.fetchFrom(params), "preinst");
+ }
+
+ private File getConfig_PrermFile(Map<String, ? super Object> params) {
+ return new File(CONFIG_DIR.fetchFrom(params), "prerm");
+ }
+
+ private File getConfig_PostinstallFile(Map<String, ? super Object> params) {
+ return new File(CONFIG_DIR.fetchFrom(params), "postinst");
+ }
+
+ private File getConfig_PostrmFile(Map<String, ? super Object> params) {
+ return new File(CONFIG_DIR.fetchFrom(params), "postrm");
+ }
+
+ private File getConfig_CopyrightFile(Map<String, ? super Object> params) {
+ return new File(CONFIG_DIR.fetchFrom(params), "copyright");
+ }
+
+ private File buildDeb(Map<String, ? super Object> params,
+ File outdir) throws IOException {
+ File outFile = new File(outdir,
+ FULL_PACKAGE_NAME.fetchFrom(params)+".deb");
+ Log.verbose(MessageFormat.format(I18N.getString(
+ "message.outputting-to-location"), outFile.getAbsolutePath()));
+
+ outFile.getParentFile().mkdirs();
+
+ // run dpkg
+ ProcessBuilder pb = new ProcessBuilder(
+ "fakeroot", TOOL_DPKG, "-b",
+ FULL_PACKAGE_NAME.fetchFrom(params),
+ outFile.getAbsolutePath());
+ pb = pb.directory(DEB_IMAGE_DIR.fetchFrom(params).getParentFile());
+ IOUtils.exec(pb);
+
+ Log.verbose(MessageFormat.format(I18N.getString(
+ "message.output-to-location"), outFile.getAbsolutePath()));
+
+ return outFile;
+ }
+
+ @Override
+ public String getName() {
+ return I18N.getString("deb.bundler.name");
+ }
+
+ @Override
+ public String getID() {
+ return "deb";
+ }
+
+ @Override
+ public String getBundleType() {
+ return "INSTALLER";
+ }
+
+ @Override
+ public File execute(Map<String, ? super Object> params,
+ File outputParentDir) throws PackagerException {
+ return bundle(params, outputParentDir);
+ }
+
+ @Override
+ public boolean supported(boolean runtimeInstaller) {
+ return isSupported();
+ }
+
+ public static boolean isSupported() {
+ if (Platform.getPlatform() == Platform.LINUX) {
+ if (testTool(TOOL_DPKG, "1")) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public int getSquareSizeOfImage(File f) {
+ try {
+ BufferedImage bi = ImageIO.read(f);
+ if (bi.getWidth() == bi.getHeight()) {
+ return bi.getWidth();
+ } else {
+ return 0;
+ }
+ } catch (Exception e) {
+ Log.verbose(e);
+ return 0;
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxRpmBundler.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,703 @@
+/*
+ * 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.jpackage.internal;
+
+import javax.imageio.ImageIO;
+import java.awt.image.BufferedImage;
+import java.io.*;
+import java.nio.file.Files;
+import java.nio.file.attribute.PosixFilePermission;
+import java.nio.file.attribute.PosixFilePermissions;
+import java.text.MessageFormat;
+import java.util.*;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import static jdk.jpackage.internal.StandardBundlerParam.*;
+import static jdk.jpackage.internal.LinuxAppBundler.LINUX_INSTALL_DIR;
+import static jdk.jpackage.internal.LinuxAppBundler.LINUX_PACKAGE_DEPENDENCIES;
+
+public class LinuxRpmBundler extends AbstractBundler {
+
+ private static final ResourceBundle I18N = ResourceBundle.getBundle(
+ "jdk.jpackage.internal.resources.LinuxResources");
+
+ public static final BundlerParamInfo<LinuxAppBundler> APP_BUNDLER =
+ new StandardBundlerParam<>(
+ "linux.app.bundler",
+ LinuxAppBundler.class,
+ params -> new LinuxAppBundler(),
+ null);
+
+ public static final BundlerParamInfo<File> RPM_IMAGE_DIR =
+ new StandardBundlerParam<>(
+ "linux.rpm.imageDir",
+ File.class,
+ params -> {
+ File imagesRoot = IMAGES_ROOT.fetchFrom(params);
+ if (!imagesRoot.exists()) imagesRoot.mkdirs();
+ return new File(imagesRoot, "linux-rpm.image");
+ },
+ (s, p) -> new File(s));
+
+ // Fedora rules for package naming are used here
+ // https://fedoraproject.org/wiki/Packaging:NamingGuidelines?rd=Packaging/NamingGuidelines
+ //
+ // all Fedora packages must be named using only the following ASCII
+ // characters. These characters are displayed here:
+ //
+ // abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._+
+ //
+ private static final Pattern RPM_BUNDLE_NAME_PATTERN =
+ Pattern.compile("[a-z\\d\\+\\-\\.\\_]+", Pattern.CASE_INSENSITIVE);
+
+ public static final BundlerParamInfo<String> BUNDLE_NAME =
+ new StandardBundlerParam<> (
+ Arguments.CLIOptions.LINUX_BUNDLE_NAME.getId(),
+ String.class,
+ params -> {
+ String nm = APP_NAME.fetchFrom(params);
+ if (nm == null) return null;
+
+ // make sure to lower case and spaces become dashes
+ nm = nm.toLowerCase().replaceAll("[ ]", "-");
+
+ return nm;
+ },
+ (s, p) -> {
+ if (!RPM_BUNDLE_NAME_PATTERN.matcher(s).matches()) {
+ String msgKey = "error.invalid-value-for-package-name";
+ throw new IllegalArgumentException(
+ new ConfigException(MessageFormat.format(
+ I18N.getString(msgKey), s),
+ I18N.getString(msgKey + ".advice")));
+ }
+
+ return s;
+ }
+ );
+
+ public static final BundlerParamInfo<String> MENU_GROUP =
+ new StandardBundlerParam<>(
+ Arguments.CLIOptions.LINUX_MENU_GROUP.getId(),
+ String.class,
+ params -> I18N.getString("param.menu-group.default"),
+ (s, p) -> s
+ );
+
+ public static final BundlerParamInfo<String> LICENSE_TYPE =
+ new StandardBundlerParam<>(
+ Arguments.CLIOptions.LINUX_RPM_LICENSE_TYPE.getId(),
+ String.class,
+ params -> I18N.getString("param.license-type.default"),
+ (s, p) -> s
+ );
+
+ public static final BundlerParamInfo<String> XDG_FILE_PREFIX =
+ new StandardBundlerParam<> (
+ "linux.xdg-prefix",
+ String.class,
+ params -> {
+ try {
+ String vendor;
+ if (params.containsKey(VENDOR.getID())) {
+ vendor = VENDOR.fetchFrom(params);
+ } else {
+ vendor = "jpackage";
+ }
+ String appName = APP_NAME.fetchFrom(params);
+
+ return (vendor + "-" + appName).replaceAll("\\s", "");
+ } catch (Exception e) {
+ if (Log.isDebug()) {
+ e.printStackTrace();
+ }
+ }
+ return "unknown-MimeInfo.xml";
+ },
+ (s, p) -> s);
+
+ private final static String DEFAULT_ICON = "javalogo_white_32.png";
+ private final static String DEFAULT_SPEC_TEMPLATE = "template.spec";
+ private final static String DEFAULT_DESKTOP_FILE_TEMPLATE =
+ "template.desktop";
+
+ public final static String TOOL_RPMBUILD = "rpmbuild";
+ public final static double TOOL_RPMBUILD_MIN_VERSION = 4.0d;
+
+ public static boolean testTool(String toolName, double minVersion) {
+ try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ PrintStream ps = new PrintStream(baos)) {
+ ProcessBuilder pb = new ProcessBuilder(toolName, "--version");
+ IOUtils.exec(pb, false, ps);
+ //not interested in the above's output
+ String content = new String(baos.toByteArray());
+ Pattern pattern = Pattern.compile(" (\\d+\\.\\d+)");
+ Matcher matcher = pattern.matcher(content);
+
+ if (matcher.find()) {
+ String v = matcher.group(1);
+ double version = Double.parseDouble(v);
+ return minVersion <= version;
+ } else {
+ return false;
+ }
+ } catch (Exception e) {
+ Log.verbose(MessageFormat.format(I18N.getString(
+ "message.test-for-tool"), toolName, e.getMessage()));
+ return false;
+ }
+ }
+
+ @Override
+ public boolean validate(Map<String, ? super Object> params)
+ throws ConfigException {
+ try {
+ if (params == null) throw new ConfigException(
+ I18N.getString("error.parameters-null"),
+ I18N.getString("error.parameters-null.advice"));
+
+ // run basic validation to ensure requirements are met
+ // we are not interested in return code, only possible exception
+ APP_BUNDLER.fetchFrom(params).validate(params);
+
+ // validate presense of required tools
+ if (!testTool(TOOL_RPMBUILD, TOOL_RPMBUILD_MIN_VERSION)){
+ throw new ConfigException(
+ MessageFormat.format(
+ I18N.getString("error.cannot-find-rpmbuild"),
+ TOOL_RPMBUILD_MIN_VERSION),
+ MessageFormat.format(
+ I18N.getString("error.cannot-find-rpmbuild.advice"),
+ TOOL_RPMBUILD_MIN_VERSION));
+ }
+
+ // only one mime type per association, at least one file extension
+ List<Map<String, ? super Object>> associations =
+ FILE_ASSOCIATIONS.fetchFrom(params);
+ if (associations != null) {
+ for (int i = 0; i < associations.size(); i++) {
+ Map<String, ? super Object> assoc = associations.get(i);
+ List<String> mimes = FA_CONTENT_TYPE.fetchFrom(assoc);
+ if (mimes == null || mimes.isEmpty()) {
+ String msgKey =
+ "error.no-content-types-for-file-association";
+ throw new ConfigException(
+ MessageFormat.format(I18N.getString(msgKey), i),
+ I18N.getString(msgKey + ".advice"));
+ } else if (mimes.size() > 1) {
+ String msgKey =
+ "error.no-content-types-for-file-association";
+ throw new ConfigException(
+ MessageFormat.format(I18N.getString(msgKey), i),
+ I18N.getString(msgKey + ".advice"));
+ }
+ }
+ }
+
+ // bundle name has some restrictions
+ // the string converter will throw an exception if invalid
+ BUNDLE_NAME.getStringConverter().apply(
+ BUNDLE_NAME.fetchFrom(params), params);
+
+ return true;
+ } catch (RuntimeException re) {
+ if (re.getCause() instanceof ConfigException) {
+ throw (ConfigException) re.getCause();
+ } else {
+ throw new ConfigException(re);
+ }
+ }
+ }
+
+ private boolean prepareProto(Map<String, ? super Object> params)
+ throws PackagerException, IOException {
+ File appImage = StandardBundlerParam.getPredefinedAppImage(params);
+ File appDir = null;
+
+ // we either have an application image or need to build one
+ if (appImage != null) {
+ appDir = new File(RPM_IMAGE_DIR.fetchFrom(params),
+ APP_NAME.fetchFrom(params));
+ // copy everything from appImage dir into appDir/name
+ IOUtils.copyRecursive(appImage.toPath(), appDir.toPath());
+ } else {
+ appDir = APP_BUNDLER.fetchFrom(params).doBundle(params,
+ RPM_IMAGE_DIR.fetchFrom(params), true);
+ }
+ return appDir != null;
+ }
+
+ public File bundle(Map<String, ? super Object> params,
+ File outdir) throws PackagerException {
+
+ IOUtils.writableOutputDir(outdir.toPath());
+
+ File imageDir = RPM_IMAGE_DIR.fetchFrom(params);
+ try {
+
+ imageDir.mkdirs();
+
+ if (prepareProto(params) && prepareProjectConfig(params)) {
+ return buildRPM(params, outdir);
+ }
+ return null;
+ } catch (IOException ex) {
+ Log.verbose(ex);
+ throw new PackagerException(ex);
+ }
+ }
+
+ private String getLicenseFileString(Map<String, ? super Object> params)
+ throws IOException {
+ StringBuilder sb = new StringBuilder();
+
+ String licenseStr = LICENSE_FILE.fetchFrom(params);
+ if (licenseStr != null) {
+ File licenseFile = new File(licenseStr);
+ File rootDir =
+ LinuxAppBundler.getRootDir(RPM_IMAGE_DIR.fetchFrom(params),
+ params);
+ File target = new File(rootDir + File.separator + "app"
+ + File.separator + licenseFile.getName());
+ Files.copy(licenseFile.toPath(), target.toPath());
+
+ sb.append("%license ");
+ sb.append(LINUX_INSTALL_DIR.fetchFrom(params));
+ sb.append("/");
+ sb.append(APP_NAME.fetchFrom(params));
+ sb.append("/app/");
+ sb.append(licenseFile.getName());
+ }
+
+ return sb.toString();
+ }
+
+ private boolean prepareProjectConfig(Map<String, ? super Object> params)
+ throws IOException {
+ 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(binDir, params);
+ File icon = LinuxAppBundler.ICON_PNG.fetchFrom(params);
+ if (!StandardBundlerParam.isRuntimeInstaller(params)) {
+ if (icon == null || !icon.exists()) {
+ fetchResource(iconTarget.getName(),
+ I18N.getString("resource.menu-icon"),
+ DEFAULT_ICON,
+ iconTarget,
+ VERBOSE.fetchFrom(params),
+ RESOURCE_DIR.fetchFrom(params));
+ } else {
+ fetchResource(iconTarget.getName(),
+ I18N.getString("resource.menu-icon"),
+ icon,
+ iconTarget,
+ VERBOSE.fetchFrom(params),
+ RESOURCE_DIR.fetchFrom(params));
+ }
+ }
+
+ StringBuilder installScripts = new StringBuilder();
+ StringBuilder removeScripts = new StringBuilder();
+ for (Map<String, ? super Object> addLauncher :
+ ADD_LAUNCHERS.fetchFrom(params)) {
+ Map<String, String> addLauncherData =
+ createReplacementData(addLauncher);
+ addLauncherData.put("APPLICATION_FS_NAME",
+ data.get("APPLICATION_FS_NAME"));
+ addLauncherData.put("DESKTOP_MIMES", "");
+
+ // prepare desktop shortcut
+ try (Writer w = Files.newBufferedWriter(
+ getConfig_DesktopShortcutFile(binDir,
+ addLauncher).toPath())) {
+ String content = preprocessTextResource(
+ getConfig_DesktopShortcutFile(binDir,
+ addLauncher).getName(),
+ I18N.getString("resource.menu-shortcut-descriptor"),
+ DEFAULT_DESKTOP_FILE_TEMPLATE, addLauncherData,
+ VERBOSE.fetchFrom(params),
+ RESOURCE_DIR.fetchFrom(params));
+ w.write(content);
+ }
+
+ // prepare installer icon
+ iconTarget = getConfig_IconFile(binDir, addLauncher);
+ icon = LinuxAppBundler.ICON_PNG.fetchFrom(addLauncher);
+ if (icon == null || !icon.exists()) {
+ fetchResource(iconTarget.getName(),
+ I18N.getString("resource.menu-icon"),
+ DEFAULT_ICON,
+ iconTarget,
+ VERBOSE.fetchFrom(params),
+ RESOURCE_DIR.fetchFrom(params));
+ } else {
+ fetchResource(iconTarget.getName(),
+ I18N.getString("resource.menu-icon"),
+ icon,
+ iconTarget,
+ VERBOSE.fetchFrom(params),
+ RESOURCE_DIR.fetchFrom(params));
+ }
+
+ // post copying of desktop icon
+ installScripts.append("xdg-desktop-menu install --novendor ");
+ installScripts.append(LINUX_INSTALL_DIR.fetchFrom(params));
+ installScripts.append("/");
+ installScripts.append(data.get("APPLICATION_FS_NAME"));
+ installScripts.append("/");
+ installScripts.append(addLauncherData.get(
+ "APPLICATION_LAUNCHER_FILENAME"));
+ installScripts.append(".desktop\n");
+
+ // preun cleanup of desktop icon
+ removeScripts.append("xdg-desktop-menu uninstall --novendor ");
+ removeScripts.append(LINUX_INSTALL_DIR.fetchFrom(params));
+ removeScripts.append("/");
+ removeScripts.append(data.get("APPLICATION_FS_NAME"));
+ removeScripts.append("/");
+ removeScripts.append(addLauncherData.get(
+ "APPLICATION_LAUNCHER_FILENAME"));
+ removeScripts.append(".desktop\n");
+
+ }
+ data.put("ADD_LAUNCHERS_INSTALL", installScripts.toString());
+ data.put("ADD_LAUNCHERS_REMOVE", removeScripts.toString());
+
+ StringBuilder cdsScript = new StringBuilder();
+
+ data.put("APP_CDS_CACHE", cdsScript.toString());
+
+ List<Map<String, ? super Object>> associations =
+ FILE_ASSOCIATIONS.fetchFrom(params);
+ data.put("FILE_ASSOCIATION_INSTALL", "");
+ data.put("FILE_ASSOCIATION_REMOVE", "");
+ data.put("DESKTOP_MIMES", "");
+ if (associations != null) {
+ String mimeInfoFile = XDG_FILE_PREFIX.fetchFrom(params)
+ + "-MimeInfo.xml";
+ StringBuilder mimeInfo = new StringBuilder(
+ "<?xml version=\"1.0\"?>\n<mime-info xmlns="
+ +"'http://www.freedesktop.org/standards/shared-mime-info'>\n");
+ StringBuilder registrations = new StringBuilder();
+ StringBuilder deregistrations = new StringBuilder();
+ StringBuilder desktopMimes = new StringBuilder("MimeType=");
+ boolean addedEntry = false;
+
+ for (Map<String, ? super Object> assoc : associations) {
+ // <mime-type type="application/x-vnd.awesome">
+ // <comment>Awesome document</comment>
+ // <glob pattern="*.awesome"/>
+ // <glob pattern="*.awe"/>
+ // </mime-type>
+
+ if (assoc == null) {
+ continue;
+ }
+
+ String description = FA_DESCRIPTION.fetchFrom(assoc);
+ File faIcon = FA_ICON.fetchFrom(assoc);
+ List<String> extensions = FA_EXTENSIONS.fetchFrom(assoc);
+ if (extensions == null) {
+ Log.verbose(I18N.getString(
+ "message.creating-association-with-null-extension"));
+ }
+
+ List<String> mimes = FA_CONTENT_TYPE.fetchFrom(assoc);
+ if (mimes == null || mimes.isEmpty()) {
+ continue;
+ }
+ String thisMime = mimes.get(0);
+ String dashMime = thisMime.replace('/', '-');
+
+ mimeInfo.append(" <mime-type type='")
+ .append(thisMime)
+ .append("'>\n");
+ if (description != null && !description.isEmpty()) {
+ mimeInfo.append(" <comment>")
+ .append(description)
+ .append("</comment>\n");
+ }
+
+ if (extensions != null) {
+ for (String ext : extensions) {
+ mimeInfo.append(" <glob pattern='*.")
+ .append(ext)
+ .append("'/>\n");
+ }
+ }
+
+ mimeInfo.append(" </mime-type>\n");
+ if (!addedEntry) {
+ registrations.append("xdg-mime install ")
+ .append(LINUX_INSTALL_DIR.fetchFrom(params))
+ .append("/")
+ .append(data.get("APPLICATION_FS_NAME"))
+ .append("/")
+ .append(mimeInfoFile)
+ .append("\n");
+
+ deregistrations.append("xdg-mime uninstall ")
+ .append(LINUX_INSTALL_DIR.fetchFrom(params))
+ .append("/")
+ .append(data.get("APPLICATION_FS_NAME"))
+ .append("/")
+ .append(mimeInfoFile)
+ .append("\n");
+ addedEntry = true;
+ } else {
+ desktopMimes.append(";");
+ }
+ desktopMimes.append(thisMime);
+
+ if (faIcon != null && faIcon.exists()) {
+ int size = getSquareSizeOfImage(faIcon);
+
+ if (size > 0) {
+ File target = new File(binDir,
+ APP_NAME.fetchFrom(params)
+ + "_fa_" + faIcon.getName());
+ IOUtils.copyFile(faIcon, target);
+
+ // xdg-icon-resource install --context mimetypes
+ // --size 64 awesomeapp_fa_1.png
+ // application-x.vnd-awesome
+ registrations.append(
+ "xdg-icon-resource install "
+ + "--context mimetypes --size ")
+ .append(size)
+ .append(" ")
+ .append(LINUX_INSTALL_DIR.fetchFrom(params))
+ .append("/")
+ .append(data.get("APPLICATION_FS_NAME"))
+ .append("/")
+ .append(target.getName())
+ .append(" ")
+ .append(dashMime)
+ .append("\n");
+
+ // xdg-icon-resource uninstall --context mimetypes
+ // --size 64 awesomeapp_fa_1.png
+ // application-x.vnd-awesome
+ deregistrations.append(
+ "xdg-icon-resource uninstall "
+ + "--context mimetypes --size ")
+ .append(size)
+ .append(" ")
+ .append(LINUX_INSTALL_DIR.fetchFrom(params))
+ .append("/")
+ .append(data.get("APPLICATION_FS_NAME"))
+ .append("/")
+ .append(target.getName())
+ .append(" ")
+ .append(dashMime)
+ .append("\n");
+ }
+ }
+ }
+ mimeInfo.append("</mime-info>");
+
+ if (addedEntry) {
+ try (Writer w = Files.newBufferedWriter(
+ new File(binDir, mimeInfoFile).toPath())) {
+ w.write(mimeInfo.toString());
+ }
+ data.put("FILE_ASSOCIATION_INSTALL", registrations.toString());
+ data.put("FILE_ASSOCIATION_REMOVE", deregistrations.toString());
+ data.put("DESKTOP_MIMES", desktopMimes.toString());
+ }
+ }
+
+ if (!StandardBundlerParam.isRuntimeInstaller(params)) {
+ //prepare desktop shortcut
+ try (Writer w = Files.newBufferedWriter(
+ getConfig_DesktopShortcutFile(binDir, params).toPath())) {
+ String content = preprocessTextResource(
+ getConfig_DesktopShortcutFile(binDir,
+ params).getName(),
+ I18N.getString("resource.menu-shortcut-descriptor"),
+ DEFAULT_DESKTOP_FILE_TEMPLATE, data,
+ VERBOSE.fetchFrom(params),
+ RESOURCE_DIR.fetchFrom(params));
+ w.write(content);
+ }
+ }
+
+ // prepare spec file
+ try (Writer w = Files.newBufferedWriter(
+ getConfig_SpecFile(params).toPath())) {
+ String content = preprocessTextResource(
+ getConfig_SpecFile(params).getName(),
+ I18N.getString("resource.rpm-spec-file"),
+ DEFAULT_SPEC_TEMPLATE, data,
+ VERBOSE.fetchFrom(params),
+ RESOURCE_DIR.fetchFrom(params));
+ w.write(content);
+ }
+
+ return true;
+ }
+
+ 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", 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));
+ data.put("APPLICATION_DESCRIPTION", DESCRIPTION.fetchFrom(params));
+ data.put("APPLICATION_SUMMARY", APP_NAME.fetchFrom(params));
+ data.put("APPLICATION_LICENSE_TYPE", LICENSE_TYPE.fetchFrom(params));
+ data.put("APPLICATION_LICENSE_FILE", getLicenseFileString(params));
+ String deps = LINUX_PACKAGE_DEPENDENCIES.fetchFrom(params);
+ data.put("PACKAGE_DEPENDENCIES",
+ deps.isEmpty() ? "" : "Requires: " + deps);
+ data.put("RUNTIME_INSTALLER", "" +
+ StandardBundlerParam.isRuntimeInstaller(params));
+ return data;
+ }
+
+ private File getConfig_DesktopShortcutFile(File rootDir,
+ Map<String, ? super Object> params) {
+ return new File(rootDir, APP_NAME.fetchFrom(params) + ".desktop");
+ }
+
+ private File getConfig_IconFile(File rootDir,
+ Map<String, ? super Object> params) {
+ return new File(rootDir, APP_NAME.fetchFrom(params) + ".png");
+ }
+
+ private File getConfig_SpecFile(Map<String, ? super Object> params) {
+ return new File(RPM_IMAGE_DIR.fetchFrom(params),
+ APP_NAME.fetchFrom(params) + ".spec");
+ }
+
+ private File buildRPM(Map<String, ? super Object> params,
+ File outdir) throws IOException {
+ Log.verbose(MessageFormat.format(I18N.getString(
+ "message.outputting-bundle-location"),
+ outdir.getAbsolutePath()));
+
+ File broot = new File(TEMP_ROOT.fetchFrom(params), "rmpbuildroot");
+
+ outdir.mkdirs();
+
+ //run rpmbuild
+ ProcessBuilder pb = new ProcessBuilder(
+ TOOL_RPMBUILD,
+ "-bb", getConfig_SpecFile(params).getAbsolutePath(),
+ "--define", "%_sourcedir "
+ + RPM_IMAGE_DIR.fetchFrom(params).getAbsolutePath(),
+ // save result to output dir
+ "--define", "%_rpmdir " + outdir.getAbsolutePath(),
+ // do not use other system directories to build as current user
+ "--define", "%_topdir " + broot.getAbsolutePath()
+ );
+ pb = pb.directory(RPM_IMAGE_DIR.fetchFrom(params));
+ IOUtils.exec(pb);
+
+ Log.verbose(MessageFormat.format(
+ I18N.getString("message.output-bundle-location"),
+ outdir.getAbsolutePath()));
+
+ // presume the result is the ".rpm" file with the newest modified time
+ // not the best solution, but it is the most reliable
+ File result = null;
+ long lastModified = 0;
+ File[] list = outdir.listFiles();
+ if (list != null) {
+ for (File f : list) {
+ if (f.getName().endsWith(".rpm") &&
+ f.lastModified() > lastModified) {
+ result = f;
+ lastModified = f.lastModified();
+ }
+ }
+ }
+
+ return result;
+ }
+
+ @Override
+ public String getName() {
+ return I18N.getString("rpm.bundler.name");
+ }
+
+ @Override
+ public String getID() {
+ return "rpm";
+ }
+
+ @Override
+ public String getBundleType() {
+ return "INSTALLER";
+ }
+
+ @Override
+ public File execute(Map<String, ? super Object> params,
+ File outputParentDir) throws PackagerException {
+ return bundle(params, outputParentDir);
+ }
+
+ @Override
+ public boolean supported(boolean runtimeInstaller) {
+ return isSupported();
+ }
+
+ public static boolean isSupported() {
+ if (Platform.getPlatform() == Platform.LINUX) {
+ if (testTool(TOOL_RPMBUILD, TOOL_RPMBUILD_MIN_VERSION)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public int getSquareSizeOfImage(File f) {
+ try {
+ BufferedImage bi = ImageIO.read(f);
+ if (bi.getWidth() == bi.getHeight()) {
+ return bi.getWidth();
+ } else {
+ return 0;
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ return 0;
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/LinuxResources.properties Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,61 @@
+#
+# Copyright (c) 2017, 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.
+#
+#
+app.bundler.name=Linux Application Image
+deb.bundler.name=DEB Installer
+rpm.bundler.name=RPM Bundle
+
+param.license-type.default=Unknown
+param.menu-group.default=Unknown
+
+resource.deb-control-file=DEB control file
+resource.deb-preinstall-script=DEB preinstall script
+resource.deb-prerm-script=DEB prerm script
+resource.deb-postinstall-script=DEB postinstall script
+resource.deb-postrm-script=DEB postrm script
+resource.deb-copyright-file=DEB copyright file
+resource.menu-shortcut-descriptor=Menu shortcut descriptor
+resource.menu-icon=menu icon
+resource.rpm-spec-file=RPM spec file
+
+error.parameters-null=Parameters map is null.
+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.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.
+error.invalid-value-for-package-name.advice=Set the "linux.bundleName" parameter to a valid Debian package name. Note that the package names must consist only of lower case letters (a-z), digits (0-9), plus (+) and minus (-) signs, and periods (.). They must be at least two characters long and must start with an alphanumeric character.
+error.cannot-find-rpmbuild=Can not find rpmbuild {0} or newer.
+error.cannot-find-rpmbuild.advice=\ Install packages needed to build RPM, version {0} or newer.
+
+message.icon-not-png=The specified icon "{0}" is not a PNG file and will not be used. The default icon will be used in it's place.
+message.test-for-tool=Test for [{0}]. Result: {1}
+message.outputting-to-location=Generating DEB for installer to: {0}.
+message.output-to-location=Package (.deb) saved to: {0}.
+message.debs-like-licenses=Debian packages should specify a license. The absence of a license will cause some linux distributions to complain about the quality of the application.
+message.outputting-bundle-location=Generating RPM for installer to: {0}.
+message.output-bundle-location=Package (.rpm) saved to: {0}.
+message.creating-association-with-null-extension=Creating association with null extension.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/LinuxResources_ja.properties Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,61 @@
+#
+# Copyright (c) 2017, 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.
+#
+#
+app.bundler.name=Linux Application Image
+deb.bundler.name=DEB Installer
+rpm.bundler.name=RPM Bundle
+
+param.license-type.default=Unknown
+param.menu-group.default=Unknown
+
+resource.deb-control-file=DEB control file
+resource.deb-preinstall-script=DEB preinstall script
+resource.deb-prerm-script=DEB prerm script
+resource.deb-postinstall-script=DEB postinstall script
+resource.deb-postrm-script=DEB postrm script
+resource.deb-copyright-file=DEB copyright file
+resource.menu-shortcut-descriptor=Menu shortcut descriptor
+resource.menu-icon=menu icon
+resource.rpm-spec-file=RPM spec file
+
+error.parameters-null=Parameters map is null.
+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.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.
+error.invalid-value-for-package-name.advice=Set the "linux.bundleName" parameter to a valid Debian package name. Note that the package names must consist only of lower case letters (a-z), digits (0-9), plus (+) and minus (-) signs, and periods (.). They must be at least two characters long and must start with an alphanumeric character.
+error.cannot-find-rpmbuild=Can not find rpmbuild {0} or newer.
+error.cannot-find-rpmbuild.advice=\ Install packages needed to build RPM, version {0} or newer.
+
+message.icon-not-png=The specified icon "{0}" is not a PNG file and will not be used. The default icon will be used in it's place.
+message.test-for-tool=Test for [{0}]. Result: {1}
+message.outputting-to-location=Generating DEB for installer to: {0}.
+message.output-to-location=Package (.deb) saved to: {0}.
+message.debs-like-licenses=Debian packages should specify a license. The absence of a license will cause some linux distributions to complain about the quality of the application.
+message.outputting-bundle-location=Generating RPM for installer to: {0}.
+message.output-bundle-location=Package (.rpm) saved to: {0}.
+message.creating-association-with-null-extension=Creating association with null extension.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/LinuxResources_zh_CN.properties Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,61 @@
+#
+# Copyright (c) 2017, 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.
+#
+#
+app.bundler.name=Linux Application Image
+deb.bundler.name=DEB Installer
+rpm.bundler.name=RPM Bundle
+
+param.license-type.default=Unknown
+param.menu-group.default=Unknown
+
+resource.deb-control-file=DEB control file
+resource.deb-preinstall-script=DEB preinstall script
+resource.deb-prerm-script=DEB prerm script
+resource.deb-postinstall-script=DEB postinstall script
+resource.deb-postrm-script=DEB postrm script
+resource.deb-copyright-file=DEB copyright file
+resource.menu-shortcut-descriptor=Menu shortcut descriptor
+resource.menu-icon=menu icon
+resource.rpm-spec-file=RPM spec file
+
+error.parameters-null=Parameters map is null.
+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.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.
+error.invalid-value-for-package-name.advice=Set the "linux.bundleName" parameter to a valid Debian package name. Note that the package names must consist only of lower case letters (a-z), digits (0-9), plus (+) and minus (-) signs, and periods (.). They must be at least two characters long and must start with an alphanumeric character.
+error.cannot-find-rpmbuild=Can not find rpmbuild {0} or newer.
+error.cannot-find-rpmbuild.advice=\ Install packages needed to build RPM, version {0} or newer.
+
+message.icon-not-png=The specified icon "{0}" is not a PNG file and will not be used. The default icon will be used in it's place.
+message.test-for-tool=Test for [{0}]. Result: {1}
+message.outputting-to-location=Generating DEB for installer to: {0}.
+message.output-to-location=Package (.deb) saved to: {0}.
+message.debs-like-licenses=Debian packages should specify a license. The absence of a license will cause some linux distributions to complain about the quality of the application.
+message.outputting-bundle-location=Generating RPM for installer to: {0}.
+message.output-bundle-location=Package (.rpm) saved to: {0}.
+message.creating-association-with-null-extension=Creating association with null extension.
Binary file src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/javalogo_white_32.png has changed
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/template.control Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,10 @@
+Package: APPLICATION_PACKAGE
+Version: APPLICATION_VERSION
+Section: unknown
+Maintainer: APPLICATION_MAINTAINER
+Priority: optional
+Architecture: APPLICATION_ARCH
+Provides: APPLICATION_PACKAGE
+Description: APPLICATION_DESCRIPTION
+Installed-Size: APPLICATION_INSTALLED_SIZE
+PACKAGE_DEPENDENCIES
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/template.copyright Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,8 @@
+
+Copyright:
+
+ APPLICATION_COPYRIGHT
+
+License:
+
+ APPLICATION_LICENSE_TEXT
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/template.desktop Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,9 @@
+[Desktop Entry]
+Name=APPLICATION_NAME
+Comment=APPLICATION_DESCRIPTION
+Exec=INSTALLATION_DIRECTORY/APPLICATION_FS_NAME/APPLICATION_LAUNCHER_FILENAME
+Icon=INSTALLATION_DIRECTORY/APPLICATION_FS_NAME/APPLICATION_LAUNCHER_FILENAME.png
+Terminal=false
+Type=Application
+Categories=DEPLOY_BUNDLE_CATEGORY
+DESKTOP_MIMES
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/template.postinst Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,61 @@
+#!/bin/sh
+# postinst script for APPLICATION_NAME
+#
+# see: dh_installdeb(1)
+
+set -e
+
+# summary of how this script can be called:
+# * <postinst> `configure' <most-recently-configured-version>
+# * <old-postinst> `abort-upgrade' <new version>
+# * <conflictor's-postinst> `abort-remove' `in-favour' <package>
+# <new-version>
+# * <postinst> `abort-remove'
+# * <deconfigured's-postinst> `abort-deconfigure' `in-favour'
+# <failed-install-package> <version> `removing'
+# <conflicting-package> <version>
+# for details, see http://www.debian.org/doc/debian-policy/ or
+# the debian-policy package
+
+case "$1" in
+ configure)
+ if [ "RUNTIME_INSTALLER" != "true" ]; then
+ echo Adding shortcut to the menu
+ADD_LAUNCHERS_INSTALL
+ xdg-desktop-menu install --novendor INSTALLATION_DIRECTORY/APPLICATION_FS_NAME/APPLICATION_LAUNCHER_FILENAME.desktop
+FILE_ASSOCIATION_INSTALL
+ fi
+ if [ "SERVICE_HINT" = "true" ]; then
+ echo Installing daemon
+ cp INSTALLATION_DIRECTORY/APPLICATION_FS_NAME/APPLICATION_PACKAGE.init /etc/init.d/APPLICATION_PACKAGE
+
+ if [ -x "/etc/init.d/APPLICATION_PACKAGE" ]; then
+ update-rc.d APPLICATION_PACKAGE defaults
+
+ if [ "START_ON_INSTALL" = "true" ]; then
+ if which invoke-rc.d >/dev/null 2>&1; then
+ invoke-rc.d APPLICATION_PACKAGE start
+ else
+ /etc/init.d/APPLICATION_PACKAGE start
+ fi
+ fi
+ fi
+
+ fi
+ ;;
+
+ abort-upgrade|abort-remove|abort-deconfigure)
+ ;;
+
+ *)
+ echo "postinst called with unknown argument \`$1'" >&2
+ exit 1
+ ;;
+esac
+
+# dh_installdeb will replace this with shell code automatically
+# generated by other debhelper scripts.
+
+#DEBHELPER#
+
+exit 0
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/template.postrm Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,44 @@
+#!/bin/sh
+# postrm script for APPLICATION_NAME
+#
+# see: dh_installdeb(1)
+
+set -e
+
+# summary of how this script can be called:
+# * <postrm> `remove'
+# * <postrm> `purge'
+# * <old-postrm> `upgrade' <new-version>
+# * <new-postrm> `failed-upgrade' <old-version>
+# * <new-postrm> `abort-install'
+# * <new-postrm> `abort-install' <old-version>
+# * <new-postrm> `abort-upgrade' <old-version>
+# * <disappearer's-postrm> `disappear' <overwriter>
+# <overwriter-version>
+# for details, see http://www.debian.org/doc/debian-policy/ or
+# the debian-policy package
+
+case "$1" in
+ purge|remove|upgrade|failed-upgrade|abort-install|abort-upgrade|disappear)
+ if [ "$1" = "purge" ] ; then
+ if [ "SERVICE_HINT" = "true" ]; then
+ echo Uninstalling daemon
+ rm -f /etc/init.d/APPLICATION_PACKAGE
+
+ update-rc.d APPLICATION_PACKAGE remove
+ fi
+ fi
+ ;;
+
+ *)
+ echo "postrm called with unknown argument \`$1'" >&2
+ exit 1
+ ;;
+esac
+
+# dh_installdeb will replace this with shell code automatically
+# generated by other debhelper scripts.
+
+#DEBHELPER#
+
+exit 0
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/template.preinst Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,35 @@
+#!/bin/sh
+# preinst script for APPLICATION_NAME
+#
+# see: dh_installdeb(1)
+
+set -e
+
+# summary of how this script can be called:
+# * <new-preinst> `install'
+# * <new-preinst> `install' <old-version>
+# * <new-preinst> `upgrade' <old-version>
+# * <old-preinst> `abort-upgrade' <new-version>
+# for details, see http://www.debian.org/doc/debian-policy/ or
+# the debian-policy package
+
+
+case "$1" in
+ install|upgrade)
+ ;;
+
+ abort-upgrade)
+ ;;
+
+ *)
+ echo "preinst called with unknown argument \`$1'" >&2
+ exit 1
+ ;;
+esac
+
+# dh_installdeb will replace this with shell code automatically
+# generated by other debhelper scripts.
+
+#DEBHELPER#
+
+exit 0
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/template.prerm Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,45 @@
+#!/bin/sh
+# prerm script for APPLICATION_NAME
+#
+# see: dh_installdeb(1)
+
+set -e
+
+# summary of how this script can be called:
+# * <prerm> `remove'
+# * <old-prerm> `upgrade' <new-version>
+# * <new-prerm> `failed-upgrade' <old-version>
+# * <conflictor's-prerm> `remove' `in-favour' <package> <new-version>
+# * <deconfigured's-prerm> `deconfigure' `in-favour'
+# <package-being-installed> <version> `removing'
+# <conflicting-package> <version>
+# for details, see http://www.debian.org/doc/debian-policy/ or
+# the debian-policy package
+
+
+case "$1" in
+ remove|upgrade|deconfigure)
+ if [ "RUNTIME_INSTALLER" != "true" ]; then
+ echo Removing shortcut
+ADD_LAUNCHERS_REMOVE
+ xdg-desktop-menu uninstall --novendor INSTALLATION_DIRECTORY/APPLICATION_FS_NAME/APPLICATION_LAUNCHER_FILENAME.desktop
+FILE_ASSOCIATION_REMOVE
+ fi
+ ;;
+
+ failed-upgrade)
+ ;;
+
+ *)
+ echo "prerm called with unknown argument \`$1'" >&2
+ exit 1
+ ;;
+esac
+
+# dh_installdeb will replace this with shell code automatically
+# generated by other debhelper scripts.
+
+#DEBHELPER#
+
+exit 0
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/template.spec Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,60 @@
+Summary: APPLICATION_SUMMARY
+Name: APPLICATION_PACKAGE
+Version: APPLICATION_VERSION
+Release: 1
+License: APPLICATION_LICENSE_TYPE
+Vendor: APPLICATION_VENDOR
+Prefix: INSTALLATION_DIRECTORY
+Provides: APPLICATION_PACKAGE
+Autoprov: 0
+Autoreq: 0
+PACKAGE_DEPENDENCIES
+
+#avoid ARCH subfolder
+%define _rpmfilename %%{NAME}-%%{VERSION}-%%{RELEASE}.%%{ARCH}.rpm
+
+#comment line below to enable effective jar compression
+#it could easily get your package size from 40 to 15Mb but
+#build time will substantially increase and it may require unpack200/system java to install
+%define __jar_repack %{nil}
+
+%description
+APPLICATION_DESCRIPTION
+
+%prep
+
+%build
+
+%install
+rm -rf %{buildroot}
+mkdir -p %{buildroot}INSTALLATION_DIRECTORY
+cp -r %{_sourcedir}/APPLICATION_FS_NAME %{buildroot}INSTALLATION_DIRECTORY
+
+%files
+APPLICATION_LICENSE_FILE
+INSTALLATION_DIRECTORY/APPLICATION_FS_NAME
+
+%post
+if [ "RUNTIME_INSTALLER" != "true" ]; then
+ADD_LAUNCHERS_INSTALL
+ xdg-desktop-menu install --novendor INSTALLATION_DIRECTORY/APPLICATION_FS_NAME/APPLICATION_LAUNCHER_FILENAME.desktop
+FILE_ASSOCIATION_INSTALL
+fi
+
+%preun
+if [ "RUNTIME_INSTALLER" != "true" ]; then
+ADD_LAUNCHERS_REMOVE
+ xdg-desktop-menu uninstall --novendor INSTALLATION_DIRECTORY/APPLICATION_FS_NAME/APPLICATION_LAUNCHER_FILENAME.desktop
+FILE_ASSOCIATION_REMOVE
+fi
+if [ "SERVICE_HINT" = "true" ]; then
+ if [ -x "/etc/init.d/APPLICATION_PACKAGE" ]; then
+ if [ "STOP_ON_UNINSTALL" = "true" ]; then
+ /etc/init.d/APPLICATION_PACKAGE stop
+ fi
+ /sbin/chkconfig --del APPLICATION_PACKAGE
+ rm -f /etc/init.d/APPLICATION_PACKAGE
+ fi
+fi
+
+%clean
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/linux/classes/module-info.java.extra Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+
+provides jdk.jpackage.internal.Bundler with
+ jdk.jpackage.internal.LinuxAppBundler,
+ jdk.jpackage.internal.LinuxDebBundler,
+ jdk.jpackage.internal.LinuxRpmBundler;
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/linux/native/jpackageapplauncher/launcher.cpp Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2014, 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.
+ */
+
+#include <dlfcn.h>
+#include <locale.h>
+#include <string>
+#include <libgen.h>
+#include <stdio.h>
+#include <unistd.h>
+
+
+typedef bool (*start_launcher)(int argc, char* argv[]);
+typedef void (*stop_launcher)();
+
+#define MAX_PATH 1024
+
+std::string GetProgramPath() {
+ ssize_t len = 0;
+ std::string result;
+ char buffer[MAX_PATH] = {0};
+
+ if ((len = readlink("/proc/self/exe", buffer, MAX_PATH - 1)) != -1) {
+ buffer[len] = '\0';
+ result = buffer;
+ }
+
+ return result;
+}
+
+int main(int argc, char *argv[]) {
+ int result = 1;
+ setlocale(LC_ALL, "en_US.utf8");
+ void* library = NULL;
+
+ {
+ std::string programPath = GetProgramPath();
+ std::string libraryName = dirname((char*)programPath.c_str());
+ libraryName += "/libapplauncher.so";
+ library = dlopen(libraryName.c_str(), RTLD_LAZY);
+
+ if (library == NULL) {
+ fprintf(stderr, "dlopen failed: %s\n", dlerror());
+ fprintf(stderr, "%s not found.\n", libraryName.c_str());
+ }
+ }
+
+ if (library != NULL) {
+ start_launcher start = (start_launcher)dlsym(library, "start_launcher");
+ stop_launcher stop = (stop_launcher)dlsym(library, "stop_launcher");
+
+ if (start != NULL && stop != NULL) {
+ if (start(argc, argv) == true) {
+ result = 0;
+ stop();
+ }
+ } else {
+ fprintf(stderr, "cannot find start_launcher and stop_launcher in libapplauncher.so");
+ }
+
+ dlclose(library);
+ }
+
+
+ return result;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/linux/native/libapplauncher/LinuxPlatform.cpp Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,1083 @@
+/*
+ * Copyright (c) 2014, 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.
+ */
+
+#include "Platform.h"
+
+#include "JavaVirtualMachine.h"
+#include "LinuxPlatform.h"
+#include "PlatformString.h"
+#include "IniFile.h"
+#include "Helpers.h"
+#include "FilePath.h"
+
+#include <stdlib.h>
+#include <pwd.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <limits.h>
+#include <signal.h>
+
+#define LINUX_JPACKAGE_TMP_DIR "/.java/jpackage/tmp"
+
+TString GetEnv(const TString &name) {
+ TString result;
+
+ char *value = ::getenv((TCHAR*) name.c_str());
+
+ if (value != NULL) {
+ result = value;
+ }
+
+ return result;
+}
+
+LinuxPlatform::LinuxPlatform(void) : Platform(),
+PosixPlatform() {
+ FMainThread = pthread_self();
+}
+
+LinuxPlatform::~LinuxPlatform(void) {
+}
+
+TString LinuxPlatform::GetPackageAppDirectory() {
+ return FilePath::IncludeTrailingSeparator(
+ GetPackageRootDirectory()) + _T("app");
+}
+
+TString LinuxPlatform::GetAppName() {
+ TString result = GetModuleFileName();
+ result = FilePath::ExtractFileName(result);
+ return result;
+}
+
+TString LinuxPlatform::GetPackageLauncherDirectory() {
+ return FilePath::IncludeTrailingSeparator(
+ GetPackageRootDirectory()) + _T("bin");
+}
+
+TString LinuxPlatform::GetPackageRuntimeBinDirectory() {
+ return FilePath::IncludeTrailingSeparator(GetPackageRootDirectory()) + _T("runtime/bin");
+}
+
+void LinuxPlatform::ShowMessage(TString title, TString description) {
+ printf("%s %s\n", PlatformString(title).toPlatformString(),
+ PlatformString(description).toPlatformString());
+ fflush(stdout);
+}
+
+void LinuxPlatform::ShowMessage(TString description) {
+ TString appname = GetModuleFileName();
+ appname = FilePath::ExtractFileName(appname);
+ ShowMessage(PlatformString(appname).toPlatformString(),
+ PlatformString(description).toPlatformString());
+}
+
+TCHAR* LinuxPlatform::ConvertStringToFileSystemString(TCHAR* Source,
+ bool &release) {
+ // Not Implemented.
+ return NULL;
+}
+
+TCHAR* LinuxPlatform::ConvertFileSystemStringToString(TCHAR* Source,
+ bool &release) {
+ // Not Implemented.
+ return NULL;
+}
+
+TString LinuxPlatform::GetModuleFileName() {
+ ssize_t len = 0;
+ TString result;
+ DynamicBuffer<TCHAR> buffer(MAX_PATH);
+ if (buffer.GetData() == NULL) {
+ return result;
+ }
+
+ if ((len = readlink("/proc/self/exe", buffer.GetData(),
+ MAX_PATH - 1)) != -1) {
+ buffer[len] = '\0';
+ result = buffer.GetData();
+ }
+
+ return result;
+}
+
+void LinuxPlatform::SetCurrentDirectory(TString Value) {
+ chdir(PlatformString(Value).toPlatformString());
+}
+
+TString LinuxPlatform::GetPackageRootDirectory() {
+ TString result;
+ TString filename = GetModuleFileName();
+ 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() {
+ TString result;
+ TString home = GetEnv(_T("HOME"));
+
+ if (home.empty() == false) {
+ result += FilePath::IncludeTrailingSeparator(home) + _T(".local");
+ }
+
+ return result;
+}
+
+ISectionalPropertyContainer* LinuxPlatform::GetConfigFile(TString FileName) {
+ IniFile *result = new IniFile();
+ if (result == NULL) {
+ return NULL;
+ }
+
+ result->LoadFromFile(FileName);
+
+ return result;
+}
+
+TString LinuxPlatform::GetBundledJavaLibraryFileName(TString RuntimePath) {
+ TString result = FilePath::IncludeTrailingSeparator(RuntimePath) +
+ "lib/libjli.so";
+
+ if (FilePath::FileExists(result) == false) {
+ result = FilePath::IncludeTrailingSeparator(RuntimePath) +
+ "lib/jli/libjli.so";
+ if (FilePath::FileExists(result) == false) {
+ printf("Cannot find libjli.so!");
+ }
+ }
+
+ return result;
+}
+
+bool LinuxPlatform::IsMainThread() {
+ bool result = (FMainThread == pthread_self());
+ return result;
+}
+
+TString LinuxPlatform::getTmpDirString() {
+ return TString(LINUX_JPACKAGE_TMP_DIR);
+}
+
+TPlatformNumber LinuxPlatform::GetMemorySize() {
+ long pages = sysconf(_SC_PHYS_PAGES);
+ long page_size = sysconf(_SC_PAGE_SIZE);
+ TPlatformNumber result = pages * page_size;
+ result = result / 1048576; // Convert from bytes to megabytes.
+ return result;
+}
+
+void PosixProcess::Cleanup() {
+ if (FOutputHandle != 0) {
+ close(FOutputHandle);
+ FOutputHandle = 0;
+ }
+
+ if (FInputHandle != 0) {
+ close(FInputHandle);
+ FInputHandle = 0;
+ }
+}
+
+#define PIPE_READ 0
+#define PIPE_WRITE 1
+
+bool PosixProcess::Execute(const TString Application,
+ const std::vector<TString> Arguments, bool AWait) {
+ bool result = false;
+
+ if (FRunning == false) {
+ FRunning = true;
+
+ int handles[2];
+
+ if (pipe(handles) == -1) {
+ return false;
+ }
+
+ struct sigaction sa;
+ sa.sa_handler = SIG_IGN;
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = 0;
+
+ FChildPID = fork();
+
+ // PID returned by vfork is 0 for the child process and the
+ // PID of the child process for the parent.
+ if (FChildPID == -1) {
+ // Error
+ TString message = PlatformString::Format(
+ _T("Error: Unable to create process %s"),
+ Application.data());
+ throw Exception(message);
+ } else if (FChildPID == 0) {
+ Cleanup();
+ TString command = Application;
+
+ for (std::vector<TString>::const_iterator iterator =
+ Arguments.begin(); iterator != Arguments.end();
+ iterator++) {
+ command += TString(_T(" ")) + *iterator;
+ }
+#ifdef DEBUG
+ printf("%s\n", command.data());
+#endif // DEBUG
+
+ dup2(handles[PIPE_READ], STDIN_FILENO);
+ dup2(handles[PIPE_WRITE], STDOUT_FILENO);
+
+ close(handles[PIPE_READ]);
+ close(handles[PIPE_WRITE]);
+
+ execl("/bin/sh", "sh", "-c", command.data(), (char *) 0);
+
+ _exit(127);
+ } else {
+ FOutputHandle = handles[PIPE_READ];
+ FInputHandle = handles[PIPE_WRITE];
+
+ if (AWait == true) {
+ ReadOutput();
+ Wait();
+ Cleanup();
+ FRunning = false;
+ result = true;
+ } else {
+ result = true;
+ }
+ }
+ }
+
+ return result;
+}
+
+
+//----------------------------------------------------------------------------
+
+#ifndef __UNIX_JPACKAGE_PLATFORM__
+#define __UNIX_JPACKAGE_PLATFORM__
+
+/** Provide an abstraction for difference in the platform APIs,
+ e.g. string manipulation functions, etc. */
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include <sys/stat.h>
+
+#define TCHAR char
+
+#define _T(x) x
+
+#define JPACKAGE_MULTIBYTE_SNPRINTF snprintf
+
+#define JPACKAGE_SNPRINTF(buffer, sizeOfBuffer, count, format, ...) \
+ snprintf((buffer), (count), (format), __VA_ARGS__)
+
+#define JPACKAGE_PRINTF(format, ...) \
+ printf((format), ##__VA_ARGS__)
+
+#define JPACKAGE_FPRINTF(dest, format, ...) \
+ fprintf((dest), (format), __VA_ARGS__)
+
+#define JPACKAGE_SSCANF(buf, format, ...) \
+ sscanf((buf), (format), __VA_ARGS__)
+
+#define JPACKAGE_STRDUP(strSource) \
+ strdup((strSource))
+
+//return "error code" (like on Windows)
+
+static int JPACKAGE_STRNCPY(char *strDest, size_t numberOfElements,
+ const char *strSource, size_t count) {
+ char *s = strncpy(strDest, strSource, count);
+ // Duplicate behavior of the Windows' _tcsncpy_s() by adding a NULL
+ // terminator at the end of the string.
+ if (count < numberOfElements) {
+ s[count] = '\0';
+ } else {
+ s[numberOfElements - 1] = '\0';
+ }
+ return (s == strDest) ? 0 : 1;
+}
+
+#define JPACKAGE_STRICMP(x, y) \
+ strcasecmp((x), (y))
+
+#define JPACKAGE_STRNICMP(x, y, cnt) \
+ strncasecmp((x), (y), (cnt))
+
+#define JPACKAGE_STRNCMP(x, y, cnt) \
+ strncmp((x), (y), (cnt))
+
+#define JPACKAGE_STRLEN(x) \
+ strlen((x))
+
+#define JPACKAGE_STRSTR(x, y) \
+ strstr((x), (y))
+
+#define JPACKAGE_STRCHR(x, y) \
+ strchr((x), (y))
+
+#define JPACKAGE_STRRCHR(x, y) \
+ strrchr((x), (y))
+
+#define JPACKAGE_STRPBRK(x, y) \
+ strpbrk((x), (y))
+
+#define JPACKAGE_GETENV(x) \
+ getenv((x))
+
+#define JPACKAGE_PUTENV(x) \
+ putenv((x))
+
+#define JPACKAGE_STRCMP(x, y) \
+ strcmp((x), (y))
+
+#define JPACKAGE_STRCPY(x, y) \
+ strcpy((x), (y))
+
+#define JPACKAGE_STRCAT(x, y) \
+ strcat((x), (y))
+
+#define JPACKAGE_ATOI(x) \
+ atoi((x))
+
+#define JPACKAGE_FOPEN(x, y) \
+ fopen((x), (y))
+
+#define JPACKAGE_FGETS(x, y, z) \
+ fgets((x), (y), (z))
+
+#define JPACKAGE_REMOVE(x) \
+ remove((x))
+
+#define JPACKAGE_SPAWNV(mode, cmd, args) \
+ spawnv((mode), (cmd), (args))
+
+#define JPACKAGE_ISDIGIT(ch) isdigit(ch)
+
+// for non-unicode, just return the input string for
+// the following 2 conversions
+#define JPACKAGE_NEW_MULTIBYTE(message) message
+
+#define JPACKAGE_NEW_FROM_MULTIBYTE(message) message
+
+// for non-unicode, no-op for the relase operation
+// since there is no memory allocated for the
+// string conversions
+#define JPACKAGE_RELEASE_MULTIBYTE(tmpMBCS)
+
+#define JPACKAGE_RELEASE_FROM_MULTIBYTE(tmpMBCS)
+
+// The size will be used for converting from 1 byte to 1 byte encoding.
+// Ensure have space for zero-terminator.
+#define JPACKAGE_GET_SIZE_FOR_ENCODING(message, theLength) (theLength + 1)
+
+#endif
+#define xmlTagType 0
+#define xmlPCDataType 1
+
+typedef struct _xmlNode XMLNode;
+typedef struct _xmlAttribute XMLAttribute;
+
+struct _xmlNode {
+ int _type; // Type of node: tag, pcdata, cdate
+ TCHAR* _name; // Contents of node
+ XMLNode* _next; // Next node at same level
+ XMLNode* _sub; // First sub-node
+ XMLAttribute* _attributes; // List of attributes
+};
+
+struct _xmlAttribute {
+ TCHAR* _name; // Name of attribute
+ TCHAR* _value; // Value of attribute
+ XMLAttribute* _next; // Next attribute for this tag
+};
+
+// Public interface
+static void RemoveNonAsciiUTF8FromBuffer(char *buf);
+XMLNode* ParseXMLDocument(TCHAR* buf);
+void FreeXMLDocument(XMLNode* root);
+
+// Utility methods for parsing document
+XMLNode* FindXMLChild(XMLNode* root, const TCHAR* name);
+TCHAR* FindXMLAttribute(XMLAttribute* attr, const TCHAR* name);
+
+// Debugging
+void PrintXMLDocument(XMLNode* node, int indt);
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <setjmp.h>
+#include <stdlib.h>
+#include <wctype.h>
+
+#define JWS_assert(s, msg) \
+ if (!(s)) { Abort(msg); }
+
+
+// Internal declarations
+static XMLNode* ParseXMLElement(void);
+static XMLAttribute* ParseXMLAttribute(void);
+static TCHAR* SkipWhiteSpace(TCHAR *p);
+static TCHAR* SkipXMLName(TCHAR *p);
+static TCHAR* SkipXMLComment(TCHAR *p);
+static TCHAR* SkipXMLDocType(TCHAR *p);
+static TCHAR* SkipXMLProlog(TCHAR *p);
+static TCHAR* SkipPCData(TCHAR *p);
+static int IsPCData(TCHAR *p);
+static void ConvertBuiltInEntities(TCHAR* p);
+static void SetToken(int type, TCHAR* start, TCHAR* end);
+static void GetNextToken(void);
+static XMLNode* CreateXMLNode(int type, TCHAR* name);
+static XMLAttribute* CreateXMLAttribute(TCHAR *name, TCHAR* value);
+static XMLNode* ParseXMLElement(void);
+static XMLAttribute* ParseXMLAttribute(void);
+static void FreeXMLAttribute(XMLAttribute* attr);
+static void PrintXMLAttributes(XMLAttribute* attr);
+static void indent(int indt);
+
+static jmp_buf jmpbuf;
+static XMLNode* root_node = NULL;
+
+/** definition of error codes for setjmp/longjmp,
+ * that can be handled in ParseXMLDocument()
+ */
+#define JMP_NO_ERROR 0
+#define JMP_OUT_OF_RANGE 1
+
+#define NEXT_CHAR(p) { \
+ if (*p != 0) { \
+ p++; \
+ } else { \
+ longjmp(jmpbuf, JMP_OUT_OF_RANGE); \
+ } \
+}
+#define NEXT_CHAR_OR_BREAK(p) { \
+ if (*p != 0) { \
+ p++; \
+ } else { \
+ break; \
+ } \
+}
+#define NEXT_CHAR_OR_RETURN(p) { \
+ if (*p != 0) { \
+ p++; \
+ } else { \
+ return; \
+ } \
+}
+#define SKIP_CHARS(p,n) { \
+ int i; \
+ for (i = 0; i < (n); i++) { \
+ if (*p != 0) { \
+ p++; \
+ } else { \
+ longjmp(jmpbuf, JMP_OUT_OF_RANGE); \
+ } \
+ } \
+}
+#define SKIP_CHARS_OR_BREAK(p,n) { \
+ int i; \
+ for (i = 0; i < (n); i++) { \
+ if (*p != 0) { \
+ p++; \
+ } else { \
+ break; \
+ } \
+ } \
+ if (i < (n)) { \
+ break; \
+ } \
+}
+
+/** Iterates through the null-terminated buffer (i.e., C string) and
+ * replaces all UTF-8 encoded character >255 with 255
+ *
+ * UTF-8 encoding:
+ *
+ * Range A: 0x0000 - 0x007F
+ * 0 | bits 0 - 7
+ * Range B : 0x0080 - 0x07FF :
+ * 110 | bits 6 - 10
+ * 10 | bits 0 - 5
+ * Range C : 0x0800 - 0xFFFF :
+ * 1110 | bits 12-15
+ * 10 | bits 6-11
+ * 10 | bits 0-5
+ */
+static void RemoveNonAsciiUTF8FromBuffer(char *buf) {
+ char* p;
+ char* q;
+ char c;
+ p = q = buf;
+ // We are not using NEXT_CHAR() to check if *q is NULL, as q is output
+ // location and offset for q is smaller than for p.
+ while (*p != '\0') {
+ c = *p;
+ if ((c & 0x80) == 0) {
+ /* Range A */
+ *q++ = *p;
+ NEXT_CHAR(p);
+ } else if ((c & 0xE0) == 0xC0) {
+ /* Range B */
+ *q++ = (char) 0xFF;
+ NEXT_CHAR(p);
+ NEXT_CHAR_OR_BREAK(p);
+ } else {
+ /* Range C */
+ *q++ = (char) 0xFF;
+ NEXT_CHAR(p);
+ SKIP_CHARS_OR_BREAK(p, 2);
+ }
+ }
+ /* Null terminate string */
+ *q = '\0';
+}
+
+static TCHAR* SkipWhiteSpace(TCHAR *p) {
+ if (p != NULL) {
+ while (iswspace(*p))
+ NEXT_CHAR_OR_BREAK(p);
+ }
+ return p;
+}
+
+static TCHAR* SkipXMLName(TCHAR *p) {
+ TCHAR c = *p;
+ /* Check if start of token */
+ if (('a' <= c && c <= 'z') ||
+ ('A' <= c && c <= 'Z') ||
+ c == '_' || c == ':') {
+
+ while (('a' <= c && c <= 'z') ||
+ ('A' <= c && c <= 'Z') ||
+ ('0' <= c && c <= '9') ||
+ c == '_' || c == ':' || c == '.' || c == '-') {
+ NEXT_CHAR(p);
+ c = *p;
+ if (c == '\0') break;
+ }
+ }
+ return p;
+}
+
+static TCHAR* SkipXMLComment(TCHAR *p) {
+ if (p != NULL) {
+ if (JPACKAGE_STRNCMP(p, _T("<!--"), 4) == 0) {
+ SKIP_CHARS(p, 4);
+ do {
+ if (JPACKAGE_STRNCMP(p, _T("-->"), 3) == 0) {
+ SKIP_CHARS(p, 3);
+ return p;
+ }
+ NEXT_CHAR(p);
+ } while (*p != '\0');
+ }
+ }
+ return p;
+}
+
+static TCHAR* SkipXMLDocType(TCHAR *p) {
+ if (p != NULL) {
+ if (JPACKAGE_STRNCMP(p, _T("<!"), 2) == 0) {
+ SKIP_CHARS(p, 2);
+ while (*p != '\0') {
+ if (*p == '>') {
+ NEXT_CHAR(p);
+ return p;
+ }
+ NEXT_CHAR(p);
+ }
+ }
+ }
+ return p;
+}
+
+static TCHAR* SkipXMLProlog(TCHAR *p) {
+ if (p != NULL) {
+ if (JPACKAGE_STRNCMP(p, _T("<?"), 2) == 0) {
+ SKIP_CHARS(p, 2);
+ do {
+ if (JPACKAGE_STRNCMP(p, _T("?>"), 2) == 0) {
+ SKIP_CHARS(p, 2);
+ return p;
+ }
+ NEXT_CHAR(p);
+ } while (*p != '\0');
+ }
+ }
+ return p;
+}
+
+/* Search for the built-in XML entities:
+ * & (&), < (<), > (>), ' ('), and "e(")
+ * and convert them to a real TCHARacter
+ */
+static void ConvertBuiltInEntities(TCHAR* p) {
+ TCHAR* q;
+ q = p;
+ // We are not using NEXT_CHAR() to check if *q is NULL,
+ // as q is output location and offset for q is smaller than for p.
+ while (*p) {
+ if (IsPCData(p)) {
+ /* dont convert &xxx values within PData */
+ TCHAR *end;
+ end = SkipPCData(p);
+ while (p < end) {
+ *q++ = *p;
+ NEXT_CHAR(p);
+ }
+ } else {
+ if (JPACKAGE_STRNCMP(p, _T("&"), 5) == 0) {
+ *q++ = '&';
+ SKIP_CHARS(p, 5);
+ } else if (JPACKAGE_STRNCMP(p, _T("<"), 4) == 0) {
+ *q = '<';
+ SKIP_CHARS(p, 4);
+ } else if (JPACKAGE_STRNCMP(p, _T(">"), 4) == 0) {
+ *q = '>';
+ SKIP_CHARS(p, 4);
+ } else if (JPACKAGE_STRNCMP(p, _T("'"), 6) == 0) {
+ *q = '\'';
+ SKIP_CHARS(p, 6);
+ } else if (JPACKAGE_STRNCMP(p, _T(""e;"), 7) == 0) {
+ *q = '\"';
+ SKIP_CHARS(p, 7);
+ } else {
+ *q++ = *p;
+ NEXT_CHAR(p);
+ }
+ }
+ }
+ *q = '\0';
+}
+
+/* ------------------------------------------------------------- */
+/* XML tokenizer */
+
+#define TOKEN_UNKNOWN 0
+#define TOKEN_BEGIN_TAG 1 /* <tag */
+#define TOKEN_END_TAG 2 /* </tag */
+#define TOKEN_CLOSE_BRACKET 3 /* > */
+#define TOKEN_EMPTY_CLOSE_BRACKET 4 /* /> */
+#define TOKEN_PCDATA 5 /* pcdata */
+#define TOKEN_CDATA 6 /* cdata */
+#define TOKEN_EOF 7
+
+static TCHAR* CurPos = NULL;
+static TCHAR* CurTokenName = NULL;
+static int CurTokenType;
+static int MaxTokenSize = -1;
+
+/* Copy token from buffer to Token variable */
+static void SetToken(int type, TCHAR* start, TCHAR* end) {
+ int len = end - start;
+ if (len > MaxTokenSize) {
+ if (CurTokenName != NULL) free(CurTokenName);
+ CurTokenName = (TCHAR *) malloc((len + 1) * sizeof (TCHAR));
+ if (CurTokenName == NULL) {
+ return;
+ }
+ MaxTokenSize = len;
+ }
+
+ CurTokenType = type;
+ JPACKAGE_STRNCPY(CurTokenName, len + 1, start, len);
+ CurTokenName[len] = '\0';
+}
+
+/* Skip XML comments, doctypes, and prolog tags */
+static TCHAR* SkipFilling(void) {
+ TCHAR *q = CurPos;
+
+ /* Skip white space and comment sections */
+ do {
+ q = CurPos;
+ CurPos = SkipWhiteSpace(CurPos);
+ CurPos = SkipXMLComment(CurPos); /* Must be called befor DocTypes */
+ CurPos = SkipXMLDocType(CurPos); /* <! ... > directives */
+ CurPos = SkipXMLProlog(CurPos); /* <? ... ?> directives */
+ } while (CurPos != q);
+
+ return CurPos;
+}
+
+/* Parses next token and initializes the global token variables above
+ The tokennizer automatically skips comments (<!-- comment -->) and
+ <! ... > directives.
+ */
+static void GetNextToken(void) {
+ TCHAR *p, *q;
+
+ /* Skip white space and comment sections */
+ p = SkipFilling();
+
+ if (p == NULL || *p == '\0') {
+ CurTokenType = TOKEN_EOF;
+ return;
+ } else if (p[0] == '<' && p[1] == '/') {
+ /* TOKEN_END_TAG */
+ q = SkipXMLName(p + 2);
+ SetToken(TOKEN_END_TAG, p + 2, q);
+ p = q;
+ } else if (*p == '<') {
+ /* TOKEN_BEGIN_TAG */
+ q = SkipXMLName(p + 1);
+ SetToken(TOKEN_BEGIN_TAG, p + 1, q);
+ p = q;
+ } else if (p[0] == '>') {
+ CurTokenType = TOKEN_CLOSE_BRACKET;
+ NEXT_CHAR(p);
+ } else if (p[0] == '/' && p[1] == '>') {
+ CurTokenType = TOKEN_EMPTY_CLOSE_BRACKET;
+ SKIP_CHARS(p, 2);
+ } else {
+ /* Search for end of data */
+ q = p + 1;
+ while (*q && *q != '<') {
+ if (IsPCData(q)) {
+ q = SkipPCData(q);
+ } else {
+ NEXT_CHAR(q);
+ }
+ }
+ SetToken(TOKEN_PCDATA, p, q);
+ /* Convert all entities inside token */
+ ConvertBuiltInEntities(CurTokenName);
+ p = q;
+ }
+ /* Advance pointer to beginning of next token */
+ CurPos = p;
+}
+
+static XMLNode* CreateXMLNode(int type, TCHAR* name) {
+ XMLNode* node;
+ node = (XMLNode*) malloc(sizeof (XMLNode));
+ if (node == NULL) {
+ return NULL;
+ }
+ node->_type = type;
+ node->_name = name;
+ node->_next = NULL;
+ node->_sub = NULL;
+ node->_attributes = NULL;
+ return node;
+}
+
+static XMLAttribute* CreateXMLAttribute(TCHAR *name, TCHAR* value) {
+ XMLAttribute* attr;
+ attr = (XMLAttribute*) malloc(sizeof (XMLAttribute));
+ if (attr == NULL) {
+ return NULL;
+ }
+ attr->_name = name;
+ attr->_value = value;
+ attr->_next = NULL;
+ return attr;
+}
+
+XMLNode* ParseXMLDocument(TCHAR* buf) {
+ XMLNode* root;
+ int err_code = setjmp(jmpbuf);
+ switch (err_code) {
+ case JMP_NO_ERROR:
+#ifndef _UNICODE
+ /* Remove UTF-8 encoding from buffer */
+ RemoveNonAsciiUTF8FromBuffer(buf);
+#endif
+
+ /* Get first Token */
+ CurPos = buf;
+ GetNextToken();
+
+ /* Parse document*/
+ root = ParseXMLElement();
+ break;
+ case JMP_OUT_OF_RANGE:
+ /* cleanup: */
+ if (root_node != NULL) {
+ FreeXMLDocument(root_node);
+ root_node = NULL;
+ }
+ if (CurTokenName != NULL) free(CurTokenName);
+ fprintf(stderr, "Error during parsing jnlp file...\n");
+ exit(-1);
+ break;
+ default:
+ root = NULL;
+ break;
+ }
+
+ return root;
+}
+
+static XMLNode* ParseXMLElement(void) {
+ XMLNode* node = NULL;
+ XMLNode* subnode = NULL;
+ XMLNode* nextnode = NULL;
+ XMLAttribute* attr = NULL;
+
+ if (CurTokenType == TOKEN_BEGIN_TAG) {
+
+ /* Create node for new element tag */
+ node = CreateXMLNode(xmlTagType, JPACKAGE_STRDUP(CurTokenName));
+ /* We need to save root node pointer to be able to cleanup
+ if an error happens during parsing */
+ if (!root_node) {
+ root_node = node;
+ }
+ /* Parse attributes. This section eats a all input until
+ EOF, a > or a /> */
+ attr = ParseXMLAttribute();
+ while (attr != NULL) {
+ attr->_next = node->_attributes;
+ node->_attributes = attr;
+ attr = ParseXMLAttribute();
+ }
+
+ /* This will eihter be a TOKEN_EOF, TOKEN_CLOSE_BRACKET, or a
+ * TOKEN_EMPTY_CLOSE_BRACKET */
+ GetNextToken();
+
+ if (CurTokenType == TOKEN_EMPTY_CLOSE_BRACKET) {
+ GetNextToken();
+ /* We are done with the sublevel - fall through to continue */
+ /* parsing tags at the same level */
+ } else if (CurTokenType == TOKEN_CLOSE_BRACKET) {
+ GetNextToken();
+
+ /* Parse until end tag if found */
+ node->_sub = ParseXMLElement();
+
+ if (CurTokenType == TOKEN_END_TAG) {
+ /* Find closing bracket '>' for end tag */
+ do {
+ GetNextToken();
+ } while (CurTokenType != TOKEN_EOF &&
+ CurTokenType != TOKEN_CLOSE_BRACKET);
+ GetNextToken();
+ }
+ }
+
+ /* Continue parsing rest on same level */
+ if (CurTokenType != TOKEN_EOF) {
+ /* Parse rest of stream at same level */
+ node->_next = ParseXMLElement();
+ }
+ return node;
+
+ } else if (CurTokenType == TOKEN_PCDATA) {
+ /* Create node for pcdata */
+ node = CreateXMLNode(xmlPCDataType, JPACKAGE_STRDUP(CurTokenName));
+ /* We need to save root node pointer to be able to cleanup
+ if an error happens during parsing */
+ if (!root_node) {
+ root_node = node;
+ }
+ GetNextToken();
+ return node;
+ }
+
+ /* Something went wrong. */
+ return NULL;
+}
+
+/* Parses an XML attribute. */
+static XMLAttribute* ParseXMLAttribute(void) {
+ TCHAR* q = NULL;
+ TCHAR* name = NULL;
+ TCHAR* PrevPos = NULL;
+
+ do {
+ /* We need to check this condition to avoid endless loop
+ in case if an error happend during parsing. */
+ if (PrevPos == CurPos) {
+ if (name != NULL) {
+ free(name);
+ name = NULL;
+ }
+
+ return NULL;
+ }
+
+ PrevPos = CurPos;
+
+ /* Skip whitespace etc. */
+ SkipFilling();
+
+ /* Check if we are done witht this attribute section */
+ if (CurPos[0] == '\0' ||
+ CurPos[0] == '>' ||
+ (CurPos[0] == '/' && CurPos[1] == '>')) {
+
+ if (name != NULL) {
+ free(name);
+ name = NULL;
+ }
+
+ return NULL;
+ }
+
+ /* Find end of name */
+ q = CurPos;
+ while (*q && !iswspace(*q) && *q != '=') NEXT_CHAR(q);
+
+ SetToken(TOKEN_UNKNOWN, CurPos, q);
+ if (name) {
+ free(name);
+ name = NULL;
+ }
+ name = JPACKAGE_STRDUP(CurTokenName);
+
+ /* Skip any whitespace */
+ CurPos = q;
+ CurPos = SkipFilling();
+
+ /* Next TCHARacter must be '=' for a valid attribute.
+ If it is not, this is really an error.
+ We ignore this, and just try to parse an attribute
+ out of the rest of the string.
+ */
+ } while (*CurPos != '=');
+
+ NEXT_CHAR(CurPos);
+ CurPos = SkipWhiteSpace(CurPos);
+ /* Parse CDATA part of attribute */
+ if ((*CurPos == '\"') || (*CurPos == '\'')) {
+ TCHAR quoteChar = *CurPos;
+ q = ++CurPos;
+ while (*q != '\0' && *q != quoteChar) NEXT_CHAR(q);
+ SetToken(TOKEN_CDATA, CurPos, q);
+ CurPos = q + 1;
+ } else {
+ q = CurPos;
+ while (*q != '\0' && !iswspace(*q)) NEXT_CHAR(q);
+ SetToken(TOKEN_CDATA, CurPos, q);
+ CurPos = q;
+ }
+
+ //Note: no need to free name and CurTokenName duplicate; they're assigned
+ // to an XMLAttribute structure in CreateXMLAttribute
+
+ return CreateXMLAttribute(name, JPACKAGE_STRDUP(CurTokenName));
+}
+
+void FreeXMLDocument(XMLNode* root) {
+ if (root == NULL) return;
+ FreeXMLDocument(root->_sub);
+ FreeXMLDocument(root->_next);
+ FreeXMLAttribute(root->_attributes);
+ free(root->_name);
+ free(root);
+}
+
+static void FreeXMLAttribute(XMLAttribute* attr) {
+ if (attr == NULL) return;
+ free(attr->_name);
+ free(attr->_value);
+ FreeXMLAttribute(attr->_next);
+ free(attr);
+}
+
+/* Find element at current level with a given name */
+XMLNode* FindXMLChild(XMLNode* root, const TCHAR* name) {
+ if (root == NULL) return NULL;
+
+ if (root->_type == xmlTagType && JPACKAGE_STRCMP(root->_name, name) == 0) {
+ return root;
+ }
+
+ return FindXMLChild(root->_next, name);
+}
+
+/* Search for an attribute with the given name and returns the contents. Returns NULL if
+ * attribute is not found
+ */
+TCHAR* FindXMLAttribute(XMLAttribute* attr, const TCHAR* name) {
+ if (attr == NULL) return NULL;
+ if (JPACKAGE_STRCMP(attr->_name, name) == 0) return attr->_value;
+ return FindXMLAttribute(attr->_next, name);
+}
+
+void PrintXMLDocument(XMLNode* node, int indt) {
+ if (node == NULL) return;
+
+ if (node->_type == xmlTagType) {
+ JPACKAGE_PRINTF(_T("\n"));
+ indent(indt);
+ JPACKAGE_PRINTF(_T("<%s"), node->_name);
+ PrintXMLAttributes(node->_attributes);
+ if (node->_sub == NULL) {
+ JPACKAGE_PRINTF(_T("/>\n"));
+ } else {
+ JPACKAGE_PRINTF(_T(">"));
+ PrintXMLDocument(node->_sub, indt + 1);
+ indent(indt);
+ JPACKAGE_PRINTF(_T("</%s>"), node->_name);
+ }
+ } else {
+ JPACKAGE_PRINTF(_T("%s"), node->_name);
+ }
+ PrintXMLDocument(node->_next, indt);
+}
+
+static void PrintXMLAttributes(XMLAttribute* attr) {
+ if (attr == NULL) return;
+
+ JPACKAGE_PRINTF(_T(" %s=\"%s\""), attr->_name, attr->_value);
+ PrintXMLAttributes(attr->_next);
+}
+
+static void indent(int indt) {
+ int i;
+ for (i = 0; i < indt; i++) {
+ JPACKAGE_PRINTF(_T(" "));
+ }
+}
+
+const TCHAR *CDStart = _T("<![CDATA[");
+const TCHAR *CDEnd = _T("]]>");
+
+static TCHAR* SkipPCData(TCHAR *p) {
+ TCHAR *end = JPACKAGE_STRSTR(p, CDEnd);
+ if (end != NULL) {
+ return end + sizeof (CDEnd);
+ }
+ return (++p);
+}
+
+static int IsPCData(TCHAR *p) {
+ const int size = sizeof (CDStart);
+ return (JPACKAGE_STRNCMP(CDStart, p, size) == 0);
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/linux/native/libapplauncher/LinuxPlatform.h Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2014, 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.
+ */
+
+#ifndef LINUXPLATFORM_H
+#define LINUXPLATFORM_H
+
+#include "Platform.h"
+#include "PosixPlatform.h"
+#include <X11/Xlib.h>
+#include <X11/Xatom.h>
+#include <pthread.h>
+#include <list>
+
+class LinuxPlatform : virtual public Platform, PosixPlatform {
+private:
+ pthread_t FMainThread;
+
+protected:
+ virtual TString getTmpDirString();
+
+public:
+ LinuxPlatform(void);
+ virtual ~LinuxPlatform(void);
+
+ TString GetPackageAppDirectory();
+ TString GetPackageLauncherDirectory();
+ TString GetPackageRuntimeBinDirectory();
+
+ virtual void ShowMessage(TString title, TString description);
+ virtual void ShowMessage(TString description);
+
+ virtual TCHAR* ConvertStringToFileSystemString(
+ TCHAR* Source, bool &release);
+ virtual TCHAR* ConvertFileSystemStringToString(
+ TCHAR* Source, bool &release);
+
+ virtual void SetCurrentDirectory(TString Value);
+ virtual TString GetPackageRootDirectory();
+ virtual TString GetAppDataDirectory();
+ virtual TString GetAppName();
+
+ virtual TString GetModuleFileName();
+
+ virtual TString GetBundledJavaLibraryFileName(TString RuntimePath);
+
+ virtual ISectionalPropertyContainer* GetConfigFile(TString FileName);
+
+ virtual bool IsMainThread();
+ virtual TPlatformNumber GetMemorySize();
+};
+
+#endif //LINUXPLATFORM_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/linux/native/libapplauncher/PlatformDefs.h Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 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.
+ */
+
+#ifndef PLATFORM_DEFS_H
+#define PLATFORM_DEFS_H
+
+#include <errno.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <dlfcn.h>
+#include <libgen.h>
+#include <string>
+
+using namespace std;
+
+#ifndef LINUX
+#define LINUX
+#endif
+
+#define _T(x) x
+
+typedef char TCHAR;
+typedef std::string TString;
+#define StringLength strlen
+
+typedef unsigned long DWORD;
+
+#define TRAILING_PATHSEPARATOR '/'
+#define BAD_TRAILING_PATHSEPARATOR '\\'
+#define PATH_SEPARATOR ':'
+#define BAD_PATH_SEPARATOR ';'
+#define MAX_PATH 1000
+
+typedef long TPlatformNumber;
+typedef pid_t TProcessID;
+
+#define HMODULE void*
+
+typedef void* Module;
+typedef void* Procedure;
+
+#define StringToFileSystemString PlatformString
+#define FileSystemStringToString PlatformString
+
+#endif // PLATFORM_DEFS_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/EnumeratedBundlerParam.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2014, 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.jpackage.internal;
+
+import java.util.*;
+import java.util.function.BiFunction;
+import java.util.function.Function;
+
+/**
+ * EnumeratedBundlerParams<T>
+ *
+ * Contains key-value pairs (elements) where keys are "displayable"
+ * keys which the IDE can display/choose and values are "identifier" values
+ * which can be stored in parameters' map.
+ *
+ * For instance the Mac has a predefined set of categories which can be applied
+ * to LSApplicationCategoryType which is required for the mac app store.
+ *
+ * The following example illustrates a simple usage of
+ * the MAC_CATEGORY parameter:
+ *
+ * <pre>{@code
+ * Set<String> keys = MAC_CATEGORY.getDisplayableKeys();
+ *
+ * String key = getLastValue(keys); // get last value for example
+ *
+ * String value = MAC_CATEGORY.getValueForDisplayableKey(key);
+ * params.put(MAC_CATEGORY.getID(), value);
+ * }</pre>
+ *
+ */
+class EnumeratedBundlerParam<T> extends BundlerParamInfo<T> {
+ // Not sure if this is the correct order, my idea is that from IDE
+ // perspective the string to display to the user is the key and then the
+ // value is some type of object (although probably a String in most cases)
+ private final Map<String, T> elements;
+ private final boolean strict;
+
+ EnumeratedBundlerParam(String id, Class<T> valueType,
+ Function<Map<String, ? super Object>, T> defaultValueFunction,
+ BiFunction<String, Map<String, ? super Object>, T> stringConverter,
+ Map<String, T> elements, boolean strict) {
+ this.id = id;
+ this.valueType = valueType;
+ this.defaultValueFunction = defaultValueFunction;
+ this.stringConverter = stringConverter;
+ this.elements = elements;
+ this.strict = strict;
+ }
+
+ boolean isInPossibleValues(T value) {
+ return elements.values().contains(value);
+ }
+
+ // Having the displayable values as the keys seems a bit wacky
+ Set<String> getDisplayableKeys() {
+ return Collections.unmodifiableSet(elements.keySet());
+ }
+
+ // mapping from a "displayable" key to an "identifier" value.
+ T getValueForDisplayableKey(String displayableKey) {
+ return elements.get(displayableKey);
+ }
+
+ boolean isStrict() {
+ return strict;
+ }
+
+ boolean isLoose() {
+ return !isStrict();
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacAppBundler.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,347 @@
+/*
+ * 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.jpackage.internal;
+
+import java.io.File;
+import java.io.IOException;
+import java.math.BigInteger;
+import java.text.MessageFormat;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+import java.util.ResourceBundle;
+
+import static jdk.jpackage.internal.StandardBundlerParam.*;
+import static jdk.jpackage.internal.MacBaseInstallerBundler.*;
+
+public class MacAppBundler extends AbstractImageBundler {
+
+ private static final ResourceBundle I18N = ResourceBundle.getBundle(
+ "jdk.jpackage.internal.resources.MacResources");
+
+ private static final String TEMPLATE_BUNDLE_ICON = "GenericApp.icns";
+
+ public static Map<String, String> getMacCategories() {
+ Map<String, String> map = new HashMap<>();
+ map.put("Business", "public.app-category.business");
+ map.put("Developer Tools", "public.app-category.developer-tools");
+ map.put("Education", "public.app-category.education");
+ map.put("Entertainment", "public.app-category.entertainment");
+ map.put("Finance", "public.app-category.finance");
+ map.put("Games", "public.app-category.games");
+ map.put("Graphics & Design", "public.app-category.graphics-design");
+ map.put("Healthcare & Fitness",
+ "public.app-category.healthcare-fitness");
+ map.put("Lifestyle", "public.app-category.lifestyle");
+ map.put("Medical", "public.app-category.medical");
+ map.put("Music", "public.app-category.music");
+ map.put("News", "public.app-category.news");
+ map.put("Photography", "public.app-category.photography");
+ map.put("Productivity", "public.app-category.productivity");
+ map.put("Reference", "public.app-category.reference");
+ map.put("Social Networking", "public.app-category.social-networking");
+ map.put("Sports", "public.app-category.sports");
+ map.put("Travel", "public.app-category.travel");
+ map.put("Utilities", "public.app-category.utilities");
+ map.put("Video", "public.app-category.video");
+ map.put("Weather", "public.app-category.weather");
+
+ map.put("Action Games", "public.app-category.action-games");
+ map.put("Adventure Games", "public.app-category.adventure-games");
+ map.put("Arcade Games", "public.app-category.arcade-games");
+ map.put("Board Games", "public.app-category.board-games");
+ map.put("Card Games", "public.app-category.card-games");
+ map.put("Casino Games", "public.app-category.casino-games");
+ map.put("Dice Games", "public.app-category.dice-games");
+ map.put("Educational Games", "public.app-category.educational-games");
+ map.put("Family Games", "public.app-category.family-games");
+ map.put("Kids Games", "public.app-category.kids-games");
+ map.put("Music Games", "public.app-category.music-games");
+ map.put("Puzzle Games", "public.app-category.puzzle-games");
+ map.put("Racing Games", "public.app-category.racing-games");
+ map.put("Role Playing Games", "public.app-category.role-playing-games");
+ map.put("Simulation Games", "public.app-category.simulation-games");
+ map.put("Sports Games", "public.app-category.sports-games");
+ map.put("Strategy Games", "public.app-category.strategy-games");
+ map.put("Trivia Games", "public.app-category.trivia-games");
+ map.put("Word Games", "public.app-category.word-games");
+
+ return map;
+ }
+
+ public static final EnumeratedBundlerParam<String> MAC_CATEGORY =
+ new EnumeratedBundlerParam<>(
+ Arguments.CLIOptions.MAC_APP_STORE_CATEGORY.getId(),
+ String.class,
+ params -> "Unknown",
+ (s, p) -> s,
+ getMacCategories(),
+ false //strict - for MacStoreBundler this should be strict
+ );
+
+ public static final BundlerParamInfo<String> MAC_CF_BUNDLE_NAME =
+ new StandardBundlerParam<>(
+ Arguments.CLIOptions.MAC_BUNDLE_NAME.getId(),
+ String.class,
+ params -> null,
+ (s, p) -> s);
+
+ public static final BundlerParamInfo<String> MAC_CF_BUNDLE_IDENTIFIER =
+ new StandardBundlerParam<>(
+ Arguments.CLIOptions.MAC_BUNDLE_IDENTIFIER.getId(),
+ String.class,
+ IDENTIFIER::fetchFrom,
+ (s, p) -> s);
+
+ public static final BundlerParamInfo<String> MAC_CF_BUNDLE_VERSION =
+ new StandardBundlerParam<>(
+ "mac.CFBundleVersion",
+ String.class,
+ p -> {
+ String s = VERSION.fetchFrom(p);
+ if (validCFBundleVersion(s)) {
+ return s;
+ } else {
+ return "100";
+ }
+ },
+ (s, p) -> s);
+
+ public static final BundlerParamInfo<String> DEFAULT_ICNS_ICON =
+ new StandardBundlerParam<>(
+ ".mac.default.icns",
+ String.class,
+ params -> TEMPLATE_BUNDLE_ICON,
+ (s, p) -> s);
+
+ public static final BundlerParamInfo<String> DEVELOPER_ID_APP_SIGNING_KEY =
+ new StandardBundlerParam<>(
+ "mac.signing-key-developer-id-app",
+ String.class,
+ params -> {
+ String result = MacBaseInstallerBundler.findKey(
+ "Developer ID Application: "
+ + SIGNING_KEY_USER.fetchFrom(params),
+ SIGNING_KEYCHAIN.fetchFrom(params),
+ VERBOSE.fetchFrom(params));
+ if (result != null) {
+ MacCertificate certificate = new MacCertificate(result,
+ VERBOSE.fetchFrom(params));
+
+ if (!certificate.isValid()) {
+ Log.error(MessageFormat.format(I18N.getString(
+ "error.certificate.expired"), result));
+ }
+ }
+
+ return result;
+ },
+ (s, p) -> s);
+
+ public static final BundlerParamInfo<String> BUNDLE_ID_SIGNING_PREFIX =
+ new StandardBundlerParam<>(
+ Arguments.CLIOptions.MAC_BUNDLE_SIGNING_PREFIX.getId(),
+ String.class,
+ params -> IDENTIFIER.fetchFrom(params) + ".",
+ (s, p) -> s);
+
+ public static final BundlerParamInfo<File> ICON_ICNS =
+ new StandardBundlerParam<>(
+ "icon.icns",
+ File.class,
+ params -> {
+ File f = ICON.fetchFrom(params);
+ if (f != null && !f.getName().toLowerCase().endsWith(".icns")) {
+ Log.error(MessageFormat.format(
+ I18N.getString("message.icon-not-icns"), f));
+ return null;
+ }
+ return f;
+ },
+ (s, p) -> new File(s));
+
+ public static boolean validCFBundleVersion(String v) {
+ // CFBundleVersion (String - iOS, OS X) specifies the build version
+ // number of the bundle, which identifies an iteration (released or
+ // unreleased) of the bundle. The build version number should be a
+ // string comprised of three non-negative, period-separated integers
+ // with the first integer being greater than zero. The string should
+ // only contain numeric (0-9) and period (.) characters. Leading zeros
+ // are truncated from each integer and will be ignored (that is,
+ // 1.02.3 is equivalent to 1.2.3). This key is not localizable.
+
+ if (v == null) {
+ return false;
+ }
+
+ String p[] = v.split("\\.");
+ if (p.length > 3 || p.length < 1) {
+ Log.verbose(I18N.getString(
+ "message.version-string-too-many-components"));
+ return false;
+ }
+
+ try {
+ BigInteger n = new BigInteger(p[0]);
+ if (BigInteger.ONE.compareTo(n) > 0) {
+ Log.verbose(I18N.getString(
+ "message.version-string-first-number-not-zero"));
+ return false;
+ }
+ if (p.length > 1) {
+ n = new BigInteger(p[1]);
+ if (BigInteger.ZERO.compareTo(n) > 0) {
+ Log.verbose(I18N.getString(
+ "message.version-string-no-negative-numbers"));
+ return false;
+ }
+ }
+ if (p.length > 2) {
+ n = new BigInteger(p[2]);
+ if (BigInteger.ZERO.compareTo(n) > 0) {
+ Log.verbose(I18N.getString(
+ "message.version-string-no-negative-numbers"));
+ return false;
+ }
+ }
+ } catch (NumberFormatException ne) {
+ Log.verbose(I18N.getString("message.version-string-numbers-only"));
+ Log.verbose(ne);
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public boolean validate(Map<String, ? super Object> params)
+ throws ConfigException {
+ try {
+ return doValidate(params);
+ } catch (RuntimeException re) {
+ if (re.getCause() instanceof ConfigException) {
+ throw (ConfigException) re.getCause();
+ } else {
+ throw new ConfigException(re);
+ }
+ }
+ }
+
+ private boolean doValidate(Map<String, ? super Object> params)
+ throws ConfigException {
+
+ imageBundleValidation(params);
+
+ if (StandardBundlerParam.getPredefinedAppImage(params) != null) {
+ return true;
+ }
+
+ // validate short version
+ if (!validCFBundleVersion(MAC_CF_BUNDLE_VERSION.fetchFrom(params))) {
+ throw new ConfigException(
+ I18N.getString("error.invalid-cfbundle-version"),
+ I18N.getString("error.invalid-cfbundle-version.advice"));
+ }
+
+ // reject explicitly set sign to true and no valid signature key
+ if (Optional.ofNullable(MacAppImageBuilder.
+ SIGN_BUNDLE.fetchFrom(params)).orElse(Boolean.FALSE)) {
+ String signingIdentity =
+ DEVELOPER_ID_APP_SIGNING_KEY.fetchFrom(params);
+ if (signingIdentity == null) {
+ throw new ConfigException(
+ I18N.getString("error.explicit-sign-no-cert"),
+ I18N.getString("error.explicit-sign-no-cert.advice"));
+ }
+ }
+
+ return true;
+ }
+
+ File doBundle(Map<String, ? super Object> params, File outputDirectory,
+ boolean dependentTask) throws PackagerException {
+ if (StandardBundlerParam.isRuntimeInstaller(params)) {
+ return PREDEFINED_RUNTIME_IMAGE.fetchFrom(params);
+ } else {
+ return doAppBundle(params, outputDirectory, dependentTask);
+ }
+ }
+
+ File doAppBundle(Map<String, ? super Object> params, File outputDirectory,
+ boolean dependentTask) throws PackagerException {
+ try {
+ File rootDirectory = createRoot(params, outputDirectory,
+ dependentTask, APP_NAME.fetchFrom(params) + ".app");
+ AbstractAppImageBuilder appBuilder =
+ new MacAppImageBuilder(params, outputDirectory.toPath());
+ if (PREDEFINED_RUNTIME_IMAGE.fetchFrom(params) == null ) {
+ JLinkBundlerHelper.execute(params, appBuilder);
+ } else {
+ StandardBundlerParam.copyPredefinedRuntimeImage(
+ params, appBuilder);
+ }
+ return rootDirectory;
+ } catch (PackagerException pe) {
+ throw pe;
+ } catch (Exception ex) {
+ Log.verbose(ex);
+ throw new PackagerException(ex);
+ }
+ }
+
+ /////////////////////////////////////////////////////////////////////////
+ // Implement Bundler
+ /////////////////////////////////////////////////////////////////////////
+
+ @Override
+ public String getName() {
+ return I18N.getString("app.bundler.name");
+ }
+
+ @Override
+ public String getID() {
+ return "mac.app";
+ }
+
+ @Override
+ public String getBundleType() {
+ return "IMAGE";
+ }
+
+ @Override
+ public File execute(Map<String, ? super Object> params,
+ File outputParentDir) throws PackagerException {
+ return doBundle(params, outputParentDir, false);
+ }
+
+ @Override
+ public boolean supported(boolean runtimeInstaller) {
+ return true;
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacAppImageBuilder.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,933 @@
+/*
+ * Copyright (c) 2015, 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.jpackage.internal;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.UncheckedIOException;
+import java.io.Writer;
+import java.math.BigInteger;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardCopyOption;
+import java.nio.file.attribute.PosixFilePermission;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.ResourceBundle;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Consumer;
+
+import static jdk.jpackage.internal.StandardBundlerParam.*;
+import static jdk.jpackage.internal.MacBaseInstallerBundler.*;
+import static jdk.jpackage.internal.MacAppBundler.*;
+
+public class MacAppImageBuilder extends AbstractAppImageBuilder {
+
+ private static final ResourceBundle I18N = ResourceBundle.getBundle(
+ "jdk.jpackage.internal.resources.MacResources");
+
+ private static final String LIBRARY_NAME = "libapplauncher.dylib";
+ private static final String TEMPLATE_BUNDLE_ICON = "GenericApp.icns";
+ private static final String OS_TYPE_CODE = "APPL";
+ private static final String TEMPLATE_INFO_PLIST_LITE =
+ "Info-lite.plist.template";
+ private static final String TEMPLATE_RUNTIME_INFO_PLIST =
+ "Runtime-Info.plist.template";
+
+ private final Path root;
+ private final Path contentsDir;
+ private final Path javaDir;
+ private final Path javaModsDir;
+ private final Path resourcesDir;
+ private final Path macOSDir;
+ private final Path runtimeDir;
+ private final Path runtimeRoot;
+ private final Path mdir;
+
+ private final Map<String, ? super Object> params;
+
+ private static List<String> keyChains;
+
+ public static final BundlerParamInfo<Boolean>
+ MAC_CONFIGURE_LAUNCHER_IN_PLIST = new StandardBundlerParam<>(
+ "mac.configure-launcher-in-plist",
+ Boolean.class,
+ params -> Boolean.FALSE,
+ (s, p) -> Boolean.valueOf(s));
+
+ public static final EnumeratedBundlerParam<String> MAC_CATEGORY =
+ new EnumeratedBundlerParam<>(
+ Arguments.CLIOptions.MAC_APP_STORE_CATEGORY.getId(),
+ String.class,
+ params -> "Unknown",
+ (s, p) -> s,
+ MacAppBundler.getMacCategories(),
+ false //strict - for MacStoreBundler this should be strict
+ );
+
+ public static final BundlerParamInfo<String> MAC_CF_BUNDLE_NAME =
+ new StandardBundlerParam<>(
+ Arguments.CLIOptions.MAC_BUNDLE_NAME.getId(),
+ String.class,
+ params -> null,
+ (s, p) -> s);
+
+ public static final BundlerParamInfo<String> MAC_CF_BUNDLE_IDENTIFIER =
+ new StandardBundlerParam<>(
+ Arguments.CLIOptions.MAC_BUNDLE_IDENTIFIER.getId(),
+ String.class,
+ IDENTIFIER::fetchFrom,
+ (s, p) -> s);
+
+ public static final BundlerParamInfo<String> MAC_CF_BUNDLE_VERSION =
+ new StandardBundlerParam<>(
+ "mac.CFBundleVersion",
+ String.class,
+ p -> {
+ String s = VERSION.fetchFrom(p);
+ if (validCFBundleVersion(s)) {
+ return s;
+ } else {
+ return "100";
+ }
+ },
+ (s, p) -> s);
+
+ public static final BundlerParamInfo<String> DEFAULT_ICNS_ICON =
+ new StandardBundlerParam<>(
+ ".mac.default.icns",
+ String.class,
+ params -> TEMPLATE_BUNDLE_ICON,
+ (s, p) -> s);
+
+ public static final BundlerParamInfo<File> ICON_ICNS =
+ new StandardBundlerParam<>(
+ "icon.icns",
+ File.class,
+ params -> {
+ File f = ICON.fetchFrom(params);
+ if (f != null && !f.getName().toLowerCase().endsWith(".icns")) {
+ Log.error(MessageFormat.format(
+ I18N.getString("message.icon-not-icns"), f));
+ return null;
+ }
+ return f;
+ },
+ (s, p) -> new File(s));
+
+ public static final StandardBundlerParam<Boolean> SIGN_BUNDLE =
+ new StandardBundlerParam<>(
+ Arguments.CLIOptions.MAC_SIGN.getId(),
+ Boolean.class,
+ params -> false,
+ // valueOf(null) is false, we actually do want null in some cases
+ (s, p) -> (s == null || "null".equalsIgnoreCase(s)) ?
+ null : Boolean.valueOf(s)
+ );
+
+ public MacAppImageBuilder(Map<String, Object> config, Path imageOutDir)
+ throws IOException {
+ super(config, imageOutDir.resolve(APP_NAME.fetchFrom(config)
+ + ".app/Contents/runtime/Contents/Home"));
+
+ Objects.requireNonNull(imageOutDir);
+
+ this.params = config;
+ this.root = imageOutDir.resolve(APP_NAME.fetchFrom(params) + ".app");
+ this.contentsDir = root.resolve("Contents");
+ this.javaDir = contentsDir.resolve("Java");
+ this.javaModsDir = javaDir.resolve("mods");
+ this.resourcesDir = contentsDir.resolve("Resources");
+ this.macOSDir = contentsDir.resolve("MacOS");
+ this.runtimeDir = contentsDir.resolve("runtime");
+ this.runtimeRoot = runtimeDir.resolve("Contents/Home");
+ this.mdir = runtimeRoot.resolve("lib");
+ Files.createDirectories(javaDir);
+ Files.createDirectories(resourcesDir);
+ Files.createDirectories(macOSDir);
+ Files.createDirectories(runtimeDir);
+ }
+
+ public MacAppImageBuilder(Map<String, Object> config, String jreName,
+ Path imageOutDir) throws IOException {
+ super(null, imageOutDir.resolve(jreName + "/Contents/Home"));
+
+ Objects.requireNonNull(imageOutDir);
+
+ this.params = config;
+ this.root = imageOutDir.resolve(jreName );
+ this.contentsDir = root.resolve("Contents");
+ this.javaDir = null;
+ this.javaModsDir = null;
+ this.resourcesDir = null;
+ this.macOSDir = null;
+ this.runtimeDir = this.root;
+ this.runtimeRoot = runtimeDir.resolve("Contents/Home");
+ this.mdir = runtimeRoot.resolve("lib");
+
+ Files.createDirectories(runtimeDir);
+ }
+
+ private void writeEntry(InputStream in, Path dstFile) throws IOException {
+ Files.createDirectories(dstFile.getParent());
+ Files.copy(in, dstFile);
+ }
+
+ public static boolean validCFBundleVersion(String v) {
+ // CFBundleVersion (String - iOS, OS X) specifies the build version
+ // number of the bundle, which identifies an iteration (released or
+ // unreleased) of the bundle. The build version number should be a
+ // string comprised of three non-negative, period-separated integers
+ // with the first integer being greater than zero. The string should
+ // only contain numeric (0-9) and period (.) characters. Leading zeros
+ // are truncated from each integer and will be ignored (that is,
+ // 1.02.3 is equivalent to 1.2.3). This key is not localizable.
+
+ if (v == null) {
+ return false;
+ }
+
+ String p[] = v.split("\\.");
+ if (p.length > 3 || p.length < 1) {
+ Log.verbose(I18N.getString(
+ "message.version-string-too-many-components"));
+ return false;
+ }
+
+ try {
+ BigInteger n = new BigInteger(p[0]);
+ if (BigInteger.ONE.compareTo(n) > 0) {
+ Log.verbose(I18N.getString(
+ "message.version-string-first-number-not-zero"));
+ return false;
+ }
+ if (p.length > 1) {
+ n = new BigInteger(p[1]);
+ if (BigInteger.ZERO.compareTo(n) > 0) {
+ Log.verbose(I18N.getString(
+ "message.version-string-no-negative-numbers"));
+ return false;
+ }
+ }
+ if (p.length > 2) {
+ n = new BigInteger(p[2]);
+ if (BigInteger.ZERO.compareTo(n) > 0) {
+ Log.verbose(I18N.getString(
+ "message.version-string-no-negative-numbers"));
+ return false;
+ }
+ }
+ } catch (NumberFormatException ne) {
+ Log.verbose(I18N.getString("message.version-string-numbers-only"));
+ Log.verbose(ne);
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public Path getAppDir() {
+ return javaDir;
+ }
+
+ @Override
+ public Path getAppModsDir() {
+ return javaModsDir;
+ }
+
+ @Override
+ public void prepareApplicationFiles() throws IOException {
+ Map<String, ? super Object> originalParams = new HashMap<>(params);
+ // Generate PkgInfo
+ File pkgInfoFile = new File(contentsDir.toFile(), "PkgInfo");
+ pkgInfoFile.createNewFile();
+ writePkgInfo(pkgInfoFile);
+
+ Path executable = macOSDir.resolve(getLauncherName(params));
+
+ // create the main app launcher
+ try (InputStream is_launcher =
+ getResourceAsStream("jpackageapplauncher");
+ InputStream is_lib = getResourceAsStream(LIBRARY_NAME)) {
+ // Copy executable and library to MacOS folder
+ writeEntry(is_launcher, executable);
+ writeEntry(is_lib, macOSDir.resolve(LIBRARY_NAME));
+ }
+ executable.toFile().setExecutable(true, false);
+ // generate main app launcher config file
+ File cfg = new File(root.toFile(), getLauncherCfgName(params));
+ writeCfgFile(params, cfg, "$APPDIR/runtime");
+
+ // create additional app launcher(s) and config file(s)
+ List<Map<String, ? super Object>> entryPoints =
+ StandardBundlerParam.ADD_LAUNCHERS.fetchFrom(params);
+ for (Map<String, ? super Object> entryPoint : entryPoints) {
+ Map<String, ? super Object> tmp =
+ AddLauncherArguments.merge(originalParams, entryPoint);
+
+ // add executable for add launcher
+ Path addExecutable = macOSDir.resolve(getLauncherName(tmp));
+ try (InputStream is = getResourceAsStream("jpackageapplauncher");) {
+ writeEntry(is, addExecutable);
+ }
+ addExecutable.toFile().setExecutable(true, false);
+
+ // add config file for add launcher
+ cfg = new File(root.toFile(), getLauncherCfgName(tmp));
+ writeCfgFile(tmp, cfg, "$APPDIR/runtime");
+ }
+
+ // Copy class path entries to Java folder
+ copyClassPathEntries(javaDir);
+
+ /*********** Take care of "config" files *******/
+ File icon = ICON_ICNS.fetchFrom(params);
+
+ InputStream in = locateResource(
+ APP_NAME.fetchFrom(params) + ".icns",
+ "icon",
+ DEFAULT_ICNS_ICON.fetchFrom(params),
+ icon,
+ VERBOSE.fetchFrom(params),
+ RESOURCE_DIR.fetchFrom(params));
+ Files.copy(in,
+ resourcesDir.resolve(APP_NAME.fetchFrom(params) + ".icns"),
+ StandardCopyOption.REPLACE_EXISTING);
+
+ // copy file association icons
+ for (Map<String, ?
+ super Object> fa : FILE_ASSOCIATIONS.fetchFrom(params)) {
+ File f = FA_ICON.fetchFrom(fa);
+ if (f != null && f.exists()) {
+ try (InputStream in2 = new FileInputStream(f)) {
+ Files.copy(in2, resourcesDir.resolve(f.getName()));
+ }
+
+ }
+ }
+
+ copyRuntimeFiles();
+ sign();
+ }
+
+ @Override
+ public void prepareJreFiles() throws IOException {
+ copyRuntimeFiles();
+ sign();
+ }
+
+ private void copyRuntimeFiles() throws IOException {
+ // Generate Info.plist
+ writeInfoPlist(contentsDir.resolve("Info.plist").toFile());
+
+ // generate java runtime info.plist
+ writeRuntimeInfoPlist(
+ runtimeDir.resolve("Contents/Info.plist").toFile());
+
+ // copy library
+ Path runtimeMacOSDir = Files.createDirectories(
+ runtimeDir.resolve("Contents/MacOS"));
+
+ // JDK 9, 10, and 11 have extra '/jli/' subdir
+ Path jli = runtimeRoot.resolve("lib/libjli.dylib");
+ if (!Files.exists(jli)) {
+ jli = runtimeRoot.resolve("lib/jli/libjli.dylib");
+ }
+
+ Files.copy(jli, runtimeMacOSDir.resolve("libjli.dylib"));
+ }
+
+ private void sign() throws IOException {
+ if (Optional.ofNullable(
+ SIGN_BUNDLE.fetchFrom(params)).orElse(Boolean.TRUE)) {
+ try {
+ addNewKeychain(params);
+ } catch (InterruptedException e) {
+ Log.error(e.getMessage());
+ }
+ String signingIdentity =
+ DEVELOPER_ID_APP_SIGNING_KEY.fetchFrom(params);
+ if (signingIdentity != null) {
+ signAppBundle(params, root, signingIdentity,
+ BUNDLE_ID_SIGNING_PREFIX.fetchFrom(params), null, null);
+ }
+ restoreKeychainList(params);
+ }
+ }
+
+ private String getLauncherName(Map<String, ? super Object> params) {
+ if (APP_NAME.fetchFrom(params) != null) {
+ return APP_NAME.fetchFrom(params);
+ } else {
+ return MAIN_CLASS.fetchFrom(params);
+ }
+ }
+
+ public static String getLauncherCfgName(
+ Map<String, ? super Object> params) {
+ return "Contents/Java/" + APP_NAME.fetchFrom(params) + ".cfg";
+ }
+
+ private void copyClassPathEntries(Path javaDirectory) 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);
+ if (bn.length() > 16) {
+ Log.error(MessageFormat.format(I18N.getString(
+ "message.bundle-name-too-long-warning"),
+ MAC_CF_BUNDLE_NAME.getID(), bn));
+ }
+ return MAC_CF_BUNDLE_NAME.fetchFrom(params);
+ } else if (APP_NAME.fetchFrom(params) != null) {
+ return APP_NAME.fetchFrom(params);
+ } else {
+ String nm = MAIN_CLASS.fetchFrom(params);
+ if (nm.length() > 16) {
+ nm = nm.substring(0, 16);
+ }
+ return nm;
+ }
+ }
+
+ private void writeRuntimeInfoPlist(File file) throws IOException {
+ Map<String, String> data = new HashMap<>();
+ String identifier = StandardBundlerParam.isRuntimeInstaller(params) ?
+ MAC_CF_BUNDLE_IDENTIFIER.fetchFrom(params) :
+ "com.oracle.java." + MAC_CF_BUNDLE_IDENTIFIER.fetchFrom(params);
+ data.put("CF_BUNDLE_IDENTIFIER", identifier);
+ String name = StandardBundlerParam.isRuntimeInstaller(params) ?
+ getBundleName(params): "Java Runtime Image";
+ data.put("CF_BUNDLE_NAME", name);
+ data.put("CF_BUNDLE_VERSION", VERSION.fetchFrom(params));
+ data.put("CF_BUNDLE_SHORT_VERSION_STRING", VERSION.fetchFrom(params));
+
+ try (Writer w = Files.newBufferedWriter(file.toPath())) {
+ w.write(preprocessTextResource("Runtime-Info.plist",
+ I18N.getString("resource.runtime-info-plist"),
+ TEMPLATE_RUNTIME_INFO_PLIST,
+ data,
+ VERBOSE.fetchFrom(params),
+ RESOURCE_DIR.fetchFrom(params)));
+ }
+ }
+
+ private void writeInfoPlist(File file) throws IOException {
+ Log.verbose(MessageFormat.format(I18N.getString(
+ "message.preparing-info-plist"), file.getAbsolutePath()));
+
+ //prepare config for exe
+ //Note: do not need CFBundleDisplayName if we don't support localization
+ Map<String, String> data = new HashMap<>();
+ data.put("DEPLOY_ICON_FILE", APP_NAME.fetchFrom(params) + ".icns");
+ data.put("DEPLOY_BUNDLE_IDENTIFIER",
+ MAC_CF_BUNDLE_IDENTIFIER.fetchFrom(params));
+ data.put("DEPLOY_BUNDLE_NAME",
+ getBundleName(params));
+ data.put("DEPLOY_BUNDLE_COPYRIGHT",
+ COPYRIGHT.fetchFrom(params) != null ?
+ COPYRIGHT.fetchFrom(params) : "Unknown");
+ data.put("DEPLOY_LAUNCHER_NAME", getLauncherName(params));
+ data.put("DEPLOY_JAVA_RUNTIME_NAME", "$APPDIR/runtime");
+ data.put("DEPLOY_BUNDLE_SHORT_VERSION",
+ VERSION.fetchFrom(params) != null ?
+ VERSION.fetchFrom(params) : "1.0.0");
+ data.put("DEPLOY_BUNDLE_CFBUNDLE_VERSION",
+ MAC_CF_BUNDLE_VERSION.fetchFrom(params) != null ?
+ MAC_CF_BUNDLE_VERSION.fetchFrom(params) : "100");
+ data.put("DEPLOY_BUNDLE_CATEGORY", MAC_CATEGORY.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));
+
+ StringBuilder macroedPath = new StringBuilder();
+ for (String s : CLASSPATH.fetchFrom(params).split("[ ;:]+")) {
+ macroedPath.append(s);
+ macroedPath.append(":");
+ }
+ macroedPath.deleteCharAt(macroedPath.length() - 1);
+
+ data.put("DEPLOY_APP_CLASSPATH", macroedPath.toString());
+
+ StringBuilder bundleDocumentTypes = new StringBuilder();
+ StringBuilder exportedTypes = new StringBuilder();
+ for (Map<String, ? super Object>
+ fileAssociation : FILE_ASSOCIATIONS.fetchFrom(params)) {
+
+ List<String> extensions = FA_EXTENSIONS.fetchFrom(fileAssociation);
+
+ if (extensions == null) {
+ Log.verbose(I18N.getString(
+ "message.creating-association-with-null-extension"));
+ }
+
+ List<String> mimeTypes = FA_CONTENT_TYPE.fetchFrom(fileAssociation);
+ String itemContentType = MAC_CF_BUNDLE_IDENTIFIER.fetchFrom(params)
+ + "." + ((extensions == null || extensions.isEmpty())
+ ? "mime" : extensions.get(0));
+ String description = FA_DESCRIPTION.fetchFrom(fileAssociation);
+ File icon = FA_ICON.fetchFrom(fileAssociation);
+
+ bundleDocumentTypes.append(" <dict>\n")
+ .append(" <key>LSItemContentTypes</key>\n")
+ .append(" <array>\n")
+ .append(" <string>")
+ .append(itemContentType)
+ .append("</string>\n")
+ .append(" </array>\n")
+ .append("\n")
+ .append(" <key>CFBundleTypeName</key>\n")
+ .append(" <string>")
+ .append(description)
+ .append("</string>\n")
+ .append("\n")
+ .append(" <key>LSHandlerRank</key>\n")
+ .append(" <string>Owner</string>\n")
+ // TODO make a bundler arg
+ .append("\n")
+ .append(" <key>CFBundleTypeRole</key>\n")
+ .append(" <string>Editor</string>\n")
+ // TODO make a bundler arg
+ .append("\n")
+ .append(" <key>LSIsAppleDefaultForType</key>\n")
+ .append(" <true/>\n")
+ // TODO make a bundler arg
+ .append("\n");
+
+ if (icon != null && icon.exists()) {
+ bundleDocumentTypes
+ .append(" <key>CFBundleTypeIconFile</key>\n")
+ .append(" <string>")
+ .append(icon.getName())
+ .append("</string>\n");
+ }
+ bundleDocumentTypes.append(" </dict>\n");
+
+ exportedTypes.append(" <dict>\n")
+ .append(" <key>UTTypeIdentifier</key>\n")
+ .append(" <string>")
+ .append(itemContentType)
+ .append("</string>\n")
+ .append("\n")
+ .append(" <key>UTTypeDescription</key>\n")
+ .append(" <string>")
+ .append(description)
+ .append("</string>\n")
+ .append(" <key>UTTypeConformsTo</key>\n")
+ .append(" <array>\n")
+ .append(" <string>public.data</string>\n")
+ //TODO expose this?
+ .append(" </array>\n")
+ .append("\n");
+
+ if (icon != null && icon.exists()) {
+ exportedTypes.append(" <key>UTTypeIconFile</key>\n")
+ .append(" <string>")
+ .append(icon.getName())
+ .append("</string>\n")
+ .append("\n");
+ }
+
+ exportedTypes.append("\n")
+ .append(" <key>UTTypeTagSpecification</key>\n")
+ .append(" <dict>\n")
+ // TODO expose via param? .append(
+ // " <key>com.apple.ostype</key>\n");
+ // TODO expose via param? .append(
+ // " <string>ABCD</string>\n")
+ .append("\n");
+
+ if (extensions != null && !extensions.isEmpty()) {
+ exportedTypes.append(
+ " <key>public.filename-extension</key>\n")
+ .append(" <array>\n");
+
+ for (String ext : extensions) {
+ exportedTypes.append(" <string>")
+ .append(ext)
+ .append("</string>\n");
+ }
+ exportedTypes.append(" </array>\n");
+ }
+ if (mimeTypes != null && !mimeTypes.isEmpty()) {
+ exportedTypes.append(" <key>public.mime-type</key>\n")
+ .append(" <array>\n");
+
+ for (String mime : mimeTypes) {
+ exportedTypes.append(" <string>")
+ .append(mime)
+ .append("</string>\n");
+ }
+ exportedTypes.append(" </array>\n");
+ }
+ exportedTypes.append(" </dict>\n")
+ .append(" </dict>\n");
+ }
+ String associationData;
+ if (bundleDocumentTypes.length() > 0) {
+ associationData =
+ "\n <key>CFBundleDocumentTypes</key>\n <array>\n"
+ + bundleDocumentTypes.toString()
+ + " </array>\n\n"
+ + " <key>UTExportedTypeDeclarations</key>\n <array>\n"
+ + exportedTypes.toString()
+ + " </array>\n";
+ } else {
+ associationData = "";
+ }
+ data.put("DEPLOY_FILE_ASSOCIATIONS", associationData);
+
+
+ try (Writer w = Files.newBufferedWriter(file.toPath())) {
+ w.write(preprocessTextResource(
+ // getConfig_InfoPlist(params).getName(),
+ "Info.plist",
+ I18N.getString("resource.app-info-plist"),
+ TEMPLATE_INFO_PLIST_LITE,
+ data, VERBOSE.fetchFrom(params),
+ RESOURCE_DIR.fetchFrom(params)));
+ }
+ }
+
+ private void writePkgInfo(File file) throws IOException {
+ //hardcoded as it does not seem we need to change it ever
+ String signature = "????";
+
+ try (Writer out = Files.newBufferedWriter(file.toPath())) {
+ out.write(OS_TYPE_CODE + signature);
+ out.flush();
+ }
+ }
+
+ public static void addNewKeychain(Map<String, ? super Object> params)
+ throws IOException, InterruptedException {
+ if (Platform.getMajorVersion() < 10 ||
+ (Platform.getMajorVersion() == 10 &&
+ Platform.getMinorVersion() < 12)) {
+ // we need this for OS X 10.12+
+ return;
+ }
+
+ String keyChain = SIGNING_KEYCHAIN.fetchFrom(params);
+ if (keyChain == null || keyChain.isEmpty()) {
+ return;
+ }
+
+ // get current keychain list
+ String keyChainPath = new File (keyChain).getAbsolutePath().toString();
+ List<String> keychainList = new ArrayList<>();
+ int ret = IOUtils.getProcessOutput(
+ keychainList, "security", "list-keychains");
+ if (ret != 0) {
+ Log.error(I18N.getString("message.keychain.error"));
+ return;
+ }
+
+ boolean contains = keychainList.stream().anyMatch(
+ str -> str.trim().equals("\""+keyChainPath.trim()+"\""));
+ if (contains) {
+ // keychain is already added in the search list
+ return;
+ }
+
+ keyChains = new ArrayList<>();
+ // remove "
+ keychainList.forEach((String s) -> {
+ String path = s.trim();
+ if (path.startsWith("\"") && path.endsWith("\"")) {
+ path = path.substring(1, path.length()-1);
+ }
+ keyChains.add(path);
+ });
+
+ List<String> args = new ArrayList<>();
+ args.add("security");
+ args.add("list-keychains");
+ args.add("-s");
+
+ args.addAll(keyChains);
+ args.add(keyChain);
+
+ ProcessBuilder pb = new ProcessBuilder(args);
+ IOUtils.exec(pb);
+ }
+
+ public static void restoreKeychainList(Map<String, ? super Object> params)
+ throws IOException{
+ if (Platform.getMajorVersion() < 10 ||
+ (Platform.getMajorVersion() == 10 &&
+ Platform.getMinorVersion() < 12)) {
+ // we need this for OS X 10.12+
+ return;
+ }
+
+ if (keyChains == null || keyChains.isEmpty()) {
+ return;
+ }
+
+ List<String> args = new ArrayList<>();
+ args.add("security");
+ args.add("list-keychains");
+ args.add("-s");
+
+ args.addAll(keyChains);
+
+ ProcessBuilder pb = new ProcessBuilder(args);
+ IOUtils.exec(pb);
+ }
+
+ public static void signAppBundle(
+ Map<String, ? super Object> params, Path appLocation,
+ String signingIdentity, String identifierPrefix,
+ String entitlementsFile, String inheritedEntitlements)
+ throws IOException {
+ AtomicReference<IOException> toThrow = new AtomicReference<>();
+ String appExecutable = "/Contents/MacOS/" + APP_NAME.fetchFrom(params);
+ String keyChain = SIGNING_KEYCHAIN.fetchFrom(params);
+
+ // sign all dylibs and jars
+ Files.walk(appLocation)
+ // fix permissions
+ .peek(path -> {
+ try {
+ Set<PosixFilePermission> pfp =
+ Files.getPosixFilePermissions(path);
+ if (!pfp.contains(PosixFilePermission.OWNER_WRITE)) {
+ pfp = EnumSet.copyOf(pfp);
+ pfp.add(PosixFilePermission.OWNER_WRITE);
+ Files.setPosixFilePermissions(path, pfp);
+ }
+ } catch (IOException e) {
+ Log.debug(e);
+ }
+ })
+ .filter(p -> Files.isRegularFile(p) &&
+ !(p.toString().contains("/Contents/MacOS/libjli.dylib")
+ || p.toString().endsWith(appExecutable))
+ ).forEach(p -> {
+ //noinspection ThrowableResultOfMethodCallIgnored
+ if (toThrow.get() != null) return;
+
+ // If p is a symlink then skip the signing process.
+ if (Files.isSymbolicLink(p)) {
+ if (VERBOSE.fetchFrom(params)) {
+ Log.verbose(MessageFormat.format(I18N.getString(
+ "message.ignoring.symlink"), p.toString()));
+ }
+ }
+ else {
+ List<String> args = new ArrayList<>();
+ args.addAll(Arrays.asList("codesign",
+ "-s", signingIdentity, // sign with this key
+ "--prefix", identifierPrefix,
+ // use the identifier as a prefix
+ "-vvvv"));
+ if (entitlementsFile != null &&
+ (p.toString().endsWith(".jar")
+ || p.toString().endsWith(".dylib"))) {
+ args.add("--entitlements");
+ args.add(entitlementsFile); // entitlements
+ } else if (inheritedEntitlements != null &&
+ Files.isExecutable(p)) {
+ args.add("--entitlements");
+ args.add(inheritedEntitlements);
+ // inherited entitlements for executable processes
+ }
+ if (keyChain != null && !keyChain.isEmpty()) {
+ args.add("--keychain");
+ args.add(keyChain);
+ }
+ args.add(p.toString());
+
+ try {
+ Set<PosixFilePermission> oldPermissions =
+ Files.getPosixFilePermissions(p);
+ File f = p.toFile();
+ f.setWritable(true, true);
+
+ ProcessBuilder pb = new ProcessBuilder(args);
+ IOUtils.exec(pb);
+
+ Files.setPosixFilePermissions(p, oldPermissions);
+ } catch (IOException ioe) {
+ toThrow.set(ioe);
+ }
+ }
+ });
+
+ IOException ioe = toThrow.get();
+ if (ioe != null) {
+ throw ioe;
+ }
+
+ // sign all runtime and frameworks
+ Consumer<? super Path> signIdentifiedByPList = path -> {
+ //noinspection ThrowableResultOfMethodCallIgnored
+ if (toThrow.get() != null) return;
+
+ try {
+ List<String> args = new ArrayList<>();
+ args.addAll(Arrays.asList("codesign",
+ "-s", signingIdentity, // sign with this key
+ "--prefix", identifierPrefix,
+ // use the identifier as a prefix
+ "-vvvv"));
+ if (keyChain != null && !keyChain.isEmpty()) {
+ args.add("--keychain");
+ args.add(keyChain);
+ }
+ args.add(path.toString());
+ ProcessBuilder pb = new ProcessBuilder(args);
+ IOUtils.exec(pb);
+
+ args = new ArrayList<>();
+ args.addAll(Arrays.asList("codesign",
+ "-s", signingIdentity, // sign with this key
+ "--prefix", identifierPrefix,
+ // use the identifier as a prefix
+ "-vvvv"));
+ if (keyChain != null && !keyChain.isEmpty()) {
+ args.add("--keychain");
+ args.add(keyChain);
+ }
+ args.add(path.toString()
+ + "/Contents/_CodeSignature/CodeResources");
+ pb = new ProcessBuilder(args);
+ IOUtils.exec(pb);
+ } catch (IOException e) {
+ toThrow.set(e);
+ }
+ };
+
+ Path javaPath = appLocation.resolve("Contents/runtime");
+ if (Files.isDirectory(javaPath)) {
+ signIdentifiedByPList.accept(javaPath);
+
+ ioe = toThrow.get();
+ if (ioe != null) {
+ throw ioe;
+ }
+ }
+ Path frameworkPath = appLocation.resolve("Contents/Frameworks");
+ if (Files.isDirectory(frameworkPath)) {
+ Files.list(frameworkPath)
+ .forEach(signIdentifiedByPList);
+
+ ioe = toThrow.get();
+ if (ioe != null) {
+ throw ioe;
+ }
+ }
+
+ // sign the app itself
+ List<String> args = new ArrayList<>();
+ args.addAll(Arrays.asList("codesign",
+ "-s", signingIdentity, // sign with this key
+ "-vvvv")); // super verbose output
+ if (entitlementsFile != null) {
+ args.add("--entitlements");
+ args.add(entitlementsFile); // entitlements
+ }
+ if (keyChain != null && !keyChain.isEmpty()) {
+ args.add("--keychain");
+ args.add(keyChain);
+ }
+ args.add(appLocation.toString());
+
+ ProcessBuilder pb =
+ new ProcessBuilder(args.toArray(new String[args.size()]));
+ IOUtils.exec(pb);
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacAppStoreBundler.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,327 @@
+/*
+ * Copyright (c) 2014, 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.jpackage.internal;
+
+import java.io.File;
+import java.io.IOException;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.ResourceBundle;
+
+import static jdk.jpackage.internal.StandardBundlerParam.*;
+import static jdk.jpackage.internal.MacAppBundler.*;
+
+public class MacAppStoreBundler extends MacBaseInstallerBundler {
+
+ private static final ResourceBundle I18N = ResourceBundle.getBundle(
+ "jdk.jpackage.internal.resources.MacResources");
+
+ private static final String TEMPLATE_BUNDLE_ICON_HIDPI =
+ "GenericAppHiDPI.icns";
+ private final static String DEFAULT_ENTITLEMENTS =
+ "MacAppStore.entitlements";
+ private final static String DEFAULT_INHERIT_ENTITLEMENTS =
+ "MacAppStore_Inherit.entitlements";
+
+ public static final BundlerParamInfo<String> MAC_APP_STORE_APP_SIGNING_KEY =
+ new StandardBundlerParam<>(
+ "mac.signing-key-app",
+ String.class,
+ params -> {
+ String result = MacBaseInstallerBundler.findKey(
+ "3rd Party Mac Developer Application: " +
+ SIGNING_KEY_USER.fetchFrom(params),
+ SIGNING_KEYCHAIN.fetchFrom(params),
+ VERBOSE.fetchFrom(params));
+ if (result != null) {
+ MacCertificate certificate = new MacCertificate(result,
+ VERBOSE.fetchFrom(params));
+
+ if (!certificate.isValid()) {
+ Log.error(MessageFormat.format(
+ I18N.getString("error.certificate.expired"),
+ result));
+ }
+ }
+
+ return result;
+ },
+ (s, p) -> s);
+
+ public static final BundlerParamInfo<String> MAC_APP_STORE_PKG_SIGNING_KEY =
+ new StandardBundlerParam<>(
+ "mac.signing-key-pkg",
+ String.class,
+ params -> {
+ String result = MacBaseInstallerBundler.findKey(
+ "3rd Party Mac Developer Installer: " +
+ SIGNING_KEY_USER.fetchFrom(params),
+ SIGNING_KEYCHAIN.fetchFrom(params),
+ VERBOSE.fetchFrom(params));
+
+ if (result != null) {
+ MacCertificate certificate = new MacCertificate(
+ result, VERBOSE.fetchFrom(params));
+
+ if (!certificate.isValid()) {
+ Log.error(MessageFormat.format(
+ I18N.getString("error.certificate.expired"),
+ result));
+ }
+ }
+
+ return result;
+ },
+ (s, p) -> s);
+
+ public static final StandardBundlerParam<File> MAC_APP_STORE_ENTITLEMENTS =
+ new StandardBundlerParam<>(
+ Arguments.CLIOptions.MAC_APP_STORE_ENTITLEMENTS.getId(),
+ File.class,
+ params -> null,
+ (s, p) -> new File(s));
+
+ public static final BundlerParamInfo<String> INSTALLER_SUFFIX =
+ new StandardBundlerParam<> (
+ "mac.app-store.installerName.suffix",
+ String.class,
+ params -> "-MacAppStore",
+ (s, p) -> s);
+
+ public File bundle(Map<String, ? super Object> params,
+ File outdir) throws PackagerException {
+ Log.verbose(MessageFormat.format(I18N.getString(
+ "message.building-bundle"), APP_NAME.fetchFrom(params)));
+
+ IOUtils.writableOutputDir(outdir.toPath());
+
+ // first, load in some overrides
+ // icns needs @2 versions, so load in the @2 default
+ params.put(DEFAULT_ICNS_ICON.getID(), TEMPLATE_BUNDLE_ICON_HIDPI);
+
+ // now we create the app
+ File appImageDir = APP_IMAGE_TEMP_ROOT.fetchFrom(params);
+ try {
+ appImageDir.mkdirs();
+
+ try {
+ MacAppImageBuilder.addNewKeychain(params);
+ } catch (InterruptedException e) {
+ Log.error(e.getMessage());
+ }
+ // first, make sure we don't use the local signing key
+ params.put(DEVELOPER_ID_APP_SIGNING_KEY.getID(), null);
+ File appLocation = prepareAppBundle(params, false);
+
+ prepareEntitlements(params);
+
+ String signingIdentity =
+ MAC_APP_STORE_APP_SIGNING_KEY.fetchFrom(params);
+ String identifierPrefix =
+ BUNDLE_ID_SIGNING_PREFIX.fetchFrom(params);
+ String entitlementsFile =
+ getConfig_Entitlements(params).toString();
+ String inheritEntitlements =
+ getConfig_Inherit_Entitlements(params).toString();
+
+ MacAppImageBuilder.signAppBundle(params, appLocation.toPath(),
+ signingIdentity, identifierPrefix,
+ entitlementsFile, inheritEntitlements);
+ MacAppImageBuilder.restoreKeychainList(params);
+
+ ProcessBuilder pb;
+
+ // create the final pkg file
+ File finalPKG = new File(outdir, INSTALLER_NAME.fetchFrom(params)
+ + INSTALLER_SUFFIX.fetchFrom(params)
+ + ".pkg");
+ outdir.mkdirs();
+
+ String installIdentify =
+ MAC_APP_STORE_PKG_SIGNING_KEY.fetchFrom(params);
+
+ List<String> buildOptions = new ArrayList<>();
+ buildOptions.add("productbuild");
+ buildOptions.add("--component");
+ buildOptions.add(appLocation.toString());
+ buildOptions.add("/Applications");
+ buildOptions.add("--sign");
+ buildOptions.add(installIdentify);
+ buildOptions.add("--product");
+ buildOptions.add(appLocation + "/Contents/Info.plist");
+ String keychainName = SIGNING_KEYCHAIN.fetchFrom(params);
+ if (keychainName != null && !keychainName.isEmpty()) {
+ buildOptions.add("--keychain");
+ buildOptions.add(keychainName);
+ }
+ buildOptions.add(finalPKG.getAbsolutePath());
+
+ pb = new ProcessBuilder(buildOptions);
+
+ IOUtils.exec(pb);
+ return finalPKG;
+ } catch (PackagerException pe) {
+ throw pe;
+ } catch (Exception ex) {
+ Log.verbose(ex);
+ throw new PackagerException(ex);
+ }
+ }
+
+ private File getConfig_Entitlements(Map<String, ? super Object> params) {
+ return new File(CONFIG_ROOT.fetchFrom(params),
+ APP_NAME.fetchFrom(params) + ".entitlements");
+ }
+
+ private File getConfig_Inherit_Entitlements(
+ Map<String, ? super Object> params) {
+ return new File(CONFIG_ROOT.fetchFrom(params),
+ APP_NAME.fetchFrom(params) + "_Inherit.entitlements");
+ }
+
+ private void prepareEntitlements(Map<String, ? super Object> params)
+ throws IOException {
+ File entitlements = MAC_APP_STORE_ENTITLEMENTS.fetchFrom(params);
+ if (entitlements == null || !entitlements.exists()) {
+ fetchResource(getEntitlementsFileName(params),
+ I18N.getString("resource.mac-app-store-entitlements"),
+ DEFAULT_ENTITLEMENTS,
+ getConfig_Entitlements(params),
+ VERBOSE.fetchFrom(params),
+ RESOURCE_DIR.fetchFrom(params));
+ } else {
+ fetchResource(getEntitlementsFileName(params),
+ I18N.getString("resource.mac-app-store-entitlements"),
+ entitlements,
+ getConfig_Entitlements(params),
+ VERBOSE.fetchFrom(params),
+ RESOURCE_DIR.fetchFrom(params));
+ }
+ fetchResource(getInheritEntitlementsFileName(params),
+ I18N.getString("resource.mac-app-store-inherit-entitlements"),
+ DEFAULT_INHERIT_ENTITLEMENTS,
+ getConfig_Inherit_Entitlements(params),
+ VERBOSE.fetchFrom(params),
+ RESOURCE_DIR.fetchFrom(params));
+ }
+
+ private String getEntitlementsFileName(Map<String, ? super Object> params) {
+ return APP_NAME.fetchFrom(params) + ".entitlements";
+ }
+
+ private String getInheritEntitlementsFileName(
+ Map<String, ? super Object> params) {
+ return APP_NAME.fetchFrom(params) + "_Inherit.entitlements";
+ }
+
+
+ ///////////////////////////////////////////////////////////////////////
+ // Implement Bundler
+ ///////////////////////////////////////////////////////////////////////
+
+ @Override
+ public String getName() {
+ return I18N.getString("store.bundler.name");
+ }
+
+ @Override
+ public String getID() {
+ return "mac.appStore";
+ }
+
+ @Override
+ public boolean validate(Map<String, ? super Object> params)
+ throws ConfigException {
+ try {
+ if (params == null) {
+ throw new ConfigException(
+ I18N.getString("error.parameters-null"),
+ I18N.getString("error.parameters-null.advice"));
+ }
+
+ // hdiutil is always available so there's no need to test for
+ // availability.
+ // run basic validation to ensure requirements are met
+
+ // we are not interested in return code, only possible exception
+ validateAppImageAndBundeler(params);
+
+ // reject explicitly set to not sign
+ if (!Optional.ofNullable(MacAppImageBuilder.
+ SIGN_BUNDLE.fetchFrom(params)).orElse(Boolean.TRUE)) {
+ throw new ConfigException(
+ I18N.getString("error.must-sign-app-store"),
+ I18N.getString("error.must-sign-app-store.advice"));
+ }
+
+ // make sure we have settings for signatures
+ if (MAC_APP_STORE_APP_SIGNING_KEY.fetchFrom(params) == null) {
+ throw new ConfigException(
+ I18N.getString("error.no-app-signing-key"),
+ I18N.getString("error.no-app-signing-key.advice"));
+ }
+ if (MAC_APP_STORE_PKG_SIGNING_KEY.fetchFrom(params) == null) {
+ throw new ConfigException(
+ I18N.getString("error.no-pkg-signing-key"),
+ I18N.getString("error.no-pkg-signing-key.advice"));
+ }
+
+ // things we could check...
+ // check the icons, make sure it has hidpi icons
+ // check the category,
+ // make sure it fits in the list apple has provided
+ // validate bundle identifier is reverse dns
+ // check for \a+\.\a+\..
+
+ return true;
+ } catch (RuntimeException re) {
+ if (re.getCause() instanceof ConfigException) {
+ throw (ConfigException) re.getCause();
+ } else {
+ throw new ConfigException(re);
+ }
+ }
+ }
+
+ @Override
+ public File execute(Map<String, ? super Object> params,
+ File outputParentDir) throws PackagerException {
+ return bundle(params, outputParentDir);
+ }
+
+ @Override
+ public boolean supported(boolean runtimeInstaller) {
+ // return (!runtimeInstaller &&
+ // Platform.getPlatform() == Platform.MAC);
+ return false; // mac-app-store not yet supported
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacBaseInstallerBundler.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,198 @@
+/*
+ * Copyright (c) 2014, 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.jpackage.internal;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.nio.file.Files;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.ResourceBundle;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import static jdk.jpackage.internal.StandardBundlerParam.*;
+
+public abstract class MacBaseInstallerBundler extends AbstractBundler {
+
+ private static final ResourceBundle I18N = ResourceBundle.getBundle(
+ "jdk.jpackage.internal.resources.MacResources");
+
+ // This could be generalized more to be for any type of Image Bundler
+ public static final BundlerParamInfo<MacAppBundler> APP_BUNDLER =
+ new StandardBundlerParam<>(
+ "mac.app.bundler",
+ MacAppBundler.class,
+ params -> new MacAppBundler(),
+ (s, p) -> null);
+
+ public final BundlerParamInfo<File> APP_IMAGE_TEMP_ROOT =
+ new StandardBundlerParam<>(
+ "mac.app.imageRoot",
+ File.class,
+ params -> {
+ File imageDir = IMAGES_ROOT.fetchFrom(params);
+ if (!imageDir.exists()) imageDir.mkdirs();
+ try {
+ return Files.createTempDirectory(
+ imageDir.toPath(), "image-").toFile();
+ } catch (IOException e) {
+ return new File(imageDir, getID()+ ".image");
+ }
+ },
+ (s, p) -> new File(s));
+
+ public static final BundlerParamInfo<String> SIGNING_KEY_USER =
+ new StandardBundlerParam<>(
+ Arguments.CLIOptions.MAC_SIGNING_KEY_NAME.getId(),
+ String.class,
+ params -> "",
+ null);
+
+ public static final BundlerParamInfo<String> SIGNING_KEYCHAIN =
+ new StandardBundlerParam<>(
+ Arguments.CLIOptions.MAC_SIGNING_KEYCHAIN.getId(),
+ String.class,
+ params -> "",
+ null);
+
+ public static final BundlerParamInfo<String> INSTALLER_NAME =
+ new StandardBundlerParam<> (
+ "mac.installerName",
+ String.class,
+ params -> {
+ String nm = APP_NAME.fetchFrom(params);
+ if (nm == null) return null;
+
+ String version = VERSION.fetchFrom(params);
+ if (version == null) {
+ return nm;
+ } else {
+ return nm + "-" + version;
+ }
+ },
+ (s, p) -> s);
+
+ protected void validateAppImageAndBundeler(
+ Map<String, ? super Object> params) throws ConfigException {
+ if (PREDEFINED_APP_IMAGE.fetchFrom(params) != null) {
+ File applicationImage = PREDEFINED_APP_IMAGE.fetchFrom(params);
+ if (!applicationImage.exists()) {
+ throw new ConfigException(
+ MessageFormat.format(I18N.getString(
+ "message.app-image-dir-does-not-exist"),
+ PREDEFINED_APP_IMAGE.getID(),
+ applicationImage.toString()),
+ MessageFormat.format(I18N.getString(
+ "message.app-image-dir-does-not-exist.advice"),
+ PREDEFINED_APP_IMAGE.getID()));
+ }
+ if (APP_NAME.fetchFrom(params) == null) {
+ throw new ConfigException(
+ I18N.getString("message.app-image-requires-app-name"),
+ I18N.getString(
+ "message.app-image-requires-app-name.advice"));
+ }
+ if (IDENTIFIER.fetchFrom(params) == null) {
+ throw new ConfigException(
+ I18N.getString("message.app-image-requires-identifier"),
+ I18N.getString(
+ "message.app-image-requires-identifier.advice"));
+ }
+ } else {
+ APP_BUNDLER.fetchFrom(params).validate(params);
+ }
+ }
+
+ protected File prepareAppBundle(Map<String, ? super Object> params,
+ boolean pkg) throws PackagerException {
+ File predefinedImage =
+ StandardBundlerParam.getPredefinedAppImage(params);
+ if (predefinedImage != null) {
+ return predefinedImage;
+ }
+ File appImageRoot = APP_IMAGE_TEMP_ROOT.fetchFrom(params);
+ if (pkg) {
+ // create pkg in dmg
+ return new MacPkgBundler().bundle(params, appImageRoot);
+ } else {
+ return APP_BUNDLER.fetchFrom(params).doBundle(
+ params, appImageRoot, true);
+ }
+ }
+
+ @Override
+ public String getBundleType() {
+ return "INSTALLER";
+ }
+
+ public static String findKey(String key, String keychainName,
+ boolean verbose) {
+ if (Platform.getPlatform() != Platform.MAC) {
+ return null;
+ }
+
+ try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ PrintStream ps = new PrintStream(baos)) {
+ List<String> searchOptions = new ArrayList<>();
+ searchOptions.add("security");
+ searchOptions.add("find-certificate");
+ searchOptions.add("-c");
+ searchOptions.add(key);
+ searchOptions.add("-a");
+ if (keychainName != null && !keychainName.isEmpty()) {
+ searchOptions.add(keychainName);
+ }
+
+ ProcessBuilder pb = new ProcessBuilder(searchOptions);
+
+ IOUtils.exec(pb, false, ps);
+ Pattern p = Pattern.compile("\"alis\"<blob>=\"([^\"]+)\"");
+ Matcher m = p.matcher(baos.toString());
+ if (!m.find()) {
+ Log.error("Did not find a key matching '" + key + "'");
+ return null;
+ }
+ String matchedKey = m.group(1);
+ if (m.find()) {
+ Log.error("Found more than one key matching '" + key + "'");
+ return null;
+ }
+ Log.debug("Using key '" + matchedKey + "'");
+ return matchedKey;
+ } catch (IOException ioe) {
+ Log.verbose(ioe);
+ return null;
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacCertificate.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,156 @@
+/*
+ * 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.jpackage.internal;
+
+import java.io.BufferedOutputStream;
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.PrintStream;
+import java.nio.file.StandardCopyOption;
+import java.nio.file.Files;
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.List;
+import java.util.Locale;
+
+final class MacCertificate {
+ private final String certificate;
+ private final boolean verbose;
+
+ MacCertificate(String certificate) {
+ this.certificate = certificate;
+ this.verbose = false;
+ }
+
+ MacCertificate(String certificate, boolean verbose) {
+ this.certificate = certificate;
+ this.verbose = verbose;
+ }
+
+ boolean isValid() {
+ return verifyCertificate(this.certificate, verbose);
+ }
+
+ private static File findCertificate(String certificate, boolean verbose) {
+ File result = null;
+
+ List<String> args = new ArrayList<>();
+ args.add("security");
+ args.add("find-certificate");
+ args.add("-c");
+ args.add(certificate);
+ args.add("-a");
+ args.add("-p");
+
+ try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ PrintStream ps = new PrintStream(baos)) {
+ ProcessBuilder security = new ProcessBuilder(args);
+ IOUtils.exec(security, false, ps);
+
+ File output = File.createTempFile("tempfile", ".tmp");
+
+ Files.copy(new ByteArrayInputStream(baos.toByteArray()),
+ output.toPath(), StandardCopyOption.REPLACE_EXISTING);
+
+ result = output;
+ }
+ catch (IOException ignored) {}
+
+ return result;
+ }
+
+ private static Date findCertificateDate(String filename, boolean verbose) {
+ Date result = null;
+
+ List<String> args = new ArrayList<>();
+ args.add("/usr/bin/openssl");
+ args.add("x509");
+ args.add("-noout");
+ args.add("-enddate");
+ args.add("-in");
+ args.add(filename);
+
+ try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ PrintStream ps = new PrintStream(baos)) {
+ ProcessBuilder security = new ProcessBuilder(args);
+ IOUtils.exec(security, false, ps);
+ String output = baos.toString();
+ output = output.substring(output.indexOf("=") + 1);
+ DateFormat df = new SimpleDateFormat(
+ "MMM dd kk:mm:ss yyyy z", Locale.ENGLISH);
+ result = df.parse(output);
+ } catch (IOException | ParseException ex) {
+ Log.debug(ex);
+ }
+
+ return result;
+ }
+
+ private static boolean verifyCertificate(
+ String certificate, boolean verbose) {
+ boolean result = false;
+
+ try {
+ File file = null;
+ Date certificateDate = null;
+
+ try {
+ file = findCertificate(certificate, verbose);
+
+ if (file != null) {
+ certificateDate = findCertificateDate(
+ file.getCanonicalPath(), verbose);
+ }
+ }
+ finally {
+ if (file != null) {
+ file.delete();
+ }
+ }
+
+ if (certificateDate != null) {
+ Calendar c = Calendar.getInstance();
+ Date today = c.getTime();
+
+ if (certificateDate.after(today)) {
+ result = true;
+ }
+ }
+ }
+ catch (IOException ignored) {}
+
+ return result;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacDmgBundler.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,467 @@
+/*
+ * 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.jpackage.internal;
+
+import java.io.*;
+import java.nio.file.Files;
+import java.text.MessageFormat;
+import java.util.*;
+
+import static jdk.jpackage.internal.StandardBundlerParam.*;
+
+public class MacDmgBundler extends MacBaseInstallerBundler {
+
+ private static final ResourceBundle I18N = ResourceBundle.getBundle(
+ "jdk.jpackage.internal.resources.MacResources");
+
+ static final String DEFAULT_BACKGROUND_IMAGE="background_dmg.png";
+ static final String DEFAULT_DMG_SETUP_SCRIPT="DMGsetup.scpt";
+ static final String TEMPLATE_BUNDLE_ICON = "GenericApp.icns";
+
+ static final String DEFAULT_LICENSE_PLIST="lic_template.plist";
+
+ public static final BundlerParamInfo<String> INSTALLER_SUFFIX =
+ new StandardBundlerParam<> (
+ "mac.dmg.installerName.suffix",
+ String.class,
+ params -> "",
+ (s, p) -> s);
+
+ public File bundle(Map<String, ? super Object> params,
+ File outdir) throws PackagerException {
+ Log.verbose(MessageFormat.format(I18N.getString("message.building-dmg"),
+ APP_NAME.fetchFrom(params)));
+
+ IOUtils.writableOutputDir(outdir.toPath());
+
+ File appImageDir = APP_IMAGE_TEMP_ROOT.fetchFrom(params);
+ try {
+ appImageDir.mkdirs();
+
+ if (prepareAppBundle(params, true) != null &&
+ prepareConfigFiles(params)) {
+ File configScript = getConfig_Script(params);
+ if (configScript.exists()) {
+ Log.verbose(MessageFormat.format(
+ I18N.getString("message.running-script"),
+ configScript.getAbsolutePath()));
+ IOUtils.run("bash", configScript);
+ }
+
+ return buildDMG(params, outdir);
+ }
+ return null;
+ } catch (IOException ex) {
+ Log.verbose(ex);
+ throw new PackagerException(ex);
+ }
+ }
+
+ private static final String hdiutil = "/usr/bin/hdiutil";
+
+ private void prepareDMGSetupScript(String volumeName,
+ Map<String, ? super Object> params) throws IOException {
+ File dmgSetup = getConfig_VolumeScript(params);
+ Log.verbose(MessageFormat.format(
+ I18N.getString("message.preparing-dmg-setup"),
+ dmgSetup.getAbsolutePath()));
+
+ //prepare config for exe
+ Map<String, String> data = new HashMap<>();
+ data.put("DEPLOY_ACTUAL_VOLUME_NAME", volumeName);
+ data.put("DEPLOY_APPLICATION_NAME", APP_NAME.fetchFrom(params));
+
+ data.put("DEPLOY_INSTALL_LOCATION", "(path to desktop folder)");
+ data.put("DEPLOY_INSTALL_NAME", "Desktop");
+
+ try (Writer w = Files.newBufferedWriter(dmgSetup.toPath())) {
+ w.write(preprocessTextResource(dmgSetup.getName(),
+ I18N.getString("resource.dmg-setup-script"),
+ DEFAULT_DMG_SETUP_SCRIPT, data, VERBOSE.fetchFrom(params),
+ RESOURCE_DIR.fetchFrom(params)));
+ }
+ }
+
+ private File getConfig_VolumeScript(Map<String, ? super Object> params) {
+ return new File(CONFIG_ROOT.fetchFrom(params),
+ APP_NAME.fetchFrom(params) + "-dmg-setup.scpt");
+ }
+
+ private File getConfig_VolumeBackground(
+ Map<String, ? super Object> params) {
+ return new File(CONFIG_ROOT.fetchFrom(params),
+ APP_NAME.fetchFrom(params) + "-background.png");
+ }
+
+ private File getConfig_VolumeIcon(Map<String, ? super Object> params) {
+ return new File(CONFIG_ROOT.fetchFrom(params),
+ APP_NAME.fetchFrom(params) + "-volume.icns");
+ }
+
+ private File getConfig_LicenseFile(Map<String, ? super Object> params) {
+ return new File(CONFIG_ROOT.fetchFrom(params),
+ APP_NAME.fetchFrom(params) + "-license.plist");
+ }
+
+ private void prepareLicense(Map<String, ? super Object> params) {
+ try {
+ String licFileStr = LICENSE_FILE.fetchFrom(params);
+ if (licFileStr == null) {
+ return;
+ }
+
+ File licFile = new File(licFileStr);
+ byte[] licenseContentOriginal = Files.readAllBytes(licFile.toPath());
+ String licenseInBase64 =
+ Base64.getEncoder().encodeToString(licenseContentOriginal);
+
+ Map<String, String> data = new HashMap<>();
+ data.put("APPLICATION_LICENSE_TEXT", licenseInBase64);
+
+ try (Writer w = Files.newBufferedWriter(
+ getConfig_LicenseFile(params).toPath())) {
+ w.write(preprocessTextResource(
+ getConfig_LicenseFile(params).getName(),
+ I18N.getString("resource.license-setup"),
+ DEFAULT_LICENSE_PLIST, data, VERBOSE.fetchFrom(params),
+ RESOURCE_DIR.fetchFrom(params)));
+ }
+
+ } catch (IOException ex) {
+ Log.verbose(ex);
+ }
+ }
+
+ private boolean prepareConfigFiles(Map<String, ? super Object> params)
+ throws IOException {
+ File bgTarget = getConfig_VolumeBackground(params);
+ fetchResource(bgTarget.getName(),
+ I18N.getString("resource.dmg-background"),
+ DEFAULT_BACKGROUND_IMAGE,
+ bgTarget,
+ VERBOSE.fetchFrom(params),
+ RESOURCE_DIR.fetchFrom(params));
+
+ File iconTarget = getConfig_VolumeIcon(params);
+ if (MacAppBundler.ICON_ICNS.fetchFrom(params) == null ||
+ !MacAppBundler.ICON_ICNS.fetchFrom(params).exists()) {
+ fetchResource(iconTarget.getName(),
+ I18N.getString("resource.volume-icon"),
+ TEMPLATE_BUNDLE_ICON,
+ iconTarget,
+ VERBOSE.fetchFrom(params),
+ RESOURCE_DIR.fetchFrom(params));
+ } else {
+ fetchResource(iconTarget.getName(),
+ I18N.getString("resource.volume-icon"),
+ MacAppBundler.ICON_ICNS.fetchFrom(params),
+ iconTarget,
+ VERBOSE.fetchFrom(params),
+ RESOURCE_DIR.fetchFrom(params));
+ }
+
+
+ fetchResource(getConfig_Script(params).getName(),
+ I18N.getString("resource.post-install-script"),
+ (String) null,
+ getConfig_Script(params),
+ VERBOSE.fetchFrom(params),
+ RESOURCE_DIR.fetchFrom(params));
+
+ prepareLicense(params);
+
+ // In theory we need to extract name from results of attach command
+ // However, this will be a problem for customization as name will
+ // possibly change every time and developer will not be able to fix it
+ // As we are using tmp dir chance we get "different" name are low =>
+ // Use fixed name we used for bundle
+ prepareDMGSetupScript(APP_NAME.fetchFrom(params), params);
+
+ return true;
+ }
+
+ // name of post-image script
+ private File getConfig_Script(Map<String, ? super Object> params) {
+ return new File(CONFIG_ROOT.fetchFrom(params),
+ APP_NAME.fetchFrom(params) + "-post-image.sh");
+ }
+
+ // Location of SetFile utility may be different depending on MacOS version
+ // We look for several known places and if none of them work will
+ // try ot find it
+ private String findSetFileUtility() {
+ String typicalPaths[] = {"/Developer/Tools/SetFile",
+ "/usr/bin/SetFile", "/Developer/usr/bin/SetFile"};
+
+ for (String path: typicalPaths) {
+ File f = new File(path);
+ if (f.exists() && f.canExecute()) {
+ return path;
+ }
+ }
+
+ // generic find attempt
+ try {
+ ProcessBuilder pb = new ProcessBuilder("xcrun", "-find", "SetFile");
+ Process p = pb.start();
+ InputStreamReader isr = new InputStreamReader(p.getInputStream());
+ BufferedReader br = new BufferedReader(isr);
+ String lineRead = br.readLine();
+ if (lineRead != null) {
+ File f = new File(lineRead);
+ if (f.exists() && f.canExecute()) {
+ return f.getAbsolutePath();
+ }
+ }
+ } catch (IOException ignored) {}
+
+ return null;
+ }
+
+ private File buildDMG(
+ Map<String, ? super Object> params, File outdir)
+ throws IOException {
+ File imagesRoot = IMAGES_ROOT.fetchFrom(params);
+ if (!imagesRoot.exists()) imagesRoot.mkdirs();
+
+ File protoDMG = new File(imagesRoot,
+ APP_NAME.fetchFrom(params) +"-tmp.dmg");
+ File finalDMG = new File(outdir, INSTALLER_NAME.fetchFrom(params)
+ + INSTALLER_SUFFIX.fetchFrom(params) + ".dmg");
+
+ File srcFolder = APP_IMAGE_TEMP_ROOT.fetchFrom(params);
+ File predefinedImage =
+ StandardBundlerParam.getPredefinedAppImage(params);
+ if (predefinedImage != null) {
+ srcFolder = predefinedImage;
+ }
+
+ Log.verbose(MessageFormat.format(I18N.getString(
+ "message.creating-dmg-file"), finalDMG.getAbsolutePath()));
+
+ protoDMG.delete();
+ if (finalDMG.exists() && !finalDMG.delete()) {
+ throw new IOException(MessageFormat.format(I18N.getString(
+ "message.dmg-cannot-be-overwritten"),
+ finalDMG.getAbsolutePath()));
+ }
+
+ protoDMG.getParentFile().mkdirs();
+ finalDMG.getParentFile().mkdirs();
+
+ String hdiUtilVerbosityFlag = Log.isDebug() ? "-verbose" : "-quiet";
+
+ // create temp image
+ ProcessBuilder pb = new ProcessBuilder(
+ hdiutil,
+ "create",
+ hdiUtilVerbosityFlag,
+ "-srcfolder", srcFolder.getAbsolutePath(),
+ "-volname", APP_NAME.fetchFrom(params),
+ "-ov", protoDMG.getAbsolutePath(),
+ "-fs", "HFS+",
+ "-format", "UDRW");
+ IOUtils.exec(pb);
+
+ // mount temp image
+ pb = new ProcessBuilder(
+ hdiutil,
+ "attach",
+ protoDMG.getAbsolutePath(),
+ hdiUtilVerbosityFlag,
+ "-mountroot", imagesRoot.getAbsolutePath());
+ IOUtils.exec(pb);
+
+ File mountedRoot = new File(imagesRoot.getAbsolutePath(),
+ APP_NAME.fetchFrom(params));
+
+ // volume icon
+ File volumeIconFile = new File(mountedRoot, ".VolumeIcon.icns");
+ IOUtils.copyFile(getConfig_VolumeIcon(params),
+ volumeIconFile);
+
+ pb = new ProcessBuilder("osascript",
+ getConfig_VolumeScript(params).getAbsolutePath());
+ IOUtils.exec(pb);
+
+ // Indicate that we want a custom icon
+ // NB: attributes of the root directory are ignored
+ // when creating the volume
+ // Therefore we have to do this after we mount image
+ String setFileUtility = findSetFileUtility();
+ if (setFileUtility != null) {
+ //can not find utility => keep going without icon
+ try {
+ volumeIconFile.setWritable(true);
+ // The "creator" attribute on a file is a legacy attribute
+ // but it seems Finder excepts these bytes to be
+ // "icnC" for the volume icon
+ // http://endrift.com/blog/2010/06/14/dmg-files-volume-icons-cli
+ // (might not work on Mac 10.13 with old XCode)
+ pb = new ProcessBuilder(
+ setFileUtility,
+ "-c", "icnC",
+ volumeIconFile.getAbsolutePath());
+ IOUtils.exec(pb);
+ volumeIconFile.setReadOnly();
+
+ pb = new ProcessBuilder(
+ setFileUtility,
+ "-a", "C",
+ mountedRoot.getAbsolutePath());
+ IOUtils.exec(pb);
+ } catch (IOException ex) {
+ Log.error(ex.getMessage());
+ Log.verbose("Cannot enable custom icon using SetFile utility");
+ }
+ } else {
+ Log.verbose(
+ "Skip enabling custom icon as SetFile utility is not found");
+ }
+
+ // Detach the temporary image
+ pb = new ProcessBuilder(
+ hdiutil,
+ "detach",
+ "-force",
+ hdiUtilVerbosityFlag,
+ mountedRoot.getAbsolutePath());
+ IOUtils.exec(pb);
+
+ // Compress it to a new image
+ pb = new ProcessBuilder(
+ hdiutil,
+ "convert",
+ protoDMG.getAbsolutePath(),
+ hdiUtilVerbosityFlag,
+ "-format", "UDZO",
+ "-o", finalDMG.getAbsolutePath());
+ IOUtils.exec(pb);
+
+ //add license if needed
+ if (getConfig_LicenseFile(params).exists()) {
+ //hdiutil unflatten your_image_file.dmg
+ pb = new ProcessBuilder(
+ hdiutil,
+ "unflatten",
+ finalDMG.getAbsolutePath()
+ );
+ IOUtils.exec(pb);
+
+ //add license
+ pb = new ProcessBuilder(
+ hdiutil,
+ "udifrez",
+ finalDMG.getAbsolutePath(),
+ "-xml",
+ getConfig_LicenseFile(params).getAbsolutePath()
+ );
+ IOUtils.exec(pb);
+
+ //hdiutil flatten your_image_file.dmg
+ pb = new ProcessBuilder(
+ hdiutil,
+ "flatten",
+ finalDMG.getAbsolutePath()
+ );
+ IOUtils.exec(pb);
+
+ }
+
+ //Delete the temporary image
+ protoDMG.delete();
+
+ Log.verbose(MessageFormat.format(I18N.getString(
+ "message.output-to-location"),
+ APP_NAME.fetchFrom(params), finalDMG.getAbsolutePath()));
+
+ return finalDMG;
+ }
+
+
+ //////////////////////////////////////////////////////////////////////////
+ // Implement Bundler
+ //////////////////////////////////////////////////////////////////////////
+
+ @Override
+ public String getName() {
+ return I18N.getString("dmg.bundler.name");
+ }
+
+ @Override
+ public String getID() {
+ return "dmg";
+ }
+
+ @Override
+ public boolean validate(Map<String, ? super Object> params)
+ throws ConfigException {
+ try {
+ if (params == null) throw new ConfigException(
+ I18N.getString("error.parameters-null"),
+ I18N.getString("error.parameters-null.advice"));
+
+ //run basic validation to ensure requirements are met
+ //we are not interested in return code, only possible exception
+ validateAppImageAndBundeler(params);
+
+ return true;
+ } catch (RuntimeException re) {
+ if (re.getCause() instanceof ConfigException) {
+ throw (ConfigException) re.getCause();
+ } else {
+ throw new ConfigException(re);
+ }
+ }
+ }
+
+ @Override
+ public File execute(Map<String, ? super Object> params,
+ File outputParentDir) throws PackagerException {
+ return bundle(params, outputParentDir);
+ }
+
+ @Override
+ public boolean supported(boolean runtimeInstaller) {
+ return isSupported();
+ }
+
+ public final static String[] required =
+ {"/usr/bin/hdiutil", "/usr/bin/osascript"};
+ public static boolean isSupported() {
+ try {
+ for (String s : required) {
+ File f = new File(s);
+ if (!f.exists() || !f.canExecute()) {
+ return false;
+ }
+ }
+ return true;
+ } catch (Exception e) {
+ return false;
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacPkgBundler.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,544 @@
+/*
+ * Copyright (c) 2014, 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.jpackage.internal;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.io.PrintWriter;
+import java.io.Writer;
+import java.net.URLEncoder;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.ResourceBundle;
+
+import static jdk.jpackage.internal.StandardBundlerParam.*;
+import static jdk.jpackage.internal.MacBaseInstallerBundler.SIGNING_KEYCHAIN;
+import static jdk.jpackage.internal.MacBaseInstallerBundler.SIGNING_KEY_USER;
+
+public class MacPkgBundler extends MacBaseInstallerBundler {
+
+ private static final ResourceBundle I18N = ResourceBundle.getBundle(
+ "jdk.jpackage.internal.resources.MacResources");
+
+ private static final String DEFAULT_BACKGROUND_IMAGE = "background_pkg.png";
+
+ private static final String TEMPLATE_PREINSTALL_SCRIPT =
+ "preinstall.template";
+ private static final String TEMPLATE_POSTINSTALL_SCRIPT =
+ "postinstall.template";
+
+ private static final BundlerParamInfo<File> PACKAGES_ROOT =
+ new StandardBundlerParam<>(
+ "mac.pkg.packagesRoot",
+ File.class,
+ params -> {
+ File packagesRoot =
+ new File(TEMP_ROOT.fetchFrom(params), "packages");
+ packagesRoot.mkdirs();
+ return packagesRoot;
+ },
+ (s, p) -> new File(s));
+
+
+ protected final BundlerParamInfo<File> SCRIPTS_DIR =
+ new StandardBundlerParam<>(
+ "mac.pkg.scriptsDir",
+ File.class,
+ params -> {
+ File scriptsDir =
+ new File(CONFIG_ROOT.fetchFrom(params), "scripts");
+ scriptsDir.mkdirs();
+ return scriptsDir;
+ },
+ (s, p) -> new File(s));
+
+ public static final
+ BundlerParamInfo<String> DEVELOPER_ID_INSTALLER_SIGNING_KEY =
+ new StandardBundlerParam<>(
+ "mac.signing-key-developer-id-installer",
+ String.class,
+ params -> {
+ String result = MacBaseInstallerBundler.findKey(
+ "Developer ID Installer: "
+ + SIGNING_KEY_USER.fetchFrom(params),
+ SIGNING_KEYCHAIN.fetchFrom(params),
+ VERBOSE.fetchFrom(params));
+ if (result != null) {
+ MacCertificate certificate = new MacCertificate(
+ result, VERBOSE.fetchFrom(params));
+
+ if (!certificate.isValid()) {
+ Log.error(MessageFormat.format(
+ I18N.getString("error.certificate.expired"),
+ result));
+ }
+ }
+
+ return result;
+ },
+ (s, p) -> s);
+
+ public static final BundlerParamInfo<String> MAC_INSTALL_DIR =
+ new StandardBundlerParam<>(
+ "mac-install-dir",
+ String.class,
+ params -> {
+ String dir = INSTALL_DIR.fetchFrom(params);
+ return (dir != null) ? dir : "/Applications";
+ },
+ (s, p) -> s
+ );
+
+ public static final BundlerParamInfo<String> INSTALLER_SUFFIX =
+ new StandardBundlerParam<> (
+ "mac.pkg.installerName.suffix",
+ String.class,
+ params -> "",
+ (s, p) -> s);
+
+ public File bundle(Map<String, ? super Object> params,
+ File outdir) throws PackagerException {
+ Log.verbose(MessageFormat.format(I18N.getString("message.building-pkg"),
+ APP_NAME.fetchFrom(params)));
+
+ IOUtils.writableOutputDir(outdir.toPath());
+
+ File appImageDir = null;
+ try {
+ appImageDir = prepareAppBundle(params, false);
+
+ if (appImageDir != null && prepareConfigFiles(params)) {
+
+ File configScript = getConfig_Script(params);
+ if (configScript.exists()) {
+ Log.verbose(MessageFormat.format(I18N.getString(
+ "message.running-script"),
+ configScript.getAbsolutePath()));
+ IOUtils.run("bash", configScript);
+ }
+
+ return createPKG(params, outdir, appImageDir);
+ }
+ return null;
+ } catch (IOException ex) {
+ Log.verbose(ex);
+ throw new PackagerException(ex);
+ }
+ }
+
+ private File getPackages_AppPackage(Map<String, ? super Object> params) {
+ return new File(PACKAGES_ROOT.fetchFrom(params),
+ APP_NAME.fetchFrom(params) + "-app.pkg");
+ }
+
+ private File getPackages_DaemonPackage(Map<String, ? super Object> params) {
+ return new File(PACKAGES_ROOT.fetchFrom(params),
+ APP_NAME.fetchFrom(params) + "-daemon.pkg");
+ }
+
+ private File getConfig_DistributionXMLFile(
+ Map<String, ? super Object> params) {
+ return new File(CONFIG_ROOT.fetchFrom(params), "distribution.dist");
+ }
+
+ private File getConfig_BackgroundImage(Map<String, ? super Object> params) {
+ return new File(CONFIG_ROOT.fetchFrom(params),
+ APP_NAME.fetchFrom(params) + "-background.png");
+ }
+
+ private File getScripts_PreinstallFile(Map<String, ? super Object> params) {
+ return new File(SCRIPTS_DIR.fetchFrom(params), "preinstall");
+ }
+
+ private File getScripts_PostinstallFile(
+ Map<String, ? super Object> params) {
+ return new File(SCRIPTS_DIR.fetchFrom(params), "postinstall");
+ }
+
+ private String getAppIdentifier(Map<String, ? super Object> params) {
+ return IDENTIFIER.fetchFrom(params);
+ }
+
+ private String getDaemonIdentifier(Map<String, ? super Object> params) {
+ return IDENTIFIER.fetchFrom(params) + ".daemon";
+ }
+
+ private void preparePackageScripts(Map<String, ? super Object> params)
+ throws IOException {
+ Log.verbose(I18N.getString("message.preparing-scripts"));
+
+ Map<String, String> data = new HashMap<>();
+
+ data.put("INSTALL_LOCATION", MAC_INSTALL_DIR.fetchFrom(params));
+
+ try (Writer w = Files.newBufferedWriter(
+ getScripts_PreinstallFile(params).toPath())) {
+ String content = preprocessTextResource(
+ getScripts_PreinstallFile(params).getName(),
+ I18N.getString("resource.pkg-preinstall-script"),
+ TEMPLATE_PREINSTALL_SCRIPT,
+ data,
+ VERBOSE.fetchFrom(params),
+ RESOURCE_DIR.fetchFrom(params));
+ w.write(content);
+ }
+ getScripts_PreinstallFile(params).setExecutable(true, false);
+
+ try (Writer w = Files.newBufferedWriter(
+ getScripts_PostinstallFile(params).toPath())) {
+ String content = preprocessTextResource(
+ getScripts_PostinstallFile(params).getName(),
+ I18N.getString("resource.pkg-postinstall-script"),
+ TEMPLATE_POSTINSTALL_SCRIPT,
+ data,
+ VERBOSE.fetchFrom(params),
+ RESOURCE_DIR.fetchFrom(params));
+ w.write(content);
+ }
+ getScripts_PostinstallFile(params).setExecutable(true, false);
+ }
+
+ private void prepareDistributionXMLFile(Map<String, ? super Object> params)
+ throws IOException {
+ File f = getConfig_DistributionXMLFile(params);
+
+ Log.verbose(MessageFormat.format(I18N.getString(
+ "message.preparing-distribution-dist"), f.getAbsolutePath()));
+
+ try (PrintStream out = new PrintStream(f)) {
+
+ out.println(
+ "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"no\"?>");
+ out.println("<installer-gui-script minSpecVersion=\"1\">");
+
+ out.println("<title>" + APP_NAME.fetchFrom(params) + "</title>");
+ out.println("<background" + " file=\""
+ + getConfig_BackgroundImage(params).getName()
+ + "\""
+ + " mime-type=\"image/png\""
+ + " alignment=\"bottomleft\" "
+ + " scaling=\"none\""
+ + "/>");
+
+ String licFileStr = LICENSE_FILE.fetchFrom(params);
+ if (licFileStr != null) {
+ File licFile = new File(licFileStr);
+ out.println("<license"
+ + " file=\"" + licFile.getAbsolutePath() + "\""
+ + " mime-type=\"text/rtf\""
+ + "/>");
+ }
+
+ /*
+ * Note that the content of the distribution file
+ * below is generated by productbuild --synthesize
+ */
+
+ String appId = getAppIdentifier(params);
+
+ out.println("<pkg-ref id=\"" + appId + "\"/>");
+ out.println(
+ "<options customize=\"never\" require-scripts=\"false\"/>");
+ out.println("<choices-outline>");
+ out.println(" <line choice=\"default\">");
+ out.println(" <line choice=\"" + appId + "\"/>");
+ out.println(" </line>");
+ out.println("</choices-outline>");
+ out.println("<choice id=\"default\"/>");
+ out.println("<choice id=\"" + appId + "\" visible=\"false\">");
+ out.println(" <pkg-ref id=\"" + appId + "\"/>");
+ out.println("</choice>");
+ out.println("<pkg-ref id=\"" + appId + "\" version=\""
+ + VERSION.fetchFrom(params) + "\" onConclusion=\"none\">"
+ + URLEncoder.encode(
+ getPackages_AppPackage(params).getName(),
+ "UTF-8") + "</pkg-ref>");
+
+ out.println("</installer-gui-script>");
+
+ }
+ }
+
+ private boolean prepareConfigFiles(Map<String, ? super Object> params)
+ throws IOException {
+ File imageTarget = getConfig_BackgroundImage(params);
+ fetchResource(imageTarget.getName(),
+ I18N.getString("resource.pkg-background-image"),
+ DEFAULT_BACKGROUND_IMAGE,
+ imageTarget,
+ VERBOSE.fetchFrom(params),
+ RESOURCE_DIR.fetchFrom(params));
+
+ prepareDistributionXMLFile(params);
+
+ fetchResource(getConfig_Script(params).getName(),
+ I18N.getString("resource.post-install-script"),
+ (String) null,
+ getConfig_Script(params),
+ VERBOSE.fetchFrom(params),
+ RESOURCE_DIR.fetchFrom(params));
+
+ return true;
+ }
+
+ // name of post-image script
+ private File getConfig_Script(Map<String, ? super Object> params) {
+ return new File(CONFIG_ROOT.fetchFrom(params),
+ APP_NAME.fetchFrom(params) + "-post-image.sh");
+ }
+
+ private void patchCPLFile(File cpl) throws IOException {
+ String cplData = Files.readString(cpl.toPath());
+ String[] lines = cplData.split("\n");
+ try (PrintWriter out = new PrintWriter(Files.newBufferedWriter(
+ cpl.toPath()))) {
+ boolean skip = false; // Used to skip Java.runtime bundle, since
+ // pkgbuild with --root will find two bundles app and Java runtime.
+ // We cannot generate component proprty list when using
+ // --component argument.
+ for (int i = 0; i < lines.length; i++) {
+ if (lines[i].trim().equals("<key>BundleIsRelocatable</key>")) {
+ out.println(lines[i]);
+ out.println("<false/>");
+ i++;
+ } else if (lines[i].trim().equals("<key>ChildBundles</key>")) {
+ skip = true;
+ } else if (skip && lines[i].trim().equals("</array>")) {
+ skip = false;
+ } else {
+ if (!skip) {
+ out.println(lines[i]);
+ }
+ }
+ }
+ }
+ }
+
+ // pkgbuild includes all components from "--root" and subfolders,
+ // so if we have app image in folder which contains other images, then they
+ // will be included as well. It does have "--filter" option which use regex
+ // to exclude files/folder, but it will overwrite default one which excludes
+ // based on doc "any .svn or CVS directories, and any .DS_Store files".
+ // So easy aproach will be to copy user provided app-image into temp folder
+ // if root path contains other files.
+ private String getRoot(Map<String, ? super Object> params,
+ File appLocation) throws IOException {
+ String root = appLocation.getParent() == null ?
+ "." : appLocation.getParent();
+ File rootDir = new File(root);
+ File[] list = rootDir.listFiles();
+ if (list != null) { // Should not happend
+ // We should only have app image and/or .DS_Store
+ if (list.length == 1) {
+ return root;
+ } else if (list.length == 2) {
+ // Check case with app image and .DS_Store
+ if (list[0].toString().toLowerCase().endsWith(".ds_store") ||
+ list[1].toString().toLowerCase().endsWith(".ds_store")) {
+ return root; // Only app image and .DS_Store
+ }
+ }
+ }
+
+ // Copy to new root
+ Path newRoot = Files.createTempDirectory(
+ TEMP_ROOT.fetchFrom(params).toPath(),
+ "root-");
+
+ IOUtils.copyRecursive(appLocation.toPath(),
+ newRoot.resolve(appLocation.getName()));
+
+ return newRoot.toString();
+ }
+
+ private File createPKG(Map<String, ? super Object> params,
+ File outdir, File appLocation) {
+ // generic find attempt
+ try {
+ File appPKG = getPackages_AppPackage(params);
+
+ String root = getRoot(params, appLocation);
+
+ // Generate default CPL file
+ File cpl = new File(CONFIG_ROOT.fetchFrom(params).getAbsolutePath()
+ + File.separator + "cpl.plist");
+ ProcessBuilder pb = new ProcessBuilder("pkgbuild",
+ "--root",
+ root,
+ "--install-location",
+ MAC_INSTALL_DIR.fetchFrom(params),
+ "--analyze",
+ cpl.getAbsolutePath());
+
+ IOUtils.exec(pb);
+
+ patchCPLFile(cpl);
+
+ preparePackageScripts(params);
+
+ // build application package
+ pb = new ProcessBuilder("pkgbuild",
+ "--root",
+ root,
+ "--install-location",
+ MAC_INSTALL_DIR.fetchFrom(params),
+ "--component-plist",
+ cpl.getAbsolutePath(),
+ "--scripts",
+ SCRIPTS_DIR.fetchFrom(params).getAbsolutePath(),
+ appPKG.getAbsolutePath());
+ IOUtils.exec(pb);
+
+ // build final package
+ File finalPKG = new File(outdir, INSTALLER_NAME.fetchFrom(params)
+ + INSTALLER_SUFFIX.fetchFrom(params)
+ + ".pkg");
+ outdir.mkdirs();
+
+ List<String> commandLine = new ArrayList<>();
+ commandLine.add("productbuild");
+
+ commandLine.add("--resources");
+ commandLine.add(CONFIG_ROOT.fetchFrom(params).getAbsolutePath());
+
+ // maybe sign
+ if (Optional.ofNullable(MacAppImageBuilder.
+ SIGN_BUNDLE.fetchFrom(params)).orElse(Boolean.TRUE)) {
+ if (Platform.getMajorVersion() > 10 ||
+ (Platform.getMajorVersion() == 10 &&
+ Platform.getMinorVersion() >= 12)) {
+ // we need this for OS X 10.12+
+ Log.verbose(I18N.getString("message.signing.pkg"));
+ }
+
+ String signingIdentity =
+ DEVELOPER_ID_INSTALLER_SIGNING_KEY.fetchFrom(params);
+ if (signingIdentity != null) {
+ commandLine.add("--sign");
+ commandLine.add(signingIdentity);
+ }
+
+ String keychainName = SIGNING_KEYCHAIN.fetchFrom(params);
+ if (keychainName != null && !keychainName.isEmpty()) {
+ commandLine.add("--keychain");
+ commandLine.add(keychainName);
+ }
+ }
+
+ commandLine.add("--distribution");
+ commandLine.add(
+ getConfig_DistributionXMLFile(params).getAbsolutePath());
+ commandLine.add("--package-path");
+ commandLine.add(PACKAGES_ROOT.fetchFrom(params).getAbsolutePath());
+
+ commandLine.add(finalPKG.getAbsolutePath());
+
+ pb = new ProcessBuilder(commandLine);
+ IOUtils.exec(pb);
+
+ return finalPKG;
+ } catch (Exception ignored) {
+ Log.verbose(ignored);
+ return null;
+ }
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Implement Bundler
+ //////////////////////////////////////////////////////////////////////////
+
+ @Override
+ public String getName() {
+ return I18N.getString("pkg.bundler.name");
+ }
+
+ @Override
+ public String getID() {
+ return "pkg";
+ }
+
+ @Override
+ public boolean validate(Map<String, ? super Object> params)
+ throws ConfigException {
+ try {
+ if (params == null) throw new ConfigException(
+ I18N.getString("error.parameters-null"),
+ I18N.getString("error.parameters-null.advice"));
+
+ // run basic validation to ensure requirements are met
+ // we are not interested in return code, only possible exception
+ validateAppImageAndBundeler(params);
+
+ // reject explicitly set sign to true and no valid signature key
+ if (Optional.ofNullable(MacAppImageBuilder.
+ SIGN_BUNDLE.fetchFrom(params)).orElse(Boolean.FALSE)) {
+ String signingIdentity =
+ DEVELOPER_ID_INSTALLER_SIGNING_KEY.fetchFrom(params);
+ if (signingIdentity == null) {
+ throw new ConfigException(
+ I18N.getString("error.explicit-sign-no-cert"),
+ I18N.getString(
+ "error.explicit-sign-no-cert.advice"));
+ }
+ }
+
+ // hdiutil is always available so there's no need
+ // to test for availability.
+
+ return true;
+ } catch (RuntimeException re) {
+ if (re.getCause() instanceof ConfigException) {
+ throw (ConfigException) re.getCause();
+ } else {
+ throw new ConfigException(re);
+ }
+ }
+ }
+
+ @Override
+ public File execute(Map<String, ? super Object> params,
+ File outputParentDir) throws PackagerException {
+ return bundle(params, outputParentDir);
+ }
+
+ @Override
+ public boolean supported(boolean runtimeInstaller) {
+ return true;
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/DMGsetup.scpt Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,15 @@
+tell application "Finder"
+ tell disk "DEPLOY_ACTUAL_VOLUME_NAME"
+ open
+ set current view of container window to icon view
+ set toolbar visible of container window to false
+ set statusbar visible of container window to false
+
+ set the bounds of container window to {400, 100, 917, 370}
+
+ set theViewOptions to the icon view options of container window
+ set arrangement of theViewOptions to not arranged
+ set icon size of theViewOptions to 128
+ end tell
+end tell
+
Binary file src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/GenericApp.icns has changed
Binary file src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/GenericAppHiDPI.icns has changed
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/Info-lite.plist.template Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,38 @@
+<?xml version="1.0" ?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+ <dict>
+ <key>LSMinimumSystemVersion</key>
+ <string>10.9</string>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>English</string>
+ <key>CFBundleAllowMixedLocalizations</key>
+ <true/>
+ <key>CFBundleExecutable</key>
+ <string>DEPLOY_LAUNCHER_NAME</string>
+ <key>CFBundleIconFile</key>
+ <string>DEPLOY_ICON_FILE</string>
+ <key>CFBundleIdentifier</key>
+ <string>DEPLOY_BUNDLE_IDENTIFIER</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundleName</key>
+ <string>DEPLOY_BUNDLE_NAME</string>
+ <key>CFBundlePackageType</key>
+ <string>APPL</string>
+ <key>CFBundleShortVersionString</key>
+ <string>DEPLOY_BUNDLE_SHORT_VERSION</string>
+ <key>CFBundleSignature</key>
+ <string>????</string>
+ <!-- See http://developer.apple.com/library/mac/#releasenotes/General/SubmittingToMacAppStore/_index.html
+ for list of AppStore categories -->
+ <key>LSApplicationCategoryType</key>
+ <string>DEPLOY_BUNDLE_CATEGORY</string>
+ <key>CFBundleVersion</key>
+ <string>DEPLOY_BUNDLE_CFBUNDLE_VERSION</string>
+ <key>NSHumanReadableCopyright</key>
+ <string>DEPLOY_BUNDLE_COPYRIGHT</string>DEPLOY_FILE_ASSOCIATIONS
+ <key>NSHighResolutionCapable</key>
+ <string>true</string>
+ </dict>
+</plist>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/Info.plist.template Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,54 @@
+<?xml version="1.0" ?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+ <dict>
+ <key>LSMinimumSystemVersion</key>
+ <string>10.7.4</string>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>English</string>
+ <key>CFBundleAllowMixedLocalizations</key>
+ <true/>
+ <key>CFBundleExecutable</key>
+ <string>DEPLOY_LAUNCHER_NAME</string>
+ <key>CFBundleIconFile</key>
+ <string>DEPLOY_ICON_FILE</string>
+ <key>CFBundleIdentifier</key>
+ <string>DEPLOY_BUNDLE_IDENTIFIER</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundleName</key>
+ <string>DEPLOY_BUNDLE_NAME</string>
+ <key>CFBundlePackageType</key>
+ <string>APPL</string>
+ <key>CFBundleShortVersionString</key>
+ <string>DEPLOY_BUNDLE_SHORT_VERSION</string>
+ <key>CFBundleSignature</key>
+ <string>????</string>
+ <!-- See http://developer.apple.com/library/mac/#releasenotes/General/SubmittingToMacAppStore/_index.html
+ for list of AppStore categories -->
+ <key>LSApplicationCategoryType</key>
+ <string>DEPLOY_BUNDLE_CATEGORY</string>
+ <key>CFBundleVersion</key>
+ <string>DEPLOY_BUNDLE_CFBUNDLE_VERSION</string>
+ <key>NSHumanReadableCopyright</key>
+ <string>DEPLOY_BUNDLE_COPYRIGHT</string>
+ <key>JavaRuntime</key>
+ <string>DEPLOY_JAVA_RUNTIME_NAME</string>
+ <key>JavaMainClassName</key>
+ <string>DEPLOY_LAUNCHER_CLASS</string>
+ <key>JavaAppClasspath</key>
+ <string>DEPLOY_APP_CLASSPATH</string>
+ <key>JavaMainJarName</key>
+ <string>DEPLOY_MAIN_JAR_NAME</string>
+ <key>JavaOptions</key>
+ <array>
+DEPLOY_JAVA_OPTIONS
+ </array>
+ <key>ArgOptions</key>
+ <array>
+DEPLOY_ARGUMENTS
+ </array>DEPLOY_FILE_ASSOCIATIONS
+ <key>NSHighResolutionCapable</key>
+ <string>true</string>
+ </dict>
+</plist>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacAppStore.entitlements Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+ <dict>
+ <key>com.apple.security.app-sandbox</key>
+ <true/>
+ </dict>
+</plist>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacAppStore_Inherit.entitlements Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+ <dict>
+ <key>com.apple.security.app-sandbox</key>
+ <true/>
+ <key>com.apple.security.inherit</key>
+ <true/>
+ </dict>
+</plist>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacResources.properties Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,89 @@
+#
+# Copyright (c) 2017, 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.
+#
+#
+
+app.bundler.name=Mac Application Image
+store.bundler.name=Mac App Store Ready Bundler
+dmg.bundler.name=DMG Installer
+pkg.bundler.name=PKG Installer
+
+error.invalid-cfbundle-version=Invalid CFBundleVersion: [{0}].
+error.invalid-cfbundle-version.advice=Set a compatible 'appVersion' or set a 'mac.CFBundleVersion'. Valid versions are one to three integers separated by dots.
+error.explicit-sign-no-cert=Signature explicitly requested but no signing certificate specified.
+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.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
+error.no-app-signing-key.advice=Install your app signing keys into your Mac Keychain using XCode.
+error.no-pkg-signing-key=No Mac App Store Installer Signing Key
+error.no-pkg-signing-key.advice=Install your app signing keys into your Mac Keychain using XCode.
+error.certificate.expired=Error: Certificate expired {0}.
+
+resource.bundle-config-file=Bundle config file
+resource.app-info-plist=Application Info.plist
+resource.runtime-info-plist=Java Runtime Info.plist
+resource.mac-app-store-entitlements=Mac App Store Entitlements
+resource.mac-app-store-inherit-entitlements=Mac App Store Inherit Entitlements
+resource.dmg-setup-script=DMG setup script
+resource.license-setup=License setup
+resource.dmg-background=dmg background
+resource.volume-icon=volume icon
+resource.post-install-script=script to run after application image is populated
+resource.pkg-preinstall-script=PKG preinstall script
+resource.pkg-postinstall-script=PKG postinstall script
+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.
+message.version-string-first-number-not-zero=The first number in a CFBundleVersion cannot be zero or negative.
+message.version-string-no-negative-numbers=Negative numbers are not allowed in version strings.
+message.version-string-numbers-only=Version strings can consist of only numbers and up to two dots.
+message.creating-association-with-null-extension=Creating association with null extension.
+message.ignoring.symlink=Warning: codesign is skipping the symlink {0}.
+message.keychain.error=Error: unable to get keychain list.
+message.building-bundle=Building Mac App Store Bundle for {0}.
+message.app-image-dir-does-not-exist=Specified application image directory {0}: {1} does not exists.
+message.app-image-dir-does-not-exist.advice=Confirm that the value for {0} exists.
+message.app-image-requires-app-name=When using an external app image you must specify the app name.
+message.app-image-requires-app-name.advice=Set the app name via the -name CLI flag, the fx:application/@name ANT attribute, or via the 'appName' bundler argument.
+message.app-image-requires-identifier=When using an external app image you must specify the identifier.
+message.app-image-requires-identifier.advice=Set the identifier via the -appId CLI flag, the fx:application/@id ANT attribute, or via the 'identifier' bundler argument.
+message.building-dmg=Building DMG package for {0}.
+message.running-script=Running shell script on application image [{0}].
+message.preparing-dmg-setup=Preparing dmg setup: {0}.
+message.creating-dmg-file=Creating DMG file: {0}.
+message.dmg-cannot-be-overwritten=Dmg file exists ({0} and can not be removed.
+message.output-to-location=Result DMG installer for {0}: {1}.
+message.building-pkg=Building PKG package for {0}.
+message.preparing-scripts=Preparing package scripts.
+message.preparing-distribution-dist=Preparing distribution.dist: {0}.
+message.signing.pkg=Warning: For signing PKG, you might need to set "Always Trust" for your certificate using "Keychain Access" tool.
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacResources_ja.properties Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,89 @@
+#
+# Copyright (c) 2017, 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.
+#
+#
+
+app.bundler.name=Mac Application Image
+store.bundler.name=Mac App Store Ready Bundler
+dmg.bundler.name=DMG Installer
+pkg.bundler.name=PKG Installer
+
+error.invalid-cfbundle-version=Invalid CFBundleVersion: [{0}].
+error.invalid-cfbundle-version.advice=Set a compatible 'appVersion' or set a 'mac.CFBundleVersion'. Valid versions are one to three integers separated by dots.
+error.explicit-sign-no-cert=Signature explicitly requested but no signing certificate specified.
+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.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
+error.no-app-signing-key.advice=Install your app signing keys into your Mac Keychain using XCode.
+error.no-pkg-signing-key=No Mac App Store Installer Signing Key
+error.no-pkg-signing-key.advice=Install your app signing keys into your Mac Keychain using XCode.
+error.certificate.expired=Error: Certificate expired {0}.
+
+resource.bundle-config-file=Bundle config file
+resource.app-info-plist=Application Info.plist
+resource.runtime-info-plist=Java Runtime Info.plist
+resource.mac-app-store-entitlements=Mac App Store Entitlements
+resource.mac-app-store-inherit-entitlements=Mac App Store Inherit Entitlements
+resource.dmg-setup-script=DMG setup script
+resource.license-setup=License setup
+resource.dmg-background=dmg background
+resource.volume-icon=volume icon
+resource.post-install-script=script to run after application image is populated
+resource.pkg-preinstall-script=PKG preinstall script
+resource.pkg-postinstall-script=PKG postinstall script
+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.
+message.version-string-first-number-not-zero=The first number in a CFBundleVersion cannot be zero or negative.
+message.version-string-no-negative-numbers=Negative numbers are not allowed in version strings.
+message.version-string-numbers-only=Version strings can consist of only numbers and up to two dots.
+message.creating-association-with-null-extension=Creating association with null extension.
+message.ignoring.symlink=Warning: codesign is skipping the symlink {0}.
+message.keychain.error=Error: unable to get keychain list.
+message.building-bundle=Building Mac App Store Bundle for {0}.
+message.app-image-dir-does-not-exist=Specified application image directory {0}: {1} does not exists.
+message.app-image-dir-does-not-exist.advice=Confirm that the value for {0} exists.
+message.app-image-requires-app-name=When using an external app image you must specify the app name.
+message.app-image-requires-app-name.advice=Set the app name via the -name CLI flag, the fx:application/@name ANT attribute, or via the 'appName' bundler argument.
+message.app-image-requires-identifier=When using an external app image you must specify the identifier.
+message.app-image-requires-identifier.advice=Set the identifier via the -appId CLI flag, the fx:application/@id ANT attribute, or via the 'identifier' bundler argument.
+message.building-dmg=Building DMG package for {0}.
+message.running-script=Running shell script on application image [{0}].
+message.preparing-dmg-setup=Preparing dmg setup: {0}.
+message.creating-dmg-file=Creating DMG file: {0}.
+message.dmg-cannot-be-overwritten=Dmg file exists ({0} and can not be removed.
+message.output-to-location=Result DMG installer for {0}: {1}.
+message.building-pkg=Building PKG package for {0}.
+message.preparing-scripts=Preparing package scripts.
+message.preparing-distribution-dist=Preparing distribution.dist: {0}.
+message.signing.pkg=Warning: For signing PKG, you might need to set "Always Trust" for your certificate using "Keychain Access" tool.
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacResources_zh_CN.properties Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,89 @@
+#
+# Copyright (c) 2017, 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.
+#
+#
+
+app.bundler.name=Mac Application Image
+store.bundler.name=Mac App Store Ready Bundler
+dmg.bundler.name=DMG Installer
+pkg.bundler.name=PKG Installer
+
+error.invalid-cfbundle-version=Invalid CFBundleVersion: [{0}].
+error.invalid-cfbundle-version.advice=Set a compatible 'appVersion' or set a 'mac.CFBundleVersion'. Valid versions are one to three integers separated by dots.
+error.explicit-sign-no-cert=Signature explicitly requested but no signing certificate specified.
+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.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
+error.no-app-signing-key.advice=Install your app signing keys into your Mac Keychain using XCode.
+error.no-pkg-signing-key=No Mac App Store Installer Signing Key
+error.no-pkg-signing-key.advice=Install your app signing keys into your Mac Keychain using XCode.
+error.certificate.expired=Error: Certificate expired {0}.
+
+resource.bundle-config-file=Bundle config file
+resource.app-info-plist=Application Info.plist
+resource.runtime-info-plist=Java Runtime Info.plist
+resource.mac-app-store-entitlements=Mac App Store Entitlements
+resource.mac-app-store-inherit-entitlements=Mac App Store Inherit Entitlements
+resource.dmg-setup-script=DMG setup script
+resource.license-setup=License setup
+resource.dmg-background=dmg background
+resource.volume-icon=volume icon
+resource.post-install-script=script to run after application image is populated
+resource.pkg-preinstall-script=PKG preinstall script
+resource.pkg-postinstall-script=PKG postinstall script
+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.
+message.version-string-first-number-not-zero=The first number in a CFBundleVersion cannot be zero or negative.
+message.version-string-no-negative-numbers=Negative numbers are not allowed in version strings.
+message.version-string-numbers-only=Version strings can consist of only numbers and up to two dots.
+message.creating-association-with-null-extension=Creating association with null extension.
+message.ignoring.symlink=Warning: codesign is skipping the symlink {0}.
+message.keychain.error=Error: unable to get keychain list.
+message.building-bundle=Building Mac App Store Bundle for {0}.
+message.app-image-dir-does-not-exist=Specified application image directory {0}: {1} does not exists.
+message.app-image-dir-does-not-exist.advice=Confirm that the value for {0} exists.
+message.app-image-requires-app-name=When using an external app image you must specify the app name.
+message.app-image-requires-app-name.advice=Set the app name via the -name CLI flag, the fx:application/@name ANT attribute, or via the 'appName' bundler argument.
+message.app-image-requires-identifier=When using an external app image you must specify the identifier.
+message.app-image-requires-identifier.advice=Set the identifier via the -appId CLI flag, the fx:application/@id ANT attribute, or via the 'identifier' bundler argument.
+message.building-dmg=Building DMG package for {0}.
+message.running-script=Running shell script on application image [{0}].
+message.preparing-dmg-setup=Preparing dmg setup: {0}.
+message.creating-dmg-file=Creating DMG file: {0}.
+message.dmg-cannot-be-overwritten=Dmg file exists ({0} and can not be removed.
+message.output-to-location=Result DMG installer for {0}: {1}.
+message.building-pkg=Building PKG package for {0}.
+message.preparing-scripts=Preparing package scripts.
+message.preparing-distribution-dist=Preparing distribution.dist: {0}.
+message.signing.pkg=Warning: For signing PKG, you might need to set "Always Trust" for your certificate using "Keychain Access" tool.
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/Runtime-Info.plist.template Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>English</string>
+ <key>CFBundleExecutable</key>
+ <string>libjli.dylib</string>
+ <key>CFBundleIdentifier</key>
+ <string>CF_BUNDLE_IDENTIFIER</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>7.0</string>
+ <key>CFBundleName</key>
+ <string>CF_BUNDLE_NAME</string>
+ <key>CFBundlePackageType</key>
+ <string>BNDL</string>
+ <key>CFBundleShortVersionString</key>
+ <string>CF_BUNDLE_SHORT_VERSION_STRING</string>
+ <key>CFBundleSignature</key>
+ <string>????</string>
+ <key>CFBundleVersion</key>
+ <string>CF_BUNDLE_VERSION</string>
+</dict>
+</plist>
Binary file src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/background_dmg.png has changed
Binary file src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/background_pkg.png has changed
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/launchd.plist.template Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>Label</key>
+ <string>DEPLOY_DAEMON_IDENTIFIER</string>
+ <key>ProgramArguments</key>
+ <array>
+ <string>DEPLOY_DAEMON_LAUNCHER_PATH</string>
+ </array>
+ <key>RunAtLoad</key><DEPLOY_RUN_AT_LOAD/>
+ <key>KeepAlive</key><DEPLOY_KEEP_ALIVE/>
+</dict>
+</plist>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/lic_template.plist Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,244 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>LPic</key>
+ <array>
+ <dict>
+ <key>Attributes</key>
+ <string>0x0000</string>
+ <key>Data</key>
+ <data>AAAAAgAAAAAAAAAAAAQAAA==</data>
+ <key>ID</key>
+ <string>5000</string>
+ <key>Name</key>
+ <string></string>
+ </dict>
+ </array>
+ <key>STR#</key>
+ <array>
+ <dict>
+ <key>Attributes</key>
+ <string>0x0000</string>
+ <key>Data</key>
+ <data>AAYPRW5nbGlzaCBkZWZhdWx0BUFncmVlCERpc2FncmVlBVByaW50B1NhdmUuLi56SWYgeW91IGFncmVlIHdpdGggdGhlIHRlcm1zIG9mIHRoaXMgbGljZW5zZSwgY2xpY2sgIkFncmVlIiB0byBhY2Nlc3MgdGhlIHNvZnR3YXJlLiAgSWYgeW91IGRvIG5vdCBhZ3JlZSwgcHJlc3MgIkRpc2FncmVlLiI=</data>
+ <key>ID</key>
+ <string>5000</string>
+ <key>Name</key>
+ <string>English buttons</string>
+ </dict>
+ <dict>
+ <key>Attributes</key>
+ <string>0x0000</string>
+ <key>Data</key>
+ <data>AAYHRGV1dHNjaAtBa3plcHRpZXJlbghBYmxlaG5lbgdEcnVja2VuClNpY2hlcm4uLi7nS2xpY2tlbiBTaWUgaW4g0kFremVwdGllcmVu0ywgd2VubiBTaWUgbWl0IGRlbiBCZXN0aW1tdW5nZW4gZGVzIFNvZnR3YXJlLUxpemVuenZlcnRyYWdzIGVpbnZlcnN0YW5kZW4gc2luZC4gRmFsbHMgbmljaHQsIGJpdHRlINJBYmxlaG5lbtMgYW5rbGlja2VuLiBTaWUga5pubmVuIGRpZSBTb2Z0d2FyZSBudXIgaW5zdGFsbGllcmVuLCB3ZW5uIFNpZSDSQWt6ZXB0aWVyZW7TIGFuZ2VrbGlja3QgaGFiZW4u</data>
+ <key>ID</key>
+ <string>5001</string>
+ <key>Name</key>
+ <string>German</string>
+ </dict>
+ <dict>
+ <key>Attributes</key>
+ <string>0x0000</string>
+ <key>Data</key>
+ <data>AAYHRW5nbGlzaAVBZ3JlZQhEaXNhZ3JlZQVQcmludAdTYXZlLi4ue0lmIHlvdSBhZ3JlZSB3aXRoIHRoZSB0ZXJtcyBvZiB0aGlzIGxpY2Vuc2UsIHByZXNzICJBZ3JlZSIgdG8gaW5zdGFsbCB0aGUgc29mdHdhcmUuICBJZiB5b3UgZG8gbm90IGFncmVlLCBwcmVzcyAiRGlzYWdyZWUiLg==</data>
+ <key>ID</key>
+ <string>5002</string>
+ <key>Name</key>
+ <string>English</string>
+ </dict>
+ <dict>
+ <key>Attributes</key>
+ <string>0x0000</string>
+ <key>Data</key>
+ <data>AAYHRXNwYZZvbAdBY2VwdGFyCk5vIGFjZXB0YXIISW1wcmltaXIKR3VhcmRhci4uLsBTaSBlc3SHIGRlIGFjdWVyZG8gY29uIGxvcyB0jnJtaW5vcyBkZSBlc3RhIGxpY2VuY2lhLCBwdWxzZSAiQWNlcHRhciIgcGFyYSBpbnN0YWxhciBlbCBzb2Z0d2FyZS4gRW4gZWwgc3VwdWVzdG8gZGUgcXVlIG5vIGVzdI4gZGUgYWN1ZXJkbyBjb24gbG9zIHSOcm1pbm9zIGRlIGVzdGEgbGljZW5jaWEsIHB1bHNlICJObyBhY2VwdGFyLiI=</data>
+ <key>ID</key>
+ <string>5003</string>
+ <key>Name</key>
+ <string>Spanish</string>
+ </dict>
+ <dict>
+ <key>Attributes</key>
+ <string>0x0000</string>
+ <key>Data</key>
+ <data>AAYIRnJhbo1haXMIQWNjZXB0ZXIHUmVmdXNlcghJbXByaW1lcg5FbnJlZ2lzdHJlci4uLrpTaSB2b3VzIGFjY2VwdGV6IGxlcyB0ZXJtZXMgZGUgbGEgcHKOc2VudGUgbGljZW5jZSwgY2xpcXVleiBzdXIgIkFjY2VwdGVyIiBhZmluIGQnaW5zdGFsbGVyIGxlIGxvZ2ljaWVsLiBTaSB2b3VzIG4nkHRlcyBwYXMgZCdhY2NvcmQgYXZlYyBsZXMgdGVybWVzIGRlIGxhIGxpY2VuY2UsIGNsaXF1ZXogc3VyICJSZWZ1c2VyIi4=</data>
+ <key>ID</key>
+ <string>5004</string>
+ <key>Name</key>
+ <string>French</string>
+ </dict>
+ <dict>
+ <key>Attributes</key>
+ <string>0x0000</string>
+ <key>Data</key>
+ <data>AAYISXRhbGlhbm8HQWNjZXR0bwdSaWZpdXRvBlN0YW1wYQtSZWdpc3RyYS4uLn9TZSBhY2NldHRpIGxlIGNvbmRpemlvbmkgZGkgcXVlc3RhIGxpY2VuemEsIGZhaSBjbGljIHN1ICJBY2NldHRvIiBwZXIgaW5zdGFsbGFyZSBpbCBzb2Z0d2FyZS4gQWx0cmltZW50aSBmYWkgY2xpYyBzdSAiUmlmaXV0byIu</data>
+ <key>ID</key>
+ <string>5005</string>
+ <key>Name</key>
+ <string>Italian</string>
+ </dict>
+ <dict>
+ <key>Attributes</key>
+ <string>0x0000</string>
+ <key>Data</key>
+ <data>AAYISmFwYW5lc2UKk6+I04K1gtyCtwyTr4jTgrWC3IK5gvEIiPON/IK3gukHlduRti4uLrSWe4Ncg3SDZ4NFg0eDQY5nl3CLlpH4jF+W8YLMj/CMj4LJk6+I04KzguqC6Y/qjYeCyYLNgUGDXIN0g2eDRYNHg0GC8INDg5ODWINngVuDi4K3gumCvYLfgsmBdZOviNOCtYLcgreBdoLwiZ+CtYLEgq2CvoKzgqKBQoFAk6+I04KzguqCyIKij+qNh4LJgs2BQYF1k6+I04K1gtyCuYLxgXaC8ImfgrWCxIKtgr6Cs4KigUI=</data>
+ <key>ID</key>
+ <string>5006</string>
+ <key>Name</key>
+ <string>Japanese</string>
+ </dict>
+ <dict>
+ <key>Attributes</key>
+ <string>0x0000</string>
+ <key>Data</key>
+ <data>AAYKTmVkZXJsYW5kcwJKYQNOZWUFUHJpbnQJQmV3YWFyLi4upEluZGllbiB1IGFra29vcmQgZ2FhdCBtZXQgZGUgdm9vcndhYXJkZW4gdmFuIGRlemUgbGljZW50aWUsIGt1bnQgdSBvcCAnSmEnIGtsaWtrZW4gb20gZGUgcHJvZ3JhbW1hdHV1ciB0ZSBpbnN0YWxsZXJlbi4gSW5kaWVuIHUgbmlldCBha2tvb3JkIGdhYXQsIGtsaWt0IHUgb3AgJ05lZScu</data>
+ <key>ID</key>
+ <string>5007</string>
+ <key>Name</key>
+ <string>Dutch</string>
+ </dict>
+ <dict>
+ <key>Attributes</key>
+ <string>0x0000</string>
+ <key>Data</key>
+ <data>AAYGU3ZlbnNrCEdvZGuKbm5zBkF2YppqcwhTa3JpdiB1dAhTcGFyYS4uLpNPbSBEdSBnb2Rrim5uZXIgbGljZW5zdmlsbGtvcmVuIGtsaWNrYSBwjCAiR29ka4pubnMiIGaaciBhdHQgaW5zdGFsbGVyYSBwcm9ncmFtcHJvZHVrdGVuLiBPbSBEdSBpbnRlIGdvZGuKbm5lciBsaWNlbnN2aWxsa29yZW4sIGtsaWNrYSBwjCAiQXZimmpzIi4=</data>
+ <key>ID</key>
+ <string>5008</string>
+ <key>Name</key>
+ <string>Swedish</string>
+ </dict>
+ <dict>
+ <key>Attributes</key>
+ <string>0x0000</string>
+ <key>Data</key>
+ <data>AAYRUG9ydHVndZBzLCBCcmFzaWwJQ29uY29yZGFyCURpc2NvcmRhcghJbXByaW1pcglTYWx2YXIuLi6MU2UgZXN0hyBkZSBhY29yZG8gY29tIG9zIHRlcm1vcyBkZXN0YSBsaWNlbo1hLCBwcmVzc2lvbmUgIkNvbmNvcmRhciIgcGFyYSBpbnN0YWxhciBvIHNvZnR3YXJlLiBTZSBui28gZXN0hyBkZSBhY29yZG8sIHByZXNzaW9uZSAiRGlzY29yZGFyIi4=</data>
+ <key>ID</key>
+ <string>5009</string>
+ <key>Name</key>
+ <string>Brazilian Portuguese</string>
+ </dict>
+ <dict>
+ <key>Attributes</key>
+ <string>0x0000</string>
+ <key>Data</key>
+ <data>AAYSU2ltcGxpZmllZCBDaGluZXNlBM2s0uIGsrvNrNLiBLTy06EGtOa0oqGtVMjnufvE+s2s0uKxvtDtv8nQrdLptcTM9b/uo6zH67C0obDNrNLiobHAtLCy17C0y8jtvP6ho8jnufvE+rK7zazS4qOsx+uwtKGwsrvNrNLiobGhow==</data>
+ <key>ID</key>
+ <string>5010</string>
+ <key>Name</key>
+ <string>Simplified Chinese</string>
+ </dict>
+ <dict>
+ <key>Attributes</key>
+ <string>0x0000</string>
+ <key>Data</key>
+ <data>AAYTVHJhZGl0aW9uYWwgQ2hpbmVzZQSmULdOBqSjplC3TgSmQ6ZMBsB4pnOhS1CmcKpHsXqmULdOpbuzXKVpw9K4zKq6sfi02qFBvdCr9qGnplC3TqGopUimd7jLs27F6aFDpnCqR6SjplC3TqFBvdCr9qGnpKOmULdOoaihQw==</data>
+ <key>ID</key>
+ <string>5011</string>
+ <key>Name</key>
+ <string>Traditional Chinese</string>
+ </dict>
+ <dict>
+ <key>Attributes</key>
+ <string>0x0000</string>
+ <key>Data</key>
+ <data>AAYFRGFuc2sERW5pZwVVZW5pZwdVZHNrcml2CkFya2l2ZXIuLi6YSHZpcyBkdSBhY2NlcHRlcmVyIGJldGluZ2Vsc2VybmUgaSBsaWNlbnNhZnRhbGVuLCBza2FsIGR1IGtsaWtrZSBwjCDSRW5pZ9MgZm9yIGF0IGluc3RhbGxlcmUgc29mdHdhcmVuLiBLbGlrIHCMINJVZW5pZ9MgZm9yIGF0IGFubnVsbGVyZSBpbnN0YWxsZXJpbmdlbi4=</data>
+ <key>ID</key>
+ <string>5012</string>
+ <key>Name</key>
+ <string>Danish</string>
+ </dict>
+ <dict>
+ <key>Attributes</key>
+ <string>0x0000</string>
+ <key>Data</key>
+ <data>AAYFU3VvbWkISHl2imtzeW4KRW4gaHl2imtzeQdUdWxvc3RhCVRhbGxlbm5hyW9IeXaKa3N5IGxpc2Vuc3Npc29waW11a3NlbiBlaGRvdCBvc29pdHRhbWFsbGEg1Uh5doprc3nVLiBKb3MgZXQgaHl2imtzeSBzb3BpbXVrc2VuIGVodG9qYSwgb3NvaXRhINVFbiBoeXaKa3N51S4=</data>
+ <key>ID</key>
+ <string>5013</string>
+ <key>Name</key>
+ <string>Finnish</string>
+ </dict>
+ <dict>
+ <key>Attributes</key>
+ <string>0x0000</string>
+ <key>Data</key>
+ <data>AAYRRnJhbo1haXMgY2FuYWRpZW4IQWNjZXB0ZXIHUmVmdXNlcghJbXByaW1lcg5FbnJlZ2lzdHJlci4uLrpTaSB2b3VzIGFjY2VwdGV6IGxlcyB0ZXJtZXMgZGUgbGEgcHKOc2VudGUgbGljZW5jZSwgY2xpcXVleiBzdXIgIkFjY2VwdGVyIiBhZmluIGQnaW5zdGFsbGVyIGxlIGxvZ2ljaWVsLiBTaSB2b3VzIG4nkHRlcyBwYXMgZCdhY2NvcmQgYXZlYyBsZXMgdGVybWVzIGRlIGxhIGxpY2VuY2UsIGNsaXF1ZXogc3VyICJSZWZ1c2VyIi4=</data>
+ <key>ID</key>
+ <string>5014</string>
+ <key>Name</key>
+ <string>French Canadian</string>
+ </dict>
+ <dict>
+ <key>Attributes</key>
+ <string>0x0000</string>
+ <key>Data</key>
+ <data>AAYGS29yZWFuBLW/wMcJtb/AxyC+yMfUBsfBuLDGrgfA+sDlLi4ufrvnv+sgsOi+4LytwMcgs7u/67+hILW/wMfHz7jpLCAitb/AxyIgtNzD37imILStt68gvNLHwcauv/6+7rimILyzxKHHz73KvcO/wC4gtb/Ax8fPwfYgvsq0wrTZuOksICK1v8DHIL7Ix9QiILTcw9+4piC0qbijvcq9w7/ALg==</data>
+ <key>ID</key>
+ <string>5015</string>
+ <key>Name</key>
+ <string>Korean</string>
+ </dict>
+ <dict>
+ <key>Attributes</key>
+ <string>0x0000</string>
+ <key>Data</key>
+ <data>AAYFTm9yc2sERW5pZwlJa2tlIGVuaWcIU2tyaXYgdXQKQXJraXZlci4uLqNIdmlzIERlIGVyIGVuaWcgaSBiZXN0ZW1tZWxzZW5lIGkgZGVubmUgbGlzZW5zYXZ0YWxlbiwga2xpa2tlciBEZSBwjCAiRW5pZyIta25hcHBlbiBmb3IgjCBpbnN0YWxsZXJlIHByb2dyYW12YXJlbi4gSHZpcyBEZSBpa2tlIGVyIGVuaWcsIGtsaWtrZXIgRGUgcIwgIklra2UgZW5pZyIu</data>
+ <key>ID</key>
+ <string>5016</string>
+ <key>Name</key>
+ <string>Norwegian</string>
+ </dict>
+ </array>
+ <key>TEXT</key>
+ <array>
+ <dict>
+ <key>Attributes</key>
+ <string>0x0000</string>
+ <key>Data</key>
+ <data>APPLICATION_LICENSE_TEXT</data>
+ <key>ID</key>
+ <string>5000</string>
+ <key>Name</key>
+ <string>English SLA</string>
+ </dict>
+ </array>
+ <key>TMPL</key>
+ <array>
+ <dict>
+ <key>Attributes</key>
+ <string>0x0000</string>
+ <key>Data</key>
+ <data>E0RlZmF1bHQgTGFuZ3VhZ2UgSUREV1JEBUNvdW50T0NOVAQqKioqTFNUQwtzeXMgbGFuZyBJRERXUkQebG9jYWwgcmVzIElEIChvZmZzZXQgZnJvbSA1MDAwRFdSRBAyLWJ5dGUgbGFuZ3VhZ2U/RFdSRAQqKioqTFNURQ==</data>
+ <key>ID</key>
+ <string>128</string>
+ <key>Name</key>
+ <string>LPic</string>
+ </dict>
+ </array>
+ <key>plst</key>
+ <array>
+ <dict>
+ <key>Attributes</key>
+ <string>0x0050</string>
+ <key>Data</key>
+ <data>AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA</data>
+ <key>ID</key>
+ <string>0</string>
+ <key>Name</key>
+ <string></string>
+ </dict>
+ </array>
+ <key>styl</key>
+ <array>
+ <dict>
+ <key>Attributes</key>
+ <string>0x0000</string>
+ <key>Data</key>
+ <data>AAMAAAAAAAwACQAUAAAAAAAAAAAAAAAAACcADAAJABQBAAAAAAAAAAAAAAAAKgAMAAkAFAAAAAAAAAAAAAA=</data>
+ <key>ID</key>
+ <string>5000</string>
+ <key>Name</key>
+ <string>English SLA</string>
+ </dict>
+ </array>
+</dict>
+</plist>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/postinstall.template Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,6 @@
+#!/usr/bin/env sh
+
+chown root:wheel "INSTALL_LOCATION"
+chmod a+rX "INSTALL_LOCATION"
+
+exit 0
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/preinstall.template Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,8 @@
+#!/usr/bin/env sh
+
+if [ ! -d "INSTALL_LOCATION" ]
+then
+ mkdir -p "INSTALL_LOCATION"
+fi
+
+exit 0
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/macosx/classes/module-info.java.extra Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,31 @@
+/*
+ * 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.
+ */
+
+provides jdk.jpackage.internal.Bundler with
+ jdk.jpackage.internal.MacAppBundler,
+ jdk.jpackage.internal.MacAppStoreBundler,
+ jdk.jpackage.internal.MacDmgBundler,
+ jdk.jpackage.internal.MacPkgBundler;
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/macosx/native/jpackageapplauncher/main.m Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,81 @@
+/*
+ * 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.
+ */
+
+#import <Cocoa/Cocoa.h>
+#include <dlfcn.h>
+#include <unistd.h>
+
+typedef bool (*start_launcher)(int argc, char* argv[]);
+typedef void (*stop_launcher)();
+
+int main(int argc, char *argv[]) {
+#if !__has_feature(objc_arc)
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+#endif
+
+ int result = 1;
+
+ @try {
+ setlocale(LC_ALL, "en_US.utf8");
+
+ NSBundle *mainBundle = [NSBundle mainBundle];
+ NSString *mainBundlePath = [mainBundle bundlePath];
+ NSString *libraryName = [mainBundlePath stringByAppendingPathComponent:@"Contents/MacOS/libapplauncher.dylib"];
+
+ void* library = dlopen([libraryName UTF8String], RTLD_LAZY);
+
+ if (library == NULL) {
+ NSLog(@"%@ not found.\n", libraryName);
+ }
+
+ if (library != NULL) {
+ start_launcher start =
+ (start_launcher)dlsym(library, "start_launcher");
+ stop_launcher stop =
+ (stop_launcher)dlsym(library, "stop_launcher");
+
+ if (start != NULL && stop != NULL) {
+ if (start(argc, argv) == true) {
+ result = 0;
+ stop();
+ }
+ } else if (start == NULL) {
+ NSLog(@"start_launcher not found in %@.\n", libraryName);
+ } else {
+ NSLog(@"stop_launcher not found in %@.\n", libraryName);
+ }
+ dlclose(library);
+ }
+ } @catch (NSException *exception) {
+ NSLog(@"%@: %@", exception, [exception callStackSymbols]);
+ result = 1;
+ }
+
+#if !__has_feature(objc_arc)
+ [pool drain];
+#endif
+
+ return result;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/macosx/native/libapplauncher/MacPlatform.h Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2014, 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.
+ */
+
+#ifndef MACPLATFORM_H
+#define MACPLATFORM_H
+
+#include "Platform.h"
+#include "PosixPlatform.h"
+
+class MacPlatform : virtual public Platform, PosixPlatform {
+private:
+ bool UsePListForConfigFile();
+
+protected:
+ virtual TString getTmpDirString();
+
+public:
+ MacPlatform(void);
+ virtual ~MacPlatform(void);
+
+public:
+ virtual void ShowMessage(TString title, TString description);
+ virtual void ShowMessage(TString description);
+
+ virtual TCHAR* ConvertStringToFileSystemString(
+ TCHAR* Source, bool &release);
+ virtual TCHAR* ConvertFileSystemStringToString(
+ TCHAR* Source, bool &release);
+
+ virtual void SetCurrentDirectory(TString Value);
+ virtual TString GetPackageRootDirectory();
+ virtual TString GetAppDataDirectory();
+ virtual TString GetBundledJavaLibraryFileName(TString RuntimePath);
+ virtual TString GetAppName();
+
+ TString GetPackageAppDirectory();
+ TString GetPackageLauncherDirectory();
+ TString GetPackageRuntimeBinDirectory();
+
+ virtual ISectionalPropertyContainer* GetConfigFile(TString FileName);
+ virtual TString GetModuleFileName();
+
+ virtual bool IsMainThread();
+ virtual TPlatformNumber GetMemorySize();
+
+ virtual std::map<TString, TString> GetKeys();
+};
+
+
+#endif // MACPLATFORM_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/macosx/native/libapplauncher/MacPlatform.mm Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,507 @@
+/*
+ * Copyright (c) 2014, 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.
+ */
+
+#include "Platform.h"
+
+#include "MacPlatform.h"
+#include "Helpers.h"
+#include "Package.h"
+#include "PropertyFile.h"
+#include "IniFile.h"
+
+#include <sys/sysctl.h>
+#include <pthread.h>
+#include <vector>
+#include <signal.h>
+#include <mach-o/dyld.h>
+
+#import <Foundation/Foundation.h>
+#import <AppKit/NSRunningApplication.h>
+
+#include <CoreFoundation/CoreFoundation.h>
+#include <CoreFoundation/CFString.h>
+
+#ifdef __OBJC__
+#import <Cocoa/Cocoa.h>
+#endif //__OBJC__
+
+#define MAC_JPACKAGE_TMP_DIR \
+ "/Library/Application Support/Java/JPackage/tmp"
+
+NSString* StringToNSString(TString Value) {
+ NSString* result = [NSString stringWithCString : Value.c_str()
+ encoding : [NSString defaultCStringEncoding]];
+ return result;
+}
+
+FileSystemStringToString::FileSystemStringToString(const TCHAR* value) {
+ bool release = false;
+ PlatformString lvalue = PlatformString(value);
+ Platform& platform = Platform::GetInstance();
+ TCHAR* buffer = platform.ConvertFileSystemStringToString(lvalue, release);
+ FData = buffer;
+
+ if (buffer != NULL && release == true) {
+ delete[] buffer;
+ }
+}
+
+FileSystemStringToString::operator TString() {
+ return FData;
+}
+
+StringToFileSystemString::StringToFileSystemString(const TString &value) {
+ FRelease = false;
+ PlatformString lvalue = PlatformString(value);
+ Platform& platform = Platform::GetInstance();
+ FData = platform.ConvertStringToFileSystemString(lvalue, FRelease);
+}
+
+StringToFileSystemString::~StringToFileSystemString() {
+ if (FRelease == true) {
+ delete[] FData;
+ }
+}
+
+StringToFileSystemString::operator TCHAR* () {
+ return FData;
+}
+
+MacPlatform::MacPlatform(void) : Platform(), PosixPlatform() {
+}
+
+MacPlatform::~MacPlatform(void) {
+}
+
+TString MacPlatform::GetPackageAppDirectory() {
+ return FilePath::IncludeTrailingSeparator(
+ GetPackageRootDirectory()) + _T("Java");
+}
+
+TString MacPlatform::GetPackageLauncherDirectory() {
+ return FilePath::IncludeTrailingSeparator(
+ GetPackageRootDirectory()) + _T("MacOS");
+}
+
+TString MacPlatform::GetPackageRuntimeBinDirectory() {
+ return FilePath::IncludeTrailingSeparator(GetPackageRootDirectory()) +
+ _T("runtime/Contents/Home/bin");
+}
+
+bool MacPlatform::UsePListForConfigFile() {
+ return FilePath::FileExists(GetConfigFileName()) == false;
+}
+
+void MacPlatform::ShowMessage(TString Title, TString Description) {
+ NSString *ltitle = StringToNSString(Title);
+ NSString *ldescription = StringToNSString(Description);
+
+ NSLog(@"%@:%@", ltitle, ldescription);
+}
+
+void MacPlatform::ShowMessage(TString Description) {
+ TString appname = GetModuleFileName();
+ appname = FilePath::ExtractFileName(appname);
+ ShowMessage(appname, Description);
+}
+
+TString MacPlatform::getTmpDirString() {
+ return TString(MAC_JPACKAGE_TMP_DIR);
+}
+
+TCHAR* MacPlatform::ConvertStringToFileSystemString(TCHAR* Source,
+ bool &release) {
+ TCHAR* result = NULL;
+ release = false;
+ CFStringRef StringRef = CFStringCreateWithCString(kCFAllocatorDefault,
+ Source, kCFStringEncodingUTF8);
+
+ if (StringRef != NULL) {
+ @ try {
+ CFIndex length =
+ CFStringGetMaximumSizeOfFileSystemRepresentation(StringRef);
+ result = new char[length + 1];
+ if (result != NULL) {
+ if (CFStringGetFileSystemRepresentation(StringRef,
+ result, length)) {
+ release = true;
+ } else {
+ delete[] result;
+ result = NULL;
+ }
+ }
+ }
+ @finally
+ {
+ CFRelease(StringRef);
+ }
+ }
+
+ return result;
+}
+
+TCHAR* MacPlatform::ConvertFileSystemStringToString(TCHAR* Source,
+ bool &release) {
+ TCHAR* result = NULL;
+ release = false;
+ CFStringRef StringRef = CFStringCreateWithFileSystemRepresentation(
+ kCFAllocatorDefault, Source);
+
+ if (StringRef != NULL) {
+ @ try {
+ CFIndex length = CFStringGetLength(StringRef);
+
+ if (length > 0) {
+ CFIndex maxSize = CFStringGetMaximumSizeForEncoding(
+ length, kCFStringEncodingUTF8);
+
+ result = new char[maxSize + 1];
+ if (result != NULL) {
+ if (CFStringGetCString(StringRef, result, maxSize,
+ kCFStringEncodingUTF8) == true) {
+ release = true;
+ } else {
+ delete[] result;
+ result = NULL;
+ }
+ }
+ }
+ }
+ @finally
+ {
+ CFRelease(StringRef);
+ }
+ }
+
+ return result;
+}
+
+void MacPlatform::SetCurrentDirectory(TString Value) {
+ chdir(PlatformString(Value).toPlatformString());
+}
+
+TString MacPlatform::GetPackageRootDirectory() {
+ NSBundle *mainBundle = [NSBundle mainBundle];
+ NSString *mainBundlePath = [mainBundle bundlePath];
+ NSString *contentsPath =
+ [mainBundlePath stringByAppendingString : @"/Contents"];
+ TString result = [contentsPath UTF8String];
+ return result;
+}
+
+TString MacPlatform::GetAppDataDirectory() {
+ TString result;
+ NSArray *paths = NSSearchPathForDirectoriesInDomains(
+ NSApplicationSupportDirectory, NSUserDomainMask, YES);
+ NSString *applicationSupportDirectory = [paths firstObject];
+ result = [applicationSupportDirectory UTF8String];
+ return result;
+}
+
+TString MacPlatform::GetBundledJavaLibraryFileName(TString RuntimePath) {
+ TString result;
+
+ // first try lib/, then lib/jli
+ result = FilePath::IncludeTrailingSeparator(RuntimePath) +
+ _T("Contents/Home/lib/libjli.dylib");
+
+ if (FilePath::FileExists(result) == false) {
+ result = FilePath::IncludeTrailingSeparator(RuntimePath) +
+ _T("Contents/Home/lib/jli/libjli.dylib");
+
+ if (FilePath::FileExists(result) == false) {
+ // cannot find
+ NSLog(@"Cannot find libjli.dysym!");
+ result = _T("");
+ }
+ }
+
+ return result;
+}
+
+TString MacPlatform::GetAppName() {
+ NSString *appName = [[NSProcessInfo processInfo] processName];
+ TString result = [appName UTF8String];
+ return result;
+}
+
+void PosixProcess::Cleanup() {
+ if (FOutputHandle != 0) {
+ close(FOutputHandle);
+ FOutputHandle = 0;
+ }
+
+ if (FInputHandle != 0) {
+ close(FInputHandle);
+ FInputHandle = 0;
+ }
+
+ sigaction(SIGINT, &savintr, (struct sigaction *) 0);
+ sigaction(SIGQUIT, &savequit, (struct sigaction *) 0);
+ sigprocmask(SIG_SETMASK, &saveblock, (sigset_t *) 0);
+}
+
+#define PIPE_READ 0
+#define PIPE_WRITE 1
+
+bool PosixProcess::Execute(const TString Application,
+ const std::vector<TString> Arguments, bool AWait) {
+ bool result = false;
+
+ if (FRunning == false) {
+ FRunning = true;
+
+ int handles[2];
+
+ if (pipe(handles) == -1) {
+ return false;
+ }
+
+ struct sigaction sa;
+ sa.sa_handler = SIG_IGN;
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = 0;
+ sigemptyset(&savintr.sa_mask);
+ sigemptyset(&savequit.sa_mask);
+ sigaction(SIGINT, &sa, &savintr);
+ sigaction(SIGQUIT, &sa, &savequit);
+ sigaddset(&sa.sa_mask, SIGCHLD);
+ sigprocmask(SIG_BLOCK, &sa.sa_mask, &saveblock);
+
+ FChildPID = fork();
+
+ // PID returned by vfork is 0 for the child process and the
+ // PID of the child process for the parent.
+ if (FChildPID == -1) {
+ // Error
+ TString message = PlatformString::Format(
+ _T("Error: Unable to create process %s"),
+ Application.data());
+ throw Exception(message);
+ } else if (FChildPID == 0) {
+ Cleanup();
+ TString command = Application;
+
+ for (std::vector<TString>::const_iterator iterator =
+ Arguments.begin(); iterator != Arguments.end();
+ iterator++) {
+ command += TString(_T(" ")) + *iterator;
+ }
+#ifdef DEBUG
+ printf("%s\n", command.data());
+#endif // DEBUG
+
+ dup2(handles[PIPE_READ], STDIN_FILENO);
+ dup2(handles[PIPE_WRITE], STDOUT_FILENO);
+
+ close(handles[PIPE_READ]);
+ close(handles[PIPE_WRITE]);
+
+ execl("/bin/sh", "sh", "-c", command.data(), (char *) 0);
+
+ _exit(127);
+ } else {
+ FOutputHandle = handles[PIPE_READ];
+ FInputHandle = handles[PIPE_WRITE];
+
+ if (AWait == true) {
+ ReadOutput();
+ Wait();
+ Cleanup();
+ FRunning = false;
+ result = true;
+ } else {
+ result = true;
+ }
+ }
+ }
+
+ return result;
+}
+
+void AppendPListArrayToIniFile(NSDictionary *infoDictionary,
+ IniFile *result, TString Section) {
+ NSString *sectionKey =
+ [NSString stringWithUTF8String : PlatformString(Section).toMultibyte()];
+ NSDictionary *array = [infoDictionary objectForKey : sectionKey];
+
+ for (id option in array) {
+ if ([option isKindOfClass : [NSString class]]) {
+ TString arg = [option UTF8String];
+
+ TString name;
+ TString value;
+
+ if (Helpers::SplitOptionIntoNameValue(arg, name, value) == true) {
+ result->Append(Section, name, value);
+ }
+ }
+ }
+}
+
+void AppendPListDictionaryToIniFile(NSDictionary *infoDictionary,
+ IniFile *result, TString Section, bool FollowSection = true) {
+ NSDictionary *dictionary = NULL;
+
+ if (FollowSection == true) {
+ NSString *sectionKey = [NSString stringWithUTF8String : PlatformString(
+ Section).toMultibyte()];
+ dictionary = [infoDictionary objectForKey : sectionKey];
+ } else {
+ dictionary = infoDictionary;
+ }
+
+ for (id key in dictionary) {
+ id option = [dictionary valueForKey : key];
+
+ if ([key isKindOfClass : [NSString class]] &&
+ [option isKindOfClass : [NSString class]]) {
+ TString name = [key UTF8String];
+ TString value = [option UTF8String];
+ result->Append(Section, name, value);
+ }
+ }
+}
+
+// Convert parts of the info.plist to the INI format the rest of the jpackage
+// uses unless a jpackage config file exists.
+ISectionalPropertyContainer* MacPlatform::GetConfigFile(TString FileName) {
+ IniFile* result = new IniFile();
+ if (result == NULL) {
+ return NULL;
+ }
+
+ if (UsePListForConfigFile() == false) {
+ result->LoadFromFile(FileName);
+ } else {
+ NSBundle *mainBundle = [NSBundle mainBundle];
+ NSDictionary *infoDictionary = [mainBundle infoDictionary];
+ std::map<TString, TString> keys = GetKeys();
+
+ // JPackage options.
+ AppendPListDictionaryToIniFile(infoDictionary, result,
+ keys[CONFIG_SECTION_APPLICATION], false);
+
+ // jvmargs
+ AppendPListArrayToIniFile(infoDictionary, result,
+ keys[CONFIG_SECTION_JAVAOPTIONS]);
+
+ // Generate AppCDS Cache
+ AppendPListDictionaryToIniFile(infoDictionary, result,
+ keys[CONFIG_SECTION_APPCDSJAVAOPTIONS]);
+ AppendPListDictionaryToIniFile(infoDictionary, result,
+ keys[CONFIG_SECTION_APPCDSGENERATECACHEJAVAOPTIONS]);
+
+ // args
+ AppendPListArrayToIniFile(infoDictionary, result,
+ keys[CONFIG_SECTION_ARGOPTIONS]);
+ }
+
+ return result;
+}
+
+TString GetModuleFileNameOSX() {
+ Dl_info module_info;
+ if (dladdr(reinterpret_cast<void*> (GetModuleFileNameOSX),
+ &module_info) == 0) {
+ // Failed to find the symbol we asked for.
+ return std::string();
+ }
+ return TString(module_info.dli_fname);
+}
+
+TString MacPlatform::GetModuleFileName() {
+ TString result;
+ DynamicBuffer<TCHAR> buffer(MAX_PATH);
+ uint32_t size = buffer.GetSize();
+
+ if (_NSGetExecutablePath(buffer.GetData(), &size) == 0) {
+ result = FileSystemStringToString(buffer.GetData());
+ }
+
+ return result;
+}
+
+bool MacPlatform::IsMainThread() {
+ bool result = (pthread_main_np() == 1);
+ return result;
+}
+
+TPlatformNumber MacPlatform::GetMemorySize() {
+ unsigned long long memory = [[NSProcessInfo processInfo] physicalMemory];
+
+ // Convert from bytes to megabytes.
+ TPlatformNumber result = memory / 1048576;
+
+ return result;
+}
+
+std::map<TString, TString> MacPlatform::GetKeys() {
+ std::map<TString, TString> keys;
+
+ if (UsePListForConfigFile() == false) {
+ return Platform::GetKeys();
+ } else {
+ keys.insert(std::map<TString, TString>::value_type(CONFIG_VERSION,
+ _T("app.version")));
+ keys.insert(std::map<TString, TString>::value_type(CONFIG_MAINJAR_KEY,
+ _T("JavaMainJarName")));
+ keys.insert(std::map<TString, TString>::value_type(CONFIG_MAINMODULE_KEY,
+ _T("JavaMainModuleName")));
+ keys.insert(std::map<TString, TString>::value_type(
+ CONFIG_MAINCLASSNAME_KEY, _T("JavaMainClassName")));
+ keys.insert(std::map<TString, TString>::value_type(
+ CONFIG_CLASSPATH_KEY, _T("JavaAppClasspath")));
+ keys.insert(std::map<TString, TString>::value_type(APP_NAME_KEY,
+ _T("CFBundleName")));
+ keys.insert(std::map<TString, TString>::value_type(JAVA_RUNTIME_KEY,
+ _T("JavaRuntime")));
+ keys.insert(std::map<TString, TString>::value_type(JPACKAGE_APP_DATA_DIR,
+ _T("CFBundleIdentifier")));
+
+ keys.insert(std::map<TString, TString>::value_type(CONFIG_SPLASH_KEY,
+ _T("app.splash")));
+ keys.insert(std::map<TString, TString>::value_type(CONFIG_APP_MEMORY,
+ _T("app.memory")));
+ keys.insert(std::map<TString, TString>::value_type(CONFIG_APP_DEBUG,
+ _T("app.debug")));
+ keys.insert(std::map<TString, TString>::value_type(
+ CONFIG_APPLICATION_INSTANCE, _T("app.application.instance")));
+
+ keys.insert(std::map<TString, TString>::value_type(
+ CONFIG_SECTION_APPLICATION, _T("Application")));
+ keys.insert(std::map<TString, TString>::value_type(
+ CONFIG_SECTION_JAVAOPTIONS, _T("JavaOptions")));
+ keys.insert(std::map<TString, TString>::value_type(
+ CONFIG_SECTION_APPCDSJAVAOPTIONS, _T("AppCDSJavaOptions")));
+ keys.insert(std::map<TString, TString>::value_type(
+ CONFIG_SECTION_APPCDSGENERATECACHEJAVAOPTIONS,
+ _T("AppCDSGenerateCacheJavaOptions")));
+ keys.insert(std::map<TString, TString>::value_type(
+ CONFIG_SECTION_ARGOPTIONS, _T("ArgOptions")));
+ }
+
+ return keys;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/macosx/native/libapplauncher/PlatformDefs.h Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 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.
+ */
+
+#ifndef PLATFORM_DEFS_H
+#define PLATFORM_DEFS_H
+
+#include <errno.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <dlfcn.h>
+#include <libgen.h>
+#include <string>
+
+using namespace std;
+
+#ifndef MAC
+#define MAC
+#endif
+
+#define _T(x) x
+
+typedef char TCHAR;
+typedef std::string TString;
+#define StringLength strlen
+
+typedef unsigned long DWORD;
+
+#define TRAILING_PATHSEPARATOR '/'
+#define BAD_TRAILING_PATHSEPARATOR '\\'
+#define PATH_SEPARATOR ':'
+#define BAD_PATH_SEPARATOR ';'
+#define MAX_PATH 1000
+
+typedef long TPlatformNumber;
+typedef pid_t TProcessID;
+
+#define HMODULE void*
+
+typedef void* Module;
+typedef void* Procedure;
+
+
+// StringToFileSystemString is a stack object. It's usage is
+// simply inline to convert a
+// TString to a file system string. Example:
+//
+// return dlopen(StringToFileSystemString(FileName), RTLD_LAZY);
+//
+class StringToFileSystemString {
+ // Prohibit Heap-Based StringToFileSystemString
+private:
+ static void *operator new(size_t size);
+ static void operator delete(void *ptr);
+
+private:
+ TCHAR* FData;
+ bool FRelease;
+
+public:
+ StringToFileSystemString(const TString &value);
+ ~StringToFileSystemString();
+
+ operator TCHAR* ();
+};
+
+
+// FileSystemStringToString is a stack object. It's usage is
+// simply inline to convert a
+// file system string to a TString. Example:
+//
+// DynamicBuffer<TCHAR> buffer(MAX_PATH);
+// if (readlink("/proc/self/exe", buffer.GetData(), MAX_PATH) != -1)
+// result = FileSystemStringToString(buffer.GetData());
+//
+class FileSystemStringToString {
+ // Prohibit Heap-Based FileSystemStringToString
+private:
+ static void *operator new(size_t size);
+ static void operator delete(void *ptr);
+
+private:
+ TString FData;
+
+public:
+ FileSystemStringToString(const TCHAR* value);
+
+ operator TString ();
+};
+
+#endif // PLATFORM_DEFS_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/AbstractAppImageBuilder.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,242 @@
+/*
+ * Copyright (c) 2015, 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.jpackage.internal;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PrintStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.text.MessageFormat;
+import java.util.List;
+import java.util.Map;
+import java.util.ResourceBundle;
+import java.util.ArrayList;
+
+import jdk.jpackage.internal.resources.ResourceLocator;
+
+import static jdk.jpackage.internal.StandardBundlerParam.*;
+
+/*
+ * AbstractAppImageBuilder
+ * This is sub-classed by each of the platform dependent AppImageBuilder
+ * classes, and contains resource processing code common to all platforms.
+ */
+
+public abstract class AbstractAppImageBuilder {
+
+ private static final ResourceBundle I18N = ResourceBundle.getBundle(
+ "jdk.jpackage.internal.resources.MainResources");
+
+ private final Path root;
+
+ public AbstractAppImageBuilder(Map<String, Object> unused, Path root) {
+ this.root = root;
+ }
+
+ public InputStream getResourceAsStream(String name) {
+ return ResourceLocator.class.getResourceAsStream(name);
+ }
+
+ public abstract void prepareApplicationFiles() throws IOException;
+ public abstract void prepareJreFiles() throws IOException;
+ public abstract Path getAppDir();
+ public abstract Path getAppModsDir();
+
+ public Path getRoot() {
+ return this.root;
+ }
+
+ protected void copyEntry(Path appDir, File srcdir, String fname)
+ throws IOException {
+ Path dest = appDir.resolve(fname);
+ Files.createDirectories(dest.getParent());
+ File src = new File(srcdir, fname);
+ if (src.isDirectory()) {
+ IOUtils.copyRecursive(src.toPath(), dest);
+ } else {
+ Files.copy(src.toPath(), dest);
+ }
+ }
+
+ protected InputStream locateResource(String publicName, String category,
+ String defaultName, File customFile,
+ boolean verbose, File publicRoot) throws IOException {
+ InputStream is = null;
+ boolean customFromClasspath = false;
+ boolean customFromFile = false;
+ if (publicName != null) {
+ if (publicRoot != null) {
+ File publicResource = new File(publicRoot, publicName);
+ if (publicResource.exists() && publicResource.isFile()) {
+ is = new FileInputStream(publicResource);
+ }
+ } else {
+ is = getResourceAsStream(publicName);
+ }
+ customFromClasspath = (is != null);
+ }
+ if (is == null && customFile != null) {
+ is = new FileInputStream(customFile);
+ customFromFile = (is != null);
+ }
+ if (is == null && defaultName != null) {
+ is = getResourceAsStream(defaultName);
+ }
+ if (verbose) {
+ String msg = null;
+ if (customFromClasspath) {
+ msg = MessageFormat.format(I18N.getString(
+ "message.using-custom-resource"),
+ category == null ? "" : "[" + category + "] ", publicName);
+ } else if (customFromFile) {
+ msg = MessageFormat.format(I18N.getString(
+ "message.using-custom-resource-from-file"),
+ category == null ? "" : "[" + category + "] ",
+ customFile.getAbsoluteFile());
+ } else if (is != null) {
+ msg = MessageFormat.format(I18N.getString(
+ "message.using-default-resource"),
+ defaultName,
+ category == null ? "" : "[" + category + "] ",
+ publicName);
+ } else {
+ msg = MessageFormat.format(I18N.getString(
+ "message.no-default-resource"),
+ defaultName == null ? "" : defaultName,
+ category == null ? "" : "[" + category + "] ",
+ publicName);
+ }
+ if (msg != null) {
+ Log.verbose(msg);
+ }
+ }
+ return is;
+ }
+
+
+ protected String preprocessTextResource(String publicName, String category,
+ String defaultName, Map<String, String> pairs,
+ boolean verbose, File publicRoot) throws IOException {
+ InputStream inp = locateResource(publicName, category,
+ defaultName, null, verbose, publicRoot);
+ if (inp == null) {
+ throw new RuntimeException(
+ "Module corrupt? No "+defaultName+" resource!");
+ }
+
+ try (InputStream is = inp) {
+ //read fully into memory
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ byte[] buffer = new byte[1024];
+ int length;
+ while ((length = is.read(buffer)) != -1) {
+ baos.write(buffer, 0, length);
+ }
+
+ //substitute
+ String result = new String(baos.toByteArray());
+ for (Map.Entry<String, String> e : pairs.entrySet()) {
+ if (e.getValue() != null) {
+ result = result.replace(e.getKey(), e.getValue());
+ }
+ }
+ return result;
+ }
+ }
+
+ public void writeCfgFile(Map<String, ? super Object> params,
+ File cfgFileName, String runtimeLocation) throws IOException {
+ 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);
+
+ try (PrintStream out = new PrintStream(cfgFileName)) {
+
+ out.println("[Application]");
+ out.println("app.name=" + APP_NAME.fetchFrom(params));
+ out.println("app.version=" + VERSION.fetchFrom(params));
+ out.println("app.runtime=" + runtimeLocation);
+ out.println("app.identifier=" + IDENTIFIER.fetchFrom(params));
+ out.println("app.classpath=" + CLASSPATH.fetchFrom(params));
+
+ // 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);
+ } else {
+ String mainClass = JLinkBundlerHelper.getMainClass(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) {
+ out.println("app.mainjar="
+ + mainJar.toPath().getFileName().toString());
+ }
+ if (mainClass != null) {
+ out.println("app.mainclass="
+ + mainClass.replaceAll("\\.", "/"));
+ }
+ }
+
+ out.println();
+ out.println("[JavaOptions]");
+ List<String> jvmargs = JAVA_OPTIONS.fetchFrom(params);
+ for (String arg : jvmargs) {
+ out.println(arg);
+ }
+ Path modsDir = getAppModsDir();
+ if (modsDir != null && modsDir.toFile().exists()) {
+ out.println("--module-path");
+ out.println(getAppDir().relativize(modsDir));
+ }
+
+ out.println();
+ out.println("[ArgOptions]");
+ List<String> args = ARGUMENTS.fetchFrom(params);
+ for (String arg : args) {
+ if (arg.endsWith("=") &&
+ (arg.indexOf("=") == arg.lastIndexOf("="))) {
+ out.print(arg.substring(0, arg.length() - 1));
+ out.println("\\=");
+ } else {
+ out.println(arg);
+ }
+ }
+ }
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/AbstractBundler.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,188 @@
+/*
+ * Copyright (c) 2014, 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.jpackage.internal;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.BufferedInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.StandardCopyOption;
+import java.nio.file.Files;
+import java.text.MessageFormat;
+import java.util.Map;
+import java.util.ResourceBundle;
+
+import jdk.jpackage.internal.resources.ResourceLocator;
+
+/**
+ * AbstractBundler
+ *
+ * This is the base class all Bundlers extend from.
+ * It contains methods and parameters common to all Bundlers.
+ * The concrete implementations are in the platform specific Bundlers.
+ */
+public abstract class AbstractBundler implements Bundler {
+
+ private static final ResourceBundle I18N = ResourceBundle.getBundle(
+ "jdk.jpackage.internal.resources.MainResources");
+
+ static final BundlerParamInfo<File> IMAGES_ROOT =
+ new StandardBundlerParam<>(
+ "imagesRoot",
+ File.class,
+ params -> new File(
+ StandardBundlerParam.TEMP_ROOT.fetchFrom(params), "images"),
+ (s, p) -> null);
+
+ public InputStream getResourceAsStream(String name) {
+ return ResourceLocator.class.getResourceAsStream(name);
+ }
+
+ protected void fetchResource(String publicName, String category,
+ String defaultName, File result, boolean verbose, File publicRoot)
+ throws IOException {
+
+ try (InputStream is = streamResource(publicName, category,
+ defaultName, verbose, publicRoot)) {
+ if (is != null) {
+ Files.copy(is, result.toPath(),
+ StandardCopyOption.REPLACE_EXISTING);
+ } else {
+ if (verbose) {
+ Log.verbose(MessageFormat.format(I18N.getString(
+ "message.no-default-resource"),
+ defaultName == null ? "" : defaultName,
+ category == null ? "" : "[" + category + "] ",
+ publicName));
+ }
+ }
+ }
+ }
+
+ protected void fetchResource(String publicName, String category,
+ File defaultFile, File result, boolean verbose, File publicRoot)
+ throws IOException {
+
+ try (InputStream is = streamResource(publicName, category,
+ null, verbose, publicRoot)) {
+ if (is != null) {
+ Files.copy(is, result.toPath());
+ } else {
+ IOUtils.copyFile(defaultFile, result);
+ if (verbose) {
+ Log.verbose(MessageFormat.format(I18N.getString(
+ "message.using-custom-resource-from-file"),
+ category == null ? "" : "[" + category + "] ",
+ defaultFile.getAbsoluteFile()));
+ }
+ }
+ }
+ }
+
+ private InputStream streamResource(String publicName, String category,
+ String defaultName, boolean verbose, File publicRoot)
+ throws IOException {
+ boolean custom = false;
+ InputStream is = null;
+ if (publicName != null) {
+ if (publicRoot != null) {
+ File publicResource = new File(publicRoot, publicName);
+ if (publicResource.exists() && publicResource.isFile()) {
+ is = new BufferedInputStream(
+ new FileInputStream(publicResource));
+ }
+ } else {
+ is = getResourceAsStream(publicName);
+ }
+ custom = (is != null);
+ }
+ if (is == null && defaultName != null) {
+ is = getResourceAsStream(defaultName);
+ }
+ if (verbose && is != null) {
+ String msg = null;
+ if (custom) {
+ msg = MessageFormat.format(I18N.getString(
+ "message.using-custom-resource"),
+ category == null ?
+ "" : "[" + category + "] ", publicName);
+ } else {
+ msg = MessageFormat.format(I18N.getString(
+ "message.using-default-resource"),
+ defaultName == null ? "" : defaultName,
+ category == null ? "" : "[" + category + "] ",
+ publicName);
+ }
+ Log.verbose(msg);
+ }
+ return is;
+ }
+
+ protected String preprocessTextResource(String publicName, String category,
+ String defaultName, Map<String, String> pairs,
+ boolean verbose, File publicRoot) throws IOException {
+ InputStream inp = streamResource(
+ publicName, category, defaultName, verbose, publicRoot);
+ if (inp == null) {
+ throw new RuntimeException(
+ "Jar corrupt? No " + defaultName + " resource!");
+ }
+
+ // read fully into memory
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ byte[] buffer = new byte[1024];
+ int length;
+ while ((length = inp.read(buffer)) != -1) {
+ baos.write(buffer, 0, length);
+ }
+
+ // substitute
+ String result = new String(baos.toByteArray());
+ for (Map.Entry<String, String> e : pairs.entrySet()) {
+ if (e.getValue() != null) {
+ result = result.replace(e.getKey(), e.getValue());
+ }
+ }
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return getName();
+ }
+
+ @Override
+ public void cleanup(Map<String, ? super Object> params) {
+ try {
+ IOUtils.deleteRecursive(
+ StandardBundlerParam.TEMP_ROOT.fetchFrom(params));
+ } catch (IOException e) {
+ Log.debug(e.getMessage());
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/AbstractImageBundler.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2015, 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.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.jpackage.internal.StandardBundlerParam.*;
+
+/**
+ * AbstractImageBundler
+ *
+ * This is the base class for each of the Application Image Bundlers.
+ *
+ * It contains methods and parameters common to all Image Bundlers.
+ *
+ * Application Image Bundlers are created in "create-app-image" mode,
+ * or as an intermediate step in "create-installer" mode.
+ *
+ * The concrete implementations are in the platform specific Bundlers.
+ */
+public abstract class AbstractImageBundler extends AbstractBundler {
+
+ private static final ResourceBundle I18N = ResourceBundle.getBundle(
+ "jdk.jpackage.internal.resources.MainResources");
+
+ public void imageBundleValidation(Map<String, ? super Object> params)
+ throws ConfigException {
+ StandardBundlerParam.validateMainClassInfoFromAppResources(params);
+
+ }
+
+ protected File createRoot(Map<String, ? super Object> params,
+ File outputDirectory, boolean dependentTask, String name)
+ throws PackagerException {
+
+ IOUtils.writableOutputDir(outputDirectory.toPath());
+
+ if (!dependentTask) {
+ Log.verbose(MessageFormat.format(
+ I18N.getString("message.creating-app-bundle"),
+ 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);
+
+ if (rootDirectory.exists()) {
+ throw new PackagerException("error.root-exists",
+ rootDirectory.getAbsolutePath());
+ }
+
+ rootDirectory.mkdirs();
+
+ return rootDirectory;
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/AddLauncherArguments.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,172 @@
+/*
+ * 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.jpackage.internal;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.io.File;
+import jdk.jpackage.internal.Arguments.CLIOptions;
+
+/*
+ * AddLauncherArguments
+ *
+ * Processes a add-launcher properties file to create the Map of
+ * bundle params applicable to the add-launcher:
+ *
+ * BundlerParams p = (new AddLauncherArguments(file)).getLauncherMap();
+ *
+ * A add-launcher is another executable program generated by either the
+ * create-app-image mode or the create-installer mode.
+ * The add-launcher may be the same program with different configuration,
+ * or a completely different program created from the same files.
+ *
+ * There may be multiple add-launchers, each created by using the
+ * command line arg "--add-launcher <file path>
+ *
+ * The add-launcher properties file may have any of:
+ *
+ * appVersion
+ * module
+ * main-jar
+ * main-class
+ * icon
+ * arguments
+ * java-options
+ * win-console
+ *
+ */
+class AddLauncherArguments {
+
+ private final String name;
+ private final String filename;
+ private Map<String, String> allArgs;
+ private Map<String, ? super Object> bundleParams;
+
+ AddLauncherArguments(String name, String filename) {
+ this.name = name;
+ this.filename = filename;
+ }
+
+ private void initLauncherMap() {
+ if (bundleParams != null) {
+ return;
+ }
+
+ allArgs = Arguments.getPropertiesFromFile(filename);
+ allArgs.put(CLIOptions.NAME.getId(), name);
+
+ bundleParams = new HashMap<>();
+ String mainJar = getOptionValue(CLIOptions.MAIN_JAR);
+ String mainClass = getOptionValue(CLIOptions.APPCLASS);
+ String module = getOptionValue(CLIOptions.MODULE);
+
+ if (module != null && mainClass != null) {
+ putUnlessNull(bundleParams, CLIOptions.MODULE.getId(),
+ module + "/" + mainClass);
+ } else if (module != null) {
+ putUnlessNull(bundleParams, CLIOptions.MODULE.getId(),
+ module);
+ } else {
+ putUnlessNull(bundleParams, CLIOptions.MAIN_JAR.getId(),
+ mainJar);
+ putUnlessNull(bundleParams, CLIOptions.APPCLASS.getId(),
+ mainClass);
+ }
+
+ putUnlessNull(bundleParams, CLIOptions.NAME.getId(),
+ getOptionValue(CLIOptions.NAME));
+
+ putUnlessNull(bundleParams, CLIOptions.VERSION.getId(),
+ getOptionValue(CLIOptions.VERSION));
+
+ putUnlessNull(bundleParams,
+ CLIOptions.WIN_CONSOLE_HINT.getId(),
+ getOptionValue(CLIOptions.WIN_CONSOLE_HINT));
+
+ String value = getOptionValue(CLIOptions.ICON);
+ putUnlessNull(bundleParams, CLIOptions.ICON.getId(),
+ (value == null) ? null : new File(value));
+
+ String argumentStr = getOptionValue(CLIOptions.ARGUMENTS);
+ putUnlessNullOrEmpty(bundleParams,
+ CLIOptions.ARGUMENTS.getId(),
+ Arguments.getArgumentList(argumentStr));
+
+ String jvmargsStr = getOptionValue(CLIOptions.JAVA_OPTIONS);
+ putUnlessNullOrEmpty(bundleParams,
+ CLIOptions.JAVA_OPTIONS.getId(),
+ Arguments.getArgumentList(jvmargsStr));
+ }
+
+ private String getOptionValue(CLIOptions option) {
+ if (option == null || allArgs == null) {
+ return null;
+ }
+
+ String id = option.getId();
+
+ if (allArgs.containsKey(id)) {
+ return allArgs.get(id);
+ }
+
+ return null;
+ }
+
+ Map<String, ? super Object> getLauncherMap() {
+ initLauncherMap();
+ return bundleParams;
+ }
+
+ private void putUnlessNull(Map<String, ? super Object> params,
+ String param, Object value) {
+ if (value != null) {
+ params.put(param, value);
+ }
+ }
+
+ private void putUnlessNullOrEmpty(Map<String, ? super Object> params,
+ String param, Collection<?> value) {
+ if (value != null && !value.isEmpty()) {
+ params.put(param, value);
+ }
+ }
+
+ static Map<String, ? super Object> merge(
+ Map<String, ? super Object> original,
+ Map<String, ? super Object> additional) {
+ Map<String, ? super Object> tmp = new HashMap<>(original);
+ if (additional.containsKey("module")) {
+ tmp.remove("main-jar");
+ tmp.remove("main-class");
+ } else if (additional.containsKey("main-jar")) {
+ tmp.remove("module");
+ }
+ tmp.putAll(additional);
+ return tmp;
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ArgAction.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,31 @@
+/*
+ * 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.jpackage.internal;
+
+@FunctionalInterface
+interface ArgAction {
+ void execute();
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/Arguments.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,788 @@
+/*
+ * 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.jpackage.internal;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.Properties;
+import java.util.ResourceBundle;
+import java.util.jar.Attributes;
+import java.util.jar.JarFile;
+import java.util.jar.Manifest;
+import java.util.stream.Stream;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Arguments
+ *
+ * This class encapsulates and processes the command line arguments,
+ * in effect, implementing all the work of jpackage tool.
+ *
+ * The primary entry point, processArguments():
+ * Processes and validates command line arguments, constructing DeployParams.
+ * Validates the DeployParams, and generate the BundleParams.
+ * Generates List of Bundlers from BundleParams valid for this platform.
+ * Executes each Bundler in the list.
+ */
+public class Arguments {
+ private static final ResourceBundle I18N = ResourceBundle.getBundle(
+ "jdk.jpackage.internal.resources.MainResources");
+
+ private static final String FA_EXTENSIONS = "extension";
+ private static final String FA_CONTENT_TYPE = "mime-type";
+ private static final String FA_DESCRIPTION = "description";
+ private static final String FA_ICON = "icon";
+
+ // regexp for parsing args (for example, for additional launchers)
+ private static Pattern pattern = Pattern.compile(
+ "(?:(?:([\"'])(?:\\\\\\1|.)*?(?:\\1|$))|(?:\\\\[\"'\\s]|[^\\s]))++");
+
+ private DeployParams deployParams = null;
+ private String packageType = null;
+
+ private int pos = 0;
+ private List<String> argList = null;
+
+ private List<CLIOptions> allOptions = null;
+
+ private String input = null;
+ private String output = null;
+
+ private boolean hasMainJar = false;
+ private boolean hasMainClass = false;
+ private boolean hasMainModule = false;
+ public boolean userProvidedBuildRoot = false;
+
+ private String buildRoot = null;
+ private String mainJarPath = null;
+
+ private static boolean runtimeInstaller = false;
+
+ private List<AddLauncherArguments> addLaunchers = null;
+
+ private static Map<String, CLIOptions> argIds = new HashMap<>();
+ private static Map<String, CLIOptions> argShortIds = new HashMap<>();
+
+ static {
+ // init maps for parsing arguments
+ (EnumSet.allOf(CLIOptions.class)).forEach(option -> {
+ argIds.put(option.getIdWithPrefix(), option);
+ if (option.getShortIdWithPrefix() != null) {
+ argShortIds.put(option.getShortIdWithPrefix(), option);
+ }
+ });
+ }
+
+ public Arguments(String[] args) {
+ argList = new ArrayList<String>(args.length);
+ for (String arg : args) {
+ argList.add(arg);
+ }
+ Log.debug ("\njpackage argument list: \n" + argList + "\n");
+ pos = 0;
+
+ deployParams = new DeployParams();
+
+ packageType = null;
+
+ allOptions = new ArrayList<>();
+
+ addLaunchers = new ArrayList<>();
+ }
+
+ // CLIOptions is public for DeployParamsTest
+ public enum CLIOptions {
+ PACKAGE_TYPE("package-type", OptionCategories.PROPERTY, () -> {
+ context().packageType = popArg();
+ context().deployParams.setTargetFormat(context().packageType);
+ }),
+
+ INPUT ("input", "i", OptionCategories.PROPERTY, () -> {
+ context().input = popArg();
+ setOptionValue("input", context().input);
+ }),
+
+ OUTPUT ("output", "o", OptionCategories.PROPERTY, () -> {
+ context().output = popArg();
+ context().deployParams.setOutput(new File(context().output));
+ }),
+
+ DESCRIPTION ("description", "d", OptionCategories.PROPERTY),
+
+ VENDOR ("vendor", OptionCategories.PROPERTY),
+
+ APPCLASS ("main-class", OptionCategories.PROPERTY, () -> {
+ context().hasMainClass = true;
+ setOptionValue("main-class", popArg());
+ }),
+
+ NAME ("name", "n", OptionCategories.PROPERTY),
+
+ IDENTIFIER ("identifier", OptionCategories.PROPERTY),
+
+ VERBOSE ("verbose", OptionCategories.PROPERTY, () -> {
+ setOptionValue("verbose", true);
+ Log.setVerbose(true);
+ }),
+
+ RESOURCE_DIR("resource-dir",
+ OptionCategories.PROPERTY, () -> {
+ String resourceDir = popArg();
+ setOptionValue("resource-dir", resourceDir);
+ }),
+
+ ARGUMENTS ("arguments", OptionCategories.PROPERTY, () -> {
+ List<String> arguments = getArgumentList(popArg());
+ setOptionValue("arguments", arguments);
+ }),
+
+ ICON ("icon", OptionCategories.PROPERTY),
+
+ COPYRIGHT ("copyright", OptionCategories.PROPERTY),
+
+ LICENSE_FILE ("license-file", OptionCategories.PROPERTY),
+
+ VERSION ("app-version", OptionCategories.PROPERTY),
+
+ JAVA_OPTIONS ("java-options", OptionCategories.PROPERTY, () -> {
+ List<String> args = getArgumentList(popArg());
+ args.forEach(a -> setOptionValue("java-options", a));
+ }),
+
+ FILE_ASSOCIATIONS ("file-associations",
+ OptionCategories.PROPERTY, () -> {
+ Map<String, ? super Object> args = new HashMap<>();
+
+ // load .properties file
+ Map<String, String> initialMap = getPropertiesFromFile(popArg());
+
+ String ext = initialMap.get(FA_EXTENSIONS);
+ if (ext != null) {
+ args.put(StandardBundlerParam.FA_EXTENSIONS.getID(), ext);
+ }
+
+ String type = initialMap.get(FA_CONTENT_TYPE);
+ if (type != null) {
+ args.put(StandardBundlerParam.FA_CONTENT_TYPE.getID(), type);
+ }
+
+ String desc = initialMap.get(FA_DESCRIPTION);
+ if (desc != null) {
+ args.put(StandardBundlerParam.FA_DESCRIPTION.getID(), desc);
+ }
+
+ String icon = initialMap.get(FA_ICON);
+ if (icon != null) {
+ args.put(StandardBundlerParam.FA_ICON.getID(), icon);
+ }
+
+ ArrayList<Map<String, ? super Object>> associationList =
+ new ArrayList<Map<String, ? super Object>>();
+
+ associationList.add(args);
+
+ // check that we really add _another_ value to the list
+ setOptionValue("file-associations", associationList);
+
+ }),
+
+ ADD_LAUNCHER ("add-launcher",
+ OptionCategories.PROPERTY, () -> {
+ String spec = popArg();
+ String name = null;
+ String filename = spec;
+ if (spec.contains("=")) {
+ String[] values = spec.split("=", 2);
+ name = values[0];
+ filename = values[1];
+ }
+ context().addLaunchers.add(
+ new AddLauncherArguments(name, filename));
+ }),
+
+ TEMP_ROOT ("temp-root", OptionCategories.PROPERTY, () -> {
+ context().buildRoot = popArg();
+ context().userProvidedBuildRoot = true;
+ setOptionValue("temp-root", context().buildRoot);
+ }),
+
+ INSTALL_DIR ("install-dir", OptionCategories.PROPERTY),
+
+ PREDEFINED_APP_IMAGE ("app-image", OptionCategories.PROPERTY),
+
+ PREDEFINED_RUNTIME_IMAGE ("runtime-image", OptionCategories.PROPERTY),
+
+ MAIN_JAR ("main-jar", OptionCategories.PROPERTY, () -> {
+ context().mainJarPath = popArg();
+ context().hasMainJar = true;
+ setOptionValue("main-jar", context().mainJarPath);
+ }),
+
+ MODULE ("module", "m", OptionCategories.MODULAR, () -> {
+ context().hasMainModule = true;
+ setOptionValue("module", popArg());
+ }),
+
+ ADD_MODULES ("add-modules", OptionCategories.MODULAR),
+
+ MODULE_PATH ("module-path", "p", OptionCategories.MODULAR),
+
+ MAC_SIGN ("mac-sign", "s", OptionCategories.PLATFORM_MAC, () -> {
+ setOptionValue("mac-sign", true);
+ }),
+
+ MAC_BUNDLE_NAME ("mac-bundle-name", OptionCategories.PLATFORM_MAC),
+
+ MAC_BUNDLE_IDENTIFIER("mac-bundle-identifier",
+ OptionCategories.PLATFORM_MAC),
+
+ MAC_APP_STORE_CATEGORY ("mac-app-store-category",
+ OptionCategories.PLATFORM_MAC),
+
+ MAC_BUNDLE_SIGNING_PREFIX ("mac-bundle-signing-prefix",
+ OptionCategories.PLATFORM_MAC),
+
+ MAC_SIGNING_KEY_NAME ("mac-signing-key-user-name",
+ OptionCategories.PLATFORM_MAC),
+
+ MAC_SIGNING_KEYCHAIN ("mac-signing-keychain",
+ OptionCategories.PLATFORM_MAC),
+
+ MAC_APP_STORE_ENTITLEMENTS ("mac-app-store-entitlements",
+ OptionCategories.PLATFORM_MAC),
+
+ WIN_MENU_HINT ("win-menu", OptionCategories.PLATFORM_WIN, () -> {
+ setOptionValue("win-menu", true);
+ }),
+
+ WIN_MENU_GROUP ("win-menu-group", OptionCategories.PLATFORM_WIN),
+
+ WIN_SHORTCUT_HINT ("win-shortcut",
+ OptionCategories.PLATFORM_WIN, () -> {
+ setOptionValue("win-shortcut", true);
+ }),
+
+ WIN_PER_USER_INSTALLATION ("win-per-user-install",
+ OptionCategories.PLATFORM_WIN, () -> {
+ setOptionValue("win-per-user-install", false);
+ }),
+
+ WIN_DIR_CHOOSER ("win-dir-chooser",
+ OptionCategories.PLATFORM_WIN, () -> {
+ setOptionValue("win-dir-chooser", true);
+ }),
+
+ WIN_REGISTRY_NAME ("win-registry-name", OptionCategories.PLATFORM_WIN),
+
+ WIN_UPGRADE_UUID ("win-upgrade-uuid",
+ OptionCategories.PLATFORM_WIN),
+
+ WIN_CONSOLE_HINT ("win-console", OptionCategories.PLATFORM_WIN, () -> {
+ setOptionValue("win-console", true);
+ }),
+
+ LINUX_BUNDLE_NAME ("linux-bundle-name",
+ OptionCategories.PLATFORM_LINUX),
+
+ LINUX_DEB_MAINTAINER ("linux-deb-maintainer",
+ OptionCategories.PLATFORM_LINUX),
+
+ LINUX_RPM_LICENSE_TYPE ("linux-rpm-license-type",
+ OptionCategories.PLATFORM_LINUX),
+
+ LINUX_PACKAGE_DEPENDENCIES ("linux-package-deps",
+ OptionCategories.PLATFORM_LINUX),
+
+ LINUX_MENU_GROUP ("linux-menu-group", OptionCategories.PLATFORM_LINUX);
+
+ private final String id;
+ private final String shortId;
+ private final OptionCategories category;
+ private final ArgAction action;
+ private static Arguments argContext;
+
+ private CLIOptions(String id, OptionCategories category) {
+ this(id, null, category, null);
+ }
+
+ private CLIOptions(String id, String shortId,
+ OptionCategories category) {
+ this(id, shortId, category, null);
+ }
+
+ private CLIOptions(String id,
+ OptionCategories category, ArgAction action) {
+ this(id, null, category, action);
+ }
+
+ private CLIOptions(String id, String shortId,
+ OptionCategories category, ArgAction action) {
+ this.id = id;
+ this.shortId = shortId;
+ this.action = action;
+ this.category = category;
+ }
+
+ static void setContext(Arguments context) {
+ argContext = context;
+ }
+
+ public static Arguments context() {
+ if (argContext != null) {
+ return argContext;
+ } else {
+ throw new RuntimeException("Argument context is not set.");
+ }
+ }
+
+ public String getId() {
+ return this.id;
+ }
+
+ String getIdWithPrefix() {
+ return "--" + this.id;
+ }
+
+ String getShortIdWithPrefix() {
+ return this.shortId == null ? null : "-" + this.shortId;
+ }
+
+ void execute() {
+ if (action != null) {
+ action.execute();
+ } else {
+ defaultAction();
+ }
+ }
+
+ private void defaultAction() {
+ context().deployParams.addBundleArgument(id, popArg());
+ }
+
+ private static void setOptionValue(String option, Object value) {
+ context().deployParams.addBundleArgument(option, value);
+ }
+
+ private static String popArg() {
+ nextArg();
+ return (context().pos >= context().argList.size()) ?
+ "" : context().argList.get(context().pos);
+ }
+
+ private static String getArg() {
+ return (context().pos >= context().argList.size()) ?
+ "" : context().argList.get(context().pos);
+ }
+
+ private static void nextArg() {
+ context().pos++;
+ }
+
+ private static boolean hasNextArg() {
+ return context().pos < context().argList.size();
+ }
+ }
+
+ enum OptionCategories {
+ MODULAR,
+ PROPERTY,
+ PLATFORM_MAC,
+ PLATFORM_WIN,
+ PLATFORM_LINUX;
+ }
+
+ public boolean processArguments() {
+ try {
+
+ // init context of arguments
+ CLIOptions.setContext(this);
+
+ // parse cmd line
+ String arg;
+ CLIOptions option;
+ for (; CLIOptions.hasNextArg(); CLIOptions.nextArg()) {
+ arg = CLIOptions.getArg();
+ if ((option = toCLIOption(arg)) != null) {
+ // found a CLI option
+ allOptions.add(option);
+ option.execute();
+ } else {
+ throw new PackagerException("ERR_InvalidOption", arg);
+ }
+ }
+
+ if (hasMainJar && !hasMainClass) {
+ // try to get main-class from manifest
+ String mainClass = getMainClassFromManifest();
+ if (mainClass != null) {
+ CLIOptions.setOptionValue(
+ CLIOptions.APPCLASS.getId(), mainClass);
+ }
+ }
+
+ // display error for arguments that are not supported
+ // for current configuration.
+
+ validateArguments();
+
+ addResources(deployParams, input);
+
+ List<Map<String, ? super Object>> launchersAsMap =
+ new ArrayList<>();
+
+ for (AddLauncherArguments sl : addLaunchers) {
+ launchersAsMap.add(sl.getLauncherMap());
+ }
+
+ deployParams.addBundleArgument(
+ StandardBundlerParam.ADD_LAUNCHERS.getID(),
+ launchersAsMap);
+
+ // at this point deployParams should be already configured
+
+ deployParams.validate();
+
+ BundleParams bp = deployParams.getBundleParams();
+
+ // validate name(s)
+ ArrayList<String> usedNames = new ArrayList<String>();
+ usedNames.add(bp.getName()); // add main app name
+
+ for (AddLauncherArguments sl : addLaunchers) {
+ Map<String, ? super Object> slMap = sl.getLauncherMap();
+ String slName =
+ (String) slMap.get(Arguments.CLIOptions.NAME.getId());
+ if (slName == null) {
+ throw new PackagerException("ERR_NoAddLauncherName");
+ }
+ // same rules apply to additional launcher names as app name
+ DeployParams.validateName(slName, false);
+ for (String usedName : usedNames) {
+ if (slName.equals(usedName)) {
+ throw new PackagerException("ERR_NoUniqueName");
+ }
+ }
+ usedNames.add(slName);
+ }
+ if (runtimeInstaller && bp.getName() == null) {
+ throw new PackagerException("ERR_NoJreInstallerName");
+ }
+
+ generateBundle(bp.getBundleParamsAsMap());
+ return true;
+ } catch (Exception e) {
+ if (Log.isVerbose()) {
+ Log.verbose(e);
+ } else {
+ String msg1 = e.getMessage();
+ Log.error(msg1);
+ if (e.getCause() != null && e.getCause() != e) {
+ String msg2 = e.getCause().getMessage();
+ if (!msg1.contains(msg2)) {
+ Log.error(msg2);
+ }
+ }
+ }
+ return false;
+ }
+ }
+
+ private void validateArguments() throws PackagerException {
+ String packageType = deployParams.getTargetFormat();
+ String ptype = (packageType != null) ? packageType : "default";
+ boolean imageOnly = (packageType == null);
+ boolean hasAppImage = allOptions.contains(
+ CLIOptions.PREDEFINED_APP_IMAGE);
+ boolean hasRuntime = allOptions.contains(
+ CLIOptions.PREDEFINED_RUNTIME_IMAGE);
+ boolean installerOnly = !imageOnly && hasAppImage;
+ runtimeInstaller = !imageOnly && hasRuntime && !hasAppImage &&
+ !hasMainModule && !hasMainJar;
+
+ for (CLIOptions option : allOptions) {
+ if (!ValidOptions.checkIfSupported(option)) {
+ // includes option valid only on different platform
+ throw new PackagerException("ERR_UnsupportedOption",
+ option.getIdWithPrefix());
+ }
+ if (imageOnly) {
+ if (!ValidOptions.checkIfImageSupported(option)) {
+ throw new PackagerException("ERR_InvalidTypeOption",
+ option.getIdWithPrefix(), packageType);
+ }
+ } else if (installerOnly || runtimeInstaller) {
+ if (!ValidOptions.checkIfInstallerSupported(option)) {
+ if (runtimeInstaller) {
+ throw new PackagerException("ERR_NoInstallerEntryPoint",
+ option.getIdWithPrefix());
+ } else {
+ throw new PackagerException("ERR_InvalidTypeOption",
+ option.getIdWithPrefix(), ptype);
+ }
+ }
+ }
+ }
+ if (installerOnly && hasRuntime) {
+ // note --runtime-image is only for image or runtime installer.
+ throw new PackagerException("ERR_InvalidTypeOption",
+ CLIOptions.PREDEFINED_RUNTIME_IMAGE.getIdWithPrefix(),
+ ptype);
+ }
+ if (hasMainJar && hasMainModule) {
+ throw new PackagerException("ERR_BothMainJarAndModule");
+ }
+ if (imageOnly && !hasMainJar && !hasMainModule) {
+ throw new PackagerException("ERR_NoEntryPoint");
+ }
+ }
+
+ private jdk.jpackage.internal.Bundler getPlatformBundler() {
+ String bundleType = (packageType == null ? "IMAGE" : "INSTALLER");
+
+ for (jdk.jpackage.internal.Bundler bundler :
+ Bundlers.createBundlersInstance().getBundlers(bundleType)) {
+ if ((packageType == null) ||
+ packageType.equalsIgnoreCase(bundler.getID())) {
+ if (bundler.supported(runtimeInstaller)) {
+ return bundler;
+ }
+ }
+ }
+ return null;
+ }
+
+ private void generateBundle(Map<String,? super Object> params)
+ throws PackagerException {
+
+ boolean bundleCreated = false;
+
+ // the temp-root needs to be fetched from the params early,
+ // to prevent each copy of the params (such as may be used for
+ // additional launchers) from generating a separate temp-root when
+ // the default is used (the default is a new temp directory)
+ // The bundler.cleanup() below would not otherwise be able to
+ // clean these extra (and unneeded) temp directories.
+ StandardBundlerParam.TEMP_ROOT.fetchFrom(params);
+
+ // determine what bundler to run
+ jdk.jpackage.internal.Bundler bundler = getPlatformBundler();
+
+ if (bundler == null) {
+ throw new PackagerException("ERR_InvalidInstallerType",
+ deployParams.getTargetFormat());
+ }
+
+ Map<String, ? super Object> localParams = new HashMap<>(params);
+ try {
+ bundler.validate(localParams);
+ File result = bundler.execute(localParams, deployParams.outdir);
+ if (result == null) {
+ throw new PackagerException("MSG_BundlerFailed",
+ bundler.getID(), bundler.getName());
+ }
+ Log.verbose(MessageFormat.format(
+ I18N.getString("message.bundle-created"),
+ bundler.getName()));
+ } catch (ConfigException e) {
+ Log.debug(e);
+ if (e.getAdvice() != null) {
+ throw new PackagerException(e, "MSG_BundlerConfigException",
+ bundler.getName(), e.getMessage(), e.getAdvice());
+ } else {
+ throw new PackagerException(e,
+ "MSG_BundlerConfigExceptionNoAdvice",
+ bundler.getName(), e.getMessage());
+ }
+ } catch (RuntimeException re) {
+ Log.debug(re);
+ throw new PackagerException(re, "MSG_BundlerRuntimeException",
+ bundler.getName(), re.toString());
+ } finally {
+ if (userProvidedBuildRoot) {
+ Log.verbose(MessageFormat.format(
+ I18N.getString("message.debug-working-directory"),
+ (new File(buildRoot)).getAbsolutePath()));
+ } else {
+ // always clean up the temporary directory created
+ // when --temp-root option not used.
+ bundler.cleanup(localParams);
+ }
+ }
+ }
+
+ private void addResources(DeployParams deployParams,
+ String inputdir) 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();
+ }
+
+ static CLIOptions toCLIOption(String arg) {
+ CLIOptions option;
+ if ((option = argIds.get(arg)) == null) {
+ option = argShortIds.get(arg);
+ }
+ return option;
+ }
+
+ static Map<String, String> getPropertiesFromFile(String filename) {
+ Map<String, String> map = new HashMap<>();
+ // load properties file
+ File file = new File(filename);
+ Properties properties = new Properties();
+ try (FileInputStream in = new FileInputStream(file)) {
+ properties.load(in);
+ } catch (IOException e) {
+ Log.error("Exception: " + e.getMessage());
+ }
+
+ for (final String name: properties.stringPropertyNames()) {
+ map.put(name, properties.getProperty(name));
+ }
+
+ return map;
+ }
+
+ static List<String> getArgumentList(String inputString) {
+ List<String> list = new ArrayList<>();
+ if (inputString == null || inputString.isEmpty()) {
+ return list;
+ }
+
+ // The "pattern" regexp attempts to abide to the rule that
+ // strings are delimited by whitespace unless surrounded by
+ // quotes, then it is anything (including spaces) in the quotes.
+ Matcher m = pattern.matcher(inputString);
+ while (m.find()) {
+ String s = inputString.substring(m.start(), m.end()).trim();
+ // Ensure we do not have an empty string. trim() will take care of
+ // whitespace only strings. The regex preserves quotes and escaped
+ // chars so we need to clean them before adding to the List
+ if (!s.isEmpty()) {
+ list.add(unquoteIfNeeded(s));
+ }
+ }
+ return list;
+ }
+
+ private static String unquoteIfNeeded(String in) {
+ if (in == null) {
+ return null;
+ }
+
+ if (in.isEmpty()) {
+ return "";
+ }
+
+ // Use code points to preserve non-ASCII chars
+ StringBuilder sb = new StringBuilder();
+ int codeLen = in.codePointCount(0, in.length());
+ int quoteChar = -1;
+ for (int i = 0; i < codeLen; i++) {
+ int code = in.codePointAt(i);
+ if (code == '"' || code == '\'') {
+ // If quote is escaped make sure to copy it
+ if (i > 0 && in.codePointAt(i - 1) == '\\') {
+ sb.deleteCharAt(sb.length() - 1);
+ sb.appendCodePoint(code);
+ continue;
+ }
+ if (quoteChar != -1) {
+ if (code == quoteChar) {
+ // close quote, skip char
+ quoteChar = -1;
+ } else {
+ sb.appendCodePoint(code);
+ }
+ } else {
+ // opening quote, skip char
+ quoteChar = code;
+ }
+ } else {
+ sb.appendCodePoint(code);
+ }
+ }
+ return sb.toString();
+ }
+
+ private String getMainClassFromManifest() {
+ if (mainJarPath == null ||
+ input == null ) {
+ return null;
+ }
+
+ JarFile jf;
+ try {
+ File file = new File(input, mainJarPath);
+ if (!file.exists()) {
+ return null;
+ }
+ jf = new JarFile(file);
+ Manifest m = jf.getManifest();
+ Attributes attrs = (m != null) ? m.getMainAttributes() : null;
+ if (attrs != null) {
+ return attrs.getValue(Attributes.Name.MAIN_CLASS);
+ }
+ } catch (IOException ignore) {}
+ return null;
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/BasicBundlers.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2014, 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.jpackage.internal;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.ServiceLoader;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+/**
+ * BasicBundlers
+ *
+ * A basic bundlers collection that loads the default bundlers.
+ * Loads the common bundlers.
+ * <UL>
+ * <LI>Windows file image</LI>
+ * <LI>Mac .app</LI>
+ * <LI>Linux file image</LI>
+ * <LI>Windows MSI</LI>
+ * <LI>Windows EXE</LI>
+ * <LI>Mac DMG</LI>
+ * <LI>Mac PKG</LI>
+ * <LI>Linux DEB</LI>
+ * <LI>Linux RPM</LI>
+ *
+ * </UL>
+ */
+public class BasicBundlers implements Bundlers {
+
+ boolean defaultsLoaded = false;
+
+ private final Collection<Bundler> bundlers = new CopyOnWriteArrayList<>();
+
+ @Override
+ public Collection<Bundler> getBundlers() {
+ return Collections.unmodifiableCollection(bundlers);
+ }
+
+ @Override
+ public Collection<Bundler> getBundlers(String type) {
+ if (type == null) return Collections.emptySet();
+ switch (type) {
+ case "NONE":
+ return Collections.emptySet();
+ case "ALL":
+ return getBundlers();
+ default:
+ return Arrays.asList(getBundlers().stream()
+ .filter(b -> type.equalsIgnoreCase(b.getBundleType()))
+ .toArray(Bundler[]::new));
+ }
+ }
+
+ // Loads bundlers from the META-INF/services direct
+ @Override
+ public void loadBundlersFromServices(ClassLoader cl) {
+ ServiceLoader<Bundler> loader = ServiceLoader.load(Bundler.class, cl);
+ for (Bundler aLoader : loader) {
+ bundlers.add(aLoader);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/BundleParams.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,133 @@
+/*
+ * 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.jpackage.internal;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.*;
+import java.util.jar.Attributes;
+import java.util.jar.JarFile;
+import java.util.jar.Manifest;
+
+import static jdk.jpackage.internal.StandardBundlerParam.*;
+
+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 - 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
+ */
+ public BundleParams() {
+ params = new HashMap<>();
+ }
+
+ /**
+ * Create a bundle params with a copy of the params
+ * @param params map of initial parameters to be copied in.
+ */
+ public BundleParams(Map<String, ?> params) {
+ this.params = new HashMap<>(params);
+ }
+
+ public void addAllBundleParams(Map<String, ? super Object> params) {
+ this.params.putAll(params);
+ }
+
+ // NOTE: we do not care about application parameters here
+ // as they will be embeded into jar file manifest and
+ // java launcher will take care of them!
+
+ public Map<String, ? super Object> getBundleParamsAsMap() {
+ return new HashMap<>(params);
+ }
+
+ public String getName() {
+ return APP_NAME.fetchFrom(params);
+ }
+
+ public void setAppResourcesList(
+ List<jdk.jpackage.internal.RelativeFileSet> rfs) {
+ putUnlessNull(APP_RESOURCES_LIST.getID(), rfs);
+ }
+
+ private void putUnlessNull(String param, Object value) {
+ if (value != null) {
+ params.put(param, value);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/Bundler.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,120 @@
+/*
+ * Copyright (c) 2014, 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.jpackage.internal;
+
+import java.io.File;
+import java.util.Collection;
+import java.util.Map;
+
+/**
+ * Bundler
+ *
+ * The basic interface implemented by all Bundlers.
+ */
+public interface Bundler {
+ /**
+ * @return User Friendly name of this bundler.
+ */
+ String getName();
+
+ /**
+ * @return Command line identifier of the bundler. Should be unique.
+ */
+ String getID();
+
+ /**
+ * @return The bundle type of the bundle that is created by this bundler.
+ */
+ String getBundleType();
+
+ /**
+ * Determines if this bundler will execute with the given parameters.
+ *
+ * @param params The parameters to be validate. Validation may modify
+ * the map, so if you are going to be using the same map
+ * across multiple bundlers you should pass in a deep copy.
+ * @return true if valid
+ * @throws ConfigException If the configuration params are incorrect. The
+ * exception may contain advice on how to modify the params map
+ * to make it valid.
+ */
+ public boolean validate(Map<String, ? super Object> params)
+ throws ConfigException;
+
+ /**
+ * Creates a bundle from existing content.
+ *
+ * If a call to {@link #validate(java.util.Map)} date} returns true with
+ * the parameters map, then you can expect a valid output.
+ * However if an exception was thrown out of validate or it returned
+ * false then you should not expect sensible results from this call.
+ * It may or may not return a value, and it may or may not throw an
+ * exception. But any output should not be considered valid or sane.
+ *
+ * @param params The Bundle parameters,
+ * Keyed by the id from the ParamInfo. Execution may
+ * modify the map, so if you are going to be using the
+ * same map across multiple bundlers you should pass
+ * in a deep copy.
+ * @param outputParentDir
+ * The parent dir that the returned bundle will be placed in.
+ * @return The resulting bundled file
+ *
+ * For a bundler that produces a single artifact file this will be the
+ * location of that artifact (.exe file, .deb file, etc)
+ *
+ * For a bundler that produces a specific directory format output this will
+ * be the location of that specific directory (.app file, etc).
+ *
+ * For a bundler that produce multiple files, this will be a parent
+ * directory of those files (linux and windows images), whose name is not
+ * relevant to the result.
+ *
+ * @throws java.lang.IllegalArgumentException for any of the following
+ * reasons:
+ * <ul>
+ * <li>A required parameter is not found in the params list, for
+ * example missing the main class.</li>
+ * <li>A parameter has the wrong type of an object, for example a
+ * String where a File is required</li>
+ * <li>Bundler specific incompatibilities with the parameters, for
+ * example a bad version number format or an application id with
+ * forward slashes.</li>
+ * </ul>
+ */
+ public File execute(Map<String, ? super Object> params,
+ File outputParentDir) throws PackagerException;
+
+ /**
+ * Removes temporary files that are used for bundling.
+ */
+ public void cleanup(Map<String, ? super Object> params);
+
+ /**
+ * Returns "true" if this bundler is supported on current platform.
+ */
+ public boolean supported(boolean runtimeInstaller);
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/BundlerParamInfo.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,125 @@
+/*
+ * Copyright (c) 2014, 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.jpackage.internal;
+
+import java.util.Map;
+import java.util.function.BiFunction;
+import java.util.function.Function;
+
+/**
+ * BundlerParamInfo<T>
+ *
+ * A BundlerParamInfo encapsulates an individual bundler parameter of type <T>.
+ */
+class BundlerParamInfo<T> {
+
+ /**
+ * The command line and hashmap name of the parameter
+ */
+ String id;
+
+ /**
+ * Type of the parameter
+ */
+ Class<T> valueType;
+
+ /**
+ * Indicates if value was set using default value function
+ */
+ boolean isDefaultValue;
+
+ /**
+ * If the value is not set, and no fallback value is found,
+ * the parameter uses the value returned by the producer.
+ */
+ Function<Map<String, ? super Object>, T> defaultValueFunction;
+
+ /**
+ * An optional string converter for command line arguments.
+ */
+ BiFunction<String, Map<String, ? super Object>, T> stringConverter;
+
+ String getID() {
+ return id;
+ }
+
+ Class<T> getValueType() {
+ return valueType;
+ }
+
+ boolean getIsDefaultValue() {
+ return isDefaultValue;
+ }
+
+ Function<Map<String, ? super Object>, T> getDefaultValueFunction() {
+ return defaultValueFunction;
+ }
+
+ BiFunction<String, Map<String, ? super Object>,T>
+ getStringConverter() {
+ return stringConverter;
+ }
+
+ @SuppressWarnings("unchecked")
+ final T fetchFrom(Map<String, ? super Object> params) {
+ return fetchFrom(params, true);
+ }
+
+ @SuppressWarnings("unchecked")
+ final T fetchFrom(Map<String, ? super Object> params,
+ boolean invokeDefault) {
+ Object o = params.get(getID());
+ if (o instanceof String && getStringConverter() != null) {
+ return getStringConverter().apply((String)o, params);
+ }
+
+ Class<T> klass = getValueType();
+ if (klass.isInstance(o)) {
+ return (T) o;
+ }
+ if (o != null) {
+ throw new IllegalArgumentException("Param " + getID()
+ + " should be of type " + getValueType()
+ + " but is a " + o.getClass());
+ }
+ if (params.containsKey(getID())) {
+ // explicit nulls are allowed
+ return null;
+ }
+
+ if (invokeDefault && (getDefaultValueFunction() != null)) {
+ T result = getDefaultValueFunction().apply(params);
+ if (result != null) {
+ params.put(getID(), result);
+ isDefaultValue = true;
+ }
+ return result;
+ }
+
+ // ultimate fallback
+ return null;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/Bundlers.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2014, 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.jpackage.internal;
+
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.ServiceLoader;
+
+/**
+ * Bundlers
+ *
+ * The interface implemented by BasicBundlers
+ */
+public interface Bundlers {
+
+ /**
+ * This convenience method will call
+ * {@link #createBundlersInstance(ClassLoader)}
+ * with the classloader that this Bundlers is loaded from.
+ *
+ * @return an instance of Bundlers loaded and configured from
+ * the current ClassLoader.
+ */
+ public static Bundlers createBundlersInstance() {
+ return createBundlersInstance(Bundlers.class.getClassLoader());
+ }
+
+ /**
+ * This convenience method will automatically load a Bundlers instance
+ * from either META-INF/services or the default
+ * {@link BasicBundlers} if none are found in
+ * the services meta-inf.
+ *
+ * After instantiating the bundlers instance it will load the default
+ * bundlers via {@link #loadDefaultBundlers()} as well as requesting
+ * the services loader to load any other bundelrs via
+ * {@link #loadBundlersFromServices(ClassLoader)}.
+
+ *
+ * @param servicesClassLoader the classloader to search for
+ * META-INF/service registered bundlers
+ * @return an instance of Bundlers loaded and configured from
+ * the specified ClassLoader
+ */
+ public static Bundlers createBundlersInstance(
+ ClassLoader servicesClassLoader) {
+ ServiceLoader<Bundlers> bundlersLoader =
+ ServiceLoader.load(Bundlers.class, servicesClassLoader);
+ Bundlers bundlers = null;
+ Iterator<Bundlers> iter = bundlersLoader.iterator();
+ if (iter.hasNext()) {
+ bundlers = iter.next();
+ }
+ if (bundlers == null) {
+ bundlers = new BasicBundlers();
+ }
+
+ bundlers.loadBundlersFromServices(servicesClassLoader);
+ return bundlers;
+ }
+
+ /**
+ * Returns all of the preconfigured, requested, and manually
+ * configured bundlers loaded with this instance.
+ *
+ * @return a read-only collection of the requested bundlers
+ */
+ Collection<Bundler> getBundlers();
+
+ /**
+ * Returns all of the preconfigured, requested, and manually
+ * configured bundlers loaded with this instance that are of
+ * a specific BundleType, such as disk images, installers, or
+ * remote installers.
+ *
+ * @return a read-only collection of the requested bundlers
+ */
+ Collection<Bundler> getBundlers(String type);
+
+ /**
+ * Loads bundlers from the META-INF/services directly.
+ *
+ * This method is called from the
+ * {@link #createBundlersInstance(ClassLoader)}
+ * and {@link #createBundlersInstance()} methods.
+ */
+ void loadBundlersFromServices(ClassLoader cl);
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/CLIHelp.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,93 @@
+/*
+ * 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.jpackage.internal;
+
+import java.util.ResourceBundle;
+import java.io.File;
+import java.text.MessageFormat;
+
+
+/**
+ * CLIHelp
+ *
+ * Generate and show the command line interface help message(s).
+ */
+public class CLIHelp {
+
+ private static final ResourceBundle I18N = ResourceBundle.getBundle(
+ "jdk.jpackage.internal.resources.HelpResources");
+
+ // generates --help for jpackage's CLI
+ public static void showHelp(boolean noArgs) {
+
+ if (noArgs) {
+ Log.info(I18N.getString("MSG_Help_no_args"));
+ } else {
+ Platform platform = (Log.isDebug()) ?
+ Platform.UNKNOWN : Platform.getPlatform();
+ String types;
+ String pLaunchOptions;
+ String pInstallOptions;
+ String pInstallDir;
+ switch (platform) {
+ case MAC:
+ types = "{\"pkg\", \"dmg\"}";
+ pLaunchOptions = I18N.getString("MSG_Help_mac_launcher");
+ pInstallOptions = "";
+ pInstallDir
+ = I18N.getString("MSG_Help_mac_linux_install_dir");
+ break;
+ case LINUX:
+ types = "{\"rpm\", \"deb\"}";
+ pLaunchOptions = "";
+ pInstallOptions = I18N.getString("MSG_Help_linux_install");
+ pInstallDir
+ = I18N.getString("MSG_Help_mac_linux_install_dir");
+ break;
+ case WINDOWS:
+ types = "{\"exe\", \"msi\"}";
+ pLaunchOptions = I18N.getString("MSG_Help_win_launcher");
+ pInstallOptions = I18N.getString("MSG_Help_win_install");
+ pInstallDir
+ = I18N.getString("MSG_Help_win_install_dir");
+ break;
+ default:
+ types =
+ "{\"exe\", \"msi\", \"rpm\", \"deb\", \"pkg\", \"dmg\"}";
+ pLaunchOptions = I18N.getString("MSG_Help_win_launcher")
+ + I18N.getString("MSG_Help_mac_launcher");
+ pInstallOptions = I18N.getString("MSG_Help_win_install")
+ + I18N.getString("MSG_Help_linux_install");
+ pInstallDir
+ = I18N.getString("MSG_Help_default_install_dir");
+ break;
+ }
+ Log.info(MessageFormat.format(I18N.getString("MSG_Help"),
+ File.pathSeparator, types, pLaunchOptions,
+ pInstallOptions, pInstallDir));
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ConfigException.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,45 @@
+/*
+ * 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.jpackage.internal;
+
+public class ConfigException extends Exception {
+ private static final long serialVersionUID = 1L;
+ final String advice;
+
+ public ConfigException(String msg, String advice) {
+ super(msg);
+ this.advice = advice;
+ }
+
+ public ConfigException(Exception cause) {
+ super(cause);
+ this.advice = null;
+ }
+
+ public String getAdvice() {
+ return advice;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/DeployParams.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,346 @@
+/*
+ * Copyright (c) 2011, 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.jpackage.internal;
+
+import java.io.File;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.InvalidPathException;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.TreeSet;
+
+/**
+ * DeployParams
+ *
+ * This class is generated and used in Arguments.processArguments() as
+ * intermediate step in generating the BundleParams and ultimately the Bundles
+ */
+public class DeployParams {
+
+ final List<RelativeFileSet> resources = new ArrayList<>();
+
+ String targetFormat = null; // means app-image
+
+ File outdir = null;
+
+ // raw arguments to the bundler
+ Map<String, ? super Object> bundlerArguments = new LinkedHashMap<>();
+
+ public void setOutput(File output) {
+ outdir = output;
+ }
+
+ static class Template {
+ File in;
+ File out;
+
+ Template(File in, File out) {
+ this.in = in;
+ this.out = out;
+ }
+ }
+
+ // we need to expand as in some cases
+ // (most notably jpackage)
+ // we may get "." as filename and assumption is we include
+ // everything in the given folder
+ // (IOUtils.copyfiles() have recursive behavior)
+ List<File> expandFileset(File root) {
+ List<File> files = new LinkedList<>();
+ if (!Files.isSymbolicLink(root.toPath())) {
+ if (root.isDirectory()) {
+ File[] children = root.listFiles();
+ if (children != null) {
+ for (File f : children) {
+ files.addAll(expandFileset(f));
+ }
+ }
+ } else {
+ files.add(root);
+ }
+ }
+ 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 classpath = "";
+ for (RelativeFileSet resource : resources) {
+ for (String file : resource.getIncludedFiles()) {
+ if (file.endsWith(".jar")) {
+ classpath += file + File.pathSeparator;
+ }
+ }
+ }
+ addBundleArgument(
+ StandardBundlerParam.CLASSPATH.getID(), classpath);
+ }
+
+ static void validateName(String s, boolean forApp)
+ throws PackagerException {
+
+ String exceptionKey = forApp ?
+ "ERR_InvalidAppName" : "ERR_InvalidSLName";
+
+ if (s == null) {
+ if (forApp) {
+ return;
+ } else {
+ throw new PackagerException(exceptionKey, s);
+ }
+ }
+ if (s.length() == 0 || s.charAt(s.length() - 1) == '\\') {
+ throw new PackagerException(exceptionKey, s);
+ }
+ try {
+ // name must be valid path element for this file system
+ Path p = (new File(s)).toPath();
+ // and it must be a single name element in a path
+ if (p.getNameCount() != 1) {
+ throw new PackagerException(exceptionKey, s);
+ }
+ } catch (InvalidPathException ipe) {
+ throw new PackagerException(ipe, exceptionKey, s);
+ }
+
+ for (int i = 0; i < s.length(); i++) {
+ char a = s.charAt(i);
+ // We check for ASCII codes first which we accept. If check fails,
+ // check if it is acceptable extended ASCII or unicode character.
+ if (a < ' ' || a > '~') {
+ // Accept anything else including special chars like copyright
+ // symbols. Note: space will be included by ASCII check above,
+ // but other whitespace like tabs or new line will be rejected.
+ if (Character.isISOControl(a) ||
+ Character.isWhitespace(a)) {
+ throw new PackagerException(exceptionKey, s);
+ }
+ } else if (a == '"' || a == '%') {
+ throw new PackagerException(exceptionKey, s);
+ }
+ }
+ }
+
+ public void validate() throws PackagerException {
+ if (outdir == null) {
+ throw new PackagerException("ERR_MissingArgument", "--output");
+ }
+
+ boolean hasModule = (bundlerArguments.get(
+ Arguments.CLIOptions.MODULE.getId()) != null);
+ boolean hasAppImage = (bundlerArguments.get(
+ Arguments.CLIOptions.PREDEFINED_APP_IMAGE.getId()) != null);
+ boolean hasClass = (bundlerArguments.get(
+ Arguments.CLIOptions.APPCLASS.getId()) != null);
+ boolean hasMain = (bundlerArguments.get(
+ Arguments.CLIOptions.MAIN_JAR.getId()) != null);
+ boolean hasRuntimeImage = (bundlerArguments.get(
+ Arguments.CLIOptions.PREDEFINED_RUNTIME_IMAGE.getId()) != null);
+ boolean hasInput = (bundlerArguments.get(
+ Arguments.CLIOptions.INPUT.getId()) != null);
+ boolean hasModulePath = (bundlerArguments.get(
+ Arguments.CLIOptions.MODULE_PATH.getId()) != null);
+ boolean runtimeInstaller = targetFormat != null &&
+ !hasAppImage && !hasModule && !hasMain && hasRuntimeImage;
+
+ if (targetFormat == null) {
+ // Module application requires --runtime-image or --module-path
+ if (hasModule) {
+ if (!hasModulePath && !hasRuntimeImage) {
+ throw new PackagerException("ERR_MissingArgument",
+ "--runtime-image or --module-path");
+ }
+ } else {
+ if (!hasInput) {
+ throw new PackagerException(
+ "ERR_MissingArgument", "--input");
+ }
+ }
+ } else {
+ if (!runtimeInstaller) {
+ if (hasModule) {
+ if (!hasModulePath && !hasRuntimeImage && !hasAppImage) {
+ throw new PackagerException("ERR_MissingArgument",
+ "--runtime-image, --module-path or --app-image");
+ }
+ } else {
+ if (!hasInput && !hasAppImage) {
+ throw new PackagerException("ERR_MissingArgument",
+ "--input or --app-image");
+ }
+ }
+ }
+ }
+
+ // 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");
+ }
+ }
+
+ String name = (String)bundlerArguments.get(
+ Arguments.CLIOptions.NAME.getId());
+ validateName(name, true);
+
+ // Validate app image if set
+ String appImage = (String)bundlerArguments.get(
+ Arguments.CLIOptions.PREDEFINED_APP_IMAGE.getId());
+ if (appImage != null) {
+ File appImageDir = new File(appImage);
+ if (!appImageDir.exists() || appImageDir.list().length == 0) {
+ throw new PackagerException("ERR_AppImageNotExist", appImage);
+ }
+ }
+
+ // Validate temp-root
+ String root = (String)bundlerArguments.get(
+ Arguments.CLIOptions.TEMP_ROOT.getId());
+ if (root != null) {
+ String [] contents = (new File(root)).list();
+
+ if (contents != null && contents.length > 0) {
+ throw new PackagerException("ERR_BuildRootInvalid", root);
+ }
+ }
+
+ // Validate license file if set
+ String license = (String)bundlerArguments.get(
+ Arguments.CLIOptions.LICENSE_FILE.getId());
+ if (license != null) {
+ File licenseFile = new File(license);
+ if (!licenseFile.exists()) {
+ throw new PackagerException("ERR_LicenseFileNotExit");
+ }
+ }
+ }
+
+ void setTargetFormat(String t) {
+ targetFormat = t;
+ }
+
+ String getTargetFormat() {
+ return targetFormat;
+ }
+
+ private static final Set<String> multi_args = new TreeSet<>(Arrays.asList(
+ StandardBundlerParam.JAVA_OPTIONS.getID(),
+ StandardBundlerParam.ARGUMENTS.getID(),
+ StandardBundlerParam.MODULE_PATH.getID(),
+ StandardBundlerParam.ADD_MODULES.getID(),
+ StandardBundlerParam.LIMIT_MODULES.getID(),
+ StandardBundlerParam.FILE_ASSOCIATIONS.getID()
+ ));
+
+ @SuppressWarnings("unchecked")
+ public void addBundleArgument(String key, Object value) {
+ // special hack for multi-line arguments
+ if (multi_args.contains(key)) {
+ Object existingValue = bundlerArguments.get(key);
+ if (existingValue instanceof String && value instanceof String) {
+ String delim = "\n\n";
+ if (key.equals(StandardBundlerParam.MODULE_PATH.getID())) {
+ delim = File.pathSeparator;
+ } else if (key.equals(
+ StandardBundlerParam.ADD_MODULES.getID())) {
+ delim = ",";
+ }
+ bundlerArguments.put(key, existingValue + delim + value);
+ } else if (existingValue instanceof List && value instanceof List) {
+ ((List)existingValue).addAll((List)value);
+ } else if (existingValue instanceof Map &&
+ value instanceof String && ((String)value).contains("=")) {
+ String[] mapValues = ((String)value).split("=", 2);
+ ((Map)existingValue).put(mapValues[0], mapValues[1]);
+ } else {
+ bundlerArguments.put(key, value);
+ }
+ } else {
+ bundlerArguments.put(key, value);
+ }
+ }
+
+ BundleParams getBundleParams() {
+ BundleParams bundleParams = new BundleParams();
+
+ // construct app resources relative to output folder!
+ bundleParams.setAppResourcesList(resources);
+
+ Map<String, String> unescapedHtmlParams = new TreeMap<>();
+ Map<String, String> escapedHtmlParams = new TreeMap<>();
+
+ // check for collisions
+ TreeSet<String> keys = new TreeSet<>(bundlerArguments.keySet());
+ keys.retainAll(bundleParams.getBundleParamsAsMap().keySet());
+
+ if (!keys.isEmpty()) {
+ throw new RuntimeException("Deploy Params and Bundler Arguments "
+ + "overlap in the following values:" + keys.toString());
+ }
+
+ bundleParams.addAllBundleParams(bundlerArguments);
+
+ return bundleParams;
+ }
+
+ @Override
+ public String toString() {
+ return "DeployParams {" + "output: " + outdir
+ + " resources: {" + resources + "}}";
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/IOUtils.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,271 @@
+/*
+ * 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.jpackage.internal;
+
+import java.io.*;
+import java.net.URL;
+import java.util.Arrays;
+import java.nio.channels.FileChannel;
+import java.nio.file.FileVisitResult;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.SimpleFileVisitor;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * IOUtils
+ *
+ * A collection of static utility methods.
+ */
+public class IOUtils {
+
+ public static void deleteRecursive(File path) throws IOException {
+ if (!path.exists()) {
+ return;
+ }
+ Path directory = path.toPath();
+ Files.walkFileTree(directory, new SimpleFileVisitor<Path>() {
+ @Override
+ public FileVisitResult visitFile(Path file,
+ BasicFileAttributes attr) throws IOException {
+ if (Platform.getPlatform() == Platform.WINDOWS) {
+ Files.setAttribute(file, "dos:readonly", false);
+ }
+ Files.delete(file);
+ return FileVisitResult.CONTINUE;
+ }
+
+ @Override
+ public FileVisitResult preVisitDirectory(Path dir,
+ BasicFileAttributes attr) throws IOException {
+ if (Platform.getPlatform() == Platform.WINDOWS) {
+ Files.setAttribute(dir, "dos:readonly", false);
+ }
+ return FileVisitResult.CONTINUE;
+ }
+
+ @Override
+ public FileVisitResult postVisitDirectory(Path dir, IOException e)
+ throws IOException {
+ Files.delete(dir);
+ return FileVisitResult.CONTINUE;
+ }
+ });
+ }
+
+ public static void copyRecursive(Path src, Path dest) throws IOException {
+ Files.walkFileTree(src, new SimpleFileVisitor<Path>() {
+ @Override
+ public FileVisitResult preVisitDirectory(final Path dir,
+ final BasicFileAttributes attrs) throws IOException {
+ Files.createDirectories(dest.resolve(src.relativize(dir)));
+ return FileVisitResult.CONTINUE;
+ }
+
+ @Override
+ public FileVisitResult visitFile(final Path file,
+ final BasicFileAttributes attrs) throws IOException {
+ Files.copy(file, dest.resolve(src.relativize(file)));
+ return FileVisitResult.CONTINUE;
+ }
+ });
+ }
+
+ public static void copyRecursive(Path src, Path dest,
+ final List<String> excludes) throws IOException {
+ Files.walkFileTree(src, new SimpleFileVisitor<Path>() {
+ @Override
+ public FileVisitResult preVisitDirectory(final Path dir,
+ final BasicFileAttributes attrs) throws IOException {
+ if (excludes.contains(dir.toFile().getName())) {
+ return FileVisitResult.SKIP_SUBTREE;
+ } else {
+ Files.createDirectories(dest.resolve(src.relativize(dir)));
+ return FileVisitResult.CONTINUE;
+ }
+ }
+
+ @Override
+ public FileVisitResult visitFile(final Path file,
+ final BasicFileAttributes attrs) throws IOException {
+ if (!excludes.contains(file.toFile().getName())) {
+ Files.copy(file, dest.resolve(src.relativize(file)));
+ }
+ return FileVisitResult.CONTINUE;
+ }
+ });
+ }
+
+ public static void copyFile(File sourceFile, File destFile)
+ throws IOException {
+ destFile.getParentFile().mkdirs();
+
+ //recreate the file as existing copy may have weird permissions
+ destFile.delete();
+ destFile.createNewFile();
+
+ try (FileChannel source = new FileInputStream(sourceFile).getChannel();
+ FileChannel destination =
+ new FileOutputStream(destFile).getChannel()) {
+
+ if (destination != null && source != null) {
+ destination.transferFrom(source, 0, source.size());
+ }
+ }
+
+ //preserve executable bit!
+ if (sourceFile.canExecute()) {
+ destFile.setExecutable(true, false);
+ }
+ if (!sourceFile.canWrite()) {
+ destFile.setReadOnly();
+ }
+ destFile.setReadable(true, false);
+ }
+
+ public static long getFolderSize(File folder) {
+ long foldersize = 0;
+
+ File[] children = folder.listFiles();
+ if (children != null) {
+ for (File f : children) {
+ if (f.isDirectory()) {
+ foldersize += getFolderSize(f);
+ } else {
+ foldersize += f.length();
+ }
+ }
+ }
+
+ return foldersize;
+ }
+
+ // run "launcher paramfile" in the directory where paramfile is kept
+ public static void run(String launcher, File paramFile)
+ throws IOException {
+ if (paramFile != null && paramFile.exists()) {
+ ProcessBuilder pb =
+ new ProcessBuilder(launcher, paramFile.getName());
+ pb = pb.directory(paramFile.getParentFile());
+ exec(pb);
+ }
+ }
+
+ public static void exec(ProcessBuilder pb)
+ throws IOException {
+ exec(pb, false, null);
+ }
+
+ public static void exec(ProcessBuilder pb, boolean testForPresenseOnly,
+ PrintStream consumer) throws IOException {
+ pb.redirectErrorStream(true);
+ Log.verbose("Running "
+ + Arrays.toString(pb.command().toArray(new String[0]))
+ + (pb.directory() != null ? (" in " + pb.directory()) : ""));
+ Process p = pb.start();
+ InputStreamReader isr = new InputStreamReader(p.getInputStream());
+ BufferedReader br = new BufferedReader(isr);
+ String lineRead;
+ while ((lineRead = br.readLine()) != null) {
+ if (consumer != null) {
+ consumer.print(lineRead + '\n');
+ } else {
+ Log.verbose(lineRead);
+ }
+ }
+ try {
+ int ret = p.waitFor();
+ if (ret != 0 && !(testForPresenseOnly && ret != 127)) {
+ throw new IOException("Exec failed with code "
+ + ret + " command ["
+ + Arrays.toString(pb.command().toArray(new String[0]))
+ + " in " + (pb.directory() != null ?
+ pb.directory().getAbsolutePath() :
+ "unspecified directory"));
+ }
+ } catch (InterruptedException ex) {
+ }
+ }
+
+ public static int getProcessOutput(List<String> result, String... args)
+ throws IOException, InterruptedException {
+
+ ProcessBuilder pb = new ProcessBuilder(args);
+
+ final Process p = pb.start();
+
+ List<String> list = new ArrayList<>();
+
+ final BufferedReader in =
+ new BufferedReader(new InputStreamReader(p.getInputStream()));
+ final BufferedReader err =
+ new BufferedReader(new InputStreamReader(p.getErrorStream()));
+
+ Thread t = new Thread(() -> {
+ try {
+ String line;
+ while ((line = in.readLine()) != null) {
+ list.add(line);
+ }
+ } catch (IOException ioe) {
+ Log.verbose(ioe);
+ }
+
+ try {
+ String line;
+ while ((line = err.readLine()) != null) {
+ Log.error(line);
+ }
+ } catch (IOException ioe) {
+ Log.verbose(ioe);
+ }
+ });
+ t.setDaemon(true);
+ t.start();
+
+ int ret = p.waitFor();
+
+ result.clear();
+ result.addAll(list);
+
+ 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());
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/JLinkBundlerHelper.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,416 @@
+/*
+ * Copyright (c) 2015, 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.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;
+import java.util.spi.ToolProvider;
+import java.lang.module.Configuration;
+import java.lang.module.ResolvedModule;
+import java.lang.module.ModuleDescriptor;
+import java.lang.module.ModuleFinder;
+import java.lang.module.ModuleReference;
+
+final class JLinkBundlerHelper {
+
+ private static final ResourceBundle I18N = ResourceBundle.getBundle(
+ "jdk.jpackage.internal.resources.MainResources");
+
+ 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 getMainClass(Map<String, ? super Object> params) {
+ String result = "";
+ String mainModule = StandardBundlerParam.MODULE.fetchFrom(params);
+ if (mainModule != null) {
+ int index = mainModule.indexOf("/");
+ if (index > 0) {
+ result = mainModule.substring(index + 1);
+ }
+ } else {
+ RelativeFileSet fileset =
+ StandardBundlerParam.MAIN_JAR.fetchFrom(params);
+ if (fileset != null) {
+ result = StandardBundlerParam.MAIN_CLASS.fetchFrom(params);
+ } else {
+ // possibly app-image
+ }
+ }
+
+ return result;
+ }
+
+ 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;
+ }
+
+ private static Set<String> getValidModules(List<Path> modulePath,
+ Set<String> addModules, Set<String> limitModules) {
+ ModuleHelper moduleHelper = new ModuleHelper(
+ modulePath, addModules, limitModules);
+ return removeInvalidModules(modulePath, moduleHelper.modules());
+ }
+
+ static void execute(Map<String, ? super Object> params,
+ AbstractAppImageBuilder imageBuilder)
+ throws IOException, Exception {
+
+ // we might be able to build it (with no main class) but it won't run
+ if (StandardBundlerParam.MAIN_CLASS.fetchFrom(params) == null) {
+ throw new PackagerException("ERR_NoMainClass");
+ }
+
+ List<Path> modulePath =
+ StandardBundlerParam.MODULE_PATH.fetchFrom(params);
+ Set<String> addModules =
+ StandardBundlerParam.ADD_MODULES.fetchFrom(params);
+ Set<String> limitModules =
+ StandardBundlerParam.LIMIT_MODULES.fetchFrom(params);
+ Path outputDir = imageBuilder.getRoot();
+ 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;
+ }
+
+ boolean bindServices = addModules.isEmpty();
+
+ // 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);
+ }
+ }
+
+ Set<String> validModules =
+ getValidModules(modulePath, addModules, limitModules);
+
+ if (mainModule != null) {
+ validModules.add(mainModule);
+ }
+
+ runJLink(outputDir, modulePath, validModules, limitModules,
+ new HashMap<String,String>(), bindServices);
+
+ imageBuilder.prepareApplicationFiles();
+ }
+
+
+ // 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;
+ }
+
+ /*
+ * Returns the set of modules that would be visible by default for
+ * a non-modular-aware application consisting of the given elements.
+ */
+ private static Set<String> getDefaultModules(
+ Path[] paths, String[] addModules) {
+
+ // the modules in the run-time image that export an API
+ Stream<String> systemRoots = ModuleFinder.ofSystem().findAll().stream()
+ .map(ModuleReference::descriptor)
+ .filter(descriptor -> exportsAPI(descriptor))
+ .map(ModuleDescriptor::name);
+
+ Set<String> roots;
+ if (addModules == null || addModules.length == 0) {
+ roots = systemRoots.collect(Collectors.toSet());
+ } else {
+ var extraRoots = Stream.of(addModules);
+ roots = Stream.concat(systemRoots,
+ extraRoots).collect(Collectors.toSet());
+ }
+
+ ModuleFinder finder = ModuleFinder.ofSystem();
+ if (paths != null && paths.length > 0) {
+ finder = ModuleFinder.compose(finder, ModuleFinder.of(paths));
+ }
+ return Configuration.empty()
+ .resolveAndBind(finder, ModuleFinder.of(), roots)
+ .modules()
+ .stream()
+ .map(ResolvedModule::name)
+ .collect(Collectors.toSet());
+ }
+
+ /*
+ * Returns true if the given module exports an API to all module.
+ */
+ private static boolean exportsAPI(ModuleDescriptor descriptor) {
+ return descriptor.exports()
+ .stream()
+ .filter(e -> !e.isQualified())
+ .findAny()
+ .isPresent();
+ }
+
+ private static Set<String> removeInvalidModules(
+ List<Path> modulePath, Set<String> modules) {
+ Set<String> result = new LinkedHashSet<String>();
+ ModuleManager mm = new ModuleManager(modulePath);
+ List<ModFile> lmodfiles =
+ mm.getModules(EnumSet.of(ModuleManager.SearchType.ModularJar,
+ ModuleManager.SearchType.Jmod,
+ ModuleManager.SearchType.ExplodedModule));
+
+ HashMap<String, ModFile> validModules = new HashMap<>();
+
+ for (ModFile modFile : lmodfiles) {
+ validModules.put(modFile.getModName(), modFile);
+ }
+
+ for (String name : modules) {
+ if (validModules.containsKey(name)) {
+ result.add(name);
+ } else {
+ Log.error(MessageFormat.format(
+ I18N.getString("warning.module.does.not.exist"), name));
+ }
+ }
+
+ return result;
+ }
+
+ private static class ModuleHelper {
+ // The token for "all modules on the module path".
+ private static final String ALL_MODULE_PATH = "ALL-MODULE-PATH";
+
+ // The token for "all valid runtime modules".
+ static final String ALL_DEFAULT = "ALL-DEFAULT";
+
+ private final Set<String> modules = new HashSet<>();
+ ModuleHelper(List<Path> paths, Set<String> addModules,
+ Set<String> limitModules) {
+ boolean addAllModulePath = false;
+ boolean addDefaultMods = false;
+
+ for (Iterator<String> iterator = addModules.iterator();
+ iterator.hasNext();) {
+ String module = iterator.next();
+
+ switch (module) {
+ case ALL_MODULE_PATH:
+ iterator.remove();
+ addAllModulePath = true;
+ break;
+ case ALL_DEFAULT:
+ iterator.remove();
+ addDefaultMods = true;
+ break;
+ default:
+ this.modules.add(module);
+ }
+ }
+
+ if (addAllModulePath) {
+ this.modules.addAll(getModuleNamesFromPath(paths));
+ } else if (addDefaultMods) {
+ this.modules.addAll(getDefaultModules(
+ paths.toArray(new Path[0]),
+ addModules.toArray(new String[0])));
+ }
+ }
+
+ Set<String> modules() {
+ return modules;
+ }
+
+ private static Set<String> getModuleNamesFromPath(List<Path> Value) {
+ Set<String> result = new LinkedHashSet<String>();
+ ModuleManager mm = new ModuleManager(Value);
+ List<ModFile> modFiles = mm.getModules(
+ EnumSet.of(ModuleManager.SearchType.ModularJar,
+ ModuleManager.SearchType.Jmod,
+ ModuleManager.SearchType.ExplodedModule));
+
+ for (ModFile modFile : modFiles) {
+ result.add(modFile.getModName());
+ }
+ return result;
+ }
+ }
+
+ private static void runJLink(Path output, List<Path> modulePath,
+ Set<String> modules, Set<String> limitModules,
+ HashMap<String, String> user, boolean bindServices)
+ throws IOException {
+
+ // This is just to ensure jlink is given a non-existant directory
+ // The passed in output path should be non-existant or empty directory
+ IOUtils.deleteRecursive(output.toFile());
+
+ ArrayList<String> args = new ArrayList<String>();
+ args.add("--output");
+ args.add(output.toString());
+ if (modulePath != null && !modulePath.isEmpty()) {
+ args.add("--module-path");
+ args.add(getPathList(modulePath));
+ }
+ if (modules != null && !modules.isEmpty()) {
+ args.add("--add-modules");
+ args.add(getStringList(modules));
+ }
+ if (limitModules != null && !limitModules.isEmpty()) {
+ args.add("--limit-modules");
+ args.add(getStringList(limitModules));
+ }
+ if (user != null && !user.isEmpty()) {
+ for (Map.Entry<String, String> entry : user.entrySet()) {
+ args.add(entry.getKey());
+ args.add(entry.getValue());
+ }
+ } else {
+ args.add("--strip-native-commands");
+ args.add("--strip-debug");
+ args.add("--no-man-pages");
+ args.add("--no-header-files");
+ if (bindServices) {
+ args.add("--bind-services");
+ }
+ }
+
+ StringWriter writer = new StringWriter();
+ PrintWriter pw = new PrintWriter(writer);
+
+ Log.verbose("jlink arguments: " + args);
+ int retVal = JLINK_TOOL.run(pw, pw, args.toArray(new String[0]));
+ String jlinkOut = writer.toString();
+
+ if (retVal != 0) {
+ throw new IOException("jlink failed with: " + jlinkOut);
+ } else if (jlinkOut.length() > 0) {
+ Log.verbose("jlink output: " + jlinkOut);
+ }
+ }
+
+ 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;
+ }
+
+ 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);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/JPackageToolProvider.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2017, 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.jpackage.internal;
+
+import java.io.PrintWriter;
+import java.util.spi.ToolProvider;
+
+/**
+ * JPackageToolProvider
+ *
+ * This is the ToolProvider implementation exported
+ * to java.util.spi.ToolProvider and ultimately javax.tools.ToolProvider
+ */
+public class JPackageToolProvider implements ToolProvider {
+
+ public String name() {
+ return "jpackage";
+ }
+
+ public synchronized int run(
+ PrintWriter out, PrintWriter err, String... args) {
+ try {
+ return new jdk.jpackage.main.Main().execute(out, err, args);
+ } catch (RuntimeException re) {
+ Log.error(re.getMessage());
+ Log.verbose(re);
+ return 1;
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/Log.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,195 @@
+/*
+ * Copyright (c) 2011, 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.jpackage.internal;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.io.PrintWriter;
+
+/**
+ * Log
+ *
+ * General purpose logging mechanism.
+ */
+public class Log {
+ public static class Logger {
+ private boolean verbose = false;
+ private PrintWriter out = null;
+ private PrintWriter err = null;
+
+ public Logger(boolean v) {
+ verbose = v;
+ }
+
+ public void setVerbose(boolean v) {
+ verbose = v;
+ }
+
+ public boolean isVerbose() {
+ return verbose;
+ }
+
+ public void setPrintWriter(PrintWriter out, PrintWriter err) {
+ this.out = out;
+ this.err = err;
+ }
+
+ public void flush() {
+ if (out != null) {
+ out.flush();
+ }
+
+ if (err != null) {
+ err.flush();
+ }
+ }
+
+ public void info(String msg) {
+ if (out != null) {
+ out.println(msg);
+ } else {
+ System.out.println(msg);
+ }
+ }
+
+ public void error(String msg) {
+ if (err != null) {
+ err.println(msg);
+ } else {
+ System.err.println(msg);
+ }
+ }
+
+ public void verbose(Throwable t) {
+ if (out != null && (Log.debug || verbose)) {
+ t.printStackTrace(out);
+ } else if (Log.debug || verbose) {
+ t.printStackTrace(System.out);
+ }
+ }
+
+ public void verbose(String msg) {
+ if (out != null && (Log.debug || verbose)) {
+ out.println(msg);
+ } else if (Log.debug || verbose) {
+ System.out.println(msg);
+ }
+ }
+
+ public void debug(String msg) {
+ if (out != null && Log.debug) {
+ out.println(msg);
+ } else if (Log.debug) {
+ System.out.println(msg);
+ }
+ }
+ }
+
+ private static Logger delegate = null;
+ private static boolean debug =
+ "true".equals(System.getenv("JPACKAGE_DEBUG"));
+
+ public static void setLogger(Logger l) {
+ delegate = l;
+ if (l == null) {
+ delegate = new Logger(false);
+ }
+ }
+
+ public static Logger getLogger() {
+ return delegate;
+ }
+
+ public static void flush() {
+ if (delegate != null) {
+ delegate.flush();
+ }
+ }
+
+ public static void info(String msg) {
+ if (delegate != null) {
+ delegate.info(msg);
+ }
+ }
+
+ public static void error(String msg) {
+ if (delegate != null) {
+ delegate.error(msg);
+ }
+ }
+
+ public static void setVerbose(boolean v) {
+ if (delegate != null) {
+ delegate.setVerbose(v);
+ }
+ }
+
+ public static boolean isVerbose() {
+ if (delegate != null) {
+ return delegate.isVerbose();
+ }
+
+ return false; // Off by default
+ }
+
+ public static void verbose(String msg) {
+ if (delegate != null) {
+ delegate.verbose(msg);
+ }
+ }
+
+ public static void verbose(Throwable t) {
+ if (delegate != null) {
+ delegate.verbose(t);
+ }
+ }
+
+ public static void debug(String msg) {
+ if (delegate != null) {
+ delegate.debug(msg);
+ }
+ }
+
+ public static void debug(Throwable t) {
+ try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
+ try (PrintStream ps = new PrintStream(baos)) {
+ t.printStackTrace(ps);
+ }
+ debug(baos.toString());
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ public static boolean isDebug() {
+ return debug;
+ }
+
+ public static void setDebug(boolean debug) {
+ Log.debug = debug;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ModFile.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,120 @@
+/*
+ * 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.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("[.][^.]+$", "");
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ModuleManager.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,116 @@
+/*
+ * 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.jpackage.internal;
+
+import java.io.File;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.EnumSet;
+import java.util.List;
+
+final class ModuleManager {
+ private final List<String> folders = new ArrayList<String>();
+
+ enum SearchType {UnnamedJar, ModularJar, Jmod, ExplodedModule}
+
+ ModuleManager(String folders) {
+ super();
+ String lfolders = folders.replaceAll("^\"|\"$", "");
+ List<Path> paths = new ArrayList<Path>();
+
+ for (String folder :
+ Arrays.asList(lfolders.split(File.pathSeparator))) {
+ File file = new File(folder);
+ paths.add(file.toPath());
+ }
+
+ initialize(paths);
+ }
+
+ ModuleManager(List<Path> Paths) {
+ super();
+ initialize(Paths);
+ }
+
+ private void initialize(List<Path> Paths) {
+ for (Path path : Paths) {
+ folders.add(path.toString().replaceAll("^\"|\"$", ""));
+ }
+ }
+
+ List<ModFile> getModules(EnumSet<SearchType> Search) {
+ List<ModFile> result = new ArrayList<ModFile>();
+
+ for (String folder : folders) {
+ result.addAll(getAllModulesInDirectory(folder, Search));
+ }
+
+ return result;
+ }
+
+ private static List<ModFile> getAllModulesInDirectory(String folder,
+ EnumSet<SearchType> Search) {
+ List<ModFile> result = new ArrayList<ModFile>();
+ File lfolder = new File(folder);
+ File[] files = { lfolder };
+ if (lfolder.isDirectory()) {
+ files = lfolder.listFiles();
+ }
+
+ if (files != null) {
+ for (File file : files) {
+ ModFile modFile = new ModFile(file);
+
+ switch (modFile.getModType()) {
+ case Unknown:
+ break;
+ case UnnamedJar:
+ if (Search.contains(SearchType.UnnamedJar)) {
+ result.add(modFile);
+ }
+ break;
+ case ModularJar:
+ if (Search.contains(SearchType.ModularJar)) {
+ result.add(modFile);
+ }
+ break;
+ case Jmod:
+ if (Search.contains(SearchType.Jmod)) {
+ result.add(modFile);
+ }
+ break;
+ case ExplodedModule:
+ if (Search.contains(SearchType.ExplodedModule)) {
+ result.add(modFile);
+ }
+ break;
+ }
+ }
+ }
+ return result;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/PackagerException.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2011, 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.jpackage.internal;
+
+import java.text.MessageFormat;
+import java.util.ResourceBundle;
+
+public class PackagerException extends Exception {
+ private static final long serialVersionUID = 1L;
+ private static final ResourceBundle bundle = ResourceBundle.getBundle(
+ "jdk.jpackage.internal.resources.MainResources");
+
+ public PackagerException(Throwable cause) {
+ super(cause);
+ }
+
+ public PackagerException(String key, Throwable cause) {
+ super(bundle.getString(key), cause);
+ }
+
+ public PackagerException(String key) {
+ super(bundle.getString(key));
+ }
+
+ public PackagerException(String key, String ... arguments) {
+ super(MessageFormat.format(
+ bundle.getString(key), (Object[]) arguments));
+ }
+
+ public PackagerException(
+ Throwable cause, String key, String ... arguments) {
+ super(MessageFormat.format(bundle.getString(key),
+ (Object[]) arguments), cause);
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/Platform.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,105 @@
+/*
+ * 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.jpackage.internal;
+
+import java.util.regex.Pattern;
+
+/**
+ * Platform
+ *
+ * Use <code>Platform</code> to detect the operating system
+ * that is currently running.
+ *
+ * Example:
+ *
+ * Platform platform = Platform.getPlatform();
+ *
+ * switch(platform) {
+ * case Platform.MAC: {
+ * // Do something
+ * break;
+ * }
+ * case Platform.WINDOWS:
+ * case Platform.LINUX: {
+ * // Do something else
+ * }
+ * }
+ *
+ */
+enum Platform {UNKNOWN, WINDOWS, LINUX, MAC;
+ private static final Platform platform;
+ private static final int majorVersion;
+ private static final int minorVersion;
+
+ static {
+ String os = System.getProperty("os.name").toLowerCase();
+
+ if (os.indexOf("win") >= 0) {
+ platform = Platform.WINDOWS;
+ }
+ else if (os.indexOf("nix") >= 0 || os.indexOf("nux") >= 0) {
+ platform = Platform.LINUX;
+ }
+ else if (os.indexOf("mac") >= 0) {
+ platform = Platform.MAC;
+ }
+ else {
+ platform = Platform.UNKNOWN;
+ }
+
+ String version = System.getProperty("os.version").toString();
+ String[] parts = version.split(Pattern.quote("."));
+
+ if (parts.length > 0) {
+ majorVersion = Integer.parseInt(parts[0]);
+
+ if (parts.length > 1) {
+ minorVersion = Integer.parseInt(parts[1]);
+ }
+ else {
+ minorVersion = -1;
+ }
+ }
+ else {
+ majorVersion = -1;
+ minorVersion = -1;
+ }
+ }
+
+ private Platform() {}
+
+ static Platform getPlatform() {
+ return platform;
+ }
+
+ static int getMajorVersion() {
+ return majorVersion;
+ }
+
+ static int getMinorVersion() {
+ return minorVersion;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/RelativeFileSet.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,80 @@
+/*
+ * 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.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 + "}";
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/StandardBundlerParam.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,749 @@
+/*
+ * Copyright (c) 2014, 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.jpackage.internal;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.StringReader;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Properties;
+import java.util.ResourceBundle;
+import java.util.Set;
+import java.util.HashSet;
+import java.util.function.BiFunction;
+import java.util.function.Function;
+import java.util.jar.Attributes;
+import java.util.jar.JarFile;
+import java.util.jar.Manifest;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+
+/**
+ * StandardBundlerParam
+ *
+ * A parameter to a bundler.
+ *
+ * Also contains static definitions of all of the common bundler parameters.
+ * (additional platform specific and mode specific bundler parameters
+ * are defined in each of the specific bundlers)
+ *
+ * Also contains static methods that operate on maps of parameters.
+ */
+class StandardBundlerParam<T> extends BundlerParamInfo<T> {
+
+ private static final ResourceBundle I18N = ResourceBundle.getBundle(
+ "jdk.jpackage.internal.resources.MainResources");
+ private static final String JAVABASEJMOD = "java.base.jmod";
+
+ StandardBundlerParam(String id, Class<T> valueType,
+ Function<Map<String, ? super Object>, T> defaultValueFunction,
+ BiFunction<String, Map<String, ? super Object>, T> stringConverter)
+ {
+ this.id = id;
+ this.valueType = valueType;
+ this.defaultValueFunction = defaultValueFunction;
+ this.stringConverter = stringConverter;
+ }
+
+ static final StandardBundlerParam<RelativeFileSet> APP_RESOURCES =
+ new StandardBundlerParam<>(
+ BundleParams.PARAM_APP_RESOURCES,
+ RelativeFileSet.class,
+ null, // no default. Required parameter
+ null // no string translation,
+ // tool must provide complex type
+ );
+
+ @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 =
+ new StandardBundlerParam<>(
+ Arguments.CLIOptions.INPUT.getId(),
+ String.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;
+ }
+ }
+ );
+
+ // note that each bundler is likely to replace this one with
+ // their own converter
+ static final StandardBundlerParam<RelativeFileSet> 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
+ );
+
+ static final StandardBundlerParam<String> MAIN_CLASS =
+ new StandardBundlerParam<>(
+ Arguments.CLIOptions.APPCLASS.getId(),
+ String.class,
+ params -> {
+ if (isRuntimeInstaller(params)) {
+ return null;
+ }
+ extractMainClassInfoFromAppResources(params);
+ String s = (String) params.get(
+ BundleParams.PARAM_APPLICATION_CLASS);
+ if (s == null) {
+ s = JLinkBundlerHelper.getMainClass(params);
+ if (s.length() == 0) {
+ s = null;
+ }
+ }
+ return s;
+ },
+ (s, p) -> s
+ );
+
+ static final StandardBundlerParam<File> PREDEFINED_RUNTIME_IMAGE =
+ new StandardBundlerParam<>(
+ Arguments.CLIOptions.PREDEFINED_RUNTIME_IMAGE.getId(),
+ File.class,
+ params -> null,
+ (s, p) -> new File(s)
+ );
+
+ static final StandardBundlerParam<String> APP_NAME =
+ new StandardBundlerParam<>(
+ Arguments.CLIOptions.NAME.getId(),
+ String.class,
+ params -> {
+ String s = MAIN_CLASS.fetchFrom(params);
+ if (s != null) {
+ int idx = s.lastIndexOf(".");
+ if (idx >= 0) {
+ return s.substring(idx+1);
+ }
+ return s;
+ } else if (isRuntimeInstaller(params)) {
+ File f = PREDEFINED_RUNTIME_IMAGE.fetchFrom(params);
+ if (f != null) {
+ return f.getName();
+ }
+ }
+ return null;
+ },
+ (s, p) -> s
+ );
+
+ static final StandardBundlerParam<File> ICON =
+ new StandardBundlerParam<>(
+ Arguments.CLIOptions.ICON.getId(),
+ File.class,
+ params -> null,
+ (s, p) -> new File(s)
+ );
+
+ static final StandardBundlerParam<String> VENDOR =
+ new StandardBundlerParam<>(
+ Arguments.CLIOptions.VENDOR.getId(),
+ String.class,
+ params -> I18N.getString("param.vendor.default"),
+ (s, p) -> s
+ );
+
+ static final StandardBundlerParam<String> DESCRIPTION =
+ new StandardBundlerParam<>(
+ Arguments.CLIOptions.DESCRIPTION.getId(),
+ String.class,
+ params -> params.containsKey(APP_NAME.getID())
+ ? APP_NAME.fetchFrom(params)
+ : I18N.getString("param.description.default"),
+ (s, p) -> s
+ );
+
+ static final StandardBundlerParam<String> COPYRIGHT =
+ new StandardBundlerParam<>(
+ Arguments.CLIOptions.COPYRIGHT.getId(),
+ String.class,
+ params -> MessageFormat.format(I18N.getString(
+ "param.copyright.default"), new Date()),
+ (s, p) -> s
+ );
+
+ @SuppressWarnings("unchecked")
+ static final StandardBundlerParam<List<String>> ARGUMENTS =
+ new StandardBundlerParam<>(
+ Arguments.CLIOptions.ARGUMENTS.getId(),
+ (Class<List<String>>) (Object) List.class,
+ params -> Collections.emptyList(),
+ (s, p) -> null
+ );
+
+ @SuppressWarnings("unchecked")
+ static final StandardBundlerParam<List<String>> JAVA_OPTIONS =
+ new StandardBundlerParam<>(
+ Arguments.CLIOptions.JAVA_OPTIONS.getId(),
+ (Class<List<String>>) (Object) List.class,
+ params -> Collections.emptyList(),
+ (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 -> I18N.getString("param.version.default"),
+ (s, p) -> s
+ );
+
+ @SuppressWarnings("unchecked")
+ public static final StandardBundlerParam<String> LICENSE_FILE =
+ new StandardBundlerParam<>(
+ Arguments.CLIOptions.LICENSE_FILE.getId(),
+ String.class,
+ params -> null,
+ (s, p) -> s
+ );
+
+ static final StandardBundlerParam<File> TEMP_ROOT =
+ new StandardBundlerParam<>(
+ Arguments.CLIOptions.TEMP_ROOT.getId(),
+ File.class,
+ params -> {
+ try {
+ return Files.createTempDirectory(
+ "jdk.jpackage").toFile();
+ } catch (IOException ioe) {
+ return null;
+ }
+ },
+ (s, p) -> new File(s)
+ );
+
+ public static final StandardBundlerParam<File> CONFIG_ROOT =
+ new StandardBundlerParam<>(
+ "configRoot",
+ File.class,
+ params -> {
+ File root =
+ new File(TEMP_ROOT.fetchFrom(params), "config");
+ root.mkdirs();
+ return root;
+ },
+ (s, p) -> null
+ );
+
+ static final StandardBundlerParam<String> IDENTIFIER =
+ new StandardBundlerParam<>(
+ Arguments.CLIOptions.IDENTIFIER.getId(),
+ String.class,
+ params -> {
+ String s = MAIN_CLASS.fetchFrom(params);
+ if (s == null) return null;
+
+ int idx = s.lastIndexOf(".");
+ if (idx >= 1) {
+ return s.substring(0, idx);
+ }
+ return s;
+ },
+ (s, p) -> s
+ );
+
+ static final StandardBundlerParam<Boolean> VERBOSE =
+ new StandardBundlerParam<>(
+ Arguments.CLIOptions.VERBOSE.getId(),
+ Boolean.class,
+ params -> false,
+ // valueOf(null) is false, and we actually do want null
+ (s, p) -> (s == null || "null".equalsIgnoreCase(s)) ?
+ true : Boolean.valueOf(s)
+ );
+
+ static final StandardBundlerParam<File> RESOURCE_DIR =
+ new StandardBundlerParam<>(
+ Arguments.CLIOptions.RESOURCE_DIR.getId(),
+ File.class,
+ params -> null,
+ (s, p) -> new File(s)
+ );
+
+ static final BundlerParamInfo<String> INSTALL_DIR =
+ new StandardBundlerParam<>(
+ Arguments.CLIOptions.INSTALL_DIR.getId(),
+ String.class,
+ params -> null,
+ (s, p) -> s
+ );
+
+ static final StandardBundlerParam<File> PREDEFINED_APP_IMAGE =
+ new StandardBundlerParam<>(
+ Arguments.CLIOptions.PREDEFINED_APP_IMAGE.getId(),
+ File.class,
+ params -> null,
+ (s, p) -> new File(s));
+
+ @SuppressWarnings("unchecked")
+ static final StandardBundlerParam<List<Map<String, ? super Object>>> ADD_LAUNCHERS =
+ new StandardBundlerParam<>(
+ Arguments.CLIOptions.ADD_LAUNCHER.getId(),
+ (Class<List<Map<String, ? super Object>>>) (Object)
+ List.class,
+ params -> new ArrayList<>(1),
+ // valueOf(null) is false, and we actually do want null
+ (s, p) -> null
+ );
+
+ @SuppressWarnings("unchecked")
+ static final StandardBundlerParam
+ <List<Map<String, ? super Object>>> FILE_ASSOCIATIONS =
+ new StandardBundlerParam<>(
+ Arguments.CLIOptions.FILE_ASSOCIATIONS.getId(),
+ (Class<List<Map<String, ? super Object>>>) (Object)
+ List.class,
+ params -> new ArrayList<>(1),
+ // valueOf(null) is false, and we actually do want null
+ (s, p) -> null
+ );
+
+ @SuppressWarnings("unchecked")
+ static final StandardBundlerParam<List<String>> FA_EXTENSIONS =
+ new StandardBundlerParam<>(
+ "fileAssociation.extension",
+ (Class<List<String>>) (Object) List.class,
+ params -> null, // null means not matched to an extension
+ (s, p) -> Arrays.asList(s.split("(,|\\s)+"))
+ );
+
+ @SuppressWarnings("unchecked")
+ static final StandardBundlerParam<List<String>> FA_CONTENT_TYPE =
+ new StandardBundlerParam<>(
+ "fileAssociation.contentType",
+ (Class<List<String>>) (Object) List.class,
+ params -> null,
+ // null means not matched to a content/mime type
+ (s, p) -> Arrays.asList(s.split("(,|\\s)+"))
+ );
+
+ static final StandardBundlerParam<String> FA_DESCRIPTION =
+ new StandardBundlerParam<>(
+ "fileAssociation.description",
+ String.class,
+ params -> APP_NAME.fetchFrom(params) + " File",
+ null
+ );
+
+ static final StandardBundlerParam<File> FA_ICON =
+ new StandardBundlerParam<>(
+ "fileAssociation.icon",
+ File.class,
+ ICON::fetchFrom,
+ (s, p) -> new File(s)
+ );
+
+ @SuppressWarnings("unchecked")
+ static final BundlerParamInfo<List<Path>> MODULE_PATH =
+ new StandardBundlerParam<>(
+ Arguments.CLIOptions.MODULE_PATH.getId(),
+ (Class<List<Path>>) (Object)List.class,
+ p -> { return getDefaultModulePath(); },
+ (s, p) -> {
+ List<Path> modulePath = Arrays.asList(s
+ .split(File.pathSeparator)).stream()
+ .map(ss -> new File(ss).toPath())
+ .collect(Collectors.toList());
+ Path javaBasePath = null;
+ if (modulePath != null) {
+ javaBasePath = JLinkBundlerHelper
+ .findPathOfModule(modulePath, JAVABASEJMOD);
+ } else {
+ modulePath = new ArrayList<Path>();
+ }
+
+ // Add the default JDK module path to the module path.
+ if (javaBasePath == null) {
+ List<Path> jdkModulePath = getDefaultModulePath();
+
+ if (jdkModulePath != null) {
+ modulePath.addAll(jdkModulePath);
+ javaBasePath =
+ JLinkBundlerHelper.findPathOfModule(
+ modulePath, JAVABASEJMOD);
+ }
+ }
+
+ if (javaBasePath == null ||
+ !Files.exists(javaBasePath)) {
+ Log.error(String.format(I18N.getString(
+ "warning.no.jdk.modules.found")));
+ }
+
+ return modulePath;
+ });
+
+ static final BundlerParamInfo<String> MODULE =
+ new StandardBundlerParam<>(
+ Arguments.CLIOptions.MODULE.getId(),
+ String.class,
+ p -> null,
+ (s, p) -> {
+ return String.valueOf(s);
+ });
+
+ @SuppressWarnings("unchecked")
+ static final BundlerParamInfo<Set<String>> ADD_MODULES =
+ new StandardBundlerParam<>(
+ Arguments.CLIOptions.ADD_MODULES.getId(),
+ (Class<Set<String>>) (Object) Set.class,
+ p -> new LinkedHashSet<String>(),
+ (s, p) -> new LinkedHashSet<>(Arrays.asList(s.split(",")))
+ );
+
+ @SuppressWarnings("unchecked")
+ static final BundlerParamInfo<Set<String>> LIMIT_MODULES =
+ new StandardBundlerParam<>(
+ "limit-modules",
+ (Class<Set<String>>) (Object) Set.class,
+ p -> new LinkedHashSet<String>(),
+ (s, p) -> new LinkedHashSet<>(Arrays.asList(s.split(",")))
+ );
+
+ static boolean isRuntimeInstaller(Map<String, ? super Object> params) {
+ if (params.containsKey(MODULE.getID()) ||
+ params.containsKey(MAIN_JAR.getID()) ||
+ params.containsKey(PREDEFINED_APP_IMAGE.getID())) {
+ return false; // we are building or are given an application
+ }
+ // runtime installer requires --runtime-image, if this is false
+ // here then we should have thrown error validating args.
+ return params.containsKey(PREDEFINED_RUNTIME_IMAGE.getID());
+ }
+
+ static File getPredefinedAppImage(Map<String, ? super Object> params) {
+ File applicationImage = null;
+ if (PREDEFINED_APP_IMAGE.fetchFrom(params) != null) {
+ applicationImage = PREDEFINED_APP_IMAGE.fetchFrom(params);
+ Log.debug("Using App Image from " + applicationImage);
+ if (!applicationImage.exists()) {
+ throw new RuntimeException(
+ MessageFormat.format(I18N.getString(
+ "message.app-image-dir-does-not-exist"),
+ PREDEFINED_APP_IMAGE.getID(),
+ applicationImage.toString()));
+ }
+ }
+ return applicationImage;
+ }
+
+ static void copyPredefinedRuntimeImage(
+ Map<String, ? super Object> params,
+ AbstractAppImageBuilder appBuilder)
+ throws IOException , ConfigException {
+ File image = PREDEFINED_RUNTIME_IMAGE.fetchFrom(params);
+ if (!image.exists()) {
+ throw new ConfigException(
+ MessageFormat.format(I18N.getString(
+ "message.runtime-image-dir-does-not-exist"),
+ PREDEFINED_RUNTIME_IMAGE.getID(),
+ image.toString()),
+ MessageFormat.format(I18N.getString(
+ "message.runtime-image-dir-does-not-exist.advice"),
+ PREDEFINED_RUNTIME_IMAGE.getID()));
+ }
+ // copy whole runtime, need to skip jmods and src.zip
+ final List<String> excludes = Arrays.asList("jmods", "src.zip");
+ IOUtils.copyRecursive(image.toPath(), appBuilder.getRoot(), excludes);
+
+ // if module-path given - copy modules to appDir/mods
+ List<Path> modulePath =
+ StandardBundlerParam.MODULE_PATH.fetchFrom(params);
+ List<Path> defaultModulePath = getDefaultModulePath();
+ Path dest = appBuilder.getAppModsDir();
+
+ if (dest != null) {
+ for (Path mp : modulePath) {
+ if (!defaultModulePath.contains(mp)) {
+ Files.createDirectories(dest);
+ IOUtils.copyRecursive(mp, dest);
+ }
+ }
+ }
+
+ appBuilder.prepareApplicationFiles();
+ }
+
+ 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();
+ }
+ }
+ }
+
+ 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 ||
+ hasModule || hasAppImage || isRuntimeInstaller(params)) {
+ return;
+ }
+
+ 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 {
+ Files.walk(f.toPath())
+ .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;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ValidOptions.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,145 @@
+/*
+ * 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.jpackage.internal;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import jdk.jpackage.internal.Arguments.CLIOptions;
+
+/**
+ * ValidOptions
+ *
+ * Two basic methods for validating command line options.
+ *
+ * initArgs()
+ * Computes the Map of valid options for each mode on this Platform.
+ *
+ * checkIfSupported(CLIOptions arg)
+ * Determine if the given arg is valid on this platform.
+ *
+ * checkIfImageSupported(CLIOptions arg)
+ * Determine if the given arg is valid for creating app image.
+ *
+ * checkIfInstallerSupported(CLIOptions arg)
+ * Determine if the given arg is valid for creating installer.
+ *
+ */
+class ValidOptions {
+
+ enum USE {
+ ALL, // valid in all cases
+ LAUNCHER, // valid when creating a launcher
+ INSTALL // valid when creating an installer
+ }
+
+ private static final HashMap<String, USE> options = new HashMap<>();
+
+
+ // initializing list of mandatory arguments
+ static {
+ options.put(CLIOptions.NAME.getId(), USE.ALL);
+ options.put(CLIOptions.VERSION.getId(), USE.ALL);
+ options.put(CLIOptions.OUTPUT.getId(), USE.ALL);
+ options.put(CLIOptions.TEMP_ROOT.getId(), USE.ALL);
+ options.put(CLIOptions.VERBOSE.getId(), USE.ALL);
+ options.put(CLIOptions.PREDEFINED_RUNTIME_IMAGE.getId(), USE.ALL);
+ options.put(CLIOptions.RESOURCE_DIR.getId(), USE.ALL);
+ options.put(CLIOptions.IDENTIFIER.getId(), USE.ALL);
+ options.put(CLIOptions.DESCRIPTION.getId(), USE.ALL);
+ options.put(CLIOptions.VENDOR.getId(), USE.ALL);
+ options.put(CLIOptions.COPYRIGHT.getId(), USE.ALL);
+ options.put(CLIOptions.PACKAGE_TYPE.getId(), USE.ALL);
+
+ options.put(CLIOptions.INPUT.getId(), USE.LAUNCHER);
+ options.put(CLIOptions.MODULE.getId(), USE.LAUNCHER);
+ options.put(CLIOptions.MODULE_PATH.getId(), USE.LAUNCHER);
+ options.put(CLIOptions.ADD_MODULES.getId(), USE.LAUNCHER);
+ options.put(CLIOptions.MAIN_JAR.getId(), USE.LAUNCHER);
+ options.put(CLIOptions.APPCLASS.getId(), USE.LAUNCHER);
+ options.put(CLIOptions.ICON.getId(), USE.LAUNCHER);
+ options.put(CLIOptions.ARGUMENTS.getId(), USE.LAUNCHER);
+ options.put(CLIOptions.JAVA_OPTIONS.getId(), USE.LAUNCHER);
+ options.put(CLIOptions.ADD_LAUNCHER.getId(), USE.LAUNCHER);
+
+ options.put(CLIOptions.LICENSE_FILE.getId(), USE.INSTALL);
+ options.put(CLIOptions.INSTALL_DIR.getId(), USE.INSTALL);
+ options.put(CLIOptions.PREDEFINED_APP_IMAGE.getId(), USE.INSTALL);
+
+ options.put(CLIOptions.FILE_ASSOCIATIONS.getId(),
+ (Platform.getPlatform() == Platform.MAC) ? USE.ALL : USE.INSTALL);
+
+ if (Platform.getPlatform() == Platform.WINDOWS) {
+ options.put(CLIOptions.WIN_CONSOLE_HINT.getId(), USE.LAUNCHER);
+
+ options.put(CLIOptions.WIN_MENU_HINT.getId(), USE.INSTALL);
+ options.put(CLIOptions.WIN_MENU_GROUP.getId(), USE.INSTALL);
+ options.put(CLIOptions.WIN_SHORTCUT_HINT.getId(), USE.INSTALL);
+ options.put(CLIOptions.WIN_DIR_CHOOSER.getId(), USE.INSTALL);
+ options.put(CLIOptions.WIN_REGISTRY_NAME.getId(), USE.INSTALL);
+ options.put(CLIOptions.WIN_UPGRADE_UUID.getId(), USE.INSTALL);
+ options.put(CLIOptions.WIN_PER_USER_INSTALLATION.getId(),
+ USE.INSTALL);
+ }
+
+ if (Platform.getPlatform() == Platform.MAC) {
+ options.put(CLIOptions.MAC_SIGN.getId(), USE.ALL);
+ options.put(CLIOptions.MAC_BUNDLE_NAME.getId(), USE.ALL);
+ options.put(CLIOptions.MAC_BUNDLE_IDENTIFIER.getId(), USE.ALL);
+ options.put(CLIOptions.MAC_BUNDLE_SIGNING_PREFIX.getId(),
+ USE.ALL);
+ options.put(CLIOptions.MAC_SIGNING_KEY_NAME.getId(), USE.ALL);
+ options.put(CLIOptions.MAC_SIGNING_KEYCHAIN.getId(), USE.ALL);
+ options.put(CLIOptions.MAC_APP_STORE_CATEGORY.getId(), USE.ALL);
+ options.put(CLIOptions.MAC_APP_STORE_ENTITLEMENTS.getId(),
+ USE.ALL);
+ }
+
+ if (Platform.getPlatform() == Platform.LINUX) {
+ options.put(CLIOptions.LINUX_BUNDLE_NAME.getId(), USE.INSTALL);
+ options.put(CLIOptions.LINUX_DEB_MAINTAINER.getId(), USE.INSTALL);
+ options.put(CLIOptions.LINUX_RPM_LICENSE_TYPE.getId(), USE.INSTALL);
+ options.put(CLIOptions.LINUX_PACKAGE_DEPENDENCIES.getId(),
+ USE.INSTALL);
+ options.put(CLIOptions.LINUX_MENU_GROUP.getId(), USE.INSTALL);
+ }
+ }
+
+ static boolean checkIfSupported(CLIOptions arg) {
+ return options.containsKey(arg.getId());
+ }
+
+ static boolean checkIfImageSupported(CLIOptions arg) {
+ USE use = options.get(arg.getId());
+ return USE.ALL == use || USE.LAUNCHER == use;
+ }
+
+ static boolean checkIfInstallerSupported(CLIOptions arg) {
+ USE use = options.get(arg.getId());
+ return USE.ALL == use || USE.INSTALL == use;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/HelpResources.properties Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,270 @@
+#
+# Copyright (c) 2017, 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.
+#
+#
+
+MSG_Help=Usage: jpackage <options>\n\
+\n\
+Sample usages:\n\
+--------------\n\
+\ Generate a non-modular application image:\n\
+\ jpackage -o outputdir -i inputdir -n name \\\n\
+\ --main-class className --main-jar MyJar.jar\n\
+\ Generate a modular application image:\n\
+\ jpackage -o outputdir -n name -p modulePath -m moduleName/className\n\
+\ To provide your own options to jlink, run jlink separately:\n\
+\ jlink --output appRuntimeImage -p ModulePath -m moduleName \\\n\
+\ --no-header-files [<additional jlink options>...]\n\
+\ jpackage -o outputdir -n name \\\n\
+\ -m moduleName/className --runtime-image appRuntimeImage\n\
+\ Generate an application package:\n\
+\ jpackage --package-type <type> -o outputdir -n name \\\n\
+\ -p modulePath -m moduleName/className\n\
+\ jpackage --package-type <type> -i inputdir -o outputdir -n name \\\n\
+\ --main-class package.ClassName --main-jar MyJar.jar\n\
+\ jpackage --package-type <type> -o outputdir -n name \\\n\
+\ --app-image <app image dir>\n\
+\ Generate a Java runtime package:\n\
+\ jpackage --package-type <type> -o outputdir -n name \\\n\
+\ --runtime-image <runtime-image>\n\
+\n\
+Generic Options:\n\
+\ @<filename> \n\
+\ Read options and/or mode from a file \n\
+\ This option can be used multiple times.\n\
+\ --package-type <type> \n\
+\ The type of package to create\n\
+\ Valid values are: {1} \n\
+\ If this option is not specified an application image will be\n\
+\ created.\n\
+\ --app-version <version>\n\
+\ Version of the application and/or package\n\
+\ --copyright <copyright string>\n\
+\ Copyright for the application\n\
+\ --description <description string>\n\
+\ Description of the application\n\
+\ --help -h \n\
+\ Print the usage text with a list and description of each valid\n\
+\ option for the current platform to the output stream, and exit\n\
+\ --name -n <name>\n\
+\ Name of the application and/or package\n\
+\ --output -o <output path>\n\
+\ Path where generated output file is placed\n\
+\ (absolute path or relative to the current directory)\n\
+\ --temp-root <file path>\n\
+\ Path of a new or empty directory used to create temporary files\n\
+\ (absolute path or relative to the current directory)\n\
+\ If specified, the temp-root will not be removed upon the task\n\
+\ completion and must be removed manually\n\
+\ If not specified, a temporary directory will be created and\n\
+\ removed upon the task completion.\n\
+\ --vendor <vendor string>\n\
+\ Vendor of the application\n\
+\ --verbose\n\
+\ Enables verbose output\n\
+\ --version\n\
+\ Print the product version to the output stream and exit\n\
+\n\
+\Options for creating the runtime image:\n\
+\ --add-modules <module name>[,<module name>...]\n\
+\ A comma (",") separated list of modules to add.\n\
+\ This module list, along with the main module (if specified)\n\
+\ will be passed to jlink as the --add-module argument.\n\
+\ if not specified, either just the main module (if --module is\n\
+\ specified), or the default set of modules (if --main-jar is \n\
+\ specified) are used.\n\
+\ This option can be used multiple times.\n\
+\ --module-path -p <module path>...\n\
+\ A {0} separated list of paths\n\
+\ Each path is either a directory of modules or the path to a\n\
+\ modular jar.\n\
+\ (each path is absolute or relative to the current directory)\n\
+\ This option can be used multiple times.\n\
+\ --runtime-image <file path>\n\
+\ Path of the predefined runtime image that will be copied into\n\
+\ the application image\n\
+\ (absolute path or relative to the current directory)\n\
+\ If --runtime-image is not specified, jpackage will run jlink to\n\
+\ create the runtime image using options:\n\
+\ --strip-debug, --no-header-files, --no-man-pages, and\n\
+\ --strip-native-commands. --bind-services will also be added if\n\
+\ --add-modules is not specified.\n\
+\n\
+\Options for creating the application image:\n\
+\ --icon <icon file path>\n\
+\ Path of the icon of the application bundle\n\
+\ (absolute path or relative to the current directory)\n\
+\ --input -i <input path>\n\
+\ Path of the input directory that contains the files to be packaged\n\
+\ (absolute path or relative to the current directory)\n\
+\ All files in the input directory will be packaged into the\n\
+\ application image.\n\
+\n\
+\Options for creating the application launcher(s):\n\
+\ --add-launcher <launcher name>=<file path>\n\
+\ Name of launcher, and a path to a Properties file that contains\n\
+\ a list of key, value pairs\n\
+\ (absolute path or relative to the current directory)\n\
+\ The keys "module", "main-jar", "main-class",\n\
+\ "arguments", "java-options", "app-version", "icon", and\n\
+\ "win-console" can be used.\n\
+\ These options are added to, or used to overwrite, the original\n\
+\ command line options to build an additional alternative launcher.\n\
+\ The main application launcher will be built from the command line\n\
+\ options. Additional alternative launchers can be built using\n\
+\ this option, and this option can be used multiple times to\n\
+\ build multiple additional launchers. \n\
+\ --arguments <main class arguments>\n\
+\ Command line arguments to pass to the main class if no command\n\
+\ line arguments are given to the launcher\n\
+\ This option can be used multiple times.\n\
+\ --java-options <java options>\n\
+\ Options to pass to the Java runtime\n\
+\ This option can be used multiple times.\n\
+\ --main-class <class name>\n\
+\ Qualified name of the application main class to execute\n\
+\ This option can only be used if --main-jar is specified.\n\
+\ --main-jar <main jar file>\n\
+\ The main JAR of the application; containing the main class\n\
+\ (specified as a path relative to the input path)\n\
+\ Either --module or --main-jar option can be specified but not\n\
+\ both.\n\
+\ --module -m <module name>[/<main class>]\n\
+\ The main module (and optionally main class) of the application\n\
+\ This module must be located on the module path.\n\
+\ When this option is specified, the main module will be linked\n\
+\ in the Java runtime image. Either --module or --main-jar\n\
+\ option can be specified but not both.\n\
+{2}\n\
+\Options for creating the application package:\n\
+\ --app-image <file path>\n\
+\ Location of the predefined application image that is used\n\
+\ to build an installable package\n\
+\ (absolute path or relative to the current directory)\n\
+\ --file-associations <file path>\n\
+\ Path to a Properties file that contains list of key, value pairs\n\
+\ (absolute path or relative to the current directory)\n\
+\ The keys "extension", "mime-type", "icon", and "description"\n\
+\ can be used to describe the association.\n\
+\ This option can be used multiple times.\n\
+\ --identifier <id string>\n\
+\ An identifier that uniquely identifies the application\n\
+\ Defaults to the main class name.\n\
+\ The value should be a valid DNS name.\n\
+\ --install-dir <file path>\n\
+\ {4}\
+\ --license-file <file path>\n\
+\ Path to the license file\n\
+\ (absolute path or relative to the current directory)\n\
+\ --resource-dir <path>\n\
+\ Path to override jpackage resources\n\
+\ Icons, template files, and other resources of jpackage can be\n\
+\ over-ridden by adding replacement resources to this directory.\n\
+\ (absolute path or relative to the current directory)\n\
+\ --runtime-image <file-path>\n\
+\ Path of the predefined runtime image to install\n\
+\ (absolute path or relative to the current directory)\n\
+\ Option is required when creating a runtime package.\n\
+\n\
+\Platform dependent options for creating the application package:\n\
+{3}
+
+MSG_Help_win_launcher=\
+\n\
+\Platform dependent option for creating the application launcher:\n\
+\ --win-console\n\
+\ Creates a console launcher for the application, should be\n\
+\ specified for application which requires console interactions\n\
+
+MSG_Help_win_install=\
+\ --win-dir-chooser\n\
+\ Adds a dialog to enable the user to choose a directory in which\n\
+\ the product is installed\n\
+\ --win-menu\n\
+\ Adds the application to the system menu\n\
+\ --win-menu-group <menu group name>\n\
+\ Start Menu group this application is placed in\n\
+\ --win-per-user-install\n\
+\ Request to perform an install on a per-user basis\n\
+\ --win-registry-name <registry name>\n\
+\ Name of the application for registry references.\n\
+\ The default is the Application Name with only\n\
+\ alphanumerics, dots, and dashes (no whitespace)\n\
+\ --win-shortcut\n\
+\ Creates a desktop shortcut for the application\n\
+\ --win-upgrade-uuid <id string>\n\
+\ UUID associated with upgrades for this package\n\
+
+MSG_Help_win_install_dir=\
+\Relative sub-path under the default installation location\n\
+
+MSG_Help_mac_launcher=\
+\ --mac-bundle-identifier <ID string>\n\
+\ An identifier that uniquely identifies the application for MacOSX\n\
+\ Defaults to the value of --identifier option.\n\
+\ May only use alphanumeric (A-Z,a-z,0-9), hyphen (-),\n\
+\ and period (.) characters.\n\
+\ --mac-bundle-name <name string>\n\
+\ Name of the application as it appears in the Menu Bar\n\
+\ This can be different from the application name.\n\
+\ This name must be less than 16 characters long and be suitable for\n\
+\ displaying in the menu bar and the application Info window.\n\
+\ Defaults to the application name.\n\
+\ --mac-bundle-signing-prefix <prefix string>\n\
+\ When signing the application bundle, this value is prefixed to all\n\
+\ components that need to be signed that don't have\n\
+\ an existing bundle identifier.\n\
+\ --mac-sign\n\
+\ Request that the bundle be signed\n\
+\ --mac-signing-keychain <file path>\n\
+\ Path of the keychain to search for the signing identity\n\
+\ (absolute path or relative to the current directory).\n\
+\ If not specified, the standard keychains are used.\n\
+\ --mac-signing-key-user-name <team name>\n\
+\ Team name portion in Apple signing identities' names.\n\
+\ For example "Developer ID Application: "\n\
+
+MSG_Help_linux_install=\
+\ --linux-bundle-name <bundle name>\n\
+\ Name for Linux bundle, defaults to the application name\n\
+\ --linux-deb-maintainer <email address>\n\
+\ Maintainer for .deb bundle\n\
+\ --linux-menu-group <menu-group-name>\n\
+\ Menu group this application is placed in\n\
+\ --linux-package-deps\n\
+\ Required packages or capabilities for the application\n\
+\ --linux-rpm-license-type <type string>\n\
+\ Type of the license ("License: <value>" of the RPM .spec)\n\
+
+MSG_Help_mac_linux_install_dir=\
+\Absolute path of the installation directory of the application\n\
+
+MSG_Help_default_install_dir=\
+\Absolute path of the installation directory of the application on OS X\n\
+\ or Linux. Relative sub-path of the installation location of the application\n\
+\ such as "Program Files" or "AppData" on Windows.\n\
+
+MSG_Help_no_args=Usage: jpackage <mode> <options>\n\
+\Use jpackage --help (or -h) for a list of possible options\
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/HelpResources_ja.properties Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,270 @@
+#
+# Copyright (c) 2017, 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.
+#
+#
+
+MSG_Help=Usage: jpackage <options>\n\
+\n\
+Sample usages:\n\
+--------------\n\
+\ Generate a non-modular application image:\n\
+\ jpackage -o outputdir -i inputdir -n name \\\n\
+\ --main-class className --main-jar MyJar.jar\n\
+\ Generate a modular application image:\n\
+\ jpackage -o outputdir -n name -p modulePath -m moduleName/className\n\
+\ To provide your own options to jlink, run jlink separately:\n\
+\ jlink --output appRuntimeImage -p ModulePath -m moduleName \\\n\
+\ --no-header-files [<additional jlink options>...]\n\
+\ jpackage -o outputdir -n name \\\n\
+\ -m moduleName/className --runtime-image appRuntimeImage\n\
+\ Generate an application package:\n\
+\ jpackage --package-type <type> -o outputdir -n name \\\n\
+\ -p modulePath -m moduleName/className\n\
+\ jpackage --package-type <type> -i inputdir -o outputdir -n name \\\n\
+\ --main-class package.ClassName --main-jar MyJar.jar\n\
+\ jpackage --package-type <type> -o outputdir -n name \\\n\
+\ --app-image <app image dir>\n\
+\ Generate a Java runtime package:\n\
+\ jpackage --package-type <type> -o outputdir -n name \\\n\
+\ --runtime-image <runtime-image>\n\
+\n\
+Generic Options:\n\
+\ @<filename> \n\
+\ Read options and/or mode from a file \n\
+\ This option can be used multiple times.\n\
+\ --package-type <type> \n\
+\ The type of package to create\n\
+\ Valid values are: {1} \n\
+\ If this option is not specified an application image will be\n\
+\ created.\n\
+\ --app-version <version>\n\
+\ Version of the application and/or package\n\
+\ --copyright <copyright string>\n\
+\ Copyright for the application\n\
+\ --description <description string>\n\
+\ Description of the application\n\
+\ --help -h \n\
+\ Print the usage text with a list and description of each valid\n\
+\ option for the current platform to the output stream, and exit\n\
+\ --name -n <name>\n\
+\ Name of the application and/or package\n\
+\ --output -o <output path>\n\
+\ Path where generated output file is placed\n\
+\ (absolute path or relative to the current directory)\n\
+\ --temp-root <file path>\n\
+\ Path of a new or empty directory used to create temporary files\n\
+\ (absolute path or relative to the current directory)\n\
+\ If specified, the temp-root will not be removed upon the task\n\
+\ completion and must be removed manually\n\
+\ If not specified, a temporary directory will be created and\n\
+\ removed upon the task completion.\n\
+\ --vendor <vendor string>\n\
+\ Vendor of the application\n\
+\ --verbose\n\
+\ Enables verbose output\n\
+\ --version\n\
+\ Print the product version to the output stream and exit\n\
+\n\
+\Options for creating the runtime image:\n\
+\ --add-modules <module name>[,<module name>...]\n\
+\ A comma (",") separated list of modules to add.\n\
+\ This module list, along with the main module (if specified)\n\
+\ will be passed to jlink as the --add-module argument.\n\
+\ if not specified, either just the main module (if --module is\n\
+\ specified), or the default set of modules (if --main-jar is \n\
+\ specified) are used.\n\
+\ This option can be used multiple times.\n\
+\ --module-path -p <module path>...\n\
+\ A {0} separated list of paths\n\
+\ Each path is either a directory of modules or the path to a\n\
+\ modular jar.\n\
+\ (each path is absolute or relative to the current directory)\n\
+\ This option can be used multiple times.\n\
+\ --runtime-image <file path>\n\
+\ Path of the predefined runtime image that will be copied into\n\
+\ the application image\n\
+\ (absolute path or relative to the current directory)\n\
+\ If --runtime-image is not specified, jpackage will run jlink to\n\
+\ create the runtime image using options:\n\
+\ --strip-debug, --no-header-files, --no-man-pages, and\n\
+\ --strip-native-commands. --bind-services will also be added if\n\
+\ --add-modules is not specified.\n\
+\n\
+\Options for creating the application image:\n\
+\ --icon <icon file path>\n\
+\ Path of the icon of the application bundle\n\
+\ (absolute path or relative to the current directory)\n\
+\ --input -i <input path>\n\
+\ Path of the input directory that contains the files to be packaged\n\
+\ (absolute path or relative to the current directory)\n\
+\ All files in the input directory will be packaged into the\n\
+\ application image.\n\
+\n\
+\Options for creating the application launcher(s):\n\
+\ --add-launcher <launcher name>=<file path>\n\
+\ Name of launcher, and a path to a Properties file that contains\n\
+\ a list of key, value pairs\n\
+\ (absolute path or relative to the current directory)\n\
+\ The keys "module", "main-jar", "main-class",\n\
+\ "arguments", "java-options", "app-version", "icon", and\n\
+\ "win-console" can be used.\n\
+\ These options are added to, or used to overwrite, the original\n\
+\ command line options to build an additional alternative launcher.\n\
+\ The main application launcher will be built from the command line\n\
+\ options. Additional alternative launchers can be built using\n\
+\ this option, and this option can be used multiple times to\n\
+\ build multiple additional launchers. \n\
+\ --arguments <main class arguments>\n\
+\ Command line arguments to pass to the main class if no command\n\
+\ line arguments are given to the launcher\n\
+\ This option can be used multiple times.\n\
+\ --java-options <java options>\n\
+\ Options to pass to the Java runtime\n\
+\ This option can be used multiple times.\n\
+\ --main-class <class name>\n\
+\ Qualified name of the application main class to execute\n\
+\ This option can only be used if --main-jar is specified.\n\
+\ --main-jar <main jar file>\n\
+\ The main JAR of the application; containing the main class\n\
+\ (specified as a path relative to the input path)\n\
+\ Either --module or --main-jar option can be specified but not\n\
+\ both.\n\
+\ --module -m <module name>[/<main class>]\n\
+\ The main module (and optionally main class) of the application\n\
+\ This module must be located on the module path.\n\
+\ When this option is specified, the main module will be linked\n\
+\ in the Java runtime image. Either --module or --main-jar\n\
+\ option can be specified but not both.\n\
+{2}\n\
+\Options for creating the application package:\n\
+\ --app-image <file path>\n\
+\ Location of the predefined application image that is used\n\
+\ to build an installable package\n\
+\ (absolute path or relative to the current directory)\n\
+\ --file-associations <file path>\n\
+\ Path to a Properties file that contains list of key, value pairs\n\
+\ (absolute path or relative to the current directory)\n\
+\ The keys "extension", "mime-type", "icon", and "description"\n\
+\ can be used to describe the association.\n\
+\ This option can be used multiple times.\n\
+\ --identifier <id string>\n\
+\ An identifier that uniquely identifies the application\n\
+\ Defaults to the main class name.\n\
+\ The value should be a valid DNS name.\n\
+\ --install-dir <file path>\n\
+\ {4}\
+\ --license-file <file path>\n\
+\ Path to the license file\n\
+\ (absolute path or relative to the current directory)\n\
+\ --resource-dir <path>\n\
+\ Path to override jpackage resources\n\
+\ Icons, template files, and other resources of jpackage can be\n\
+\ over-ridden by adding replacement resources to this directory.\n\
+\ (absolute path or relative to the current directory)\n\
+\ --runtime-image <file-path>\n\
+\ Path of the predefined runtime image to install\n\
+\ (absolute path or relative to the current directory)\n\
+\ Option is required when creating a runtime package.\n\
+\n\
+\Platform dependent options for creating the application package:\n\
+{3}
+
+MSG_Help_win_launcher=\
+\n\
+\Platform dependent option for creating the application launcher:\n\
+\ --win-console\n\
+\ Creates a console launcher for the application, should be\n\
+\ specified for application which requires console interactions\n\
+
+MSG_Help_win_install=\
+\ --win-dir-chooser\n\
+\ Adds a dialog to enable the user to choose a directory in which\n\
+\ the product is installed\n\
+\ --win-menu\n\
+\ Adds the application to the system menu\n\
+\ --win-menu-group <menu group name>\n\
+\ Start Menu group this application is placed in\n\
+\ --win-per-user-install\n\
+\ Request to perform an install on a per-user basis\n\
+\ --win-registry-name <registry name>\n\
+\ Name of the application for registry references.\n\
+\ The default is the Application Name with only\n\
+\ alphanumerics, dots, and dashes (no whitespace)\n\
+\ --win-shortcut\n\
+\ Creates a desktop shortcut for the application\n\
+\ --win-upgrade-uuid <id string>\n\
+\ UUID associated with upgrades for this package\n\
+
+MSG_Help_win_install_dir=\
+\Relative sub-path under the default installation location\n\
+
+MSG_Help_mac_launcher=\
+\ --mac-bundle-identifier <ID string>\n\
+\ An identifier that uniquely identifies the application for MacOSX\n\
+\ Defaults to the value of --identifier option.\n\
+\ May only use alphanumeric (A-Z,a-z,0-9), hyphen (-),\n\
+\ and period (.) characters.\n\
+\ --mac-bundle-name <name string>\n\
+\ Name of the application as it appears in the Menu Bar\n\
+\ This can be different from the application name.\n\
+\ This name must be less than 16 characters long and be suitable for\n\
+\ displaying in the menu bar and the application Info window.\n\
+\ Defaults to the application name.\n\
+\ --mac-bundle-signing-prefix <prefix string>\n\
+\ When signing the application bundle, this value is prefixed to all\n\
+\ components that need to be signed that don't have\n\
+\ an existing bundle identifier.\n\
+\ --mac-sign\n\
+\ Request that the bundle be signed\n\
+\ --mac-signing-keychain <file path>\n\
+\ Path of the keychain to search for the signing identity\n\
+\ (absolute path or relative to the current directory).\n\
+\ If not specified, the standard keychains are used.\n\
+\ --mac-signing-key-user-name <team name>\n\
+\ Team name portion in Apple signing identities' names.\n\
+\ For example "Developer ID Application: "\n\
+
+MSG_Help_linux_install=\
+\ --linux-bundle-name <bundle name>\n\
+\ Name for Linux bundle, defaults to the application name\n\
+\ --linux-deb-maintainer <email address>\n\
+\ Maintainer for .deb bundle\n\
+\ --linux-menu-group <menu-group-name>\n\
+\ Menu group this application is placed in\n\
+\ --linux-package-deps\n\
+\ Required packages or capabilities for the application\n\
+\ --linux-rpm-license-type <type string>\n\
+\ Type of the license ("License: <value>" of the RPM .spec)\n\
+
+MSG_Help_mac_linux_install_dir=\
+\Absolute path of the installation directory of the application\n\
+
+MSG_Help_default_install_dir=\
+\Absolute path of the installation directory of the application on OS X\n\
+\ or Linux. Relative sub-path of the installation location of the application\n\
+\ such as "Program Files" or "AppData" on Windows.\n\
+
+MSG_Help_no_args=Usage: jpackage <mode> <options>\n\
+\Use jpackage --help (or -h) for a list of possible options\
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/HelpResources_zh_CN.properties Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,270 @@
+#
+# Copyright (c) 2017, 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.
+#
+#
+
+MSG_Help=Usage: jpackage <options>\n\
+\n\
+Sample usages:\n\
+--------------\n\
+\ Generate a non-modular application image:\n\
+\ jpackage -o outputdir -i inputdir -n name \\\n\
+\ --main-class className --main-jar MyJar.jar\n\
+\ Generate a modular application image:\n\
+\ jpackage -o outputdir -n name -p modulePath -m moduleName/className\n\
+\ To provide your own options to jlink, run jlink separately:\n\
+\ jlink --output appRuntimeImage -p ModulePath -m moduleName \\\n\
+\ --no-header-files [<additional jlink options>...]\n\
+\ jpackage -o outputdir -n name \\\n\
+\ -m moduleName/className --runtime-image appRuntimeImage\n\
+\ Generate an application package:\n\
+\ jpackage --package-type <type> -o outputdir -n name \\\n\
+\ -p modulePath -m moduleName/className\n\
+\ jpackage --package-type <type> -i inputdir -o outputdir -n name \\\n\
+\ --main-class package.ClassName --main-jar MyJar.jar\n\
+\ jpackage --package-type <type> -o outputdir -n name \\\n\
+\ --app-image <app image dir>\n\
+\ Generate a Java runtime package:\n\
+\ jpackage --package-type <type> -o outputdir -n name \\\n\
+\ --runtime-image <runtime-image>\n\
+\n\
+Generic Options:\n\
+\ @<filename> \n\
+\ Read options and/or mode from a file \n\
+\ This option can be used multiple times.\n\
+\ --package-type <type> \n\
+\ The type of package to create\n\
+\ Valid values are: {1} \n\
+\ If this option is not specified an application image will be\n\
+\ created.\n\
+\ --app-version <version>\n\
+\ Version of the application and/or package\n\
+\ --copyright <copyright string>\n\
+\ Copyright for the application\n\
+\ --description <description string>\n\
+\ Description of the application\n\
+\ --help -h \n\
+\ Print the usage text with a list and description of each valid\n\
+\ option for the current platform to the output stream, and exit\n\
+\ --name -n <name>\n\
+\ Name of the application and/or package\n\
+\ --output -o <output path>\n\
+\ Path where generated output file is placed\n\
+\ (absolute path or relative to the current directory)\n\
+\ --temp-root <file path>\n\
+\ Path of a new or empty directory used to create temporary files\n\
+\ (absolute path or relative to the current directory)\n\
+\ If specified, the temp-root will not be removed upon the task\n\
+\ completion and must be removed manually\n\
+\ If not specified, a temporary directory will be created and\n\
+\ removed upon the task completion.\n\
+\ --vendor <vendor string>\n\
+\ Vendor of the application\n\
+\ --verbose\n\
+\ Enables verbose output\n\
+\ --version\n\
+\ Print the product version to the output stream and exit\n\
+\n\
+\Options for creating the runtime image:\n\
+\ --add-modules <module name>[,<module name>...]\n\
+\ A comma (",") separated list of modules to add.\n\
+\ This module list, along with the main module (if specified)\n\
+\ will be passed to jlink as the --add-module argument.\n\
+\ if not specified, either just the main module (if --module is\n\
+\ specified), or the default set of modules (if --main-jar is \n\
+\ specified) are used.\n\
+\ This option can be used multiple times.\n\
+\ --module-path -p <module path>...\n\
+\ A {0} separated list of paths\n\
+\ Each path is either a directory of modules or the path to a\n\
+\ modular jar.\n\
+\ (each path is absolute or relative to the current directory)\n\
+\ This option can be used multiple times.\n\
+\ --runtime-image <file path>\n\
+\ Path of the predefined runtime image that will be copied into\n\
+\ the application image\n\
+\ (absolute path or relative to the current directory)\n\
+\ If --runtime-image is not specified, jpackage will run jlink to\n\
+\ create the runtime image using options:\n\
+\ --strip-debug, --no-header-files, --no-man-pages, and\n\
+\ --strip-native-commands. --bind-services will also be added if\n\
+\ --add-modules is not specified.\n\
+\n\
+\Options for creating the application image:\n\
+\ --icon <icon file path>\n\
+\ Path of the icon of the application bundle\n\
+\ (absolute path or relative to the current directory)\n\
+\ --input -i <input path>\n\
+\ Path of the input directory that contains the files to be packaged\n\
+\ (absolute path or relative to the current directory)\n\
+\ All files in the input directory will be packaged into the\n\
+\ application image.\n\
+\n\
+\Options for creating the application launcher(s):\n\
+\ --add-launcher <launcher name>=<file path>\n\
+\ Name of launcher, and a path to a Properties file that contains\n\
+\ a list of key, value pairs\n\
+\ (absolute path or relative to the current directory)\n\
+\ The keys "module", "main-jar", "main-class",\n\
+\ "arguments", "java-options", "app-version", "icon", and\n\
+\ "win-console" can be used.\n\
+\ These options are added to, or used to overwrite, the original\n\
+\ command line options to build an additional alternative launcher.\n\
+\ The main application launcher will be built from the command line\n\
+\ options. Additional alternative launchers can be built using\n\
+\ this option, and this option can be used multiple times to\n\
+\ build multiple additional launchers. \n\
+\ --arguments <main class arguments>\n\
+\ Command line arguments to pass to the main class if no command\n\
+\ line arguments are given to the launcher\n\
+\ This option can be used multiple times.\n\
+\ --java-options <java options>\n\
+\ Options to pass to the Java runtime\n\
+\ This option can be used multiple times.\n\
+\ --main-class <class name>\n\
+\ Qualified name of the application main class to execute\n\
+\ This option can only be used if --main-jar is specified.\n\
+\ --main-jar <main jar file>\n\
+\ The main JAR of the application; containing the main class\n\
+\ (specified as a path relative to the input path)\n\
+\ Either --module or --main-jar option can be specified but not\n\
+\ both.\n\
+\ --module -m <module name>[/<main class>]\n\
+\ The main module (and optionally main class) of the application\n\
+\ This module must be located on the module path.\n\
+\ When this option is specified, the main module will be linked\n\
+\ in the Java runtime image. Either --module or --main-jar\n\
+\ option can be specified but not both.\n\
+{2}\n\
+\Options for creating the application package:\n\
+\ --app-image <file path>\n\
+\ Location of the predefined application image that is used\n\
+\ to build an installable package\n\
+\ (absolute path or relative to the current directory)\n\
+\ --file-associations <file path>\n\
+\ Path to a Properties file that contains list of key, value pairs\n\
+\ (absolute path or relative to the current directory)\n\
+\ The keys "extension", "mime-type", "icon", and "description"\n\
+\ can be used to describe the association.\n\
+\ This option can be used multiple times.\n\
+\ --identifier <id string>\n\
+\ An identifier that uniquely identifies the application\n\
+\ Defaults to the main class name.\n\
+\ The value should be a valid DNS name.\n\
+\ --install-dir <file path>\n\
+\ {4}\
+\ --license-file <file path>\n\
+\ Path to the license file\n\
+\ (absolute path or relative to the current directory)\n\
+\ --resource-dir <path>\n\
+\ Path to override jpackage resources\n\
+\ Icons, template files, and other resources of jpackage can be\n\
+\ over-ridden by adding replacement resources to this directory.\n\
+\ (absolute path or relative to the current directory)\n\
+\ --runtime-image <file-path>\n\
+\ Path of the predefined runtime image to install\n\
+\ (absolute path or relative to the current directory)\n\
+\ Option is required when creating a runtime package.\n\
+\n\
+\Platform dependent options for creating the application package:\n\
+{3}
+
+MSG_Help_win_launcher=\
+\n\
+\Platform dependent option for creating the application launcher:\n\
+\ --win-console\n\
+\ Creates a console launcher for the application, should be\n\
+\ specified for application which requires console interactions\n\
+
+MSG_Help_win_install=\
+\ --win-dir-chooser\n\
+\ Adds a dialog to enable the user to choose a directory in which\n\
+\ the product is installed\n\
+\ --win-menu\n\
+\ Adds the application to the system menu\n\
+\ --win-menu-group <menu group name>\n\
+\ Start Menu group this application is placed in\n\
+\ --win-per-user-install\n\
+\ Request to perform an install on a per-user basis\n\
+\ --win-registry-name <registry name>\n\
+\ Name of the application for registry references.\n\
+\ The default is the Application Name with only\n\
+\ alphanumerics, dots, and dashes (no whitespace)\n\
+\ --win-shortcut\n\
+\ Creates a desktop shortcut for the application\n\
+\ --win-upgrade-uuid <id string>\n\
+\ UUID associated with upgrades for this package\n\
+
+MSG_Help_win_install_dir=\
+\Relative sub-path under the default installation location\n\
+
+MSG_Help_mac_launcher=\
+\ --mac-bundle-identifier <ID string>\n\
+\ An identifier that uniquely identifies the application for MacOSX\n\
+\ Defaults to the value of --identifier option.\n\
+\ May only use alphanumeric (A-Z,a-z,0-9), hyphen (-),\n\
+\ and period (.) characters.\n\
+\ --mac-bundle-name <name string>\n\
+\ Name of the application as it appears in the Menu Bar\n\
+\ This can be different from the application name.\n\
+\ This name must be less than 16 characters long and be suitable for\n\
+\ displaying in the menu bar and the application Info window.\n\
+\ Defaults to the application name.\n\
+\ --mac-bundle-signing-prefix <prefix string>\n\
+\ When signing the application bundle, this value is prefixed to all\n\
+\ components that need to be signed that don't have\n\
+\ an existing bundle identifier.\n\
+\ --mac-sign\n\
+\ Request that the bundle be signed\n\
+\ --mac-signing-keychain <file path>\n\
+\ Path of the keychain to search for the signing identity\n\
+\ (absolute path or relative to the current directory).\n\
+\ If not specified, the standard keychains are used.\n\
+\ --mac-signing-key-user-name <team name>\n\
+\ Team name portion in Apple signing identities' names.\n\
+\ For example "Developer ID Application: "\n\
+
+MSG_Help_linux_install=\
+\ --linux-bundle-name <bundle name>\n\
+\ Name for Linux bundle, defaults to the application name\n\
+\ --linux-deb-maintainer <email address>\n\
+\ Maintainer for .deb bundle\n\
+\ --linux-menu-group <menu-group-name>\n\
+\ Menu group this application is placed in\n\
+\ --linux-package-deps\n\
+\ Required packages or capabilities for the application\n\
+\ --linux-rpm-license-type <type string>\n\
+\ Type of the license ("License: <value>" of the RPM .spec)\n\
+
+MSG_Help_mac_linux_install_dir=\
+\Absolute path of the installation directory of the application\n\
+
+MSG_Help_default_install_dir=\
+\Absolute path of the installation directory of the application on OS X\n\
+\ or Linux. Relative sub-path of the installation location of the application\n\
+\ such as "Program Files" or "AppData" on Windows.\n\
+
+MSG_Help_no_args=Usage: jpackage <mode> <options>\n\
+\Use jpackage --help (or -h) for a list of possible options\
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/MainResources.properties Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,84 @@
+#
+# Copyright (c) 2017, 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.
+#
+#
+
+param.copyright.default=Copyright (C) {0,date,YYYY}
+param.description.default=none
+param.vendor.default=Unknown
+param.version.default=1.0
+
+message.using-default-resource=Using default package resource {0} {1} (add {2} to the resource-dir to customize).
+message.no-default-resource=no default package resource {0} {1} (add {2} to the resource-dir to customize).
+message.using-custom-resource-from-file=Using custom package resource {0} (loaded from file {1}).
+message.using-custom-resource=Using custom package resource {0} (loaded from {1}).
+message.creating-app-bundle=Creating app bundle: {0} in {1}.
+message.app-image-dir-does-not-exist=Specified application image directory {0}: {1} does not exists.
+message.app-image-dir-does-not-exist.advice=Confirm that the value for {0} exists.
+message.runtime-image-dir-does-not-exist=Specified runtime image directory {0}: {1} does not exists.
+message.runtime-image-dir-does-not-exist.advice=Confirm that the value for {0} exists.
+message.debug-working-directory=Kept working directory for debug: {0}.
+message.bundle-created=Succeeded in building {0} bundle.
+
+error.cannot-create-output-dir=Output directory {0} cannot be created.
+error.cannot-write-to-output-dir=Output directory {0} is not writable.
+error.root-exists=Error: Application output directory {0} already exists.
+error.no-main-class-with-main-jar=A main class was not specified nor was one found in the jar {0}.
+error.no-main-class-with-main-jar.advice=Specify a main class or ensure that the jar {0} specifies one in the manifest.
+error.no-main-class=A main class was not specified nor was one found in the supplied application resources.
+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.
+
+warning.module.does.not.exist=Module [{0}] does not exist.
+warning.no.jdk.modules.found=Warning: No JDK Modules found.
+
+MSG_BundlerFailed=Error: Bundler "{1}" ({0}) failed to produce a bundle.
+MSG_BundlerConfigException=Bundler {0} skipped because of a configuration problem: {1}. \n\
+Advice to fix: {2}
+MSG_BundlerConfigExceptionNoAdvice=Bundler {0} skipped because of a configuration problem: {1}.
+MSG_BundlerRuntimeException=Bundler {0} failed because of {1}.
+MSG_BundlerFailed=Error: Bundler "{1}" ({0}) failed to produce a bundle.
+
+ERR_NoMainClass=Error: Main application class is missing.
+ERR_UnsupportedOption=Error: Option [{0}] is not valid on this platform.
+ERR_InvalidTypeOption=Error: Option [{0}] is not valid with package-type [{1}].
+ERR_NoInstallerEntryPoint=Error: Option [{0}] is not valid without --module or --main-jar entry point option.
+
+ERR_MissingArgument=Error: Missing argument: {0}.
+ERR_MissingAppResources=Error: No application jars found.
+ERR_AppImageNotExist=Error: App image directory "{0}" does not exist.
+ERR_NoAddLauncherName=Error: --add-launcher option requires a name and a file path (--add-launcher <name>=<file path>).
+ERR_NoUniqueName=Error: --add-launcher <name>=<file path> requires a unique name.
+ERR_NoJreInstallerName=Error: Jre Installers require a name parameter.
+ERR_InvalidAppName=Error: Invalid Application name: {0}.
+ERR_InvalidSLName=Error: Invalid Add Launcher name: {0}.
+ERR_LicenseFileNotExit=Error: Specified license file does not exist.
+ERR_BuildRootInvalid=Error: temp-root ({0}) must be non-existant directory.
+ERR_InvalidOption=Error: Invalid Option: [{0}].
+ERR_InvalidInstallerType=Error: Invalid or unsupported package type: [{0}].
+ERR_BothMainJarAndModule=Error: Cannot have both --main-jar and --module Options.
+ERR_NoEntryPoint=Error: creating application image requires --main-jar or --module Option.
+ERR_InputNotDirectory=Error: Input directory specified is not a directory: {0}.
+ERR_CannotReadInputDir=Error: No permission to read from input directory: {0}.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/MainResources_ja.properties Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,84 @@
+#
+# Copyright (c) 2017, 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.
+#
+#
+
+param.copyright.default=Copyright (C) {0,date,YYYY}
+param.description.default=none
+param.vendor.default=Unknown
+param.version.default=1.0
+
+message.using-default-resource=Using default package resource {0} {1} (add {2} to the resource-dir to customize).
+message.no-default-resource=no default package resource {0} {1} (add {2} to the resource-dir to customize).
+message.using-custom-resource-from-file=Using custom package resource {0} (loaded from file {1}).
+message.using-custom-resource=Using custom package resource {0} (loaded from {1}).
+message.creating-app-bundle=Creating app bundle: {0} in {1}.
+message.app-image-dir-does-not-exist=Specified application image directory {0}: {1} does not exists.
+message.app-image-dir-does-not-exist.advice=Confirm that the value for {0} exists.
+message.runtime-image-dir-does-not-exist=Specified runtime image directory {0}: {1} does not exists.
+message.runtime-image-dir-does-not-exist.advice=Confirm that the value for {0} exists.
+message.debug-working-directory=Kept working directory for debug: {0}.
+message.bundle-created=Succeeded in building {0} bundle.
+
+error.cannot-create-output-dir=Output directory {0} cannot be created.
+error.cannot-write-to-output-dir=Output directory {0} is not writable.
+error.root-exists=Error: Application output directory {0} already exists.
+error.no-main-class-with-main-jar=A main class was not specified nor was one found in the jar {0}.
+error.no-main-class-with-main-jar.advice=Specify a main class or ensure that the jar {0} specifies one in the manifest.
+error.no-main-class=A main class was not specified nor was one found in the supplied application resources.
+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.
+
+warning.module.does.not.exist=Module [{0}] does not exist.
+warning.no.jdk.modules.found=Warning: No JDK Modules found.
+
+MSG_BundlerFailed=Error: Bundler "{1}" ({0}) failed to produce a bundle.
+MSG_BundlerConfigException=Bundler {0} skipped because of a configuration problem: {1}. \n\
+Advice to fix: {2}
+MSG_BundlerConfigExceptionNoAdvice=Bundler {0} skipped because of a configuration problem: {1}.
+MSG_BundlerRuntimeException=Bundler {0} failed because of {1}.
+MSG_BundlerFailed=Error: Bundler "{1}" ({0}) failed to produce a bundle.
+
+ERR_NoMainClass=Error: Main application class is missing.
+ERR_UnsupportedOption=Error: Option [{0}] is not valid on this platform.
+ERR_InvalidTypeOption=Error: Option [{0}] is not valid with package-type [{1}].
+ERR_NoInstallerEntryPoint=Error: Option [{0}] is not valid without --module or --main-jar entry point option.
+
+ERR_MissingArgument=Error: Missing argument: {0}.
+ERR_MissingAppResources=Error: No application jars found.
+ERR_AppImageNotExist=Error: App image directory "{0}" does not exist.
+ERR_NoAddLauncherName=Error: --add-launcher option requires a name and a file path (--add-launcher <name>=<file path>).
+ERR_NoUniqueName=Error: --add-launcher <name>=<file path> requires a unique name.
+ERR_NoJreInstallerName=Error: Jre Installers require a name parameter.
+ERR_InvalidAppName=Error: Invalid Application name: {0}.
+ERR_InvalidSLName=Error: Invalid Add Launcher name: {0}.
+ERR_LicenseFileNotExit=Error: Specified license file does not exist.
+ERR_BuildRootInvalid=Error: temp-root ({0}) must be non-existant directory.
+ERR_InvalidOption=Error: Invalid Option: [{0}].
+ERR_InvalidInstallerType=Error: Invalid or unsupported package type: [{0}].
+ERR_BothMainJarAndModule=Error: Cannot have both --main-jar and --module Options.
+ERR_NoEntryPoint=Error: creating application image requires --main-jar or --module Option.
+ERR_InputNotDirectory=Error: Input directory specified is not a directory: {0}.
+ERR_CannotReadInputDir=Error: No permission to read from input directory: {0}.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/MainResources_zh_CN.properties Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,84 @@
+#
+# Copyright (c) 2017, 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.
+#
+#
+
+param.copyright.default=Copyright (C) {0,date,YYYY}
+param.description.default=none
+param.vendor.default=Unknown
+param.version.default=1.0
+
+message.using-default-resource=Using default package resource {0} {1} (add {2} to the resource-dir to customize).
+message.no-default-resource=no default package resource {0} {1} (add {2} to the resource-dir to customize).
+message.using-custom-resource-from-file=Using custom package resource {0} (loaded from file {1}).
+message.using-custom-resource=Using custom package resource {0} (loaded from {1}).
+message.creating-app-bundle=Creating app bundle: {0} in {1}.
+message.app-image-dir-does-not-exist=Specified application image directory {0}: {1} does not exists.
+message.app-image-dir-does-not-exist.advice=Confirm that the value for {0} exists.
+message.runtime-image-dir-does-not-exist=Specified runtime image directory {0}: {1} does not exists.
+message.runtime-image-dir-does-not-exist.advice=Confirm that the value for {0} exists.
+message.debug-working-directory=Kept working directory for debug: {0}.
+message.bundle-created=Succeeded in building {0} bundle.
+
+error.cannot-create-output-dir=Output directory {0} cannot be created.
+error.cannot-write-to-output-dir=Output directory {0} is not writable.
+error.root-exists=Error: Application output directory {0} already exists.
+error.no-main-class-with-main-jar=A main class was not specified nor was one found in the jar {0}.
+error.no-main-class-with-main-jar.advice=Specify a main class or ensure that the jar {0} specifies one in the manifest.
+error.no-main-class=A main class was not specified nor was one found in the supplied application resources.
+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.
+
+warning.module.does.not.exist=Module [{0}] does not exist.
+warning.no.jdk.modules.found=Warning: No JDK Modules found.
+
+MSG_BundlerFailed=Error: Bundler "{1}" ({0}) failed to produce a bundle.
+MSG_BundlerConfigException=Bundler {0} skipped because of a configuration problem: {1}. \n\
+Advice to fix: {2}
+MSG_BundlerConfigExceptionNoAdvice=Bundler {0} skipped because of a configuration problem: {1}.
+MSG_BundlerRuntimeException=Bundler {0} failed because of {1}.
+MSG_BundlerFailed=Error: Bundler "{1}" ({0}) failed to produce a bundle.
+
+ERR_NoMainClass=Error: Main application class is missing.
+ERR_UnsupportedOption=Error: Option [{0}] is not valid on this platform.
+ERR_InvalidTypeOption=Error: Option [{0}] is not valid with package-type [{1}].
+ERR_NoInstallerEntryPoint=Error: Option [{0}] is not valid without --module or --main-jar entry point option.
+
+ERR_MissingArgument=Error: Missing argument: {0}.
+ERR_MissingAppResources=Error: No application jars found.
+ERR_AppImageNotExist=Error: App image directory "{0}" does not exist.
+ERR_NoAddLauncherName=Error: --add-launcher option requires a name and a file path (--add-launcher <name>=<file path>).
+ERR_NoUniqueName=Error: --add-launcher <name>=<file path> requires a unique name.
+ERR_NoJreInstallerName=Error: Jre Installers require a name parameter.
+ERR_InvalidAppName=Error: Invalid Application name: {0}.
+ERR_InvalidSLName=Error: Invalid Add Launcher name: {0}.
+ERR_LicenseFileNotExit=Error: Specified license file does not exist.
+ERR_BuildRootInvalid=Error: temp-root ({0}) must be non-existant directory.
+ERR_InvalidOption=Error: Invalid Option: [{0}].
+ERR_InvalidInstallerType=Error: Invalid or unsupported package type: [{0}].
+ERR_BothMainJarAndModule=Error: Cannot have both --main-jar and --module Options.
+ERR_NoEntryPoint=Error: creating application image requires --main-jar or --module Option.
+ERR_InputNotDirectory=Error: Input directory specified is not a directory: {0}.
+ERR_CannotReadInputDir=Error: No permission to read from input directory: {0}.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/ResourceLocator.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2011, 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.jpackage.internal.resources;
+
+/*
+ * ResourceLocator
+ * This empty class is the only class in this package. Otherwise the
+ * package consists only of resources. ResourceLocator is needed in order
+ * to call getResourceAsStream() to get those resources.
+ */
+
+public class ResourceLocator {
+ public ResourceLocator() {
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/classes/jdk/jpackage/main/CommandLine.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,203 @@
+/*
+ * Copyright (c) 1999, 2018, 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.jpackage.main;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.nio.charset.Charset;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * This file is direct copy of CommandLine.java in com.sun.tools.javac.main.
+ * It should track changes made to that file.
+ * It is modified only to remove content not used by jpackage
+ */
+
+/**
+ * Various utility methods for processing Java tool command line arguments.
+ *
+ * <p><b>This is NOT part of any supported API.
+ * If you write code that depends on this, you do so at your own risk.
+ * This code and its internal interfaces are subject to change or
+ * deletion without notice.</b>
+ */
+class CommandLine {
+ /**
+ * Process Win32-style command files for the specified command line
+ * arguments and return the resulting arguments. A command file argument
+ * is of the form '@file' where 'file' is the name of the file whose
+ * contents are to be parsed for additional arguments. The contents of
+ * the command file are parsed using StreamTokenizer and the original
+ * '@file' argument replaced with the resulting tokens. Recursive command
+ * files are not supported. The '@' character itself can be quoted with
+ * the sequence '@@'.
+ * @param args the arguments that may contain @files
+ * @return the arguments, with @files expanded
+ * @throws IOException if there is a problem reading any of the @files
+ */
+ public static String[] parse(String[] args) throws IOException {
+ List<String> newArgs = new ArrayList<>();
+ appendParsedCommandArgs(newArgs, Arrays.asList(args));
+ return newArgs.toArray(new String[newArgs.size()]);
+ }
+
+ private static void appendParsedCommandArgs(List<String> newArgs, List<String> args) throws IOException {
+ for (String arg : args) {
+ if (arg.length() > 1 && arg.charAt(0) == '@') {
+ arg = arg.substring(1);
+ if (arg.charAt(0) == '@') {
+ newArgs.add(arg);
+ } else {
+ loadCmdFile(arg, newArgs);
+ }
+ } else {
+ newArgs.add(arg);
+ }
+ }
+ }
+
+ private static void loadCmdFile(String name, List<String> args) throws IOException {
+ try (Reader r = Files.newBufferedReader(Paths.get(name), Charset.defaultCharset())) {
+ Tokenizer t = new Tokenizer(r);
+ String s;
+ while ((s = t.nextToken()) != null) {
+ args.add(s);
+ }
+ }
+ }
+
+ public static class Tokenizer {
+ private final Reader in;
+ private int ch;
+
+ public Tokenizer(Reader in) throws IOException {
+ this.in = in;
+ ch = in.read();
+ }
+
+ public String nextToken() throws IOException {
+ skipWhite();
+ if (ch == -1) {
+ return null;
+ }
+
+ StringBuilder sb = new StringBuilder();
+ char quoteChar = 0;
+
+ while (ch != -1) {
+ switch (ch) {
+ case ' ':
+ case '\t':
+ case '\f':
+ if (quoteChar == 0) {
+ return sb.toString();
+ }
+ sb.append((char) ch);
+ break;
+
+ case '\n':
+ case '\r':
+ return sb.toString();
+
+ case '\'':
+ case '"':
+ if (quoteChar == 0) {
+ quoteChar = (char) ch;
+ } else if (quoteChar == ch) {
+ quoteChar = 0;
+ } else {
+ sb.append((char) ch);
+ }
+ break;
+
+ case '\\':
+ if (quoteChar != 0) {
+ ch = in.read();
+ switch (ch) {
+ case '\n':
+ case '\r':
+ while (ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t' || ch == '\f') {
+ ch = in.read();
+ }
+ continue;
+
+ case 'n':
+ ch = '\n';
+ break;
+ case 'r':
+ ch = '\r';
+ break;
+ case 't':
+ ch = '\t';
+ break;
+ case 'f':
+ ch = '\f';
+ break;
+ }
+ }
+ sb.append((char) ch);
+ break;
+
+ default:
+ sb.append((char) ch);
+ }
+
+ ch = in.read();
+ }
+
+ return sb.toString();
+ }
+
+ void skipWhite() throws IOException {
+ while (ch != -1) {
+ switch (ch) {
+ case ' ':
+ case '\t':
+ case '\n':
+ case '\r':
+ case '\f':
+ break;
+
+ case '#':
+ ch = in.read();
+ while (ch != '\n' && ch != '\r' && ch != -1) {
+ ch = in.read();
+ }
+ break;
+
+ default:
+ return;
+ }
+
+ ch = in.read();
+ }
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/classes/jdk/jpackage/main/Main.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) 2011, 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
+ * 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.jpackage.main;
+
+import jdk.jpackage.internal.Arguments;
+import jdk.jpackage.internal.Log;
+import jdk.jpackage.internal.CLIHelp;
+import java.io.PrintWriter;
+import java.util.ResourceBundle;
+import java.io.IOException;
+
+public class Main {
+
+ private static final ResourceBundle bundle = ResourceBundle.getBundle(
+ "jdk.jpackage.internal.resources.MainResources");
+
+ /**
+ * main(String... args)
+ * This is the entry point for the jpackage tool.
+ *
+ * @param args command line arguments
+ */
+ public static void main(String... args) throws Exception {
+ // Create logger with default system.out and system.err
+ Log.Logger logger = new Log.Logger(false);
+ Log.setLogger(logger);
+
+ int status = new jdk.jpackage.main.Main().execute(args);
+ System.exit(status);
+ }
+
+ /**
+ * execute() - this is the entry point for the ToolProvider API.
+ *
+ * @param out output stream
+ * @param err error output stream
+ * @param args command line arguments
+ * @return an exit code. 0 means success, non-zero means an error occurred.
+ */
+ public int execute(PrintWriter out, PrintWriter err, String... args) {
+ // Create logger with provided streams
+ Log.Logger logger = new Log.Logger(false);
+ logger.setPrintWriter(out, err);
+ Log.setLogger(logger);
+
+ return execute(args);
+ }
+
+ private int execute(String... args) {
+ try {
+ String[] newArgs;
+ try {
+ newArgs = CommandLine.parse(args);
+ } catch (IOException ioe) {
+ Log.error(ioe.getMessage());
+ return 1;
+ }
+
+ if (newArgs.length == 0) {
+ CLIHelp.showHelp(true);
+ } else if (hasHelp(newArgs)){
+ if (hasVersion(newArgs)) {
+ Log.info(System.getProperty("java.version") + "\n");
+ }
+ CLIHelp.showHelp(false);
+ } else if (hasVersion(newArgs)) {
+ Log.info(System.getProperty("java.version"));
+ } else {
+ Arguments arguments = new Arguments(newArgs);
+ if (!arguments.processArguments()) {
+ // processArguments() will log error message if failed.
+ return 1;
+ }
+ }
+ return 0;
+ } finally {
+ Log.flush();
+ }
+ }
+
+ private boolean hasHelp(String[] args) {
+ for (String a : args) {
+ if ("--help".equals(a) || "-h".equals(a)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean hasVersion(String[] args) {
+ for (String a : args) {
+ if ("--version".equals(a)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/classes/module-info.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,62 @@
+/*
+ * 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.
+ */
+
+/**
+ * Defines the Java Packaging tool, jpackage.
+ *
+ * <p>jpackage is a tool for generating self-contained application bundles.
+ *
+ * <p> This module provides the equivalent of command-line access to <em>jpackage</em>
+ * via the {@link java.util.spi.ToolProvider ToolProvider} SPI.
+ * Instances of the tool can be obtained by calling
+ * {@link java.util.spi.ToolProvider#findFirst ToolProvider.findFirst}
+ * or the {@link java.util.ServiceLoader service loader} with the name
+ * {@code "jpackage"}.
+ *
+ * @implNote The {@code jpackage} tool is not thread-safe. An application
+ * should not call either of the
+ * {@link java.util.spi.ToolProvider ToolProvider} {@code run} methods
+ * concurrently, even with separate {@code "jpackage"} {@code ToolProvider}
+ * instances, or undefined behavior may result.
+ * <p></p>
+ *
+ * @moduleGraph
+ * @since 13
+ */
+
+module jdk.jpackage {
+ requires jdk.jlink;
+
+ requires java.desktop;
+
+ uses jdk.jpackage.internal.Bundler;
+ uses jdk.jpackage.internal.Bundlers;
+
+ provides jdk.jpackage.internal.Bundlers with
+ jdk.jpackage.internal.BasicBundlers;
+
+ provides java.util.spi.ToolProvider
+ with jdk.jpackage.internal.JPackageToolProvider;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/native/libapplauncher/FileAttributes.h Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2014, 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.
+ */
+
+#ifndef FILEATTRIBUTES_H
+#define FILEATTRIBUTES_H
+
+#include "Platform.h"
+#include "PlatformString.h"
+#include "FileAttribute.h"
+
+#include <vector>
+
+class FileAttributes {
+private:
+ TString FFileName;
+ bool FFollowLink;
+ std::vector<FileAttribute> FAttributes;
+
+ bool WriteAttributes();
+ bool ReadAttributes();
+ bool Valid(const FileAttribute Value);
+
+public:
+ FileAttributes(const TString FileName, bool FollowLink = true);
+
+ void Append(const FileAttribute Value);
+ bool Contains(const FileAttribute Value);
+ void Remove(const FileAttribute Value);
+};
+
+#endif // FILEATTRIBUTES_H
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/native/libapplauncher/FilePath.h Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2014, 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.
+ */
+
+#ifndef FILEPATH_H
+#define FILEPATH_H
+
+#include "Platform.h"
+#include "PlatformString.h"
+#include "FileAttribute.h"
+
+#include <vector>
+
+class FileAttributes {
+private:
+ TString FFileName;
+ bool FFollowLink;
+ std::vector<FileAttribute> FAttributes;
+
+ bool WriteAttributes();
+ bool ReadAttributes();
+ bool Valid(const FileAttribute Value);
+
+public:
+ FileAttributes(const TString FileName, bool FollowLink = true);
+
+ void Append(const FileAttribute Value);
+ bool Contains(const FileAttribute Value);
+ void Remove(const FileAttribute Value);
+};
+
+class FilePath {
+private:
+ FilePath(void) {}
+ ~FilePath(void) {}
+
+public:
+ static bool FileExists(const TString FileName);
+ static bool DirectoryExists(const TString DirectoryName);
+
+ static bool DeleteFile(const TString FileName);
+ static bool DeleteDirectory(const TString DirectoryName);
+
+ static TString ExtractFilePath(TString Path);
+ static TString ExtractFileExt(TString Path);
+ static TString ExtractFileName(TString Path);
+ static TString ChangeFileExt(TString Path, TString Extension);
+
+ static TString IncludeTrailingSeparator(const TString value);
+ static TString IncludeTrailingSeparator(const char* value);
+ static TString IncludeTrailingSeparator(const wchar_t* value);
+ static TString FixPathForPlatform(TString Path);
+ static TString FixPathSeparatorForPlatform(TString Path);
+ static TString PathSeparator();
+
+ static bool CreateDirectory(TString Path, bool ownerOnly);
+ static void ChangePermissions(TString FileName, bool ownerOnly);
+};
+
+#endif //FILEPATH_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/native/libapplauncher/Helpers.cpp Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,240 @@
+/*
+ * Copyright (c) 2014, 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.
+ */
+
+#include "Helpers.h"
+#include "PlatformString.h"
+#include "PropertyFile.h"
+
+
+bool Helpers::SplitOptionIntoNameValue(
+ TString option, TString& Name, TString& Value) {
+ bool hasValue = false;
+ Name = _T("");
+ Value = _T("");
+ unsigned int index = 0;
+
+ for (; index < option.length(); index++) {
+ TCHAR c = option[index];
+
+ switch (c) {
+ case '=': {
+ index++;
+ hasValue = true;
+ break;
+ }
+
+ case '\\': {
+ if (index + 1 < option.length()) {
+ c = option[index + 1];
+
+ switch (c) {
+ case '\\': {
+ index++;
+ Name += '\\';
+ break;
+ }
+
+ case '=': {
+ index++;
+ Name += '=';
+ break;
+ }
+ }
+
+ }
+
+ continue;
+ }
+
+ default: {
+ Name += c;
+ continue;
+ }
+ }
+
+ break;
+ }
+
+ if (hasValue) {
+ Value = option.substr(index, index - option.length());
+ }
+
+ return (option.length() > 0);
+}
+
+
+TString Helpers::ReplaceString(TString subject, const TString& search,
+ const TString& replace) {
+ size_t pos = 0;
+ while((pos = subject.find(search, pos)) != TString::npos) {
+ subject.replace(pos, search.length(), replace);
+ pos += replace.length();
+ }
+ return subject;
+}
+
+TString Helpers::ConvertIdToFilePath(TString Value) {
+ TString search;
+ search = '.';
+ TString replace;
+ replace = '/';
+ TString result = ReplaceString(Value, search, replace);
+ return result;
+}
+
+TString Helpers::ConvertIdToJavaPath(TString Value) {
+ TString search;
+ search = '.';
+ TString replace;
+ replace = '/';
+ TString result = ReplaceString(Value, search, replace);
+ search = '\\';
+ result = ReplaceString(result, search, replace);
+ return result;
+}
+
+TString Helpers::ConvertJavaPathToId(TString Value) {
+ TString search;
+ search = '/';
+ TString replace;
+ replace = '.';
+ TString result = ReplaceString(Value, search, replace);
+ return result;
+}
+
+OrderedMap<TString, TString>
+ Helpers::GetJavaOptionsFromConfig(IPropertyContainer* config) {
+ OrderedMap<TString, TString> result;
+
+ for (unsigned int index = 0; index < config->GetCount(); index++) {
+ TString argname =
+ TString(_T("jvmarg.")) + PlatformString(index + 1).toString();
+ TString argvalue;
+
+ if (config->GetValue(argname, argvalue) == false) {
+ break;
+ }
+ else if (argvalue.empty() == false) {
+ TString name;
+ TString value;
+ if (Helpers::SplitOptionIntoNameValue(argvalue, name, value)) {
+ result.Append(name, value);
+ }
+ }
+ }
+
+ return result;
+}
+
+std::list<TString> Helpers::GetArgsFromConfig(IPropertyContainer* config) {
+ std::list<TString> result;
+
+ for (unsigned int index = 0; index < config->GetCount(); index++) {
+ TString argname = TString(_T("arg."))
+ + PlatformString(index + 1).toString();
+ TString argvalue;
+
+ if (config->GetValue(argname, argvalue) == false) {
+ break;
+ }
+ else if (argvalue.empty() == false) {
+ result.push_back((argvalue));
+ }
+ }
+
+ return result;
+}
+
+std::list<TString>
+ Helpers::MapToNameValueList(OrderedMap<TString, TString> Map) {
+ std::list<TString> result;
+ std::vector<TString> keys = Map.GetKeys();
+
+ for (OrderedMap<TString, TString>::const_iterator iterator = Map.begin();
+ iterator != Map.end(); iterator++) {
+ JPPair<TString, TString> *item = *iterator;
+ TString key = item->first;
+ TString value = item->second;
+
+ if (value.length() == 0) {
+ result.push_back(key);
+ } else {
+ result.push_back(key + _T('=') + value);
+ }
+ }
+
+ return result;
+}
+
+TString Helpers::NameValueToString(TString name, TString value) {
+ TString result;
+
+ if (value.empty() == true) {
+ result = name;
+ }
+ else {
+ result = name + TString(_T("=")) + value;
+ }
+
+ return result;
+}
+
+std::list<TString> Helpers::StringToArray(TString Value) {
+ std::list<TString> result;
+ TString line;
+
+ for (unsigned int index = 0; index < Value.length(); index++) {
+ TCHAR c = Value[index];
+
+ switch (c) {
+ case '\n': {
+ result.push_back(line);
+ line = _T("");
+ break;
+ }
+
+ case '\r': {
+ result.push_back(line);
+ line = _T("");
+
+ if (Value[index + 1] == '\n')
+ index++;
+
+ break;
+ }
+
+ default: {
+ line += c;
+ }
+ }
+ }
+
+ // The buffer may not have ended with a Carriage Return/Line Feed.
+ if (line.length() > 0) {
+ result.push_back(line);
+ }
+
+ return result;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/native/libapplauncher/Helpers.h Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2014, 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.
+ */
+
+#ifndef HELPERS_H
+#define HELPERS_H
+
+#include "Platform.h"
+#include "OrderedMap.h"
+#include "IniFile.h"
+
+
+class Helpers {
+private:
+ Helpers(void) {}
+ ~Helpers(void) {}
+
+public:
+ // Supports two formats for option:
+ // Example 1:
+ // foo=bar
+ //
+ // Example 2:
+ // <name=foo=, value=goo>
+ static bool SplitOptionIntoNameValue(TString option,
+ TString& Name, TString& Value);
+ static TString ReplaceString(TString subject, const TString& search,
+ const TString& replace);
+ static TString ConvertIdToFilePath(TString Value);
+ static TString ConvertIdToJavaPath(TString Value);
+ static TString ConvertJavaPathToId(TString Value);
+
+ static OrderedMap<TString, TString>
+ GetJavaOptionsFromConfig(IPropertyContainer* config);
+ static std::list<TString> GetArgsFromConfig(IPropertyContainer* config);
+
+ static std::list<TString>
+ MapToNameValueList(OrderedMap<TString, TString> Map);
+
+ static TString NameValueToString(TString name, TString value);
+
+ static std::list<TString> StringToArray(TString Value);
+};
+
+#endif // HELPERS_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/native/libapplauncher/IniFile.cpp Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,261 @@
+/*
+ * Copyright (c) 2015, 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.
+ */
+
+#include "IniFile.h"
+#include "Helpers.h"
+
+#include <string>
+
+
+IniFile::IniFile() : ISectionalPropertyContainer() {
+}
+
+IniFile::~IniFile() {
+ for (OrderedMap<TString, IniSectionData*>::iterator iterator =
+ FMap.begin(); iterator != FMap.end(); iterator++) {
+ JPPair<TString, IniSectionData*> *item = *iterator;
+ delete item->second;
+ }
+}
+
+bool IniFile::LoadFromFile(const TString FileName) {
+ bool result = false;
+ Platform& platform = Platform::GetInstance();
+
+ std::list<TString> contents = platform.LoadFromFile(FileName);
+
+ if (contents.empty() == false) {
+ bool found = false;
+
+ // Determine the if file is an INI file or property file.
+ // Assign FDefaultSection if it is
+ // an INI file. Otherwise FDefaultSection is NULL.
+ for (std::list<TString>::const_iterator iterator = contents.begin();
+ iterator != contents.end(); iterator++) {
+ TString line = *iterator;
+
+ if (line[0] == ';') {
+ // Semicolon is a comment so ignore the line.
+ continue;
+ }
+ else {
+ if (line[0] == '[') {
+ found = true;
+ }
+
+ break;
+ }
+ }
+
+ if (found == true) {
+ TString sectionName;
+
+ for (std::list<TString>::const_iterator iterator = contents.begin();
+ iterator != contents.end(); iterator++) {
+ TString line = *iterator;
+
+ if (line[0] == ';') {
+ // Semicolon is a comment so ignore the line.
+ continue;
+ }
+ else if (line[0] == '[' && line[line.length() - 1] == ']') {
+ sectionName = line.substr(1, line.size() - 2);
+ }
+ else if (sectionName.empty() == false) {
+ TString name;
+ TString value;
+
+ if (Helpers::SplitOptionIntoNameValue(
+ line, name, value) == true) {
+ Append(sectionName, name, value);
+ }
+ }
+ }
+
+ result = true;
+ }
+ }
+
+ return result;
+}
+
+bool IniFile::SaveToFile(const TString FileName, bool ownerOnly) {
+ bool result = false;
+
+ std::list<TString> contents;
+ std::vector<TString> keys = FMap.GetKeys();
+
+ for (unsigned int index = 0; index < keys.size(); index++) {
+ TString name = keys[index];
+ IniSectionData *section;
+
+ if (FMap.GetValue(name, section) == true) {
+ contents.push_back(_T("[") + name + _T("]"));
+ std::list<TString> lines = section->GetLines();
+ contents.insert(contents.end(), lines.begin(), lines.end());
+ contents.push_back(_T(""));
+ }
+ }
+
+ Platform& platform = Platform::GetInstance();
+ platform.SaveToFile(FileName, contents, ownerOnly);
+ result = true;
+ return result;
+}
+
+void IniFile::Append(const TString SectionName,
+ const TString Key, TString Value) {
+ if (FMap.ContainsKey(SectionName) == true) {
+ IniSectionData* section;
+
+ if (FMap.GetValue(SectionName, section) == true && section != NULL) {
+ section->SetValue(Key, Value);
+ }
+ }
+ else {
+ IniSectionData *section = new IniSectionData();
+ section->SetValue(Key, Value);
+ FMap.Append(SectionName, section);
+ }
+}
+
+void IniFile::AppendSection(const TString SectionName,
+ OrderedMap<TString, TString> Values) {
+ if (FMap.ContainsKey(SectionName) == true) {
+ IniSectionData* section;
+
+ if (FMap.GetValue(SectionName, section) == true && section != NULL) {
+ section->Append(Values);
+ }
+ }
+ else {
+ IniSectionData *section = new IniSectionData(Values);
+ FMap.Append(SectionName, section);
+ }
+}
+
+bool IniFile::GetValue(const TString SectionName,
+ const TString Key, TString& Value) {
+ bool result = false;
+ IniSectionData* section;
+
+ if (FMap.GetValue(SectionName, section) == true && section != NULL) {
+ result = section->GetValue(Key, Value);
+ }
+
+ return result;
+}
+
+bool IniFile::SetValue(const TString SectionName,
+ const TString Key, TString Value) {
+ bool result = false;
+ IniSectionData* section;
+
+ if (FMap.GetValue(SectionName, section) && section != NULL) {
+ result = section->SetValue(Key, Value);
+ }
+ else {
+ Append(SectionName, Key, Value);
+ }
+
+
+ return result;
+}
+
+bool IniFile::GetSection(const TString SectionName,
+ OrderedMap<TString, TString> &Data) {
+ bool result = false;
+
+ if (FMap.ContainsKey(SectionName) == true) {
+ IniSectionData* section;
+
+ if (FMap.GetValue(SectionName, section) == true && section != NULL) {
+ OrderedMap<TString, TString> data = section->GetData();
+ Data.Append(data);
+ result = true;
+ }
+ }
+
+ return result;
+}
+
+bool IniFile::ContainsSection(const TString SectionName) {
+ return FMap.ContainsKey(SectionName);
+}
+
+//----------------------------------------------------------------------------
+
+IniSectionData::IniSectionData() {
+ FMap.SetAllowDuplicates(true);
+}
+
+IniSectionData::IniSectionData(OrderedMap<TString, TString> Values) {
+ FMap = Values;
+}
+
+std::vector<TString> IniSectionData::GetKeys() {
+ return FMap.GetKeys();
+}
+
+std::list<TString> IniSectionData::GetLines() {
+ std::list<TString> result;
+ std::vector<TString> keys = FMap.GetKeys();
+
+ for (unsigned int index = 0; index < keys.size(); index++) {
+ TString name = keys[index];
+ TString value;
+
+ if (FMap.GetValue(name, value) == true) {
+ name = Helpers::ReplaceString(name, _T("="), _T("\\="));
+ value = Helpers::ReplaceString(value, _T("="), _T("\\="));
+
+ TString line = name + _T('=') + value;
+ result.push_back(line);
+ }
+ }
+
+ return result;
+}
+
+OrderedMap<TString, TString> IniSectionData::GetData() {
+ OrderedMap<TString, TString> result = FMap;
+ return result;
+}
+
+bool IniSectionData::GetValue(const TString Key, TString& Value) {
+ return FMap.GetValue(Key, Value);
+}
+
+bool IniSectionData::SetValue(const TString Key, TString Value) {
+ return FMap.SetValue(Key, Value);
+}
+
+void IniSectionData::Append(OrderedMap<TString, TString> Values) {
+ FMap.Append(Values);
+}
+
+size_t IniSectionData::GetCount() {
+ return FMap.Count();
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/native/libapplauncher/IniFile.h Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2015, 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.
+ */
+
+#ifndef INIFILE_H
+#define INIFILE_H
+
+#include "Platform.h"
+#include "OrderedMap.h"
+
+#include <map>
+
+
+class IniSectionData : public IPropertyContainer {
+private:
+ OrderedMap<TString, TString> FMap;
+
+public:
+ IniSectionData();
+ IniSectionData(OrderedMap<TString, TString> Values);
+
+ std::vector<TString> GetKeys();
+ std::list<TString> GetLines();
+ OrderedMap<TString, TString> GetData();
+
+ bool SetValue(const TString Key, TString Value);
+ void Append(OrderedMap<TString, TString> Values);
+
+ virtual bool GetValue(const TString Key, TString& Value);
+ virtual size_t GetCount();
+};
+
+
+class IniFile : public ISectionalPropertyContainer {
+private:
+ OrderedMap<TString, IniSectionData*> FMap;
+
+public:
+ IniFile();
+ virtual ~IniFile();
+
+ void internalTest();
+
+ bool LoadFromFile(const TString FileName);
+ bool SaveToFile(const TString FileName, bool ownerOnly = true);
+
+ void Append(const TString SectionName, const TString Key, TString Value);
+ void AppendSection(const TString SectionName,
+ OrderedMap<TString, TString> Values);
+ bool SetValue(const TString SectionName,
+ const TString Key, TString Value);
+
+ // ISectionalPropertyContainer
+ virtual bool GetSection(const TString SectionName,
+ OrderedMap<TString, TString> &Data);
+ virtual bool ContainsSection(const TString SectionName);
+ virtual bool GetValue(const TString SectionName,
+ const TString Key, TString& Value);
+};
+
+#endif // INIFILE_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/native/libapplauncher/JavaVirtualMachine.cpp Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,303 @@
+/*
+ * Copyright (c) 2014, 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.
+ */
+
+#include "JavaVirtualMachine.h"
+#include "Platform.h"
+#include "PlatformString.h"
+#include "FilePath.h"
+#include "Package.h"
+#include "Helpers.h"
+#include "Messages.h"
+#include "Macros.h"
+
+#include "jni.h"
+
+#include <map>
+#include <list>
+#include <sstream>
+
+
+bool RunVM() {
+ JavaVirtualMachine javavm;
+
+ bool result = javavm.StartJVM();
+
+ if (!result) {
+ Platform& platform = Platform::GetInstance();
+ platform.ShowMessage(_T("Failed to launch JVM\n"));
+ }
+
+ return result;
+}
+
+//----------------------------------------------------------------------------
+
+JavaOptions::JavaOptions(): FOptions(NULL) {
+}
+
+JavaOptions::~JavaOptions() {
+ if (FOptions != NULL) {
+ for (unsigned int index = 0; index < GetCount(); index++) {
+ delete[] FOptions[index].optionString;
+ }
+
+ delete[] FOptions;
+ }
+}
+
+void JavaOptions::AppendValue(const TString Key, TString Value, void* Extra) {
+ JavaOptionItem item;
+ item.name = Key;
+ item.value = Value;
+ item.extraInfo = Extra;
+ FItems.push_back(item);
+}
+
+void JavaOptions::AppendValue(const TString Key, TString Value) {
+ AppendValue(Key, Value, NULL);
+}
+
+void JavaOptions::AppendValue(const TString Key) {
+ AppendValue(Key, _T(""), NULL);
+}
+
+void JavaOptions::AppendValues(OrderedMap<TString, TString> Values) {
+ std::vector<TString> orderedKeys = Values.GetKeys();
+
+ for (std::vector<TString>::const_iterator iterator = orderedKeys.begin();
+ iterator != orderedKeys.end(); iterator++) {
+ TString name = *iterator;
+ TString value;
+
+ if (Values.GetValue(name, value) == true) {
+ AppendValue(name, value);
+ }
+ }
+}
+
+void JavaOptions::ReplaceValue(const TString Key, TString Value) {
+ for (std::list<JavaOptionItem>::iterator iterator = FItems.begin();
+ iterator != FItems.end(); iterator++) {
+
+ TString lkey = iterator->name;
+
+ if (lkey == Key) {
+ JavaOptionItem item = *iterator;
+ item.value = Value;
+ iterator = FItems.erase(iterator);
+ FItems.insert(iterator, item);
+ break;
+ }
+ }
+}
+
+std::list<TString> JavaOptions::ToList() {
+ std::list<TString> result;
+ Macros& macros = Macros::GetInstance();
+
+ for (std::list<JavaOptionItem>::const_iterator iterator = FItems.begin();
+ iterator != FItems.end(); iterator++) {
+ TString key = iterator->name;
+ TString value = iterator->value;
+ TString option = Helpers::NameValueToString(key, value);
+ option = macros.ExpandMacros(option);
+ result.push_back(option);
+ }
+
+ return result;
+}
+
+size_t JavaOptions::GetCount() {
+ return FItems.size();
+}
+
+//----------------------------------------------------------------------------
+
+JavaVirtualMachine::JavaVirtualMachine() {
+}
+
+JavaVirtualMachine::~JavaVirtualMachine(void) {
+}
+
+bool JavaVirtualMachine::StartJVM() {
+ Platform& platform = Platform::GetInstance();
+ Package& package = Package::GetInstance();
+
+ TString classpath = package.GetClassPath();
+ TString modulepath = package.GetModulePath();
+ JavaOptions options;
+
+ if (modulepath.empty() == false) {
+ options.AppendValue(_T("-Djava.module.path"), modulepath);
+ }
+
+ options.AppendValue(_T("-Djava.library.path"),
+ package.GetPackageAppDirectory() + FilePath::PathSeparator()
+ + package.GetPackageLauncherDirectory());
+ options.AppendValue(
+ _T("-Djava.launcher.path"), package.GetPackageLauncherDirectory());
+ options.AppendValues(package.GetJavaOptions());
+
+#ifdef DEBUG
+ if (package.Debugging() == dsJava) {
+ options.AppendValue(_T("-Xdebug"), _T(""));
+ options.AppendValue(
+ _T("-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=localhost:5005"),
+ _T(""));
+ platform.ShowMessage(_T("localhost:5005"));
+ }
+#endif // DEBUG
+
+ TString maxHeapSizeOption;
+ TString minHeapSizeOption;
+
+
+ if (package.GetMemoryState() == PackageBootFields::msAuto) {
+ TPlatformNumber memorySize = package.GetMemorySize();
+ TString memory =
+ PlatformString((size_t)memorySize).toString() + _T("m");
+ maxHeapSizeOption = TString(_T("-Xmx")) + memory;
+ options.AppendValue(maxHeapSizeOption, _T(""));
+
+ if (memorySize > 256)
+ minHeapSizeOption = _T("-Xms256m");
+ else
+ minHeapSizeOption = _T("-Xms") + memory;
+
+ options.AppendValue(minHeapSizeOption, _T(""));
+ }
+
+ TString mainClassName = package.GetMainClassName();
+ TString mainModule = package.GetMainModule();
+
+ if (mainClassName.empty() == true && mainModule.empty() == true) {
+ Messages& messages = Messages::GetInstance();
+ platform.ShowMessage(messages.GetMessage(NO_MAIN_CLASS_SPECIFIED));
+ return false;
+ }
+
+ configureLibrary();
+
+ // Initialize the arguments to JLI_Launch()
+ //
+ // On Mac OS X JLI_Launch spawns a new thread that actually starts the JVM.
+ // This new thread simply re-runs main(argc, argv). Therefore we do not
+ // want to add new args if we are still in the original main thread so we
+ // will treat them as command line args provided by the user ...
+ // Only propagate original set of args first time.
+
+ options.AppendValue(_T("-classpath"));
+ options.AppendValue(classpath);
+
+ std::list<TString> vmargs;
+ vmargs.push_back(package.GetCommandName());
+
+ if (package.HasSplashScreen() == true) {
+ options.AppendValue(TString(_T("-splash:"))
+ + package.GetSplashScreenFileName(), _T(""));
+ }
+
+ if (mainModule.empty() == true) {
+ options.AppendValue(Helpers::ConvertJavaPathToId(mainClassName),
+ _T(""));
+ } else {
+ options.AppendValue(_T("-m"));
+ options.AppendValue(mainModule);
+ }
+
+ return launchVM(options, vmargs);
+}
+
+void JavaVirtualMachine::configureLibrary() {
+ Platform& platform = Platform::GetInstance();
+ Package& package = Package::GetInstance();
+ TString libName = package.GetJavaLibraryFileName();
+ platform.addPlatformDependencies(&javaLibrary);
+ javaLibrary.Load(libName);
+}
+
+bool JavaVirtualMachine::launchVM(JavaOptions& options,
+ std::list<TString>& vmargs) {
+ Platform& platform = Platform::GetInstance();
+ Package& package = Package::GetInstance();
+
+#ifdef MAC
+ // Mac adds a ProcessSerialNumber to args when launched from .app
+ // filter out the psn since they it's not expected in the app
+ if (platform.IsMainThread() == false) {
+ std::list<TString> loptions = options.ToList();
+ vmargs.splice(vmargs.end(), loptions,
+ loptions.begin(), loptions.end());
+ }
+#else
+ std::list<TString> loptions = options.ToList();
+ vmargs.splice(vmargs.end(), loptions, loptions.begin(), loptions.end());
+#endif
+
+ std::list<TString> largs = package.GetArgs();
+ vmargs.splice(vmargs.end(), largs, largs.begin(), largs.end());
+
+ size_t argc = vmargs.size();
+ DynamicBuffer<char*> argv(argc + 1);
+ if (argv.GetData() == NULL) {
+ return false;
+ }
+
+ unsigned int index = 0;
+ for (std::list<TString>::const_iterator iterator = vmargs.begin();
+ iterator != vmargs.end(); iterator++) {
+ TString item = *iterator;
+ std::string arg = PlatformString(item).toStdString();
+#ifdef DEBUG
+ printf("%i %s\n", index, arg.c_str());
+#endif // DEBUG
+ argv[index] = PlatformString::duplicate(arg.c_str());
+ index++;
+ }
+
+ argv[argc] = NULL;
+
+// On Mac we can only free the boot fields if the calling thread is
+// not the main thread.
+#ifdef MAC
+ if (platform.IsMainThread() == false) {
+ package.FreeBootFields();
+ }
+#else
+ package.FreeBootFields();
+#endif // MAC
+
+ if (javaLibrary.JavaVMCreate(argc, argv.GetData()) == true) {
+ return true;
+ }
+
+ for (index = 0; index < argc; index++) {
+ if (argv[index] != NULL) {
+ delete[] argv[index];
+ }
+ }
+
+ return false;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/native/libapplauncher/JavaVirtualMachine.h Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2014, 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.
+ */
+
+#ifndef JAVAVIRTUALMACHINE_H
+#define JAVAVIRTUALMACHINE_H
+
+
+#include "jni.h"
+#include "Platform.h"
+#include "Library.h"
+
+struct JavaOptionItem {
+ TString name;
+ TString value;
+ void* extraInfo;
+};
+
+class JavaOptions {
+private:
+ std::list<JavaOptionItem> FItems;
+ JavaVMOption* FOptions;
+
+public:
+ JavaOptions();
+ ~JavaOptions();
+
+ void AppendValue(const TString Key, TString Value, void* Extra);
+ void AppendValue(const TString Key, TString Value);
+ void AppendValue(const TString Key);
+ void AppendValues(OrderedMap<TString, TString> Values);
+ void ReplaceValue(const TString Key, TString Value);
+ std::list<TString> ToList();
+ size_t GetCount();
+};
+
+class JavaVirtualMachine {
+private:
+ JavaLibrary javaLibrary;
+
+ void configureLibrary();
+ bool launchVM(JavaOptions& options, std::list<TString>& vmargs);
+public:
+ JavaVirtualMachine();
+ ~JavaVirtualMachine(void);
+
+ bool StartJVM();
+};
+
+bool RunVM();
+
+#endif // JAVAVIRTUALMACHINE_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/native/libapplauncher/Library.cpp Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,188 @@
+/*
+ * Copyright (c) 2014, 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.
+ */
+
+#include "Library.h"
+#include "Platform.h"
+#include "Messages.h"
+#include "PlatformString.h"
+
+#include <fstream>
+#include <locale>
+
+Library::Library() {
+ Initialize();
+}
+
+Library::Library(const TString &FileName) {
+ Initialize();
+ Load(FileName);
+}
+
+Library::~Library() {
+ Unload();
+}
+
+void Library::Initialize() {
+ FModule = NULL;
+ FDependentLibraryNames = NULL;
+ FDependenciesLibraries = NULL;
+}
+
+void Library::InitializeDependencies() {
+ if (FDependentLibraryNames == NULL) {
+ FDependentLibraryNames = new std::vector<TString>();
+ }
+
+ if (FDependenciesLibraries == NULL) {
+ FDependenciesLibraries = new std::vector<Library*>();
+ }
+}
+
+void Library::LoadDependencies() {
+ if (FDependentLibraryNames != NULL && FDependenciesLibraries != NULL) {
+ for (std::vector<TString>::const_iterator iterator =
+ FDependentLibraryNames->begin();
+ iterator != FDependentLibraryNames->end(); iterator++) {
+ Library* library = new Library();
+
+ if (library->Load(*iterator) == true) {
+ FDependenciesLibraries->push_back(library);
+ }
+ }
+
+ delete FDependentLibraryNames;
+ FDependentLibraryNames = NULL;
+ }
+}
+
+void Library::UnloadDependencies() {
+ if (FDependenciesLibraries != NULL) {
+ for (std::vector<Library*>::const_iterator iterator =
+ FDependenciesLibraries->begin();
+ iterator != FDependenciesLibraries->end(); iterator++) {
+ Library* library = *iterator;
+
+ if (library != NULL) {
+ library->Unload();
+ delete library;
+ }
+ }
+
+ delete FDependenciesLibraries;
+ FDependenciesLibraries = NULL;
+ }
+}
+
+Procedure Library::GetProcAddress(const std::string& MethodName) const {
+ Platform& platform = Platform::GetInstance();
+ return platform.GetProcAddress(FModule, MethodName);
+}
+
+bool Library::Load(const TString &FileName) {
+ bool result = true;
+
+ if (FModule == NULL) {
+ LoadDependencies();
+ Platform& platform = Platform::GetInstance();
+ FModule = platform.LoadLibrary(FileName);
+
+ if (FModule == NULL) {
+ Messages& messages = Messages::GetInstance();
+ platform.ShowMessage(messages.GetMessage(LIBRARY_NOT_FOUND),
+ FileName);
+ result = false;
+ } else {
+ fname = PlatformString(FileName).toStdString();
+ }
+ }
+
+ return result;
+}
+
+bool Library::Unload() {
+ bool result = false;
+
+ if (FModule != NULL) {
+ Platform& platform = Platform::GetInstance();
+ platform.FreeLibrary(FModule);
+ FModule = NULL;
+ UnloadDependencies();
+ result = true;
+ }
+
+ return result;
+}
+
+void Library::AddDependency(const TString &FileName) {
+ InitializeDependencies();
+
+ if (FDependentLibraryNames != NULL) {
+ FDependentLibraryNames->push_back(FileName);
+ }
+}
+
+void Library::AddDependencies(const std::vector<TString> &Dependencies) {
+ if (Dependencies.size() > 0) {
+ InitializeDependencies();
+
+ if (FDependentLibraryNames != NULL) {
+ for (std::vector<TString>::const_iterator iterator =
+ FDependentLibraryNames->begin();
+ iterator != FDependentLibraryNames->end(); iterator++) {
+ TString fileName = *iterator;
+ AddDependency(fileName);
+ }
+ }
+ }
+}
+
+JavaLibrary::JavaLibrary() : Library(), FCreateProc(NULL) {
+}
+
+bool JavaLibrary::JavaVMCreate(size_t argc, char *argv[]) {
+ if (FCreateProc == NULL) {
+ FCreateProc = (JAVA_CREATE) GetProcAddress(LAUNCH_FUNC);
+ }
+
+ if (FCreateProc == NULL) {
+ Platform& platform = Platform::GetInstance();
+ Messages& messages = Messages::GetInstance();
+ platform.ShowMessage(
+ messages.GetMessage(FAILED_LOCATING_JVM_ENTRY_POINT));
+ return false;
+ }
+
+ return FCreateProc((int) argc, argv,
+ 0, NULL,
+ 0, NULL,
+ "",
+ "",
+ "java",
+ "java",
+ false,
+ false,
+ false,
+ 0) == 0;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/native/libapplauncher/Library.h Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2014, 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.
+ */
+
+#ifndef LIBRARY_H
+#define LIBRARY_H
+
+#include "PlatformDefs.h"
+//#include "Platform.h"
+#include "OrderedMap.h"
+
+#include "jni.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <memory.h>
+#include <string>
+#include <map>
+#include <list>
+#include <vector>
+#include <fstream>
+
+using namespace std;
+
+// Private typedef for function pointer casting
+#define LAUNCH_FUNC "JLI_Launch"
+
+typedef int (JNICALL *JAVA_CREATE)(int argc, char ** argv,
+ int jargc, const char** jargv,
+ int appclassc, const char** appclassv,
+ const char* fullversion,
+ const char* dotversion,
+ const char* pname,
+ const char* lname,
+ jboolean javaargs,
+ jboolean cpwildcard,
+ jboolean javaw,
+ jint ergo);
+
+class Library {
+private:
+ std::vector<TString> *FDependentLibraryNames;
+ std::vector<Library*> *FDependenciesLibraries;
+ Module FModule;
+ std::string fname;
+
+ void Initialize();
+ void InitializeDependencies();
+ void LoadDependencies();
+ void UnloadDependencies();
+
+public:
+ void* GetProcAddress(const std::string& MethodName) const;
+
+public:
+ Library();
+ Library(const TString &FileName);
+ ~Library();
+
+ bool Load(const TString &FileName);
+ bool Unload();
+
+ const std::string& GetName() const {
+ return fname;
+ }
+
+ void AddDependency(const TString &FileName);
+ void AddDependencies(const std::vector<TString> &Dependencies);
+};
+
+class JavaLibrary : public Library {
+ JAVA_CREATE FCreateProc;
+ JavaLibrary(const TString &FileName);
+public:
+ JavaLibrary();
+ bool JavaVMCreate(size_t argc, char *argv[]);
+};
+
+#endif // LIBRARY_H
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/native/libapplauncher/Macros.cpp Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2014, 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.
+ */
+
+#include "Macros.h"
+#include "Package.h"
+#include "Helpers.h"
+
+
+Macros::Macros(void) {
+}
+
+Macros::~Macros(void) {
+}
+
+void Macros::Initialize() {
+ Package& package = Package::GetInstance();
+ Macros& macros = Macros::GetInstance();
+
+ // Public macros.
+ macros.AddMacro(_T("$APPDIR"), package.GetPackageRootDirectory());
+ macros.AddMacro(_T("$PACKAGEDIR"), package.GetPackageAppDirectory());
+ macros.AddMacro(_T("$LAUNCHERDIR"), package.GetPackageLauncherDirectory());
+ macros.AddMacro(_T("$APPDATADIR"), package.GetAppDataDirectory());
+
+ TString javaHome =
+ FilePath::ExtractFilePath(package.GetJavaLibraryFileName());
+ macros.AddMacro(_T("$JREHOME"), javaHome);
+
+ // App CDS Macros
+ macros.AddMacro(_T("$CACHEDIR"), package.GetAppCDSCacheDirectory());
+
+ // Private macros.
+ TString javaVMLibraryName = FilePath::ExtractFileName(javaHome);
+ macros.AddMacro(_T("$JAVAVMLIBRARYNAME"), javaVMLibraryName);
+}
+
+Macros& Macros::GetInstance() {
+ static Macros instance;
+ return instance;
+}
+
+TString Macros::ExpandMacros(TString Value) {
+ TString result = Value;
+
+ for (std::map<TString, TString>::iterator iterator = FData.begin();
+ iterator != FData.end();
+ iterator++) {
+
+ TString name = iterator->first;
+
+ if (Value.find(name) != TString::npos) {
+ TString lvalue = iterator->second;
+ result = Helpers::ReplaceString(Value, name, lvalue);
+ result = ExpandMacros(result);
+ break;
+ }
+ }
+
+ return result;
+}
+
+void Macros::AddMacro(TString Key, TString Value) {
+ FData.insert(std::map<TString, TString>::value_type(Key, Value));
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/native/libapplauncher/Macros.h Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2014, 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.
+ */
+
+#ifndef MACROS_H
+#define MACROS_H
+
+#include "Platform.h"
+
+#include <map>
+
+
+class Macros {
+private:
+ std::map<TString, TString> FData;
+
+ Macros(void);
+
+public:
+ static Macros& GetInstance();
+ static void Initialize();
+ ~Macros(void);
+
+ TString ExpandMacros(TString Value);
+ void AddMacro(TString Key, TString Value);
+};
+
+#endif // MACROS_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/native/libapplauncher/Messages.cpp Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2014, 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.
+ */
+
+#include "Messages.h"
+#include "Platform.h"
+#include "FilePath.h"
+#include "Helpers.h"
+#include "Macros.h"
+#include "JavaVirtualMachine.h"
+
+Messages::Messages(void) {
+ FMessages.SetReadOnly(false);
+ FMessages.SetValue(LIBRARY_NOT_FOUND, _T("Failed to find library."));
+ FMessages.SetValue(FAILED_CREATING_JVM, _T("Failed to create JVM"));
+ FMessages.SetValue(FAILED_LOCATING_JVM_ENTRY_POINT,
+ _T("Failed to locate JLI_Launch"));
+ FMessages.SetValue(NO_MAIN_CLASS_SPECIFIED, _T("No main class specified"));
+ FMessages.SetValue(METHOD_NOT_FOUND, _T("No method %s in class %s."));
+ FMessages.SetValue(CLASS_NOT_FOUND, _T("Class %s not found."));
+ FMessages.SetValue(ERROR_INVOKING_METHOD, _T("Error invoking method."));
+ FMessages.SetValue(APPCDS_CACHE_FILE_NOT_FOUND,
+ _T("Error: AppCDS cache does not exists:\n%s\n"));
+}
+
+Messages& Messages::GetInstance() {
+ static Messages instance;
+ // Guaranteed to be destroyed. Instantiated on first use.
+ return instance;
+}
+
+Messages::~Messages(void) {
+}
+
+TString Messages::GetMessage(const TString Key) {
+ TString result;
+ FMessages.GetValue(Key, result);
+ Macros& macros = Macros::GetInstance();
+ result = macros.ExpandMacros(result);
+ return result;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/native/libapplauncher/Messages.h Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2014, 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.
+ */
+
+#ifndef MESSAGES_H
+#define MESSAGES_H
+
+#include "PropertyFile.h"
+
+#define LIBRARY_NOT_FOUND _T("library.not.found")
+#define FAILED_CREATING_JVM _T("failed.creating.jvm")
+#define FAILED_LOCATING_JVM_ENTRY_POINT _T("failed.locating.jvm.entry.point")
+#define NO_MAIN_CLASS_SPECIFIED _T("no.main.class.specified")
+
+#define METHOD_NOT_FOUND _T("method.not.found")
+#define CLASS_NOT_FOUND _T("class.not.found")
+#define ERROR_INVOKING_METHOD _T("error.invoking.method")
+
+#define CONFIG_FILE_NOT_FOUND _T("config.file.not.found")
+
+#define BUNDLED_JVM_NOT_FOUND _T("bundled.jvm.not.found")
+
+#define APPCDS_CACHE_FILE_NOT_FOUND _T("appcds.cache.file.not.found")
+
+class Messages {
+private:
+ PropertyFile FMessages;
+
+ Messages(void);
+public:
+ static Messages& GetInstance();
+ ~Messages(void);
+
+ TString GetMessage(const TString Key);
+};
+
+#endif // MESSAGES_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/native/libapplauncher/OrderedMap.h Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,239 @@
+/*
+ * Copyright (c) 2015, 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.
+ */
+
+#ifndef ORDEREDMAP_H
+#define ORDEREDMAP_H
+
+#include <map>
+#include <vector>
+#include <assert.h>
+#include <stdexcept>
+
+#include <iostream>
+
+template <typename _T1, typename _T2>
+struct JPPair
+{
+ typedef _T1 first_type;
+ typedef _T2 second_type;
+
+ first_type first;
+ second_type second;
+
+ JPPair(first_type Value1, second_type Value2) {
+ first = Value1;
+ second = Value2;
+ }
+};
+
+
+template <typename TKey, typename TValue>
+class OrderedMap {
+public:
+ typedef TKey key_type;
+ typedef TValue mapped_type;
+ typedef JPPair<key_type, mapped_type> container_type;
+ typedef typename std::vector<container_type*>::iterator iterator;
+ typedef typename std::vector<container_type*>::const_iterator const_iterator;
+
+private:
+ typedef std::map<key_type, container_type*> map_type;
+ typedef std::vector<container_type*> list_type;
+
+ map_type FMap;
+ list_type FList;
+ bool FAllowDuplicates;
+
+ typename list_type::iterator FindListItem(const key_type Key) {
+ typename list_type::iterator result = FList.end();
+
+ for (typename list_type::iterator iterator =
+ FList.begin(); iterator != FList.end(); iterator++) {
+ container_type *item = *iterator;
+
+ if (item->first == Key) {
+ result = iterator;
+ break;
+ }
+ }
+
+ return result;
+ }
+
+public:
+ OrderedMap() {
+ FAllowDuplicates = false;
+ }
+
+ OrderedMap(const OrderedMap<key_type, mapped_type> &Value) {
+ Append(Value);
+ }
+
+ ~OrderedMap() {
+ Clear();
+ }
+
+ void SetAllowDuplicates(bool Value) {
+ FAllowDuplicates = Value;
+ }
+
+ iterator begin() {
+ return FList.begin();
+ }
+
+ const_iterator begin() const {
+ return FList.begin();
+ }
+
+ iterator end() {
+ return FList.end();
+ }
+
+ const_iterator end() const {
+ return FList.end();
+ }
+
+ void Clear() {
+ for (typename list_type::iterator iterator =
+ FList.begin(); iterator != FList.end(); iterator++) {
+ container_type *item = *iterator;
+
+ if (item != NULL) {
+ delete item;
+ item = NULL;
+ }
+ }
+
+ FMap.clear();
+ FList.clear();
+ }
+
+ bool ContainsKey(key_type Key) {
+ bool result = false;
+
+ if (FMap.find(Key) != FMap.end()) {
+ result = true;
+ }
+
+ return result;
+ }
+
+ std::vector<key_type> GetKeys() {
+ std::vector<key_type> result;
+
+ for (typename list_type::const_iterator iterator = FList.begin();
+ iterator != FList.end(); iterator++) {
+ container_type *item = *iterator;
+ result.push_back(item->first);
+ }
+
+ return result;
+ }
+
+ void Assign(const OrderedMap<key_type, mapped_type> &Value) {
+ Clear();
+ Append(Value);
+ }
+
+ void Append(const OrderedMap<key_type, mapped_type> &Value) {
+ for (size_t index = 0; index < Value.FList.size(); index++) {
+ container_type *item = Value.FList[index];
+ Append(item->first, item->second);
+ }
+ }
+
+ void Append(key_type Key, mapped_type Value) {
+ container_type *item = new container_type(Key, Value);
+ FMap.insert(std::pair<key_type, container_type*>(Key, item));
+ FList.push_back(item);
+ }
+
+ bool RemoveByKey(key_type Key) {
+ bool result = false;
+ typename list_type::iterator iterator = FindListItem(Key);
+
+ if (iterator != FList.end()) {
+ FMap.erase(Key);
+ FList.erase(iterator);
+ result = true;
+ }
+
+ return result;
+ }
+
+ bool GetValue(key_type Key, mapped_type &Value) {
+ bool result = false;
+ container_type* item = FMap[Key];
+
+ if (item != NULL) {
+ Value = item->second;
+ result = true;
+ }
+
+ return result;
+ }
+
+ bool SetValue(key_type Key, mapped_type &Value) {
+ bool result = false;
+
+ if ((FAllowDuplicates == false) && (ContainsKey(Key) == true)) {
+ container_type *item = FMap[Key];
+
+ if (item != NULL) {
+ item->second = Value;
+ result = true;
+ }
+ }
+ else {
+ Append(Key, Value);
+ result = true;
+ }
+
+ return result;
+ }
+
+ mapped_type &operator[](key_type Key) {
+ container_type* item = FMap[Key];
+ assert(item != NULL);
+
+ if (item != NULL) {
+ return item->second;
+ }
+
+ throw std::invalid_argument("Key not found");
+ }
+
+ OrderedMap& operator= (OrderedMap &Value) {
+ Clear();
+ Append(Value);
+ return *this;
+ }
+
+ size_t Count() {
+ return FList.size();
+ }
+};
+
+#endif // ORDEREDMAP_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/native/libapplauncher/Package.cpp Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,556 @@
+/*
+ * Copyright (c) 2014, 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.
+ */
+
+#include "Package.h"
+#include "Helpers.h"
+#include "Macros.h"
+#include "IniFile.h"
+
+#include <assert.h>
+
+
+Package::Package(void) {
+ FInitialized = false;
+ Initialize();
+}
+
+TPlatformNumber StringToPercentageOfNumber(TString Value,
+ TPlatformNumber Number) {
+ TPlatformNumber result = 0;
+ size_t percentage = atoi(PlatformString(Value.c_str()));
+
+ if (percentage > 0 && Number > 0) {
+ result = Number * percentage / 100;
+ }
+
+ return result;
+}
+
+void Package::Initialize() {
+ if (FInitialized == true) {
+ return;
+ }
+
+ Platform& platform = Platform::GetInstance();
+
+ FBootFields = new PackageBootFields();
+ FDebugging = dsNone;
+
+ FBootFields->FPackageRootDirectory = platform.GetPackageRootDirectory();
+ FBootFields->FPackageAppDirectory = platform.GetPackageAppDirectory();
+ FBootFields->FPackageLauncherDirectory =
+ platform.GetPackageLauncherDirectory();
+ FBootFields->FAppDataDirectory = platform.GetAppDataDirectory();
+
+ std::map<TString, TString> keys = platform.GetKeys();
+
+ // Read from configure.cfg/Info.plist
+ AutoFreePtr<ISectionalPropertyContainer> config =
+ platform.GetConfigFile(platform.GetConfigFileName());
+
+ config->GetValue(keys[CONFIG_SECTION_APPLICATION],
+ keys[JPACKAGE_APP_DATA_DIR], FBootFields->FPackageAppDataDirectory);
+ FBootFields->FPackageAppDataDirectory =
+ FilePath::FixPathForPlatform(FBootFields->FPackageAppDataDirectory);
+
+ // Main JAR.
+ config->GetValue(keys[CONFIG_SECTION_APPLICATION],
+ keys[CONFIG_MAINJAR_KEY], FBootFields->FMainJar);
+ FBootFields->FMainJar =
+ FilePath::IncludeTrailingSeparator(GetPackageAppDirectory())
+ + FilePath::FixPathForPlatform(FBootFields->FMainJar);
+
+ // Main Module.
+ config->GetValue(keys[CONFIG_SECTION_APPLICATION],
+ keys[CONFIG_MAINMODULE_KEY], FBootFields->FMainModule);
+
+ // Classpath.
+ // 1. If the provided class path contains main jar then only use
+ // provided class path.
+ // 2. If class path provided by config file is empty then add main jar.
+ // 3. If main jar is not in provided class path then add it.
+ config->GetValue(keys[CONFIG_SECTION_APPLICATION],
+ keys[CONFIG_CLASSPATH_KEY], FBootFields->FClassPath);
+ FBootFields->FClassPath =
+ FilePath::FixPathSeparatorForPlatform(FBootFields->FClassPath);
+
+ if (FBootFields->FClassPath.empty() == true) {
+ FBootFields->FClassPath = GetMainJar();
+ } else if (FBootFields->FClassPath.find(GetMainJar()) == TString::npos) {
+ FBootFields->FClassPath = GetMainJar()
+ + FilePath::PathSeparator() + FBootFields->FClassPath;
+ }
+
+ // Modulepath.
+ config->GetValue(keys[CONFIG_SECTION_APPLICATION],
+ keys[CONFIG_MODULEPATH_KEY], FBootFields->FModulePath);
+ FBootFields->FModulePath =
+ FilePath::FixPathSeparatorForPlatform(FBootFields->FModulePath);
+
+ // Main Class.
+ config->GetValue(keys[CONFIG_SECTION_APPLICATION],
+ keys[CONFIG_MAINCLASSNAME_KEY], FBootFields->FMainClassName);
+
+ // Splash Screen.
+ if (config->GetValue(keys[CONFIG_SECTION_APPLICATION],
+ keys[CONFIG_SPLASH_KEY],
+ FBootFields->FSplashScreenFileName) == true) {
+ FBootFields->FSplashScreenFileName =
+ FilePath::IncludeTrailingSeparator(GetPackageAppDirectory())
+ + FilePath::FixPathForPlatform(FBootFields->FSplashScreenFileName);
+
+ if (FilePath::FileExists(FBootFields->FSplashScreenFileName) == false) {
+ FBootFields->FSplashScreenFileName = _T("");
+ }
+ }
+
+ // Runtime.
+ config->GetValue(keys[CONFIG_SECTION_APPLICATION],
+ keys[JAVA_RUNTIME_KEY], FBootFields->FJavaRuntimeDirectory);
+
+ // Read jvmargs.
+ PromoteAppCDSState(config);
+ ReadJavaOptions(config);
+
+ // Read args if none were passed in.
+ if (FBootFields->FArgs.size() == 0) {
+ OrderedMap<TString, TString> args;
+
+ if (config->GetSection(keys[CONFIG_SECTION_ARGOPTIONS], args) == true) {
+ FBootFields->FArgs = Helpers::MapToNameValueList(args);
+ }
+ }
+
+ // Auto Memory.
+ TString autoMemory;
+
+ if (config->GetValue(keys[CONFIG_SECTION_APPLICATION],
+ keys[CONFIG_APP_MEMORY], autoMemory) == true) {
+ if (autoMemory == _T("auto") || autoMemory == _T("100%")) {
+ FBootFields->FMemoryState = PackageBootFields::msAuto;
+ FBootFields->FMemorySize = platform.GetMemorySize();
+ } else if (autoMemory.length() == 2 && isdigit(autoMemory[0]) &&
+ autoMemory[1] == '%') {
+ FBootFields->FMemoryState = PackageBootFields::msAuto;
+ FBootFields->FMemorySize =
+ StringToPercentageOfNumber(autoMemory.substr(0, 1),
+ platform.GetMemorySize());
+ } else if (autoMemory.length() == 3 && isdigit(autoMemory[0]) &&
+ isdigit(autoMemory[1]) && autoMemory[2] == '%') {
+ FBootFields->FMemoryState = PackageBootFields::msAuto;
+ FBootFields->FMemorySize =
+ StringToPercentageOfNumber(autoMemory.substr(0, 2),
+ platform.GetMemorySize());
+ } else {
+ FBootFields->FMemoryState = PackageBootFields::msManual;
+ FBootFields->FMemorySize = 0;
+ }
+ }
+
+ // Debug
+ TString debug;
+ if (config->GetValue(keys[CONFIG_SECTION_APPLICATION],
+ keys[CONFIG_APP_DEBUG], debug) == true) {
+ FBootFields->FArgs.push_back(debug);
+ }
+}
+
+void Package::Clear() {
+ FreeBootFields();
+ FInitialized = false;
+}
+
+// This is the only location that the AppCDS state should be modified except
+// by command line arguments provided by the user.
+//
+// The state of AppCDS is as follows:
+//
+// -> cdsUninitialized
+// -> cdsGenCache If -Xappcds:generatecache
+// -> cdsDisabled If -Xappcds:off
+// -> cdsEnabled If "AppCDSJavaOptions" section is present
+// -> cdsAuto If "AppCDSJavaOptions" section is present and
+// app.appcds.cache=auto
+// -> cdsDisabled Default
+//
+void Package::PromoteAppCDSState(ISectionalPropertyContainer* Config) {
+ Platform& platform = Platform::GetInstance();
+ std::map<TString, TString> keys = platform.GetKeys();
+
+ // The AppCDS state can change at this point.
+ switch (platform.GetAppCDSState()) {
+ case cdsEnabled:
+ case cdsAuto:
+ case cdsDisabled:
+ case cdsGenCache: {
+ // Do nothing.
+ break;
+ }
+
+ case cdsUninitialized: {
+ if (Config->ContainsSection(
+ keys[CONFIG_SECTION_APPCDSJAVAOPTIONS]) == true) {
+ // If the AppCDS section is present then enable AppCDS.
+ TString appCDSCacheValue;
+
+ // If running with AppCDS enabled, and the configuration has
+ // been setup so "auto" is enabled, then
+ // the launcher will attempt to generate the cache file
+ // automatically and run the application.
+ if (Config->GetValue(keys[CONFIG_SECTION_APPLICATION],
+ _T("app.appcds.cache"), appCDSCacheValue) == true &&
+ appCDSCacheValue == _T("auto")) {
+ platform.SetAppCDSState(cdsAuto);
+ }
+ else {
+ platform.SetAppCDSState(cdsEnabled);
+ }
+ } else {
+
+ platform.SetAppCDSState(cdsDisabled);
+ }
+ }
+ }
+}
+
+void Package::ReadJavaOptions(ISectionalPropertyContainer* Config) {
+ Platform& platform = Platform::GetInstance();
+ std::map<TString, TString> keys = platform.GetKeys();
+
+ // Evaluate based on the current AppCDS state.
+ switch (platform.GetAppCDSState()) {
+ case cdsUninitialized: {
+ throw Exception(_T("Internal Error"));
+ }
+
+ case cdsDisabled: {
+ Config->GetSection(keys[CONFIG_SECTION_JAVAOPTIONS],
+ FBootFields->FJavaOptions);
+ break;
+ }
+
+ case cdsGenCache: {
+ Config->GetSection(keys[
+ CONFIG_SECTION_APPCDSGENERATECACHEJAVAOPTIONS],
+ FBootFields->FJavaOptions);
+ break;
+ }
+
+ case cdsAuto:
+ case cdsEnabled: {
+ if (Config->GetValue(keys[CONFIG_SECTION_APPCDSJAVAOPTIONS],
+ _T( "-XX:SharedArchiveFile"),
+ FBootFields->FAppCDSCacheFileName) == true) {
+ // File names may contain the incorrect path separators.
+ // The cache file name must be corrected at this point.
+ if (FBootFields->FAppCDSCacheFileName.empty() == false) {
+ IniFile* iniConfig = dynamic_cast<IniFile*>(Config);
+
+ if (iniConfig != NULL) {
+ FBootFields->FAppCDSCacheFileName =
+ FilePath::FixPathForPlatform(
+ FBootFields->FAppCDSCacheFileName);
+ iniConfig->SetValue(keys[
+ CONFIG_SECTION_APPCDSJAVAOPTIONS],
+ _T( "-XX:SharedArchiveFile"),
+ FBootFields->FAppCDSCacheFileName);
+ }
+ }
+
+ Config->GetSection(keys[CONFIG_SECTION_APPCDSJAVAOPTIONS],
+ FBootFields->FJavaOptions);
+ }
+
+ break;
+ }
+ }
+}
+
+void Package::SetCommandLineArguments(int argc, TCHAR* argv[]) {
+ if (argc > 0) {
+ std::list<TString> args;
+
+ // Prepare app arguments. Skip value at index 0 -
+ // this is path to executable.
+ FBootFields->FCommandName = argv[0];
+
+ // Path to executable is at 0 index so start at index 1.
+ for (int index = 1; index < argc; index++) {
+ TString arg = argv[index];
+
+#ifdef DEBUG
+ if (arg == _T("-debug")) {
+ FDebugging = dsNative;
+ }
+
+ if (arg == _T("-javadebug")) {
+ FDebugging = dsJava;
+ }
+#endif //DEBUG
+#ifdef MAC
+ if (arg.find(_T("-psn_"), 0) != TString::npos) {
+ Platform& platform = Platform::GetInstance();
+
+ if (platform.IsMainThread() == true) {
+#ifdef DEBUG
+ printf("%s\n", arg.c_str());
+#endif //DEBUG
+ continue;
+ }
+ }
+
+ if (arg == _T("-NSDocumentRevisionsDebugMode")) {
+ // Ignore -NSDocumentRevisionsDebugMode and
+ // the following YES/NO
+ index++;
+ continue;
+ }
+#endif //MAC
+
+ args.push_back(arg);
+ }
+
+ if (args.size() > 0) {
+ FBootFields->FArgs = args;
+ }
+ }
+}
+
+Package& Package::GetInstance() {
+ static Package instance;
+ // Guaranteed to be destroyed. Instantiated on first use.
+ return instance;
+}
+
+Package::~Package(void) {
+ FreeBootFields();
+}
+
+void Package::FreeBootFields() {
+ if (FBootFields != NULL) {
+ delete FBootFields;
+ FBootFields = NULL;
+ }
+}
+
+OrderedMap<TString, TString> Package::GetJavaOptions() {
+ return FBootFields->FJavaOptions;
+}
+
+std::vector<TString> GetKeysThatAreNotDuplicates(OrderedMap<TString,
+ TString> &Defaults, OrderedMap<TString, TString> &Overrides) {
+ std::vector<TString> result;
+ std::vector<TString> overrideKeys = Overrides.GetKeys();
+
+ for (size_t index = 0; index < overrideKeys.size(); index++) {
+ TString overridesKey = overrideKeys[index];
+ TString overridesValue;
+ TString defaultValue;
+
+ if ((Defaults.ContainsKey(overridesKey) == false) ||
+ (Defaults.GetValue(overridesKey, defaultValue) == true &&
+ Overrides.GetValue(overridesKey, overridesValue) == true &&
+ defaultValue != overridesValue)) {
+ result.push_back(overridesKey);
+ }
+ }
+
+ return result;
+}
+
+OrderedMap<TString, TString> CreateOrderedMapFromKeyList(OrderedMap<TString,
+ TString> &Map, std::vector<TString> &Keys) {
+ OrderedMap<TString, TString> result;
+
+ for (size_t index = 0; index < Keys.size(); index++) {
+ TString key = Keys[index];
+ TString value;
+
+ if (Map.GetValue(key, value) == true) {
+ result.Append(key, value);
+ }
+ }
+
+ return result;
+}
+
+std::vector<TString> GetKeysThatAreNotOverridesOfDefaultValues(
+ OrderedMap<TString, TString> &Defaults, OrderedMap<TString,
+ TString> &Overrides) {
+ std::vector<TString> result;
+ std::vector<TString> keys = Overrides.GetKeys();
+
+ for (unsigned int index = 0; index< keys.size(); index++) {
+ TString key = keys[index];
+
+ if (Defaults.ContainsKey(key) == true) {
+ try {
+ TString value = Overrides[key];
+ Defaults[key] = value;
+ }
+ catch (std::out_of_range &) {
+ }
+ }
+ else {
+ result.push_back(key);
+ }
+ }
+
+ return result;
+}
+
+std::list<TString> Package::GetArgs() {
+ assert(FBootFields != NULL);
+ return FBootFields->FArgs;
+}
+
+TString Package::GetPackageRootDirectory() {
+ assert(FBootFields != NULL);
+ return FBootFields->FPackageRootDirectory;
+}
+
+TString Package::GetPackageAppDirectory() {
+ assert(FBootFields != NULL);
+ return FBootFields->FPackageAppDirectory;
+}
+
+TString Package::GetPackageLauncherDirectory() {
+ assert(FBootFields != NULL);
+ return FBootFields->FPackageLauncherDirectory;
+}
+
+TString Package::GetAppDataDirectory() {
+ assert(FBootFields != NULL);
+ return FBootFields->FAppDataDirectory;
+}
+
+TString Package::GetAppCDSCacheDirectory() {
+ if (FAppCDSCacheDirectory.empty()) {
+ Platform& platform = Platform::GetInstance();
+ FAppCDSCacheDirectory = FilePath::IncludeTrailingSeparator(
+ platform.GetAppDataDirectory())
+ + FilePath::IncludeTrailingSeparator(
+ GetPackageAppDataDirectory()) + _T("cache");
+
+ Macros& macros = Macros::GetInstance();
+ FAppCDSCacheDirectory = macros.ExpandMacros(FAppCDSCacheDirectory);
+ FAppCDSCacheDirectory =
+ FilePath::FixPathForPlatform(FAppCDSCacheDirectory);
+ }
+
+ return FAppCDSCacheDirectory;
+}
+
+TString Package::GetAppCDSCacheFileName() {
+ assert(FBootFields != NULL);
+
+ if (FBootFields->FAppCDSCacheFileName.empty() == false) {
+ Macros& macros = Macros::GetInstance();
+ FBootFields->FAppCDSCacheFileName =
+ macros.ExpandMacros(FBootFields->FAppCDSCacheFileName);
+ FBootFields->FAppCDSCacheFileName =
+ FilePath::FixPathForPlatform(FBootFields->FAppCDSCacheFileName);
+ }
+
+ return FBootFields->FAppCDSCacheFileName;
+}
+
+TString Package::GetPackageAppDataDirectory() {
+ assert(FBootFields != NULL);
+ return FBootFields->FPackageAppDataDirectory;
+}
+
+TString Package::GetClassPath() {
+ assert(FBootFields != NULL);
+ return FBootFields->FClassPath;
+}
+
+TString Package::GetModulePath() {
+ assert(FBootFields != NULL);
+ return FBootFields->FModulePath;
+}
+
+TString Package::GetMainJar() {
+ assert(FBootFields != NULL);
+ return FBootFields->FMainJar;
+}
+
+TString Package::GetMainModule() {
+ assert(FBootFields != NULL);
+ return FBootFields->FMainModule;
+}
+
+TString Package::GetMainClassName() {
+ assert(FBootFields != NULL);
+ return FBootFields->FMainClassName;
+}
+
+TString Package::GetJavaLibraryFileName() {
+ assert(FBootFields != NULL);
+
+ if (FBootFields->FJavaLibraryFileName.empty() == true) {
+ Platform& platform = Platform::GetInstance();
+ Macros& macros = Macros::GetInstance();
+ TString jvmRuntimePath = macros.ExpandMacros(GetJavaRuntimeDirectory());
+ FBootFields->FJavaLibraryFileName =
+ platform.GetBundledJavaLibraryFileName(jvmRuntimePath);
+ }
+
+ return FBootFields->FJavaLibraryFileName;
+}
+
+TString Package::GetJavaRuntimeDirectory() {
+ assert(FBootFields != NULL);
+ return FBootFields->FJavaRuntimeDirectory;
+}
+
+TString Package::GetSplashScreenFileName() {
+ assert(FBootFields != NULL);
+ return FBootFields->FSplashScreenFileName;
+}
+
+bool Package::HasSplashScreen() {
+ assert(FBootFields != NULL);
+ return FilePath::FileExists(FBootFields->FSplashScreenFileName);
+}
+
+TString Package::GetCommandName() {
+ assert(FBootFields != NULL);
+ return FBootFields->FCommandName;
+}
+
+TPlatformNumber Package::GetMemorySize() {
+ assert(FBootFields != NULL);
+ return FBootFields->FMemorySize;
+}
+
+PackageBootFields::MemoryState Package::GetMemoryState() {
+ assert(FBootFields != NULL);
+ return FBootFields->FMemoryState;
+}
+
+DebugState Package::Debugging() {
+ return FDebugging;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/native/libapplauncher/Package.h Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,126 @@
+/*
+ * Copyright (c) 2014, 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.
+ */
+
+#ifndef PACKAGE_H
+#define PACKAGE_H
+
+
+#include "Platform.h"
+#include "PlatformString.h"
+#include "FilePath.h"
+#include "PropertyFile.h"
+
+#include <map>
+#include <list>
+
+class PackageBootFields {
+public:
+ enum MemoryState {msManual, msAuto};
+
+public:
+ OrderedMap<TString, TString> FJavaOptions;
+ std::list<TString> FArgs;
+
+ TString FPackageRootDirectory;
+ TString FPackageAppDirectory;
+ TString FPackageLauncherDirectory;
+ TString FAppDataDirectory;
+ TString FPackageAppDataDirectory;
+ TString FClassPath;
+ TString FModulePath;
+ TString FMainJar;
+ TString FMainModule;
+ TString FMainClassName;
+ TString FJavaRuntimeDirectory;
+ TString FJavaLibraryFileName;
+ TString FSplashScreenFileName;
+ bool FUseJavaPreferences;
+ TString FCommandName;
+
+ TString FAppCDSCacheFileName;
+
+ TPlatformNumber FMemorySize;
+ MemoryState FMemoryState;
+};
+
+
+class Package {
+private:
+ Package(Package const&); // Don't Implement.
+ void operator=(Package const&); // Don't implement
+
+private:
+ bool FInitialized;
+ PackageBootFields* FBootFields;
+ TString FAppCDSCacheDirectory;
+
+ DebugState FDebugging;
+
+ Package(void);
+
+ TString GetMainJar();
+ void ReadJavaOptions(ISectionalPropertyContainer* Config);
+ void PromoteAppCDSState(ISectionalPropertyContainer* Config);
+
+public:
+ static Package& GetInstance();
+ ~Package(void);
+
+ void Initialize();
+ void Clear();
+ void FreeBootFields();
+
+ void SetCommandLineArguments(int argc, TCHAR* argv[]);
+
+ OrderedMap<TString, TString> GetJavaOptions();
+ TString GetMainModule();
+
+ std::list<TString> GetArgs();
+
+ TString GetPackageRootDirectory();
+ TString GetPackageAppDirectory();
+ TString GetPackageLauncherDirectory();
+ TString GetAppDataDirectory();
+
+ TString GetAppCDSCacheDirectory();
+ TString GetAppCDSCacheFileName();
+
+ TString GetPackageAppDataDirectory();
+ TString GetClassPath();
+ TString GetModulePath();
+ TString GetMainClassName();
+ TString GetJavaLibraryFileName();
+ TString GetJavaRuntimeDirectory();
+ TString GetSplashScreenFileName();
+ bool HasSplashScreen();
+ TString GetCommandName();
+
+ TPlatformNumber GetMemorySize();
+ PackageBootFields::MemoryState GetMemoryState();
+
+ DebugState Debugging();
+};
+
+#endif // PACKAGE_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/native/libapplauncher/Platform.cpp Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,173 @@
+/*
+ * Copyright (c) 2014, 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.
+ */
+
+#include "Platform.h"
+#include "Messages.h"
+#include "PlatformString.h"
+#include "FilePath.h"
+
+#include <fstream>
+#include <locale>
+
+#ifdef WINDOWS
+#include "WindowsPlatform.h"
+#endif // WINDOWS
+#ifdef LINUX
+#include "LinuxPlatform.h"
+#endif // LINUX
+#ifdef MAC
+#include "MacPlatform.h"
+#endif // MAC
+
+Platform& Platform::GetInstance() {
+#ifdef WINDOWS
+ static WindowsPlatform instance;
+#endif // WINDOWS
+
+#ifdef LINUX
+ static LinuxPlatform instance;
+#endif // LINUX
+
+#ifdef MAC
+ static MacPlatform instance;
+#endif // MAC
+
+ return instance;
+}
+
+TString Platform::GetConfigFileName() {
+ TString result;
+ TString basedir = GetPackageAppDirectory();
+
+ if (basedir.empty() == false) {
+ basedir = FilePath::IncludeTrailingSeparator(basedir);
+ TString appConfig = basedir + GetAppName() + _T(".cfg");
+
+ if (FilePath::FileExists(appConfig) == true) {
+ result = appConfig;
+ }
+ else {
+ result = basedir + _T("package.cfg");
+
+ if (FilePath::FileExists(result) == false) {
+ result = _T("");
+ }
+ }
+ }
+
+ return result;
+}
+
+std::list<TString> Platform::LoadFromFile(TString FileName) {
+ std::list<TString> result;
+
+ if (FilePath::FileExists(FileName) == true) {
+ std::wifstream stream(FileName.data());
+ InitStreamLocale(&stream);
+
+ if (stream.is_open() == true) {
+ while (stream.eof() == false) {
+ std::wstring line;
+ std::getline(stream, line);
+
+ // # at the first character will comment out the line.
+ if (line.empty() == false && line[0] != '#') {
+ result.push_back(PlatformString(line).toString());
+ }
+ }
+ }
+ }
+
+ return result;
+}
+
+void Platform::SaveToFile(TString FileName, std::list<TString> Contents, bool ownerOnly) {
+ TString path = FilePath::ExtractFilePath(FileName);
+
+ if (FilePath::DirectoryExists(path) == false) {
+ FilePath::CreateDirectory(path, ownerOnly);
+ }
+
+ std::wofstream stream(FileName.data());
+ InitStreamLocale(&stream);
+
+ FilePath::ChangePermissions(FileName.data(), ownerOnly);
+
+ if (stream.is_open() == true) {
+ for (std::list<TString>::const_iterator iterator =
+ Contents.begin(); iterator != Contents.end(); iterator++) {
+ TString line = *iterator;
+ stream << PlatformString(line).toUnicodeString() << std::endl;
+ }
+ }
+}
+
+std::map<TString, TString> Platform::GetKeys() {
+ std::map<TString, TString> keys;
+ keys.insert(std::map<TString, TString>::value_type(CONFIG_VERSION,
+ _T("app.version")));
+ keys.insert(std::map<TString, TString>::value_type(CONFIG_MAINJAR_KEY,
+ _T("app.mainjar")));
+ keys.insert(std::map<TString, TString>::value_type(CONFIG_MAINMODULE_KEY,
+ _T("app.mainmodule")));
+ keys.insert(std::map<TString, TString>::value_type(CONFIG_MAINCLASSNAME_KEY,
+ _T("app.mainclass")));
+ keys.insert(std::map<TString, TString>::value_type(CONFIG_CLASSPATH_KEY,
+ _T("app.classpath")));
+ keys.insert(std::map<TString, TString>::value_type(CONFIG_MODULEPATH_KEY,
+ _T("app.modulepath")));
+ keys.insert(std::map<TString, TString>::value_type(APP_NAME_KEY,
+ _T("app.name")));
+ keys.insert(std::map<TString, TString>::value_type(JAVA_RUNTIME_KEY,
+ _T("app.runtime")));
+ keys.insert(std::map<TString, TString>::value_type(JPACKAGE_APP_DATA_DIR,
+ _T("app.identifier")));
+ keys.insert(std::map<TString, TString>::value_type(CONFIG_SPLASH_KEY,
+ _T("app.splash")));
+ keys.insert(std::map<TString, TString>::value_type(CONFIG_APP_MEMORY,
+ _T("app.memory")));
+ keys.insert(std::map<TString, TString>::value_type(CONFIG_APP_DEBUG,
+ _T("app.debug")));
+ keys.insert(std::map<TString,
+ TString>::value_type(CONFIG_APPLICATION_INSTANCE,
+ _T("app.application.instance")));
+ keys.insert(std::map<TString,
+ TString>::value_type(CONFIG_SECTION_APPLICATION,
+ _T("Application")));
+ keys.insert(std::map<TString,
+ TString>::value_type(CONFIG_SECTION_JAVAOPTIONS,
+ _T("JavaOptions")));
+ keys.insert(std::map<TString,
+ TString>::value_type(CONFIG_SECTION_APPCDSJAVAOPTIONS,
+ _T("AppCDSJavaOptions")));
+ keys.insert(std::map<TString,
+ TString>::value_type(CONFIG_SECTION_APPCDSGENERATECACHEJAVAOPTIONS,
+ _T("AppCDSGenerateCacheJavaOptions")));
+ keys.insert(std::map<TString,
+ TString>::value_type(CONFIG_SECTION_ARGOPTIONS,
+ _T("ArgOptions")));
+
+ return keys;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/native/libapplauncher/Platform.h Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,266 @@
+/*
+ * Copyright (c) 2014, 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.
+ */
+
+#ifndef PLATFORM_H
+#define PLATFORM_H
+
+#include "PlatformDefs.h"
+#include "Properties.h"
+#include "OrderedMap.h"
+#include "Library.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <memory.h>
+#include <string>
+#include <map>
+#include <list>
+#include <vector>
+#include <fstream>
+
+using namespace std;
+
+// Config file sections
+#define CONFIG_SECTION_APPLICATION _T("CONFIG_SECTION_APPLICATION")
+#define CONFIG_SECTION_JAVAOPTIONS _T("CONFIG_SECTION_JAVAOPTIONS")
+#define CONFIG_SECTION_APPCDSJAVAOPTIONS _T("CONFIG_SECTION_APPCDSJAVAOPTIONS")
+#define CONFIG_SECTION_ARGOPTIONS _T("CONFIG_SECTION_ARGOPTIONS")
+#define CONFIG_SECTION_APPCDSGENERATECACHEJAVAOPTIONS \
+ _T("CONFIG_SECTION_APPCDSGENERATECACHEJAVAOPTIONS")
+
+// Config file keys.
+#define CONFIG_VERSION _T("CONFIG_VERSION")
+#define CONFIG_MAINJAR_KEY _T("CONFIG_MAINJAR_KEY")
+#define CONFIG_MAINMODULE_KEY _T("CONFIG_MAINMODULE_KEY")
+#define CONFIG_MAINCLASSNAME_KEY _T("CONFIG_MAINCLASSNAME_KEY")
+#define CONFIG_CLASSPATH_KEY _T("CONFIG_CLASSPATH_KEY")
+#define CONFIG_MODULEPATH_KEY _T("CONFIG_MODULEPATH_KEY")
+#define APP_NAME_KEY _T("APP_NAME_KEY")
+#define CONFIG_SPLASH_KEY _T("CONFIG_SPLASH_KEY")
+#define CONFIG_APP_MEMORY _T("CONFIG_APP_MEMORY")
+#define CONFIG_APP_DEBUG _T("CONFIG_APP_DEBUG")
+#define CONFIG_APPLICATION_INSTANCE _T("CONFIG_APPLICATION_INSTANCE")
+
+#define JAVA_RUNTIME_KEY _T("JAVA_RUNTIME_KEY")
+#define JPACKAGE_APP_DATA_DIR _T("CONFIG_APP_IDENTIFIER")
+
+struct WideString {
+ size_t length;
+ wchar_t* data;
+
+ WideString() { length = 0; data = NULL; }
+};
+
+struct MultibyteString {
+ size_t length;
+ char* data;
+
+ MultibyteString() { length = 0; data = NULL; }
+};
+
+class Process {
+protected:
+ std::list<TString> FOutput;
+
+public:
+ Process() {
+ Output.SetInstance(this);
+ Input.SetInstance(this);
+ }
+
+ virtual ~Process() {}
+
+ virtual bool IsRunning() = 0;
+ virtual bool Terminate() = 0;
+ virtual bool Execute(const TString Application,
+ const std::vector<TString> Arguments, bool AWait = false) = 0;
+ virtual bool Wait() = 0;
+ virtual TProcessID GetProcessID() = 0;
+
+ virtual std::list<TString> GetOutput() { return FOutput; }
+ virtual void SetInput(TString Value) = 0;
+
+ ReadProperty<Process, std::list<TString>, &Process::GetOutput> Output;
+ WriteProperty<Process, TString, &Process::SetInput> Input;
+};
+
+
+template <typename T>
+class AutoFreePtr {
+private:
+ T* FObject;
+
+public:
+ AutoFreePtr() {
+ FObject = NULL;
+ }
+
+ AutoFreePtr(T* Value) {
+ FObject = Value;
+ }
+
+ ~AutoFreePtr() {
+ if (FObject != NULL) {
+ delete FObject;
+ }
+ }
+
+ operator T* () const {
+ return FObject;
+ }
+
+ T& operator* () const {
+ return *FObject;
+ }
+
+ T* operator->() const {
+ return FObject;
+ }
+
+ T** operator&() {
+ return &FObject;
+ }
+
+ T* operator=(const T * rhs) {
+ FObject = rhs;
+ return FObject;
+ }
+};
+
+enum DebugState {dsNone, dsNative, dsJava};
+enum MessageResponse {mrOK, mrCancel};
+enum AppCDSState {cdsUninitialized, cdsDisabled,
+ cdsEnabled, cdsAuto, cdsGenCache};
+
+class Platform {
+private:
+ AppCDSState FAppCDSState;
+
+protected:
+ Platform(void): FAppCDSState(cdsUninitialized) {
+ }
+
+public:
+ AppCDSState GetAppCDSState() { return FAppCDSState; }
+ void SetAppCDSState(AppCDSState Value) { FAppCDSState = Value; }
+
+ static Platform& GetInstance();
+
+ virtual ~Platform(void) {}
+
+public:
+ virtual void ShowMessage(TString title, TString description) = 0;
+ virtual void ShowMessage(TString description) = 0;
+ virtual MessageResponse ShowResponseMessage(TString title,
+ TString description) = 0;
+
+ virtual void SetCurrentDirectory(TString Value) = 0;
+
+ // Caller must free result using delete[].
+ virtual TCHAR* ConvertStringToFileSystemString(TCHAR* Source,
+ bool &release) = 0;
+
+ // Caller must free result using delete[].
+ virtual TCHAR* ConvertFileSystemStringToString(TCHAR* Source,
+ bool &release) = 0;
+
+ // Returns:
+ // Windows=C:\Users\<username>\AppData\Local
+ // Linux=~/.local
+ // Mac=~/Library/Application Support
+ virtual TString GetAppDataDirectory() = 0;
+
+ virtual TString GetPackageAppDirectory() = 0;
+ virtual TString GetPackageLauncherDirectory() = 0;
+ virtual TString GetPackageRuntimeBinDirectory() = 0;
+ virtual TString GetAppName() = 0;
+
+ virtual TString GetConfigFileName();
+
+ virtual TString GetBundledJavaLibraryFileName(TString RuntimePath) = 0;
+
+ // Caller must free result.
+ virtual ISectionalPropertyContainer* GetConfigFile(TString FileName) = 0;
+
+ virtual TString GetModuleFileName() = 0;
+ virtual TString GetPackageRootDirectory() = 0;
+
+ virtual Module LoadLibrary(TString FileName) = 0;
+ virtual void FreeLibrary(Module Module) = 0;
+ virtual Procedure GetProcAddress(Module Module, std::string MethodName) = 0;
+
+ // Caller must free result.
+ virtual Process* CreateProcess() = 0;
+
+ virtual bool IsMainThread() = 0;
+
+ // Returns megabytes.
+ virtual TPlatformNumber GetMemorySize() = 0;
+
+ virtual std::map<TString, TString> GetKeys();
+
+ virtual void InitStreamLocale(wios *stream) = 0;
+ virtual std::list<TString> LoadFromFile(TString FileName);
+ virtual void SaveToFile(TString FileName,
+ std::list<TString> Contents, bool ownerOnly);
+
+ virtual TString GetTempDirectory() = 0;
+
+ virtual void addPlatformDependencies(JavaLibrary *pJavaLibrary) = 0;
+
+public:
+ // String helpers
+ // Caller must free result using delete[].
+ static void CopyString(char *Destination,
+ size_t NumberOfElements, const char *Source);
+
+ // Caller must free result using delete[].
+ static void CopyString(wchar_t *Destination,
+ size_t NumberOfElements, const wchar_t *Source);
+
+ static WideString MultibyteStringToWideString(const char* value);
+ static MultibyteString WideStringToMultibyteString(const wchar_t* value);
+};
+
+class Exception: public std::exception {
+private:
+ TString FMessage;
+
+protected:
+ void SetMessage(const TString Message) {
+ FMessage = Message;
+ }
+
+public:
+ explicit Exception() : exception() {}
+ explicit Exception(const TString Message) : exception() {
+ SetMessage(Message);
+ }
+ virtual ~Exception() throw() {}
+
+ TString GetMessage() { return FMessage; }
+};
+
+#endif // PLATFORM_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/native/libapplauncher/PlatformString.cpp Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,227 @@
+/*
+ * Copyright (c) 2014, 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.
+ */
+
+#include "PlatformString.h"
+
+#include "Helpers.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdlib.h>
+#include <memory.h>
+#include <sstream>
+#include <string.h>
+
+#include "jni.h"
+
+void PlatformString::initialize() {
+ FWideTStringToFree = NULL;
+ FLength = 0;
+ FData = NULL;
+}
+
+PlatformString::PlatformString(void) {
+ initialize();
+}
+
+PlatformString::~PlatformString(void) {
+ if (FData != NULL) {
+ delete[] FData;
+ }
+
+ if (FWideTStringToFree != NULL) {
+ delete[] FWideTStringToFree;
+ }
+}
+
+PlatformString::PlatformString(const PlatformString &value) {
+ initialize();
+ FLength = value.FLength;
+ FData = new char[FLength + 1];
+ Platform::CopyString(FData, FLength + 1, value.FData);
+}
+
+PlatformString::PlatformString(const char* value) {
+ initialize();
+ FLength = strlen(value);
+ FData = new char[FLength + 1];
+ Platform::CopyString(FData, FLength + 1, value);
+}
+
+PlatformString::PlatformString(size_t Value) {
+ initialize();
+
+ std::stringstream ss;
+ std::string s;
+ ss << Value;
+ s = ss.str();
+
+ FLength = strlen(s.c_str());
+ FData = new char[FLength + 1];
+ Platform::CopyString(FData, FLength + 1, s.c_str());
+}
+
+PlatformString::PlatformString(const wchar_t* value) {
+ initialize();
+ MultibyteString temp = Platform::WideStringToMultibyteString(value);
+ FLength = temp.length;
+ FData = temp.data;
+}
+
+PlatformString::PlatformString(const std::string &value) {
+ initialize();
+ const char* lvalue = value.data();
+ FLength = value.size();
+ FData = new char[FLength + 1];
+ Platform::CopyString(FData, FLength + 1, lvalue);
+}
+
+PlatformString::PlatformString(const std::wstring &value) {
+ initialize();
+ const wchar_t* lvalue = value.data();
+ MultibyteString temp = Platform::WideStringToMultibyteString(lvalue);
+ FLength = temp.length;
+ FData = temp.data;
+}
+
+TString PlatformString::Format(const TString value, ...) {
+ TString result = value;
+
+ va_list arglist;
+ va_start(arglist, value);
+
+ while (1) {
+ size_t pos = result.find(_T("%s"), 0);
+
+ if (pos == TString::npos) {
+ break;
+ }
+ else {
+ TCHAR* arg = va_arg(arglist, TCHAR*);
+
+ if (arg == NULL) {
+ break;
+ }
+ else {
+ result.replace(pos, StringLength(_T("%s")), arg);
+ }
+ }
+ }
+
+ va_end(arglist);
+
+ return result;
+}
+
+size_t PlatformString::length() {
+ return FLength;
+}
+
+char* PlatformString::c_str() {
+ return FData;
+}
+
+char* PlatformString::toMultibyte() {
+ return FData;
+}
+
+wchar_t* PlatformString::toWideString() {
+ WideString result = Platform::MultibyteStringToWideString(FData);
+
+ if (result.data != NULL) {
+ if (FWideTStringToFree != NULL) {
+ delete [] FWideTStringToFree;
+ }
+
+ FWideTStringToFree = result.data;
+ }
+
+ return result.data;
+}
+
+std::wstring PlatformString::toUnicodeString() {
+ std::wstring result;
+ wchar_t* data = toWideString();
+
+ if (FLength != 0 && data != NULL) {
+ // NOTE: Cleanup of result is handled by PlatformString destructor.
+ result = data;
+ }
+
+ return result;
+}
+
+std::string PlatformString::toStdString() {
+ std::string result;
+ char* data = toMultibyte();
+
+ if (FLength > 0 && data != NULL) {
+ result = data;
+ }
+
+ return result;
+}
+
+TCHAR* PlatformString::toPlatformString() {
+#ifdef _UNICODE
+ return toWideString();
+#else
+ return c_str();
+#endif //_UNICODE
+}
+
+TString PlatformString::toString() {
+#ifdef _UNICODE
+ return toUnicodeString();
+#else
+ return toStdString();
+#endif //_UNICODE
+}
+
+PlatformString::operator char* () {
+ return c_str();
+}
+
+PlatformString::operator wchar_t* () {
+ return toWideString();
+}
+
+PlatformString::operator std::wstring () {
+ return toUnicodeString();
+}
+
+char* PlatformString::duplicate(const char* Value) {
+ size_t length = strlen(Value);
+ char* result = new char[length + 1];
+ Platform::CopyString(result, length + 1, Value);
+ return result;
+}
+
+wchar_t* PlatformString::duplicate(const wchar_t* Value) {
+ size_t length = wcslen(Value);
+ wchar_t* result = new wchar_t[length + 1];
+ Platform::CopyString(result, length + 1, Value);
+ return result;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/native/libapplauncher/PlatformString.h Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,136 @@
+/*
+ * Copyright (c) 2014, 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.
+ */
+
+#ifndef PLATFORMSTRING_H
+#define PLATFORMSTRING_H
+
+
+#include <string>
+#include <list>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "jni.h"
+#include "Platform.h"
+
+
+template <typename T>
+class DynamicBuffer {
+private:
+ T* FData;
+ size_t FSize;
+
+public:
+ DynamicBuffer(size_t Size) {
+ FSize = 0;
+ FData = NULL;
+ Resize(Size);
+ }
+
+ ~DynamicBuffer() {
+ delete[] FData;
+ }
+
+ T* GetData() { return FData; }
+ size_t GetSize() { return FSize; }
+
+ bool Resize(size_t Size) {
+ FSize = Size;
+
+ if (FData != NULL) {
+ delete[] FData;
+ FData = NULL;
+ }
+
+ if (FSize != 0) {
+ FData = new T[FSize];
+ if (FData != NULL) {
+ Zero();
+ } else {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ void Zero() {
+ memset(FData, 0, FSize * sizeof(T));
+ }
+
+ T& operator[](size_t index) {
+ return FData[index];
+ }
+};
+
+class PlatformString {
+private:
+ char* FData; // Stored as UTF-8
+ size_t FLength;
+ wchar_t* FWideTStringToFree;
+
+ void initialize();
+
+// Prohibit Heap-Based PlatformStrings
+private:
+ static void *operator new(size_t size);
+ static void operator delete(void *ptr);
+
+public:
+ PlatformString(void);
+ PlatformString(const PlatformString &value);
+ PlatformString(const char* value);
+ PlatformString(const wchar_t* value);
+ PlatformString(const std::string &value);
+ PlatformString(const std::wstring &value);
+ PlatformString(size_t Value);
+
+ static TString Format(const TString value, ...);
+
+ ~PlatformString(void);
+
+ size_t length();
+
+ char* c_str();
+ char* toMultibyte();
+ wchar_t* toWideString();
+ std::wstring toUnicodeString();
+ std::string toStdString();
+ TCHAR* toPlatformString();
+ TString toString();
+
+ operator char* ();
+ operator wchar_t* ();
+ operator std::wstring ();
+
+ // Caller must free result using delete[].
+ static char* duplicate(const char* Value);
+
+ // Caller must free result using delete[].
+ static wchar_t* duplicate(const wchar_t* Value);
+};
+
+
+#endif // PLATFORMSTRING_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/native/libapplauncher/Properties.h Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,184 @@
+/*
+ * Copyright (c) 2014, 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.
+ */
+
+#ifndef PROPERTIES_H
+#define PROPERTIES_H
+
+#include "PlatformDefs.h"
+#include "OrderedMap.h"
+
+//#include <stdio.h>
+//#include <stdlib.h>
+//#include <memory.h>
+//#include <string>
+//#include <map>
+//#include <list>
+//#include <vector>
+//#include <fstream>
+
+//using namespace std;
+
+template <typename ObjectType, typename ValueType,
+ ValueType (ObjectType::*getter)(void),
+ void (ObjectType::*setter)(ValueType)>
+class Property {
+private:
+ ObjectType* FObject;
+
+public:
+ Property() {
+ FObject = NULL;
+ }
+
+ void SetInstance(ObjectType* Value) {
+ FObject = Value;
+ }
+
+ // To set the value using the set method.
+ ValueType operator =(const ValueType& Value) {
+ assert(FObject != NULL);
+ (FObject->*setter)(Value);
+ return Value;
+ }
+
+ // The Property class is treated as the internal type.
+ operator ValueType() {
+ assert(FObject != NULL);
+ return (FObject->*getter)();
+ }
+};
+
+template <typename ObjectType, typename ValueType,
+ ValueType (ObjectType::*getter)(void)>
+class ReadProperty {
+private:
+ ObjectType* FObject;
+
+public:
+ ReadProperty() {
+ FObject = NULL;
+ }
+
+ void SetInstance(ObjectType* Value) {
+ FObject = Value;
+ }
+
+ // The Property class is treated as the internal type.
+ operator ValueType() {
+ assert(FObject != NULL);
+ return (FObject->*getter)();
+ }
+};
+
+template <typename ObjectType, typename ValueType,
+ void (ObjectType::*setter)(ValueType)>
+class WriteProperty {
+private:
+ ObjectType* FObject;
+
+public:
+ WriteProperty() {
+ FObject = NULL;
+ }
+
+ void SetInstance(ObjectType* Value) {
+ FObject = Value;
+ }
+
+ // To set the value using the set method.
+ ValueType operator =(const ValueType& Value) {
+ assert(FObject != NULL);
+ (FObject->*setter)(Value);
+ return Value;
+ }
+};
+
+template <typename ValueType,
+ ValueType (*getter)(void), void (*setter)(ValueType)>
+class StaticProperty {
+public:
+ StaticProperty() {
+ }
+
+ // To set the value using the set method.
+ ValueType operator =(const ValueType& Value) {
+ (*getter)(Value);
+ return Value;
+ }
+
+ // The Property class is treated as the internal type which is the getter.
+ operator ValueType() {
+ return (*setter)();
+ }
+};
+
+template <typename ValueType, ValueType (*getter)(void)>
+class StaticReadProperty {
+public:
+ StaticReadProperty() {
+ }
+
+ // The Property class is treated as the internal type which is the getter.
+ operator ValueType() {
+ return (*getter)();
+ }
+};
+
+template <typename ValueType, void (*setter)(ValueType)>
+class StaticWriteProperty {
+public:
+ StaticWriteProperty() {
+ }
+
+ // To set the value using the set method.
+ ValueType operator =(const ValueType& Value) {
+ (*setter)(Value);
+ return Value;
+ }
+};
+
+class IPropertyContainer {
+public:
+ IPropertyContainer(void) {}
+ virtual ~IPropertyContainer(void) {}
+
+ virtual bool GetValue(const TString Key, TString& Value) = 0;
+ virtual size_t GetCount() = 0;
+};
+
+class ISectionalPropertyContainer {
+public:
+ ISectionalPropertyContainer(void) {}
+ virtual ~ISectionalPropertyContainer(void) {}
+
+ virtual bool GetValue(const TString SectionName,
+ const TString Key, TString& Value) = 0;
+ virtual bool ContainsSection(const TString SectionName) = 0;
+ virtual bool GetSection(const TString SectionName,
+ OrderedMap<TString, TString> &Data) = 0;
+};
+
+#endif // PROPERTIES_H
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/native/libapplauncher/PropertyFile.cpp Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,168 @@
+/*
+ * Copyright (c) 2014, 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.
+ */
+
+#include "PropertyFile.h"
+
+#include "Helpers.h"
+#include "FilePath.h"
+
+#include <string>
+
+
+PropertyFile::PropertyFile(void) : IPropertyContainer() {
+ FReadOnly = false;
+ FModified = false;
+}
+
+PropertyFile::PropertyFile(const TString FileName) : IPropertyContainer() {
+ FReadOnly = true;
+ FModified = false;
+ LoadFromFile(FileName);
+}
+
+PropertyFile::PropertyFile(OrderedMap<TString, TString> Value) {
+ FData.Append(Value);
+}
+
+PropertyFile::PropertyFile(PropertyFile &Value) {
+ FData = Value.FData;
+ FReadOnly = Value.FReadOnly;
+ FModified = Value.FModified;
+}
+
+PropertyFile::~PropertyFile(void) {
+ FData.Clear();
+}
+
+void PropertyFile::SetModified(bool Value) {
+ FModified = Value;
+}
+
+bool PropertyFile::IsModified() {
+ return FModified;
+}
+
+bool PropertyFile::GetReadOnly() {
+ return FReadOnly;
+}
+
+void PropertyFile::SetReadOnly(bool Value) {
+ FReadOnly = Value;
+}
+
+bool PropertyFile::LoadFromFile(const TString FileName) {
+ bool result = false;
+ Platform& platform = Platform::GetInstance();
+
+ std::list<TString> contents = platform.LoadFromFile(FileName);
+
+ if (contents.empty() == false) {
+ for (std::list<TString>::const_iterator iterator = contents.begin();
+ iterator != contents.end(); iterator++) {
+ TString line = *iterator;
+ TString name;
+ TString value;
+
+ if (Helpers::SplitOptionIntoNameValue(line, name, value) == true) {
+ FData.Append(name, value);
+ }
+ }
+
+ SetModified(false);
+ result = true;
+ }
+
+ return result;
+}
+
+bool PropertyFile::SaveToFile(const TString FileName, bool ownerOnly) {
+ bool result = false;
+
+ if (GetReadOnly() == false && IsModified()) {
+ std::list<TString> contents;
+ std::vector<TString> keys = FData.GetKeys();
+
+ for (size_t index = 0; index < keys.size(); index++) {
+ TString name = keys[index];
+
+ try {
+ TString value;// = FData[index];
+
+ if (FData.GetValue(name, value) == true) {
+ TString line = name + _T('=') + value;
+ contents.push_back(line);
+ }
+ }
+ catch (std::out_of_range &) {
+ }
+ }
+
+ Platform& platform = Platform::GetInstance();
+ platform.SaveToFile(FileName, contents, ownerOnly);
+
+ SetModified(false);
+ result = true;
+ }
+
+ return result;
+}
+
+bool PropertyFile::GetValue(const TString Key, TString& Value) {
+ return FData.GetValue(Key, Value);
+}
+
+bool PropertyFile::SetValue(const TString Key, TString Value) {
+ bool result = false;
+
+ if (GetReadOnly() == false) {
+ FData.SetValue(Key, Value);
+ SetModified(true);
+ result = true;
+ }
+
+ return result;
+}
+
+bool PropertyFile::RemoveKey(const TString Key) {
+ bool result = false;
+
+ if (GetReadOnly() == false) {
+ result = FData.RemoveByKey(Key);
+
+ if (result == true) {
+ SetModified(true);
+ }
+ }
+
+ return result;
+}
+
+size_t PropertyFile::GetCount() {
+ return FData.Count();
+}
+
+OrderedMap<TString, TString> PropertyFile::GetData() {
+ return FData;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/native/libapplauncher/PropertyFile.h Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2014, 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.
+ */
+
+#ifndef PROPERTYFILE_H
+#define PROPERTYFILE_H
+
+#include "Platform.h"
+#include "Helpers.h"
+
+
+class PropertyFile : public IPropertyContainer {
+private:
+ bool FReadOnly;
+ bool FModified;
+ OrderedMap<TString, TString> FData;
+
+ void SetModified(bool Value);
+
+public:
+ PropertyFile(void);
+ PropertyFile(const TString FileName);
+ PropertyFile(OrderedMap<TString, TString> Value);
+ PropertyFile(PropertyFile &Value);
+ virtual ~PropertyFile(void);
+
+ bool IsModified();
+ bool GetReadOnly();
+ void SetReadOnly(bool Value);
+
+ bool LoadFromFile(const TString FileName);
+ bool SaveToFile(const TString FileName, bool ownerOnly = true);
+
+ bool SetValue(const TString Key, TString Value);
+ bool RemoveKey(const TString Key);
+
+ OrderedMap<TString, TString> GetData();
+
+ // IPropertyContainer
+ virtual bool GetValue(const TString Key, TString& Value);
+ virtual size_t GetCount();
+};
+
+#endif // PROPERTYFILE_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/native/libapplauncher/main.cpp Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,181 @@
+/*
+ * Copyright (c) 2014, 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.
+ */
+
+#include "Platform.h"
+#include "PlatformString.h"
+#include "FilePath.h"
+#include "PropertyFile.h"
+#include "JavaVirtualMachine.h"
+#include "Package.h"
+#include "Macros.h"
+#include "Messages.h"
+
+#include <stdio.h>
+#include <signal.h>
+#include <stdlib.h>
+
+/*
+This is the app launcher program for application packaging on Windows, Mac,
+ and Linux.
+
+Basic approach:
+ - Launcher (jpackageapplauncher) is executable that loads
+ applauncher.dll/libapplauncher.dylib/libapplauncher.so
+ and calls start_launcher below.
+ - Reads app/package.cfg or Info.plist or app/<appname>.cfg for application
+ launch configuration (package.cfg is property file).
+ - Load Java with requested Java settings (bundled client Java if availble,
+ server or installed Java otherwise).
+ - Wait for Java to exit and then exit from Main
+ - To debug application by passing command line argument.
+ - Application folder is added to the library path (so LoadLibrary()) works.
+
+Limitations and future work:
+ - Running Java code in primordial thread may cause problems
+ (example: can not use custom stack size).
+ Solution used by java launcher is to create a new thread to invoke Java.
+ See CR 6316197 for more information.
+*/
+
+extern "C" {
+
+ JNIEXPORT bool start_launcher(int argc, TCHAR* argv[]) {
+ bool result = false;
+ bool parentProcess = true;
+
+ // Platform must be initialize first.
+ Platform& platform = Platform::GetInstance();
+
+ try {
+ for (int index = 0; index < argc; index++) {
+ TString argument = argv[index];
+
+ if (argument == _T("-Xappcds:generatecache")) {
+ platform.SetAppCDSState(cdsGenCache);
+ }
+ else if (argument == _T("-Xappcds:off")) {
+ platform.SetAppCDSState(cdsDisabled);
+ }
+ else if (argument == _T("-Xapp:child")) {
+ parentProcess = false;
+ }
+ }
+
+ // Package must be initialized after Platform is fully initialized.
+ Package& package = Package::GetInstance();
+ Macros::Initialize();
+ package.SetCommandLineArguments(argc, argv);
+ platform.SetCurrentDirectory(package.GetPackageAppDirectory());
+
+ switch (platform.GetAppCDSState()) {
+ case cdsDisabled:
+ case cdsUninitialized:
+ case cdsEnabled: {
+ break;
+ }
+
+ case cdsGenCache: {
+ TString cacheDirectory = package.GetAppCDSCacheDirectory();
+
+ if (FilePath::DirectoryExists(cacheDirectory) == false) {
+ FilePath::CreateDirectory(cacheDirectory, true);
+ } else {
+ TString cacheFileName =
+ package.GetAppCDSCacheFileName();
+ if (FilePath::FileExists(cacheFileName) == true) {
+ FilePath::DeleteFile(cacheFileName);
+ }
+ }
+
+ break;
+ }
+
+ case cdsAuto: {
+ TString cacheFileName = package.GetAppCDSCacheFileName();
+
+ if (parentProcess == true &&
+ FilePath::FileExists(cacheFileName) == false) {
+ AutoFreePtr<Process> process = platform.CreateProcess();
+ std::vector<TString> args;
+ args.push_back(_T("-Xappcds:generatecache"));
+ args.push_back(_T("-Xapp:child"));
+ process->Execute(
+ platform.GetModuleFileName(), args, true);
+
+ if (FilePath::FileExists(cacheFileName) == false) {
+ // Cache does not exist after trying to generate it,
+ // so run without cache.
+ platform.SetAppCDSState(cdsDisabled);
+ package.Clear();
+ package.Initialize();
+ }
+ }
+
+ break;
+ }
+ }
+
+ // Validation
+ switch (platform.GetAppCDSState()) {
+ case cdsDisabled:
+ case cdsGenCache: {
+ // Do nothing.
+ break;
+ }
+
+ case cdsEnabled:
+ case cdsAuto: {
+ TString cacheFileName =
+ package.GetAppCDSCacheFileName();
+
+ if (FilePath::FileExists(cacheFileName) == false) {
+ Messages& messages = Messages::GetInstance();
+ TString message = PlatformString::Format(
+ messages.GetMessage(
+ APPCDS_CACHE_FILE_NOT_FOUND),
+ cacheFileName.data());
+ throw Exception(message);
+ }
+ break;
+ }
+
+ case cdsUninitialized: {
+ platform.ShowMessage(_T("Internal Error"));
+ break;
+ }
+ }
+
+ // Run App
+ result = RunVM();
+ } catch (Exception &e) {
+ platform.ShowMessage(e.GetMessage());
+ }
+
+ return result;
+ }
+
+ JNIEXPORT void stop_launcher() {
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/unix/native/libapplauncher/FileAttribute.h Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 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.
+ */
+
+#ifndef FILEATTRIBUTE_H
+#define FILEATTRIBUTE_H
+
+enum FileAttribute {
+ faBlockSpecial,
+ faCharacterSpecial,
+ faFIFOSpecial,
+ faNormal,
+ faDirectory,
+ faSymbolicLink,
+ faSocket,
+
+ // Owner
+ faReadOnly,
+ faWriteOnly,
+ faReadWrite,
+ faExecute,
+
+ // Group
+ faGroupReadOnly,
+ faGroupWriteOnly,
+ faGroupReadWrite,
+ faGroupExecute,
+
+ // Others
+ faOthersReadOnly,
+ faOthersWriteOnly,
+ faOthersReadWrite,
+ faOthersExecute,
+
+ faHidden
+};
+
+#endif // FILEATTRIBUTE_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/unix/native/libapplauncher/FileAttributes.cpp Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,331 @@
+/*
+ * Copyright (c) 2014, 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.
+ */
+
+#include "FileAttributes.h"
+
+#include <algorithm>
+#include <list>
+#include <sys/stat.h>
+
+FileAttributes::FileAttributes(const TString FileName, bool FollowLink) {
+ FFileName = FileName;
+ FFollowLink = FollowLink;
+ ReadAttributes();
+}
+
+bool FileAttributes::WriteAttributes() {
+ bool result = false;
+
+ mode_t attributes = 0;
+
+ for (std::vector<FileAttribute>::const_iterator iterator =
+ FAttributes.begin();
+ iterator != FAttributes.end(); iterator++) {
+ switch (*iterator) {
+ case faBlockSpecial:
+ {
+ attributes |= S_IFBLK;
+ break;
+ }
+ case faCharacterSpecial:
+ {
+ attributes |= S_IFCHR;
+ break;
+ }
+ case faFIFOSpecial:
+ {
+ attributes |= S_IFIFO;
+ break;
+ }
+ case faNormal:
+ {
+ attributes |= S_IFREG;
+ break;
+ }
+ case faDirectory:
+ {
+ attributes |= S_IFDIR;
+ break;
+ }
+ case faSymbolicLink:
+ {
+ attributes |= S_IFLNK;
+ break;
+ }
+ case faSocket:
+ {
+ attributes |= S_IFSOCK;
+ break;
+ }
+
+ // Owner
+ case faReadOnly:
+ {
+ attributes |= S_IRUSR;
+ break;
+ }
+ case faWriteOnly:
+ {
+ attributes |= S_IWUSR;
+ break;
+ }
+ case faReadWrite:
+ {
+ attributes |= S_IRUSR;
+ attributes |= S_IWUSR;
+ break;
+ }
+ case faExecute:
+ {
+ attributes |= S_IXUSR;
+ break;
+ }
+
+ // Group
+ case faGroupReadOnly:
+ {
+ attributes |= S_IRGRP;
+ break;
+ }
+ case faGroupWriteOnly:
+ {
+ attributes |= S_IWGRP;
+ break;
+ }
+ case faGroupReadWrite:
+ {
+ attributes |= S_IRGRP;
+ attributes |= S_IWGRP;
+ break;
+ }
+ case faGroupExecute:
+ {
+ attributes |= S_IXGRP;
+ break;
+ }
+
+ // Others
+ case faOthersReadOnly:
+ {
+ attributes |= S_IROTH;
+ break;
+ }
+ case faOthersWriteOnly:
+ {
+ attributes |= S_IWOTH;
+ break;
+ }
+ case faOthersReadWrite:
+ {
+ attributes |= S_IROTH;
+ attributes |= S_IWOTH;
+ break;
+ }
+ case faOthersExecute:
+ {
+ attributes |= S_IXOTH;
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ if (chmod(FFileName.data(), attributes) == 0) {
+ result = true;
+ }
+
+ return result;
+}
+
+#define S_ISRUSR(m) (((m) & S_IRWXU) == S_IRUSR)
+#define S_ISWUSR(m) (((m) & S_IRWXU) == S_IWUSR)
+#define S_ISXUSR(m) (((m) & S_IRWXU) == S_IXUSR)
+
+#define S_ISRGRP(m) (((m) & S_IRWXG) == S_IRGRP)
+#define S_ISWGRP(m) (((m) & S_IRWXG) == S_IWGRP)
+#define S_ISXGRP(m) (((m) & S_IRWXG) == S_IXGRP)
+
+#define S_ISROTH(m) (((m) & S_IRWXO) == S_IROTH)
+#define S_ISWOTH(m) (((m) & S_IRWXO) == S_IWOTH)
+#define S_ISXOTH(m) (((m) & S_IRWXO) == S_IXOTH)
+
+bool FileAttributes::ReadAttributes() {
+ bool result = false;
+
+ struct stat status;
+
+ if (stat(StringToFileSystemString(FFileName), &status) == 0) {
+ result = true;
+
+ if (S_ISBLK(status.st_mode) != 0) {
+ FAttributes.push_back(faBlockSpecial);
+ }
+ if (S_ISCHR(status.st_mode) != 0) {
+ FAttributes.push_back(faCharacterSpecial);
+ }
+ if (S_ISFIFO(status.st_mode) != 0) {
+ FAttributes.push_back(faFIFOSpecial);
+ }
+ if (S_ISREG(status.st_mode) != 0) {
+ FAttributes.push_back(faNormal);
+ }
+ if (S_ISDIR(status.st_mode) != 0) {
+ FAttributes.push_back(faDirectory);
+ }
+ if (S_ISLNK(status.st_mode) != 0) {
+ FAttributes.push_back(faSymbolicLink);
+ }
+ if (S_ISSOCK(status.st_mode) != 0) {
+ FAttributes.push_back(faSocket);
+ }
+
+ // Owner
+ if (S_ISRUSR(status.st_mode) != 0) {
+ if (S_ISWUSR(status.st_mode) != 0) {
+ FAttributes.push_back(faReadWrite);
+ } else {
+ FAttributes.push_back(faReadOnly);
+ }
+ } else if (S_ISWUSR(status.st_mode) != 0) {
+ FAttributes.push_back(faWriteOnly);
+ }
+
+ if (S_ISXUSR(status.st_mode) != 0) {
+ FAttributes.push_back(faExecute);
+ }
+
+ // Group
+ if (S_ISRGRP(status.st_mode) != 0) {
+ if (S_ISWGRP(status.st_mode) != 0) {
+ FAttributes.push_back(faGroupReadWrite);
+ } else {
+ FAttributes.push_back(faGroupReadOnly);
+ }
+ } else if (S_ISWGRP(status.st_mode) != 0) {
+ FAttributes.push_back(faGroupWriteOnly);
+ }
+
+ if (S_ISXGRP(status.st_mode) != 0) {
+ FAttributes.push_back(faGroupExecute);
+ }
+
+
+ // Others
+ if (S_ISROTH(status.st_mode) != 0) {
+ if (S_ISWOTH(status.st_mode) != 0) {
+ FAttributes.push_back(faOthersReadWrite);
+ } else {
+ FAttributes.push_back(faOthersReadOnly);
+ }
+ } else if (S_ISWOTH(status.st_mode) != 0) {
+ FAttributes.push_back(faOthersWriteOnly);
+ }
+
+ if (S_ISXOTH(status.st_mode) != 0) {
+ FAttributes.push_back(faOthersExecute);
+ }
+
+ if (FFileName.size() > 0 && FFileName[0] == '.') {
+ FAttributes.push_back(faHidden);
+ }
+ }
+
+ return result;
+}
+
+bool FileAttributes::Valid(const FileAttribute Value) {
+ bool result = false;
+
+ switch (Value) {
+ case faReadWrite:
+ case faWriteOnly:
+ case faExecute:
+
+ case faGroupReadWrite:
+ case faGroupWriteOnly:
+ case faGroupReadOnly:
+ case faGroupExecute:
+
+ case faOthersReadWrite:
+ case faOthersWriteOnly:
+ case faOthersReadOnly:
+ case faOthersExecute:
+
+ case faReadOnly:
+ result = true;
+ break;
+
+ default:
+ break;
+ }
+
+ return result;
+}
+
+void FileAttributes::Append(FileAttribute Value) {
+ if (Valid(Value) == true) {
+ if ((Value == faReadOnly && Contains(faWriteOnly) == true) ||
+ (Value == faWriteOnly && Contains(faReadOnly) == true)) {
+ Value = faReadWrite;
+ }
+
+ FAttributes.push_back(Value);
+ WriteAttributes();
+ }
+}
+
+bool FileAttributes::Contains(FileAttribute Value) {
+ bool result = false;
+
+ std::vector<FileAttribute>::const_iterator iterator =
+ std::find(FAttributes.begin(), FAttributes.end(), Value);
+
+ if (iterator != FAttributes.end()) {
+ result = true;
+ }
+
+ return result;
+}
+
+void FileAttributes::Remove(FileAttribute Value) {
+ if (Valid(Value) == true) {
+ if (Value == faReadOnly && Contains(faReadWrite) == true) {
+ Append(faWriteOnly);
+ Remove(faReadWrite);
+ } else if (Value == faWriteOnly && Contains(faReadWrite) == true) {
+ Append(faReadOnly);
+ Remove(faReadWrite);
+ }
+
+ std::vector<FileAttribute>::iterator iterator =
+ std::find(FAttributes.begin(), FAttributes.end(), Value);
+
+ if (iterator != FAttributes.end()) {
+ FAttributes.erase(iterator);
+ WriteAttributes();
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/unix/native/libapplauncher/FilePath.cpp Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,197 @@
+/*
+ * Copyright (c) 2014, 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.
+ */
+
+#include "PlatformDefs.h"
+#include "FilePath.h"
+
+#include <algorithm>
+#include <list>
+#include <sys/stat.h>
+
+bool FilePath::FileExists(const TString FileName) {
+ bool result = false;
+ struct stat buf;
+
+ if ((stat(StringToFileSystemString(FileName), &buf) == 0) &&
+ (S_ISREG(buf.st_mode) != 0)) {
+ result = true;
+ }
+
+ return result;
+}
+
+bool FilePath::DirectoryExists(const TString DirectoryName) {
+ bool result = false;
+
+ struct stat buf;
+
+ if ((stat(StringToFileSystemString(DirectoryName), &buf) == 0) &&
+ (S_ISDIR(buf.st_mode) != 0)) {
+ result = true;
+ }
+
+ return result;
+}
+
+bool FilePath::DeleteFile(const TString FileName) {
+ bool result = false;
+
+ if (FileExists(FileName) == true) {
+ if (unlink(StringToFileSystemString(FileName)) == 0) {
+ result = true;
+ }
+ }
+
+ return result;
+}
+
+bool FilePath::DeleteDirectory(const TString DirectoryName) {
+ bool result = false;
+
+ if (DirectoryExists(DirectoryName) == true) {
+ if (unlink(StringToFileSystemString(DirectoryName)) == 0) {
+ result = true;
+ }
+ }
+
+ return result;
+}
+
+TString FilePath::IncludeTrailingSeparator(const TString value) {
+ TString result = value;
+
+ if (value.size() > 0) {
+ TString::iterator i = result.end();
+ i--;
+
+ if (*i != TRAILING_PATHSEPARATOR) {
+ result += TRAILING_PATHSEPARATOR;
+ }
+ }
+
+ return result;
+}
+
+TString FilePath::IncludeTrailingSeparator(const char* value) {
+ TString lvalue = PlatformString(value).toString();
+ return IncludeTrailingSeparator(lvalue);
+}
+
+TString FilePath::IncludeTrailingSeparator(const wchar_t* value) {
+ TString lvalue = PlatformString(value).toString();
+ return IncludeTrailingSeparator(lvalue);
+}
+
+TString FilePath::ExtractFilePath(TString Path) {
+ return dirname(StringToFileSystemString(Path));
+}
+
+TString FilePath::ExtractFileExt(TString Path) {
+ TString result;
+ size_t dot = Path.find_last_of('.');
+
+ if (dot != TString::npos) {
+ result = Path.substr(dot, Path.size() - dot);
+ }
+
+ return result;
+}
+
+TString FilePath::ExtractFileName(TString Path) {
+ return basename(StringToFileSystemString(Path));
+}
+
+TString FilePath::ChangeFileExt(TString Path, TString Extension) {
+ TString result;
+ size_t dot = Path.find_last_of('.');
+
+ if (dot != TString::npos) {
+ result = Path.substr(0, dot) + Extension;
+ }
+
+ if (result.empty() == true) {
+ result = Path;
+ }
+
+ return result;
+}
+
+TString FilePath::FixPathForPlatform(TString Path) {
+ TString result = Path;
+ std::replace(result.begin(), result.end(),
+ BAD_TRAILING_PATHSEPARATOR, TRAILING_PATHSEPARATOR);
+ return result;
+}
+
+TString FilePath::FixPathSeparatorForPlatform(TString Path) {
+ TString result = Path;
+ std::replace(result.begin(), result.end(),
+ BAD_PATH_SEPARATOR, PATH_SEPARATOR);
+ return result;
+}
+
+TString FilePath::PathSeparator() {
+ TString result;
+ result = PATH_SEPARATOR;
+ return result;
+}
+
+bool FilePath::CreateDirectory(TString Path, bool ownerOnly) {
+ bool result = false;
+
+ std::list<TString> paths;
+ TString lpath = Path;
+
+ while (lpath.empty() == false && DirectoryExists(lpath) == false) {
+ paths.push_front(lpath);
+ lpath = ExtractFilePath(lpath);
+ }
+
+ for (std::list<TString>::iterator iterator = paths.begin();
+ iterator != paths.end(); iterator++) {
+ lpath = *iterator;
+
+ mode_t mode = S_IRWXU;
+ if (!ownerOnly) {
+ mode |= S_IRWXG | S_IROTH | S_IXOTH;
+ }
+ if (mkdir(StringToFileSystemString(lpath), mode) == 0) {
+ result = true;
+ } else {
+ result = false;
+ break;
+ }
+ }
+
+ return result;
+}
+
+void FilePath::ChangePermissions(TString FileName, bool ownerOnly) {
+ mode_t mode = S_IRWXU;
+ if (!ownerOnly) {
+ mode |= S_IRWXG | S_IROTH | S_IXOTH;
+ }
+ chmod(FileName.data(), mode);
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/unix/native/libapplauncher/PosixPlatform.cpp Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,320 @@
+/*
+ * Copyright (c) 2014, 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.
+ */
+
+#include "PosixPlatform.h"
+
+#include "PlatformString.h"
+#include "FilePath.h"
+#include "Helpers.h"
+
+#include <assert.h>
+#include <stdbool.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <sys/sysctl.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <errno.h>
+#include <limits.h>
+#include <pwd.h>
+#include <iostream>
+#include <algorithm>
+#include <dlfcn.h>
+#include <signal.h>
+
+using namespace std;
+
+PosixPlatform::PosixPlatform(void) {
+}
+
+PosixPlatform::~PosixPlatform(void) {
+}
+
+TString PosixPlatform::GetTempDirectory() {
+ struct passwd* pw = getpwuid(getuid());
+ TString homedir(pw->pw_dir);
+ homedir += getTmpDirString();
+ if (!FilePath::DirectoryExists(homedir)) {
+ if (!FilePath::CreateDirectory(homedir, false)) {
+ homedir.clear();
+ }
+ }
+
+ return homedir;
+}
+
+TString PosixPlatform::fixName(const TString& name) {
+ TString fixedName(name);
+ const TString chars("?:*<>/\\");
+ for (TString::const_iterator it = chars.begin(); it != chars.end(); it++) {
+ fixedName.erase(std::remove(fixedName.begin(),
+ fixedName.end(), *it), fixedName.end());
+ }
+ return fixedName;
+}
+
+MessageResponse PosixPlatform::ShowResponseMessage(TString title,
+ TString description) {
+ MessageResponse result = mrCancel;
+
+ printf("%s %s (Y/N)\n", PlatformString(title).toPlatformString(),
+ PlatformString(description).toPlatformString());
+ fflush(stdout);
+
+ std::string input;
+ std::cin >> input;
+
+ if (input == "Y") {
+ result = mrOK;
+ }
+
+ return result;
+}
+
+void PosixPlatform::SetCurrentDirectory(TString Value) {
+ chdir(StringToFileSystemString(Value));
+}
+
+Module PosixPlatform::LoadLibrary(TString FileName) {
+ return dlopen(StringToFileSystemString(FileName), RTLD_LAZY);
+}
+
+void PosixPlatform::FreeLibrary(Module AModule) {
+ dlclose(AModule);
+}
+
+Procedure PosixPlatform::GetProcAddress(Module AModule,
+ std::string MethodName) {
+ return dlsym(AModule, PlatformString(MethodName));
+}
+
+Process* PosixPlatform::CreateProcess() {
+ return new PosixProcess();
+}
+
+void PosixPlatform::addPlatformDependencies(JavaLibrary *pJavaLibrary) {
+}
+
+void Platform::CopyString(char *Destination,
+ size_t NumberOfElements, const char *Source) {
+ strncpy(Destination, Source, NumberOfElements);
+
+ if (NumberOfElements > 0) {
+ Destination[NumberOfElements - 1] = '\0';
+ }
+}
+
+void Platform::CopyString(wchar_t *Destination,
+ size_t NumberOfElements, const wchar_t *Source) {
+ wcsncpy(Destination, Source, NumberOfElements);
+
+ if (NumberOfElements > 0) {
+ Destination[NumberOfElements - 1] = '\0';
+ }
+}
+
+// Owner must free the return value.
+
+MultibyteString Platform::WideStringToMultibyteString(
+ const wchar_t* value) {
+ MultibyteString result;
+ size_t count = 0;
+
+ if (value == NULL) {
+ return result;
+ }
+
+ count = wcstombs(NULL, value, 0);
+ if (count > 0) {
+ result.data = new char[count + 1];
+ result.data[count] = '\0';
+ result.length = count;
+ wcstombs(result.data, value, count);
+ }
+
+ return result;
+}
+
+// Owner must free the return value.
+
+WideString Platform::MultibyteStringToWideString(const char* value) {
+ WideString result;
+ size_t count = 0;
+
+ if (value == NULL) {
+ return result;
+ }
+
+ count = mbstowcs(NULL, value, 0);
+ if (count > 0) {
+ result.data = new wchar_t[count + 1];
+ result.data[count] = '\0';
+ result.length = count;
+ mbstowcs(result.data, value, count);
+ }
+
+ return result;
+}
+
+void PosixPlatform::InitStreamLocale(wios *stream) {
+ // Nothing to do for POSIX platforms.
+}
+
+PosixProcess::PosixProcess() : Process() {
+ FChildPID = 0;
+ FRunning = false;
+ FOutputHandle = 0;
+ FInputHandle = 0;
+}
+
+PosixProcess::~PosixProcess() {
+ Terminate();
+}
+
+bool PosixProcess::ReadOutput() {
+ bool result = false;
+
+ if (FOutputHandle != 0 && IsRunning() == true) {
+ char buffer[4096] = {0};
+
+ ssize_t count = read(FOutputHandle, buffer, sizeof (buffer));
+
+ if (count == -1) {
+ if (errno == EINTR) {
+ // continue;
+ } else {
+ perror("read");
+ exit(1);
+ }
+ } else if (count == 0) {
+ // break;
+ } else {
+ if (buffer[count - 1] == EOF) {
+ buffer[count - 1] = '\0';
+ }
+
+ std::list<TString> output = Helpers::StringToArray(buffer);
+ FOutput.splice(FOutput.end(), output, output.begin(), output.end());
+ result = true;
+ }
+ }
+
+ return false;
+}
+
+bool PosixProcess::IsRunning() {
+ bool result = false;
+
+ if (kill(FChildPID, 0) == 0) {
+ result = true;
+ }
+
+ return result;
+}
+
+bool PosixProcess::Terminate() {
+ bool result = false;
+
+ if (IsRunning() == true && FRunning == true) {
+ FRunning = false;
+ Cleanup();
+ int status = kill(FChildPID, SIGTERM);
+
+ if (status == 0) {
+ result = true;
+ } else {
+#ifdef DEBUG
+ if (errno == EINVAL) {
+ printf("Kill error: The value of the sig argument is an invalid or unsupported signal number.");
+ } else if (errno == EPERM) {
+ printf("Kill error: The process does not have permission to send the signal to any receiving process.");
+ } else if (errno == ESRCH) {
+ printf("Kill error: No process or process group can be found corresponding to that specified by pid.");
+ }
+#endif // DEBUG
+ if (IsRunning() == true) {
+ status = kill(FChildPID, SIGKILL);
+
+ if (status == 0) {
+ result = true;
+ }
+ }
+ }
+ }
+
+ return result;
+}
+
+bool PosixProcess::Wait() {
+ bool result = false;
+
+ int status = 0;
+ pid_t wpid = 0;
+
+ wpid = wait(&status);
+ if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
+ if (errno != EINTR) {
+ status = -1;
+ }
+ }
+
+#ifdef DEBUG
+ if (WIFEXITED(status)) {
+ printf("child exited, status=%d\n", WEXITSTATUS(status));
+ } else if (WIFSIGNALED(status)) {
+ printf("child killed (signal %d)\n", WTERMSIG(status));
+ } else if (WIFSTOPPED(status)) {
+ printf("child stopped (signal %d)\n", WSTOPSIG(status));
+#ifdef WIFCONTINUED // Not all implementations support this
+ } else if (WIFCONTINUED(status)) {
+ printf("child continued\n");
+#endif // WIFCONTINUED
+ } else { // Non-standard case -- may never happen
+ printf("Unexpected status (0x%x)\n", status);
+ }
+#endif // DEBUG
+
+ if (wpid != -1) {
+ result = true;
+ }
+
+ return result;
+}
+
+TProcessID PosixProcess::GetProcessID() {
+ return FChildPID;
+}
+
+void PosixProcess::SetInput(TString Value) {
+ if (FInputHandle != 0) {
+ write(FInputHandle, Value.data(), Value.size());
+ }
+}
+
+std::list<TString> PosixProcess::GetOutput() {
+ ReadOutput();
+ return Process::GetOutput();
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/unix/native/libapplauncher/PosixPlatform.h Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2014, 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.
+ */
+
+#ifndef POSIXPLATFORM_H
+#define POSIXPLATFORM_H
+
+#include "Platform.h"
+#include <signal.h>
+
+class PosixPlatform : virtual public Platform {
+protected:
+
+ TString fixName(const TString& name);
+
+ virtual TString getTmpDirString() = 0;
+
+public:
+ PosixPlatform(void);
+ virtual ~PosixPlatform(void);
+
+public:
+ virtual MessageResponse ShowResponseMessage(TString title,
+ TString description);
+
+ virtual void SetCurrentDirectory(TString Value);
+
+ virtual Module LoadLibrary(TString FileName);
+ virtual void FreeLibrary(Module AModule);
+ virtual Procedure GetProcAddress(Module AModule, std::string MethodName);
+
+ virtual Process* CreateProcess();
+ virtual TString GetTempDirectory();
+ void InitStreamLocale(wios *stream);
+ void addPlatformDependencies(JavaLibrary *pJavaLibrary);
+};
+
+class PosixProcess : public Process {
+private:
+ pid_t FChildPID;
+ sigset_t saveblock;
+ int FOutputHandle;
+ int FInputHandle;
+ struct sigaction savintr, savequit;
+ bool FRunning;
+
+ void Cleanup();
+ bool ReadOutput();
+
+public:
+ PosixProcess();
+ virtual ~PosixProcess();
+
+ virtual bool IsRunning();
+ virtual bool Terminate();
+ virtual bool Execute(const TString Application,
+ const std::vector<TString> Arguments, bool AWait = false);
+ virtual bool Wait();
+ virtual TProcessID GetProcessID();
+ virtual void SetInput(TString Value);
+ virtual std::list<TString> GetOutput();
+};
+
+#endif // POSIXPLATFORM_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/VersionExtractor.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 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.jpackage.internal;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+import java.text.MessageFormat;
+import java.util.ResourceBundle;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class VersionExtractor extends PrintStream {
+
+ private static final ResourceBundle I18N = ResourceBundle.getBundle(
+ "jdk.jpackage.internal.resources.WinResources");
+
+ private final String pattern;
+ private String version = null;
+
+ public VersionExtractor(String pattern) {
+ super(new ByteArrayOutputStream());
+
+ this.pattern = pattern;
+ }
+
+ public String getVersion() {
+ if (version == null) {
+ String content
+ = new String(((ByteArrayOutputStream) out).toByteArray());
+ Pattern p = Pattern.compile(pattern);
+ Matcher matcher = p.matcher(content);
+ if (matcher.find()) {
+ version = matcher.group(1);
+ }
+ }
+ return version;
+ }
+
+ public static boolean isLessThan(String version, String compareTo)
+ throws RuntimeException {
+ if (version == null || version.isEmpty()) {
+ throw new RuntimeException(MessageFormat.format(
+ I18N.getString("error.version-compare"),
+ version, compareTo));
+ }
+
+ if (compareTo == null || compareTo.isEmpty()) {
+ throw new RuntimeException(MessageFormat.format(
+ I18N.getString("error.version-compare"),
+ version, compareTo));
+ }
+
+ String [] versionArray = version.trim().split(Pattern.quote("."));
+ String [] compareToArray = compareTo.trim().split(Pattern.quote("."));
+
+ for (int i = 0; i < versionArray.length; i++) {
+ int v1 = Integer.parseInt(versionArray[i]);
+ int v2 = Integer.parseInt(compareToArray[i]);
+ if (v1 < v2) {
+ return true;
+ } else if (v1 > v2) {
+ return false;
+ }
+ }
+
+ return false;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinAppBundler.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,222 @@
+/*
+ * 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.jpackage.internal;
+
+import java.io.File;
+import java.nio.file.Path;
+import java.text.MessageFormat;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Map;
+import java.util.ResourceBundle;
+
+import static jdk.jpackage.internal.WindowsBundlerParam.*;
+import static jdk.jpackage.internal.WinMsiBundler.WIN_APP_IMAGE;
+
+public class WinAppBundler extends AbstractImageBundler {
+
+ private static final ResourceBundle I18N = ResourceBundle.getBundle(
+ "jdk.jpackage.internal.resources.WinResources");
+
+ static final BundlerParamInfo<File> ICON_ICO =
+ new StandardBundlerParam<>(
+ "icon.ico",
+ File.class,
+ params -> {
+ File f = ICON.fetchFrom(params);
+ if (f != null && !f.getName().toLowerCase().endsWith(".ico")) {
+ Log.error(MessageFormat.format(
+ I18N.getString("message.icon-not-ico"), f));
+ return null;
+ }
+ return f;
+ },
+ (s, p) -> new File(s));
+
+ @Override
+ public boolean validate(Map<String, ? super Object> params)
+ throws ConfigException {
+ try {
+ if (params == null) throw new ConfigException(
+ I18N.getString("error.parameters-null"),
+ I18N.getString("error.parameters-null.advice"));
+
+ return doValidate(params);
+ } catch (RuntimeException re) {
+ if (re.getCause() instanceof ConfigException) {
+ throw (ConfigException) re.getCause();
+ } else {
+ throw new ConfigException(re);
+ }
+ }
+ }
+
+ // to be used by chained bundlers, e.g. by EXE bundler to avoid
+ // skipping validation if p.type does not include "image"
+ private boolean doValidate(Map<String, ? super Object> p)
+ throws ConfigException {
+
+ imageBundleValidation(p);
+
+ if (StandardBundlerParam.getPredefinedAppImage(p) != null) {
+ return true;
+ }
+
+ // Make sure that jpackage.exe exists.
+ File tool = new File(
+ System.getProperty("java.home") + "\\bin\\jpackage.exe");
+
+ if (!tool.exists()) {
+ throw new ConfigException(
+ I18N.getString("error.no-windows-resources"),
+ I18N.getString("error.no-windows-resources.advice"));
+ }
+
+ return true;
+ }
+
+ private static boolean usePredefineAppName(Map<String, ? super Object> p) {
+ return (PREDEFINED_APP_IMAGE.fetchFrom(p) != null);
+ }
+
+ private static String appName;
+ synchronized static String getAppName(
+ Map<String, ? super Object> p) {
+ // If we building from predefined app image, then we should use names
+ // from image and not from CLI.
+ if (usePredefineAppName(p)) {
+ if (appName == null) {
+ // Use WIN_APP_IMAGE here, since we already copy pre-defined
+ // image to WIN_APP_IMAGE
+ File appImageDir = WIN_APP_IMAGE.fetchFrom(p);
+
+ File appDir = new File(appImageDir.toString() + "\\app");
+ File [] files = appDir.listFiles(
+ (File dir, String name) -> name.endsWith(".cfg"));
+ if (files == null || files.length == 0) {
+ String name = APP_NAME.fetchFrom(p);
+ Path exePath = appImageDir.toPath().resolve(name + ".exe");
+ Path icoPath = appImageDir.toPath().resolve(name + ".ico");
+ if (exePath.toFile().exists() &&
+ icoPath.toFile().exists()) {
+ return name;
+ } else {
+ throw new RuntimeException(MessageFormat.format(
+ I18N.getString("error.cannot-find-launcher"),
+ appImageDir));
+ }
+ } else {
+ appName = files[0].getName();
+ int index = appName.indexOf(".");
+ if (index != -1) {
+ appName = appName.substring(0, index);
+ }
+ if (files.length > 1) {
+ Log.error(MessageFormat.format(I18N.getString(
+ "message.multiple-launchers"), appName));
+ }
+ }
+ return appName;
+ } else {
+ return appName;
+ }
+ }
+
+ return APP_NAME.fetchFrom(p);
+ }
+
+ public static String getLauncherRelativePath(
+ Map<String, ? super Object> p) {
+ return "bin" + File.separator + getAppName(p) + ".exe";
+ }
+
+ public boolean bundle(Map<String, ? super Object> p, File outputDirectory)
+ throws PackagerException {
+ return doBundle(p, outputDirectory, false) != null;
+ }
+
+ File doBundle(Map<String, ? super Object> p, File outputDirectory,
+ boolean dependentTask) throws PackagerException {
+ if (StandardBundlerParam.isRuntimeInstaller(p)) {
+ return PREDEFINED_RUNTIME_IMAGE.fetchFrom(p);
+ } else {
+ return doAppBundle(p, outputDirectory, dependentTask);
+ }
+ }
+
+ File doAppBundle(Map<String, ? super Object> p, File outputDirectory,
+ boolean dependentTask) throws PackagerException {
+ try {
+ File rootDirectory = createRoot(p, outputDirectory, dependentTask,
+ APP_NAME.fetchFrom(p));
+ AbstractAppImageBuilder appBuilder =
+ new WindowsAppImageBuilder(p, outputDirectory.toPath());
+ if (PREDEFINED_RUNTIME_IMAGE.fetchFrom(p) == null ) {
+ JLinkBundlerHelper.execute(p, appBuilder);
+ } else {
+ StandardBundlerParam.copyPredefinedRuntimeImage(p, appBuilder);
+ }
+ if (!dependentTask) {
+ Log.verbose(MessageFormat.format(
+ I18N.getString("message.result-dir"),
+ outputDirectory.getAbsolutePath()));
+ }
+ return rootDirectory;
+ } catch (PackagerException pe) {
+ throw pe;
+ } catch (Exception e) {
+ Log.verbose(e);
+ throw new PackagerException(e);
+ }
+ }
+
+ @Override
+ public String getName() {
+ return I18N.getString("app.bundler.name");
+ }
+
+ @Override
+ public String getID() {
+ return "windows.app";
+ }
+
+ @Override
+ public String getBundleType() {
+ return "IMAGE";
+ }
+
+ @Override
+ public File execute(Map<String, ? super Object> params,
+ File outputParentDir) throws PackagerException {
+ return doBundle(params, outputParentDir, false);
+ }
+
+ @Override
+ public boolean supported(boolean platformInstaller) {
+ return true;
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinExeBundler.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,149 @@
+/*
+ * Copyright (c) 2017, 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.jpackage.internal;
+
+import java.io.*;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.text.MessageFormat;
+import java.util.*;
+
+public class WinExeBundler extends AbstractBundler {
+
+ static {
+ System.loadLibrary("jpackage");
+ }
+
+ private static final ResourceBundle I18N = ResourceBundle.getBundle(
+ "jdk.jpackage.internal.resources.WinResources");
+
+ public static final BundlerParamInfo<WinAppBundler> APP_BUNDLER
+ = new WindowsBundlerParam<>(
+ "win.app.bundler",
+ WinAppBundler.class,
+ params -> new WinAppBundler(),
+ null);
+
+ public static final BundlerParamInfo<File> EXE_IMAGE_DIR
+ = new WindowsBundlerParam<>(
+ "win.exe.imageDir",
+ File.class,
+ params -> {
+ File imagesRoot = IMAGES_ROOT.fetchFrom(params);
+ if (!imagesRoot.exists()) {
+ imagesRoot.mkdirs();
+ }
+ return new File(imagesRoot, "win-exe.image");
+ },
+ (s, p) -> null);
+
+ private final static String EXE_WRAPPER_NAME = "msiwrapper.exe";
+
+ @Override
+ public String getName() {
+ return getString("exe.bundler.name");
+ }
+
+ @Override
+ public String getID() {
+ return "exe";
+ }
+
+ @Override
+ public String getBundleType() {
+ return "INSTALLER";
+ }
+
+ @Override
+ public File execute(Map<String, ? super Object> params,
+ File outputParentDir) throws PackagerException {
+ return bundle(params, outputParentDir);
+ }
+
+ @Override
+ public boolean supported(boolean platformInstaller) {
+ return WinMsiBundler.isSupported();
+ }
+
+ @Override
+ public boolean validate(Map<String, ? super Object> params)
+ throws ConfigException {
+ return new WinMsiBundler().validate(params);
+ }
+
+ public File bundle(Map<String, ? super Object> params, File outdir)
+ throws PackagerException {
+
+ IOUtils.writableOutputDir(outdir.toPath());
+
+ File exeImageDir = EXE_IMAGE_DIR.fetchFrom(params);
+
+ // Write msi to temporary directory.
+ File msi = new WinMsiBundler().bundle(params, exeImageDir);
+
+ try {
+ return buildEXE(msi, outdir);
+ } catch (IOException ex) {
+ Log.verbose(ex);
+ throw new PackagerException(ex);
+ }
+ }
+
+ private File buildEXE(File msi, File outdir)
+ throws IOException {
+
+ Log.verbose(MessageFormat.format(
+ getString("message.outputting-to-location"),
+ outdir.getAbsolutePath()));
+
+ // Copy template msi wrapper next to msi file
+ String exePath = msi.getAbsolutePath();
+ exePath = exePath.substring(0, exePath.lastIndexOf('.')) + ".exe";
+ try (InputStream is = getResourceAsStream(EXE_WRAPPER_NAME)) {
+ Files.copy(is, Path.of(exePath));
+ }
+ // Embed msi in msi wrapper exe.
+ embedMSI(exePath, msi.getAbsolutePath());
+
+ Path dstExePath = Paths.get(outdir.getAbsolutePath(), Path.of(exePath).getFileName().toString());
+ Files.deleteIfExists(dstExePath);
+
+ Files.copy(Path.of(exePath), dstExePath);
+
+ Log.verbose(MessageFormat.format(
+ getString("message.output-location"),
+ outdir.getAbsolutePath()));
+
+ return dstExePath.toFile();
+ }
+
+ private static String getString(String key)
+ throws MissingResourceException {
+ return I18N.getString(key);
+ }
+
+ private static native int embedMSI(String exePath, String msiPath);
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinMsiBundler.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,1147 @@
+/*
+ * 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.jpackage.internal;
+
+import java.io.*;
+import java.nio.charset.Charset;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.text.MessageFormat;
+import java.util.*;
+import java.util.regex.Pattern;
+import javax.xml.stream.XMLOutputFactory;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamWriter;
+
+import static jdk.jpackage.internal.WindowsBundlerParam.*;
+
+/**
+ * WinMsiBundler
+ *
+ * Produces .msi installer from application image. Uses WiX Toolkit to build
+ * .msi installer.
+ * <p>
+ * {@link #execute} method creates a number of source files with the description
+ * of installer to be processed by WiX tools. Generated source files are stored
+ * in "config" subdirectory next to "app" subdirectory in the root work
+ * directory. The following WiX source files are generated:
+ * <ul>
+ * <li>main.wxs. Main source file with the installer description
+ * <li>bundle.wxi. Source file with application and Java run-time directory tree
+ * description. This source file is included from main.wxs
+ * <li>icons.wxi. Source file with the list of icons used by the application.
+ * This source file is included from main.wxs
+ * </ul>
+ * <p>
+ * main.wxs file is a copy of main.wxs resource from
+ * jdk.jpackage.internal.resources package. It is parametrized with the
+ * following WiX variables:
+ * <ul>
+ * <li>JpAppName. Name of the application. Set to the value of --name command
+ * line option
+ * <li>JpAppVersion. Version of the application. Set to the value of
+ * --app-version command line option
+ * <li>JpAppVendor. Vendor of the application. Set to the value of --vendor
+ * command line option
+ * <li>JpAppDescription. Description of the application. Set to the value of
+ * --description command line option
+ * <li>JpProductCode. Set to product code UUID of the application. Random value
+ * generated by jpackage every time {@link #execute} method is called
+ * <li>JpProductUpgradeCode. Set to upgrade code UUID of the application. Random
+ * value generated by jpackage every time {@link #execute} method is called if
+ * --win-upgrade-uuid command line option is not specified. Otherwise this
+ * variable is set to the value of --win-upgrade-uuid command line option
+ * <li>JpAllowDowngrades. Set to "yes" if --win-upgrade-uuid command line option
+ * was specified. Undefined otherwise
+ * <li>JpLicenseRtf. Set to the value of --license-file command line option.
+ * Undefined is --license-file command line option was not specified
+ * <li>JpInstallDirChooser. Set to "yes" if --win-dir-chooser command line
+ * option was specified. Undefined otherwise
+ * <li>JpConfigDir. Absolute path to the directory with generated WiX source
+ * files.
+ * <li>JpIsSystemWide. Set to "yes" if --win-per-user-install command line
+ * option was not specified. Undefined otherwise
+ * <li>JpWixVersion36OrNewer. Set to "yes" if WiX Toolkit v3.6 or newer is used.
+ * Undefined otherwise
+ * </ul>
+ */
+public class WinMsiBundler extends AbstractBundler {
+
+ private static final ResourceBundle I18N = ResourceBundle.getBundle(
+ "jdk.jpackage.internal.resources.WinResources");
+
+ public static final BundlerParamInfo<WinAppBundler> APP_BUNDLER =
+ new WindowsBundlerParam<>(
+ "win.app.bundler",
+ WinAppBundler.class,
+ params -> new WinAppBundler(),
+ null);
+
+ public static final BundlerParamInfo<Boolean> CAN_USE_WIX36 =
+ new WindowsBundlerParam<>(
+ "win.msi.canUseWix36",
+ Boolean.class,
+ params -> false,
+ (s, p) -> Boolean.valueOf(s));
+
+ public static final BundlerParamInfo<File> MSI_IMAGE_DIR =
+ new WindowsBundlerParam<>(
+ "win.msi.imageDir",
+ File.class,
+ params -> {
+ File imagesRoot = IMAGES_ROOT.fetchFrom(params);
+ if (!imagesRoot.exists()) imagesRoot.mkdirs();
+ return new File(imagesRoot, "win-msi.image");
+ },
+ (s, p) -> null);
+
+ public static final BundlerParamInfo<File> WIN_APP_IMAGE =
+ new WindowsBundlerParam<>(
+ "win.app.image",
+ File.class,
+ null,
+ (s, p) -> null);
+
+ public static final StandardBundlerParam<Boolean> MSI_SYSTEM_WIDE =
+ new StandardBundlerParam<>(
+ Arguments.CLIOptions.WIN_PER_USER_INSTALLATION.getId(),
+ Boolean.class,
+ params -> true, // MSIs default to system wide
+ // valueOf(null) is false,
+ // and we actually do want null
+ (s, p) -> (s == null || "null".equalsIgnoreCase(s))? null
+ : Boolean.valueOf(s)
+ );
+
+
+ public static final StandardBundlerParam<String> PRODUCT_VERSION =
+ new StandardBundlerParam<>(
+ "win.msi.productVersion",
+ String.class,
+ VERSION::fetchFrom,
+ (s, p) -> s
+ );
+
+ public static final BundlerParamInfo<UUID> UPGRADE_UUID =
+ new WindowsBundlerParam<>(
+ Arguments.CLIOptions.WIN_UPGRADE_UUID.getId(),
+ UUID.class,
+ params -> UUID.randomUUID(),
+ (s, p) -> UUID.fromString(s));
+
+ private static final String TOOL_CANDLE = "candle.exe";
+ private static final String TOOL_LIGHT = "light.exe";
+ // autodetect just v3.7, v3.8, 3.9, 3.10 and 3.11
+ private static final String AUTODETECT_DIRS =
+ ";C:\\Program Files (x86)\\WiX Toolset v3.11\\bin;"
+ + "C:\\Program Files\\WiX Toolset v3.11\\bin;"
+ + "C:\\Program Files (x86)\\WiX Toolset v3.10\\bin;"
+ + "C:\\Program Files\\WiX Toolset v3.10\\bin;"
+ + "C:\\Program Files (x86)\\WiX Toolset v3.9\\bin;"
+ + "C:\\Program Files\\WiX Toolset v3.9\\bin;"
+ + "C:\\Program Files (x86)\\WiX Toolset v3.8\\bin;"
+ + "C:\\Program Files\\WiX Toolset v3.8\\bin;"
+ + "C:\\Program Files (x86)\\WiX Toolset v3.7\\bin;"
+ + "C:\\Program Files\\WiX Toolset v3.7\\bin";
+
+ private static String getCandlePath() {
+ for (String dirString : (System.getenv("PATH")
+ + AUTODETECT_DIRS).split(";")) {
+ File f = new File(dirString.replace("\"", ""), TOOL_CANDLE);
+ if (f.isFile()) {
+ return f.toString();
+ }
+ }
+ return null;
+ }
+
+ private static String getLightPath() {
+ for (String dirString : (System.getenv("PATH")
+ + AUTODETECT_DIRS).split(";")) {
+ File f = new File(dirString.replace("\"", ""), TOOL_LIGHT);
+ if (f.isFile()) {
+ return f.toString();
+ }
+ }
+ return null;
+ }
+
+
+ public static final StandardBundlerParam<Boolean> MENU_HINT =
+ new WindowsBundlerParam<>(
+ Arguments.CLIOptions.WIN_MENU_HINT.getId(),
+ Boolean.class,
+ params -> false,
+ // valueOf(null) is false,
+ // and we actually do want null in some cases
+ (s, p) -> (s == null ||
+ "null".equalsIgnoreCase(s))? true : Boolean.valueOf(s)
+ );
+
+ public static final StandardBundlerParam<Boolean> SHORTCUT_HINT =
+ new WindowsBundlerParam<>(
+ Arguments.CLIOptions.WIN_SHORTCUT_HINT.getId(),
+ Boolean.class,
+ params -> false,
+ // valueOf(null) is false,
+ // and we actually do want null in some cases
+ (s, p) -> (s == null ||
+ "null".equalsIgnoreCase(s))? false : Boolean.valueOf(s)
+ );
+
+ @Override
+ public String getName() {
+ return I18N.getString("msi.bundler.name");
+ }
+
+ @Override
+ public String getID() {
+ return "msi";
+ }
+
+ @Override
+ public String getBundleType() {
+ return "INSTALLER";
+ }
+
+ @Override
+ public File execute(Map<String, ? super Object> params,
+ File outputParentDir) throws PackagerException {
+ return bundle(params, outputParentDir);
+ }
+
+ @Override
+ public boolean supported(boolean platformInstaller) {
+ return isSupported();
+ }
+
+ public static boolean isSupported() {
+ try {
+ return validateWixTools();
+ } catch (Exception e) {
+ return false;
+ }
+ }
+
+ private static String findToolVersion(String toolName) {
+ try {
+ if (toolName == null || "".equals(toolName)) return null;
+
+ ProcessBuilder pb = new ProcessBuilder(
+ toolName,
+ "/?");
+ VersionExtractor ve = new VersionExtractor("version (\\d+.\\d+)");
+ // not interested in the output
+ IOUtils.exec(pb, true, ve);
+ String version = ve.getVersion();
+ Log.verbose(MessageFormat.format(
+ I18N.getString("message.tool-version"),
+ toolName, version));
+ return version;
+ } catch (Exception e) {
+ if (Log.isDebug()) {
+ Log.verbose(e);
+ }
+ return null;
+ }
+ }
+
+ public static boolean validateWixTools() {
+ String candleVersion = findToolVersion(getCandlePath());
+ String lightVersion = findToolVersion(getLightPath());
+
+ // WiX 3.0+ is required
+ String minVersion = "3.0";
+
+ if (VersionExtractor.isLessThan(candleVersion, minVersion)) {
+ Log.verbose(MessageFormat.format(
+ I18N.getString("message.wrong-tool-version"),
+ TOOL_CANDLE, candleVersion, minVersion));
+ return false;
+ }
+ if (VersionExtractor.isLessThan(lightVersion, minVersion)) {
+ Log.verbose(MessageFormat.format(
+ I18N.getString("message.wrong-tool-version"),
+ TOOL_LIGHT, lightVersion, minVersion));
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public boolean validate(Map<String, ? super Object> params)
+ throws ConfigException {
+ try {
+ if (params == null) throw new ConfigException(
+ I18N.getString("error.parameters-null"),
+ I18N.getString("error.parameters-null.advice"));
+
+ // run basic validation to ensure requirements are met
+ // we are not interested in return code, only possible exception
+ if (!validateWixTools()){
+ throw new ConfigException(
+ I18N.getString("error.no-wix-tools"),
+ I18N.getString("error.no-wix-tools.advice"));
+ }
+
+ String lightVersion = findToolVersion(getLightPath());
+ if (!VersionExtractor.isLessThan(lightVersion, "3.6")) {
+ Log.verbose(I18N.getString("message.use-wix36-features"));
+ params.put(CAN_USE_WIX36.getID(), Boolean.TRUE);
+ }
+
+ /********* validate bundle parameters *************/
+
+ String version = PRODUCT_VERSION.fetchFrom(params);
+ if (!isVersionStringValid(version)) {
+ throw new ConfigException(
+ MessageFormat.format(I18N.getString(
+ "error.version-string-wrong-format"), version),
+ MessageFormat.format(I18N.getString(
+ "error.version-string-wrong-format.advice"),
+ PRODUCT_VERSION.getID()));
+ }
+
+ // only one mime type per association, at least one file extension
+ List<Map<String, ? super Object>> associations =
+ FILE_ASSOCIATIONS.fetchFrom(params);
+ if (associations != null) {
+ for (int i = 0; i < associations.size(); i++) {
+ Map<String, ? super Object> assoc = associations.get(i);
+ List<String> mimes = FA_CONTENT_TYPE.fetchFrom(assoc);
+ if (mimes.size() > 1) {
+ throw new ConfigException(MessageFormat.format(
+ I18N.getString("error.too-many-content-"
+ + "types-for-file-association"), i),
+ I18N.getString("error.too-many-content-"
+ + "types-for-file-association.advice"));
+ }
+ }
+ }
+
+ return true;
+ } catch (RuntimeException re) {
+ if (re.getCause() instanceof ConfigException) {
+ throw (ConfigException) re.getCause();
+ } else {
+ throw new ConfigException(re);
+ }
+ }
+ }
+
+ // http://msdn.microsoft.com/en-us/library/aa370859%28v=VS.85%29.aspx
+ // The format of the string is as follows:
+ // major.minor.build
+ // The first field is the major version and has a maximum value of 255.
+ // The second field is the minor version and has a maximum value of 255.
+ // The third field is called the build version or the update version and
+ // has a maximum value of 65,535.
+ static boolean isVersionStringValid(String v) {
+ if (v == null) {
+ return true;
+ }
+
+ String p[] = v.split("\\.");
+ if (p.length > 3) {
+ Log.verbose(I18N.getString(
+ "message.version-string-too-many-components"));
+ return false;
+ }
+
+ try {
+ int val = Integer.parseInt(p[0]);
+ if (val < 0 || val > 255) {
+ Log.verbose(I18N.getString(
+ "error.version-string-major-out-of-range"));
+ return false;
+ }
+ if (p.length > 1) {
+ val = Integer.parseInt(p[1]);
+ if (val < 0 || val > 255) {
+ Log.verbose(I18N.getString(
+ "error.version-string-minor-out-of-range"));
+ return false;
+ }
+ }
+ if (p.length > 2) {
+ val = Integer.parseInt(p[2]);
+ if (val < 0 || val > 65535) {
+ Log.verbose(I18N.getString(
+ "error.version-string-build-out-of-range"));
+ return false;
+ }
+ }
+ } catch (NumberFormatException ne) {
+ Log.verbose(I18N.getString("error.version-string-part-not-number"));
+ Log.verbose(ne);
+ return false;
+ }
+
+ return true;
+ }
+
+ private boolean prepareProto(Map<String, ? super Object> params)
+ throws PackagerException, IOException {
+ File appImage = StandardBundlerParam.getPredefinedAppImage(params);
+ File appDir = null;
+
+ // we either have an application image or need to build one
+ if (appImage != null) {
+ appDir = new File(MSI_IMAGE_DIR.fetchFrom(params),
+ APP_NAME.fetchFrom(params));
+ // copy everything from appImage dir into appDir/name
+ IOUtils.copyRecursive(appImage.toPath(), appDir.toPath());
+ } else {
+ appDir = APP_BUNDLER.fetchFrom(params).doBundle(params,
+ MSI_IMAGE_DIR.fetchFrom(params), true);
+ }
+
+ params.put(WIN_APP_IMAGE.getID(), appDir);
+
+ String licenseFile = LICENSE_FILE.fetchFrom(params);
+ if (licenseFile != null) {
+ // need to copy license file to the working directory and convert to rtf if needed
+ File lfile = new File(licenseFile);
+ File destFile = new File(CONFIG_ROOT.fetchFrom(params),
+ lfile.getName());
+
+ IOUtils.copyFile(lfile, destFile);
+ destFile.setWritable(true);
+ ensureByMutationFileIsRTF(destFile);
+ }
+
+ // copy file association icons
+ List<Map<String, ? super Object>> fileAssociations =
+ FILE_ASSOCIATIONS.fetchFrom(params);
+ for (Map<String, ? super Object> fa : fileAssociations) {
+ File icon = FA_ICON.fetchFrom(fa);
+ if (icon == null) {
+ continue;
+ }
+
+ File faIconFile = new File(appDir, icon.getName());
+
+ if (icon.exists()) {
+ try {
+ IOUtils.copyFile(icon, faIconFile);
+ } catch (IOException e) {
+ Log.verbose(e);
+ }
+ }
+ }
+
+ return appDir != null;
+ }
+
+ public File bundle(Map<String, ? super Object> params, File outdir)
+ throws PackagerException {
+
+ IOUtils.writableOutputDir(outdir.toPath());
+
+ // validate we have valid tools before continuing
+ String light = getLightPath();
+ String candle = getCandlePath();
+ if (light == null || !new File(light).isFile() ||
+ candle == null || !new File(candle).isFile()) {
+ Log.verbose(MessageFormat.format(
+ I18N.getString("message.light-file-string"), light));
+ Log.verbose(MessageFormat.format(
+ I18N.getString("message.candle-file-string"), candle));
+ throw new PackagerException("error.no-wix-tools");
+ }
+
+ Map<String, String> wixVars = null;
+
+ File imageDir = MSI_IMAGE_DIR.fetchFrom(params);
+ try {
+ imageDir.mkdirs();
+
+ boolean menuShortcut = MENU_HINT.fetchFrom(params);
+ boolean desktopShortcut = SHORTCUT_HINT.fetchFrom(params);
+ if (!menuShortcut && !desktopShortcut) {
+ // both can not be false - user will not find the app
+ Log.verbose(I18N.getString("message.one-shortcut-required"));
+ params.put(MENU_HINT.getID(), true);
+ }
+
+ prepareBasicProjectConfig(params);
+ if (prepareProto(params)) {
+ wixVars = prepareWiXConfig(params);
+
+ File configScriptSrc = getConfig_Script(params);
+ if (configScriptSrc.exists()) {
+ // we need to be running post script in the image folder
+
+ // NOTE: Would it be better to generate it to the image
+ // folder and save only if "verbose" is requested?
+
+ // for now we replicate it
+ File configScript =
+ new File(imageDir, configScriptSrc.getName());
+ IOUtils.copyFile(configScriptSrc, configScript);
+ Log.verbose(MessageFormat.format(
+ I18N.getString("message.running-wsh-script"),
+ configScript.getAbsolutePath()));
+ IOUtils.run("wscript", configScript);
+ }
+ return buildMSI(params, wixVars, outdir);
+ }
+ return null;
+ } catch (IOException ex) {
+ Log.verbose(ex);
+ throw new PackagerException(ex);
+ }
+ }
+
+ // name of post-image script
+ private File getConfig_Script(Map<String, ? super Object> params) {
+ return new File(CONFIG_ROOT.fetchFrom(params),
+ APP_NAME.fetchFrom(params) + "-post-image.wsf");
+ }
+
+ private void prepareBasicProjectConfig(
+ Map<String, ? super Object> params) throws IOException {
+ fetchResource(getConfig_Script(params).getName(),
+ I18N.getString("resource.post-install-script"),
+ (String) null,
+ getConfig_Script(params),
+ VERBOSE.fetchFrom(params),
+ RESOURCE_DIR.fetchFrom(params));
+ }
+
+ private static String relativePath(File basedir, File file) {
+ return file.getAbsolutePath().substring(
+ basedir.getAbsolutePath().length() + 1);
+ }
+
+ private void prepareIconsFile(
+ Map<String, ? super Object> params) throws IOException {
+
+ File imageRootDir = WIN_APP_IMAGE.fetchFrom(params);
+
+ List<Map<String, ? super Object>> addLaunchers =
+ ADD_LAUNCHERS.fetchFrom(params);
+
+ XMLOutputFactory xmlFactory = XMLOutputFactory.newInstance();
+ try (Writer w = new BufferedWriter(new FileWriter(new File(
+ CONFIG_ROOT.fetchFrom(params), "icons.wxi")))) {
+ XMLStreamWriter xml = xmlFactory.createXMLStreamWriter(w);
+
+ xml.writeStartDocument();
+ xml.writeStartElement("Include");
+
+ File launcher = new File(imageRootDir,
+ WinAppBundler.getLauncherRelativePath(params));
+ if (launcher.exists()) {
+ String iconPath = launcher.getAbsolutePath().replace(".exe", ".ico");
+ if (MENU_HINT.fetchFrom(params)) {
+ xml.writeStartElement("Icon");
+ xml.writeAttribute("Id", "StartMenuIcon.exe");
+ xml.writeAttribute("SourceFile", iconPath);
+ xml.writeEndElement();
+ }
+ if (SHORTCUT_HINT.fetchFrom(params)) {
+ xml.writeStartElement("Icon");
+ xml.writeAttribute("Id", "DesktopIcon.exe");
+ xml.writeAttribute("SourceFile", iconPath);
+ xml.writeEndElement();
+ }
+ }
+
+ for (int i = 0; i < addLaunchers.size(); i++) {
+ Map<String, ? super Object> sl = addLaunchers.get(i);
+ if (SHORTCUT_HINT.fetchFrom(sl) || MENU_HINT.fetchFrom(sl)) {
+ File addLauncher = new File(imageRootDir,
+ WinAppBundler.getLauncherRelativePath(sl));
+ String addLauncherPath
+ = relativePath(imageRootDir, addLauncher);
+ String addLauncherIconPath
+ = addLauncherPath.replace(".exe", ".ico");
+
+ xml.writeStartElement("Icon");
+ xml.writeAttribute("Id", "Launcher" + i + ".exe");
+ xml.writeAttribute("SourceFile", addLauncherIconPath);
+ xml.writeEndElement();
+ }
+ }
+
+ xml.writeEndElement();
+ xml.writeEndDocument();
+ xml.flush();
+ xml.close();
+ } catch (XMLStreamException ex) {
+ Log.verbose(ex);
+ throw new IOException(ex);
+ }
+ }
+
+ Map<String, String> prepareMainProjectFile(
+ Map<String, ? super Object> params) throws IOException {
+ Map<String, String> data = new HashMap<>();
+
+ UUID productGUID = UUID.randomUUID();
+
+ Log.verbose(MessageFormat.format(
+ I18N.getString("message.generated-product-guid"),
+ productGUID.toString()));
+
+ // we use random GUID for product itself but
+ // user provided for upgrade guid
+ // Upgrade guid is important to decide whether it is an upgrade of
+ // installed app. I.e. we need it to be the same for
+ // 2 different versions of app if possible
+ data.put("JpProductCode", productGUID.toString());
+ data.put("JpProductUpgradeCode",
+ UPGRADE_UUID.fetchFrom(params).toString());
+
+ if (!UPGRADE_UUID.getIsDefaultValue()) {
+ data.put("JpAllowDowngrades", "yes");
+ }
+
+ if (CAN_USE_WIX36.fetchFrom(params)) {
+ data.put("JpWixVersion36OrNewer", "yes");
+ }
+
+ data.put("JpAppName", APP_NAME.fetchFrom(params));
+ data.put("JpAppDescription", DESCRIPTION.fetchFrom(params));
+ data.put("JpAppVendor", VENDOR.fetchFrom(params));
+ data.put("JpAppVersion", PRODUCT_VERSION.fetchFrom(params));
+
+ data.put("JpConfigDir", CONFIG_ROOT.fetchFrom(params).getAbsolutePath());
+
+ File imageRootDir = WIN_APP_IMAGE.fetchFrom(params);
+
+ if (MSI_SYSTEM_WIDE.fetchFrom(params)) {
+ data.put("JpIsSystemWide", "yes");
+ }
+
+ String licenseFile = LICENSE_FILE.fetchFrom(params);
+ if (licenseFile != null) {
+ String lname = new File(licenseFile).getName();
+ File destFile = new File(CONFIG_ROOT.fetchFrom(params), lname);
+ data.put("JpLicenseRtf", destFile.getAbsolutePath());
+ }
+
+ // Copy CA dll to include with installer
+ if (INSTALLDIR_CHOOSER.fetchFrom(params)) {
+ data.put("JpInstallDirChooser", "yes");
+ String fname = "wixhelper.dll";
+ try (InputStream is = getResourceAsStream(fname)) {
+ Files.copy(is, Paths.get(
+ CONFIG_ROOT.fetchFrom(params).getAbsolutePath(), fname));
+ }
+ }
+
+ // Copy l10n files.
+ for (String loc : Arrays.asList("en", "ja", "zh_CN")) {
+ String fname = "MsiInstallerStrings_" + loc + ".wxl";
+ try (InputStream is = getResourceAsStream(fname)) {
+ Files.copy(is, Paths.get(
+ CONFIG_ROOT.fetchFrom(params).getAbsolutePath(), fname));
+ }
+ }
+
+ try (InputStream is = getResourceAsStream("main.wxs")) {
+ Files.copy(is, Paths.get(
+ getConfig_ProjectFile(params).getAbsolutePath()));
+ }
+
+ return data;
+ }
+ private int id;
+ private int compId;
+ private final static String LAUNCHER_ID = "LauncherId";
+
+ private void walkFileTree(Map<String, ? super Object> params,
+ File root, PrintStream out, String prefix) {
+ List<File> dirs = new ArrayList<>();
+ List<File> files = new ArrayList<>();
+
+ if (!root.isDirectory()) {
+ throw new RuntimeException(
+ MessageFormat.format(
+ I18N.getString("error.cannot-walk-directory"),
+ root.getAbsolutePath()));
+ }
+
+ // sort to files and dirs
+ File[] children = root.listFiles();
+ if (children != null) {
+ for (File f : children) {
+ if (f.isDirectory()) {
+ dirs.add(f);
+ } else {
+ files.add(f);
+ }
+ }
+ }
+
+ // have files => need to output component
+ out.println(prefix + " <Component Id=\"comp" + (compId++)
+ + "\" DiskId=\"1\""
+ + " Guid=\"" + UUID.randomUUID().toString() + "\""
+ + " Win64=\"yes\""
+ + ">");
+ out.println(prefix + " <CreateFolder/>");
+ out.println(prefix + " <RemoveFolder Id=\"RemoveDir"
+ + (id++) + "\" On=\"uninstall\" />");
+
+ boolean needRegistryKey = !MSI_SYSTEM_WIDE.fetchFrom(params);
+ File imageRootDir = WIN_APP_IMAGE.fetchFrom(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
+ // - if we adding shortcut in this component
+
+ for (File f: files) {
+ boolean isLauncher = f.equals(launcherFile);
+ if (isLauncher) {
+ needRegistryKey = true;
+ }
+ }
+
+ if (needRegistryKey) {
+ // has to be under HKCU to make WiX happy
+ out.println(prefix + " <RegistryKey Root=\"HKCU\" "
+ + " Key=\"Software\\" + VENDOR.fetchFrom(params) + "\\"
+ + APP_NAME.fetchFrom(params) + "\""
+ + (CAN_USE_WIX36.fetchFrom(params) ?
+ ">" : " Action=\"createAndRemoveOnUninstall\">"));
+ out.println(prefix
+ + " <RegistryValue Name=\"Version\" Value=\""
+ + VERSION.fetchFrom(params)
+ + "\" Type=\"string\" KeyPath=\"yes\"/>");
+ out.println(prefix + " </RegistryKey>");
+ }
+
+ boolean menuShortcut = MENU_HINT.fetchFrom(params);
+ boolean desktopShortcut = SHORTCUT_HINT.fetchFrom(params);
+
+ Map<String, String> idToFileMap = new TreeMap<>();
+ boolean launcherSet = false;
+
+ for (File f : files) {
+ boolean isLauncher = f.equals(launcherFile);
+
+ launcherSet = launcherSet || isLauncher;
+
+ boolean doShortcuts =
+ isLauncher && (menuShortcut || desktopShortcut);
+
+ String thisFileId = isLauncher ? LAUNCHER_ID : ("FileId" + (id++));
+ idToFileMap.put(f.getName(), thisFileId);
+
+ out.println(prefix + " <File Id=\"" +
+ thisFileId + "\""
+ + " Name=\"" + f.getName() + "\" "
+ + " Source=\"" + relativePath(imageRootDir, f) + "\""
+ + " ProcessorArchitecture=\"x64\"" + ">");
+ if (doShortcuts && desktopShortcut) {
+ out.println(prefix
+ + " <Shortcut Id=\"desktopShortcut\" Directory="
+ + "\"DesktopFolder\""
+ + " Name=\"" + APP_NAME.fetchFrom(params)
+ + "\" WorkingDirectory=\"INSTALLDIR\""
+ + " Advertise=\"no\" Icon=\"DesktopIcon.exe\""
+ + " IconIndex=\"0\" />");
+ }
+ if (doShortcuts && menuShortcut) {
+ out.println(prefix
+ + " <Shortcut Id=\"ExeShortcut\" Directory="
+ + "\"ProgramMenuDir\""
+ + " Name=\"" + APP_NAME.fetchFrom(params)
+ + "\" Advertise=\"no\" Icon=\"StartMenuIcon.exe\""
+ + " IconIndex=\"0\" />");
+ }
+
+ List<Map<String, ? super Object>> addLaunchers =
+ ADD_LAUNCHERS.fetchFrom(params);
+ for (int i = 0; i < addLaunchers.size(); i++) {
+ Map<String, ? super Object> sl = addLaunchers.get(i);
+ File addLauncherFile = new File(imageRootDir,
+ WinAppBundler.getLauncherRelativePath(sl));
+ if (f.equals(addLauncherFile)) {
+ if (SHORTCUT_HINT.fetchFrom(sl)) {
+ out.println(prefix
+ + " <Shortcut Id=\"desktopShortcut"
+ + i + "\" Directory=\"DesktopFolder\""
+ + " Name=\"" + APP_NAME.fetchFrom(sl)
+ + "\" WorkingDirectory=\"INSTALLDIR\""
+ + " Advertise=\"no\" Icon=\"Launcher"
+ + i + ".exe\" IconIndex=\"0\" />");
+ }
+ if (MENU_HINT.fetchFrom(sl)) {
+ out.println(prefix
+ + " <Shortcut Id=\"ExeShortcut"
+ + i + "\" Directory=\"ProgramMenuDir\""
+ + " Name=\"" + APP_NAME.fetchFrom(sl)
+ + "\" Advertise=\"no\" Icon=\"Launcher"
+ + i + ".exe\" IconIndex=\"0\" />");
+ // Should we allow different menu groups? Not for now.
+ }
+ }
+ }
+ out.println(prefix + " </File>");
+ }
+
+ if (launcherSet) {
+ List<Map<String, ? super Object>> fileAssociations =
+ FILE_ASSOCIATIONS.fetchFrom(params);
+ String regName = APP_REGISTRY_NAME.fetchFrom(params);
+ Set<String> defaultedMimes = new TreeSet<>();
+ int count = 0;
+ for (Map<String, ? super Object> fa : fileAssociations) {
+ String description = FA_DESCRIPTION.fetchFrom(fa);
+ List<String> extensions = FA_EXTENSIONS.fetchFrom(fa);
+ List<String> mimeTypes = FA_CONTENT_TYPE.fetchFrom(fa);
+ File icon = FA_ICON.fetchFrom(fa);
+
+ String mime = (mimeTypes == null ||
+ mimeTypes.isEmpty()) ? null : mimeTypes.get(0);
+
+ if (extensions == null) {
+ Log.verbose(I18N.getString(
+ "message.creating-association-with-null-extension"));
+
+ String entryName = regName + "File";
+ if (count > 0) {
+ entryName += "." + count;
+ }
+ count++;
+ out.print(prefix + " <ProgId Id='" + entryName
+ + "' Description='" + description + "'");
+ if (icon != null && icon.exists()) {
+ out.print(" Icon='" + idToFileMap.get(icon.getName())
+ + "' IconIndex='0'");
+ }
+ out.println(" />");
+ } else {
+ for (String ext : extensions) {
+ String entryName = regName + "File";
+ if (count > 0) {
+ entryName += "." + count;
+ }
+ count++;
+
+ out.print(prefix + " <ProgId Id='" + entryName
+ + "' Description='" + description + "'");
+ if (icon != null && icon.exists()) {
+ out.print(" Icon='"
+ + idToFileMap.get(icon.getName())
+ + "' IconIndex='0'");
+ }
+ out.println(">");
+
+ if (extensions == null) {
+ Log.verbose(I18N.getString(
+ "message.creating-association-with-null-extension"));
+ } else {
+ out.print(prefix + " <Extension Id='"
+ + ext + "' Advertise='no'");
+ if (mime == null) {
+ out.println(">");
+ } else {
+ out.println(" ContentType='" + mime + "'>");
+ if (!defaultedMimes.contains(mime)) {
+ out.println(prefix
+ + " <MIME ContentType='"
+ + mime + "' Default='yes' />");
+ defaultedMimes.add(mime);
+ }
+ }
+ out.println(prefix
+ + " <Verb Id='open' Command='Open' "
+ + "TargetFile='" + LAUNCHER_ID
+ + "' Argument='\"%1\"' />");
+ out.println(prefix + " </Extension>");
+ }
+ out.println(prefix + " </ProgId>");
+ }
+ }
+ }
+ }
+
+ out.println(prefix + " </Component>");
+
+ for (File d : dirs) {
+ out.println(prefix + " <Directory Id=\"dirid" + (id++)
+ + "\" Name=\"" + d.getName() + "\">");
+ walkFileTree(params, d, out, prefix + " ");
+ out.println(prefix + " </Directory>");
+ }
+ }
+
+ void prepareContentList(Map<String, ? super Object> params)
+ throws FileNotFoundException {
+ File f = new File(
+ CONFIG_ROOT.fetchFrom(params), MSI_PROJECT_CONTENT_FILE);
+
+ try (PrintStream out = new PrintStream(f)) {
+
+ // opening
+ out.println("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>");
+ out.println("<Include>");
+
+ out.println(" <Directory Id=\"TARGETDIR\" Name=\"SourceDir\">");
+ if (MSI_SYSTEM_WIDE.fetchFrom(params)) {
+ // install to programfiles
+ out.println(" <Directory Id=\"ProgramFiles64Folder\" "
+ + "Name=\"PFiles\">");
+ } else {
+ // install to user folder
+ out.println(
+ " <Directory Name=\"AppData\" Id=\"LocalAppDataFolder\">");
+ }
+
+ // We should get valid folder or subfolders
+ String installDir = WINDOWS_INSTALL_DIR.fetchFrom(params);
+ String [] installDirs = installDir.split(Pattern.quote("\\"));
+ for (int i = 0; i < (installDirs.length - 1); i++) {
+ out.println(" <Directory Id=\"SUBDIR" + i + "\" Name=\""
+ + installDirs[i] + "\">");
+ }
+
+ out.println(" <Directory Id=\"APPLICATIONFOLDER\" Name=\""
+ + installDirs[installDirs.length - 1] + "\">");
+
+ // dynamic part
+ id = 0;
+ compId = 0; // reset counters
+ walkFileTree(params, WIN_APP_IMAGE.fetchFrom(params), out, " ");
+
+ // closing
+ for (int i = 0; i < installDirs.length; i++) {
+ out.println(" </Directory>");
+ }
+ out.println(" </Directory>");
+
+ // for shortcuts
+ if (SHORTCUT_HINT.fetchFrom(params)) {
+ out.println(" <Directory Id=\"DesktopFolder\" />");
+ }
+ if (MENU_HINT.fetchFrom(params)) {
+ out.println(" <Directory Id=\"ProgramMenuFolder\">");
+ out.println(" <Directory Id=\"ProgramMenuDir\" Name=\""
+ + MENU_GROUP.fetchFrom(params) + "\">");
+ out.println(" <Component Id=\"comp" + (compId++) + "\""
+ + " Guid=\"" + UUID.randomUUID().toString() + "\""
+ + " Win64=\"yes\""
+ + ">");
+ out.println(" <RemoveFolder Id=\"ProgramMenuDir\" "
+ + "On=\"uninstall\" />");
+ // This has to be under HKCU to make WiX happy.
+ // There are numberous discussions on this amoung WiX users
+ // (if user A installs and user B uninstalls key is left behind)
+ // there are suggested workarounds but none are appealing.
+ // Leave it for now
+ out.println(
+ " <RegistryValue Root=\"HKCU\" Key=\"Software\\"
+ + VENDOR.fetchFrom(params) + "\\"
+ + APP_NAME.fetchFrom(params)
+ + "\" Type=\"string\" Value=\"\" />");
+ out.println(" </Component>");
+ out.println(" </Directory>");
+ out.println(" </Directory>");
+ }
+
+ out.println(" </Directory>");
+
+ out.println(" <Feature Id=\"DefaultFeature\" "
+ + "Title=\"Main Feature\" Level=\"1\">");
+ for (int j = 0; j < compId; j++) {
+ out.println(" <ComponentRef Id=\"comp" + j + "\" />");
+ }
+ // component is defined in the main.wsx
+ out.println(
+ " <ComponentRef Id=\"CleanupMainApplicationFolder\" />");
+ out.println(" </Feature>");
+ out.println("</Include>");
+
+ }
+ }
+
+ private File getConfig_ProjectFile(Map<String, ? super Object> params) {
+ return new File(CONFIG_ROOT.fetchFrom(params),
+ APP_NAME.fetchFrom(params) + ".wxs");
+ }
+
+ private Map<String, String> prepareWiXConfig(
+ Map<String, ? super Object> params) throws IOException {
+ prepareContentList(params);
+ prepareIconsFile(params);
+ return prepareMainProjectFile(params);
+ }
+
+ private final static String MSI_PROJECT_CONTENT_FILE = "bundle.wxi";
+
+ private File buildMSI(Map<String, ? super Object> params,
+ Map<String, String> wixVars, File outdir)
+ throws IOException {
+ File tmpDir = new File(TEMP_ROOT.fetchFrom(params), "tmp");
+ File candleOut = new File(
+ tmpDir, APP_NAME.fetchFrom(params) + ".wixobj");
+ File msiOut = new File(
+ outdir, INSTALLER_FILE_NAME.fetchFrom(params) + ".msi");
+
+ Log.verbose(MessageFormat.format(I18N.getString(
+ "message.preparing-msi-config"), msiOut.getAbsolutePath()));
+
+ msiOut.getParentFile().mkdirs();
+
+ List<String> commandLine = new ArrayList<>(Arrays.asList(
+ getCandlePath(),
+ "-nologo",
+ getConfig_ProjectFile(params).getAbsolutePath(),
+ "-ext", "WixUtilExtension",
+ "-out", candleOut.getAbsolutePath()));
+ for(Map.Entry<String, String> wixVar: wixVars.entrySet()) {
+ String v = "-d" + wixVar.getKey() + "=" + wixVar.getValue();
+ commandLine.add(v);
+ }
+ ProcessBuilder pb = new ProcessBuilder(commandLine);
+ pb = pb.directory(WIN_APP_IMAGE.fetchFrom(params));
+ IOUtils.exec(pb);
+
+ Log.verbose(MessageFormat.format(I18N.getString(
+ "message.generating-msi"), msiOut.getAbsolutePath()));
+
+ boolean enableLicenseUI = (LICENSE_FILE.fetchFrom(params) != null);
+ boolean enableInstalldirUI = INSTALLDIR_CHOOSER.fetchFrom(params);
+
+ commandLine = new ArrayList<>();
+
+ commandLine.add(getLightPath());
+
+ commandLine.add("-nologo");
+ commandLine.add("-spdb");
+ commandLine.add("-sice:60");
+ // ignore warnings due to "missing launcguage info" (ICE60)
+ commandLine.add(candleOut.getAbsolutePath());
+ commandLine.add("-ext");
+ commandLine.add("WixUtilExtension");
+ if (enableLicenseUI || enableInstalldirUI) {
+ commandLine.add("-ext");
+ commandLine.add("WixUIExtension");
+ }
+
+ commandLine.add("-loc");
+ commandLine.add(new File(CONFIG_ROOT.fetchFrom(params), I18N.getString(
+ "resource.wxl-file-name")).getAbsolutePath());
+
+ // Only needed if we using CA dll, so Wix can find it
+ if (enableInstalldirUI) {
+ commandLine.add("-b");
+ commandLine.add(CONFIG_ROOT.fetchFrom(params).getAbsolutePath());
+ }
+
+ commandLine.add("-out");
+ commandLine.add(msiOut.getAbsolutePath());
+
+ // create .msi
+ pb = new ProcessBuilder(commandLine);
+
+ pb = pb.directory(WIN_APP_IMAGE.fetchFrom(params));
+ IOUtils.exec(pb);
+
+ candleOut.delete();
+ IOUtils.deleteRecursive(tmpDir);
+
+ return msiOut;
+ }
+
+ public static void ensureByMutationFileIsRTF(File f) {
+ if (f == null || !f.isFile()) return;
+
+ try {
+ boolean existingLicenseIsRTF = false;
+
+ try (FileInputStream fin = new FileInputStream(f)) {
+ byte[] firstBits = new byte[7];
+
+ if (fin.read(firstBits) == firstBits.length) {
+ String header = new String(firstBits);
+ existingLicenseIsRTF = "{\\rtf1\\".equals(header);
+ }
+ }
+
+ if (!existingLicenseIsRTF) {
+ List<String> oldLicense = Files.readAllLines(f.toPath());
+ try (Writer w = Files.newBufferedWriter(
+ f.toPath(), Charset.forName("Windows-1252"))) {
+ w.write("{\\rtf1\\ansi\\ansicpg1252\\deff0\\deflang1033"
+ + "{\\fonttbl{\\f0\\fnil\\fcharset0 Arial;}}\n"
+ + "\\viewkind4\\uc1\\pard\\sa200\\sl276"
+ + "\\slmult1\\lang9\\fs20 ");
+ oldLicense.forEach(l -> {
+ try {
+ for (char c : l.toCharArray()) {
+ // 0x00 <= ch < 0x20 Escaped (\'hh)
+ // 0x20 <= ch < 0x80 Raw(non - escaped) char
+ // 0x80 <= ch <= 0xFF Escaped(\ 'hh)
+ // 0x5C, 0x7B, 0x7D (special RTF characters
+ // \,{,})Escaped(\'hh)
+ // ch > 0xff Escaped (\\ud###?)
+ if (c < 0x10) {
+ w.write("\\'0");
+ w.write(Integer.toHexString(c));
+ } else if (c > 0xff) {
+ w.write("\\ud");
+ w.write(Integer.toString(c));
+ // \\uc1 is in the header and in effect
+ // so we trail with a replacement char if
+ // the font lacks that character - '?'
+ w.write("?");
+ } else if ((c < 0x20) || (c >= 0x80) ||
+ (c == 0x5C) || (c == 0x7B) ||
+ (c == 0x7D)) {
+ w.write("\\'");
+ w.write(Integer.toHexString(c));
+ } else {
+ w.write(c);
+ }
+ }
+ // blank lines are interpreted as paragraph breaks
+ if (l.length() < 1) {
+ w.write("\\par");
+ } else {
+ w.write(" ");
+ }
+ w.write("\r\n");
+ } catch (IOException e) {
+ Log.verbose(e);
+ }
+ });
+ w.write("}\r\n");
+ }
+ }
+ } catch (IOException e) {
+ Log.verbose(e);
+ }
+
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WindowsAppImageBuilder.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,397 @@
+/*
+ * Copyright (c) 2015, 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.jpackage.internal;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.UncheckedIOException;
+import java.io.Writer;
+import java.io.BufferedWriter;
+import java.io.FileWriter;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardCopyOption;
+import java.nio.file.attribute.PosixFilePermission;
+import java.text.MessageFormat;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.ResourceBundle;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.regex.Pattern;
+import java.util.stream.Stream;
+
+import static jdk.jpackage.internal.StandardBundlerParam.*;
+
+public class WindowsAppImageBuilder extends AbstractAppImageBuilder {
+
+ static {
+ System.loadLibrary("jpackage");
+ }
+
+ private static final ResourceBundle I18N = ResourceBundle.getBundle(
+ "jdk.jpackage.internal.resources.WinResources");
+
+ private final static String LIBRARY_NAME = "applauncher.dll";
+ private final static String REDIST_MSVCR = "vcruntimeVS_VER.dll";
+ private final static String REDIST_MSVCP = "msvcpVS_VER.dll";
+
+ private final static String TEMPLATE_APP_ICON ="javalogo_white_48.ico";
+
+ private static final String EXECUTABLE_PROPERTIES_TEMPLATE =
+ "WinLauncher.template";
+
+ private final Path root;
+ private final Path appDir;
+ private final Path appModsDir;
+ private final Path runtimeDir;
+ private final Path mdir;
+ private final Path binDir;
+
+ private final Map<String, ? super Object> params;
+
+ public static final BundlerParamInfo<Boolean> REBRAND_EXECUTABLE =
+ new WindowsBundlerParam<>(
+ "win.launcher.rebrand",
+ Boolean.class,
+ params -> Boolean.TRUE,
+ (s, p) -> Boolean.valueOf(s));
+
+ public static final BundlerParamInfo<File> ICON_ICO =
+ new StandardBundlerParam<>(
+ "icon.ico",
+ File.class,
+ params -> {
+ File f = ICON.fetchFrom(params);
+ if (f != null && !f.getName().toLowerCase().endsWith(".ico")) {
+ Log.error(MessageFormat.format(
+ I18N.getString("message.icon-not-ico"), f));
+ return null;
+ }
+ return f;
+ },
+ (s, p) -> new File(s));
+
+ public static final StandardBundlerParam<Boolean> CONSOLE_HINT =
+ new WindowsBundlerParam<>(
+ Arguments.CLIOptions.WIN_CONSOLE_HINT.getId(),
+ Boolean.class,
+ params -> false,
+ // valueOf(null) is false,
+ // and we actually do want null in some cases
+ (s, p) -> (s == null
+ || "null".equalsIgnoreCase(s)) ? true : Boolean.valueOf(s));
+
+ public WindowsAppImageBuilder(Map<String, Object> config, Path imageOutDir)
+ throws IOException {
+ super(config,
+ imageOutDir.resolve(APP_NAME.fetchFrom(config) + "/runtime"));
+
+ Objects.requireNonNull(imageOutDir);
+
+ this.params = config;
+
+ this.root = imageOutDir.resolve(APP_NAME.fetchFrom(params));
+ this.appDir = root.resolve("app");
+ 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);
+ }
+
+ public WindowsAppImageBuilder(String jreName, Path imageOutDir)
+ throws IOException {
+ super(null, imageOutDir.resolve(jreName));
+
+ Objects.requireNonNull(imageOutDir);
+
+ this.params = null;
+ this.root = imageOutDir.resolve(jreName);
+ this.appDir = null;
+ this.appModsDir = null;
+ this.runtimeDir = root;
+ this.mdir = runtimeDir.resolve("lib");
+ this.binDir = null;
+ Files.createDirectories(runtimeDir);
+ }
+
+ private void writeEntry(InputStream in, Path dstFile) throws IOException {
+ Files.createDirectories(dstFile.getParent());
+ Files.copy(in, dstFile);
+ }
+
+ private static String getLauncherName(Map<String, ? super Object> params) {
+ return APP_NAME.fetchFrom(params) + ".exe";
+ }
+
+ // Returns launcher resource name for launcher we need to use.
+ public static String getLauncherResourceName(
+ Map<String, ? super Object> params) {
+ if (CONSOLE_HINT.fetchFrom(params)) {
+ return "jpackageapplauncher.exe";
+ } else {
+ return "jpackageapplauncherw.exe";
+ }
+ }
+
+ public static String getLauncherCfgName(
+ Map<String, ? super Object> params) {
+ return "app/" + APP_NAME.fetchFrom(params) +".cfg";
+ }
+
+ private File getConfig_AppIcon(Map<String, ? super Object> params) {
+ return new File(getConfigRoot(params),
+ APP_NAME.fetchFrom(params) + ".ico");
+ }
+
+ private File getConfig_ExecutableProperties(
+ Map<String, ? super Object> params) {
+ return new File(getConfigRoot(params),
+ APP_NAME.fetchFrom(params) + ".properties");
+ }
+
+ File getConfigRoot(Map<String, ? super Object> params) {
+ return CONFIG_ROOT.fetchFrom(params);
+ }
+
+ @Override
+ public Path getAppDir() {
+ return appDir;
+ }
+
+ @Override
+ public Path getAppModsDir() {
+ return appModsDir;
+ }
+
+ @Override
+ 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 .exe launchers
+ createLauncherForEntryPoint(params);
+
+ // copy the jars
+ copyApplication(params);
+
+ // copy in the needed libraries
+ try (InputStream is_lib = getResourceAsStream(LIBRARY_NAME)) {
+ Files.copy(is_lib, binDir.resolve(LIBRARY_NAME));
+ }
+
+ copyMSVCDLLs();
+
+ // create the additional launcher(s), if any
+ List<Map<String, ? super Object>> entryPoints =
+ StandardBundlerParam.ADD_LAUNCHERS.fetchFrom(params);
+ for (Map<String, ? super Object> entryPoint : entryPoints) {
+ createLauncherForEntryPoint(
+ AddLauncherArguments.merge(originalParams, entryPoint));
+ }
+ }
+
+ @Override
+ public void prepareJreFiles() throws IOException {}
+
+ private void copyMSVCDLLs() throws IOException {
+ AtomicReference<IOException> ioe = new AtomicReference<>();
+ try (Stream<Path> files = Files.list(runtimeDir.resolve("bin"))) {
+ files.filter(p -> Pattern.matches(
+ "^(vcruntime|msvcp|msvcr|ucrtbase|api-ms-win-).*\\.dll$",
+ p.toFile().getName().toLowerCase()))
+ .forEach(p -> {
+ try {
+ Files.copy(p, binDir.resolve((p.toFile().getName())));
+ } catch (IOException e) {
+ ioe.set(e);
+ }
+ });
+ }
+
+ IOException e = ioe.get();
+ if (e != null) {
+ throw e;
+ }
+ }
+
+ private void validateValueAndPut(
+ Map<String, String> data, String key,
+ BundlerParamInfo<String> param,
+ Map<String, ? super Object> params) {
+ String value = param.fetchFrom(params);
+ if (value.contains("\r") || value.contains("\n")) {
+ Log.error("Configuration Parameter " + param.getID()
+ + " contains multiple lines of text, ignore it");
+ data.put(key, "");
+ return;
+ }
+ data.put(key, value);
+ }
+
+ protected void prepareExecutableProperties(
+ Map<String, ? super Object> params) throws IOException {
+ Map<String, String> data = new HashMap<>();
+
+ // mapping Java parameters in strings for version resource
+ validateValueAndPut(data, "COMPANY_NAME", VENDOR, params);
+ validateValueAndPut(data, "FILE_DESCRIPTION", DESCRIPTION, params);
+ validateValueAndPut(data, "FILE_VERSION", VERSION, params);
+ data.put("INTERNAL_NAME", getLauncherName(params));
+ validateValueAndPut(data, "LEGAL_COPYRIGHT", COPYRIGHT, params);
+ data.put("ORIGINAL_FILENAME", getLauncherName(params));
+ validateValueAndPut(data, "PRODUCT_NAME", APP_NAME, params);
+ validateValueAndPut(data, "PRODUCT_VERSION", VERSION, params);
+
+ try (Writer w = Files.newBufferedWriter(
+ getConfig_ExecutableProperties(params).toPath(),
+ StandardCharsets.UTF_8)) {
+ String content = preprocessTextResource(
+ getConfig_ExecutableProperties(params).getName(),
+ I18N.getString("resource.executable-properties-template"),
+ EXECUTABLE_PROPERTIES_TEMPLATE, data,
+ VERBOSE.fetchFrom(params),
+ RESOURCE_DIR.fetchFrom(params));
+ w.write(content);
+ }
+ }
+
+ private void createLauncherForEntryPoint(
+ Map<String, ? super Object> params) throws IOException {
+
+ File launcherIcon = ICON_ICO.fetchFrom(params);
+ File icon = launcherIcon != null ?
+ launcherIcon : ICON_ICO.fetchFrom(params);
+ File iconTarget = getConfig_AppIcon(params);
+
+ InputStream in = locateResource(
+ APP_NAME.fetchFrom(params) + ".ico",
+ "icon",
+ TEMPLATE_APP_ICON,
+ icon,
+ VERBOSE.fetchFrom(params),
+ RESOURCE_DIR.fetchFrom(params));
+
+ Files.copy(in, iconTarget.toPath(),
+ StandardCopyOption.REPLACE_EXISTING);
+
+ writeCfgFile(params, root.resolve(
+ getLauncherCfgName(params)).toFile(), "$APPDIR\\runtime");
+
+ prepareExecutableProperties(params);
+
+ // Copy executable to bin folder
+ Path executableFile = binDir.resolve(getLauncherName(params));
+
+ try (InputStream is_launcher =
+ getResourceAsStream(getLauncherResourceName(params))) {
+ writeEntry(is_launcher, executableFile);
+ }
+
+ File launcher = executableFile.toFile();
+ launcher.setWritable(true, true);
+
+ // Update branding of EXE file
+ if (REBRAND_EXECUTABLE.fetchFrom(params)) {
+ try {
+ String tempDirectory = WindowsDefender.getUserTempDirectory();
+ if (Arguments.CLIOptions.context().userProvidedBuildRoot) {
+ tempDirectory =
+ TEMP_ROOT.fetchFrom(params).getAbsolutePath();
+ }
+ if (WindowsDefender.isThereAPotentialWindowsDefenderIssue(
+ tempDirectory)) {
+ Log.error(MessageFormat.format(I18N.getString(
+ "message.potential.windows.defender.issue"),
+ tempDirectory));
+ }
+
+ launcher.setWritable(true);
+
+ if (iconTarget.exists()) {
+ iconSwap(iconTarget.getAbsolutePath(),
+ launcher.getAbsolutePath());
+ }
+
+ File executableProperties =
+ getConfig_ExecutableProperties(params);
+
+ if (executableProperties.exists()) {
+ if (versionSwap(executableProperties.getAbsolutePath(),
+ launcher.getAbsolutePath()) != 0) {
+ throw new RuntimeException(MessageFormat.format(
+ I18N.getString("error.version-swap"),
+ executableProperties.getAbsolutePath()));
+ }
+ }
+ } finally {
+ executableFile.toFile().setExecutable(true);
+ executableFile.toFile().setReadOnly();
+ }
+ }
+
+ Files.copy(iconTarget.toPath(),
+ binDir.resolve(APP_NAME.fetchFrom(params) + ".ico"));
+ }
+
+ 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);
+ }
+ }
+ }
+
+ private static native int iconSwap(String iconTarget, String launcher);
+
+ private static native int versionSwap(String executableProperties, String launcher);
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WindowsBundlerParam.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,116 @@
+/*
+ * Copyright (c) 2014, 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.jpackage.internal;
+
+import java.text.MessageFormat;
+import java.util.Map;
+import java.util.ResourceBundle;
+import java.util.function.BiFunction;
+import java.util.function.Function;
+
+class WindowsBundlerParam<T> extends StandardBundlerParam<T> {
+
+ private static final ResourceBundle I18N = ResourceBundle.getBundle(
+ "jdk.jpackage.internal.resources.WinResources");
+
+ WindowsBundlerParam(String id, Class<T> valueType,
+ Function<Map<String, ? super Object>, T> defaultValueFunction,
+ BiFunction<String,
+ Map<String, ? super Object>, T> stringConverter) {
+ super(id, valueType, defaultValueFunction, stringConverter);
+ }
+
+ static final BundlerParamInfo<String> INSTALLER_FILE_NAME =
+ new StandardBundlerParam<> (
+ "win.installerName",
+ String.class,
+ params -> {
+ String nm = APP_NAME.fetchFrom(params);
+ if (nm == null) return null;
+
+ String version = VERSION.fetchFrom(params);
+ if (version == null) {
+ return nm;
+ } else {
+ return nm + "-" + version;
+ }
+ },
+ (s, p) -> s);
+
+ static final BundlerParamInfo<String> APP_REGISTRY_NAME =
+ new StandardBundlerParam<> (
+ Arguments.CLIOptions.WIN_REGISTRY_NAME.getId(),
+ String.class,
+ params -> {
+ String nm = APP_NAME.fetchFrom(params);
+ if (nm == null) return null;
+
+ return nm.replaceAll("[^-a-zA-Z\\.0-9]", "");
+ },
+ (s, p) -> s);
+
+ static final StandardBundlerParam<String> MENU_GROUP =
+ new StandardBundlerParam<>(
+ Arguments.CLIOptions.WIN_MENU_GROUP.getId(),
+ String.class,
+ params -> I18N.getString("param.menu-group.default"),
+ (s, p) -> s
+ );
+
+ static final BundlerParamInfo<Boolean> INSTALLDIR_CHOOSER =
+ new StandardBundlerParam<> (
+ Arguments.CLIOptions.WIN_DIR_CHOOSER.getId(),
+ Boolean.class,
+ params -> Boolean.FALSE,
+ (s, p) -> Boolean.valueOf(s)
+ );
+
+ static final BundlerParamInfo<String> WINDOWS_INSTALL_DIR =
+ new StandardBundlerParam<>(
+ "windows-install-dir",
+ String.class,
+ params -> {
+ String dir = INSTALL_DIR.fetchFrom(params);
+ if (dir != null) {
+ if (dir.contains(":") || dir.contains("..")) {
+ Log.error(MessageFormat.format(I18N.getString(
+ "message.invalid.install.dir"), dir,
+ APP_NAME.fetchFrom(params)));
+ } else {
+ if (dir.startsWith("\\")) {
+ dir = dir.substring(1);
+ }
+ if (dir.endsWith("\\")) {
+ dir = dir.substring(0, dir.length() - 1);
+ }
+ return dir;
+ }
+ }
+ return APP_NAME.fetchFrom(params); // Default to app name
+ },
+ (s, p) -> s
+ );
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WindowsDefender.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,70 @@
+/*
+ * 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.jpackage.internal;
+
+import java.util.List;
+
+final class WindowsDefender {
+
+ private WindowsDefender() {}
+
+ static final boolean isThereAPotentialWindowsDefenderIssue(String dir) {
+ boolean result = false;
+
+ if (Platform.getPlatform() == Platform.WINDOWS &&
+ Platform.getMajorVersion() == 10) {
+
+ // If DisableRealtimeMonitoring is not enabled then there
+ // may be a problem.
+ if (!WindowsRegistry.readDisableRealtimeMonitoring() &&
+ !isDirectoryInExclusionPath(dir)) {
+ result = true;
+ }
+ }
+
+ return result;
+ }
+
+ private static boolean isDirectoryInExclusionPath(String dir) {
+ boolean result = false;
+ // If the user temp directory is not found in the exclusion
+ // list then there may be a problem.
+ List<String> paths = WindowsRegistry.readExclusionsPaths();
+ for (String s : paths) {
+ if (WindowsRegistry.comparePaths(s, dir)) {
+ result = true;
+ break;
+ }
+ }
+
+ return result;
+ }
+
+ static final String getUserTempDirectory() {
+ String tempDirectory = System.getProperty("java.io.tmpdir");
+ return tempDirectory;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WindowsRegistry.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,134 @@
+/*
+ * 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.jpackage.internal;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.PrintStream;
+import java.util.ArrayList;
+import java.util.List;
+
+final class WindowsRegistry {
+
+ // Currently we only support HKEY_LOCAL_MACHINE. Native implementation will
+ // require support for additinal HKEY if needed.
+ private static final int HKEY_LOCAL_MACHINE = 1;
+
+ static {
+ System.loadLibrary("jpackage");
+ }
+
+ private WindowsRegistry() {}
+
+ /**
+ * Reads the registry value for DisableRealtimeMonitoring.
+ * @return true if DisableRealtimeMonitoring is set to 0x1,
+ * false otherwise.
+ */
+ static final boolean readDisableRealtimeMonitoring() {
+ final String subKey = "Software\\Microsoft\\"
+ + "Windows Defender\\Real-Time Protection";
+ final String value = "DisableRealtimeMonitoring";
+ int result = readDwordValue(HKEY_LOCAL_MACHINE, subKey, value, 0);
+ return (result == 1);
+ }
+
+ static final List<String> readExclusionsPaths() {
+ List<String> result = new ArrayList<>();
+ final String subKey = "Software\\Microsoft\\"
+ + "Windows Defender\\Exclusions\\Paths";
+ long lKey = openRegistryKey(HKEY_LOCAL_MACHINE, subKey);
+ if (lKey == 0) {
+ return result;
+ }
+
+ String valueName;
+ int index = 0;
+ do {
+ valueName = enumRegistryValue(lKey, index);
+ if (valueName != null) {
+ result.add(valueName);
+ index++;
+ }
+ } while (valueName != null);
+
+ closeRegistryKey(lKey);
+
+ return result;
+ }
+
+ /**
+ * Reads DWORD registry value.
+ *
+ * @param key one of HKEY predefine value
+ * @param subKey registry sub key
+ * @param value value to read
+ * @param defaultValue default value in case if subKey or value not found
+ * or any other errors occurred
+ * @return value's data only if it was read successfully, otherwise
+ * defaultValue
+ */
+ private static native int readDwordValue(int key, String subKey,
+ String value, int defaultValue);
+
+ /**
+ * Open registry key.
+ *
+ * @param key one of HKEY predefine value
+ * @param subKey registry sub key
+ * @return native handle to open key
+ */
+ private static native long openRegistryKey(int key, String subKey);
+
+ /**
+ * Enumerates the values for registry key.
+ *
+ * @param lKey native handle to open key returned by openRegistryKey
+ * @param index index of value starting from 0. Increment until this
+ * function returns NULL which means no more values.
+ * @return returns value or NULL if error or no more data
+ */
+ private static native String enumRegistryValue(long lKey, int index);
+
+ /**
+ * Close registry key.
+ *
+ * @param lKey native handle to open key returned by openRegistryKey
+ */
+ private static native void closeRegistryKey(long lKey);
+
+ /**
+ * Compares two Windows paths regardless case and if paths are short or long.
+ *
+ * @param path1 path to compare
+ * @param path2 path to compare
+ * @return true if paths point to same location
+ */
+ public static native boolean comparePaths(String path1, String path2);
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/MsiInstallerStrings_en.wxl Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<WixLocalization Culture="en-us" xmlns="http://schemas.microsoft.com/wix/2006/localization" Codepage="1252">
+ <String Id="message.install.dir.exist">The folder [APPLICATIONFOLDER] already exist. Would you like to install to that folder anyway?</String>
+</WixLocalization>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/MsiInstallerStrings_ja.wxl Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<WixLocalization Culture="en-us" xmlns="http://schemas.microsoft.com/wix/2006/localization" Codepage="1252">
+ <String Id="message.install.dir.exist">The folder [APPLICATIONFOLDER] already exist. Would you like to install to that folder anyway?</String>
+</WixLocalization>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/MsiInstallerStrings_zh_CN.wxl Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<WixLocalization Culture="en-us" xmlns="http://schemas.microsoft.com/wix/2006/localization" Codepage="1252">
+ <String Id="message.install.dir.exist">The folder [APPLICATIONFOLDER] already exist. Would you like to install to that folder anyway?</String>
+</WixLocalization>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinLauncher.template Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,34 @@
+#
+# Copyright (c) 2017, 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.
+#
+#
+
+CompanyName=COMPANY_NAME
+FileDescription=FILE_DESCRIPTION
+FileVersion=FILE_VERSION
+InternalName=INTERNAL_NAME
+LegalCopyright=LEGAL_COPYRIGHT
+OriginalFilename=ORIGINAL_FILENAME
+ProductName=PRODUCT_NAME
+ProductVersion=PRODUCT_VERSION
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources.properties Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,82 @@
+#
+# Copyright (c) 2017, 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.
+#
+#
+
+app.bundler.name=Windows Application Image
+exe.bundler.name=EXE Installer
+msi.bundler.name=MSI Installer
+
+param.menu-group.default=Unknown
+
+resource.executable-properties-template=Template for creating executable properties file.
+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_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.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.
+error.copyright-is-too-long.advice=Provide a copyright string shorter than 100 characters.
+error.no-wix-tools=Can not find WiX tools (light.exe, candle.exe).
+error.no-wix-tools.advice=Download WiX 3.0 or later from http://wix.sf.net and add it to the PATH.
+error.version-string-wrong-format=Version string is not compatible with MSI rules [{0}].
+error.version-string-wrong-format.advice=Set the bundler argument "{0}" according to these rules: http://msdn.microsoft.com/en-us/library/aa370859%28v\=VS.85%29.aspx .
+error.version-string-major-out-of-range=Major version must be in the range [0, 255].
+error.version-string-build-out-of-range=Build part of version must be in the range [0, 65535].
+error.version-string-minor-out-of-range=Minor version must be in the range [0, 255].
+error.version-string-part-not-number=Failed to convert version component to int.
+error.cannot-walk-directory=Can not walk [{0}] - it is not a valid directory.
+error.version-swap=Failed to update version information for {0}.
+error.version-compare=Error: Failed to compare version {0} with {1}.
+
+message.result-dir=Result application bundle: {0}.
+message.icon-not-ico=The specified icon "{0}" is not an ICO file and will not be used. The default icon will be used in it's place.
+message.multiple-launchers=Multiple launchers found in predefined app-image. {0} will be used as primary launcher.
+message.potential.windows.defender.issue=Warning: Windows Defender may prevent jpackage from functioning. If there is an issue, it can be addressed by either disabling realtime monitoring, or adding an exclusion for the directory "{0}".
+message.tool-wrong-version=Detected [{0}] version {1} but version {2} is required.
+message.outputting-to-location=Generating EXE for installer to: {0}.
+message.output-location=Installer (.exe) saved to: {0}
+message.tool-version=Detected [{0}] version [{1}].
+message.one-shortcut-required=At least one type of shortcut is required. Enabling menu shortcut.
+message.running-wsh-script=Running WSH script on application image [{0}].
+message.iscc-file-string=InnoSetup compiler set to {0}.
+message.creating-association-with-null-extension=Creating association with null extension.
+message.wrong-tool-version=Detected [{0}] version {1} but version {2} is required.
+message.version-string-too-many-components=Version sting may have up to 3 components - major.minor.build .
+message.truncating.id=Truncating Application ID to 126 chars for Inno Setup.
+message.use-wix36-features=WiX 3.6 detected. Enabling advanced cleanup action.
+message.generated-product-guid=Generated product GUID: {0}.
+message.preparing-msi-config=Preparing MSI config: {0}.
+message.generating-msi=Generating MSI: {0}.
+message.light-file-string=WiX light tool set to {0}.
+message.candle-file-string=WiX candle tool set to {0}.
+message.invalid.install.dir=Warning: Invalid install directory {0}. Install directory should be a relative sub-path under the default installation location such as "Program Files". Defaulting to application name "{1}".
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources_ja.properties Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,82 @@
+#
+# Copyright (c) 2017, 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.
+#
+#
+
+app.bundler.name=Windows Application Image
+exe.bundler.name=EXE Installer
+msi.bundler.name=MSI Installer
+
+param.menu-group.default=Unknown
+
+resource.executable-properties-template=Template for creating executable properties file.
+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_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.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.
+error.copyright-is-too-long.advice=Provide a copyright string shorter than 100 characters.
+error.no-wix-tools=Can not find WiX tools (light.exe, candle.exe).
+error.no-wix-tools.advice=Download WiX 3.0 or later from http://wix.sf.net and add it to the PATH.
+error.version-string-wrong-format=Version string is not compatible with MSI rules [{0}].
+error.version-string-wrong-format.advice=Set the bundler argument "{0}" according to these rules: http://msdn.microsoft.com/en-us/library/aa370859%28v\=VS.85%29.aspx .
+error.version-string-major-out-of-range=Major version must be in the range [0, 255].
+error.version-string-build-out-of-range=Build part of version must be in the range [0, 65535].
+error.version-string-minor-out-of-range=Minor version must be in the range [0, 255].
+error.version-string-part-not-number=Failed to convert version component to int.
+error.cannot-walk-directory=Can not walk [{0}] - it is not a valid directory.
+error.version-swap=Failed to update version information for {0}.
+error.version-compare=Error: Failed to compare version {0} with {1}.
+
+message.result-dir=Result application bundle: {0}.
+message.icon-not-ico=The specified icon "{0}" is not an ICO file and will not be used. The default icon will be used in it's place.
+message.multiple-launchers=Multiple launchers found in predefined app-image. {0} will be used as primary launcher.
+message.potential.windows.defender.issue=Warning: Windows Defender may prevent jpackage from functioning. If there is an issue, it can be addressed by either disabling realtime monitoring, or adding an exclusion for the directory "{0}".
+message.tool-wrong-version=Detected [{0}] version {1} but version {2} is required.
+message.outputting-to-location=Generating EXE for installer to: {0}.
+message.output-location=Installer (.exe) saved to: {0}
+message.tool-version=Detected [{0}] version [{1}].
+message.one-shortcut-required=At least one type of shortcut is required. Enabling menu shortcut.
+message.running-wsh-script=Running WSH script on application image [{0}].
+message.iscc-file-string=InnoSetup compiler set to {0}.
+message.creating-association-with-null-extension=Creating association with null extension.
+message.wrong-tool-version=Detected [{0}] version {1} but version {2} is required.
+message.version-string-too-many-components=Version sting may have up to 3 components - major.minor.build .
+message.truncating.id=Truncating Application ID to 126 chars for Inno Setup.
+message.use-wix36-features=WiX 3.6 detected. Enabling advanced cleanup action.
+message.generated-product-guid=Generated product GUID: {0}.
+message.preparing-msi-config=Preparing MSI config: {0}.
+message.generating-msi=Generating MSI: {0}.
+message.light-file-string=WiX light tool set to {0}.
+message.candle-file-string=WiX candle tool set to {0}.
+message.invalid.install.dir=Warning: Invalid install directory {0}. Install directory should be a relative sub-path under the default installation location such as "Program Files". Defaulting to application name "{1}".
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources_zh_CN.properties Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,82 @@
+#
+# Copyright (c) 2017, 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.
+#
+#
+
+app.bundler.name=Windows Application Image
+exe.bundler.name=EXE Installer
+msi.bundler.name=MSI Installer
+
+param.menu-group.default=Unknown
+
+resource.executable-properties-template=Template for creating executable properties file.
+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_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.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.
+error.copyright-is-too-long.advice=Provide a copyright string shorter than 100 characters.
+error.no-wix-tools=Can not find WiX tools (light.exe, candle.exe).
+error.no-wix-tools.advice=Download WiX 3.0 or later from http://wix.sf.net and add it to the PATH.
+error.version-string-wrong-format=Version string is not compatible with MSI rules [{0}].
+error.version-string-wrong-format.advice=Set the bundler argument "{0}" according to these rules: http://msdn.microsoft.com/en-us/library/aa370859%28v\=VS.85%29.aspx .
+error.version-string-major-out-of-range=Major version must be in the range [0, 255].
+error.version-string-build-out-of-range=Build part of version must be in the range [0, 65535].
+error.version-string-minor-out-of-range=Minor version must be in the range [0, 255].
+error.version-string-part-not-number=Failed to convert version component to int.
+error.cannot-walk-directory=Can not walk [{0}] - it is not a valid directory.
+error.version-swap=Failed to update version information for {0}.
+error.version-compare=Error: Failed to compare version {0} with {1}.
+
+message.result-dir=Result application bundle: {0}.
+message.icon-not-ico=The specified icon "{0}" is not an ICO file and will not be used. The default icon will be used in it's place.
+message.multiple-launchers=Multiple launchers found in predefined app-image. {0} will be used as primary launcher.
+message.potential.windows.defender.issue=Warning: Windows Defender may prevent jpackage from functioning. If there is an issue, it can be addressed by either disabling realtime monitoring, or adding an exclusion for the directory "{0}".
+message.tool-wrong-version=Detected [{0}] version {1} but version {2} is required.
+message.outputting-to-location=Generating EXE for installer to: {0}.
+message.output-location=Installer (.exe) saved to: {0}
+message.tool-version=Detected [{0}] version [{1}].
+message.one-shortcut-required=At least one type of shortcut is required. Enabling menu shortcut.
+message.running-wsh-script=Running WSH script on application image [{0}].
+message.iscc-file-string=InnoSetup compiler set to {0}.
+message.creating-association-with-null-extension=Creating association with null extension.
+message.wrong-tool-version=Detected [{0}] version {1} but version {2} is required.
+message.version-string-too-many-components=Version sting may have up to 3 components - major.minor.build .
+message.truncating.id=Truncating Application ID to 126 chars for Inno Setup.
+message.use-wix36-features=WiX 3.6 detected. Enabling advanced cleanup action.
+message.generated-product-guid=Generated product GUID: {0}.
+message.preparing-msi-config=Preparing MSI config: {0}.
+message.generating-msi=Generating MSI: {0}.
+message.light-file-string=WiX light tool set to {0}.
+message.candle-file-string=WiX candle tool set to {0}.
+message.invalid.install.dir=Warning: Invalid install directory {0}. Install directory should be a relative sub-path under the default installation location such as "Program Files". Defaulting to application name "{1}".
+
Binary file src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/icon_inno_setup.bmp has changed
Binary file src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/javalogo_white_48.ico has changed
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/main.wxs Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,112 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi"
+ xmlns:util="http://schemas.microsoft.com/wix/UtilExtension">
+
+ <?ifdef JpIsSystemWide ?>
+ <?define JpInstallScope="perMachine"?>
+ <?define JpRegistryRoot="HKLM"?>
+ <?else?>
+ <?define JpInstallScope="perUser"?>
+ <?define JpRegistryRoot="HKCU"?>
+ <?endif?>
+
+ <Product Id="$(var.JpProductCode)" Name="$(var.JpAppName)"
+ Language="1033" Version="$(var.JpAppVersion)"
+ Manufacturer="$(var.JpAppVendor)"
+ UpgradeCode="$(var.JpProductUpgradeCode)">
+ <Package Description="$(var.JpAppDescription)"
+ Manufacturer="$(var.JpAppVendor)"
+ InstallerVersion="200" Compressed="yes"
+ InstallScope="$(var.JpInstallScope)" Platform="x64"/>
+ <Media Id="1" Cabinet="simple.cab" EmbedCab="yes" />
+
+ <?ifdef JpAllowDowngrades ?>
+ <MajorUpgrade AllowDowngrades="yes"/>
+ <?endif?>
+
+ <!-- We use RemoveFolderEx to ensure application folder is fully
+ removed on uninstall. Including files created outside of MSI
+ after application had been installed (e.g. on AU or user state).
+
+ However, RemoveFolderEx is only available in WiX 3.6,
+ we will comment it out if we running older WiX.
+
+ RemoveFolderEx requires that we "remember" the path for uninstall.
+ Read the path value and set the APPLICATIONFOLDER property with the value.
+ -->
+ <Property Id="APPLICATIONFOLDER">
+ <RegistrySearch Key="SOFTWARE\$(var.JpAppVendor)\$(var.JpAppName)"
+ Root="$(var.JpRegistryRoot)" Type="raw" Win64="yes"
+ Id="APPLICATIONFOLDER_REGSEARCH" Name="Path" />
+ </Property>
+ <DirectoryRef Id="APPLICATIONFOLDER">
+ <Component Id="CleanupMainApplicationFolder" Guid="*" Win64="yes">
+ <RegistryValue Root="$(var.JpRegistryRoot)"
+ Key="SOFTWARE\$(var.JpAppVendor)\$(var.JpAppName)"
+ Name="Path" Type="string" Value="[APPLICATIONFOLDER]"
+ KeyPath="yes" />
+ <!-- We need to use APPLICATIONFOLDER variable here or RemoveFolderEx
+ will not remove on "install". But only if WiX 3.6 is used. -->
+ <?ifdef JpWixVersion36OrNewer ?>
+ <util:RemoveFolderEx On="uninstall" Property="APPLICATIONFOLDER" />
+ <?endif?>
+ </Component>
+ </DirectoryRef>
+
+ <?include $(var.JpConfigDir)/bundle.wxi ?>
+
+ <?ifdef JpInstallDirChooser ?>
+ <Binary Id="JpCaDll" SourceFile="wixhelper.dll"/>
+ <CustomAction Id="JpCheckInstallDir" BinaryKey="JpCaDll" DllEntry="CheckInstallDir" />
+ <?endif?>
+
+ <UI>
+ <?ifdef JpInstallDirChooser ?>
+ <Dialog Id="JpInvalidInstallDir" Width="300" Height="85" Title="[ProductName] Setup" NoMinimize="yes">
+ <Control Id="JpInvalidInstallDirYes" Type="PushButton" X="100" Y="55" Width="50" Height="15" Default="no" Cancel="no" Text="Yes">
+ <Publish Event="NewDialog" Value="VerifyReadyDlg">1</Publish>
+ </Control>
+ <Control Id="JpInvalidInstallDirNo" Type="PushButton" X="150" Y="55" Width="50" Height="15" Default="yes" Cancel="yes" Text="No">
+ <Publish Event="NewDialog" Value="InstallDirDlg">1</Publish>
+ </Control>
+ <Control Id="Text" Type="Text" X="25" Y="15" Width="250" Height="30" TabSkip="no">
+ <Text>!(loc.message.install.dir.exist)</Text>
+ </Control>
+ </Dialog>
+
+ <!--
+ Run WixUI_InstallDir dialog in the default install directory.
+ -->
+ <Property Id="WIXUI_INSTALLDIR" Value="APPLICATIONFOLDER"/>
+ <UIRef Id="WixUI_InstallDir" />
+
+ <Publish Dialog="InstallDirDlg" Control="Next" Event="DoAction" Value="JpCheckInstallDir" Order="3">1</Publish>
+ <Publish Dialog="InstallDirDlg" Control="Next" Event="NewDialog" Value="JpInvalidInstallDir" Order="5">INSTALLDIR_VALID="0"</Publish>
+ <Publish Dialog="InstallDirDlg" Control="Next" Event="NewDialog" Value="VerifyReadyDlg" Order="5">INSTALLDIR_VALID="1"</Publish>
+
+ <?ifndef JpLicenseRtf ?>
+ <!--
+ No license file provided.
+ Override the dialog sequence in built-in dialog set "WixUI_InstallDir"
+ to exclude license dialog.
+ -->
+ <Publish Dialog="WelcomeDlg" Control="Next" Event="NewDialog" Value="InstallDirDlg" Order="2">1</Publish>
+ <Publish Dialog="InstallDirDlg" Control="Back" Event="NewDialog" Value="WelcomeDlg" Order="2">1</Publish>
+ <?endif?>
+
+ <?else?>
+
+ <?ifdef JpLicenseRtf ?>
+ <UIRef Id="WixUI_Minimal" />
+ <?endif?>
+
+ <?endif?>
+ </UI>
+
+ <?ifdef JpLicenseRtf ?>
+ <WixVariable Id="WixUILicenseRtf" Value="$(var.JpLicenseRtf)"/>
+ <?endif?>
+
+ <?include $(var.JpConfigDir)/icons.wxi ?>
+ </Product>
+</Wix>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/template.iss Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,74 @@
+;This file will be executed next to the application bundle image
+;I.e. current directory will contain folder INSTALLER_NAME with application files
+[Setup]
+AppId=PRODUCT_APP_IDENTIFIER
+AppName=INSTALLER_NAME
+AppVersion=APPLICATION_VERSION
+AppVerName=INSTALLER_NAME APPLICATION_VERSION
+AppPublisher=APPLICATION_VENDOR
+AppComments=APPLICATION_DESCRIPTION
+AppCopyright=APPLICATION_COPYRIGHT
+VersionInfoVersion=APPLICATION_VERSION
+VersionInfoDescription=APPLICATION_DESCRIPTION
+DefaultDirName=APPLICATION_INSTALL_ROOT\INSTALL_DIR
+DisableStartupPrompt=Yes
+DisableDirPage=DISABLE_DIR_PAGE
+DisableProgramGroupPage=Yes
+DisableReadyPage=Yes
+DisableFinishedPage=Yes
+DisableWelcomePage=Yes
+DefaultGroupName=APPLICATION_GROUP
+;Optional License
+LicenseFile=APPLICATION_LICENSE_FILE
+;WinXP or above
+MinVersion=0,5.1
+OutputBaseFilename=INSTALLER_FILE_NAME
+Compression=lzma
+SolidCompression=yes
+PrivilegesRequired=APPLICATION_INSTALL_PRIVILEGE
+SetupIconFile=INSTALLER_NAME\bin\LAUNCHER_NAME.ico
+UninstallDisplayIcon={app}\LAUNCHER_NAME.ico
+UninstallDisplayName=INSTALLER_NAME
+WizardImageStretch=No
+WizardSmallImageFile=INSTALLER_NAME-setup-icon.bmp
+ArchitecturesInstallIn64BitMode=ARCHITECTURE_BIT_MODE
+FILE_ASSOCIATIONS
+
+[Languages]
+Name: "english"; MessagesFile: "compiler:Default.isl"
+
+[Files]
+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}\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}\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}\bin\RUN_FILENAME.exe "; Parameters: "-uninstall -svcName INSTALLER_NAME STOP_ON_UNINSTALL"; Check: APPLICATION_SERVICE()
+
+[Code]
+function returnTrue(): Boolean;
+begin
+ Result := True;
+end;
+
+function returnFalse(): Boolean;
+begin
+ Result := False;
+end;
+
+function InitializeSetup(): Boolean;
+begin
+// Possible future improvements:
+// if version less or same => just launch app
+// if upgrade => check if same app is running and wait for it to exit
+ Result := True;
+end;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/template.jre.iss Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,57 @@
+;This file will be executed next to the application bundle image
+;I.e. current directory will contain folder INSTALLER_NAME with application files
+[Setup]
+AppId=PRODUCT_APP_IDENTIFIER
+AppName=INSTALLER_NAME
+AppVersion=APPLICATION_VERSION
+AppVerName=INSTALLER_NAME APPLICATION_VERSION
+AppPublisher=APPLICATION_VENDOR
+AppComments=APPLICATION_DESCRIPTION
+AppCopyright=APPLICATION_COPYRIGHT
+VersionInfoVersion=APPLICATION_VERSION
+VersionInfoDescription=APPLICATION_DESCRIPTION
+DefaultDirName=APPLICATION_INSTALL_ROOT\INSTALLER_NAME
+DisableStartupPrompt=Yes
+DisableDirPage=DISABLE_DIR_PAGE
+DisableProgramGroupPage=Yes
+DisableReadyPage=Yes
+DisableFinishedPage=Yes
+DisableWelcomePage=Yes
+DefaultGroupName=APPLICATION_GROUP
+;Optional License
+LicenseFile=APPLICATION_LICENSE_FILE
+;WinXP or above
+MinVersion=0,5.1
+OutputBaseFilename=INSTALLER_FILE_NAME
+Compression=lzma
+SolidCompression=yes
+PrivilegesRequired=APPLICATION_INSTALL_PRIVILEGE
+SetupIconFile=
+UninstallDisplayIcon=
+UninstallDisplayName=INSTALLER_NAME
+WizardImageStretch=No
+WizardSmallImageFile=INSTALLER_NAME-setup-icon.bmp
+ArchitecturesInstallIn64BitMode=ARCHITECTURE_BIT_MODE
+FILE_ASSOCIATIONS
+
+[Languages]
+Name: "english"; MessagesFile: "compiler:Default.isl"
+
+[Files]
+Source: "APPLICATION_IMAGE\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs
+
+[Code]
+function returnTrue(): Boolean;
+begin
+ Result := True;
+end;
+
+function returnFalse(): Boolean;
+begin
+ Result := False;
+end;
+
+function InitializeSetup(): Boolean;
+begin
+ Result := True;
+end;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/windows/classes/module-info.java.extra Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+
+provides jdk.jpackage.internal.Bundler with
+ jdk.jpackage.internal.WinAppBundler,
+ jdk.jpackage.internal.WinExeBundler,
+ jdk.jpackage.internal.WinMsiBundler;
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/windows/native/jpackageapplauncher/WinLauncher.cpp Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,98 @@
+/*
+ * 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.
+ */
+
+#include <Windows.h>
+#include <Shellapi.h>
+#include <locale.h>
+#include <tchar.h>
+#include <string>
+
+#define JPACKAGE_LIBRARY TEXT("applauncher.dll")
+
+typedef bool (*start_launcher)(int argc, TCHAR* argv[]);
+typedef void (*stop_launcher)();
+
+std::wstring GetTitle() {
+ std::wstring result;
+ wchar_t buffer[MAX_PATH];
+ GetModuleFileName(NULL, buffer, MAX_PATH - 1);
+ buffer[MAX_PATH - 1] = '\0';
+ result = buffer;
+ size_t slash = result.find_last_of('\\');
+
+ if (slash != std::wstring::npos)
+ result = result.substr(slash + 1, result.size() - slash - 1);
+
+ return result;
+}
+
+#ifdef LAUNCHERC
+int main(int argc0, char *argv0[]) {
+#else // LAUNCHERC
+int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
+ LPTSTR lpCmdLine, int nCmdShow) {
+#endif // LAUNCHERC
+ int result = 1;
+ TCHAR **argv;
+ int argc;
+
+ // [RT-31061] otherwise UI can be left in back of other windows.
+ ::AllowSetForegroundWindow(ASFW_ANY);
+
+ ::setlocale(LC_ALL, "en_US.utf8");
+ argv = CommandLineToArgvW(GetCommandLine(), &argc);
+
+ HMODULE library = ::LoadLibrary(JPACKAGE_LIBRARY);
+
+ if (library == NULL) {
+ std::wstring title = GetTitle();
+ std::wstring description = std::wstring(JPACKAGE_LIBRARY)
+ + std::wstring(TEXT(" not found."));
+ MessageBox(NULL, description.data(),
+ title.data(), MB_ICONERROR | MB_OK);
+ }
+ else {
+ start_launcher start =
+ (start_launcher)GetProcAddress(library, "start_launcher");
+ stop_launcher stop =
+ (stop_launcher)GetProcAddress(library, "stop_launcher");
+
+ if (start != NULL && stop != NULL) {
+ if (start(argc, argv) == true) {
+ result = 0;
+ stop();
+ }
+ }
+
+ ::FreeLibrary(library);
+ }
+
+ if (argv != NULL) {
+ LocalFree(argv);
+ }
+
+ return result;
+}
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/windows/native/libapplauncher/DllMain.cpp Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 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.
+ */
+
+#include <windows.h>
+
+extern "C" {
+
+ BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason,
+ LPVOID lpvReserved) {
+ return true;
+ }
+}
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/windows/native/libapplauncher/FileAttribute.h Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 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.
+ */
+
+#ifndef FILEATTRIBUTE_H
+#define FILEATTRIBUTE_H
+
+enum FileAttribute {
+ faArchive = FILE_ATTRIBUTE_ARCHIVE,
+ faCompressed = FILE_ATTRIBUTE_COMPRESSED,
+ faDevice = FILE_ATTRIBUTE_DEVICE,
+ faDirectory = FILE_ATTRIBUTE_DIRECTORY,
+ faEncrypted = FILE_ATTRIBUTE_ENCRYPTED,
+ faHidden = FILE_ATTRIBUTE_HIDDEN,
+ faNormal = FILE_ATTRIBUTE_NORMAL,
+ faNotContentIndexed = FILE_ATTRIBUTE_NOT_CONTENT_INDEXED,
+ faOffline = FILE_ATTRIBUTE_OFFLINE,
+ faSystem = FILE_ATTRIBUTE_SYSTEM,
+ faSymbolicLink = FILE_ATTRIBUTE_REPARSE_POINT,
+ faSparceFile = FILE_ATTRIBUTE_SPARSE_FILE,
+ faReadOnly = FILE_ATTRIBUTE_READONLY,
+ faTemporary = FILE_ATTRIBUTE_TEMPORARY,
+ faVirtual = FILE_ATTRIBUTE_VIRTUAL
+};
+
+#endif // FILEATTRIBUTE_H
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/windows/native/libapplauncher/FilePath.cpp Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,467 @@
+/*
+ * Copyright (c) 2014, 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.
+ */
+
+#include "FilePath.h"
+
+#include <algorithm>
+#include <list>
+#include <ShellAPI.h>
+
+bool FilePath::FileExists(const TString FileName) {
+ bool result = false;
+ WIN32_FIND_DATA FindFileData;
+ TString fileName = FixPathForPlatform(FileName);
+ HANDLE handle = FindFirstFile(fileName.data(), &FindFileData);
+
+ if (handle != INVALID_HANDLE_VALUE) {
+ if (FILE_ATTRIBUTE_DIRECTORY & FindFileData.dwFileAttributes) {
+ result = true;
+ }
+ else {
+ result = true;
+ }
+
+ FindClose(handle);
+ }
+ return result;
+}
+
+bool FilePath::DirectoryExists(const TString DirectoryName) {
+ bool result = false;
+ WIN32_FIND_DATA FindFileData;
+ TString directoryName = FixPathForPlatform(DirectoryName);
+ HANDLE handle = FindFirstFile(directoryName.data(), &FindFileData);
+
+ if (handle != INVALID_HANDLE_VALUE) {
+ if (FILE_ATTRIBUTE_DIRECTORY & FindFileData.dwFileAttributes) {
+ result = true;
+ }
+
+ FindClose(handle);
+ }
+ return result;
+}
+
+std::string GetLastErrorAsString() {
+ // Get the error message, if any.
+ DWORD errorMessageID = ::GetLastError();
+
+ if (errorMessageID == 0) {
+ return "No error message has been recorded";
+ }
+
+ LPSTR messageBuffer = NULL;
+ size_t size = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER
+ | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL, errorMessageID, MAKELANGID(LANG_NEUTRAL,
+ SUBLANG_DEFAULT), (LPSTR)&messageBuffer, 0, NULL);
+
+ std::string message(messageBuffer, size);
+
+ // Free the buffer.
+ LocalFree(messageBuffer);
+
+ return message;
+}
+
+bool FilePath::DeleteFile(const TString FileName) {
+ bool result = false;
+
+ if (FileExists(FileName) == true) {
+ TString lFileName = FixPathForPlatform(FileName);
+ FileAttributes attributes(lFileName);
+
+ if (attributes.Contains(faReadOnly) == true) {
+ attributes.Remove(faReadOnly);
+ }
+
+ result = ::DeleteFile(lFileName.data()) == TRUE;
+ }
+
+ return result;
+}
+
+bool FilePath::DeleteDirectory(const TString DirectoryName) {
+ bool result = false;
+
+ if (DirectoryExists(DirectoryName) == true) {
+ SHFILEOPSTRUCTW fos = {0};
+ TString directoryName = FixPathForPlatform(DirectoryName);
+ DynamicBuffer<TCHAR> lDirectoryName(directoryName.size() + 2);
+ if (lDirectoryName.GetData() == NULL) {
+ return false;
+ }
+ memcpy(lDirectoryName.GetData(), directoryName.data(), (directoryName.size() + 2) * sizeof(TCHAR));
+ lDirectoryName[directoryName.size() + 1] = NULL;
+ // Double null terminate for SHFileOperation.
+
+ // Delete the folder and everything inside.
+ fos.wFunc = FO_DELETE;
+ fos.pFrom = lDirectoryName.GetData();
+ fos.fFlags = FOF_NO_UI;
+ result = SHFileOperation(&fos) == 0;
+ }
+
+ return result;
+}
+
+TString FilePath::IncludeTrailingSeparator(const TString value) {
+ TString result = value;
+
+ if (value.size() > 0) {
+ TString::iterator i = result.end();
+ i--;
+
+ if (*i != TRAILING_PATHSEPARATOR) {
+ result += TRAILING_PATHSEPARATOR;
+ }
+ }
+
+ return result;
+}
+
+TString FilePath::IncludeTrailingSeparator(const char* value) {
+ TString lvalue = PlatformString(value).toString();
+ return IncludeTrailingSeparator(lvalue);
+}
+
+TString FilePath::IncludeTrailingSeparator(const wchar_t* value) {
+ TString lvalue = PlatformString(value).toString();
+ return IncludeTrailingSeparator(lvalue);
+}
+
+TString FilePath::ExtractFilePath(TString Path) {
+ TString result;
+ size_t slash = Path.find_last_of(TRAILING_PATHSEPARATOR);
+ if (slash != TString::npos)
+ result = Path.substr(0, slash);
+ return result;
+}
+
+TString FilePath::ExtractFileExt(TString Path) {
+ TString result;
+ size_t dot = Path.find_last_of('.');
+
+ if (dot != TString::npos) {
+ result = Path.substr(dot, Path.size() - dot);
+ }
+
+ return result;
+}
+
+TString FilePath::ExtractFileName(TString Path) {
+ TString result;
+
+ size_t slash = Path.find_last_of(TRAILING_PATHSEPARATOR);
+ if (slash != TString::npos)
+ result = Path.substr(slash + 1, Path.size() - slash - 1);
+
+ return result;
+}
+
+TString FilePath::ChangeFileExt(TString Path, TString Extension) {
+ TString result;
+ size_t dot = Path.find_last_of('.');
+
+ if (dot != TString::npos) {
+ result = Path.substr(0, dot) + Extension;
+ }
+
+ if (result.empty() == true) {
+ result = Path;
+ }
+
+ return result;
+}
+
+TString FilePath::FixPathForPlatform(TString Path) {
+ TString result = Path;
+ std::replace(result.begin(), result.end(),
+ BAD_TRAILING_PATHSEPARATOR, TRAILING_PATHSEPARATOR);
+ // The maximum path that does not require long path prefix. On Windows the
+ // maximum path is 260 minus 1 (NUL) but for directories it is 260 minus
+ // 12 minus 1 (to allow for the creation of a 8.3 file in the directory).
+ const int maxPath = 247;
+ if (result.length() > maxPath &&
+ result.find(_T("\\\\?\\")) == TString::npos &&
+ result.find(_T("\\\\?\\UNC")) == TString::npos) {
+ const TString prefix(_T("\\\\"));
+ if (!result.compare(0, prefix.size(), prefix)) {
+ // UNC path, converting to UNC path in long notation
+ result = _T("\\\\?\\UNC") + result.substr(1, result.length());
+ } else {
+ // converting to non-UNC path in long notation
+ result = _T("\\\\?\\") + result;
+ }
+ }
+ return result;
+}
+
+TString FilePath::FixPathSeparatorForPlatform(TString Path) {
+ TString result = Path;
+ std::replace(result.begin(), result.end(),
+ BAD_PATH_SEPARATOR, PATH_SEPARATOR);
+ return result;
+}
+
+TString FilePath::PathSeparator() {
+ TString result;
+ result = PATH_SEPARATOR;
+ return result;
+}
+
+bool FilePath::CreateDirectory(TString Path, bool ownerOnly) {
+ bool result = false;
+
+ std::list<TString> paths;
+ TString lpath = Path;
+
+ while (lpath.empty() == false && DirectoryExists(lpath) == false) {
+ paths.push_front(lpath);
+ lpath = ExtractFilePath(lpath);
+ }
+
+ for (std::list<TString>::iterator iterator = paths.begin();
+ iterator != paths.end(); iterator++) {
+ lpath = *iterator;
+
+ if (_wmkdir(lpath.data()) == 0) {
+ result = true;
+ } else {
+ result = false;
+ break;
+ }
+ }
+
+ return result;
+}
+
+void FilePath::ChangePermissions(TString FileName, bool ownerOnly) {
+}
+
+#include <algorithm>
+
+FileAttributes::FileAttributes(const TString FileName, bool FollowLink) {
+ FFileName = FileName;
+ FFollowLink = FollowLink;
+ ReadAttributes();
+}
+
+bool FileAttributes::WriteAttributes() {
+ bool result = false;
+
+ DWORD attributes = 0;
+
+ for (std::vector<FileAttribute>::const_iterator iterator =
+ FAttributes.begin();
+ iterator != FAttributes.end(); iterator++) {
+ switch (*iterator) {
+ case faArchive: {
+ attributes = attributes & FILE_ATTRIBUTE_ARCHIVE;
+ break;
+ }
+ case faCompressed: {
+ attributes = attributes & FILE_ATTRIBUTE_COMPRESSED;
+ break;
+ }
+ case faDevice: {
+ attributes = attributes & FILE_ATTRIBUTE_DEVICE;
+ break;
+ }
+ case faDirectory: {
+ attributes = attributes & FILE_ATTRIBUTE_DIRECTORY;
+ break;
+ }
+ case faEncrypted: {
+ attributes = attributes & FILE_ATTRIBUTE_ENCRYPTED;
+ break;
+ }
+ case faHidden: {
+ attributes = attributes & FILE_ATTRIBUTE_HIDDEN;
+ break;
+ }
+ case faNormal: {
+ attributes = attributes & FILE_ATTRIBUTE_NORMAL;
+ break;
+ }
+ case faNotContentIndexed: {
+ attributes = attributes & FILE_ATTRIBUTE_NOT_CONTENT_INDEXED;
+ break;
+ }
+ case faOffline: {
+ attributes = attributes & FILE_ATTRIBUTE_OFFLINE;
+ break;
+ }
+ case faSystem: {
+ attributes = attributes & FILE_ATTRIBUTE_SYSTEM;
+ break;
+ }
+ case faSymbolicLink: {
+ attributes = attributes & FILE_ATTRIBUTE_REPARSE_POINT;
+ break;
+ }
+ case faSparceFile: {
+ attributes = attributes & FILE_ATTRIBUTE_SPARSE_FILE;
+ break;
+ }
+ case faReadOnly: {
+ attributes = attributes & FILE_ATTRIBUTE_READONLY;
+ break;
+ }
+ case faTemporary: {
+ attributes = attributes & FILE_ATTRIBUTE_TEMPORARY;
+ break;
+ }
+ case faVirtual: {
+ attributes = attributes & FILE_ATTRIBUTE_VIRTUAL;
+ break;
+ }
+ }
+ }
+
+ if (::SetFileAttributes(FFileName.data(), attributes) != 0) {
+ result = true;
+ }
+
+ return result;
+}
+
+#define S_ISRUSR(m) (((m) & S_IRWXU) == S_IRUSR)
+#define S_ISWUSR(m) (((m) & S_IRWXU) == S_IWUSR)
+#define S_ISXUSR(m) (((m) & S_IRWXU) == S_IXUSR)
+
+#define S_ISRGRP(m) (((m) & S_IRWXG) == S_IRGRP)
+#define S_ISWGRP(m) (((m) & S_IRWXG) == S_IWGRP)
+#define S_ISXGRP(m) (((m) & S_IRWXG) == S_IXGRP)
+
+#define S_ISROTH(m) (((m) & S_IRWXO) == S_IROTH)
+#define S_ISWOTH(m) (((m) & S_IRWXO) == S_IWOTH)
+#define S_ISXOTH(m) (((m) & S_IRWXO) == S_IXOTH)
+
+bool FileAttributes::ReadAttributes() {
+ bool result = false;
+
+ DWORD attributes = ::GetFileAttributes(FFileName.data());
+
+ if (attributes != INVALID_FILE_ATTRIBUTES) {
+ result = true;
+
+ if (attributes | FILE_ATTRIBUTE_ARCHIVE) {
+ FAttributes.push_back(faArchive);
+ }
+ if (attributes | FILE_ATTRIBUTE_COMPRESSED) {
+ FAttributes.push_back(faCompressed);
+ }
+ if (attributes | FILE_ATTRIBUTE_DEVICE) {
+ FAttributes.push_back(faDevice);
+ }
+ if (attributes | FILE_ATTRIBUTE_DIRECTORY) {
+ FAttributes.push_back(faDirectory);
+ }
+ if (attributes | FILE_ATTRIBUTE_ENCRYPTED) {
+ FAttributes.push_back(faEncrypted);
+ }
+ if (attributes | FILE_ATTRIBUTE_HIDDEN) {
+ FAttributes.push_back(faHidden);
+ }
+ if (attributes | FILE_ATTRIBUTE_NORMAL) {
+ FAttributes.push_back(faNormal);
+ }
+ if (attributes | FILE_ATTRIBUTE_NOT_CONTENT_INDEXED) {
+ FAttributes.push_back(faNotContentIndexed);
+ }
+ if (attributes | FILE_ATTRIBUTE_SYSTEM) {
+ FAttributes.push_back(faSystem);
+ }
+ if (attributes | FILE_ATTRIBUTE_OFFLINE) {
+ FAttributes.push_back(faOffline);
+ }
+ if (attributes | FILE_ATTRIBUTE_REPARSE_POINT) {
+ FAttributes.push_back(faSymbolicLink);
+ }
+ if (attributes | FILE_ATTRIBUTE_SPARSE_FILE) {
+ FAttributes.push_back(faSparceFile);
+ }
+ if (attributes | FILE_ATTRIBUTE_READONLY ) {
+ FAttributes.push_back(faReadOnly);
+ }
+ if (attributes | FILE_ATTRIBUTE_TEMPORARY) {
+ FAttributes.push_back(faTemporary);
+ }
+ if (attributes | FILE_ATTRIBUTE_VIRTUAL) {
+ FAttributes.push_back(faVirtual);
+ }
+ }
+
+ return result;
+}
+
+bool FileAttributes::Valid(const FileAttribute Value) {
+ bool result = false;
+
+ switch (Value) {
+ case faHidden:
+ case faReadOnly: {
+ result = true;
+ break;
+ }
+ default:
+ break;
+ }
+
+ return result;
+}
+
+void FileAttributes::Append(FileAttribute Value) {
+ if (Valid(Value) == true) {
+ FAttributes.push_back(Value);
+ WriteAttributes();
+ }
+}
+
+bool FileAttributes::Contains(FileAttribute Value) {
+ bool result = false;
+
+ std::vector<FileAttribute>::const_iterator iterator =
+ std::find(FAttributes.begin(), FAttributes.end(), Value);
+
+ if (iterator != FAttributes.end()) {
+ result = true;
+ }
+
+ return result;
+}
+
+void FileAttributes::Remove(FileAttribute Value) {
+ if (Valid(Value) == true) {
+ std::vector<FileAttribute>::iterator iterator =
+ std::find(FAttributes.begin(), FAttributes.end(), Value);
+
+ if (iterator != FAttributes.end()) {
+ FAttributes.erase(iterator);
+ WriteAttributes();
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/windows/native/libapplauncher/PlatformDefs.h Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 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.
+ */
+
+#ifndef PLATFORM_DEFS_H
+#define PLATFORM_DEFS_H
+
+// Define Windows compatibility requirements XP or later
+#define WINVER 0x0600
+#define _WIN32_WINNT 0x0600
+
+#include <Windows.h>
+#include <tchar.h>
+#include <shlobj.h>
+#include <direct.h>
+#include <process.h>
+#include <malloc.h>
+#include <string>
+
+using namespace std;
+
+#ifndef WINDOWS
+#define WINDOWS
+#endif
+
+typedef std::wstring TString;
+#define StringLength wcslen
+
+#define TRAILING_PATHSEPARATOR '\\'
+#define BAD_TRAILING_PATHSEPARATOR '/'
+#define PATH_SEPARATOR ';'
+#define BAD_PATH_SEPARATOR ':'
+
+typedef ULONGLONG TPlatformNumber;
+typedef DWORD TProcessID;
+
+typedef void* Module;
+typedef void* Procedure;
+
+#endif // PLATFORM_DEFS_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/windows/native/libapplauncher/WindowsPlatform.cpp Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,770 @@
+/*
+ * Copyright (c) 2014, 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.
+ */
+
+#include "Platform.h"
+
+#include "JavaVirtualMachine.h"
+#include "WindowsPlatform.h"
+#include "Package.h"
+#include "Helpers.h"
+#include "PlatformString.h"
+#include "Macros.h"
+
+#include <map>
+#include <vector>
+#include <regex>
+#include <fstream>
+#include <locale>
+#include <codecvt>
+
+using namespace std;
+
+#define WINDOWS_JPACKAGE_TMP_DIR \
+ L"\\AppData\\Local\\Java\\JPackage\\tmp"
+
+class Registry {
+private:
+ HKEY FKey;
+ HKEY FOpenKey;
+ bool FOpen;
+
+public:
+
+ Registry(HKEY Key) {
+ FOpen = false;
+ FKey = Key;
+ }
+
+ ~Registry() {
+ Close();
+ }
+
+ void Close() {
+ if (FOpen == true) {
+ RegCloseKey(FOpenKey);
+ }
+ }
+
+ bool Open(TString SubKey) {
+ bool result = false;
+ Close();
+
+ if (RegOpenKeyEx(FKey, SubKey.data(), 0, KEY_READ, &FOpenKey) ==
+ ERROR_SUCCESS) {
+ result = true;
+ }
+
+ return result;
+ }
+
+ std::list<TString> GetKeys() {
+ std::list<TString> result;
+ DWORD count;
+
+ if (RegQueryInfoKey(FOpenKey, NULL, NULL, NULL, NULL, NULL, NULL,
+ &count, NULL, NULL, NULL, NULL) == ERROR_SUCCESS) {
+
+ DWORD length = 255;
+ DynamicBuffer<TCHAR> buffer(length);
+ if (buffer.GetData() == NULL) {
+ return result;
+ }
+
+ for (unsigned int index = 0; index < count; index++) {
+ buffer.Zero();
+ DWORD status = RegEnumValue(FOpenKey, index, buffer.GetData(),
+ &length, NULL, NULL, NULL, NULL);
+
+ while (status == ERROR_MORE_DATA) {
+ length = length * 2;
+ if (!buffer.Resize(length)) {
+ return result;
+ }
+ status = RegEnumValue(FOpenKey, index, buffer.GetData(),
+ &length, NULL, NULL, NULL, NULL);
+ }
+
+ if (status == ERROR_SUCCESS) {
+ TString value = buffer.GetData();
+ result.push_back(value);
+ }
+ }
+ }
+
+ return result;
+ }
+
+ TString ReadString(TString Name) {
+ TString result;
+ DWORD length;
+ DWORD dwRet;
+ DynamicBuffer<wchar_t> buffer(0);
+ length = 0;
+
+ dwRet = RegQueryValueEx(FOpenKey, Name.data(), NULL, NULL, NULL,
+ &length);
+ if (dwRet == ERROR_MORE_DATA || dwRet == 0) {
+ if (!buffer.Resize(length + 1)) {
+ return result;
+ }
+ dwRet = RegQueryValueEx(FOpenKey, Name.data(), NULL, NULL,
+ (LPBYTE) buffer.GetData(), &length);
+ result = buffer.GetData();
+ }
+
+ return result;
+ }
+};
+
+WindowsPlatform::WindowsPlatform(void) : Platform() {
+ FMainThread = ::GetCurrentThreadId();
+}
+
+WindowsPlatform::~WindowsPlatform(void) {
+}
+
+TString WindowsPlatform::GetPackageAppDirectory() {
+ return FilePath::IncludeTrailingSeparator(
+ GetPackageRootDirectory()) + _T("app");
+}
+
+TString WindowsPlatform::GetPackageLauncherDirectory() {
+ return FilePath::IncludeTrailingSeparator(
+ GetPackageRootDirectory()) + _T("bin");
+}
+
+TString WindowsPlatform::GetPackageRuntimeBinDirectory() {
+ return FilePath::IncludeTrailingSeparator(GetPackageRootDirectory()) + _T("runtime\\bin");
+}
+
+TCHAR* WindowsPlatform::ConvertStringToFileSystemString(TCHAR* Source,
+ bool &release) {
+ // Not Implemented.
+ return NULL;
+}
+
+TCHAR* WindowsPlatform::ConvertFileSystemStringToString(TCHAR* Source,
+ bool &release) {
+ // Not Implemented.
+ return NULL;
+}
+
+void WindowsPlatform::SetCurrentDirectory(TString Value) {
+ _wchdir(Value.data());
+}
+
+TString WindowsPlatform::GetPackageRootDirectory() {
+ TString result;
+ TString filename = GetModuleFileName();
+ 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() {
+ TString result;
+ TCHAR path[MAX_PATH];
+
+ if (SHGetFolderPath(NULL, CSIDL_APPDATA, NULL, 0, path) == S_OK) {
+ result = path;
+ }
+
+ return result;
+}
+
+TString WindowsPlatform::GetAppName() {
+ TString result = GetModuleFileName();
+ result = FilePath::ExtractFileName(result);
+ result = FilePath::ChangeFileExt(result, _T(""));
+ return result;
+}
+
+void WindowsPlatform::ShowMessage(TString title, TString description) {
+ MessageBox(NULL, description.data(),
+ !title.empty() ? title.data() : description.data(),
+ MB_ICONERROR | MB_OK);
+}
+
+void WindowsPlatform::ShowMessage(TString description) {
+ TString appname = GetModuleFileName();
+ appname = FilePath::ExtractFileName(appname);
+ MessageBox(NULL, description.data(), appname.data(), MB_ICONERROR | MB_OK);
+}
+
+MessageResponse WindowsPlatform::ShowResponseMessage(TString title,
+ TString description) {
+ MessageResponse result = mrCancel;
+
+ if (::MessageBox(NULL, description.data(), title.data(), MB_OKCANCEL) ==
+ IDOK) {
+ result = mrOK;
+ }
+
+ return result;
+}
+
+TString WindowsPlatform::GetBundledJavaLibraryFileName(TString RuntimePath) {
+ TString result = FilePath::IncludeTrailingSeparator(RuntimePath) +
+ _T("jre\\bin\\jli.dll");
+
+ if (FilePath::FileExists(result) == false) {
+ result = FilePath::IncludeTrailingSeparator(RuntimePath) +
+ _T("bin\\jli.dll");
+ }
+
+ return result;
+}
+
+ISectionalPropertyContainer* WindowsPlatform::GetConfigFile(TString FileName) {
+ IniFile *result = new IniFile();
+ if (result == NULL) {
+ return NULL;
+ }
+
+ result->LoadFromFile(FileName);
+
+ return result;
+}
+
+TString WindowsPlatform::GetModuleFileName() {
+ TString result;
+ DynamicBuffer<wchar_t> buffer(MAX_PATH);
+ if (buffer.GetData() == NULL) {
+ return result;
+ }
+
+ ::GetModuleFileName(NULL, buffer.GetData(),
+ static_cast<DWORD> (buffer.GetSize()));
+
+ while (ERROR_INSUFFICIENT_BUFFER == GetLastError()) {
+ if (!buffer.Resize(buffer.GetSize() * 2)) {
+ return result;
+ }
+ ::GetModuleFileName(NULL, buffer.GetData(),
+ static_cast<DWORD> (buffer.GetSize()));
+ }
+
+ result = buffer.GetData();
+ return result;
+}
+
+Module WindowsPlatform::LoadLibrary(TString FileName) {
+ return ::LoadLibrary(FileName.data());
+}
+
+void WindowsPlatform::FreeLibrary(Module AModule) {
+ ::FreeLibrary((HMODULE) AModule);
+}
+
+Procedure WindowsPlatform::GetProcAddress(Module AModule,
+ std::string MethodName) {
+ return ::GetProcAddress((HMODULE) AModule, MethodName.c_str());
+}
+
+bool WindowsPlatform::IsMainThread() {
+ bool result = (FMainThread == ::GetCurrentThreadId());
+ return result;
+}
+
+TString WindowsPlatform::GetTempDirectory() {
+ TString result;
+ PWSTR userDir = 0;
+
+ if (SUCCEEDED(SHGetKnownFolderPath(
+ FOLDERID_Profile,
+ 0,
+ NULL,
+ &userDir))) {
+ result = userDir;
+ result += WINDOWS_JPACKAGE_TMP_DIR;
+ CoTaskMemFree(userDir);
+ }
+
+ return result;
+}
+
+static BOOL CALLBACK enumWindows(HWND winHandle, LPARAM lParam) {
+ DWORD pid = (DWORD) lParam, wPid = 0;
+ GetWindowThreadProcessId(winHandle, &wPid);
+ if (pid == wPid) {
+ SetForegroundWindow(winHandle);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+TPlatformNumber WindowsPlatform::GetMemorySize() {
+ SYSTEM_INFO si;
+ GetSystemInfo(&si);
+ size_t result = (size_t) si.lpMaximumApplicationAddress;
+ result = result / 1048576; // Convert from bytes to megabytes.
+ return result;
+}
+
+std::vector<TString> FilterList(std::vector<TString> &Items,
+ std::wregex Pattern) {
+ std::vector<TString> result;
+
+ for (std::vector<TString>::iterator it = Items.begin();
+ it != Items.end(); ++it) {
+ TString item = *it;
+ std::wsmatch match;
+
+ if (std::regex_search(item, match, Pattern)) {
+ result.push_back(item);
+ }
+ }
+ return result;
+}
+
+Process* WindowsPlatform::CreateProcess() {
+ return new WindowsProcess();
+}
+
+void WindowsPlatform::InitStreamLocale(wios *stream) {
+ const std::locale empty_locale = std::locale::empty();
+ const std::locale utf8_locale =
+ std::locale(empty_locale, new std::codecvt_utf8<wchar_t>());
+ stream->imbue(utf8_locale);
+}
+
+void WindowsPlatform::addPlatformDependencies(JavaLibrary *pJavaLibrary) {
+ if (pJavaLibrary == NULL) {
+ return;
+ }
+
+ if (FilePath::FileExists(_T("msvcr100.dll")) == true) {
+ pJavaLibrary->AddDependency(_T("msvcr100.dll"));
+ }
+
+ TString runtimeBin = GetPackageRuntimeBinDirectory();
+ SetDllDirectory(runtimeBin.c_str());
+}
+
+void Platform::CopyString(char *Destination,
+ size_t NumberOfElements, const char *Source) {
+ strcpy_s(Destination, NumberOfElements, Source);
+
+ if (NumberOfElements > 0) {
+ Destination[NumberOfElements - 1] = '\0';
+ }
+}
+
+void Platform::CopyString(wchar_t *Destination,
+ size_t NumberOfElements, const wchar_t *Source) {
+ wcscpy_s(Destination, NumberOfElements, Source);
+
+ if (NumberOfElements > 0) {
+ Destination[NumberOfElements - 1] = '\0';
+ }
+}
+
+// Owner must free the return value.
+MultibyteString Platform::WideStringToMultibyteString(
+ const wchar_t* value) {
+ MultibyteString result;
+ size_t count = 0;
+
+ if (value == NULL) {
+ return result;
+ }
+
+ count = WideCharToMultiByte(CP_UTF8, 0, value, -1, NULL, 0, NULL, NULL);
+
+ if (count > 0) {
+ result.data = new char[count + 1];
+ result.length = WideCharToMultiByte(CP_UTF8, 0, value, -1,
+ result.data, (int)count, NULL, NULL);
+ }
+
+ return result;
+}
+
+// Owner must free the return value.
+WideString Platform::MultibyteStringToWideString(const char* value) {
+ WideString result;
+ size_t count = 0;
+
+ if (value == NULL) {
+ return result;
+ }
+
+ mbstowcs_s(&count, NULL, 0, value, _TRUNCATE);
+
+ if (count > 0) {
+ result.data = new wchar_t[count + 1];
+ mbstowcs_s(&result.length, result.data, count, value, count);
+ }
+
+ return result;
+}
+
+FileHandle::FileHandle(std::wstring FileName) {
+ FHandle = ::CreateFile(FileName.data(), GENERIC_READ, FILE_SHARE_READ,
+ NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
+}
+
+FileHandle::~FileHandle() {
+ if (IsValid() == true) {
+ ::CloseHandle(FHandle);
+ }
+}
+
+bool FileHandle::IsValid() {
+ return FHandle != INVALID_HANDLE_VALUE;
+}
+
+HANDLE FileHandle::GetHandle() {
+ return FHandle;
+}
+
+FileMappingHandle::FileMappingHandle(HANDLE FileHandle) {
+ FHandle = ::CreateFileMapping(FileHandle, NULL, PAGE_READONLY, 0, 0, NULL);
+}
+
+bool FileMappingHandle::IsValid() {
+ return FHandle != NULL;
+}
+
+FileMappingHandle::~FileMappingHandle() {
+ if (IsValid() == true) {
+ ::CloseHandle(FHandle);
+ }
+}
+
+HANDLE FileMappingHandle::GetHandle() {
+ return FHandle;
+}
+
+FileData::FileData(HANDLE Handle) {
+ FBaseAddress = ::MapViewOfFile(Handle, FILE_MAP_READ, 0, 0, 0);
+}
+
+FileData::~FileData() {
+ if (IsValid() == true) {
+ ::UnmapViewOfFile(FBaseAddress);
+ }
+}
+
+bool FileData::IsValid() {
+ return FBaseAddress != NULL;
+}
+
+LPVOID FileData::GetBaseAddress() {
+ return FBaseAddress;
+}
+
+WindowsLibrary::WindowsLibrary(std::wstring FileName) {
+ FFileName = FileName;
+}
+
+std::vector<TString> WindowsLibrary::GetImports() {
+ std::vector<TString> result;
+ FileHandle library(FFileName);
+
+ if (library.IsValid() == true) {
+ FileMappingHandle mapping(library.GetHandle());
+
+ if (mapping.IsValid() == true) {
+ FileData fileData(mapping.GetHandle());
+
+ if (fileData.IsValid() == true) {
+ PIMAGE_DOS_HEADER dosHeader =
+ (PIMAGE_DOS_HEADER) fileData.GetBaseAddress();
+ PIMAGE_FILE_HEADER pImgFileHdr =
+ (PIMAGE_FILE_HEADER) fileData.GetBaseAddress();
+ if (dosHeader->e_magic == IMAGE_DOS_SIGNATURE) {
+ result = DumpPEFile(dosHeader);
+ }
+ }
+ }
+ }
+
+ return result;
+}
+
+// Given an RVA, look up the section header that encloses it and return a
+// pointer to its IMAGE_SECTION_HEADER
+
+PIMAGE_SECTION_HEADER WindowsLibrary::GetEnclosingSectionHeader(DWORD rva,
+ PIMAGE_NT_HEADERS pNTHeader) {
+ PIMAGE_SECTION_HEADER result = 0;
+ PIMAGE_SECTION_HEADER section = IMAGE_FIRST_SECTION(pNTHeader);
+
+ for (unsigned index = 0; index < pNTHeader->FileHeader.NumberOfSections;
+ index++, section++) {
+ // Is the RVA is within this section?
+ if ((rva >= section->VirtualAddress) &&
+ (rva < (section->VirtualAddress + section->Misc.VirtualSize))) {
+ result = section;
+ }
+ }
+
+ return result;
+}
+
+LPVOID WindowsLibrary::GetPtrFromRVA(DWORD rva, PIMAGE_NT_HEADERS pNTHeader,
+ DWORD imageBase) {
+ LPVOID result = 0;
+ PIMAGE_SECTION_HEADER pSectionHdr = GetEnclosingSectionHeader(rva,
+ pNTHeader);
+
+ if (pSectionHdr != NULL) {
+ INT delta = (INT) (
+ pSectionHdr->VirtualAddress - pSectionHdr->PointerToRawData);
+ DWORD_PTR dwp = (DWORD_PTR) (imageBase + rva - delta);
+ result = reinterpret_cast<LPVOID> (dwp); // VS2017 - FIXME
+ }
+
+ return result;
+}
+
+std::vector<TString> WindowsLibrary::GetImportsSection(DWORD base,
+ PIMAGE_NT_HEADERS pNTHeader) {
+ std::vector<TString> result;
+
+ // Look up where the imports section is located. Normally in
+ // the .idata section,
+ // but not necessarily so. Therefore, grab the RVA from the data dir.
+ DWORD importsStartRVA = pNTHeader->OptionalHeader.DataDirectory[
+ IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;
+
+ if (importsStartRVA != NULL) {
+ // Get the IMAGE_SECTION_HEADER that contains the imports. This is
+ // usually the .idata section, but doesn't have to be.
+ PIMAGE_SECTION_HEADER pSection =
+ GetEnclosingSectionHeader(importsStartRVA, pNTHeader);
+
+ if (pSection != NULL) {
+ PIMAGE_IMPORT_DESCRIPTOR importDesc =
+ (PIMAGE_IMPORT_DESCRIPTOR) GetPtrFromRVA(
+ importsStartRVA, pNTHeader, base);
+
+ if (importDesc != NULL) {
+ while (true) {
+ // See if we've reached an empty IMAGE_IMPORT_DESCRIPTOR
+ if ((importDesc->TimeDateStamp == 0) &&
+ (importDesc->Name == 0)) {
+ break;
+ }
+
+ std::string filename = (char*) GetPtrFromRVA(
+ importDesc->Name, pNTHeader, base);
+ result.push_back(PlatformString(filename));
+ importDesc++; // advance to next IMAGE_IMPORT_DESCRIPTOR
+ }
+ }
+ }
+ }
+
+ return result;
+}
+
+std::vector<TString> WindowsLibrary::DumpPEFile(PIMAGE_DOS_HEADER dosHeader) {
+ std::vector<TString> result;
+ // all of this is VS2017 - FIXME
+ DWORD_PTR dwDosHeaders = reinterpret_cast<DWORD_PTR> (dosHeader);
+ DWORD_PTR dwPIHeaders = dwDosHeaders + (DWORD) (dosHeader->e_lfanew);
+
+ PIMAGE_NT_HEADERS pNTHeader =
+ reinterpret_cast<PIMAGE_NT_HEADERS> (dwPIHeaders);
+
+ // Verify that the e_lfanew field gave us a reasonable
+ // pointer and the PE signature.
+ // TODO: To really fix JDK-8131321 this condition needs to be changed.
+ // There is a matching change
+ // in JavaVirtualMachine.cpp that also needs to be changed.
+ if (pNTHeader->Signature == IMAGE_NT_SIGNATURE) {
+ DWORD base = (DWORD) (dwDosHeaders);
+ result = GetImportsSection(base, pNTHeader);
+ }
+
+ return result;
+}
+
+#include <TlHelp32.h>
+
+WindowsJob::WindowsJob() {
+ FHandle = NULL;
+}
+
+WindowsJob::~WindowsJob() {
+ if (FHandle != NULL) {
+ CloseHandle(FHandle);
+ }
+}
+
+HANDLE WindowsJob::GetHandle() {
+ if (FHandle == NULL) {
+ FHandle = CreateJobObject(NULL, NULL); // GLOBAL
+
+ if (FHandle == NULL) {
+ ::MessageBox(0, _T("Could not create job object"),
+ _T("TEST"), MB_OK);
+ } else {
+ JOBOBJECT_EXTENDED_LIMIT_INFORMATION jeli = {0};
+
+ // Configure all child processes associated with
+ // the job to terminate when the
+ jeli.BasicLimitInformation.LimitFlags =
+ JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
+ if (0 == SetInformationJobObject(FHandle,
+ JobObjectExtendedLimitInformation, &jeli, sizeof (jeli))) {
+ ::MessageBox(0, _T("Could not SetInformationJobObject"),
+ _T("TEST"), MB_OK);
+ }
+ }
+ }
+
+ return FHandle;
+}
+
+// Initialize static member of WindowsProcess
+WindowsJob WindowsProcess::FJob;
+
+WindowsProcess::WindowsProcess() : Process() {
+ FRunning = false;
+}
+
+WindowsProcess::~WindowsProcess() {
+ Terminate();
+}
+
+void WindowsProcess::Cleanup() {
+ CloseHandle(FProcessInfo.hProcess);
+ CloseHandle(FProcessInfo.hThread);
+}
+
+bool WindowsProcess::IsRunning() {
+ bool result = false;
+
+ HANDLE handle = ::CreateToolhelp32Snapshot(TH32CS_SNAPALL, 0);
+ if (handle == INVALID_HANDLE_VALUE) {
+ return false;
+ }
+
+ PROCESSENTRY32 process = {0};
+ process.dwSize = sizeof (process);
+
+ if (::Process32First(handle, &process)) {
+ do {
+ if (process.th32ProcessID == FProcessInfo.dwProcessId) {
+ result = true;
+ break;
+ }
+ } while (::Process32Next(handle, &process));
+ }
+
+ CloseHandle(handle);
+
+ return result;
+}
+
+bool WindowsProcess::Terminate() {
+ bool result = false;
+
+ if (IsRunning() == true && FRunning == true) {
+ FRunning = false;
+ }
+
+ return result;
+}
+
+bool WindowsProcess::Execute(const TString Application,
+ const std::vector<TString> Arguments, bool AWait) {
+ bool result = false;
+
+ if (FRunning == false) {
+ FRunning = true;
+
+ STARTUPINFO startupInfo;
+ ZeroMemory(&startupInfo, sizeof (startupInfo));
+ startupInfo.cb = sizeof (startupInfo);
+ ZeroMemory(&FProcessInfo, sizeof (FProcessInfo));
+
+ TString command = Application;
+
+ for (std::vector<TString>::const_iterator iterator = Arguments.begin();
+ iterator != Arguments.end(); iterator++) {
+ command += TString(_T(" ")) + *iterator;
+ }
+
+ if (::CreateProcess(Application.data(), (wchar_t*)command.data(), NULL,
+ NULL, FALSE, 0, NULL, NULL, &startupInfo, &FProcessInfo) == FALSE) {
+ TString message = PlatformString::Format(
+ _T("Error: Unable to create process %s"),
+ Application.data());
+ throw Exception(message);
+ } else {
+ if (FJob.GetHandle() != NULL) {
+ if (::AssignProcessToJobObject(FJob.GetHandle(),
+ FProcessInfo.hProcess) == 0) {
+ // Failed to assign process to job. It doesn't prevent
+ // anything from continuing so continue.
+ }
+ }
+
+ // Wait until child process exits.
+ if (AWait == true) {
+ Wait();
+ // Close process and thread handles.
+ Cleanup();
+ }
+ }
+ }
+
+ return result;
+}
+
+bool WindowsProcess::Wait() {
+ bool result = false;
+
+ WaitForSingleObject(FProcessInfo.hProcess, INFINITE);
+ return result;
+}
+
+TProcessID WindowsProcess::GetProcessID() {
+ return FProcessInfo.dwProcessId;
+}
+
+bool WindowsProcess::ReadOutput() {
+ bool result = false;
+ // TODO implement
+ return result;
+}
+
+void WindowsProcess::SetInput(TString Value) {
+ // TODO implement
+}
+
+std::list<TString> WindowsProcess::GetOutput() {
+ ReadOutput();
+ return Process::GetOutput();
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/windows/native/libapplauncher/WindowsPlatform.h Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,172 @@
+/*
+ * Copyright (c) 2014, 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.
+ */
+
+#ifndef WINDOWSPLATFORM_H
+#define WINDOWSPLATFORM_H
+
+#include <Windows.h>
+#include "Platform.h"
+
+class WindowsPlatform : virtual public Platform {
+private:
+ DWORD FMainThread;
+
+public:
+ WindowsPlatform(void);
+ virtual ~WindowsPlatform(void);
+
+ virtual TCHAR* ConvertStringToFileSystemString(TCHAR* Source,
+ bool &release);
+ virtual TCHAR* ConvertFileSystemStringToString(TCHAR* Source,
+ bool &release);
+
+ virtual void ShowMessage(TString title, TString description);
+ virtual void ShowMessage(TString description);
+ virtual MessageResponse ShowResponseMessage(TString title,
+ TString description);
+
+ virtual void SetCurrentDirectory(TString Value);
+ virtual TString GetPackageRootDirectory();
+ virtual TString GetAppDataDirectory();
+ virtual TString GetAppName();
+ virtual TString GetBundledJavaLibraryFileName(TString RuntimePath);
+ TString GetPackageAppDirectory();
+ TString GetPackageLauncherDirectory();
+ TString GetPackageRuntimeBinDirectory();
+
+ virtual ISectionalPropertyContainer* GetConfigFile(TString FileName);
+
+ virtual TString GetModuleFileName();
+ virtual Module LoadLibrary(TString FileName);
+ virtual void FreeLibrary(Module AModule);
+ virtual Procedure GetProcAddress(Module AModule, std::string MethodName);
+
+ virtual Process* CreateProcess();
+
+ virtual bool IsMainThread();
+ virtual TPlatformNumber GetMemorySize();
+
+ virtual TString GetTempDirectory();
+ void InitStreamLocale(wios *stream);
+ void addPlatformDependencies(JavaLibrary *pJavaLibrary);
+};
+
+class FileHandle {
+private:
+ HANDLE FHandle;
+
+public:
+ FileHandle(std::wstring FileName);
+ ~FileHandle();
+
+ bool IsValid();
+ HANDLE GetHandle();
+};
+
+
+class FileMappingHandle {
+private:
+ HANDLE FHandle;
+
+public:
+ FileMappingHandle(HANDLE FileHandle);
+ ~FileMappingHandle();
+
+ bool IsValid();
+ HANDLE GetHandle();
+};
+
+
+class FileData {
+private:
+ LPVOID FBaseAddress;
+
+public:
+ FileData(HANDLE Handle);
+ ~FileData();
+
+ bool IsValid();
+ LPVOID GetBaseAddress();
+};
+
+
+class WindowsLibrary {
+private:
+ TString FFileName;
+
+ // Given an RVA, look up the section header that encloses it and return a
+ // pointer to its IMAGE_SECTION_HEADER
+ static PIMAGE_SECTION_HEADER GetEnclosingSectionHeader(DWORD rva,
+ PIMAGE_NT_HEADERS pNTHeader);
+ static LPVOID GetPtrFromRVA(DWORD rva, PIMAGE_NT_HEADERS pNTHeader,
+ DWORD imageBase);
+ static std::vector<TString> GetImportsSection(DWORD base,
+ PIMAGE_NT_HEADERS pNTHeader);
+ static std::vector<TString> DumpPEFile(PIMAGE_DOS_HEADER dosHeader);
+
+public:
+ WindowsLibrary(const TString FileName);
+
+ std::vector<TString> GetImports();
+};
+
+
+class WindowsJob {
+private:
+ HANDLE FHandle;
+
+public:
+ WindowsJob();
+ ~WindowsJob();
+
+ HANDLE GetHandle();
+};
+
+
+class WindowsProcess : public Process {
+private:
+ bool FRunning;
+
+ PROCESS_INFORMATION FProcessInfo;
+ static WindowsJob FJob;
+
+ void Cleanup();
+ bool ReadOutput();
+
+public:
+ WindowsProcess();
+ virtual ~WindowsProcess();
+
+ virtual bool IsRunning();
+ virtual bool Terminate();
+ virtual bool Execute(const TString Application,
+ const std::vector<TString> Arguments, bool AWait = false);
+ virtual bool Wait();
+ virtual TProcessID GetProcessID();
+ virtual void SetInput(TString Value);
+ virtual std::list<TString> GetOutput();
+};
+
+#endif // WINDOWSPLATFORM_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/windows/native/libjpackage/ByteBuffer.cpp Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2015, 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.
+ */
+
+#include "ByteBuffer.h"
+
+#include <stdio.h>
+
+ByteBuffer::ByteBuffer() {
+ buffer.reserve(1024);
+}
+
+ByteBuffer::~ByteBuffer() {
+}
+
+LPBYTE ByteBuffer::getPtr() {
+ return &buffer[0];
+}
+
+size_t ByteBuffer::getPos() {
+ return buffer.size();
+}
+
+void ByteBuffer::AppendString(wstring str) {
+ size_t len = (str.size() + 1) * sizeof (WCHAR);
+ AppendBytes((BYTE*) str.c_str(), len);
+}
+
+void ByteBuffer::AppendWORD(WORD word) {
+ AppendBytes((BYTE*) & word, sizeof (WORD));
+}
+
+void ByteBuffer::Align(size_t bytesNumber) {
+ size_t pos = getPos();
+ if (pos % bytesNumber) {
+ DWORD dwNull = 0;
+ size_t len = bytesNumber - pos % bytesNumber;
+ AppendBytes((BYTE*) & dwNull, len);
+ }
+}
+
+void ByteBuffer::AppendBytes(BYTE* ptr, size_t len) {
+ buffer.insert(buffer.end(), ptr, ptr + len);
+}
+
+void ByteBuffer::ReplaceWORD(size_t offset, WORD word) {
+ ReplaceBytes(offset, (BYTE*) & word, sizeof (WORD));
+}
+
+void ByteBuffer::ReplaceBytes(size_t offset, BYTE* ptr, size_t len) {
+ for (size_t i = 0; i < len; i++) {
+ buffer[offset + i] = *(ptr + i);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/windows/native/libjpackage/ByteBuffer.h Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2015, 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.
+ */
+
+#ifndef BYTEBUFFER_H
+#define BYTEBUFFER_H
+
+#include <windows.h>
+#include <vector>
+#include <string>
+
+using namespace std;
+
+class ByteBuffer {
+public:
+ ByteBuffer();
+ ~ByteBuffer();
+
+ LPBYTE getPtr();
+ size_t getPos();
+
+ void AppendString(wstring str);
+ void AppendWORD(WORD word);
+ void AppendBytes(BYTE* ptr, size_t len);
+
+ void ReplaceWORD(size_t offset, WORD word);
+ void ReplaceBytes(size_t offset, BYTE* ptr, size_t len);
+
+ void Align(size_t bytesNumber);
+
+private:
+ vector<BYTE> buffer;
+};
+
+#endif // BYTEBUFFER_H
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/windows/native/libjpackage/ErrorHandling.cpp Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,141 @@
+/*
+ * Copyright (c) 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.
+ */
+
+#include <algorithm>
+
+#include "ErrorHandling.h"
+#include "Log.h"
+
+
+namespace {
+
+tstring getFilename(const SourceCodePos& pos) {
+ const std::string buf(pos.file);
+ const std::string::size_type idx = buf.find_last_of("\\/");
+ if (idx == std::string::npos) {
+ return tstrings::fromUtf8(buf);
+ }
+ return tstrings::fromUtf8(buf.substr(idx + 1));
+}
+
+void reportError(const SourceCodePos& pos, const tstring& msg) {
+ Logger::defaultLogger().log(Logger::LOG_ERROR, getFilename(pos).c_str(),
+ pos.lno, tstrings::fromUtf8(pos.func).c_str(), msg);
+}
+
+} // namespace
+
+void reportError(const SourceCodePos& pos, const std::exception& e) {
+ reportError(pos, (tstrings::any() << "Exception with message \'"
+ << e.what() << "\' caught").tstr());
+}
+
+
+void reportUnknownError(const SourceCodePos& pos) {
+ reportError(pos, _T("Unknown exception caught"));
+}
+
+
+std::string makeMessage(const std::exception& e, const SourceCodePos& pos) {
+ std::ostringstream printer;
+ printer << getFilename(pos) << "(" << pos.lno << ") at "
+ << pos.func << "(): "
+ << e.what();
+ return printer.str();
+}
+
+
+namespace {
+
+bool isNotSpace(int chr) {
+ return isspace(chr) == 0;
+}
+
+
+enum TrimMode {
+ TrimLeading = 0x10,
+ TrimTrailing = 0x20,
+ TrimBoth = TrimLeading | TrimTrailing
+};
+
+// Returns position of the last printed character in the given string.
+// Returns std::string::npos if nothing was printed.
+size_t printWithoutWhitespaces(std::ostream& out, const std::string& str,
+ TrimMode mode) {
+ std::string::const_reverse_iterator it = str.rbegin();
+ std::string::const_reverse_iterator end = str.rend();
+
+ if (mode & TrimLeading) {
+ // skip leading whitespace
+ std::string::const_iterator entry = std::find_if(str.begin(),
+ str.end(), isNotSpace);
+ end = std::string::const_reverse_iterator(entry);
+ }
+
+ if (mode & TrimTrailing) {
+ // skip trailing whitespace
+ it = std::find_if(it, end, isNotSpace);
+ }
+
+ if (it == end) {
+ return std::string::npos;
+ }
+
+ const size_t pos = str.rend() - end;
+ const size_t len = end - it;
+ out.write(str.c_str() + pos, len);
+ return pos + len - 1;
+}
+
+} // namespace
+
+std::string joinErrorMessages(const std::string& a, const std::string& b) {
+ const std::string endPhraseChars(";.,:!?");
+ const std::string space(" ");
+ const std::string dotAndSpace(". ");
+
+ std::ostringstream printer;
+ printer.exceptions(std::ios::failbit | std::ios::badbit);
+
+ size_t idx = printWithoutWhitespaces(printer, a, TrimTrailing);
+ size_t extra = 0;
+ if (idx < a.size() && endPhraseChars.find(a[idx]) == std::string::npos) {
+ printer << dotAndSpace;
+ extra = dotAndSpace.size();
+ } else if (idx != std::string::npos) {
+ printer << space;
+ extra = space.size();
+ }
+
+ idx = printWithoutWhitespaces(printer, b, TrimBoth);
+
+ const std::string str = printer.str();
+
+ if (std::string::npos == idx && extra) {
+ // Nothing printed from the 'b' message. Backout delimiter string.
+ return str.substr(0, str.size() - extra);
+ }
+ return str;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/windows/native/libjpackage/ErrorHandling.h Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,151 @@
+/*
+ * Copyright (c) 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.
+ */
+
+#ifndef ErrorHandling_h
+#define ErrorHandling_h
+
+
+#include <stdexcept>
+
+#include "SourceCodePos.h"
+#include "tstrings.h"
+
+
+//
+// Exception handling helpers. Allow transparent exception logging.
+// Use as follows:
+//
+// void foo () {
+// JP_TRY;
+//
+// if (!do_something()) {
+// JP_THROW("do_something() failed");
+// }
+//
+// JP_CATCH_ALL;
+// }
+//
+
+
+// Logs std::exception caught at 'pos'.
+void reportError(const SourceCodePos& pos, const std::exception& e);
+// Logs unknown exception caught at 'pos'.
+// Assumed to be called from catch (...) {}
+void reportUnknownError(const SourceCodePos& pos);
+
+std::string makeMessage(const std::exception& e, const SourceCodePos& pos);
+
+std::string joinErrorMessages(const std::string& a, const std::string& b);
+
+
+template <class Base>
+class JpError: public Base {
+public:
+ JpError(const Base& e, const SourceCodePos& pos):
+ Base(e), msg(::makeMessage(e, pos)) {
+ }
+
+ ~JpError() throw() {
+ }
+
+ // override Base::what()
+ const char* what() const throw() {
+ return msg.c_str();
+ }
+private:
+ // Assert Base is derived from std::exception
+ enum { isDerivedFromStdException =
+ sizeof(static_cast<std::exception*>((Base*)0)) };
+
+ std::string msg;
+};
+
+template <class T>
+inline JpError<T> makeException(const T& obj, const SourceCodePos& p) {
+ return JpError<T>(obj, p);
+}
+
+inline JpError<std::runtime_error> makeException(
+ const std::string& msg, const SourceCodePos& p) {
+ return JpError<std::runtime_error>(std::runtime_error(msg), p);
+}
+
+inline JpError<std::runtime_error> makeException(
+ const tstrings::any& msg, const SourceCodePos& p) {
+ return makeException(msg.str(), p);
+}
+
+inline JpError<std::runtime_error> makeException(
+ std::string::const_pointer msg, const SourceCodePos& p) {
+ return makeException(std::string(msg), p);
+}
+
+
+#define JP_REPORT_ERROR(e) reportError(JP_SOURCE_CODE_POS, e)
+#define JP_REPORT_UNKNOWN_ERROR reportUnknownError(JP_SOURCE_CODE_POS)
+
+// Redefine locally in cpp file(s) if need more handling than just reporting
+#define JP_HANDLE_ERROR(e) JP_REPORT_ERROR(e)
+#define JP_HANDLE_UNKNOWN_ERROR JP_REPORT_UNKNOWN_ERROR
+
+
+#define JP_TRY \
+ try \
+ { \
+ do {} while(0)
+
+#define JP_DEFAULT_CATCH_EXCEPTIONS \
+ JP_CATCH_STD_EXCEPTION \
+ JP_CATCH_UNKNOWN_EXCEPTION
+
+#define JP_CATCH_EXCEPTIONS \
+ JP_DEFAULT_CATCH_EXCEPTIONS
+
+#define JP_CATCH_ALL \
+ } \
+ JP_CATCH_EXCEPTIONS \
+ do {} while(0)
+
+#define JP_CATCH_STD_EXCEPTION \
+ catch (const std::exception& e) \
+ { \
+ JP_HANDLE_ERROR(e); \
+ }
+
+#define JP_CATCH_UNKNOWN_EXCEPTION \
+ catch (...) \
+ { \
+ JP_HANDLE_UNKNOWN_ERROR; \
+ }
+
+
+#define JP_THROW(e) throw makeException((e), JP_SOURCE_CODE_POS)
+
+#define JP_NO_THROW(expr) \
+ JP_TRY; \
+ expr; \
+ JP_CATCH_ALL
+
+#endif // #ifndef ErrorHandling_h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/windows/native/libjpackage/FileUtils.cpp Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,702 @@
+/*
+ * Copyright (c) 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.
+ */
+
+#include <memory>
+#include <algorithm>
+#include <shlwapi.h>
+
+#include "FileUtils.h"
+#include "WinErrorHandling.h"
+#include "Log.h"
+
+
+// Needed by FileUtils::isDirectoryNotEmpty
+#pragma comment(lib, "shlwapi")
+
+
+namespace FileUtils {
+
+namespace {
+
+
+tstring reservedFilenameChars() {
+ tstring buf;
+ for (char charCode = 0; charCode < 32; ++charCode) {
+ buf.append(1, charCode);
+ }
+ buf += _T("<>:\"|?*/\\");
+ return buf;
+}
+
+} // namespace
+
+bool isDirSeparator(const tstring::value_type c) {
+ return (c == '/' || c == '\\');
+}
+
+bool isFileExists(const tstring &filePath) {
+ return GetFileAttributes(filePath.c_str()) != INVALID_FILE_ATTRIBUTES;
+}
+
+namespace {
+bool isDirectoryAttrs(const DWORD attrs) {
+ return attrs != INVALID_FILE_ATTRIBUTES && (attrs & FILE_ATTRIBUTE_DIRECTORY) != 0;
+}
+} // namespace
+
+bool isDirectory(const tstring &filePath) {
+ return isDirectoryAttrs(GetFileAttributes(filePath.c_str()));
+}
+
+bool isDirectoryNotEmpty(const tstring &dirPath) {
+ if (!isDirectory(dirPath)) {
+ return false;
+ }
+ return FALSE == PathIsDirectoryEmpty(dirPath.c_str());
+}
+
+tstring dirname(const tstring &path)
+{
+ tstring::size_type pos = path.find_last_of(_T("\\/"));
+ if (pos != tstring::npos) {
+ pos = path.find_last_not_of(_T("\\/"), pos); // skip trailing slashes
+ }
+ return pos == tstring::npos ? tstring() : path.substr(0, pos + 1);
+}
+
+tstring basename(const tstring &path) {
+ const tstring::size_type pos = path.find_last_of(_T("\\/"));
+ if (pos == tstring::npos) {
+ return path;
+ }
+ return path.substr(pos + 1);
+}
+
+tstring suffix(const tstring &path) {
+ const tstring::size_type pos = path.rfind('.');
+ if (pos == tstring::npos) {
+ return tstring();
+ }
+ const tstring::size_type dirSepPos = path.find_first_of(_T("\\/"),
+ pos + 1);
+ if (dirSepPos != tstring::npos) {
+ return tstring();
+ }
+ // test for '/..' and '..' cases
+ if (pos != 0 && path[pos - 1] == '.'
+ && (pos == 1 || isDirSeparator(path[pos - 2]))) {
+ return tstring();
+ }
+ return path.substr(pos);
+}
+
+tstring combinePath(const tstring& parent, const tstring& child) {
+ if (parent.empty()) {
+ return child;
+ }
+ if (child.empty()) {
+ return parent;
+ }
+
+ tstring parentWOSlash = removeTrailingSlash(parent);
+ // also handle the case when child contains starting slash
+ bool childHasSlash = isDirSeparator(child.front());
+ tstring childWOSlash = childHasSlash ? child.substr(1) : child;
+
+ return parentWOSlash + _T("\\") + childWOSlash;
+}
+
+tstring removeTrailingSlash(const tstring& path) {
+ if (path.empty()) {
+ return path;
+ }
+ tstring::const_reverse_iterator it = path.rbegin();
+ tstring::const_reverse_iterator end = path.rend();
+
+ while (it != end && isDirSeparator(*it)) {
+ ++it;
+ }
+ return path.substr(0, end - it);
+}
+
+tstring normalizePath(tstring v) {
+ std::replace(v.begin(), v.end(), '/', '\\');
+ return tstrings::toLower(v);
+}
+
+namespace {
+
+bool createNewFile(const tstring& path) {
+ HANDLE h = CreateFile(path.c_str(), GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);
+ // if the file exists => h == INVALID_HANDLE_VALUE & GetLastError returns ERROR_FILE_EXISTS
+ if (h != INVALID_HANDLE_VALUE) {
+ CloseHandle(h);
+ LOG_TRACE(tstrings::any() << "Created [" << path << "] file");
+ return true;
+ }
+ return false;
+}
+
+} // namespace
+
+tstring createTempFile(const tstring &prefix, const tstring &suffix, const tstring &path)
+{
+ const tstring invalidChars = reservedFilenameChars();
+
+ if (prefix.find_first_of(invalidChars) != tstring::npos) {
+ JP_THROW(tstrings::any() << "Illegal characters in prefix=" << prefix);
+ }
+
+ if (suffix.find_first_of(invalidChars) != tstring::npos) {
+ JP_THROW(tstrings::any() << "Illegal characters in suffix=" << suffix);
+ }
+
+ int rnd = (int)GetTickCount();
+
+ // do no more than 100 attempts
+ for (int i=0; i<100; i++) {
+ const tstring filePath = mkpath() << path << (prefix + (tstrings::any() << (rnd + i)).tstr() + suffix);
+ if (createNewFile(filePath)) {
+ return filePath;
+ }
+ }
+
+ // 100 attempts failed
+ JP_THROW(tstrings::any() << "createTempFile(" << prefix << ", "
+ << suffix << ", "
+ << path << ") failed");
+}
+
+tstring createTempDirectory(const tstring &prefix, const tstring &suffix, const tstring &basedir) {
+ const tstring filePath = createTempFile(prefix, suffix, basedir);
+ // delete the file and create directory with the same name
+ deleteFile(filePath);
+ createDirectory(filePath);
+ return filePath;
+}
+
+tstring createUniqueFile(const tstring &prototype) {
+ if (createNewFile(prototype)) {
+ return prototype;
+ }
+
+ return createTempFile(replaceSuffix(basename(prototype)),
+ suffix(prototype), dirname(prototype));
+}
+
+namespace {
+
+void createDir(const tstring path, LPSECURITY_ATTRIBUTES saAttr, tstring_array* createdDirs=0) {
+ if (CreateDirectory(path.c_str(), saAttr)) {
+ LOG_TRACE(tstrings::any() << "Created [" << path << "] directory");
+ if (createdDirs) {
+ createdDirs->push_back(removeTrailingSlash(path));
+ }
+ } else {
+ const DWORD createDirectoryErr = GetLastError();
+ // if saAttr is specified, fail even if the directory exists
+ if (saAttr != NULL || !isDirectory(path)) {
+ JP_THROW(SysError(tstrings::any() << "CreateDirectory(" << path << ") failed",
+ CreateDirectory, createDirectoryErr));
+ }
+ }
+}
+
+}
+
+void createDirectory(const tstring &path, tstring_array* createdDirs) {
+ const tstring dirPath = removeTrailingSlash(path) + _T("\\");
+
+ tstring::size_type pos = dirPath.find_first_of(_T("\\/"));
+ while (pos != tstring::npos) {
+ const tstring subdirPath = dirPath.substr(0, pos + 1);
+ createDir(subdirPath, NULL, createdDirs);
+ pos = dirPath.find_first_of(_T("\\/"), pos + 1);
+ }
+}
+
+
+void copyFile(const tstring& fromPath, const tstring& toPath, bool failIfExists) {
+ createDirectory(dirname(toPath));
+ if (!CopyFile(fromPath.c_str(), toPath.c_str(), (failIfExists ? TRUE : FALSE))) {
+ JP_THROW(SysError(tstrings::any()
+ << "CopyFile(" << fromPath << ", " << toPath << ", "
+ << failIfExists << ") failed", CopyFile));
+ }
+ LOG_TRACE(tstrings::any() << "Copied [" << fromPath << "] file to ["
+ << toPath << "]");
+}
+
+
+namespace {
+
+void moveFileImpl(const tstring& fromPath, const tstring& toPath,
+ DWORD flags) {
+ const bool isDir = isDirectory(fromPath);
+ if (!MoveFileEx(fromPath.c_str(), toPath.empty() ? NULL : toPath.c_str(),
+ flags)) {
+ JP_THROW(SysError(tstrings::any() << "MoveFileEx(" << fromPath
+ << ", " << toPath << ", " << flags << ") failed",
+ MoveFileEx));
+ }
+
+ const bool onReboot = 0 != (flags & MOVEFILE_DELAY_UNTIL_REBOOT);
+
+ const LPCTSTR label = isDir ? _T("folder") : _T("file");
+
+ tstrings::any msg;
+ if (!toPath.empty()) {
+ if (onReboot) {
+ msg << "Move";
+ } else {
+ msg << "Moved";
+ }
+ msg << " '" << fromPath << "' " << label << " to '" << toPath << "'";
+ } else {
+ if (onReboot) {
+ msg << "Delete";
+ } else {
+ msg << "Deleted";
+ }
+ msg << " '" << fromPath << "' " << label;
+ }
+ if (onReboot) {
+ msg << " on reboot";
+ }
+ LOG_TRACE(msg);
+}
+
+} // namespace
+
+
+void moveFile(const tstring& fromPath, const tstring& toPath, bool failIfExists)
+{
+ createDirectory(dirname(toPath));
+
+ DWORD flags = MOVEFILE_COPY_ALLOWED;
+ if (!failIfExists) {
+ flags |= MOVEFILE_REPLACE_EXISTING;
+ }
+
+ moveFileImpl(fromPath, toPath, flags);
+}
+
+void deleteFile(const tstring &path)
+{
+ if (!deleteFile(path, std::nothrow)) {
+ JP_THROW(SysError(tstrings::any()
+ << "DeleteFile(" << path << ") failed", DeleteFile));
+ }
+}
+
+namespace {
+
+bool notFound(const DWORD status=GetLastError()) {
+ return status == ERROR_FILE_NOT_FOUND || status == ERROR_PATH_NOT_FOUND;
+}
+
+bool deleteFileImpl(const std::nothrow_t &, const tstring &path) {
+ const bool deleted = (DeleteFile(path.c_str()) != 0);
+ if (deleted) {
+ LOG_TRACE(tstrings::any() << "Deleted [" << path << "] file");
+ return true;
+ }
+ return notFound();
+}
+
+} // namespace
+
+bool deleteFile(const tstring &path, const std::nothrow_t &) throw()
+{
+ bool deleted = deleteFileImpl(std::nothrow, path);
+ const DWORD status = GetLastError();
+ if (!deleted && status == ERROR_ACCESS_DENIED) {
+ DWORD attrs = GetFileAttributes(path.c_str());
+ SetLastError(status);
+ if (attrs == INVALID_FILE_ATTRIBUTES) {
+ return false;
+ }
+ if (attrs & FILE_ATTRIBUTE_READONLY) {
+ // DeleteFile() failed because file is R/O.
+ // Remove R/O attribute and retry DeleteFile().
+ attrs &= ~FILE_ATTRIBUTE_READONLY;
+ if (SetFileAttributes(path.c_str(), attrs)) {
+ LOG_TRACE(tstrings::any() << "Discarded R/O attribute from ["
+ << path << "] file");
+ deleted = deleteFileImpl(std::nothrow, path);
+ } else {
+ LOG_WARNING(SysError(tstrings::any()
+ << "Failed to discard R/O attribute from ["
+ << path << "] file. File will not be deleted",
+ SetFileAttributes).what());
+ SetLastError(status);
+ }
+ }
+ }
+
+ return deleted || notFound();
+}
+
+void deleteDirectory(const tstring &path)
+{
+ if (!deleteDirectory(path, std::nothrow)) {
+ JP_THROW(SysError(tstrings::any()
+ << "RemoveDirectory(" << path << ") failed", RemoveDirectory));
+ }
+}
+
+bool deleteDirectory(const tstring &path, const std::nothrow_t &) throw()
+{
+ const bool deleted = (RemoveDirectory(path.c_str()) != 0);
+ if (deleted) {
+ LOG_TRACE(tstrings::any() << "Deleted [" << path << "] directory");
+ }
+ return deleted || notFound();
+}
+
+namespace {
+
+class DeleteFilesCallback: public DirectoryCallback {
+public:
+ explicit DeleteFilesCallback(bool ff): failfast(ff), failed(false) {
+ }
+
+ virtual bool onFile(const tstring& path) {
+ if (failfast) {
+ deleteFile(path);
+ } else {
+ updateStatus(deleteFile(path, std::nothrow));
+ }
+ return true;
+ }
+
+ bool good() const {
+ return !failed;
+ }
+
+protected:
+ void updateStatus(bool success) {
+ if (!success) {
+ failed = true;
+ }
+ }
+
+ const bool failfast;
+private:
+ bool failed;
+};
+
+class DeleteAllCallback: public DeleteFilesCallback {
+public:
+ explicit DeleteAllCallback(bool failfast): DeleteFilesCallback(failfast) {
+ }
+
+ virtual bool onDirectory(const tstring& path) {
+ if (failfast) {
+ deleteDirectoryRecursive(path);
+ } else {
+ updateStatus(deleteDirectoryRecursive(path, std::nothrow));
+ }
+ return true;
+ }
+};
+
+
+class BatchDeleter {
+ const tstring dirPath;
+ bool recursive;
+public:
+ explicit BatchDeleter(const tstring& path): dirPath(path) {
+ deleteSubdirs(false);
+ }
+
+ BatchDeleter& deleteSubdirs(bool v) {
+ recursive = v;
+ return *this;
+ }
+
+ void execute() const {
+ if (!isFileExists(dirPath)) {
+ return;
+ }
+ iterateDirectory(true /* fail fast */);
+ if (recursive) {
+ deleteDirectory(dirPath);
+ }
+ }
+
+ bool execute(const std::nothrow_t&) const {
+ if (!isFileExists(dirPath)) {
+ return true;
+ }
+
+ if (!isDirectory(dirPath)) {
+ return false;
+ }
+
+ JP_TRY;
+ if (!iterateDirectory(false /* ignore errors */)) {
+ return false;
+ }
+ if (recursive) {
+ return deleteDirectory(dirPath, std::nothrow);
+ }
+ return true;
+ JP_CATCH_ALL;
+
+ return false;
+ }
+
+private:
+ bool iterateDirectory(bool failfast) const {
+ std::unique_ptr<DeleteFilesCallback> callback;
+ if (recursive) {
+ callback = std::unique_ptr<DeleteFilesCallback>(
+ new DeleteAllCallback(failfast));
+ } else {
+ callback = std::unique_ptr<DeleteFilesCallback>(
+ new DeleteFilesCallback(failfast));
+ }
+
+ FileUtils::iterateDirectory(dirPath, *callback);
+ return callback->good();
+ }
+};
+
+} // namespace
+
+void deleteFilesInDirectory(const tstring &dirPath) {
+ BatchDeleter(dirPath).execute();
+}
+
+bool deleteFilesInDirectory(const tstring &dirPath,
+ const std::nothrow_t &) throw() {
+ return BatchDeleter(dirPath).execute(std::nothrow);
+}
+
+void deleteDirectoryRecursive(const tstring &dirPath) {
+ BatchDeleter(dirPath).deleteSubdirs(true).execute();
+}
+
+bool deleteDirectoryRecursive(const tstring &dirPath,
+ const std::nothrow_t &) throw() {
+ return BatchDeleter(dirPath).deleteSubdirs(true).execute(std::nothrow);
+}
+
+namespace {
+
+struct FindFileDeleter {
+ typedef HANDLE pointer;
+
+ void operator()(HANDLE h) {
+ if (h && h != INVALID_HANDLE_VALUE) {
+ FindClose(h);
+ }
+ }
+};
+
+typedef std::unique_ptr<HANDLE, FindFileDeleter> UniqueFindFileHandle;
+
+}; // namesace
+void iterateDirectory(const tstring &dirPath, DirectoryCallback& callback)
+{
+ const tstring searchString = combinePath(dirPath, _T("*"));
+ WIN32_FIND_DATA findData;
+ UniqueFindFileHandle h(FindFirstFile(searchString.c_str(), &findData));
+ if (h.get() == INVALID_HANDLE_VALUE) {
+ // GetLastError() == ERROR_FILE_NOT_FOUND is OK - no files in the directory
+ // ERROR_PATH_NOT_FOUND is returned if the parent directory does not exist
+ if (GetLastError() != ERROR_FILE_NOT_FOUND) {
+ JP_THROW(SysError(tstrings::any() << "FindFirstFile(" << dirPath << ") failed", FindFirstFile));
+ }
+ return;
+ }
+
+ do {
+ const tstring fname(findData.cFileName);
+ const tstring filePath = combinePath(dirPath, fname);
+ if (!isDirectoryAttrs(findData.dwFileAttributes)) {
+ if (!callback.onFile(filePath)) {
+ return;
+ }
+ } else if (fname != _T(".") && fname != _T("..")) {
+ if (!callback.onDirectory(filePath)) {
+ return;
+ }
+ }
+ } while (FindNextFile(h.get(), &findData));
+
+ // expect GetLastError() == ERROR_NO_MORE_FILES
+ if (GetLastError() != ERROR_NO_MORE_FILES) {
+ JP_THROW(SysError(tstrings::any() << "FindNextFile(" << dirPath << ") failed", FindNextFile));
+ }
+}
+
+
+tstring replaceSuffix(const tstring& path, const tstring& newSuffix) {
+ return (path.substr(0, path.size() - suffix(path).size()) + newSuffix);
+}
+
+
+DirectoryIterator& DirectoryIterator::findItems(tstring_array& v) {
+ if (!isDirectory(root)) {
+ return *this;
+ }
+
+ iterateDirectory(root, *this);
+ v.insert(v.end(), items.begin(), items.end());
+ items = tstring_array();
+ return *this;
+}
+
+bool DirectoryIterator::onFile(const tstring& path) {
+ if (theWithFiles) {
+ items.push_back(path);
+ }
+ return true;
+}
+
+bool DirectoryIterator::onDirectory(const tstring& path) {
+ if (theWithFolders) {
+ items.push_back(path);
+ }
+ if (theRecurse) {
+ DirectoryIterator(path).recurse(theRecurse)
+ .withFiles(theWithFiles)
+ .withFolders(theWithFolders)
+ .findItems(items);
+ }
+ return true;
+}
+
+
+namespace {
+
+struct DeleterFunctor {
+ // Order of items in the following enum is important!
+ // It controls order in which items of particular type will be deleted.
+ // See Deleter::execute().
+ enum {
+ File,
+ FilesInDirectory,
+ RecursiveDirectory,
+ EmptyDirectory
+ };
+
+ void operator () (const Deleter::Path& path) const {
+ switch (path.second) {
+#define DELETE_SOME(o, f)\
+ case o:\
+ f(path.first, std::nothrow);\
+ break
+
+ DELETE_SOME(File, deleteFile);
+ DELETE_SOME(EmptyDirectory, deleteDirectory);
+ DELETE_SOME(FilesInDirectory, deleteFilesInDirectory);
+ DELETE_SOME(RecursiveDirectory, deleteDirectoryRecursive);
+
+#undef DELETE_SOME
+ default:
+ break;
+ }
+ }
+};
+
+} // namespace
+
+void Deleter::execute() {
+ Paths tmp;
+ tmp.swap(paths);
+
+ // Reorder items to delete.
+ std::stable_sort(tmp.begin(), tmp.end(), [] (const Paths::value_type& a,
+ const Paths::value_type& b) {
+ return a.second < b.second;
+ });
+
+ std::for_each(tmp.begin(), tmp.end(), DeleterFunctor());
+}
+
+Deleter& Deleter::appendFile(const tstring& path) {
+ paths.push_back(std::make_pair(path, DeleterFunctor::File));
+ return *this;
+}
+
+Deleter& Deleter::appendEmptyDirectory(const Directory& dir) {
+ tstring path = normalizePath(removeTrailingSlash(dir));
+ const tstring parent = normalizePath(removeTrailingSlash(dir.parent));
+ while(parent != path) {
+ appendEmptyDirectory(path);
+ path = dirname(path);
+ }
+
+ return *this;
+}
+
+Deleter& Deleter::appendEmptyDirectory(const tstring& path) {
+ paths.push_back(std::make_pair(path,
+ DeleterFunctor::EmptyDirectory));
+ return *this;
+}
+
+Deleter& Deleter::appendAllFilesInDirectory(const tstring& path) {
+ paths.push_back(std::make_pair(path,
+ DeleterFunctor::FilesInDirectory));
+ return *this;
+}
+
+Deleter& Deleter::appendRecursiveDirectory(const tstring& path) {
+ paths.push_back(std::make_pair(path,
+ DeleterFunctor::RecursiveDirectory));
+ return *this;
+}
+
+
+FileWriter::FileWriter(const tstring& path): dstPath(path) {
+ tmpFile = FileUtils::createTempFile(_T("jds"), _T(".tmp"),
+ FileUtils::dirname(path));
+
+ cleaner.appendFile(tmpFile);
+
+ // we want to get exception on error
+ tmp.exceptions(std::ifstream::failbit | std::ifstream::badbit);
+ tmp.open(tmpFile, std::ios::binary | std::ios::trunc);
+}
+
+FileWriter& FileWriter::write(const void* buf, size_t bytes) {
+ tmp.write(static_cast<const char*>(buf), bytes);
+ return *this;
+}
+
+void FileWriter::finalize() {
+ tmp.close();
+
+ FileUtils::moveFile(tmpFile, dstPath, false);
+
+ // cancel file deletion
+ cleaner.cancel();
+}
+
+} // namespace FileUtils
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/windows/native/libjpackage/FileUtils.h Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,378 @@
+/*
+ * Copyright (c) 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.
+ */
+
+#ifndef FILEUTILS_H
+#define FILEUTILS_H
+
+
+#include <fstream>
+#include "SysInfo.h"
+
+
+namespace FileUtils {
+
+ // Returns 'true' if the given character is a path separator.
+ bool isDirSeparator(const tstring::value_type c);
+
+ // checks if the file or directory exists
+ bool isFileExists(const tstring &filePath);
+
+ // checks is the specified file is a directory
+ // returns false if the path does not exist
+ bool isDirectory(const tstring &filePath);
+
+ //checks if the specified directory is not empty
+ //returns true if the path is an existing directory and it contains at least one file other than "." or "..".
+ bool isDirectoryNotEmpty(const tstring &dirPath);
+
+ // returns directory part of the path.
+ // returns empty string if the path contains only filename.
+ // if the path ends with slash/backslash, returns removeTrailingSlashes(path).
+ tstring dirname(const tstring &path);
+
+ // returns basename part of the path
+ // if the path ends with slash/backslash, returns empty string.
+ tstring basename(const tstring &path);
+
+ /**
+ * Translates forward slashes to back slashes and returns lower case version
+ * of the given string.
+ */
+ tstring normalizePath(tstring v);
+
+ // Returns suffix of the path. If the given path has a suffix the first
+ // character of the return value is '.'.
+ // Otherwise return value if empty string.
+ tstring suffix(const tstring &path);
+
+ // combines two strings into a path
+ tstring combinePath(const tstring& parent, const tstring& child);
+
+ // removes trailing slashes and backslashes in the path if any
+ tstring removeTrailingSlash(const tstring& path);
+
+ // Creates a file with unique name in the specified base directory, throws an exception if operation fails
+ // path is constructed as <prefix><random number><suffix>.
+ // The function fails and throws exception if 'path' doesn't exist.
+ tstring createTempFile(const tstring &prefix = _T(""), const tstring &suffix = _T(".tmp"), const tstring &path=SysInfo::getTempDir());
+
+ // Creates a directory with unique name in the specified base directory, throws an exception if operation fails
+ // path is constructed as <prefix><random number><suffix>
+ // The function fails and throws exception if 'path' doesn't exist.
+ tstring createTempDirectory(const tstring &prefix = _T(""), const tstring &suffix = _T(".tmp"), const tstring &basedir=SysInfo::getTempDir());
+
+ // If the file referenced with "prototype" parameter DOES NOT exist, the return
+ // value is the given path. No new files created.
+ // Otherwise the function creates another file in the same directory as
+ // the given file with the same suffix and with the basename from the
+ // basename of the given file with some random chars appended to ensure
+ // created file is unique.
+ tstring createUniqueFile(const tstring &prototype);
+
+ // Creates directory and subdirectories if don't exist.
+ // Currently supports only "standard" path like "c:\bla-bla"
+ // If 'createdDirs' parameter is not NULL, the given array is appended with
+ // all subdirectories created by this function call.
+ void createDirectory(const tstring &path, tstring_array* createdDirs=0);
+
+ // copies file from fromPath to toPath. Creates output directory if doesn't exist.
+ void copyFile(const tstring& fromPath, const tstring& toPath, bool failIfExists);
+
+ // moves file from fromPath to toPath. Creates output directory if doesn't exist.
+ void moveFile(const tstring& fromPath, const tstring& toPath, bool failIfExists);
+
+ // Throws exception if fails to delete specified 'path'.
+ // Exits normally if 'path' doesn't exist or it has been deleted.
+ // Attempts to strip R/O attribute if delete fails and retry delete.
+ void deleteFile(const tstring &path);
+ // Returns 'false' if fails to delete specified 'path'.
+ // Returns 'true' if 'path' doesn't exist or it has been deleted.
+ // Attempts to strip R/O attribute if delete fails and retry delete.
+ bool deleteFile(const tstring &path, const std::nothrow_t &) throw();
+
+ // Like deleteFile(), but applies to directories.
+ void deleteDirectory(const tstring &path);
+ bool deleteDirectory(const tstring &path, const std::nothrow_t &) throw();
+
+ // Deletes all files (not subdirectories) from the specified directory.
+ // Exits normally if all files in 'dirPath' have been deleted or if
+ // 'dirPath' doesn't exist.
+ // Throws exception if 'dirPath' references existing file system object
+ // which is not a directory or when the first failure of file delete
+ // occurs.
+ void deleteFilesInDirectory(const tstring &dirPath);
+ // Deletes all files (not subdirectories) from the specified directory.
+ // Returns 'true' normally if all files in 'dirPath' have been deleted or
+ // if 'dirPath' doesn't exist.
+ // Returns 'false' if 'dirPath' references existing file system object
+ // which is not a directory or if failed to delete one ore more files in
+ // 'dirPath' directory.
+ // Doesn't abort iteration over files if the given directory after the
+ // first failure to delete a file.
+ bool deleteFilesInDirectory(const tstring &dirPath, const std::nothrow_t &) throw();
+ // Like deleteFilesInDirectory, but deletes subdirectories as well
+ void deleteDirectoryRecursive(const tstring &dirPath);
+ bool deleteDirectoryRecursive(const tstring &dirPath, const std::nothrow_t &) throw();
+
+ class DirectoryCallback {
+ public:
+ virtual ~DirectoryCallback() {};
+
+ virtual bool onFile(const tstring& path) {
+ return true;
+ }
+ virtual bool onDirectory(const tstring& path) {
+ return true;
+ }
+ };
+
+ // Calls the given callback for every file and subdirectory of
+ // the given directory.
+ void iterateDirectory(const tstring &dirPath, DirectoryCallback& callback);
+
+ /**
+ * Replace file suffix, example replaceSuffix("file/path.txt", ".csv")
+ * @param path file path to replace suffix
+ * @param suffix new suffix for path
+ * @return return file path with new suffix
+ */
+ tstring replaceSuffix(const tstring& path, const tstring& suffix=tstring());
+
+ class DirectoryIterator: DirectoryCallback {
+ public:
+ DirectoryIterator(const tstring& root=tstring()): root(root) {
+ recurse().withFiles().withFolders();
+ }
+
+ DirectoryIterator& recurse(bool v=true) {
+ theRecurse = v;
+ return *this;
+ }
+
+ DirectoryIterator& withFiles(bool v=true) {
+ theWithFiles = v;
+ return *this;
+ }
+
+ DirectoryIterator& withFolders(bool v=true) {
+ theWithFolders = v;
+ return *this;
+ }
+
+ tstring_array findItems() {
+ tstring_array reply;
+ findItems(reply);
+ return reply;
+ }
+
+ DirectoryIterator& findItems(tstring_array& v);
+
+ private:
+ virtual bool onFile(const tstring& path);
+ virtual bool onDirectory(const tstring& path);
+
+ private:
+ bool theRecurse;
+ bool theWithFiles;
+ bool theWithFolders;
+ tstring root;
+ tstring_array items;
+ };
+
+ // Returns array of all the files/sub-folders from the given directory, empty array if basedir is not a directory. The returned
+ // array is ordered from top down (i.e. dirs are listed first followed by subfolders and files).
+ // Order of subfolders and files is undefined but usually they are sorted by names.
+ inline tstring_array listAllContents(const tstring& basedir) {
+ return DirectoryIterator(basedir).findItems();
+ }
+
+ // Helper to construct path from multiple components.
+ //
+ // Sample usage:
+ // Construct "c:\Program Files\Java" string from three components
+ //
+ // tstring path = FileUtils::mkpath() << _T("c:")
+ // << _T("Program Files")
+ // << _T("Java");
+ //
+ class mkpath {
+ public:
+ operator const tstring& () const {
+ return path;
+ }
+
+ mkpath& operator << (const tstring& p) {
+ path = combinePath(path, p);
+ return *this;
+ }
+
+ // mimic std::string
+ const tstring::value_type* c_str() const {
+ return path.c_str();
+ }
+ private:
+ tstring path;
+ };
+
+ struct Directory {
+ Directory() {
+ }
+
+ Directory(const tstring &parent, const tstring &subdir) : parent(parent), subdir(subdir) {
+ }
+
+ operator tstring () const {
+ return getPath();
+ }
+
+ tstring getPath() const {
+ return combinePath(parent, subdir);
+ }
+
+ bool empty() const {
+ return (parent.empty() && subdir.empty());
+ }
+
+ tstring parent;
+ tstring subdir;
+ };
+
+ // Deletes list of files and directories in batch mode.
+ // Registered files and directories are deleted when destructor is called.
+ // Order or delete operations is following:
+ // - delete items registered with appendFile() calls;
+ // - delete items registered with appendAllFilesInDirectory() calls;
+ // - delete items registered with appendRecursiveDirectory() calls;
+ // - delete items registered with appendEmptyDirectory() calls.
+ class Deleter {
+ public:
+ Deleter() {
+ }
+
+ ~Deleter() {
+ execute();
+ }
+
+ typedef std::pair<tstring, int> Path;
+ typedef std::vector<Path> Paths;
+
+ /**
+ * Appends all records from the given deleter Deleter into this Deleter
+ * instance. On success array with records in the passed in Deleter
+ * instance is emptied.
+ */
+ Deleter& appendFrom(Deleter& other) {
+ Paths tmp(paths);
+ tmp.insert(tmp.end(), other.paths.begin(), other.paths.end());
+ Paths empty;
+ other.paths.swap(empty);
+ paths.swap(tmp);
+ return *this;
+ }
+
+ // Schedule file for deletion.
+ Deleter& appendFile(const tstring& path);
+
+ // Schedule files for deletion.
+ template <class It>
+ Deleter& appendFiles(It b, It e) {
+ for (It it = b; it != e; ++it) {
+ appendFile(*it);
+ }
+ return *this;
+ }
+
+ // Schedule files for deletion in the given directory.
+ template <class It>
+ Deleter& appendFiles(const tstring& dirname, It b, It e) {
+ for (It it = b; it != e; ++it) {
+ appendFile(FileUtils::mkpath() << dirname << *it);
+ }
+ return *this;
+ }
+
+ // Schedule empty directory for deletion with empty roots (up to Directory.parent).
+ Deleter& appendEmptyDirectory(const Directory& dir);
+
+ // Schedule empty directory for deletion without roots.
+ // This is a particular case of appendEmptyDirectory(const Directory& dir)
+ // with Directory(dirname(path), basename(path)).
+ Deleter& appendEmptyDirectory(const tstring& path);
+
+ // Schedule all file from the given directory for deletion.
+ Deleter& appendAllFilesInDirectory(const tstring& path);
+
+ // Schedule directory for recursive deletion.
+ Deleter& appendRecursiveDirectory(const tstring& path);
+
+ void cancel() {
+ paths.clear();
+ }
+
+ // Deletes scheduled files and directories. After this function
+ // is called internal list of scheduled items is emptied.
+ void execute();
+
+ private:
+ Paths paths;
+ };
+
+
+ /**
+ * Helper to write chunks of data into binary file.
+ * Creates temporary file in the same folder with destination file.
+ * All subsequent requests to save data chunks are redirected to temporary
+ * file. finalize() method closes temporary file stream and renames
+ * temporary file.
+ * If finalize() method is not called, temporary file is deleted in
+ * ~FileWriter(), destination file is not touched.
+ */
+ class FileWriter {
+ public:
+ explicit FileWriter(const tstring& path);
+
+ FileWriter& write(const void* buf, size_t bytes);
+
+ template <class Ctnr>
+ FileWriter& write(const Ctnr& buf) {
+ return write(buf.data(),
+ buf.size() * sizeof(typename Ctnr::value_type));
+ }
+
+ void finalize();
+
+ private:
+ // Not accessible by design!
+ FileWriter& write(const std::wstring& str);
+
+ private:
+ tstring tmpFile;
+ Deleter cleaner;
+ std::ofstream tmp;
+ tstring dstPath;
+ };
+} // FileUtils
+
+#endif // FILEUTILS_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/windows/native/libjpackage/IconSwap.cpp Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,203 @@
+/*
+ * 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.
+ */
+
+#include <stdio.h>
+#include <windows.h>
+#include <stdlib.h>
+#include <string>
+#include <malloc.h>
+
+using namespace std;
+
+// http://msdn.microsoft.com/en-us/library/ms997538.aspx
+
+typedef struct _ICONDIRENTRY {
+ BYTE bWidth;
+ BYTE bHeight;
+ BYTE bColorCount;
+ BYTE bReserved;
+ WORD wPlanes;
+ WORD wBitCount;
+ DWORD dwBytesInRes;
+ DWORD dwImageOffset;
+} ICONDIRENTRY, * LPICONDIRENTRY;
+
+typedef struct _ICONDIR {
+ WORD idReserved;
+ WORD idType;
+ WORD idCount;
+ ICONDIRENTRY idEntries[1];
+} ICONDIR, * LPICONDIR;
+
+// #pragmas are used here to insure that the structure's
+// packing in memory matches the packing of the EXE or DLL.
+#pragma pack(push)
+#pragma pack(2)
+
+typedef struct _GRPICONDIRENTRY {
+ BYTE bWidth;
+ BYTE bHeight;
+ BYTE bColorCount;
+ BYTE bReserved;
+ WORD wPlanes;
+ WORD wBitCount;
+ DWORD dwBytesInRes;
+ WORD nID;
+} GRPICONDIRENTRY, * LPGRPICONDIRENTRY;
+#pragma pack(pop)
+
+#pragma pack(push)
+#pragma pack(2)
+
+typedef struct _GRPICONDIR {
+ WORD idReserved;
+ WORD idType;
+ WORD idCount;
+ GRPICONDIRENTRY idEntries[1];
+} GRPICONDIR, * LPGRPICONDIR;
+#pragma pack(pop)
+
+void PrintError() {
+ LPVOID message = NULL;
+ DWORD error = GetLastError();
+
+ if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
+ FORMAT_MESSAGE_IGNORE_INSERTS, NULL, error,
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ (LPTSTR) & message, 0, NULL) != 0) {
+ printf("%S", (LPTSTR) message);
+ LocalFree(message);
+ }
+}
+
+// Note: We do not check here that iconTarget is valid icon.
+// Java code will already do this for us.
+
+bool ChangeIcon(wstring iconTarget, wstring launcher) {
+ WORD language = MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT);
+
+ HANDLE icon = CreateFile(iconTarget.c_str(), GENERIC_READ, 0, NULL,
+ OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+ if (icon == INVALID_HANDLE_VALUE) {
+ PrintError();
+ return false;
+ }
+
+ // Reading .ICO file
+ WORD idReserved, idType, idCount;
+
+ DWORD dwBytesRead;
+ ReadFile(icon, &idReserved, sizeof (WORD), &dwBytesRead, NULL);
+ ReadFile(icon, &idType, sizeof (WORD), &dwBytesRead, NULL);
+ ReadFile(icon, &idCount, sizeof (WORD), &dwBytesRead, NULL);
+
+ LPICONDIR lpid = (LPICONDIR) malloc(
+ sizeof (ICONDIR) + (sizeof (ICONDIRENTRY) * (idCount - 1)));
+ if (lpid == NULL) {
+ CloseHandle(icon);
+ printf("Error: Failed to allocate memory\n");
+ return false;
+ }
+
+ lpid->idReserved = idReserved;
+ lpid->idType = idType;
+ lpid->idCount = idCount;
+
+ ReadFile(icon, &lpid->idEntries[0], sizeof (ICONDIRENTRY) * lpid->idCount,
+ &dwBytesRead, NULL);
+
+ LPGRPICONDIR lpgid = (LPGRPICONDIR) malloc(
+ sizeof (GRPICONDIR) + (sizeof (GRPICONDIRENTRY) * (idCount - 1)));
+ if (lpid == NULL) {
+ CloseHandle(icon);
+ free(lpid);
+ printf("Error: Failed to allocate memory\n");
+ return false;
+ }
+
+ lpgid->idReserved = idReserved;
+ lpgid->idType = idType;
+ lpgid->idCount = idCount;
+
+ for (int i = 0; i < lpgid->idCount; i++) {
+ lpgid->idEntries[i].bWidth = lpid->idEntries[i].bWidth;
+ lpgid->idEntries[i].bHeight = lpid->idEntries[i].bHeight;
+ lpgid->idEntries[i].bColorCount = lpid->idEntries[i].bColorCount;
+ lpgid->idEntries[i].bReserved = lpid->idEntries[i].bReserved;
+ lpgid->idEntries[i].wPlanes = lpid->idEntries[i].wPlanes;
+ lpgid->idEntries[i].wBitCount = lpid->idEntries[i].wBitCount;
+ lpgid->idEntries[i].dwBytesInRes = lpid->idEntries[i].dwBytesInRes;
+ lpgid->idEntries[i].nID = i + 1;
+ }
+
+ // Store images in .EXE
+ HANDLE update = BeginUpdateResource(launcher.c_str(), FALSE);
+ if (update == NULL) {
+ free(lpid);
+ free(lpgid);
+ CloseHandle(icon);
+ PrintError();
+ return false;
+ }
+
+ for (int i = 0; i < lpid->idCount; i++) {
+ LPBYTE lpBuffer = (LPBYTE) malloc(lpid->idEntries[i].dwBytesInRes);
+ SetFilePointer(icon, lpid->idEntries[i].dwImageOffset,
+ NULL, FILE_BEGIN);
+ ReadFile(icon, lpBuffer, lpid->idEntries[i].dwBytesInRes,
+ &dwBytesRead, NULL);
+ if (!UpdateResource(update, RT_ICON,
+ MAKEINTRESOURCE(lpgid->idEntries[i].nID),
+ language, &lpBuffer[0], lpid->idEntries[i].dwBytesInRes)) {
+ free(lpBuffer);
+ free(lpid);
+ free(lpgid);
+ CloseHandle(icon);
+ PrintError();
+ return false;
+ }
+ free(lpBuffer);
+ }
+
+ free(lpid);
+ CloseHandle(icon);
+
+ if (!UpdateResource(update, RT_GROUP_ICON,
+ MAKEINTRESOURCE(1), language, &lpgid[0],
+ (sizeof (WORD) * 3) + (sizeof (GRPICONDIRENTRY) * lpgid->idCount))) {
+ free(lpgid);
+ PrintError();
+ return false;
+ }
+
+ free(lpgid);
+
+ if (EndUpdateResource(update, FALSE) == FALSE) {
+ PrintError();
+ return false;
+ }
+
+ return true;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/windows/native/libjpackage/IconSwap.h Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,35 @@
+/*
+ * 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.
+ */
+
+#ifndef ICONSWAP_H
+#define ICONSWAP_H
+
+#include <string>
+
+using namespace std;
+
+bool ChangeIcon(wstring iconTarget, wstring launcher);
+
+#endif // ICONSWAP_H
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/windows/native/libjpackage/Log.cpp Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,207 @@
+/*
+ * Copyright (c) 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.
+ */
+
+#include "Log.h"
+#include "SysInfo.h"
+#include "FileUtils.h"
+
+
+namespace {
+ //
+ // IMPORTANT: Static objects with non-trivial constructors are NOT allowed
+ // in logger module. Allocate buffers only and do lazy initialization of
+ // globals in Logger::getDefault().
+ //
+ // Logging subsystem is used almost in every module, and logging API can be
+ // called from constructors of static objects in various modules. As
+ // ordering of static objects initialization between modules is undefined,
+ // this means some module may call logging api before logging static
+ // variables are initialized if any. This will result in AV. To avoid such
+ // use cases keep logging module free from static variables that require
+ // initialization with functions called by CRT.
+ //
+
+ // by default log everything
+ const Logger::LogLevel defaultLogLevel = Logger::LOG_TRACE;
+
+ char defaultLogAppenderMemory[sizeof(StderrLogAppender)] = {};
+
+ char defaultLoggerMemory[sizeof(Logger)] = {};
+
+ NopLogAppender nopLogApender;
+
+ LPCTSTR getLogLevelStr(Logger::LogLevel level) {
+ switch (level) {
+ case Logger::LOG_TRACE:
+ return _T("TRACE");
+ case Logger::LOG_INFO:
+ return _T("INFO");
+ case Logger::LOG_WARNING:
+ return _T("WARNING");
+ case Logger::LOG_ERROR:
+ return _T("ERROR");
+ }
+ return _T("UNKNOWN");
+ }
+
+ tstring retrieveModuleName() {
+ try {
+ return FileUtils::basename(SysInfo::getCurrentModulePath());
+ } catch (const std::exception&) {
+ return _T("Unknown");
+ }
+ }
+
+ TCHAR moduleName[MAX_PATH] = { 'U', 'n', 'k', 'o', 'w', 'n', TCHAR(0) };
+
+ const LPCTSTR format = _T("[%04u/%02u/%02u %02u:%02u:%02u.%03u, %s (PID: %u, TID: %u), %s:%u (%s)]\n\t%s: %s\n");
+
+ enum State { NotInitialized, Initializing, Initialized };
+ State state = NotInitialized;
+}
+
+
+LogEvent::LogEvent() {
+ memset(this, 0, sizeof(*this));
+ moduleName = tstring();
+ logLevel = tstring();
+ fileName = tstring();
+ funcName = tstring();
+ message = tstring();
+}
+
+
+StderrLogAppender::StderrLogAppender() {
+}
+
+
+/*static*/
+Logger& Logger::defaultLogger()
+{
+ Logger* reply = reinterpret_cast<Logger*>(defaultLoggerMemory);
+
+ if (!reply->appender) {
+ // Memory leak by design. Not an issue at all as this is global
+ // object. OS will do resources clean up anyways when application
+ // terminates and the default log appender should live as long as
+ // application lives.
+ reply->appender = new (defaultLogAppenderMemory) StderrLogAppender();
+ }
+
+ if (Initializing == state) {
+ // Recursive call to Logger::defaultLogger.
+ moduleName[0] = TCHAR(0);
+ } else if (NotInitialized == state) {
+ state = Initializing;
+
+ tstring mname = retrieveModuleName();
+ mname.resize(_countof(moduleName) - 1);
+ std::memcpy(moduleName, mname.c_str(), mname.size());
+ moduleName[mname.size()] = TCHAR(0);
+
+ // if JPACKAGE_DEBUG environment variable is NOT set to "true" disable
+ // logging.
+ if (SysInfo::getEnvVariable(std::nothrow, L"JPACKAGE_DEBUG") != L"true") {
+ reply->appender = &nopLogApender;
+ }
+
+ state = Initialized;
+ }
+
+ return *reply;
+}
+
+Logger::Logger(LogAppender& appender, LogLevel logLevel)
+ : level(logLevel), appender(&appender)
+{
+}
+
+void Logger::setLogLevel(LogLevel logLevel)
+{
+ level = logLevel;
+}
+
+Logger::~Logger()
+{
+}
+
+
+bool Logger::isLoggable(LogLevel logLevel) const
+{
+ return logLevel >= level;
+}
+
+void Logger::log(LogLevel logLevel, LPCTSTR fileName, int lineNum, LPCTSTR funcName, const tstring& message) const
+{
+ LogEvent logEvent;
+
+ // [YYYY/MM/DD HH:MM:SS.ms, <module> (PID: processID, TID: threadID), fileName:lineNum (funcName)]
+ // <tab>LEVEL: message
+ GetLocalTime(&logEvent.ts);
+
+ logEvent.pid = GetCurrentProcessId();
+ logEvent.tid = GetCurrentThreadId();
+ logEvent.moduleName = moduleName;
+ logEvent.fileName = FileUtils::basename(fileName);
+ logEvent.funcName = funcName;
+ logEvent.logLevel = getLogLevelStr(logLevel);
+ logEvent.lineNum = lineNum;
+ logEvent.message = message;
+
+ appender->append(logEvent);
+}
+
+
+void StderrLogAppender::append(const LogEvent& v)
+{
+ const tstring out = tstrings::unsafe_format(format,
+ unsigned(v.ts.wYear), unsigned(v.ts.wMonth), unsigned(v.ts.wDay), // date
+ unsigned(v.ts.wHour), unsigned(v.ts.wMinute), unsigned(v.ts.wSecond), unsigned(v.ts.wMilliseconds), // time
+ v.moduleName.c_str(), v.pid, v.tid,
+ v.fileName.c_str(), v.lineNum, v.funcName.c_str(),
+ v.logLevel.c_str(),
+ v.message.c_str());
+
+ std::cerr << tstrings::toUtf8(out);
+}
+
+
+// Logger::ScopeTracer
+Logger::ScopeTracer::ScopeTracer(Logger &logger, LogLevel logLevel, LPCTSTR fileName, int lineNum, LPCTSTR funcName, const tstring& scopeName)
+ : log(logger), level(logLevel), file(fileName), line(lineNum), func(funcName), scope(scopeName), needLog(logger.isLoggable(logLevel))
+{
+ if (needLog) {
+ log.log(level, file.c_str(), line, func.c_str(), tstrings::any() << "Entering " << scope);
+ }
+}
+
+Logger::ScopeTracer::~ScopeTracer() {
+ if (needLog) {
+ // we don't know what line is end of scope at, so specify line 0
+ // and add note about line when the scope begins
+ log.log(level, file.c_str(), 0, func.c_str(),
+ tstrings::any() << "Exiting " << scope << " (entered at " << FileUtils::basename(file) << ":" << line << ")");
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/windows/native/libjpackage/Log.h Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,198 @@
+/*
+ * Copyright (c) 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.
+ */
+
+#ifndef __LOG_H_INCLUDED_
+#define __LOG_H_INCLUDED_
+
+#include <windows.h>
+#include "tstrings.h"
+
+
+/* Default logger (Logger::defaultLogger()) writes log messages to the default log file.
+ * Common scenario:
+ * - main() function configures default logger:
+ * FileLogAppender appender(_T("my_log_filename.log"));
+ * Logger::defaultLogger().setAppender(appender);
+ * Logger::defaultLogger().setLogLevel(LOG_INFO);
+ * If the default file name and log level are not set, _T("jusched.log")/LOG_TRACE are used.
+ *
+ * Logger fileName specifies only file name,
+ * full path for the log file depends on the platform (usually value of the TMP env. var)
+ */
+
+
+struct LogEvent {
+ SYSTEMTIME ts;
+ long tid;
+ long pid;
+ tstring moduleName;
+ tstring logLevel;
+ tstring fileName;
+ int lineNum;
+ tstring funcName;
+ tstring message;
+
+ LogEvent();
+};
+
+
+class LogAppender {
+public:
+ virtual ~LogAppender() {
+ }
+ virtual void append(const LogEvent& v) = 0;
+};
+
+
+class NopLogAppender: public LogAppender {
+public:
+ virtual void append(const LogEvent& v) {};
+};
+
+
+class TeeLogAppender: public LogAppender {
+public:
+ TeeLogAppender(LogAppender* first, LogAppender* second):
+ first(first), second(second) {
+ }
+ virtual ~TeeLogAppender() {
+ }
+ virtual void append(const LogEvent& v) {
+ if (first) {
+ first->append(v);
+ }
+ if (second) {
+ second->append(v);
+ }
+ }
+private:
+ LogAppender* first;
+ LogAppender* second;
+};
+
+
+/**
+ * Writes log events to stderr.
+ */
+class StderrLogAppender: public LogAppender {
+public:
+ explicit StderrLogAppender();
+
+ virtual void append(const LogEvent& v);
+};
+
+
+class Logger {
+public:
+ enum LogLevel {
+ LOG_TRACE,
+ LOG_INFO,
+ LOG_WARNING,
+ LOG_ERROR
+ };
+
+ static Logger& defaultLogger();
+
+ explicit Logger(LogAppender& appender, LogLevel logLevel = LOG_TRACE);
+ ~Logger();
+
+ LogAppender& setAppender(LogAppender& v) {
+ LogAppender& oldAppender = *appender;
+ appender = &v;
+ return oldAppender;
+ }
+
+ LogAppender& getAppender() const {
+ return *appender;
+ }
+
+ void setLogLevel(LogLevel logLevel);
+
+ bool isLoggable(LogLevel logLevel) const ;
+ void log(LogLevel logLevel, LPCTSTR fileName, int lineNum, LPCTSTR funcName, const tstring& message) const;
+ void log(LogLevel logLevel, LPCTSTR fileName, int lineNum,
+ LPCTSTR funcName, const tstrings::any& message) const {
+ return log(logLevel, fileName, lineNum, funcName, message.tstr());
+ }
+ void log(LogLevel logLevel, LPCTSTR fileName, int lineNum,
+ LPCTSTR funcName, tstring::const_pointer message) const {
+ return log(logLevel, fileName, lineNum, funcName, tstring(message));
+ }
+
+ // internal class for scope tracing
+ class ScopeTracer {
+ public:
+ ScopeTracer(Logger &logger, LogLevel logLevel, LPCTSTR fileName, int lineNum, LPCTSTR funcName, const tstring& scopeName);
+ ~ScopeTracer();
+
+ private:
+ const Logger &log;
+ const LogLevel level;
+ const bool needLog;
+ const tstring file;
+ const int line;
+ const tstring func;
+ const tstring scope;
+ };
+
+private:
+ LogLevel level;
+ LogAppender* appender;
+};
+
+
+// base logging macro
+#define LOGGER_LOG(logger, logLevel, message) \
+ do { \
+ if (logger.isLoggable(logLevel)) { \
+ logger.log(logLevel, _T(__FILE__), __LINE__, _T(__FUNCTION__), message); \
+ } \
+ } while(false)
+
+
+// custom logger macros
+#define LOGGER_TRACE(logger, message) LOGGER_LOG(logger, Logger::LOG_TRACE, message)
+#define LOGGER_INFO(logger, message) LOGGER_LOG(logger, Logger::LOG_INFO, message)
+#define LOGGER_WARNING(logger, message) LOGGER_LOG(logger, Logger::LOG_WARNING, message)
+#define LOGGER_ERROR(logger, message) LOGGER_LOG(logger, Logger::LOG_ERROR, message)
+// scope tracing macros
+#define LOGGER_TRACE_SCOPE(logger, scopeName) \
+ Logger::ScopeTracer tracer__COUNTER__(logger, Logger::LOG_TRACE, _T(__FILE__), __LINE__, _T(__FUNCTION__), scopeName)
+#define LOGGER_TRACE_FUNCTION(logger) LOGGER_TRACE_SCOPE(logger, _T(__FUNCTION__))
+
+
+// default logger macros
+#define LOG_TRACE(message) LOGGER_LOG(Logger::defaultLogger(), Logger::LOG_TRACE, message)
+#define LOG_INFO(message) LOGGER_LOG(Logger::defaultLogger(), Logger::LOG_INFO, message)
+#define LOG_WARNING(message) LOGGER_LOG(Logger::defaultLogger(), Logger::LOG_WARNING, message)
+#define LOG_ERROR(message) LOGGER_LOG(Logger::defaultLogger(), Logger::LOG_ERROR, message)
+// scope tracing macros
+// logs (_T("Entering ") + scopeName) at the beging, (_T("Exiting ") + scopeName) at the end of scope
+#define LOG_TRACE_SCOPE(scopeName) LOGGER_TRACE_SCOPE(Logger::defaultLogger(), scopeName)
+// logs (_T("Entering ") + functionName) at the beging, (_T("Exiting ") + __FUNCTION__) at the end of scope
+#define LOG_TRACE_FUNCTION() LOGGER_TRACE_FUNCTION(Logger::defaultLogger())
+
+
+#endif // __LOG_H_INCLUDED_
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/windows/native/libjpackage/ResourceEditor.cpp Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 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.
+ */
+
+#include <algorithm>
+#include <fstream>
+#include "ResourceEditor.h"
+#include "WinErrorHandling.h"
+#include "Log.h"
+
+
+ResourceEditor::FileLock::FileLock(const std::wstring& binaryPath) {
+ h = BeginUpdateResource(binaryPath.c_str(), FALSE);
+ if (NULL == h) {
+ JP_THROW(SysError(tstrings::any() << "BeginUpdateResource("
+ << binaryPath << ") failed", BeginUpdateResource));
+ }
+
+ discard(false);
+}
+
+
+ResourceEditor::FileLock::~FileLock() {
+ if (!EndUpdateResource(h, theDiscard)) {
+ JP_NO_THROW(JP_THROW(SysError(tstrings::any()
+ << "EndUpdateResource(" << h << ") failed.", EndUpdateResource)));
+ }
+}
+
+
+ResourceEditor::ResourceEditor() {
+ language(MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL)).type(unsigned(0)).id(unsigned(0));
+}
+
+
+ResourceEditor& ResourceEditor::type(unsigned v) {
+ return type(MAKEINTRESOURCE(v));
+}
+
+
+ResourceEditor& ResourceEditor::type(LPCWSTR v) {
+ if (IS_INTRESOURCE(v)) {
+ std::wostringstream printer;
+ printer << L"#" << reinterpret_cast<size_t>(v);
+ theType = printer.str();
+ theTypePtr = MAKEINTRESOURCE(static_cast<DWORD>(reinterpret_cast<DWORD_PTR>(v)));
+ } else {
+ theType = v;
+ theTypePtr = theType.c_str();
+ }
+ return *this;
+}
+
+
+ResourceEditor& ResourceEditor::id(unsigned v) {
+ return id(MAKEINTRESOURCE(v));
+}
+
+
+ResourceEditor& ResourceEditor::id(LPCWSTR v) {
+ if (IS_INTRESOURCE(v)) {
+ std::wostringstream printer;
+ printer << L"#" << reinterpret_cast<size_t>(v);
+ theId = printer.str();
+ } else {
+ theId = v;
+ theIdPtr = theId.c_str();
+ }
+ return *this;
+}
+
+
+ResourceEditor& ResourceEditor::apply(const FileLock& dstBinary,
+ std::istream& srcStream, std::streamsize size) {
+
+ typedef std::vector<BYTE> ByteArray;
+ ByteArray buf;
+ if (size <= 0) {
+ // Read the entire stream.
+ buf = ByteArray((std::istreambuf_iterator<char>(srcStream)),
+ std::istreambuf_iterator<char>());
+ } else {
+ buf.resize(size_t(size));
+ srcStream.read(reinterpret_cast<char*>(buf.data()), size);
+ }
+
+ auto reply = UpdateResource(dstBinary.get(), theTypePtr, theIdPtr, lang,
+ buf.data(), static_cast<DWORD>(buf.size()));
+ if (reply == FALSE) {
+ JP_THROW(SysError("UpdateResource() failed", UpdateResource));
+ }
+
+ return *this;
+}
+
+
+ResourceEditor& ResourceEditor::apply(const FileLock& dstBinary,
+ const std::wstring& srcFile) {
+ std::ifstream input(srcFile, std::ios_base::binary);
+ input.exceptions(std::ios::failbit | std::ios::badbit);
+ return apply(dstBinary, input);
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/windows/native/libjpackage/ResourceEditor.h Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 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.
+ */
+
+#ifndef RESOURCEEDITOR_H
+#define RESOURCEEDITOR_H
+
+#include <windows.h>
+#include <vector>
+#include <string>
+
+
+class ResourceEditor {
+public:
+ class FileLock {
+ public:
+ FileLock(const std::wstring& binaryPath);
+ ~FileLock();
+
+ HANDLE get() const {
+ return h;
+ }
+
+ void discard(bool v = true) {
+ theDiscard = v;
+ }
+
+ private:
+ FileLock(const FileLock&);
+ FileLock& operator=(const FileLock&);
+ private:
+ HANDLE h;
+ bool theDiscard;
+ };
+
+public:
+ ResourceEditor();
+
+ /**
+ * Set the language identifier of the resource to be updated.
+ */
+ ResourceEditor& language(unsigned v) {
+ lang = v;
+ return *this;
+ }
+
+ /**
+ * Set the resource type to be updated.
+ */
+ ResourceEditor& type(unsigned v);
+
+ /**
+ * Set the resource type to be updated.
+ */
+ ResourceEditor& type(LPCWSTR v);
+
+ /**
+ * Set resource ID.
+ */
+ ResourceEditor& id(unsigned v);
+
+ /**
+ * Set resource ID.
+ */
+ ResourceEditor& id(LPCWSTR v);
+
+ /**
+ * Relaces resource configured in the given binary with the given data stream.
+ */
+ ResourceEditor& apply(const FileLock& dstBinary, std::istream& srcStream, std::streamsize size=0);
+
+ /**
+ * Relaces resource configured in the given binary with contents of
+ * the given binary file.
+ */
+ ResourceEditor& apply(const FileLock& dstBinary, const std::wstring& srcFile);
+
+private:
+ unsigned lang;
+ std::wstring theId;
+ LPCWSTR theIdPtr;
+ std::wstring theType;
+ LPCWSTR theTypePtr;
+};
+
+#endif // #ifndef RESOURCEEDITOR_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/windows/native/libjpackage/SourceCodePos.h Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 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.
+ */
+
+
+#ifndef SourceCodePos_h
+#define SourceCodePos_h
+
+
+//
+// Position in source code.
+//
+
+struct SourceCodePos
+{
+ SourceCodePos(const char* fl, const char* fnc, int l):
+ file(fl), func(fnc), lno(l)
+ {
+ }
+
+ const char* file;
+ const char* func;
+ int lno;
+};
+
+
+// Initializes SourceCodePos instance with the
+// information from the point of calling.
+#define JP_SOURCE_CODE_POS SourceCodePos(__FILE__, __FUNCTION__, __LINE__)
+
+
+#endif // #ifndef SourceCodePos_h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/windows/native/libjpackage/SysInfo.h Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 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.
+ */
+
+
+#ifndef SYSINFO_H
+#define SYSINFO_H
+
+#include "tstrings.h"
+
+
+//
+// This namespace provides information about environment in which
+// the current application runs.
+// It is for general purpose use.
+// Functions in this namespaces are just queries about the environment.
+// Functions that change the existing environment like file or directory
+// creation should not be added to this namespace.
+//
+namespace SysInfo {
+ /**
+ * Returns temp dir (for the current user).
+ */
+ tstring getTempDir();
+
+ /**
+ * Returns absolute path to the process executable.
+ */
+ tstring getProcessModulePath();
+
+ /**
+ * Returns absolute path to the current executable module.
+ */
+ tstring getCurrentModulePath();
+
+ /**
+ * Returns value of environment variable with the given name.
+ * Throws exception if variable is not set or any other error occurred
+ * reading the value.
+ */
+ tstring getEnvVariable(const tstring& name);
+
+ /**
+ * Returns value of environment variable with the given name.
+ * Returns value of 'defValue' parameter if variable is not set or any
+ * other error occurred reading the value.
+ */
+ tstring getEnvVariable(const std::nothrow_t&, const tstring& name,
+ const tstring& defValue=tstring());
+
+ /**
+ * Returns 'true' if environment variable with the given name is set.
+ */
+ bool isEnvVariableSet(const tstring& name);
+}
+
+#endif // SYSINFO_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/windows/native/libjpackage/UniqueHandle.h Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 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.
+ */
+
+#ifndef UNIQUEHANDLE_H
+#define UNIQUEHANDLE_H
+
+#include <windows.h>
+#include <memory>
+
+
+struct WndHandleDeleter {
+ typedef HANDLE pointer;
+
+ void operator()(HANDLE h) {
+ ::CloseHandle(h);
+ }
+};
+
+typedef std::unique_ptr<HANDLE, WndHandleDeleter> UniqueHandle;
+
+#endif // #ifndef UNIQUEHANDLE_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/windows/native/libjpackage/Utils.cpp Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 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.
+ */
+
+#include "Windows.h"
+#include "Utils.h"
+
+#define BUFFER_SIZE 4096
+
+wstring GetStringFromJString(JNIEnv *pEnv, jstring jstr) {
+ const jchar *pJChars = pEnv->GetStringChars(jstr, NULL);
+ if (pJChars == NULL) {
+ return wstring(L"");
+ }
+
+ wstring wstr(pJChars);
+
+ pEnv->ReleaseStringChars(jstr, pJChars);
+
+ return wstr;
+}
+
+jstring GetJStringFromString(JNIEnv *pEnv, const jchar *unicodeChars, jsize len) {
+ return pEnv->NewString(unicodeChars, len);
+}
+
+wstring GetLongPath(wstring path) {
+ wstring result(L"");
+
+ size_t len = path.length();
+ if (len > 1) {
+ if (path.at(len - 1) == '\\') {
+ path.erase(len - 1);
+ }
+ }
+
+ TCHAR *pBuffer = new TCHAR[BUFFER_SIZE];
+ if (pBuffer != NULL) {
+ DWORD dwResult = GetLongPathName(path.c_str(), pBuffer, BUFFER_SIZE);
+ if (dwResult > 0 && dwResult < BUFFER_SIZE) {
+ result = wstring(pBuffer);
+ } else {
+ delete [] pBuffer;
+ pBuffer = new TCHAR[dwResult];
+ if (pBuffer != NULL) {
+ DWORD dwResult2 = GetLongPathName(path.c_str(), pBuffer, dwResult);
+ if (dwResult2 == (dwResult - 1)) {
+ result = wstring(pBuffer);
+ }
+ }
+ }
+
+ if (pBuffer != NULL) {
+ delete [] pBuffer;
+ }
+ }
+
+ return result;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/windows/native/libjpackage/Utils.h Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 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.
+ */
+
+#ifndef UTILS_H
+#define UTILS_H
+
+#include <string>
+#include "jni.h"
+
+using namespace std;
+
+wstring GetStringFromJString(JNIEnv *pEnv, jstring jstr);
+jstring GetJStringFromString(JNIEnv *pEnv, const jchar *unicodeChars, jsize len);
+
+wstring GetLongPath(wstring path);
+
+#endif // UTILS_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/windows/native/libjpackage/VersionInfoSwap.cpp Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,284 @@
+/*
+ * Copyright (c) 2015, 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.
+ */
+
+#include "VersionInfoSwap.h"
+
+#include <stdio.h>
+#include <tchar.h>
+
+#include <windows.h>
+#include <stdio.h>
+#include <Strsafe.h>
+#include <fstream>
+#include <locale>
+#include <codecvt>
+
+using namespace std;
+
+/*
+ * [Property file] contains key/value pairs
+ * The swap tool uses these pairs to create new version resource
+ *
+ * See MSDN docs for VS_VERSIONINFO structure that
+ * depicts organization of data in this version resource
+ * https://msdn.microsoft.com/en-us/library/ms647001(v=vs.85).aspx
+ *
+ * The swap tool makes changes in [Executable file]
+ * The tool assumes that the executable file has no version resource
+ * and it adds new resource in the executable file.
+ * If the executable file has an existing version resource, then
+ * the existing version resource will be replaced with new one.
+ */
+
+VersionInfoSwap::VersionInfoSwap(wstring executableProperties, wstring launcher) {
+ m_executableProperties = executableProperties;
+ m_launcher = launcher;
+}
+
+bool VersionInfoSwap::PatchExecutable() {
+ bool b = LoadFromPropertyFile();
+ if (!b) {
+ return false;
+ }
+
+ ByteBuffer buf;
+ b = CreateNewResource(&buf);
+ if (!b) {
+ return false;
+ }
+
+ b = this->UpdateResource(buf.getPtr(), static_cast<DWORD> (buf.getPos()));
+ if (!b) {
+ return false;
+ }
+
+ return true;
+}
+
+bool VersionInfoSwap::LoadFromPropertyFile() {
+ wifstream stream(m_executableProperties.c_str());
+
+ const locale empty_locale = locale::empty();
+ const locale utf8_locale =
+ locale(empty_locale, new codecvt_utf8<wchar_t>());
+ stream.imbue(utf8_locale);
+
+ if (stream.is_open() == true) {
+ int lineNumber = 1;
+ while (stream.eof() == false) {
+ wstring line;
+ getline(stream, line);
+
+ // # at the first character will comment out the line.
+ if (line.empty() == false && line[0] != '#') {
+ wstring::size_type pos = line.find('=');
+ if (pos != wstring::npos) {
+ wstring name = line.substr(0, pos);
+ wstring value = line.substr(pos + 1);
+ m_props[name] = value;
+ }
+ }
+ lineNumber++;
+ }
+ return true;
+ }
+
+ return false;
+}
+
+/*
+ * Creates new version resource
+ *
+ * MSND docs for VS_VERSION_INFO structure
+ * https://msdn.microsoft.com/en-us/library/ms647001(v=vs.85).aspx
+ */
+bool VersionInfoSwap::CreateNewResource(ByteBuffer *buf) {
+ size_t versionInfoStart = buf->getPos();
+ buf->AppendWORD(0);
+ buf->AppendWORD(sizeof VS_FIXEDFILEINFO);
+ buf->AppendWORD(0);
+ buf->AppendString(TEXT("VS_VERSION_INFO"));
+ buf->Align(4);
+
+ VS_FIXEDFILEINFO fxi;
+ if (!FillFixedFileInfo(&fxi)) {
+ return false;
+ }
+ buf->AppendBytes((BYTE*) & fxi, sizeof (VS_FIXEDFILEINFO));
+ buf->Align(4);
+
+ // String File Info
+ size_t stringFileInfoStart = buf->getPos();
+ buf->AppendWORD(0);
+ buf->AppendWORD(0);
+ buf->AppendWORD(1);
+ buf->AppendString(TEXT("StringFileInfo"));
+ buf->Align(4);
+
+ // String Table
+ size_t stringTableStart = buf->getPos();
+ buf->AppendWORD(0);
+ buf->AppendWORD(0);
+ buf->AppendWORD(1);
+
+ // "040904B0" = LANG_ENGLISH/SUBLANG_ENGLISH_US, Unicode CP
+ buf->AppendString(TEXT("040904B0"));
+ buf->Align(4);
+
+ // Strings
+ vector<wstring> keys;
+ for (map<wstring, wstring>::const_iterator it =
+ m_props.begin(); it != m_props.end(); ++it) {
+ keys.push_back(it->first);
+ }
+
+ for (size_t index = 0; index < keys.size(); index++) {
+ wstring name = keys[index];
+ wstring value = m_props[name];
+
+ size_t stringStart = buf->getPos();
+ buf->AppendWORD(0);
+ buf->AppendWORD(static_cast<WORD> (value.length()));
+ buf->AppendWORD(1);
+ buf->AppendString(name);
+ buf->Align(4);
+ buf->AppendString(value);
+ buf->ReplaceWORD(stringStart,
+ static_cast<WORD> (buf->getPos() - stringStart));
+ buf->Align(4);
+ }
+
+ buf->ReplaceWORD(stringTableStart,
+ static_cast<WORD> (buf->getPos() - stringTableStart));
+ buf->ReplaceWORD(stringFileInfoStart,
+ static_cast<WORD> (buf->getPos() - stringFileInfoStart));
+
+ // VarFileInfo
+ size_t varFileInfoStart = buf->getPos();
+ buf->AppendWORD(1);
+ buf->AppendWORD(0);
+ buf->AppendWORD(1);
+ buf->AppendString(TEXT("VarFileInfo"));
+ buf->Align(4);
+
+ buf->AppendWORD(0x24);
+ buf->AppendWORD(0x04);
+ buf->AppendWORD(0x00);
+ buf->AppendString(TEXT("Translation"));
+ buf->Align(4);
+ // "000004B0" = LANG_NEUTRAL/SUBLANG_ENGLISH_US, Unicode CP
+ buf->AppendWORD(0x0000);
+ buf->AppendWORD(0x04B0);
+
+ buf->ReplaceWORD(varFileInfoStart,
+ static_cast<WORD> (buf->getPos() - varFileInfoStart));
+ buf->ReplaceWORD(versionInfoStart,
+ static_cast<WORD> (buf->getPos() - versionInfoStart));
+
+ return true;
+}
+
+bool VersionInfoSwap::FillFixedFileInfo(VS_FIXEDFILEINFO *fxi) {
+ wstring fileVersion;
+ wstring productVersion;
+ int ret;
+
+ fileVersion = m_props[TEXT("FileVersion")];
+ productVersion = m_props[TEXT("ProductVersion")];
+
+ unsigned fv_1 = 0, fv_2 = 0, fv_3 = 0, fv_4 = 0;
+ unsigned pv_1 = 0, pv_2 = 0, pv_3 = 0, pv_4 = 0;
+
+ ret = _stscanf_s(fileVersion.c_str(),
+ TEXT("%d.%d.%d.%d"), &fv_1, &fv_2, &fv_3, &fv_4);
+ if (ret <= 0 || ret > 4) {
+ return false;
+ }
+
+ ret = _stscanf_s(productVersion.c_str(),
+ TEXT("%d.%d.%d.%d"), &pv_1, &pv_2, &pv_3, &pv_4);
+ if (ret <= 0 || ret > 4) {
+ return false;
+ }
+
+ fxi->dwSignature = 0xFEEF04BD;
+ fxi->dwStrucVersion = 0x00010000;
+
+ fxi->dwFileVersionMS = MAKELONG(fv_2, fv_1);
+ fxi->dwFileVersionLS = MAKELONG(fv_4, fv_3);
+ fxi->dwProductVersionMS = MAKELONG(pv_2, pv_1);
+ fxi->dwProductVersionLS = MAKELONG(pv_4, pv_3);
+
+ fxi->dwFileFlagsMask = 0;
+ fxi->dwFileFlags = 0;
+ fxi->dwFileOS = VOS_NT_WINDOWS32;
+
+ wstring exeExt =
+ m_launcher.substr(m_launcher.find_last_of(TEXT(".")));
+ if (exeExt == TEXT(".exe")) {
+ fxi->dwFileType = VFT_APP;
+ } else if (exeExt == TEXT(".dll")) {
+ fxi->dwFileType = VFT_DLL;
+ } else {
+ fxi->dwFileType = VFT_UNKNOWN;
+ }
+ fxi->dwFileSubtype = 0;
+
+ fxi->dwFileDateLS = 0;
+ fxi->dwFileDateMS = 0;
+
+ return true;
+}
+
+/*
+ * Adds new resource in the executable
+ */
+bool VersionInfoSwap::UpdateResource(LPVOID lpResLock, DWORD size) {
+
+ HANDLE hUpdateRes;
+ BOOL r;
+
+ hUpdateRes = ::BeginUpdateResource(m_launcher.c_str(), FALSE);
+ if (hUpdateRes == NULL) {
+ return false;
+ }
+
+ r = ::UpdateResource(hUpdateRes,
+ RT_VERSION,
+ MAKEINTRESOURCE(VS_VERSION_INFO),
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL),
+ lpResLock,
+ size);
+
+ if (!r) {
+ return false;
+ }
+
+ if (!::EndUpdateResource(hUpdateRes, FALSE)) {
+ return false;
+ }
+
+ return true;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/windows/native/libjpackage/VersionInfoSwap.h Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2015, 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.
+ */
+
+#ifndef VERSIONINFOSWAP_H
+#define VERSIONINFOSWAP_H
+
+#include "ByteBuffer.h"
+#include <map>
+
+using namespace std;
+
+class VersionInfoSwap {
+public:
+ VersionInfoSwap(wstring executableProperties, wstring launcher);
+
+ bool PatchExecutable();
+
+private:
+ wstring m_executableProperties;
+ wstring m_launcher;
+
+ map<wstring, wstring> m_props;
+
+ bool LoadFromPropertyFile();
+ bool CreateNewResource(ByteBuffer *buf);
+ bool UpdateResource(LPVOID lpResLock, DWORD size);
+ bool FillFixedFileInfo(VS_FIXEDFILEINFO *fxi);
+};
+
+#endif // VERSIONINFOSWAP_H
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/windows/native/libjpackage/WinErrorHandling.cpp Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,125 @@
+/*
+ * Copyright (c) 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.
+ */
+
+#include "WinErrorHandling.h"
+#include "Log.h"
+#include "SysInfo.h"
+#include "FileUtils.h"
+
+
+namespace {
+
+std::string makeMessage(const std::string& msg, const char* label,
+ const void* c, DWORD errorCode) {
+ std::ostringstream err;
+ err << (label ? label : "Some error") << " [" << errorCode << "]";
+
+ HMODULE hmodule = NULL;
+ if (c) {
+ GetModuleHandleEx(
+ GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
+ reinterpret_cast<LPCTSTR>(c),
+ &hmodule);
+
+ if (!hmodule) {
+ LOG_WARNING(tstrings::any() << "GetModuleHandleEx() failed for " << c << " address.");
+ }
+ }
+ if (hmodule || !c) {
+ err << "(" << SysError::getSysErrorMessage(errorCode, hmodule) << ")";
+ }
+
+ return joinErrorMessages(msg, err.str());
+}
+
+
+std::wstring getSystemMessageDescription(DWORD messageId, HMODULE moduleHandle) {
+ LPWSTR pMsg = NULL;
+ std::wstring descr;
+
+ // we always retrieve UNICODE description from system,
+ // convert it to utf8 if UNICODE is not defined
+
+ while (true) {
+ DWORD res = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER
+ | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS
+ | (moduleHandle != NULL ? FORMAT_MESSAGE_FROM_HMODULE : 0),
+ moduleHandle, messageId, 0, (LPWSTR)&pMsg, 0, NULL);
+ if (res > 0) {
+ // replace all non-printed chars with space
+ for (DWORD i=0; i<res; i++) {
+ if (pMsg[i] < L' ') {
+ pMsg[i] = L' ';
+ }
+ }
+ // trim right (spaces and dots)
+ for (DWORD i=res; i>0; i--) {
+ if (pMsg[i] > L' ' && pMsg[i] != L'.') {
+ break;
+ }
+ pMsg[i] = 0;
+ }
+
+ descr = pMsg;
+
+ LocalFree(pMsg);
+ } else {
+ // if we fail to get description for specific moduleHandle,
+ // try to get "common" description.
+ if (moduleHandle != NULL) {
+ moduleHandle = NULL;
+ continue;
+ }
+ descr = L"No description available";
+ }
+ break;
+ }
+
+ return descr;
+}
+
+} // namespace
+
+
+SysError::SysError(const tstrings::any& msg, const void* caller, DWORD ec,
+ const char* label):
+ std::runtime_error(makeMessage(msg.str(), label, caller, ec)) {
+}
+
+std::wstring SysError::getSysErrorMessage(DWORD errCode, HMODULE moduleHandle) {
+ tstrings::any msg;
+ msg << "system error " << errCode
+ << " (" << getSystemMessageDescription(errCode, moduleHandle) << ")";
+ return msg.tstr();
+}
+
+std::wstring SysError::getComErrorMessage(HRESULT hr) {
+ HRESULT hrOrig = hr;
+ // for FACILITY_WIN32 facility we need to reset hiword
+ if(HRESULT_FACILITY(hr) == FACILITY_WIN32) {
+ hr = HRESULT_CODE(hr);
+ }
+ return tstrings::format(_T("COM error 0x%08X (%s)"), hrOrig, getSystemMessageDescription(hr, NULL));
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/windows/native/libjpackage/WinErrorHandling.h Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 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.
+ */
+
+
+#ifndef WinErrorHandling_h
+#define WinErrorHandling_h
+
+
+#include "ErrorHandling.h"
+
+
+class SysError : public std::runtime_error {
+public:
+ SysError(const tstrings::any& msg, const void* caller,
+ DWORD errorCode=GetLastError(), const char* label="System error");
+
+ // returns string "system error <errCode> (error_description)"
+ // in UNICODE is not defined, the string returned is utf8-encoded
+ static std::wstring getSysErrorMessage(DWORD errCode = GetLastError(), HMODULE moduleHandle = NULL);
+
+ // returns string "COM error 0x<hr> (error_description)"
+ // in UNICODE is not defined, the string returned is utf8-encoded
+ static std::wstring getComErrorMessage(HRESULT hr);
+};
+
+#endif // #ifndef WinErrorHandling_h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/windows/native/libjpackage/WinSysInfo.cpp Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,174 @@
+/*
+ * Copyright (c) 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.
+ */
+
+#include "WinSysInfo.h"
+#include "FileUtils.h"
+#include "WinErrorHandling.h"
+
+namespace SysInfo {
+
+tstring getTempDir() {
+ std::vector<TCHAR> buffer(MAX_PATH);
+ DWORD res = GetTempPath(static_cast<DWORD>(buffer.size()), buffer.data());
+ if (res > buffer.size()) {
+ buffer.resize(res);
+ GetTempPath(static_cast<DWORD>(buffer.size()), buffer.data());
+ }
+ return FileUtils::removeTrailingSlash(buffer.data());
+}
+
+namespace {
+
+template <class Func>
+tstring getSystemDirImpl(Func func, const std::string& label) {
+ std::vector<TCHAR> buffer(MAX_PATH);
+ for (int i=0; i<2; i++) {
+ DWORD res = func(buffer.data(), static_cast<DWORD>(buffer.size()));
+ if (!res) {
+ JP_THROW(SysError(label + " failed", func));
+ }
+ if (res < buffer.size()) {
+ return FileUtils::removeTrailingSlash(buffer.data());
+ }
+ buffer.resize(res + 1);
+ }
+ JP_THROW("Unexpected reply from" + label);
+}
+
+} // namespace
+
+tstring getSystem32Dir() {
+ return getSystemDirImpl(GetSystemDirectory, "GetSystemDirectory");
+}
+
+tstring getWIPath() {
+ return FileUtils::mkpath() << getSystem32Dir() << _T("msiexec.exe");
+}
+
+namespace {
+
+tstring getModulePath(HMODULE h)
+{
+ std::vector<TCHAR> buf(MAX_PATH);
+ DWORD len = 0;
+ while (true) {
+ len = GetModuleFileName(h, buf.data(), (DWORD)buf.size());
+ if (len < buf.size()) {
+ break;
+ }
+ // buffer is too small, increase it
+ buf.resize(buf.size() * 2);
+ }
+
+ if (len == 0) {
+ // error occured
+ JP_THROW(SysError("GetModuleFileName failed", GetModuleFileName));
+ }
+ return tstring(buf.begin(), buf.begin() + len);
+}
+
+} // namespace
+
+tstring getProcessModulePath() {
+ return getModulePath(NULL);
+}
+
+HMODULE getCurrentModuleHandle()
+{
+ // get module handle for the address of this function
+ LPCWSTR address = reinterpret_cast<LPCWSTR>(getCurrentModuleHandle);
+ HMODULE hmodule = NULL;
+ if (!GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
+ address, &hmodule))
+ {
+ JP_THROW(SysError(tstrings::any() << "GetModuleHandleExW failed", GetModuleHandleExW));
+ }
+ return hmodule;
+}
+
+tstring getCurrentModulePath()
+{
+ return getModulePath(getCurrentModuleHandle());
+}
+
+namespace {
+
+tstring getEnvVariableImpl(const tstring& name, bool* errorOccured=0) {
+ std::vector<TCHAR> buf(10);
+ SetLastError(ERROR_SUCCESS);
+ const DWORD size = GetEnvironmentVariable(name.c_str(), buf.data(),
+ DWORD(buf.size()));
+ if (GetLastError() == ERROR_ENVVAR_NOT_FOUND) {
+ if (errorOccured) {
+ *errorOccured = true;
+ return tstring();
+ }
+ JP_THROW(SysError(tstrings::any() << "GetEnvironmentVariable("
+ << name << ") failed. Variable not set", GetEnvironmentVariable));
+ }
+
+ if (size > buf.size()) {
+ buf.resize(size);
+ GetEnvironmentVariable(name.c_str(), buf.data(), DWORD(buf.size()));
+ if (GetLastError() != ERROR_SUCCESS) {
+ if (errorOccured) {
+ *errorOccured = true;
+ return tstring();
+ }
+ JP_THROW(SysError(tstrings::any() << "GetEnvironmentVariable("
+ << name << ") failed", GetEnvironmentVariable));
+ }
+ }
+
+ if (errorOccured) {
+ *errorOccured = false;
+ }
+ return tstring(buf.data());
+}
+
+} // namespace
+
+tstring getEnvVariable(const tstring& name) {
+ return getEnvVariableImpl(name);
+}
+
+tstring getEnvVariable(const std::nothrow_t&, const tstring& name,
+ const tstring& defValue) {
+ bool errorOccured = false;
+ const tstring reply = getEnvVariableImpl(name, &errorOccured);
+ if (errorOccured) {
+ return defValue;
+ }
+ return reply;
+}
+
+bool isEnvVariableSet(const tstring& name) {
+ TCHAR unused[1];
+ SetLastError(ERROR_SUCCESS);
+ GetEnvironmentVariable(name.c_str(), unused, _countof(unused));
+ return GetLastError() != ERROR_ENVVAR_NOT_FOUND;
+}
+
+} // end of namespace SysInfo
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/windows/native/libjpackage/WinSysInfo.h Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 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.
+ */
+
+
+#ifndef WINSYSINFO_H
+#define WINSYSINFO_H
+
+#include "SysInfo.h"
+
+
+//
+// Windows specific SysInfo.
+//
+namespace SysInfo {
+ // gets Windows System folder. A typical path is C:\Windows\System32.
+ tstring getSystem32Dir();
+
+ // returns full path to msiexec.exe executable
+ tstring getWIPath();
+
+ // Returns handle of the current module (exe or dll).
+ // The function assumes this code is statically linked to the module.
+ HMODULE getCurrentModuleHandle();
+}
+
+
+#endif // WINSYSINFO_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/windows/native/libjpackage/WindowsRegistry.cpp Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,161 @@
+/*
+ * Copyright (c) 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.
+ */
+
+#include <Windows.h>
+#include <strsafe.h>
+#include <tchar.h>
+#include <jni.h>
+
+#include "Utils.h"
+
+// Max value name size per MSDN plus NULL
+#define VALUE_NAME_SIZE 16384
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+#undef jdk_jpackage_internal_WindowsRegistry_HKEY_LOCAL_MACHINE
+#define jdk_jpackage_internal_WindowsRegistry_HKEY_LOCAL_MACHINE 1L
+
+ /*
+ * Class: jdk_jpackage_internal_WindowsRegistry
+ * Method: readDwordValue
+ * Signature: (ILjava/lang/String;Ljava/lang/String;I)I
+ */
+ JNIEXPORT jint JNICALL Java_jdk_jpackage_internal_WindowsRegistry_readDwordValue(
+ JNIEnv *pEnv, jclass c, jint key, jstring jSubKey, jstring jValue, jint defaultValue) {
+ jint jResult = defaultValue;
+
+ if (key != jdk_jpackage_internal_WindowsRegistry_HKEY_LOCAL_MACHINE) {
+ return jResult;
+ }
+
+ wstring subKey = GetStringFromJString(pEnv, jSubKey);
+ wstring value = GetStringFromJString(pEnv, jValue);
+
+ HKEY hSubKey = NULL;
+ LSTATUS status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, subKey.c_str(), 0,
+ KEY_QUERY_VALUE, &hSubKey);
+ if (status == ERROR_SUCCESS) {
+ DWORD dwValue = 0;
+ DWORD cbData = sizeof (DWORD);
+ status = RegQueryValueEx(hSubKey, value.c_str(), NULL, NULL,
+ (LPBYTE) & dwValue, &cbData);
+ if (status == ERROR_SUCCESS) {
+ jResult = (jint) dwValue;
+ }
+
+ RegCloseKey(hSubKey);
+ }
+
+ return jResult;
+ }
+
+ /*
+ * Class: jdk_jpackage_internal_WindowsRegistry
+ * Method: openRegistryKey
+ * Signature: (ILjava/lang/String;)J
+ */
+ JNIEXPORT jlong JNICALL Java_jdk_jpackage_internal_WindowsRegistry_openRegistryKey(
+ JNIEnv *pEnv, jclass c, jint key, jstring jSubKey) {
+ if (key != jdk_jpackage_internal_WindowsRegistry_HKEY_LOCAL_MACHINE) {
+ return 0;
+ }
+
+ wstring subKey = GetStringFromJString(pEnv, jSubKey);
+ HKEY hSubKey = NULL;
+ LSTATUS status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, subKey.c_str(), 0,
+ KEY_QUERY_VALUE, &hSubKey);
+ if (status == ERROR_SUCCESS) {
+ return (jlong)hSubKey;
+ }
+
+ return 0;
+ }
+
+ /*
+ * Class: jdk_jpackage_internal_WindowsRegistry
+ * Method: enumRegistryValue
+ * Signature: (JI)Ljava/lang/String;
+ */
+ JNIEXPORT jstring JNICALL Java_jdk_jpackage_internal_WindowsRegistry_enumRegistryValue(
+ JNIEnv *pEnv, jclass c, jlong lKey, jint jIndex) {
+ HKEY hKey = (HKEY)lKey;
+ TCHAR valueName[VALUE_NAME_SIZE] = {0}; // Max value name size per MSDN plus NULL
+ DWORD cchValueName = VALUE_NAME_SIZE;
+ LSTATUS status = RegEnumValue(hKey, (DWORD)jIndex, valueName, &cchValueName,
+ NULL, NULL, NULL, NULL);
+ if (status == ERROR_SUCCESS) {
+ size_t chLength = 0;
+ if (StringCchLength(valueName, VALUE_NAME_SIZE, &chLength) == S_OK) {
+ return GetJStringFromString(pEnv, valueName, (jsize)chLength);
+ }
+ }
+
+ return NULL;
+ }
+
+ /*
+ * Class: jdk_jpackage_internal_WindowsRegistry
+ * Method: closeRegistryKey
+ * Signature: (J)V
+ */
+ JNIEXPORT void JNICALL Java_jdk_jpackage_internal_WindowsRegistry_closeRegistryKey(
+ JNIEnv *pEnc, jclass c, jlong lKey) {
+ HKEY hKey = (HKEY)lKey;
+ RegCloseKey(hKey);
+ }
+
+ /*
+ * Class: jdk_jpackage_internal_WindowsRegistry
+ * Method: comparePaths
+ * Signature: (Ljava/lang/String;Ljava/lang/String;)Z
+ */
+ JNIEXPORT jboolean JNICALL Java_jdk_jpackage_internal_WindowsRegistry_comparePaths(
+ JNIEnv *pEnv, jclass c, jstring jPath1, jstring jPath2) {
+ wstring path1 = GetStringFromJString(pEnv, jPath1);
+ wstring path2 = GetStringFromJString(pEnv, jPath2);
+
+ path1 = GetLongPath(path1);
+ path2 = GetLongPath(path2);
+
+ if (path1.length() == 0 || path2.length() == 0) {
+ return JNI_FALSE;
+ }
+
+ if (path1.length() != path2.length()) {
+ return JNI_FALSE;
+ }
+
+ if (_tcsnicmp(path1.c_str(), path2.c_str(), path1.length()) == 0) {
+ return JNI_TRUE;
+ }
+
+ return JNI_FALSE;
+ }
+
+#ifdef __cplusplus
+}
+#endif
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/windows/native/libjpackage/jpackage.cpp Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 2011, 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.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string>
+#include <windows.h>
+
+#include "ResourceEditor.h"
+#include "WinErrorHandling.h"
+#include "IconSwap.h"
+#include "VersionInfoSwap.h"
+#include "Utils.h"
+
+using namespace std;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ /*
+ * Class: jdk_jpackage_internal_WindowsAppImageBuilder
+ * Method: iconSwap
+ * Signature: (Ljava/lang/String;Ljava/lang/String;)I
+ */
+ JNIEXPORT jint JNICALL Java_jdk_jpackage_internal_WindowsAppImageBuilder_iconSwap(
+ JNIEnv *pEnv, jclass c, jstring jIconTarget, jstring jLauncher) {
+ wstring iconTarget = GetStringFromJString(pEnv, jIconTarget);
+ wstring launcher = GetStringFromJString(pEnv, jLauncher);
+
+ if (ChangeIcon(iconTarget, launcher)) {
+ return 0;
+ }
+
+ return 1;
+ }
+
+ /*
+ * Class: jdk_jpackage_internal_WindowsAppImageBuilder
+ * Method: versionSwap
+ * Signature: (Ljava/lang/String;Ljava/lang/String;)I
+ */
+ JNIEXPORT jint JNICALL Java_jdk_jpackage_internal_WindowsAppImageBuilder_versionSwap(
+ JNIEnv *pEnv, jclass c, jstring jExecutableProperties, jstring jLauncher) {
+
+ wstring executableProperties = GetStringFromJString(pEnv, jExecutableProperties);
+ wstring launcher = GetStringFromJString(pEnv, jLauncher);
+
+ VersionInfoSwap vs(executableProperties, launcher);
+ if (vs.PatchExecutable()) {
+ return 0;
+ }
+
+ return 1;
+ }
+
+ /*
+ * Class: jdk_jpackage_internal_WinExeBundler
+ * Method: embedMSI
+ * Signature: (Ljava/lang/String;Ljava/lang/String;)I
+ */
+ JNIEXPORT jint JNICALL Java_jdk_jpackage_internal_WinExeBundler_embedMSI(
+ JNIEnv *pEnv, jclass c, jstring jexePath, jstring jmsiPath) {
+
+ const wstring exePath = GetStringFromJString(pEnv, jexePath);
+ const wstring msiPath = GetStringFromJString(pEnv, jmsiPath);
+
+ JP_TRY;
+
+ ResourceEditor()
+ .id(L"msi")
+ .type(RT_RCDATA)
+ .apply(ResourceEditor::FileLock(exePath), msiPath);
+
+ return 0;
+
+ JP_CATCH_ALL;
+
+ return 1;
+ }
+
+ BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) {
+ return TRUE;
+ }
+
+#ifdef __cplusplus
+}
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/windows/native/libjpackage/tstrings.cpp Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,280 @@
+/*
+ * Copyright (c) 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.
+ */
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdexcept>
+#include <algorithm>
+
+#include "tstrings.h"
+#include "ErrorHandling.h"
+
+
+namespace tstrings {
+
+/* Create formatted string
+ */
+tstring unsafe_format(tstring::const_pointer format, ...) {
+ if (!format) {
+ throw std::invalid_argument("Destination buffer can't be NULL");
+ }
+
+ tstring fmtout;
+ int ret;
+ const int inc = 256;
+
+ va_list args;
+ va_start(args, format);
+ do {
+ fmtout.resize(fmtout.size() + inc);
+#ifdef _MSC_VER
+ ret = _vsntprintf_s(&*fmtout.begin(), fmtout.size(), _TRUNCATE, format, args);
+#else
+ // With g++ this compiles only with '-std=gnu++0x' option
+ ret = vsnprintf(&*fmtout.begin(), fmtout.size(), format, args);
+#endif
+ } while(-1 == ret);
+ va_end(args);
+
+ //update string size by actual value
+ fmtout.resize(ret);
+
+ return fmtout;
+}
+
+/*
+ * Tests if two strings are equal according to CompareType.
+ *
+ * a - string to compare
+ * b - string to compare
+ * ct - CASE_SENSITIVE: case sensitive comparing type
+ * IGNORE_CASE: case insensitive comparing type
+ */
+bool equals(const tstring& a, const tstring& b, const CompareType ct) {
+ if (IGNORE_CASE==ct) {
+ return toLower(a) == toLower(b);
+ }
+ return a == b;
+}
+
+bool startsWith(const tstring &str, const tstring &substr, const CompareType ct)
+{
+ if (str.size() < substr.size()) {
+ return false;
+ }
+ const tstring startOfStr = str.substr(0, substr.size());
+ return tstrings::equals(startOfStr, substr, ct);
+}
+
+bool endsWith(const tstring &str, const tstring &substr, const CompareType ct)
+{
+ if (str.size() < substr.size()) {
+ return false;
+ }
+ const tstring endOfStr = str.substr(str.size() - substr.size());
+ return tstrings::equals(endOfStr, substr, ct);
+}
+
+/*
+ * Split string into a vector with given delimiter string
+ *
+ * strVector - string vector to store split tstring
+ * str - string to split
+ * delimiter - delimiter to split the string around
+ * st - ST_ALL: return value includes an empty string
+ * ST_EXCEPT_EMPTY_STRING: return value does not include an empty string
+ *
+ * Note: It does not support multiple delimiters
+ */
+void split(tstring_array &strVector, const tstring &str,
+ const tstring &delimiter, const SplitType st) {
+ tstring::size_type start = 0, end = 0, length = str.length();
+
+ if (length == 0 || delimiter.length() == 0) {
+ return;
+ }
+
+ end = str.find(delimiter, start);
+ while(end != tstring::npos) {
+ if(st == ST_ALL || end - start > 1 ) {
+ strVector.push_back(str.substr(start, end == tstring::npos ?
+ tstring::npos : end - start));
+ }
+ start = end > (tstring::npos - delimiter.size()) ?
+ tstring::npos : end + delimiter.size();
+ end = str.find(delimiter, start);
+ }
+
+ if(st == ST_ALL || start < length) {
+ strVector.push_back(str.substr(start, length - start));
+ }
+}
+
+/*
+ * Convert uppercase letters to lowercase
+ */
+tstring toLower(const tstring& str) {
+ tstring lower(str);
+ tstring::iterator ok = std::transform(lower.begin(), lower.end(),
+ lower.begin(), tolower);
+ if (ok!=lower.end()) {
+ lower.resize(0);
+ }
+ return lower;
+}
+
+
+/*
+ * Replace all substring occurrences in a tstring.
+ * If 'str' or 'search' is empty the function returns 'str'.
+ * The given 'str' remains unchanged in any case.
+ * The function returns changed copy of 'str'.
+ */
+tstring replace(const tstring &str, const tstring &search, const tstring &replace)
+{
+ if (search.empty()) {
+ return str;
+ }
+
+ tstring s(str);
+
+ for (size_t pos = 0; ; pos += replace.length()) {
+ pos = s.find(search, pos);
+ if (pos == tstring::npos) {
+ break;
+ }
+ s.erase(pos, search.length());
+ s.insert(pos, replace);
+ }
+ return s;
+}
+
+
+/*
+ * Remove trailing spaces
+ */
+
+tstring trim(const tstring& str, const tstring& whitespace) {
+ const size_t strBegin = str.find_first_not_of(whitespace);
+ if (strBegin == std::string::npos) {
+ return tstring(); // no content
+ }
+
+ const size_t strEnd = str.find_last_not_of(whitespace);
+ const size_t strRange = strEnd - strBegin + 1;
+
+ return str.substr(strBegin, strRange);
+}
+
+} // namespace tstrings
+
+
+#ifdef TSTRINGS_WITH_WCHAR
+namespace tstrings {
+
+namespace {
+/*
+ * Converts UTF16-encoded string into multi-byte string of the given encoding.
+ */
+std::string toMultiByte(const std::wstring& utf16str, int encoding) {
+ std::string reply;
+ do {
+ int cm = WideCharToMultiByte(encoding,
+ 0,
+ utf16str.c_str(),
+ int(utf16str.size()),
+ NULL,
+ 0,
+ NULL,
+ NULL);
+ if (cm < 0) {
+ JP_THROW("Unexpected reply from WideCharToMultiByte()");
+ }
+ if (0 == cm) {
+ break;
+ }
+
+ reply.resize(cm);
+ int cm2 = WideCharToMultiByte(encoding,
+ 0,
+ utf16str.c_str(),
+ int(utf16str.size()),
+ &*reply.begin(),
+ cm,
+ NULL,
+ NULL);
+ if (cm != cm2) {
+ JP_THROW("Unexpected reply from WideCharToMultiByte()");
+ }
+ } while(0);
+
+ return reply;
+}
+
+/*
+ * Converts multi-byte string of the given encoding into UTF16-encoded string.
+ */
+std::wstring fromMultiByte(const std::string& str, int encoding) {
+ std::wstring utf16;
+ do {
+ int cw = MultiByteToWideChar(encoding,
+ MB_ERR_INVALID_CHARS,
+ str.c_str(),
+ int(str.size()),
+ NULL,
+ 0);
+ if (cw < 0) {
+ JP_THROW("Unexpected reply from MultiByteToWideChar()");
+ }
+ if (0 == cw) {
+ break;
+ }
+
+ utf16.resize(cw);
+ int cw2 = MultiByteToWideChar(encoding,
+ MB_ERR_INVALID_CHARS,
+ str.c_str(),
+ int(str.size()),
+ &*utf16.begin(),
+ cw);
+ if (cw != cw2) {
+ JP_THROW("Unexpected reply from MultiByteToWideChar()");
+ }
+ } while(0);
+
+ return utf16;
+}
+} // namespace
+
+std::string toUtf8(const std::wstring& utf16str) {
+ return toMultiByte(utf16str, CP_UTF8);
+}
+
+std::wstring toUtf16(const std::string& utf8str) {
+ return fromMultiByte(utf8str, CP_UTF8);
+}
+
+} // namespace tstrings
+#endif // ifdef TSTRINGS_WITH_WCHAR
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/windows/native/libjpackage/tstrings.h Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,415 @@
+/*
+ * Copyright (c) 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.
+ */
+
+#ifndef TSTRINGS_H
+#define TSTRINGS_H
+
+#ifdef _MSC_VER
+# define TSTRINGS_WITH_WCHAR
+#endif
+
+#ifdef TSTRINGS_WITH_WCHAR
+#include <windows.h>
+#include <tchar.h>
+// Want compiler issue C4995 warnings for encounters of deprecated functions.
+#include <strsafe.h>
+#endif
+
+// STL's string header depends on deprecated functions.
+// We don't care about warnings from STL header, so disable them locally.
+#ifdef _MSC_VER
+# pragma warning(push)
+# pragma warning(disable:4995)
+#endif
+
+#include <string>
+#include <sstream>
+#include <iostream>
+#include <vector>
+
+#ifdef _MSC_VER
+# pragma warning(pop)
+#endif
+
+
+#ifndef _T
+# define _T(x) x
+#endif
+
+
+#ifdef TSTRINGS_WITH_WCHAR
+typedef std::wstring tstring;
+typedef std::wostringstream tostringstream;
+typedef std::wistringstream tistringstream;
+typedef std::wstringstream tstringstream;
+typedef std::wistream tistream;
+typedef std::wostream tostream;
+typedef std::wiostream tiostream;
+typedef std::wios tios;
+#else
+typedef std::string tstring;
+typedef std::ostringstream tostringstream;
+typedef std::istringstream tistringstream;
+typedef std::stringstream tstringstream;
+typedef std::istream tistream;
+typedef std::ostream tostream;
+typedef std::iostream tiostream;
+typedef std::ios tios;
+
+typedef const char* LPCTSTR;
+typedef char TCHAR;
+#endif
+
+// frequently used "array of tstrings" type
+typedef std::vector<tstring> tstring_array;
+
+namespace tstrings {
+ tstring unsafe_format(tstring::const_pointer format, ...);
+
+ enum CompareType {CASE_SENSITIVE, IGNORE_CASE};
+ bool equals(const tstring& a, const tstring& b, const CompareType ct=CASE_SENSITIVE);
+ bool startsWith(const tstring &str, const tstring &substr, const CompareType ct=CASE_SENSITIVE);
+ bool endsWith(const tstring &str, const tstring &substr, const CompareType ct=CASE_SENSITIVE);
+
+ enum SplitType {ST_ALL, ST_EXCEPT_EMPTY_STRING};
+ void split(tstring_array &strVector, const tstring &str,
+ const tstring &delimiter, const SplitType st = ST_ALL);
+ inline tstring_array split(const tstring &str, const tstring &delimiter, const SplitType st = ST_ALL) {
+ tstring_array result;
+ split(result, str, delimiter, st);
+ return result;
+ }
+ tstring trim(const tstring& str, const tstring& whitespace = _T(" \t"));
+
+ /**
+ * Writes sequence of values from [b, e) range into string buffer inserting
+ * 'delimiter' after each value except of the last one.
+ * Returns contents of string buffer.
+ */
+ template <class It>
+ tstring join(It b, It e, const tstring& delimiter=tstring()) {
+ tostringstream buf;
+ if (b != e) {
+ for (;;) {
+ buf << *b;
+ if (++b == e) {
+ break;
+ }
+ buf << delimiter;
+ }
+ }
+ return buf.str();
+ }
+
+ tstring toLower(const tstring& str);
+
+ tstring replace(const tstring &str, const tstring &search, const tstring &replace);
+}
+
+
+namespace tstrings {
+ inline std::string toUtf8(const std::string& utf8str) { return utf8str; }
+
+#ifdef TSTRINGS_WITH_WCHAR
+ // conversion to Utf8
+ std::string toUtf8(const std::wstring& utf16str);
+
+ // conversion to Utf16
+ std::wstring toUtf16(const std::string& utf8str);
+
+ inline std::wstring fromUtf8(const std::string& utf8str) { return toUtf16(utf8str); }
+
+#else
+ inline std::string fromUtf8(const std::string& utf8str) { return utf8str; }
+#endif
+} // namespace tstrings
+
+
+namespace tstrings {
+namespace format_detail {
+
+ template <class T>
+ struct str_arg_value {
+ const tstring value;
+
+ str_arg_value(const std::string& v): value(fromUtf8(v)) {
+ }
+
+#ifdef TSTRINGS_WITH_WCHAR
+ str_arg_value(const std::wstring& v): value(v) {
+ }
+#endif
+
+ tstring::const_pointer operator () () const {
+ return value.c_str();
+ }
+ };
+
+ template <>
+ struct str_arg_value<tstring> {
+ const tstring::const_pointer value;
+
+ str_arg_value(const tstring& v): value(v.c_str()) {
+ }
+
+ str_arg_value(tstring::const_pointer v): value(v) {
+ }
+
+ tstring::const_pointer operator () () const {
+ return value;
+ }
+ };
+
+ inline str_arg_value<std::string> arg(const std::string& v) {
+ return v;
+ }
+
+ inline str_arg_value<std::string> arg(std::string::const_pointer v) {
+ return (v ? v : "(null)");
+ }
+
+#ifdef TSTRINGS_WITH_WCHAR
+ inline str_arg_value<std::wstring> arg(const std::wstring& v) {
+ return v;
+ }
+
+ inline str_arg_value<std::wstring> arg(std::wstring::const_pointer v) {
+ return (v ? v : L"(null)");
+ }
+#else
+ void arg(const std::wstring&); // Compilation error by design.
+ void arg(std::wstring::const_pointer); // Compilation error by design.
+#endif
+
+ template <class T>
+ struct arg_value {
+ arg_value(const T v): v(v) {
+ }
+ T operator () () const {
+ return v;
+ }
+ private:
+ const T v;
+ };
+
+ inline arg_value<int> arg(int v) {
+ return v;
+ }
+ inline arg_value<unsigned> arg(unsigned v) {
+ return v;
+ }
+ inline arg_value<long> arg(long v) {
+ return v;
+ }
+ inline arg_value<unsigned long> arg(unsigned long v) {
+ return v;
+ }
+ inline arg_value<long long> arg(long long v) {
+ return v;
+ }
+ inline arg_value<unsigned long long> arg(unsigned long long v) {
+ return v;
+ }
+ inline arg_value<float> arg(float v) {
+ return v;
+ }
+ inline arg_value<double> arg(double v) {
+ return v;
+ }
+ inline arg_value<bool> arg(bool v) {
+ return v;
+ }
+ inline arg_value<const void*> arg(const void* v) {
+ return v;
+ }
+
+} // namespace format_detail
+} // namespace tstrings
+
+
+namespace tstrings {
+ template <class T, class T2, class T3, class T4, class T5, class T6, class T7>
+ inline tstring format(const tstring& fmt, const T& v, const T2& v2, const T3& v3, const T4& v4, const T5& v5, const T6& v6, const T7& v7) {
+ return unsafe_format(fmt.c_str(), format_detail::arg(v)(),
+ format_detail::arg(v2)(),
+ format_detail::arg(v3)(),
+ format_detail::arg(v4)(),
+ format_detail::arg(v5)(),
+ format_detail::arg(v6)(),
+ format_detail::arg(v7)());
+ }
+
+ template <class T, class T2, class T3, class T4, class T5, class T6>
+ inline tstring format(const tstring& fmt, const T& v, const T2& v2, const T3& v3, const T4& v4, const T5& v5, const T6& v6) {
+ return unsafe_format(fmt.c_str(), format_detail::arg(v)(),
+ format_detail::arg(v2)(),
+ format_detail::arg(v3)(),
+ format_detail::arg(v4)(),
+ format_detail::arg(v5)(),
+ format_detail::arg(v6)());
+ }
+
+ template <class T, class T2, class T3, class T4, class T5>
+ inline tstring format(const tstring& fmt, const T& v, const T2& v2, const T3& v3, const T4& v4, const T5& v5) {
+ return unsafe_format(fmt.c_str(), format_detail::arg(v)(),
+ format_detail::arg(v2)(),
+ format_detail::arg(v3)(),
+ format_detail::arg(v4)(),
+ format_detail::arg(v5)());
+ }
+
+ template <class T, class T2, class T3, class T4>
+ inline tstring format(const tstring& fmt, const T& v, const T2& v2, const T3& v3, const T4& v4) {
+ return unsafe_format(fmt.c_str(), format_detail::arg(v)(),
+ format_detail::arg(v2)(),
+ format_detail::arg(v3)(),
+ format_detail::arg(v4)());
+ }
+
+ template <class T, class T2, class T3>
+ inline tstring format(const tstring& fmt, const T& v, const T2& v2, const T3& v3) {
+ return unsafe_format(fmt.c_str(), format_detail::arg(v)(),
+ format_detail::arg(v2)(),
+ format_detail::arg(v3)());
+ }
+
+ template <class T, class T2>
+ inline tstring format(const tstring& fmt, const T& v, const T2& v2) {
+ return unsafe_format(fmt.c_str(), format_detail::arg(v)(),
+ format_detail::arg(v2)());
+
+ }
+
+ template <class T>
+ inline tstring format(const tstring& fmt, const T& v) {
+ return unsafe_format(fmt.c_str(), format_detail::arg(v)());
+ }
+} // namespace tstrings
+
+
+namespace tstrings {
+ /**
+ * Buffer that accepts both std::wstring and std::string instances doing
+ * encoding conversions behind the scenes. All std::string-s assumed to be
+ * UTF8-encoded, all std::wstring-s assumed to be UTF16-encoded.
+ */
+ class any {
+ public:
+ any() {
+ }
+
+ any(std::string::const_pointer msg) {
+ data << fromUtf8(msg);
+ }
+
+ any(const std::string& msg) {
+ data << fromUtf8(msg);
+ }
+
+#ifdef TSTRINGS_WITH_WCHAR
+ any(std::wstring::const_pointer msg) {
+ data << msg;
+ }
+
+ any(const std::wstring& msg) {
+ data << msg;
+ }
+
+ any& operator << (const std::wstring& v) {
+ data << v;
+ return *this;
+ }
+
+ // need this specialization instead std::wstring::pointer,
+ // otherwise LPWSTR is handled as abstract pointer (void*)
+ any& operator << (LPWSTR v) {
+ data << (v ? v : L"NULL");
+ return *this;
+ }
+
+ // need this specialization instead std::wstring::const_pointer,
+ // otherwise LPCWSTR is handled as abstract pointer (const void*)
+ any& operator << (LPCWSTR v) {
+ data << (v ? v : L"NULL");
+ return *this;
+ }
+
+ std::wstring wstr() const {
+ return data.str();
+ }
+#endif
+
+ template <class T>
+ any& operator << (T v) {
+ data << v;
+ return *this;
+ }
+
+ any& operator << (tostream& (*pf)(tostream&)) {
+ data << pf;
+ return *this;
+ }
+
+ any& operator << (tios& (*pf)(tios&)) {
+ data << pf;
+ return *this;
+ }
+
+ any& operator << (std::ios_base& (*pf)(std::ios_base&)) {
+ data << pf;
+ return *this;
+ }
+
+ std::string str() const {
+ return toUtf8(data.str());
+ }
+
+ tstring tstr() const {
+ return data.str();
+ }
+
+ private:
+ tostringstream data;
+ };
+
+ inline tstring to_tstring(const any& val) {
+ return val.tstr();
+ }
+} // namespace tstrings
+
+
+inline std::ostream& operator << (std::ostream& os, const tstrings::any& buf) {
+ os << buf.str();
+ return os;
+}
+
+#ifdef TSTRINGS_WITH_WCHAR
+inline std::wostream& operator << (std::wostream& os, const tstrings::any& buf) {
+ os << buf.wstr();
+ return os;
+}
+#endif
+
+#endif //TSTRINGS_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/windows/native/libwixhelper/libwixhelper.cpp Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 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.
+ */
+
+#include <Windows.h>
+#include <msiquery.h>
+#include <shlwapi.h>
+
+extern "C" {
+
+#ifdef JP_EXPORT_FUNCTION
+#error Unexpected JP_EXPORT_FUNCTION define
+#endif
+#define JP_EXPORT_FUNCTION comment(linker, "/EXPORT:" __FUNCTION__ "=" __FUNCDNAME__)
+
+ BOOL WINAPI DllMain(HINSTANCE hInst, ULONG ulReason,
+ LPVOID lpvReserved) {
+ return TRUE;
+ }
+
+ BOOL DirectoryExist(TCHAR *szValue) {
+ DWORD attr = GetFileAttributes(szValue);
+ if (attr == INVALID_FILE_ATTRIBUTES) {
+ return FALSE;
+ }
+
+ if (attr & FILE_ATTRIBUTE_DIRECTORY) {
+ return TRUE;
+ }
+
+ return FALSE;
+ }
+
+ UINT __stdcall CheckInstallDir(MSIHANDLE hInstall) {
+ #pragma JP_EXPORT_FUNCTION
+
+ TCHAR *szValue = NULL;
+ DWORD cchSize = 0;
+
+ UINT result = MsiGetProperty(hInstall, TEXT("APPLICATIONFOLDER"), TEXT(""), &cchSize);
+ if (result == ERROR_MORE_DATA) {
+ cchSize = cchSize + 1; // NULL termination
+ szValue = new TCHAR[cchSize];
+ if (szValue) {
+ result = MsiGetProperty(hInstall, TEXT("APPLICATIONFOLDER"), szValue, &cchSize);
+ } else {
+ return ERROR_INSTALL_FAILURE;
+ }
+ }
+
+ if (result != ERROR_SUCCESS) {
+ delete [] szValue;
+ return ERROR_INSTALL_FAILURE;
+ }
+
+ if (DirectoryExist(szValue)) {
+ if (PathIsDirectoryEmpty(szValue)) {
+ MsiSetProperty(hInstall, TEXT("INSTALLDIR_VALID"), TEXT("1"));
+ } else {
+ MsiSetProperty(hInstall, TEXT("INSTALLDIR_VALID"), TEXT("0"));
+ }
+ } else {
+ MsiSetProperty(hInstall, TEXT("INSTALLDIR_VALID"), TEXT("1"));
+ }
+
+ delete [] szValue;
+
+ return ERROR_SUCCESS;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/windows/native/msiwrapper/Executor.cpp Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,129 @@
+/*
+ * Copyright (c) 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.
+ */
+
+#include <algorithm>
+#include "Executor.h"
+#include "Log.h"
+#include "WinErrorHandling.h"
+
+
+namespace {
+
+void escapeArg(std::wstring& str) {
+ if (str.empty()) {
+ return;
+ }
+
+ if (str.front() == L'\"' && str.back() == L'\"' && str.size() > 1) {
+ return;
+ }
+
+ if (str.find_first_of(L" \t") != std::wstring::npos) {
+ str = L'"' + str + L'"';
+ }
+}
+
+} // namespace
+
+
+std::wstring Executor::args() const {
+ tstring_array tmpArgs;
+ // argv[0] is the module name.
+ tmpArgs.push_back(appPath);
+ tmpArgs.insert(tmpArgs.end(), argsArray.begin(), argsArray.end());
+
+ std::for_each(tmpArgs.begin(), tmpArgs.end(), escapeArg);
+ return tstrings::join(tmpArgs.begin(), tmpArgs.end(), _T(" "));
+}
+
+
+int Executor::execAndWaitForExit() const {
+ UniqueHandle h = startProcess();
+
+ const DWORD res = ::WaitForSingleObject(h.get(), INFINITE);
+ if (WAIT_FAILED == res) {
+ JP_THROW(SysError("WaitForSingleObject() failed", WaitForSingleObject));
+ }
+
+ DWORD exitCode = 0;
+ if (!GetExitCodeProcess(h.get(), &exitCode)) {
+ // Error reading process's exit code.
+ JP_THROW(SysError("GetExitCodeProcess() failed", GetExitCodeProcess));
+ }
+
+ const DWORD processId = GetProcessId(h.get());
+ if (!processId) {
+ JP_THROW(SysError("GetProcessId() failed.", GetProcessId));
+ }
+
+ LOG_TRACE(tstrings::any() << "Process with PID=" << processId
+ << " terminated. Exit code=" << exitCode);
+
+ return static_cast<int>(exitCode);
+}
+
+
+UniqueHandle Executor::startProcess() const {
+ const std::wstring argsStr = args();
+
+ std::vector<TCHAR> argsBuffer(argsStr.begin(), argsStr.end());
+ argsBuffer.push_back(0); // terminating '\0'
+
+ STARTUPINFO startupInfo;
+ ZeroMemory(&startupInfo, sizeof(startupInfo));
+ startupInfo.cb = sizeof(startupInfo);
+
+ PROCESS_INFORMATION processInfo;
+ ZeroMemory(&processInfo, sizeof(processInfo));
+
+ DWORD creationFlags = 0;
+
+ if (!theVisible) {
+ // For GUI applications.
+ startupInfo.dwFlags |= STARTF_USESHOWWINDOW;
+ startupInfo.wShowWindow = SW_HIDE;
+
+ // For console applications.
+ creationFlags |= CREATE_NO_WINDOW;
+ }
+
+ tstrings::any msg;
+ msg << "CreateProcess(" << appPath << ", " << argsStr << ")";
+
+ if (!CreateProcess(appPath.c_str(), argsBuffer.data(), NULL, NULL, FALSE,
+ creationFlags, NULL, NULL, &startupInfo, &processInfo)) {
+ msg << " failed";
+ JP_THROW(SysError(msg, CreateProcess));
+ }
+
+ msg << " succeeded; PID=" << processInfo.dwProcessId;
+ LOG_TRACE(msg);
+
+ // Close unneeded handles immediately.
+ UniqueHandle(processInfo.hThread);
+
+ // Return process handle.
+ return UniqueHandle(processInfo.hProcess);
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/windows/native/msiwrapper/Executor.h Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 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.
+ */
+
+#ifndef EXECUTOR_H
+#define EXECUTOR_H
+
+#include "tstrings.h"
+#include "UniqueHandle.h"
+
+
+class Executor {
+public:
+ explicit Executor(const std::wstring& appPath=std::wstring()) {
+ app(appPath).visible(false);
+ }
+
+ /**
+ * Returns command line configured with arg() calls so far.
+ */
+ std::wstring args() const;
+
+ /**
+ * Set path to application to execute.
+ */
+ Executor& app(const std::wstring& v) {
+ appPath = v;
+ return *this;
+ }
+
+ /**
+ * Adds another command line argument.
+ */
+ Executor& arg(const std::wstring& v) {
+ argsArray.push_back(v);
+ return *this;
+ }
+
+ /**
+ * Controls if application window should be visible.
+ */
+ Executor& visible(bool v) {
+ theVisible = v;
+ return *this;
+ }
+
+ /**
+ * Starts application process and blocks waiting when the started
+ * process terminates.
+ * Returns process exit code.
+ * Throws exception if process start failed.
+ */
+ int execAndWaitForExit() const;
+
+private:
+ UniqueHandle startProcess() const;
+
+ bool theVisible;
+ tstring_array argsArray;
+ std::wstring appPath;
+};
+
+#endif // #ifndef EXECUTOR_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/windows/native/msiwrapper/MsiWrapper.cpp Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,37 @@
+#include <windows.h>
+
+#include "WinSysInfo.h"
+#include "FileUtils.h"
+#include "Executor.h"
+#include "Resources.h"
+#include "WinErrorHandling.h"
+
+
+int wmain(int argc, wchar_t *argv[])
+{
+ JP_TRY;
+
+ // Create temporary directory where to extract msi file.
+ const auto tempMsiDir = FileUtils::createTempDirectory();
+
+ // Schedule temporary directory for deletion.
+ FileUtils::Deleter cleaner;
+ cleaner.appendRecursiveDirectory(tempMsiDir);
+
+ const auto msiPath = FileUtils::mkpath() << tempMsiDir << L"main.msi";
+
+ // Extract msi file.
+ Resource(L"msi", RT_RCDATA).saveToFile(msiPath);
+
+ // Setup executor to run msiexec
+ Executor msiExecutor(SysInfo::getWIPath());
+ msiExecutor.arg(L"/i").arg(msiPath);
+ for (int i = 1; i < argc; ++i) {
+ msiExecutor.arg(argv[i]);
+ }
+
+ // Install msi file.
+ return msiExecutor.execAndWaitForExit();
+
+ JP_CATCH_ALL;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/windows/native/msiwrapper/Resources.cpp Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,144 @@
+/*
+ * Copyright (c) 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.
+ */
+
+#include "Resources.h"
+#include "FileUtils.h"
+#include "WinErrorHandling.h"
+
+#include <fstream>
+
+
+Resource::Resource(LPCTSTR name, LPCTSTR type, HINSTANCE module) {
+ init(name, type, module);
+}
+
+Resource::Resource(UINT id, LPCTSTR type, HINSTANCE module) {
+ init(MAKEINTRESOURCE(id), type, module);
+}
+
+void Resource::init(LPCTSTR name, LPCTSTR type, HINSTANCE module) {
+ if (IS_INTRESOURCE(name)) {
+ std::wostringstream printer;
+ printer << L"#" << reinterpret_cast<size_t>(name);
+ nameStr = printer.str();
+ namePtr = name;
+ } else {
+ nameStr = name;
+ namePtr = nameStr.c_str();
+ }
+ if (IS_INTRESOURCE(type)) {
+ std::wostringstream printer;
+ printer << L"#" << reinterpret_cast<size_t>(name);
+ typeStr = printer.str();
+ typePtr = type;
+ } else {
+ typeStr = type;
+ typePtr = typeStr.c_str();
+ }
+ instance = module;
+}
+
+std::string Resource::getErrMsg(const std::string &descr) const {
+ return (tstrings::any() << descr << " (name='" << nameStr << "', type='" << typeStr << "')").str();
+}
+
+HRSRC Resource::findResource() const {
+ LPCTSTR id = namePtr;
+ // string resources are stored in blocks (stringtables)
+ // id of the resource is (stringId / 16 + 1)
+ if (typePtr == RT_STRING) {
+ id = MAKEINTRESOURCE(UINT(size_t(id) / 16 + 1));
+ }
+ return FindResource(instance, id, typePtr);
+}
+
+LPVOID Resource::getPtr(DWORD &size) const
+{
+ // LoadString returns the same result if value is zero-length or if if the value does not exists,
+ // so wee need to ensure the stringtable exists
+ HRSRC resInfo = findResource();
+ if (resInfo == NULL) {
+ JP_THROW(SysError(getErrMsg("cannot find resource"), FindResource));
+ }
+
+ HGLOBAL res = LoadResource(instance, resInfo);
+ if (res == NULL) {
+ JP_THROW(SysError(getErrMsg("cannot load resource"), LoadResource));
+ }
+
+ LPVOID ptr = LockResource(res);
+ if (res == NULL) {
+ JP_THROW(SysError(getErrMsg("cannot lock resource"), LockResource));
+ }
+
+ if (typePtr == RT_STRING) {
+ // string resources are stored in stringtables and need special handling
+ // The simplest way (while we don't need handle resource locale) is LoadString
+ // But this adds dependency on user32.dll, so implement custom string extraction
+
+ // number in the block (namePtr is an integer)
+ size_t num = size_t(namePtr) & 0xf;
+ LPWSTR strPtr = (LPWSTR)ptr;
+ for (size_t i = 0; i < num; i++) {
+ // 1st symbol contains string length
+ strPtr += DWORD(*strPtr) + 1;
+ }
+ // *strPtr contains string length, string value starts at strPtr+1
+ size = DWORD(*strPtr) * sizeof(wchar_t);
+ ptr = strPtr+1;
+ } else {
+ size = SizeofResource(instance, resInfo);
+ }
+
+ return ptr;
+}
+
+bool Resource::available() const {
+ return NULL != findResource();
+}
+
+unsigned Resource::size() const {
+ DWORD size = 0;
+ getPtr(size);
+ return size;
+}
+
+LPCVOID Resource::rawData() const {
+ DWORD size = 0;
+ return getPtr(size);
+}
+
+void Resource::saveToFile(const std::wstring &filePath) const {
+ DWORD size = 0;
+ const char *resPtr = (const char *)getPtr(size);
+
+ FileUtils::FileWriter(filePath).write(resPtr, size).finalize();
+}
+
+Resource::ByteArray Resource::binary() const {
+ DWORD size = 0;
+ LPBYTE resPtr = (LPBYTE)getPtr(size);
+ return ByteArray(resPtr, resPtr+size);
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/windows/native/msiwrapper/Resources.h Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 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.
+ */
+
+#ifndef RESOURCES_H
+#define RESOURCES_H
+
+#include "WinSysInfo.h"
+
+
+/**
+ * Classes for resource loading.
+ * Common use cases:
+ * - check if resource is available and save it to file:
+ * Resource res(_T("MyResource"), _T("CustomResourceType"));
+ * if (res.available()) {
+ * res.saveToFile(_T("c:\\temp\\my_resource.bin"));
+ * }
+ */
+
+class Resource {
+public:
+ // name and type can be specified by string id, by integer id (RT_* constants or MAKEINTRESOURCE)
+ Resource(LPCWSTR name, LPCWSTR type, HINSTANCE module = SysInfo::getCurrentModuleHandle());
+ Resource(UINT id, LPCWSTR type, HINSTANCE module = SysInfo::getCurrentModuleHandle());
+
+ bool available() const;
+
+ // all this methods throw exception if the resource is not available
+ unsigned size() const;
+ // gets raw pointer to the resource data
+ LPCVOID rawData() const;
+
+ // save the resource to a file
+ void saveToFile(const std::wstring &filePath) const;
+
+ typedef std::vector<BYTE> ByteArray;
+ // returns the resource as byte array
+ ByteArray binary() const;
+
+private:
+ std::wstring nameStr;
+ LPCWSTR namePtr; // can be integer (MAKEINTRESOURCE) value or point to nameStr.c_str()
+ std::wstring typeStr;
+ LPCWSTR typePtr; // can be integer (MAKEINTRESOURCE) value or point to nameStr.c_str()
+ HINSTANCE instance;
+
+ void init(LPCWSTR name, LPCWSTR type, HINSTANCE module);
+
+ // generates error message
+ std::string getErrMsg(const std::string &descr) const;
+ HRSRC findResource() const;
+ LPVOID getPtr(DWORD &size) const;
+
+private:
+ // disable copying
+ Resource(const Resource&);
+ Resource& operator = (const Resource&);
+};
+
+#endif // RESOURCES_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/JPackageHelpTest.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,117 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+ /*
+ * @test
+ * @summary jpackage help test
+ * @library helpers
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @modules jdk.jpackage
+ * @run main/othervm -Xmx512m JPackageHelpTest
+ */
+public class JPackageHelpTest {
+
+ // Platform specific help messages.
+ private static final String WINDOWS_HELP =
+ "--win-dir-chooser";
+ private static final String OSX_HELP =
+ "--mac-bundle-identifier";
+ private static final String LINUX_HELP =
+ "--linux-bundle-name";
+
+ private static void validate(String output1, String output2)
+ throws Exception {
+ if (output1.split("\n").length < 25) {
+ throw new AssertionError("jpacakger --help failed");
+ }
+
+ if (output2.split("\n").length < 25) {
+ throw new AssertionError("jpacakger -h failed");
+ }
+
+ // Make sure output matches for --help and -h
+ if (!output1.equals(output2)) {
+ System.err.println("================= --help =================");
+ System.err.println(output1);
+
+ System.err.println("=================== -h ===================");
+ System.err.println(output2);
+
+ throw new AssertionError(
+ "jpacakger help text does not match between --help and -h");
+ }
+
+ if (JPackageHelper.isWindows()) {
+ if (!output1.contains(WINDOWS_HELP)) {
+ throw new AssertionError(
+ "jpacakger help text missing Windows specific help");
+ }
+
+ if (output1.contains(OSX_HELP) || output1.contains(LINUX_HELP)) {
+ throw new AssertionError(
+ "jpacakger help text contains other platforms specific help");
+
+ }
+ } else if (JPackageHelper.isOSX()) {
+ if (!output1.contains(OSX_HELP)) {
+ throw new AssertionError(
+ "jpacakger help text missing OS X specific help");
+ }
+
+ if (output1.contains(WINDOWS_HELP) ||
+ output1.contains(LINUX_HELP)) {
+ throw new AssertionError(
+ "jpacakger help text contains other platforms specific help");
+ }
+ } else if (JPackageHelper.isLinux()) {
+ if (!output1.contains(LINUX_HELP)) {
+ throw new AssertionError(
+ "jpacakger help text missing Linux specific help");
+ }
+
+ if (output1.contains(OSX_HELP) || output1.contains(WINDOWS_HELP)) {
+ throw new AssertionError(
+ "jpacakger help text contains other platforms specific help");
+ }
+ }
+ }
+
+ private static void testHelp() throws Exception {
+ String output1 = JPackageHelper.executeCLI(true, "--help");
+ String output2 = JPackageHelper.executeCLI(true, "-h");
+ validate(output1, output2);
+ }
+
+ private static void testHelpToolProvider() throws Exception {
+ String output1 = JPackageHelper.executeToolProvider(true, "--help");
+ String output2 = JPackageHelper.executeToolProvider(true, "-h");
+ validate(output1, output2);
+ }
+
+ public static void main(String[] args) throws Exception {
+ testHelp();
+ testHelpToolProvider();
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/JPackageInvalidArgTest.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,83 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+ /*
+ * @test
+ * @summary jpackage invalid argument test
+ * @library helpers
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @modules jdk.jpackage
+ * @run main/othervm -Xmx512m JPackageInvalidArgTest
+ */
+public class JPackageInvalidArgTest {
+
+ private static final String ARG1 = "--no-such-argument";
+ private static final String ARG2 = "--output";
+ private static final String RESULT1 =
+ "Invalid Option: [--no-such-argument]";
+ private static final String RESULT2 = "--main-jar or --module";
+
+ private static void validate(String arg, String output) throws Exception {
+ String[] result = JPackageHelper.splitAndFilter(output);
+ if (result.length != 1) {
+ System.err.println(output);
+ throw new AssertionError("Invalid number of lines in output: "
+ + result.length);
+ }
+
+ if (arg.equals(ARG1)) {
+ if (!result[0].trim().contains(RESULT1)) {
+ System.err.println("Expected: " + RESULT1);
+ System.err.println("Actual: " + result[0]);
+ throw new AssertionError("Unexpected output: " + result[0]);
+ }
+ } else if (arg.equals(ARG2)) {
+ if (!result[0].trim().contains(RESULT2)) {
+ System.err.println("Expected: " + RESULT2);
+ System.err.println("Actual: " + result[0]);
+ throw new AssertionError("Unexpected output: " + result[0]);
+ }
+ }
+ }
+
+ private static void testInvalidArg() throws Exception {
+ String output = JPackageHelper.executeCLI(false, ARG1);
+ validate(ARG1, output);
+ output = JPackageHelper.executeCLI(false, ARG2);
+ validate(ARG2, output);
+ }
+
+ private static void testInvalidArgToolProvider() throws Exception {
+ String output = JPackageHelper.executeToolProvider(false, ARG1);
+ validate(ARG1, output);
+ output = JPackageHelper.executeToolProvider(false, ARG2);
+ validate(ARG2, output);
+ }
+
+ public static void main(String[] args) throws Exception {
+ testInvalidArg();
+ testInvalidArgToolProvider();
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/JPackageMissingArgumentsTest.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,163 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+ /*
+ * @test
+ * @summary jpackage create image missing arguments test
+ * @library helpers
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @modules jdk.jpackage
+ * @run main/othervm -Xmx512m JPackageMissingArgumentsTest
+ */
+
+public class JPackageMissingArgumentsTest {
+ private static final String [] RESULT_1 = {"--output"};
+ private static final String [] CMD_1 = {
+ "--input", "input",
+ "--name", "test",
+ "--main-jar", "hello.jar",
+ "--main-class", "Hello",
+ };
+
+ private static final String [] RESULT_2 = {"--input"};
+ private static final String [] CMD_2 = {
+ "--output", "output",
+ "--name", "test",
+ "--main-jar", "hello.jar",
+ "--main-class", "Hello",
+ };
+
+ private static final String [] RESULT_3 = {"--input", "--app-image"};
+ private static final String [] CMD_3 = {
+ "--package-type", "invalid-package-type",
+ "--output", "output",
+ "--name", "test",
+ "--main-jar", "hello.jar",
+ "--main-class", "Hello",
+ };
+
+ private static final String [] RESULT_4 = {"main class was not specified"};
+ private static final String [] CMD_4 = {
+ "--input", "input",
+ "--output", "output",
+ "--name", "test",
+ "--main-jar", "hello.jar",
+ };
+
+ private static final String [] RESULT_5 = {"--main-jar"};
+ private static final String [] CMD_5 = {
+ "--input", "input",
+ "--output", "output",
+ "--name", "test",
+ "--main-class", "Hello",
+ };
+
+ private static final String [] RESULT_6 = {"--module-path", "--runtime-image"};
+ private static final String [] CMD_6 = {
+ "--output", "output",
+ "--name", "test",
+ "--module", "com.hello/com.hello.Hello",
+ };
+
+ private static final String [] RESULT_7 = {"--module-path", "--runtime-image",
+ "--app-image"};
+ private static final String [] CMD_7 = {
+ "--package-type", "invalid-package-type",
+ "--output", "output",
+ "--name", "test",
+ "--module", "com.hello/com.hello.Hello",
+ };
+
+ private static void validate(String output, String [] expected,
+ boolean single) throws Exception {
+ String[] result = JPackageHelper.splitAndFilter(output);
+ if (single && result.length != 1) {
+ System.err.println(output);
+ throw new AssertionError("Invalid number of lines in output: "
+ + result.length);
+ }
+
+ for (String s : expected) {
+ if (!result[0].contains(s)) {
+ System.err.println("Expected to contain: " + s);
+ System.err.println("Actual: " + result[0]);
+ throw new AssertionError("Unexpected error message");
+ }
+ }
+ }
+
+ private static void testMissingArg() throws Exception {
+ String output = JPackageHelper.executeCLI(false, CMD_1);
+ validate(output, RESULT_1, true);
+
+ output = JPackageHelper.executeCLI(false, CMD_2);
+ validate(output, RESULT_2, true);
+
+ output = JPackageHelper.executeCLI(false, CMD_3);
+ validate(output, RESULT_3, true);
+
+ output = JPackageHelper.executeCLI(false, CMD_4);
+ validate(output, RESULT_4, false);
+
+ output = JPackageHelper.executeCLI(false, CMD_5);
+ validate(output, RESULT_5, true);
+
+ output = JPackageHelper.executeCLI(false, CMD_6);
+ validate(output, RESULT_6, true);
+
+ output = JPackageHelper.executeCLI(false, CMD_7);
+ validate(output, RESULT_7, true);
+
+ }
+
+ private static void testMissingArgToolProvider() throws Exception {
+ String output = JPackageHelper.executeToolProvider(false, CMD_1);
+ validate(output, RESULT_1, true);
+
+ output = JPackageHelper.executeToolProvider(false, CMD_2);
+ validate(output, RESULT_2, true);
+
+ output = JPackageHelper.executeToolProvider(false, CMD_3);
+ validate(output, RESULT_3, true);
+
+ output = JPackageHelper.executeToolProvider(false, CMD_4);
+ validate(output, RESULT_4, false);
+
+ output = JPackageHelper.executeToolProvider(false, CMD_5);
+ validate(output, RESULT_5, true);
+
+ output = JPackageHelper.executeToolProvider(false, CMD_6);
+ validate(output, RESULT_6, true);
+
+ output = JPackageHelper.executeToolProvider(false, CMD_7);
+ validate(output, RESULT_7, true);
+ }
+
+ public static void main(String[] args) throws Exception {
+ JPackageHelper.createHelloImageJar();
+ testMissingArg();
+ testMissingArgToolProvider();
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/JPackageNoArgTest.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,78 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+ /*
+ * @test
+ * @summary jpackage no argument test
+ * @library helpers
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @modules jdk.jpackage
+ * @run main/othervm -Xmx512m JPackageNoArgTest
+ */
+public class JPackageNoArgTest {
+
+ private static final String RESULT1 = "Usage: jpackage <mode> <options>";
+ private static final String[] EXPECTED =
+ {"--help", "list of possible options"};
+
+ private static void validate(String output) throws Exception {
+ String[] result = JPackageHelper.splitAndFilter(output);
+ if (result.length != 2) {
+ System.err.println(output);
+ throw new AssertionError(
+ "Invalid number of lines in output: " + result.length);
+ }
+
+ if (!result[0].trim().equals(RESULT1)) {
+ System.err.println("Expected: " + RESULT1);
+ System.err.println("Actual: " + result[0]);
+ throw new AssertionError("Unexpected line 1");
+ }
+
+ for (String expected : EXPECTED) {
+ if (!result[1].contains(expected)) {
+ System.err.println("Expected to contain: " + expected);
+ System.err.println("Actual: " + result[1]);
+ throw new AssertionError("Unexpected line 2");
+ }
+ }
+ }
+
+ private static void testNoArg() throws Exception {
+ String output = JPackageHelper.executeCLI(true, new String[0]);
+ validate(output);
+ }
+
+ private static void testNoArgToolProvider() throws Exception {
+ String output =
+ JPackageHelper.executeToolProvider(true, new String[0]);
+ validate(output);
+ }
+
+ public static void main(String[] args) throws Exception {
+ testNoArg();
+ testNoArgToolProvider();
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/JPackageVersionTest.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,67 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+ /*
+ * @test
+ * @summary jpackage version test
+ * @library helpers
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @modules jdk.jpackage
+ * @run main/othervm -Xmx512m JPackageVersionTest
+ */
+public class JPackageVersionTest {
+
+ private static final String ARG = "--version";
+ private static final String RESULT = System.getProperty("java.version");
+
+ private static void validate(String output) throws Exception {
+ String[] result = JPackageHelper.splitAndFilter(output);
+ if (result.length != 1) {
+ System.err.println(output);
+ throw new AssertionError("Invalid number of lines in output: " + result.length);
+ }
+
+ if (!result[0].trim().equals(RESULT)) {
+ System.err.println("Expected: " + RESULT);
+ System.err.println("Actual: " + result[0]);
+ throw new AssertionError("Unexpected line 1");
+ }
+ }
+
+ private static void testVersion() throws Exception {
+ String output = JPackageHelper.executeCLI(true, ARG);
+ validate(output);
+ }
+
+ private static void testVersionToolProvider() throws Exception {
+ String output = JPackageHelper.executeToolProvider(true, ARG);
+ validate(output);
+ }
+
+ public static void main(String[] args) throws Exception {
+ testVersion();
+ testVersionToolProvider();
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/apps/com.hello/com/hello/Hello.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,64 @@
+/*
+ * 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.
+ *
+ * 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 com.hello;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.PrintWriter;
+
+public class Hello {
+
+ private static final String MSG = "jpackage test application";
+ private static final int EXPECTED_NUM_OF_PARAMS = 3; // Starts at 1
+
+ public static void main(String[] args) {
+ String outputFile = "appOutput.txt";
+ File file = new File(outputFile);
+
+ try (PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter(file)))) {
+ System.out.println(MSG);
+ out.println(MSG);
+
+ System.out.println("args.length: " + args.length);
+ out.println("args.length: " + args.length);
+
+ for (String arg : args) {
+ System.out.println(arg);
+ out.println(arg);
+ }
+
+ for (int index = 1; index <= EXPECTED_NUM_OF_PARAMS; index++) {
+ String value = System.getProperty("param" + index);
+ if (value != null) {
+ System.out.println("-Dparam" + index + "=" + value);
+ out.println("-Dparam" + index + "=" + value);
+ }
+ }
+ } catch (Exception ex) {
+ System.err.println(ex.toString());
+ }
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/apps/com.hello/module-info.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,26 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+module com.hello {
+ exports com.hello;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/apps/com.other/com/other/Other.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,64 @@
+/*
+ * 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.
+ *
+ * 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 com.other;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.PrintWriter;
+
+public class Other {
+
+ private static final String MSG = "other jpackage test application";
+ private static final int EXPECTED_NUM_OF_PARAMS = 3; // Starts at 1
+
+ public static void main(String[] args) {
+ String outputFile = "appOutput.txt";
+ File file = new File(outputFile);
+
+ try (PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter(file)))) {
+ System.out.println(MSG);
+ out.println(MSG);
+
+ System.out.println("args.length: " + args.length);
+ out.println("args.length: " + args.length);
+
+ for (String arg : args) {
+ System.out.println(arg);
+ out.println(arg);
+ }
+
+ for (int index = 1; index <= EXPECTED_NUM_OF_PARAMS; index++) {
+ String value = System.getProperty("param" + index);
+ if (value != null) {
+ System.out.println("-Dparam" + index + "=" + value);
+ out.println("-Dparam" + index + "=" + value);
+ }
+ }
+ } catch (Exception ex) {
+ System.err.println(ex.toString());
+ }
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/apps/com.other/module-info.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,26 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+module com.other {
+ exports com.other;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/apps/image/Hello.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,80 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.PrintWriter;
+
+public class Hello {
+
+ private static final String MSG = "jpackage test application";
+ private static final int EXPECTED_NUM_OF_PARAMS = 3; // Starts at 1
+
+ public static void main(String[] args) {
+ printToStdout(args);
+ printToFile(args);
+ }
+
+ private static void printToStdout(String[] args) {
+ System.out.println(MSG);
+
+ System.out.println("args.length: " + args.length);
+
+ for (String arg : args) {
+ System.out.println(arg);
+ }
+
+ for (int index = 1; index <= EXPECTED_NUM_OF_PARAMS; index++) {
+ String value = System.getProperty("param" + index);
+ if (value != null) {
+ System.out.println("-Dparam" + index + "=" + value);
+ }
+ }
+ }
+
+ private static void printToFile(String[] args) {
+ String outputFile = "appOutput.txt";
+ File file = new File(outputFile);
+
+ try (PrintWriter out
+ = new PrintWriter(new BufferedWriter(new FileWriter(file)))) {
+ out.println(MSG);
+
+ out.println("args.length: " + args.length);
+
+ for (String arg : args) {
+ out.println(arg);
+ }
+
+ for (int index = 1; index <= EXPECTED_NUM_OF_PARAMS; index++) {
+ String value = System.getProperty("param" + index);
+ if (value != null) {
+ out.println("-Dparam" + index + "=" + value);
+ }
+ }
+ } catch (Exception ex) {
+ System.err.println(ex.getMessage());
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/apps/installer/Hello.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,113 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.PrintWriter;
+import java.awt.Desktop;
+import java.awt.desktop.OpenFilesEvent;
+import java.awt.desktop.OpenFilesHandler;
+import java.util.List;
+
+public class Hello implements OpenFilesHandler {
+
+ private static final String MSG = "jpackage test application";
+ private static final int EXPECTED_NUM_OF_PARAMS = 3; // Starts at 1
+ private static List<File> files;
+
+ public static void main(String[] args) {
+ if(Desktop.getDesktop().isSupported(Desktop.Action.APP_OPEN_FILE)) {
+ Desktop.getDesktop().setOpenFileHandler(new Hello());
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+ printToStdout(args);
+ if (args.length == 1 || (files != null && files.size() == 1)) { // Called via file association
+ printToFile(args);
+ }
+ }
+
+ private static void printToStdout(String[] args) {
+ System.out.println(MSG);
+
+ System.out.println("args.length: " + (files == null ? args.length : args.length + files.size()));
+
+ for (String arg : args) {
+ System.out.println(arg);
+ }
+
+ if (files != null) {
+ for (File file : files) {
+ System.out.println(file.getAbsolutePath());
+ }
+ }
+
+ for (int index = 1; index <= EXPECTED_NUM_OF_PARAMS; index++) {
+ String value = System.getProperty("param" + index);
+ if (value != null) {
+ System.out.println("-Dparam" + index + "=" + value);
+ }
+ }
+ }
+
+ private static void printToFile(String[] args) {
+ File inputFile = files == null ? new File(args[0]) : files.get(0);
+ String outputFile = inputFile.getParent() + File.separator + "appOutput.txt";
+ File file = new File(outputFile);
+
+ try (PrintWriter out
+ = new PrintWriter(new BufferedWriter(new FileWriter(file)))) {
+ out.println(MSG);
+
+ out.println("args.length: " + (files == null ? args.length : args.length + files.size()));
+
+ for (String arg : args) {
+ out.println(arg);
+ }
+
+ if (files != null) {
+ for (File f : files) {
+ out.println(f.getAbsolutePath());
+ }
+ }
+
+ for (int index = 1; index <= EXPECTED_NUM_OF_PARAMS; index++) {
+ String value = System.getProperty("param" + index);
+ if (value != null) {
+ out.println("-Dparam" + index + "=" + value);
+ }
+ }
+ } catch (Exception ex) {
+ System.err.println(ex.getMessage());
+ }
+ }
+
+ @Override
+ public void openFiles(OpenFilesEvent e) {
+ files = e.getFiles();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createappimage/JPackageCreateAppImageAddLauncherBase.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,173 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.PrintWriter;
+import java.nio.file.Files;
+import java.util.ArrayList;
+import java.util.List;
+
+public class JPackageCreateAppImageAddLauncherBase {
+ private static final String app = JPackagePath.getApp();
+ private static final String appOutput = JPackagePath.getAppOutputFile();
+ private static final String appWorkingDir = JPackagePath.getAppWorkingDir();
+
+ // Note: quotes in argument for add launcher is not support by test
+ private static final String ARGUMENT1 = "argument 1";
+ private static final String ARGUMENT2 = "argument 2";
+ private static final String ARGUMENT3 = "argument 3";
+
+ private static final List<String> arguments = new ArrayList<>();
+
+ private static final String PARAM1 = "-Dparam1=Some Param 1";
+ private static final String PARAM2 = "-Dparam2=Some Param 2";
+ private static final String PARAM3 = "-Dparam3=Some Param 3";
+
+ private static final List<String> vmArguments = new ArrayList<>();
+ private static final List<String> empty = new ArrayList<>();
+
+ private static void validateResult(List<String> args, List<String> vmArgs)
+ throws Exception {
+ File outfile = new File(appWorkingDir + File.separator + appOutput);
+ if (!outfile.exists()) {
+ throw new AssertionError(appOutput + " was not created");
+ }
+
+ String output = Files.readString(outfile.toPath());
+ String[] result = output.split("\n");
+
+ int expected = 2 + args.size() + vmArgs.size();
+
+ if (result.length != expected) {
+ throw new AssertionError("Unexpected number of lines: "
+ + result.length + " expected: " + expected + " - results: " + output);
+ }
+
+ if (!result[0].trim().endsWith("jpackage test application")) {
+ throw new AssertionError("Unexpected result[0]: " + result[0]);
+ }
+
+ if (!result[1].trim().equals("args.length: " + args.size())) {
+ throw new AssertionError("Unexpected result[1]: " + result[1]);
+ }
+
+ int index = 2;
+ for (String arg : args) {
+ if (!result[index].trim().equals(arg)) {
+ throw new AssertionError("Unexpected result["
+ + index + "]: " + result[index]);
+ }
+ index++;
+ }
+
+ for (String vmArg : vmArgs) {
+ if (!result[index].trim().equals(vmArg)) {
+ throw new AssertionError("Unexpected result["
+ + index + "]: " + result[index]);
+ }
+ index++;
+ }
+ }
+
+ private static void validate(boolean includeArgs, String name)
+ throws Exception {
+ int retVal = JPackageHelper.execute(null, app);
+ if (retVal != 0) {
+ throw new AssertionError("Test application " + app
+ + " exited with error: " + retVal);
+ }
+ validateResult(new ArrayList<>(), new ArrayList<>());
+
+ String app2 = JPackagePath.getAppSL(name);
+ retVal = JPackageHelper.execute(null, app2);
+ if (retVal != 0) {
+ throw new AssertionError("Test application " + app2
+ + " exited with error: " + retVal);
+ }
+ if (includeArgs) {
+ validateResult(arguments, vmArguments);
+ } else {
+ validateResult(empty, empty);
+ }
+ }
+
+ public static void testCreateAppImage(String [] cmd) throws Exception {
+ testCreateAppImage(cmd, true, "test2");
+ }
+
+ public static void testCreateAppImage(String [] cmd,
+ boolean includeArgs, String name) throws Exception {
+ JPackageHelper.executeCLI(true, cmd);
+ validate(includeArgs, name);
+ }
+
+ public static void testCreateAppImageToolProvider(String [] cmd)
+ throws Exception {
+ testCreateAppImageToolProvider(cmd, true, "test2");
+ }
+
+ public static void testCreateAppImageToolProvider(String [] cmd,
+ boolean includeArgs, String name) throws Exception {
+ JPackageHelper.executeToolProvider(true, cmd);
+ validate(includeArgs, name);
+ }
+
+ public static void createSLProperties() throws Exception {
+ arguments.add(ARGUMENT1);
+ arguments.add(ARGUMENT2);
+ arguments.add(ARGUMENT3);
+
+ String argumentsMap =
+ JPackageHelper.listToArgumentsMap(arguments, true);
+
+ vmArguments.add(PARAM1);
+ vmArguments.add(PARAM2);
+ vmArguments.add(PARAM3);
+
+ String vmArgumentsMap =
+ JPackageHelper.listToArgumentsMap(vmArguments, true);
+
+ try (PrintWriter out = new PrintWriter(new BufferedWriter(
+ new FileWriter("sl.properties")))) {
+ out.println("arguments=" + argumentsMap);
+ out.println("java-options=" + vmArgumentsMap);
+ }
+
+ try (PrintWriter out = new PrintWriter(new BufferedWriter(
+ new FileWriter("m1.properties")))) {
+ out.println("module=com.hello/com.hello.Hello");
+ out.println("main-jar=");
+ }
+
+ try (PrintWriter out = new PrintWriter(new BufferedWriter(
+ new FileWriter("j1.properties")))) {
+ out.println("main-jar hello.jar");
+ out.println("main-class Hello");
+ }
+
+
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createappimage/JPackageCreateAppImageAddLauncherModuleTest.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,50 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+ /*
+ * @test
+ * @summary jpackage create image with additional launcher test
+ * @library ../helpers
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageCreateAppImageAddLauncherBase
+ * @modules jdk.jpackage
+ * @run main/othervm -Xmx512m JPackageCreateAppImageAddLauncherModuleTest
+ */
+public class JPackageCreateAppImageAddLauncherModuleTest {
+ private static final String OUTPUT = "output";
+ private static final String [] CMD = {
+ "--output", OUTPUT,
+ "--name", "test",
+ "--module", "com.hello/com.hello.Hello",
+ "--module-path", "input",
+ "--add-launcher", "test2=sl.properties"};
+
+ public static void main(String[] args) throws Exception {
+ JPackageHelper.createHelloModule();
+ JPackageCreateAppImageAddLauncherBase.createSLProperties();
+ JPackageCreateAppImageAddLauncherBase.testCreateAppImageToolProvider(
+ CMD);
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createappimage/JPackageCreateAppImageAddLauncherTest.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,50 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+ /*
+ * @test
+ * @summary jpackage create image with additional launcher test
+ * @library ../helpers
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageCreateAppImageAddLauncherBase
+ * @modules jdk.jpackage
+ * @run main/othervm -Xmx512m JPackageCreateAppImageAddLauncherTest
+ */
+public class JPackageCreateAppImageAddLauncherTest {
+ private static final String OUTPUT = "output";
+ private static final String [] CMD = {
+ "--input", "input",
+ "--output", OUTPUT,
+ "--name", "test",
+ "--main-jar", "hello.jar",
+ "--main-class", "Hello",
+ "--add-launcher", "test2=sl.properties"};
+
+ public static void main(String[] args) throws Exception {
+ JPackageHelper.createHelloImageJar();
+ JPackageCreateAppImageAddLauncherBase.createSLProperties();
+ JPackageCreateAppImageAddLauncherBase.testCreateAppImage(CMD);
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createappimage/JPackageCreateAppImageAddLaunchersTest.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,81 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+ /*
+ * @test
+ * @summary jpackage create image with additional launcher test
+ * @library ../helpers
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageCreateAppImageAddLauncherBase
+ * @modules jdk.jpackage
+ * @run main/othervm -Xmx512m JPackageCreateAppImageAddLaunchersTest
+ */
+public class JPackageCreateAppImageAddLaunchersTest {
+ private static final String OUTPUT = "output";
+ private static final String [] CMD1 = {
+ "--description", "Test non modular app with multiple add-launchers where one is modular app and other is non modular app",
+ "--input", "input",
+ "--output", OUTPUT,
+ "--name", "test",
+ "--main-jar", "hello.jar",
+ "--main-class", "Hello",
+ "--module-path", "module",
+ "--add-modules", "com.hello",
+ "--add-launcher", "test3=j1.properties",
+ "--add-launcher", "test4=m1.properties"};
+
+ private static final String [] CMD2 = {
+ "--description", "Test modular app with multiple add-launchers where one is modular app and other is non modular app",
+ "--input", "input",
+ "--output", OUTPUT,
+ "--name", "test",
+ "--module", "com.hello/com.hello.Hello",
+ "--module-path", "module",
+ "--add-launcher", "test5=jl.properties",
+ "--add-launcher", "test6=m1.properties"};
+
+ public static void main(String[] args) throws Exception {
+ JPackageHelper.createHelloImageJar();
+ JPackageHelper.createHelloModule();
+ JPackageCreateAppImageAddLauncherBase.createSLProperties();
+
+ JPackageHelper.deleteOutputFolder(OUTPUT);
+ JPackageCreateAppImageAddLauncherBase.testCreateAppImageToolProvider(
+ CMD1, false, "test3");
+
+ JPackageHelper.deleteOutputFolder(OUTPUT);
+ JPackageCreateAppImageAddLauncherBase.testCreateAppImage(
+ CMD1, false, "test4");
+
+ JPackageHelper.deleteOutputFolder(OUTPUT);
+ JPackageCreateAppImageAddLauncherBase.testCreateAppImage(
+ CMD2, false, "test5");
+
+ JPackageHelper.deleteOutputFolder(OUTPUT);
+ JPackageCreateAppImageAddLauncherBase.testCreateAppImageToolProvider(
+ CMD2, false, "test6");
+
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createappimage/JPackageCreateAppImageAddModulesTest.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,73 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+ /*
+ * @test
+ * @summary jpackage create image module test
+ * @library ../helpers
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageCreateAppImageBase
+ * @modules jdk.jpackage
+ * @run main/othervm -Xmx512m JPackageCreateAppImageAddModulesTest
+ */
+public class JPackageCreateAppImageAddModulesTest {
+ private static final String OUTPUT = "output";
+
+ private static final String [] CMD1 = {
+ "--output", OUTPUT,
+ "--name", "test",
+ "--module", "com.hello/com.hello.Hello",
+ "--module-path", "input",
+ "--add-modules", "java.desktop",
+ };
+
+ private static final String [] CMD2 = {
+ "--output", OUTPUT,
+ "--name", "test",
+ "--module", "com.hello/com.hello.Hello",
+ "--module-path", "input",
+ "--add-modules", "java.desktop,java.xml",
+ };
+
+ private static final String [] CMD3 = {
+ "--output", OUTPUT,
+ "--name", "test",
+ "--module", "com.hello/com.hello.Hello",
+ "--module-path", "input",
+ "--add-modules", "java.desktop",
+ "--add-modules", "java.xml",
+ };
+
+ public static void main(String[] args) throws Exception {
+ JPackageHelper.createHelloModule();
+ JPackageCreateAppImageBase.testCreateAppImage(CMD1);
+ JPackageHelper.deleteOutputFolder(OUTPUT);
+ JPackageCreateAppImageBase.testCreateAppImageToolProvider(CMD1);
+ JPackageHelper.deleteOutputFolder(OUTPUT);
+ JPackageCreateAppImageBase.testCreateAppImageToolProvider(CMD2);
+ JPackageHelper.deleteOutputFolder(OUTPUT);
+ JPackageCreateAppImageBase.testCreateAppImageToolProvider(CMD3);
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createappimage/JPackageCreateAppImageArgumentsBase.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,122 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+import java.io.File;
+import java.nio.file.Files;
+import java.util.ArrayList;
+import java.util.List;
+
+public class JPackageCreateAppImageArgumentsBase {
+
+ private static final String app = JPackagePath.getApp();
+ private static final String appOutput = JPackagePath.getAppOutputFile();
+ private static final String appWorkingDir = JPackagePath.getAppWorkingDir();
+
+ private static final String ARGUMENT1 = "argument";
+ private static final String ARGUMENT2 = "Some Arguments";
+ private static final String ARGUMENT3 = "Value \"with\" quotes";
+
+ private static final String ARGUMENT_CMD1 = "test";
+
+ private static final List<String> arguments = new ArrayList<>();
+ private static final List<String> argumentsCmd = new ArrayList<>();
+
+ public static void initArguments(boolean toolProvider, String[] cmd) {
+ if (arguments.isEmpty()) {
+ arguments.add(ARGUMENT1);
+ arguments.add(ARGUMENT2);
+ arguments.add(ARGUMENT3);
+ }
+
+ if (argumentsCmd.isEmpty()) {
+ argumentsCmd.add(ARGUMENT_CMD1);
+ }
+
+ String argumentsMap
+ = JPackageHelper.listToArgumentsMap(arguments, toolProvider);
+ cmd[cmd.length - 1] = argumentsMap;
+ }
+
+ private static void validateResult(String[] result, List<String> args)
+ throws Exception {
+ if (result.length != (args.size() + 2)) {
+ throw new AssertionError(
+ "Unexpected number of lines: " + result.length);
+ }
+
+ if (!result[0].trim().equals("jpackage test application")) {
+ throw new AssertionError("Unexpected result[0]: " + result[0]);
+ }
+
+ if (!result[1].trim().equals("args.length: " + args.size())) {
+ throw new AssertionError("Unexpected result[1]: " + result[1]);
+ }
+
+ int index = 2;
+ for (String arg : args) {
+ if (!result[index].trim().equals(arg)) {
+ throw new AssertionError(
+ "Unexpected result[" + index + "]: " + result[index]);
+ }
+ index++;
+ }
+ }
+
+ private static void validate(String arg, List<String> expectedArgs)
+ throws Exception {
+ int retVal;
+
+ if (arg == null) {
+ retVal = JPackageHelper.execute(null, app);
+ } else {
+ retVal = JPackageHelper.execute(null, app, arg);
+ }
+ if (retVal != 0) {
+ throw new AssertionError("Test application exited with error: "
+ + retVal);
+ }
+
+ File outfile = new File(appWorkingDir + File.separator + appOutput);
+ if (!outfile.exists()) {
+ throw new AssertionError(appOutput + " was not created");
+ }
+
+ String output = Files.readString(outfile.toPath());
+ String[] result = output.split("\n");
+ validateResult(result, expectedArgs);
+ }
+
+ public static void testCreateAppImage(String[] cmd) throws Exception {
+ initArguments(false, cmd);
+ JPackageHelper.executeCLI(true, cmd);
+ validate(null, arguments);
+ validate(ARGUMENT_CMD1, argumentsCmd);
+ }
+
+ public static void testCreateAppImageToolProvider(String[] cmd) throws Exception {
+ initArguments(true, cmd);
+ JPackageHelper.executeToolProvider(true, cmd);
+ validate(null, arguments);
+ validate(ARGUMENT_CMD1, argumentsCmd);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createappimage/JPackageCreateAppImageArgumentsModuleTest.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,51 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+/*
+ * @test
+ * @summary jpackage create image with --arguments test
+ * @library ../helpers
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageCreateAppImageArgumentsBase
+ * @modules jdk.jpackage
+ * @run main/othervm -Xmx512m JPackageCreateAppImageArgumentsModuleTest
+ */
+public class JPackageCreateAppImageArgumentsModuleTest {
+ private static final String OUTPUT = "output";
+
+ private static final String[] CMD = {
+ "--output", OUTPUT,
+ "--name", "test",
+ "--module", "com.hello/com.hello.Hello",
+ "--module-path", "input",
+ "--arguments", "TBD"};
+
+ public static void main(String[] args) throws Exception {
+ JPackageHelper.createHelloModule();
+ JPackageCreateAppImageArgumentsBase.testCreateAppImage(CMD);
+ JPackageHelper.deleteOutputFolder(OUTPUT);
+ JPackageCreateAppImageArgumentsBase.testCreateAppImageToolProvider(CMD);
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createappimage/JPackageCreateAppImageArgumentsTest.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,52 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+/*
+ * @test
+ * @summary jpackage create image with --arguments test
+ * @library ../helpers
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageCreateAppImageArgumentsBase
+ * @modules jdk.jpackage
+ * @run main/othervm -Xmx512m JPackageCreateAppImageArgumentsTest
+ */
+public class JPackageCreateAppImageArgumentsTest {
+ private static final String OUTPUT = "output";
+
+ private static final String[] CMD = {
+ "--input", "input",
+ "--output", OUTPUT,
+ "--name", "test",
+ "--main-jar", "hello.jar",
+ "--main-class", "Hello",
+ "--arguments", "TBD"};
+
+ public static void main(String[] args) throws Exception {
+ JPackageHelper.createHelloImageJar();
+ JPackageCreateAppImageArgumentsBase.testCreateAppImage(CMD);
+ JPackageHelper.deleteOutputFolder(OUTPUT);
+ JPackageCreateAppImageArgumentsBase.testCreateAppImageToolProvider(CMD);
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createappimage/JPackageCreateAppImageAtFilenameTest.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,64 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+ /*
+ * @test
+ * @summary jpackage create image test
+ * @library ../helpers
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageCreateAppImageBase
+ * @modules jdk.jpackage
+ * @run main/othervm -Xmx512m JPackageCreateAppImageAtFilenameTest
+ */
+public class JPackageCreateAppImageAtFilenameTest {
+ private static final String OUTPUT = "output";
+
+ private static final String [] CMD = {
+ "--input", "input",
+ "--output", OUTPUT,
+ "--name", "test",
+ "--main-jar", "hello.jar",
+ "--main-class", "Hello",
+ };
+
+ public static void main(String[] args) throws Exception {
+ JPackageHelper.createHelloImageJar();
+
+ doTest(0, 1); // replace just the mode
+ doTest(0, 11); // replace everything
+ doTest(1, 10); // replace everything except mode
+ doTest(4, 2); // replace the name and --main-jar without jar name
+
+ }
+
+ private static void doTest(int index, int len) throws Exception {
+ String[] cmdWithAtFilename =
+ JPackageHelper.cmdWithAtFilename(CMD, index, len);
+
+ JPackageCreateAppImageBase.
+ testCreateAppImageToolProvider(cmdWithAtFilename);
+ JPackageHelper.deleteOutputFolder(OUTPUT);
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createappimage/JPackageCreateAppImageBase.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,81 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+import java.nio.file.Files;
+import java.nio.file.Path;
+
+public abstract class JPackageCreateAppImageBase {
+ private static final String appOutput = JPackagePath.getAppOutputFile();
+ private static final String appWorkingDir = JPackagePath.getAppWorkingDir();
+
+ private static void validateResult(String[] result) throws Exception {
+ if (result.length != 2) {
+ throw new AssertionError(
+ "Unexpected number of lines: " + result.length);
+ }
+
+ if (!result[0].trim().endsWith("jpackage test application")) {
+ throw new AssertionError("Unexpected result[0]: " + result[0]);
+ }
+
+ if (!result[1].trim().equals("args.length: 0")) {
+ throw new AssertionError("Unexpected result[1]: " + result[1]);
+ }
+ }
+
+ public static void validate(String app) throws Exception {
+ Path outPath = Path.of(appWorkingDir, appOutput);
+ int retVal = JPackageHelper.execute(null, 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 (" + app + ") exited with error: " + retVal);
+ }
+
+ if (!outPath.toFile().exists()) {
+ throw new AssertionError(appOutput + " was not created");
+ }
+
+ String output = Files.readString(outPath);
+ String[] result = JPackageHelper.splitAndFilter(output);
+ validateResult(result);
+ }
+
+ public static void testCreateAppImage(String [] cmd) throws Exception {
+ JPackageHelper.executeCLI(true, cmd);
+ validate(JPackagePath.getApp());
+ }
+
+ public static void testCreateAppImageToolProvider(String [] cmd) throws Exception {
+ JPackageHelper.executeToolProvider(true, cmd);
+ validate(JPackagePath.getApp());
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createappimage/JPackageCreateAppImageErrorTest.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,96 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+ /*
+ * @test
+ * @summary jpackage create app image error test
+ * @library ../helpers
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageCreateAppImageBase
+ * @modules jdk.jpackage
+ * @run main/othervm -Xmx512m JPackageCreateAppImageErrorTest
+ */
+import java.util.*;
+import java.io.*;
+import java.nio.*;
+import java.nio.file.*;
+import java.nio.file.attribute.*;
+
+public class JPackageCreateAppImageErrorTest {
+
+ private static final String OUTPUT = "output";
+
+ private static final String ARG1 = "--no-such-argument";
+ private static final String EXPECTED1 =
+ "Invalid Option: [--no-such-argument]";
+ private static final String ARG2 = "--output";
+ private static final String EXPECTED2 = "--main-jar or --module";
+
+ private static final String [] CMD1 = {
+ "--input", "input",
+ "--output", OUTPUT,
+ "--name", "test",
+ "--main-jar", "non-existant.jar",
+ };
+ private static final String EXP1 = "main jar does not exist";
+
+ private static final String [] CMD2 = {
+ "--input", "input",
+ "--output", OUTPUT,
+ "--name", "test",
+ "--main-jar", "hello.jar",
+ };
+ private static final String EXP2 = "class was not specified nor was";
+
+ private static void validate(String output, String expected, boolean single)
+ throws Exception {
+ String[] result = output.split("\n");
+ if (single && result.length != 1) {
+ System.err.println(output);
+ throw new AssertionError("Unexpected multiple lines of output: "
+ + output);
+ }
+
+ if (!result[0].trim().contains(expected)) {
+ throw new AssertionError("Unexpected output: " + result[0]
+ + " - expected output to contain: " + expected);
+ }
+ }
+
+
+ public static void main(String[] args) throws Exception {
+ JPackageHelper.createHelloImageJar();
+
+ validate(JPackageHelper.executeToolProvider(false, ARG1), EXPECTED1, true);
+ validate(JPackageHelper.executeToolProvider(false, ARG2), EXPECTED2, true);
+
+ JPackageHelper.deleteOutputFolder(OUTPUT);
+ validate(JPackageHelper.executeToolProvider(false, CMD1), EXP1, false);
+
+ JPackageHelper.deleteOutputFolder(OUTPUT);
+ validate(JPackageHelper.executeToolProvider(false, CMD2), EXP2, false);
+
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createappimage/JPackageCreateAppImageIconTest.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,125 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+import java.io.File;
+import java.nio.file.Files;
+
+/*
+ * @test
+ * @summary jpackage create image to verify --icon
+ * @library ../helpers
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @modules jdk.jpackage
+ * @run main/othervm -Xmx512m JPackageCreateAppImageIconTest
+ */
+public class JPackageCreateAppImageIconTest {
+ private static final String OUTPUT = "output";
+ private static final String app = JPackagePath.getApp();
+ private static final String appOutput = JPackagePath.getAppOutputFile();
+ private static final String appWorkingDir = JPackagePath.getAppWorkingDir();
+
+ private static final String[] CMD = {
+ "--input", "input",
+ "--name", "test",
+ "--main-jar", "hello.jar",
+ "--main-class", "Hello",
+ "--icon", getIconPath(),
+ "--output", OUTPUT};
+
+ private static void validateResult(String[] result) throws Exception {
+ if (result.length != 2) {
+ throw new AssertionError(
+ "Unexpected number of lines: " + result.length);
+ }
+
+ if (!result[0].trim().equals("jpackage test application")) {
+ throw new AssertionError("Unexpected result[0]: " + result[0]);
+ }
+
+ if (!result[1].trim().equals("args.length: 0")) {
+ throw new AssertionError("Unexpected result[1]: " + result[1]);
+ }
+ }
+
+ private static void validate() throws Exception {
+ int retVal = JPackageHelper.execute(null, app);
+ if (retVal != 0) {
+ throw new AssertionError(
+ "Test application exited with error: " + retVal);
+ }
+
+ File outfile = new File(appWorkingDir + File.separator + appOutput);
+ if (!outfile.exists()) {
+ throw new AssertionError(appOutput + " was not created");
+ }
+
+ String output = Files.readString(outfile.toPath());
+ String[] result = output.split("\n");
+ validateResult(result);
+ }
+
+ private static void validateIcon() throws Exception {
+ File origIcon = new File(getIconPath());
+ File icon = new File(JPackagePath.getAppIcon());
+ if (origIcon.length() != icon.length()) {
+ System.err.println("origIcon.length(): " + origIcon.length());
+ System.err.println("icon.length(): " + icon.length());
+ throw new AssertionError("Icons size does not match");
+ }
+ }
+
+ private static void testIcon() throws Exception {
+ JPackageHelper.executeCLI(true, CMD);
+ validate();
+ validateIcon();
+ }
+
+ private static void testIconToolProvider() throws Exception {
+ JPackageHelper.deleteOutputFolder(OUTPUT);
+ JPackageHelper.executeToolProvider(true, CMD);
+ validate();
+ validateIcon();
+ }
+
+ private static String getIconPath() {
+ String ext = ".ico";
+ if (JPackageHelper.isOSX()) {
+ ext = ".icns";
+ } else if (JPackageHelper.isLinux()) {
+ ext = ".png";
+ }
+
+ String path = JPackagePath.getTestSrcRoot() + File.separator + "resources"
+ + File.separator + "icon" + ext;
+
+ return path;
+ }
+
+ public static void main(String[] args) throws Exception {
+ JPackageHelper.createHelloImageJar();
+ testIcon();
+ testIconToolProvider();
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createappimage/JPackageCreateAppImageJLinkModuleTest.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 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.
+ *
+ * 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.
+ */
+
+import java.util.ArrayList;
+
+ /*
+ * @test
+ * @summary jpackage create image modular jar test
+ * @library ../helpers
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageCreateAppImageBase
+ * @modules jdk.jpackage
+ * @run main/othervm -Xmx512m JPackageCreateAppImageJLinkModuleTest
+ */
+public class JPackageCreateAppImageJLinkModuleTest {
+ private static final String OUTPUT = "output";
+ private static final String RUNTIME = "runtime";
+
+ private static final String [] CMD = {
+ "--output", OUTPUT,
+ "--name", "test",
+ "--module", "com.other/com.other.Other",
+ "--runtime-image", RUNTIME,
+ };
+
+ public static void main(String[] args) throws Exception {
+ JPackageHelper.createOtherModule();
+
+ ArrayList<String> jlargs = new ArrayList<>();
+ jlargs.add("--add-modules");
+ jlargs.add("com.other");
+ jlargs.add("--module-path");
+ jlargs.add("module");
+ jlargs.add("--strip-debug");
+ jlargs.add("--no-header-files");
+ jlargs.add("--no-man-pages");
+ jlargs.add("--strip-native-commands");
+ JPackageHelper.createRuntime(jlargs);
+
+
+ JPackageHelper.deleteOutputFolder(OUTPUT);
+ JPackageCreateAppImageBase.testCreateAppImage(CMD);
+
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createappimage/JPackageCreateAppImageJavaOptionsBase.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,149 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+import java.io.File;
+import java.nio.file.Files;
+import java.util.ArrayList;
+import java.util.List;
+
+public class JPackageCreateAppImageJavaOptionsBase {
+
+ private static final String app = JPackagePath.getApp();
+ private static final String appOutput = JPackagePath.getAppOutputFile();
+ private static final String appWorkingDir = JPackagePath.getAppWorkingDir();
+
+ private static final String ARGUMENT1 = "-Dparam1=Some Param 1";
+ private static final String ARGUMENT2 = "-Dparam2=Some \"Param\" 2";
+ private static final String ARGUMENT3 =
+ "-Dparam3=Some \"Param\" with \" 3";
+
+ private static final List<String> arguments = new ArrayList<>();
+
+ private static void initArguments(boolean toolProvider, String [] cmd) {
+ if (arguments.isEmpty()) {
+ arguments.add(ARGUMENT1);
+ arguments.add(ARGUMENT2);
+ arguments.add(ARGUMENT3);
+ }
+
+ String argumentsMap = JPackageHelper.listToArgumentsMap(arguments,
+ toolProvider);
+ cmd[cmd.length - 1] = argumentsMap;
+ }
+
+ private static void initArguments2(boolean toolProvider, String [] cmd) {
+ int index = cmd.length - 6;
+
+ cmd[index++] = "--java-options";
+ arguments.clear();
+ arguments.add(ARGUMENT1);
+ cmd[index++] = JPackageHelper.listToArgumentsMap(arguments,
+ toolProvider);
+
+ cmd[index++] = "--java-options";
+ arguments.clear();
+ arguments.add(ARGUMENT2);
+ cmd[index++] = JPackageHelper.listToArgumentsMap(arguments,
+ toolProvider);
+
+ cmd[index++] = "--java-options";
+ arguments.clear();
+ arguments.add(ARGUMENT3);
+ cmd[index++] = JPackageHelper.listToArgumentsMap(arguments,
+ toolProvider);
+
+ arguments.clear();
+ arguments.add(ARGUMENT1);
+ arguments.add(ARGUMENT2);
+ arguments.add(ARGUMENT3);
+ }
+
+ private static void validateResult(String[] result, List<String> args)
+ throws Exception {
+ if (result.length != (args.size() + 2)) {
+ for (String r : result) {
+ System.err.println(r.trim());
+ }
+ throw new AssertionError("Unexpected number of lines: "
+ + result.length);
+ }
+
+ if (!result[0].trim().equals("jpackage test application")) {
+ throw new AssertionError("Unexpected result[0]: " + result[0]);
+ }
+
+ if (!result[1].trim().equals("args.length: 0")) {
+ throw new AssertionError("Unexpected result[1]: " + result[1]);
+ }
+
+ int index = 2;
+ for (String arg : args) {
+ if (!result[index].trim().equals(arg)) {
+ throw new AssertionError("Unexpected result[" + index + "]: "
+ + result[index]);
+ }
+ index++;
+ }
+ }
+
+ private static void validate(List<String> expectedArgs) throws Exception {
+ int retVal = JPackageHelper.execute(null, app);
+ if (retVal != 0) {
+ throw new AssertionError("Test application exited with error: "
+ + retVal);
+ }
+
+ File outfile = new File(appWorkingDir + File.separator + appOutput);
+ if (!outfile.exists()) {
+ throw new AssertionError(appOutput + " was not created");
+ }
+
+ String output = Files.readString(outfile.toPath());
+ String[] result = JPackageHelper.splitAndFilter(output);
+ validateResult(result, expectedArgs);
+ }
+
+ public static void testCreateAppImageJavaOptions(String [] cmd) throws Exception {
+ initArguments(false, cmd);
+ JPackageHelper.executeCLI(true, cmd);
+ validate(arguments);
+ }
+
+ public static void testCreateAppImageJavaOptionsToolProvider(String [] cmd) throws Exception {
+ initArguments(true, cmd);
+ JPackageHelper.executeToolProvider(true, cmd);
+ validate(arguments);
+ }
+
+ public static void testCreateAppImageJavaOptions2(String [] cmd) throws Exception {
+ initArguments2(false, cmd);
+ JPackageHelper.executeCLI(true, cmd);
+ validate(arguments);
+ }
+
+ public static void testCreateAppImageJavaOptions2ToolProvider(String [] cmd) throws Exception {
+ initArguments2(true, cmd);
+ JPackageHelper.executeToolProvider(true, cmd);
+ validate(arguments);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createappimage/JPackageCreateAppImageJavaOptionsEqualsTest.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,113 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+import java.io.File;
+import java.nio.file.Files;
+
+/*
+ * @test
+ * @summary jpackage create image with --java-options test
+ * @library ../helpers
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageCreateAppImageJavaOptionsBase
+ * @modules jdk.jpackage
+ * @run main/othervm -Xmx512m JPackageCreateAppImageJavaOptionsEqualsTest
+ */
+public class JPackageCreateAppImageJavaOptionsEqualsTest {
+
+ private static final String app = JPackagePath.getApp();
+ private static final String appWorkingDir = JPackagePath.getAppWorkingDir();
+
+ private static final String OUTPUT = "output";
+
+ private static final String[] CMD = {
+ "--input", "input",
+ "--description", "the two options below should cause two app execution "
+ + "Warnings with two lines output saying: "
+ + "WARNING: Unknown module: <module-name>",
+ "--output", OUTPUT,
+ "--name", "test",
+ "--main-jar", "hello.jar",
+ "--main-class", "Hello",
+ "--java-options",
+ "--add-exports=java.base/sun.util=me.mymodule.foo,ALL-UNNAMED",
+ "--java-options",
+ "--add-exports=java.base/sun.security.util=other.mod.bar,ALL-UNNAMED",
+ };
+
+ private static void validate() throws Exception {
+ File outfile = new File(appWorkingDir + File.separator + "app.out");
+
+ int retVal = JPackageHelper.execute(outfile, app);
+ if (retVal != 0) {
+ throw new AssertionError(
+ "Test application exited with error: " + retVal);
+ }
+
+ if (!outfile.exists()) {
+ throw new AssertionError(
+ "outfile: " + outfile + " was not created");
+ }
+
+ String output = Files.readString(outfile.toPath());
+ String[] result = JPackageHelper.splitAndFilter(output);
+ if (result.length != 4) {
+ throw new AssertionError(
+ "Unexpected number of lines: " + result.length
+ + " - output: " + output);
+ }
+
+ if (!result[0].startsWith("WARNING: Unknown module: me.mymodule.foo")){
+ throw new AssertionError("Unexpected result[0]: " + result[0]);
+ }
+
+ if (result[1].equals(result[0])) {
+ System.err.println("--- This is known bug JDK-8224486, remove this "
+ + "if/else block when JDK-8224486 is fixed");
+ } else
+
+ if (!result[1].startsWith("WARNING: Unknown module: other.mod.bar")) {
+ throw new AssertionError("Unexpected result[1]: " + result[1]);
+ }
+
+ if (!result[2].trim().endsWith("jpackage test application")) {
+ throw new AssertionError("Unexpected result[2]: " + result[2]);
+ }
+
+ if (!result[3].trim().equals("args.length: 0")) {
+ throw new AssertionError("Unexpected result[3]: " + result[3]);
+ }
+ }
+
+ public static void main(String[] args) throws Exception {
+ JPackageHelper.createHelloImageJar();
+ String output = JPackageHelper.executeCLI(true, CMD);
+ validate();
+
+ JPackageHelper.deleteOutputFolder(OUTPUT);
+ output = JPackageHelper.executeToolProvider(true, CMD);
+ validate();
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createappimage/JPackageCreateAppImageJavaOptionsModuleTest.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,66 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+/*
+ * @test
+ * @summary jpackage create image with --java-options test
+ * @library ../helpers
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageCreateAppImageJavaOptionsBase
+ * @modules jdk.jpackage
+ * @run main/othervm -Xmx512m JPackageCreateAppImageJavaOptionsModuleTest
+ */
+public class JPackageCreateAppImageJavaOptionsModuleTest {
+ private static final String OUTPUT = "output";
+
+ private static final String[] CMD = {
+ "--output", OUTPUT,
+ "--name", "test",
+ "--module", "com.hello/com.hello.Hello",
+ "--module-path", "input",
+ "--java-options", "TBD"};
+
+ private static final String[] CMD2 = {
+ "--output", OUTPUT,
+ "--name", "test",
+ "--module", "com.hello/com.hello.Hello",
+ "--module-path", "input",
+ "--java-options", "TBD",
+ "--java-options", "TBD",
+ "--java-options", "TBD"};
+
+ public static void main(String[] args) throws Exception {
+ JPackageHelper.createHelloModule();
+
+ JPackageCreateAppImageJavaOptionsBase.testCreateAppImageJavaOptions(CMD);
+ JPackageHelper.deleteOutputFolder(OUTPUT);
+ JPackageCreateAppImageJavaOptionsBase.testCreateAppImageJavaOptionsToolProvider(CMD);
+
+ JPackageHelper.deleteOutputFolder(OUTPUT);
+ JPackageCreateAppImageJavaOptionsBase.testCreateAppImageJavaOptions2(CMD2);
+ JPackageHelper.deleteOutputFolder(OUTPUT);
+ JPackageCreateAppImageJavaOptionsBase.testCreateAppImageJavaOptions2ToolProvider(CMD2);
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createappimage/JPackageCreateAppImageJavaOptionsTest.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,67 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+/*
+ * @test
+ * @summary jpackage create image with --java-options test
+ * @library ../helpers
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageCreateAppImageJavaOptionsBase
+ * @modules jdk.jpackage
+ * @run main/othervm -Xmx512m JPackageCreateAppImageJavaOptionsTest
+ */
+public class JPackageCreateAppImageJavaOptionsTest {
+ private static final String OUTPUT = "output";
+
+ private static final String[] CMD = {
+ "--input", "input",
+ "--output", OUTPUT,
+ "--name", "test",
+ "--main-jar", "hello.jar",
+ "--main-class", "Hello",
+ "--java-options", "TBD"};
+
+ private static final String[] CMD2 = {
+ "--input", "input",
+ "--output", OUTPUT,
+ "--name", "test",
+ "--main-jar", "hello.jar",
+ "--main-class", "Hello",
+ "--java-options", "TBD",
+ "--java-options", "TBD",
+ "--java-options", "TBD"};
+
+ public static void main(String[] args) throws Exception {
+ JPackageHelper.createHelloImageJar();
+ JPackageCreateAppImageJavaOptionsBase.testCreateAppImageJavaOptions(CMD);
+ JPackageHelper.deleteOutputFolder(OUTPUT);
+ JPackageCreateAppImageJavaOptionsBase.testCreateAppImageJavaOptionsToolProvider(CMD);
+
+ JPackageHelper.deleteOutputFolder(OUTPUT);
+ JPackageCreateAppImageJavaOptionsBase.testCreateAppImageJavaOptions2(CMD2);
+ JPackageHelper.deleteOutputFolder(OUTPUT);
+ JPackageCreateAppImageJavaOptionsBase.testCreateAppImageJavaOptions2ToolProvider(CMD2);
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createappimage/JPackageCreateAppImageMainClassAttributeTest.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,97 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+import java.io.File;
+import java.nio.file.Files;
+
+/*
+ * @test
+ * @summary jpackage create image with no main class arguments and with main-class attribute
+ * @library ../helpers
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @modules jdk.jpackage
+ * @run main/othervm -Xmx512m JPackageCreateAppImageMainClassAttributeTest
+ */
+public class JPackageCreateAppImageMainClassAttributeTest {
+ private static final String OUTPUT = "output";
+ private static final String app = JPackagePath.getApp();
+ private static final String appOutput = JPackagePath.getAppOutputFile();
+ private static final String appWorkingDir = JPackagePath.getAppWorkingDir();
+
+ private static final String[] CMD = {
+ "--input", "input",
+ "--output", OUTPUT,
+ "--name", "test",
+ "--main-jar", "hello.jar"};
+
+ private static void validateResult(String[] result) throws Exception {
+ if (result.length != 2) {
+ throw new AssertionError(
+ "Unexpected number of lines: " + result.length);
+ }
+
+ if (!result[0].trim().equals("jpackage test application")) {
+ throw new AssertionError("Unexpected result[0]: " + result[0]);
+ }
+
+ if (!result[1].trim().equals("args.length: 0")) {
+ throw new AssertionError("Unexpected result[1]: " + result[1]);
+ }
+ }
+
+ private static void validate() throws Exception {
+ int retVal = JPackageHelper.execute(null, app);
+ if (retVal != 0) {
+ throw new AssertionError(
+ "Test application exited with error: " + retVal);
+ }
+
+ File outfile = new File(appWorkingDir + File.separator + appOutput);
+ if (!outfile.exists()) {
+ throw new AssertionError(appOutput + " was not created");
+ }
+
+ String output = Files.readString(outfile.toPath());
+ String[] result = output.split("\n");
+ validateResult(result);
+ }
+
+ private static void testMainClassAttribute() throws Exception {
+ JPackageHelper.executeCLI(true, CMD);
+ validate();
+ }
+
+ private static void testMainClassAttributeToolProvider() throws Exception {
+ JPackageHelper.deleteOutputFolder(OUTPUT);
+ JPackageHelper.executeToolProvider(true, CMD);
+ validate();
+ }
+
+ public static void main(String[] args) throws Exception {
+ JPackageHelper.createHelloImageJarWithMainClass();
+ testMainClassAttribute();
+ testMainClassAttributeToolProvider();
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createappimage/JPackageCreateAppImageMainClassErrorTest.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,74 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+import java.io.File;
+import java.nio.file.Files;
+
+/*
+ * @test
+ * @summary jpackage create image with no main class arguments and with main-class attribute
+ * @library ../helpers
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @modules jdk.jpackage
+ * @run main/othervm -Xmx512m JPackageCreateAppImageMainClassErrorTest
+ */
+public class JPackageCreateAppImageMainClassErrorTest {
+ private static final String OUTPUT = "output";
+ private static final String app = JPackagePath.getApp();
+ private static final String appOutput = JPackagePath.getAppOutputFile();
+ private static final String appWorkingDir = JPackagePath.getAppWorkingDir();
+
+ private static final String[] CMD = {
+ "--input", "input",
+ "--output", OUTPUT,
+ "--name", "test",
+ "--main-jar", "hello.jar"};
+
+ private static void validate(String output) throws Exception {
+ String[] result = JPackageHelper.splitAndFilter(output);
+ if (result.length != 2) {
+ throw new AssertionError(
+ "Unexpected number of lines: " + result.length);
+ }
+
+ if (!result[0].trim().contains("main class was not specified")) {
+ throw new AssertionError("Unexpected result[0]: " + result[0]);
+ }
+
+ if (!result[1].trim().startsWith("Advice to fix: ")) {
+ throw new AssertionError("Unexpected result[1]: " + result[1]);
+ }
+ }
+
+ public static void main(String[] args) throws Exception {
+ JPackageHelper.createHelloImageJar();
+
+ JPackageHelper.deleteOutputFolder(OUTPUT);
+ validate(JPackageHelper.executeCLI(false, CMD));
+
+ JPackageHelper.deleteOutputFolder(OUTPUT);
+ validate(JPackageHelper.executeToolProvider(false, CMD));
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createappimage/JPackageCreateAppImageModularJarTest.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 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.
+ *
+ * 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.
+ */
+
+ /*
+ * @test
+ * @summary jpackage create image modular jar test
+ * @library ../helpers
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageCreateAppImageBase
+ * @modules jdk.jpackage
+ * @run main/othervm -Xmx512m JPackageCreateAppImageModularJarTest
+ */
+public class JPackageCreateAppImageModularJarTest {
+ private static final String OUTPUT = "output";
+
+ private static final String [] CMD1 = {
+ "--input", "input",
+ "--output", OUTPUT,
+ "--name", "test",
+ "--main-jar", "com.hello.jar",
+ "--main-class", "com.hello.Hello",
+ };
+
+ private static final String [] CMD2 = {
+ "--output", OUTPUT,
+ "--name", "test",
+ "--module", "com.hello/com.hello.Hello",
+ "--module-path", "input/com.hello.jar",
+ };
+
+ public static void main(String[] args) throws Exception {
+ JPackageHelper.createHelloModule();
+
+ JPackageCreateAppImageBase.testCreateAppImage(CMD1);
+ JPackageHelper.deleteOutputFolder(OUTPUT);
+ JPackageCreateAppImageBase.testCreateAppImageToolProvider(CMD1);
+
+ JPackageHelper.deleteOutputFolder(OUTPUT);
+ JPackageCreateAppImageBase.testCreateAppImage(CMD2);
+ JPackageHelper.deleteOutputFolder(OUTPUT);
+ JPackageCreateAppImageBase.testCreateAppImageToolProvider(CMD2);
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createappimage/JPackageCreateAppImageModuleMainClassErrorTest.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,94 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+import java.io.File;
+import java.nio.file.Files;
+
+/*
+ * @test
+ * @summary jpackage create image with no main class arguments and with main-class attribute
+ * @library ../helpers
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @modules jdk.jpackage
+ * @run main/othervm -Xmx512m JPackageCreateAppImageModuleMainClassErrorTest
+ */
+public class JPackageCreateAppImageModuleMainClassErrorTest {
+ private static final String OUTPUT = "output";
+ private static final String app = JPackagePath.getApp();
+ private static final String appOutput = JPackagePath.getAppOutputFile();
+ private static final String appWorkingDir = JPackagePath.getAppWorkingDir();
+
+ private static final String [] CMD1 = {
+ "--output", OUTPUT,
+ "--name", "test",
+ "--module", "com.hello",
+ "--module-path", "input"};
+
+ private static final String [] CMD2 = {
+ "--output", OUTPUT,
+ "--name", "test",
+ "--module", "com.hello/com.hello.Hello",
+ "--module-path", "input"};
+
+ private static void validate(String buildOutput) throws Exception {
+
+ File outfile = new File(appWorkingDir + File.separator + appOutput);
+ int retVal = JPackageHelper.execute(outfile, app);
+ if (retVal != 0) {
+ throw new AssertionError(
+ "Test application exited with error: ");
+ }
+
+ if (!outfile.exists()) {
+ throw new AssertionError(appOutput + " was not created");
+ }
+ String output = Files.readString(outfile.toPath());
+ String[] result = output.split("\n");
+
+ if (!result[0].trim().equals("jpackage test application")) {
+ throw new AssertionError("Unexpected result[0]: " + result[0]);
+ }
+
+ if (!result[1].trim().equals("args.length: 0")) {
+ throw new AssertionError("Unexpected result[1]: " + result[1]);
+ }
+ }
+
+ public static void main(String[] args) throws Exception {
+ JPackageHelper.createHelloModule();
+
+ JPackageHelper.deleteOutputFolder(OUTPUT);
+ JPackageHelper.executeCLI(false, CMD1);
+
+ JPackageHelper.deleteOutputFolder(OUTPUT);
+ JPackageHelper.executeToolProvider(false, CMD1);
+
+ JPackageHelper.deleteOutputFolder(OUTPUT);
+ validate(JPackageHelper.executeCLI(true, CMD2));
+
+ JPackageHelper.deleteOutputFolder(OUTPUT);
+ validate(JPackageHelper.executeToolProvider(true, CMD2));
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createappimage/JPackageCreateAppImageModulePathTest.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,71 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+ /*
+ * @test
+ * @summary jpackage create image module test
+ * @library ../helpers
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageCreateAppImageBase
+ * @modules jdk.jpackage
+ * @run main/othervm -Xmx512m JPackageCreateAppImageModulePathTest
+ */
+
+import java.io.File;
+
+public class JPackageCreateAppImageModulePathTest {
+ private static final String OUTPUT = "output";
+
+ private static final String [] CMD1 = {
+ "--output", OUTPUT,
+ "--name", "test",
+ "--module", "com.hello/com.hello.Hello",
+ "--module-path", "input",
+ };
+
+ private static final String [] CMD2 = {
+ "--output", OUTPUT,
+ "--name", "test",
+ "--module", "com.hello/com.hello.Hello",
+ "--module-path", "input" + File.pathSeparator + "input-other",
+ };
+
+ private static final String [] CMD3 = {
+ "--output", OUTPUT,
+ "--name", "test",
+ "--module", "com.hello/com.hello.Hello",
+ "--module-path", "input",
+ "--module-path", "input-other",
+ };
+
+ public static void main(String[] args) throws Exception {
+ JPackageHelper.createHelloModule();
+ JPackageCreateAppImageBase.testCreateAppImageToolProvider(CMD1);
+ JPackageHelper.deleteOutputFolder(OUTPUT);
+ JPackageCreateAppImageBase.testCreateAppImageToolProvider(CMD2);
+ JPackageHelper.deleteOutputFolder(OUTPUT);
+ JPackageCreateAppImageBase.testCreateAppImageToolProvider(CMD3);
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createappimage/JPackageCreateAppImageModuleTest.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 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.
+ *
+ * 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.
+ */
+
+ /*
+ * @test
+ * @summary jpackage create image modular jar test
+ * @library ../helpers
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageCreateAppImageBase
+ * @modules jdk.jpackage
+ * @run main/othervm -Xmx512m JPackageCreateAppImageModuleTest
+ */
+public class JPackageCreateAppImageModuleTest {
+ private static final String OUTPUT = "output";
+
+ private static final String [] CMD1 = {
+ "--module-path", "module",
+ "--module", "com.other/com.other.Other",
+ "--output", OUTPUT,
+ "--name", "test",
+ };
+
+ private static String [] commands = {
+ "--module-path", "module",
+ "--module", "com.other/com.other.Other",
+ "--output", OUTPUT,
+ "--name", "test",
+ "--add-modules", "TBD",
+ };
+
+ private final static String [] paths = {
+ "ALL-MODULES",
+ "ALL_MODULE_PATH",
+ "ALL-SYSTEM",
+ "ALL-DEFAULT",
+ };
+
+ public static void main(String[] args) throws Exception {
+ JPackageHelper.createOtherModule();
+
+ JPackageHelper.deleteOutputFolder(OUTPUT);
+ JPackageCreateAppImageBase.testCreateAppImage(CMD1);
+
+ for (String path : paths) {
+ commands[commands.length - 1] = path;
+ System.out.println("using --add-modules " + path);
+ JPackageHelper.deleteOutputFolder(OUTPUT);
+ JPackageCreateAppImageBase.testCreateAppImageToolProvider(commands);
+ System.out.println("succeeded");
+ }
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createappimage/JPackageCreateAppImageNoNameTest.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,99 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+import java.io.File;
+import java.nio.file.Files;
+
+/*
+ * @test
+ * @summary jpackage create image with no --name
+ * @library ../helpers
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @modules jdk.jpackage
+ * @run main/othervm -Xmx512m JPackageCreateAppImageNoNameTest
+ */
+public class JPackageCreateAppImageNoNameTest {
+ private static final String OUTPUT = "output";
+ private static final String app = JPackagePath.getApp("Hello");
+ private static final String appOutput = JPackagePath.getAppOutputFile();
+ private static final String appWorkingDir =
+ JPackagePath.getAppWorkingDir("Hello");
+
+ private static final String[] CMD = {
+ "--input", "input",
+ "--output", OUTPUT,
+ "--main-jar", "hello.jar",
+ "--main-class", "Hello",
+ };
+
+ private static void validateResult(String[] result) throws Exception {
+ if (result.length != 2) {
+ throw new AssertionError(
+ "Unexpected number of lines: " + result.length);
+ }
+
+ if (!result[0].trim().equals("jpackage test application")) {
+ throw new AssertionError("Unexpected result[0]: " + result[0]);
+ }
+
+ if (!result[1].trim().equals("args.length: 0")) {
+ throw new AssertionError("Unexpected result[1]: " + result[1]);
+ }
+ }
+
+ private static void validate() throws Exception {
+ int retVal = JPackageHelper.execute(null, app);
+ if (retVal != 0) {
+ throw new AssertionError(
+ "Test application exited with error: " + retVal);
+ }
+
+ File outfile = new File(appWorkingDir + File.separator + appOutput);
+ if (!outfile.exists()) {
+ throw new AssertionError(appOutput + " was not created");
+ }
+
+ String output = Files.readString(outfile.toPath());
+ String[] result = output.split("\n");
+ validateResult(result);
+ }
+
+ private static void testMainClassAttribute() throws Exception {
+ JPackageHelper.executeCLI(true, CMD);
+ validate();
+ }
+
+ private static void testMainClassAttributeToolProvider() throws Exception {
+ JPackageHelper.deleteOutputFolder(OUTPUT);
+ JPackageHelper.executeToolProvider(true, CMD);
+ validate();
+ }
+
+ public static void main(String[] args) throws Exception {
+ JPackageHelper.createHelloImageJar();
+ testMainClassAttribute();
+ testMainClassAttributeToolProvider();
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createappimage/JPackageCreateAppImageResourceTest.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,130 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+import java.io.File;
+import java.nio.file.Files;
+
+/*
+ * @test
+ * @summary jpackage create image to verify --icon
+ * @library ../helpers
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @requires ((os.family == "windows") | (os.family == "mac"))
+ * @modules jdk.jpackage
+ * @run main/othervm -Xmx512m JPackageCreateAppImageResourceTest
+ */
+public class JPackageCreateAppImageResourceTest {
+ private static final String OUTPUT = "output";
+ private static final String app = JPackagePath.getApp("icon");
+ private static final String appOutput = JPackagePath.getAppOutputFile();
+ private static final String appWorkingDir =
+ JPackagePath.getAppWorkingDir("icon");
+ private static final String resourceDir =
+ JPackagePath.getTestSrcRoot() + File.separator + "resources";
+
+ private static final String[] CMD = {
+ "--input", "input",
+ "--name", "icon",
+ "--main-jar", "hello.jar",
+ "--main-class", "Hello",
+ "--resource-dir", resourceDir,
+ "--output", OUTPUT};
+
+ private static void validateResult(String[] result) throws Exception {
+ if (result.length != 2) {
+ throw new AssertionError(
+ "Unexpected number of lines: " + result.length);
+ }
+
+ if (!result[0].trim().equals("jpackage test application")) {
+ throw new AssertionError("Unexpected result[0]: " + result[0]);
+ }
+
+ if (!result[1].trim().equals("args.length: 0")) {
+ throw new AssertionError("Unexpected result[1]: " + result[1]);
+ }
+ }
+
+ private static void validate() throws Exception {
+ int retVal = JPackageHelper.execute(null, app);
+ if (retVal != 0) {
+ throw new AssertionError(
+ "Test application exited with error: " + retVal);
+ }
+
+ File outfile = new File(appWorkingDir + File.separator + appOutput);
+ if (!outfile.exists()) {
+ throw new AssertionError(appOutput + " was not created");
+ }
+
+ String output = Files.readString(outfile.toPath());
+ String[] result = output.split("\n");
+ validateResult(result);
+ }
+
+ private static void validateIcon() throws Exception {
+ File origIcon = new File(getIconPath());
+ File icon = new File(JPackagePath.getAppIcon("icon"));
+ if (origIcon.length() != icon.length()) {
+ System.err.println("file: " + origIcon + " - origIcon.length(): "
+ + origIcon.length());
+ System.err.println("file: " + icon + " - icon.length(): "
+ + icon.length());
+ throw new AssertionError("Icons size does not match");
+ }
+ }
+
+ private static void testIcon() throws Exception {
+ JPackageHelper.executeCLI(true, CMD);
+ validate();
+ validateIcon();
+ }
+
+ private static void testIconToolProvider() throws Exception {
+ JPackageHelper.deleteOutputFolder(OUTPUT);
+ JPackageHelper.executeToolProvider(true, CMD);
+ validate();
+ validateIcon();
+ }
+
+ private static String getResourcenPath() {
+ return JPackagePath.getTestSrcRoot() + File.separator + "resources";
+ }
+
+ private static String getIconPath() {
+ String ext = ".ico";
+ if (JPackageHelper.isOSX()) {
+ ext = ".icns";
+ } else if (JPackageHelper.isLinux()) {
+ ext = ".png";
+ }
+ return resourceDir + File.separator + "icon" + ext;
+ }
+
+ public static void main(String[] args) throws Exception {
+ JPackageHelper.createHelloImageJar();
+ testIcon();
+ testIconToolProvider();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createappimage/JPackageCreateAppImageRuntimeBase.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,98 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+import java.io.File;
+import java.nio.file.Files;
+
+ public class JPackageCreateAppImageRuntimeBase {
+ private static final String app = JPackagePath.getApp();
+ private static final String appWorkingDir = JPackagePath.getAppWorkingDir();
+ private static final String runtimeJava = JPackagePath.getRuntimeJava();
+ private static final String runtimeJavaOutput = "javaOutput.txt";
+ private static final String appOutput = JPackagePath.getAppOutputFile();
+
+ private static void validateResult(String[] result) throws Exception {
+ if (result.length != 2) {
+ throw new AssertionError("Unexpected number of lines: " + result.length);
+ }
+
+ if (!result[0].trim().equals("jpackage test application")) {
+ throw new AssertionError("Unexpected result[0]: " + result[0]);
+ }
+
+ if (!result[1].trim().equals("args.length: 0")) {
+ throw new AssertionError("Unexpected result[1]: " + result[1]);
+ }
+ }
+
+ private static void validate() throws Exception {
+ int retVal = JPackageHelper.execute(null, app);
+ if (retVal != 0) {
+ throw new AssertionError("Test application exited with error: " + retVal);
+ }
+
+ File outfile = new File(appWorkingDir + File.separator + appOutput);
+ if (!outfile.exists()) {
+ throw new AssertionError(appOutput + " was not created");
+ }
+
+ String output = Files.readString(outfile.toPath());
+ String[] result = JPackageHelper.splitAndFilter(output);
+ validateResult(result);
+ }
+
+ private static void validateRuntime() throws Exception {
+ int retVal = JPackageHelper.execute(new File(runtimeJavaOutput), runtimeJava, "--list-modules");
+ if (retVal != 0) {
+ throw new AssertionError("Test application exited with error: " + retVal);
+ }
+
+ File outfile = new File(runtimeJavaOutput);
+ if (!outfile.exists()) {
+ throw new AssertionError(runtimeJavaOutput + " was not created");
+ }
+
+ String output = Files.readString(outfile.toPath());
+ String[] result = JPackageHelper.splitAndFilter(output);
+ if (result.length != 1) {
+ throw new AssertionError("Unexpected number of lines: " + result.length);
+ }
+
+ if (!result[0].startsWith("java.base")) {
+ throw new AssertionError("Unexpected result: " + result[0]);
+ }
+ }
+
+ public static void testCreateAppImage(String [] cmd) throws Exception {
+ JPackageHelper.executeCLI(true, cmd);
+ validate();
+ validateRuntime();
+ }
+
+ public static void testCreateAppImageToolProvider(String [] cmd) throws Exception {
+ JPackageHelper.executeToolProvider(true, cmd);
+ validate();
+ validateRuntime();
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createappimage/JPackageCreateAppImageRuntimeModuleTest.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,51 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+ /*
+ * @test
+ * @summary jpackage create image runtime test
+ * @library ../helpers
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageCreateAppImageRuntimeBase
+ * @modules jdk.jpackage
+ * @run main/othervm -Xmx512m JPackageCreateAppImageRuntimeModuleTest
+ */
+public class JPackageCreateAppImageRuntimeModuleTest {
+ private static final String OUTPUT = "output";
+ private static final String [] CMD = {
+ "--runtime-image", "runtime",
+ "--output", OUTPUT,
+ "--name", "test",
+ "--module", "com.hello/com.hello.Hello",
+ "--module-path", "input"};
+
+ public static void main(String[] args) throws Exception {
+ JPackageHelper.createHelloModule();
+ JPackageHelper.createRuntime();
+ JPackageCreateAppImageRuntimeBase.testCreateAppImage(CMD);
+ JPackageHelper.deleteOutputFolder(OUTPUT);
+ JPackageCreateAppImageRuntimeBase.testCreateAppImageToolProvider(CMD);
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createappimage/JPackageCreateAppImageRuntimeTest.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,53 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+ /*
+ * @test
+ * @summary jpackage create image runtime test
+ * @library ../helpers
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageCreateAppImageRuntimeBase
+ * @modules jdk.jpackage
+ * @run main/othervm -Xmx512m JPackageCreateAppImageRuntimeTest
+ */
+public class JPackageCreateAppImageRuntimeTest {
+ private static final String OUTPUT = "output";
+ private static final String [] CMD = {
+ "--runtime-image", "runtime",
+ "--input", "input",
+ "--output", OUTPUT,
+ "--name", "test",
+ "--main-jar", "hello.jar",
+ "--main-class", "Hello",
+ };
+
+ public static void main(String[] args) throws Exception {
+ JPackageHelper.createHelloImageJar();
+ JPackageHelper.createRuntime();
+ JPackageCreateAppImageRuntimeBase.testCreateAppImage(CMD);
+ JPackageHelper.deleteOutputFolder(OUTPUT);
+ JPackageCreateAppImageRuntimeBase.testCreateAppImageToolProvider(CMD);
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createappimage/JPackageCreateAppImageTempRootTest.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,106 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+import java.io.File;
+
+ /*
+ * @test
+ * @requires (os.family == "windows")
+ * @summary jpackage create image to test --temp-root
+ * @library ../helpers
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @modules jdk.jpackage
+ * @run main/othervm -Xmx512m JPackageCreateAppImageTempRootTest
+ */
+public class JPackageCreateAppImageTempRootTest {
+ private static final String OUTPUT = "output";
+ private static String buildRoot = null;
+ private static final String BUILD_ROOT = "buildRoot";
+ private static final String BUILD_ROOT_TB = "buildRootToolProvider";
+
+ private static final String [] CMD = {
+ "--input", "input",
+ "--output", OUTPUT,
+ "--name", "test",
+ "--main-jar", "hello.jar",
+ "--main-class", "Hello",
+ };
+
+ private static final String [] CMD_BUILD_ROOT = {
+ "--input", "input",
+ "--output", OUTPUT,
+ "--name", "test",
+ "--main-jar", "hello.jar",
+ "--main-class", "Hello",
+ "--temp-root", "TBD"};
+
+ private static void validate(boolean retain) throws Exception {
+ File br = new File(buildRoot);
+ if (retain) {
+ if (!br.exists()) {
+ throw new AssertionError(br.getAbsolutePath() + " does not exist");
+ }
+ } else {
+ if (br.exists()) {
+ throw new AssertionError(br.getAbsolutePath() + " exist");
+ }
+ }
+ }
+
+ private static void init(boolean toolProvider) {
+ if (toolProvider) {
+ buildRoot = BUILD_ROOT_TB;
+ } else {
+ buildRoot = BUILD_ROOT;
+ }
+
+ CMD_BUILD_ROOT[CMD_BUILD_ROOT.length - 1] = buildRoot;
+ }
+
+ private static void testTempRoot() throws Exception {
+ init(false);
+ JPackageHelper.executeCLI(true, CMD);
+ validate(false);
+ JPackageHelper.deleteOutputFolder(OUTPUT);
+ JPackageHelper.executeCLI(true, CMD_BUILD_ROOT);
+ validate(true);
+ }
+
+ private static void testTempRootToolProvider() throws Exception {
+ init(true);
+ JPackageHelper.deleteOutputFolder(OUTPUT);
+ JPackageHelper.executeToolProvider(true, CMD);
+ validate(false);
+ JPackageHelper.deleteOutputFolder(OUTPUT);
+ JPackageHelper.executeToolProvider(true, CMD_BUILD_ROOT);
+ validate(true);
+ }
+
+ public static void main(String[] args) throws Exception {
+ JPackageHelper.createHelloImageJar();
+ testTempRoot();
+ testTempRootToolProvider();
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createappimage/JPackageCreateAppImageTest.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,51 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+ /*
+ * @test
+ * @summary jpackage create image test
+ * @library ../helpers
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageCreateAppImageBase
+ * @modules jdk.jpackage
+ * @run main/othervm -Xmx512m JPackageCreateAppImageTest
+ */
+public class JPackageCreateAppImageTest {
+ private static final String OUTPUT = "output";
+
+ private static final String [] CMD = {
+ "--input", "input",
+ "--output", OUTPUT,
+ "--name", "test",
+ "--main-jar", "hello.jar",
+ "--main-class", "Hello",
+ };
+
+ public static void main(String[] args) throws Exception {
+ JPackageHelper.createHelloImageJar();
+ JPackageCreateAppImageBase.testCreateAppImage(CMD);
+ JPackageHelper.deleteOutputFolder(OUTPUT);
+ JPackageCreateAppImageBase.testCreateAppImageToolProvider(CMD);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createappimage/JPackageCreateAppImageVerboseTest.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,88 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+/*
+ * @test
+ * @summary jpackage create image verbose test
+ * @library ../helpers
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @modules jdk.jpackage
+ * @run main/othervm -Xmx512m JPackageCreateAppImageVerboseTest
+ */
+public class JPackageCreateAppImageVerboseTest {
+ private static final String OUTPUT = "output";
+ private static final String[] CMD = {
+ "--input", "input",
+ "--output", OUTPUT,
+ "--name", "test",
+ "--main-jar", "hello.jar",
+ "--main-class", "Hello",
+ };
+
+ private static final String[] CMD_VERBOSE = {
+ "--input", "input",
+ "--output", OUTPUT,
+ "--name", "test",
+ "--main-jar", "hello.jar",
+ "--main-class", "Hello",
+ "--verbose"};
+
+ private static void validate(String result, String resultVerbose)
+ throws Exception {
+ String[] r = result.split("\n");
+ String[] rv = resultVerbose.split("\n");
+
+ if (r.length >= rv.length) {
+ System.err.println("r.length: " + r.length);
+ System.err.println(result);
+ System.err.println("rv.length: " + rv.length);
+ System.err.println(resultVerbose);
+ throw new AssertionError(
+ "non-verbose output is less or equal to verbose output");
+ }
+ }
+
+ private static void testCreateAppImage() throws Exception {
+ String result = JPackageHelper.executeCLI(true, CMD);
+ JPackageHelper.deleteOutputFolder(OUTPUT);
+ String resultVerbose = JPackageHelper.executeCLI(true, CMD_VERBOSE);
+ validate(result, resultVerbose);
+ }
+
+ private static void testCreateAppImageToolProvider() throws Exception {
+ JPackageHelper.deleteOutputFolder(OUTPUT);
+ String result = JPackageHelper.executeToolProvider(true, CMD);
+ JPackageHelper.deleteOutputFolder(OUTPUT);
+ String resultVerbose =
+ JPackageHelper.executeToolProvider(true, CMD_VERBOSE);
+ validate(result, resultVerbose);
+ }
+
+ public static void main(String[] args) throws Exception {
+ JPackageHelper.createHelloImageJar();
+ testCreateAppImage();
+ testCreateAppImageToolProvider();
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createappimage/JPackageCreateAppImageVersionTest.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,100 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+import java.io.File;
+import java.nio.file.Files;
+
+/*
+ * @test
+ * @summary jpackage create image --app-version test
+ * @library ../helpers
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @modules jdk.jpackage
+ * @run main/othervm -Xmx512m JPackageCreateAppImageVersionTest
+ */
+public class JPackageCreateAppImageVersionTest {
+ private static final String OUTPUT = "output";
+ private static final String appCfg = JPackagePath.getAppCfg();
+ private static final String VERSION = "2.3";
+ private static final String VERSION_DEFAULT = "1.0";
+
+ private static final String[] CMD = {
+ "--input", "input",
+ "--output", OUTPUT,
+ "--name", "test",
+ "--main-jar", "hello.jar",
+ "--main-class", "Hello",
+ };
+
+ private static final String[] CMD_VERSION = {
+ "--input", "input",
+ "--output", OUTPUT,
+ "--name", "test",
+ "--main-jar", "hello.jar",
+ "--main-class", "Hello",
+ "--app-version", VERSION};
+
+ private static void validate(String version)
+ throws Exception {
+ File outfile = new File(appCfg);
+ if (!outfile.exists()) {
+ throw new AssertionError(appCfg + " was not created");
+ }
+
+ String output = Files.readString(outfile.toPath());
+ if (version == null) {
+ version = VERSION_DEFAULT;
+ }
+
+ String expected = "app.version=" + version;
+ if (!output.contains(expected)) {
+ System.err.println("Expected: " + expected);
+ throw new AssertionError("Cannot find expected entry in config file");
+ }
+ }
+
+ private static void testVersion() throws Exception {
+ JPackageHelper.executeCLI(true, CMD);
+ validate(null);
+ JPackageHelper.deleteOutputFolder(OUTPUT);
+ JPackageHelper.executeCLI(true, CMD_VERSION);
+ validate(VERSION);
+ }
+
+ private static void testVersionToolProvider() throws Exception {
+ JPackageHelper.deleteOutputFolder(OUTPUT);
+ JPackageHelper.executeToolProvider(true, CMD);
+ validate(null);
+ JPackageHelper.deleteOutputFolder(OUTPUT);
+ JPackageHelper.executeToolProvider(true, CMD_VERSION);
+ validate(VERSION);
+ }
+
+ public static void main(String[] args) throws Exception {
+ JPackageHelper.createHelloImageJar();
+ testVersion();
+ testVersionToolProvider();
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createappimage/JPackageCreateAppImageWithSpaceTest.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,68 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+import java.io.File;
+
+ /*
+ * @test
+ * @summary jpackage create image test
+ * @library ../helpers
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageCreateAppImageBase
+ * @modules jdk.jpackage
+ * @run main/othervm -Xmx512m JPackageCreateAppImageWithSpaceTest
+ */
+public class JPackageCreateAppImageWithSpaceTest {
+ private static final String OUTPUT = "output";
+
+ private static final String [] CMD1 = {
+ "--input", "input dir",
+ "--output", OUTPUT,
+ "--name", "test",
+ "--main-jar", "hello.jar",
+ "--main-class", "Hello",
+ };
+
+ private static final String [] CMD2 = {
+ "--input", "input dir2",
+ "--output", OUTPUT,
+ "--name", "test",
+ "--main-jar", "sub dir/hello.jar",
+ "--main-class", "Hello",
+ };
+
+ public static void main(String[] args) throws Exception {
+
+ JPackageHelper.deleteOutputFolder(OUTPUT);
+ JPackageHelper.createHelloImageJar("input dir");
+ JPackageCreateAppImageBase.testCreateAppImage(CMD1);
+
+ JPackageHelper.deleteOutputFolder(OUTPUT);
+ JPackageHelper.createHelloImageJar(
+ "input dir2" + File.separator + "sub dir");
+
+ JPackageCreateAppImageBase.testCreateAppImageToolProvider(CMD2);
+ JPackageHelper.deleteOutputFolder(OUTPUT);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createappimage/macosx/JPackageCreateAppImageBundleIdentifierTest.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,157 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.nio.file.Files;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.xpath.XPath;
+import javax.xml.xpath.XPathConstants;
+import javax.xml.xpath.XPathFactory;
+
+/*
+ * @test
+ * @summary jpackage create image bundle identifier test
+ * @library ../../helpers
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @modules jdk.jpackage
+ * @requires (os.family == "mac")
+ * @run main/othervm -Xmx512m JPackageCreateAppImageBundleIdentifierTest
+ */
+public class JPackageCreateAppImageBundleIdentifierTest {
+ private static final String OUTPUT = "output";
+ private static final String app = JPackagePath.getApp();
+ private static final String appOutput = JPackagePath.getAppOutputFile();
+ private static final String appWorkingDir = JPackagePath.getAppWorkingDir();
+ private static final String MAC_BUNDLE_IDENTIFIER = "TestBundleIdentifier";
+ private static final String APP_NAME = "test";
+ private static final String MAIN_CLASS = "Hello";
+
+ private static final String [] CMD_1 = {
+ "--input", "input",
+ "--output", OUTPUT,
+ "--name", APP_NAME,
+ "--main-jar", "hello.jar",
+ "--main-class", MAIN_CLASS
+ };
+
+ private static final String [] CMD_2 = {
+ "--input", "input",
+ "--output", OUTPUT,
+ "--name", APP_NAME,
+ "--main-jar", "hello.jar",
+ "--main-class", MAIN_CLASS,
+ "--mac-bundle-identifier", MAC_BUNDLE_IDENTIFIER
+ };
+
+ private static void validateResult(String[] result) throws Exception {
+ if (result.length != 2) {
+ throw new AssertionError(
+ "Unexpected number of lines: " + result.length);
+ }
+
+ if (!result[0].trim().equals("jpackage test application")) {
+ throw new AssertionError("Unexpected result[0]: " + result[0]);
+ }
+
+ if (!result[1].trim().equals("args.length: 0")) {
+ throw new AssertionError("Unexpected result[1]: " + result[1]);
+ }
+ }
+
+ private static void validate() throws Exception {
+ int retVal = JPackageHelper.execute(null, app);
+ if (retVal != 0) {
+ throw new AssertionError(
+ "Test application exited with error: " + retVal);
+ }
+
+ File outfile = new File(appWorkingDir + File.separator + appOutput);
+ if (!outfile.exists()) {
+ throw new AssertionError(appOutput + " was not created");
+ }
+
+ String output = Files.readString(outfile.toPath());
+ String[] result = output.split("\n");
+ validateResult(result);
+ }
+
+ private static void validateBundleIdentifier(String bundleIdentifier)
+ throws Exception {
+ System.out.println("Validating bundleIdentifier: " + bundleIdentifier);
+
+ File infoPList = new File(OUTPUT + File.separator + APP_NAME + ".app" +
+ File.separator + "Contents" + File.separator + "Info.plist");
+
+ DocumentBuilderFactory dbf = DocumentBuilderFactory.newDefaultInstance();
+ dbf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
+ DocumentBuilder b = dbf.newDocumentBuilder();
+ org.w3c.dom.Document doc = b.parse(new FileInputStream(
+ infoPList.getAbsolutePath()));
+
+ XPath xPath = XPathFactory.newInstance().newXPath();
+ // Query for the value of <string> element preceding <key> element
+ // with value equal to CFBundleIdentifier
+ String v = (String)xPath.evaluate(
+ "//string[preceding-sibling::key = \"CFBundleIdentifier\"][1]",
+ doc, XPathConstants.STRING);
+
+ if (!v.equals(bundleIdentifier)) {
+ throw new AssertionError("Unexpected value of CFBundleIdentifier key: ["
+ + v + "]. Expected value: [" + bundleIdentifier + "]");
+ }
+ }
+
+ private static void testCreateAppImage(String [] cmd,
+ String bundleIdentifier,
+ boolean validateApp) throws Exception {
+ JPackageHelper.executeCLI(true, cmd);
+ if (validateApp) {
+ validate();
+ }
+ validateBundleIdentifier(bundleIdentifier);
+ }
+
+ private static void testCreateAppImageToolProvider(String [] cmd,
+ String bundleIdentifier,
+ boolean validateApp) throws Exception {
+ JPackageHelper.executeToolProvider(true, cmd);
+ if (validateApp) {
+ validate();
+ }
+ validateBundleIdentifier(bundleIdentifier);
+ }
+
+ public static void main(String[] args) throws Exception {
+ JPackageHelper.createHelloImageJar();
+ testCreateAppImage(CMD_1, MAIN_CLASS, false);
+ JPackageHelper.deleteOutputFolder(OUTPUT);
+ testCreateAppImageToolProvider(CMD_1, MAIN_CLASS, false);
+ JPackageHelper.deleteOutputFolder(OUTPUT);
+ testCreateAppImage(CMD_2, MAC_BUNDLE_IDENTIFIER, true);
+ JPackageHelper.deleteOutputFolder(OUTPUT);
+ testCreateAppImageToolProvider(CMD_2, MAC_BUNDLE_IDENTIFIER, true);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createappimage/macosx/JPackageCreateAppImageBundleNameTest.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,155 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.nio.file.Files;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.xpath.XPath;
+import javax.xml.xpath.XPathConstants;
+import javax.xml.xpath.XPathFactory;
+
+/*
+ * @test
+ * @summary jpackage create image bundle name test
+ * @library ../../helpers
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @modules jdk.jpackage
+ * @requires (os.family == "mac")
+ * @run main/othervm -Xmx512m JPackageCreateAppImageBundleNameTest
+ */
+public class JPackageCreateAppImageBundleNameTest {
+ private static final String OUTPUT = "output";
+ private static final String app = JPackagePath.getApp();
+ private static final String appOutput = JPackagePath.getAppOutputFile();
+ private static final String appWorkingDir = JPackagePath.getAppWorkingDir();
+ private static final String MAC_BUNDLE_NAME = "TestBundleName";
+ private static final String APP_NAME = "test";
+
+ private static final String [] CMD_1 = {
+ "--input", "input",
+ "--output", OUTPUT,
+ "--name", APP_NAME,
+ "--main-jar", "hello.jar",
+ "--main-class", "Hello"
+ };
+
+ private static final String [] CMD_2 = {
+ "--input", "input",
+ "--output", OUTPUT,
+ "--name", APP_NAME,
+ "--main-jar", "hello.jar",
+ "--main-class", "Hello",
+ "--mac-bundle-name", MAC_BUNDLE_NAME
+ };
+
+ private static void validateResult(String[] result) throws Exception {
+ if (result.length != 2) {
+ throw new AssertionError(
+ "Unexpected number of lines: " + result.length);
+ }
+
+ if (!result[0].trim().equals("jpackage test application")) {
+ throw new AssertionError("Unexpected result[0]: " + result[0]);
+ }
+
+ if (!result[1].trim().equals("args.length: 0")) {
+ throw new AssertionError("Unexpected result[1]: " + result[1]);
+ }
+ }
+
+ private static void validate() throws Exception {
+ int retVal = JPackageHelper.execute(null, app);
+ if (retVal != 0) {
+ throw new AssertionError(
+ "Test application exited with error: " + retVal);
+ }
+
+ File outfile = new File(appWorkingDir + File.separator + appOutput);
+ if (!outfile.exists()) {
+ throw new AssertionError(appOutput + " was not created");
+ }
+
+ String output = Files.readString(outfile.toPath());
+ String[] result = output.split("\n");
+ validateResult(result);
+ }
+
+ private static void validateBundleName(String bundleName) throws Exception {
+ System.out.println("Validating bundleName: " + bundleName);
+
+ File infoPList = new File(OUTPUT + File.separator + APP_NAME + ".app" +
+ File.separator + "Contents" + File.separator + "Info.plist");
+
+ DocumentBuilderFactory dbf = DocumentBuilderFactory.newDefaultInstance();
+ dbf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
+ DocumentBuilder b = dbf.newDocumentBuilder();
+ org.w3c.dom.Document doc = b.parse(new FileInputStream(
+ infoPList.getAbsolutePath()));
+
+ XPath xPath = XPathFactory.newInstance().newXPath();
+ // Query for the value of <string> element preceding <key> element
+ // with value equal to CFBundleName
+ String v = (String)xPath.evaluate(
+ "//string[preceding-sibling::key = \"CFBundleName\"][1]",
+ doc, XPathConstants.STRING);
+
+ if (!v.equals(bundleName)) {
+ throw new AssertionError("Unexpected value of CFBundleName key: ["
+ + v + "]. Expected value: [" + bundleName + "]");
+ }
+ }
+
+ private static void testCreateAppImage(String [] cmd,
+ String bundleName,
+ boolean validateApp) throws Exception {
+ JPackageHelper.executeCLI(true, cmd);
+ if (validateApp) {
+ validate();
+ }
+ validateBundleName(bundleName);
+ }
+
+ private static void testCreateAppImageToolProvider(String [] cmd,
+ String bundleName,
+ boolean validateApp) throws Exception {
+ JPackageHelper.executeToolProvider(true, cmd);
+ if (validateApp) {
+ validate();
+ }
+ validateBundleName(bundleName);
+ }
+
+ public static void main(String[] args) throws Exception {
+ JPackageHelper.createHelloImageJar();
+ testCreateAppImage(CMD_1, APP_NAME, false);
+ JPackageHelper.deleteOutputFolder(OUTPUT);
+ testCreateAppImageToolProvider(CMD_1, APP_NAME, false);
+ JPackageHelper.deleteOutputFolder(OUTPUT);
+ testCreateAppImage(CMD_2, MAC_BUNDLE_NAME, true);
+ JPackageHelper.deleteOutputFolder(OUTPUT);
+ testCreateAppImageToolProvider(CMD_2, MAC_BUNDLE_NAME, true);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createappimage/windows/JPackageCreateAppImageWinConsoleTest.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,136 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+import java.io.IOException;
+import java.nio.file.Path;
+import java.io.InputStream;
+import java.io.FileInputStream;
+
+/*
+ * @test
+ * @summary jpackage create image win console test
+ * @library ../../helpers
+ * @library ../
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageCreateAppImageBase
+ * @requires (os.family == "windows")
+ * @modules jdk.jpackage
+ * @run main/othervm -Xmx512m JPackageCreateAppImageWinConsoleTest
+ */
+
+public class JPackageCreateAppImageWinConsoleTest {
+ private static final String NAME = "test";
+ private static final String OUTPUT = "output";
+ private static final String OUTPUT_WIN_CONSOLE = "outputWinConsole";
+ private static final int BUFFER_SIZE = 512;
+ private static final int GUI_SUBSYSTEM = 2;
+ private static final int CONSOLE_SUBSYSTEM = 3;
+
+ private static final String [] CMD = {
+ "--input", "input",
+ "--output", OUTPUT,
+ "--name", NAME,
+ "--main-jar", "hello.jar",
+ "--main-class", "Hello",
+ };
+
+ private static final String [] CMD_WIN_CONSOLE = {
+ "--input", "input",
+ "--output", OUTPUT_WIN_CONSOLE,
+ "--name", NAME,
+ "--main-jar", "hello.jar",
+ "--main-class", "Hello",
+ "--win-console"
+ };
+
+ private static void checkSubsystem(boolean console) throws Exception {
+ Path path = Path.of(console ? OUTPUT_WIN_CONSOLE : OUTPUT,
+ NAME, "bin", NAME + ".exe");
+
+ System.out.println("validate path: " + path.toString());
+ JPackageCreateAppImageBase.validate(path.toString());
+
+ 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");
+ }
+
+ // Check PE header for console or Win GUI app.
+ // https://docs.microsoft.com/en-us/windows/desktop/api/winnt/ns-winnt-_image_nt_headers
+ 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) {
+
+ // 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);
+ } else {
+ return;
+ }
+ } else {
+ if (subsystem != GUI_SUBSYSTEM) {
+ throw new AssertionError("Unexpected subsystem: "
+ + subsystem);
+ } else {
+ return;
+ }
+ }
+ }
+ }
+ }
+
+ throw new AssertionError("Unable to verify PE header");
+ }
+
+ private static void validate() throws Exception {
+ checkSubsystem(false);
+ checkSubsystem(true);
+ }
+
+ private static void testCreateAppImage(String [] cmd) throws Exception {
+ JPackageHelper.executeCLI(true, cmd);
+ }
+
+ private static void testCreateAppImageToolProvider(String [] cmd)
+ throws Exception {
+ JPackageHelper.executeToolProvider(true, cmd);
+ }
+
+ public static void main(String[] args) throws Exception {
+ JPackageHelper.createHelloImageJar();
+ testCreateAppImage(CMD);
+ testCreateAppImage(CMD_WIN_CONSOLE);
+ validate();
+ JPackageHelper.deleteOutputFolder(OUTPUT);
+ JPackageHelper.deleteOutputFolder(OUTPUT_WIN_CONSOLE);
+ testCreateAppImageToolProvider(CMD);
+ testCreateAppImageToolProvider(CMD_WIN_CONSOLE);
+ validate();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/linux/base/JPLinuxAssociationsBase.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,155 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+import java.awt.Desktop;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.PrintWriter;
+import java.nio.file.Files;
+import java.util.ArrayList;
+import java.util.List;
+
+public class JPLinuxAssociationsBase {
+
+ private static String TEST_NAME;
+ private static String EXT;
+ private static String TEST_EXT;
+ private static String OUTPUT;
+ private static String[] CMD;
+
+ private static void copyResults() throws Exception {
+ List<String> files = new ArrayList<>();
+ files.add(OUTPUT.toLowerCase());
+ JPackageInstallerHelper.copyTestResults(files);
+ }
+
+ private static void testCreateInstaller() throws Exception {
+ JPackageHelper.executeCLI(true, CMD);
+ JPackageInstallerHelper.validateOutput(OUTPUT);
+ copyResults();
+ }
+
+ private static void validateAppOutput() throws Exception {
+ File outFile = new File("appOutput.txt");
+ int count = 10;
+ boolean bOutputCreated = false;
+ while (count > 0) {
+ if (!outFile.exists()) {
+ Thread.sleep(3000);
+ count--;
+ } else {
+ bOutputCreated = true;
+ break;
+ }
+ }
+
+ if (!bOutputCreated) {
+ throw new AssertionError(outFile.getAbsolutePath() + " was not created");
+ }
+
+ String output = Files.readString(outFile.toPath());
+ String[] result = output.split("\n");
+ if (result.length != 3) {
+ System.err.println(output);
+ throw new AssertionError(
+ "Unexpected number of lines: " + result.length);
+ }
+
+ if (!result[0].trim().equals("jpackage test application")) {
+ throw new AssertionError("Unexpected result[0]: " + result[0]);
+ }
+
+ if (!result[1].trim().equals("args.length: 1")) {
+ throw new AssertionError("Unexpected result[1]: " + result[1]);
+ }
+
+ File faFile = new File(TEST_NAME + "." + TEST_EXT);
+ if (!result[2].trim().equals(faFile.getAbsolutePath())) {
+ throw new AssertionError("Unexpected result[2]: " + result[2]);
+ }
+ }
+
+ private static void verifyInstall() throws Exception {
+ createAssociationsTestFile();
+ Desktop.getDesktop().open(new File(TEST_NAME + "." + TEST_EXT));
+ validateAppOutput();
+ }
+
+ private static void verifyUnInstall() throws Exception {
+ String folderPath = JPackagePath.getLinuxInstallFolder(TEST_NAME);
+ File folder = new File(folderPath);
+ if (folder.exists()) {
+ throw new AssertionError("Error: " + folder.getAbsolutePath() + " exist");
+ }
+ }
+
+ private static void createAssociationsTestFile() throws Exception {
+ try (PrintWriter out = new PrintWriter(new BufferedWriter(
+ new FileWriter(TEST_NAME + "." + TEST_EXT)))) {
+ out.println(TEST_NAME);
+ }
+ }
+
+ private static void createAssociationsProperties() throws Exception {
+ try (PrintWriter out = new PrintWriter(new BufferedWriter(
+ new FileWriter("fa.properties")))) {
+ out.println("extension=" + TEST_EXT);
+ out.println("mime-type=application/" + TEST_EXT);
+ out.println("description=jpackage test extention");
+ }
+ }
+
+ private static void init(String name, String ext) {
+ TEST_NAME = name;
+ EXT = ext;
+ TEST_EXT = "jptest1";
+ if (EXT.equals("rpm")) {
+ OUTPUT = "output" + File.separator + TEST_NAME + "-1.0-1.x86_64." + EXT;
+ } else {
+ OUTPUT = "output" + File.separator + TEST_NAME + "-1.0." + EXT;
+ }
+ CMD = new String[]{
+ "--package-type", EXT,
+ "--input", "input",
+ "--output", "output",
+ "--name", TEST_NAME,
+ "--main-jar", "hello.jar",
+ "--main-class", "Hello",
+ "--file-associations", "fa.properties"};
+ }
+
+ public static void run(String name, String ext) throws Exception {
+ init(name, ext);
+
+ if (JPackageInstallerHelper.isVerifyInstall()) {
+ verifyInstall();
+ } else if (JPackageInstallerHelper.isVerifyUnInstall()) {
+ verifyUnInstall();
+ } else {
+ JPackageHelper.createHelloInstallerJar();
+ createAssociationsProperties();
+ testCreateInstaller();
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/linux/base/JPLinuxBase.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,89 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+public class JPLinuxBase {
+
+ private static String TEST_NAME;
+ private static String EXT;
+ private static String OUTPUT;
+ private static String[] CMD;
+
+ private static void copyResults() throws Exception {
+ List<String> files = new ArrayList<>();
+ files.add(OUTPUT.toLowerCase());
+ JPackageInstallerHelper.copyTestResults(files);
+ }
+
+ private static void testCreateInstaller() throws Exception {
+ JPackageHelper.executeCLI(true, CMD);
+ JPackageInstallerHelper.validateOutput(OUTPUT);
+ copyResults();
+ }
+
+ private static void verifyInstall() throws Exception {
+ String app = JPackagePath.getLinuxInstalledApp(TEST_NAME);
+ JPackageInstallerHelper.validateApp(app);
+ }
+
+ private static void verifyUnInstall() throws Exception {
+ String folderPath = JPackagePath.getLinuxInstallFolder(TEST_NAME);
+ File folder = new File(folderPath);
+ if (folder.exists()) {
+ throw new AssertionError("Error: " + folder.getAbsolutePath() + " exist");
+ }
+ }
+
+ private static void init(String name, String ext) {
+ TEST_NAME = name;
+ EXT = ext;
+ if (EXT.equals("rpm")) {
+ OUTPUT = "output" + File.separator + TEST_NAME + "-1.0-1.x86_64." + EXT;
+ } else {
+ OUTPUT = "output" + File.separator + TEST_NAME + "-1.0." + EXT;
+ }
+ CMD = new String[]{
+ "--package-type", EXT,
+ "--input", "input",
+ "--output", "output",
+ "--name", TEST_NAME,
+ "--main-jar", "hello.jar",
+ "--main-class", "Hello" };
+ }
+
+ public static void run(String name, String ext) throws Exception {
+ init(name, ext);
+
+ if (JPackageInstallerHelper.isVerifyInstall()) {
+ verifyInstall();
+ } else if (JPackageInstallerHelper.isVerifyUnInstall()) {
+ verifyUnInstall();
+ } else {
+ JPackageHelper.createHelloInstallerJar();
+ testCreateInstaller();
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/linux/base/JPLinuxBundleNameBase.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,92 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+public class JPLinuxBundleNameBase {
+
+ private static String TEST_NAME;
+ private static String BUNDLE_NAME;
+ private static String EXT;
+ private static String OUTPUT;
+ private static String[] CMD;
+
+ private static void copyResults() throws Exception {
+ List<String> files = new ArrayList<>();
+ files.add(OUTPUT.toLowerCase());
+ JPackageInstallerHelper.copyTestResults(files);
+ }
+
+ private static void testCreateInstaller() throws Exception {
+ JPackageHelper.executeCLI(true, CMD);
+ JPackageInstallerHelper.validateOutput(OUTPUT);
+ copyResults();
+ }
+
+ private static void verifyInstall() throws Exception {
+ String app = JPackagePath.getLinuxInstalledApp(TEST_NAME);
+ JPackageInstallerHelper.validateApp(app);
+ }
+
+ private static void verifyUnInstall() throws Exception {
+ String folderPath = JPackagePath.getLinuxInstallFolder(TEST_NAME);
+ File folder = new File(folderPath);
+ if (folder.exists()) {
+ throw new AssertionError("Error: " + folder.getAbsolutePath() + " exist");
+ }
+ }
+
+ private static void init(String name, String ext) {
+ TEST_NAME = name;
+ BUNDLE_NAME = "jpackage-test-bundle-name";
+ EXT = ext;
+ if (EXT.equals("rpm")) {
+ OUTPUT = "output" + File.separator + BUNDLE_NAME + "-1.0-1.x86_64." + EXT;
+ } else {
+ OUTPUT = "output" + File.separator + BUNDLE_NAME + "-1.0." + EXT;
+ }
+ CMD = new String[]{
+ "--package-type", EXT,
+ "--input", "input",
+ "--output", "output",
+ "--name", TEST_NAME,
+ "--main-jar", "hello.jar",
+ "--main-class", "Hello",
+ "--linux-bundle-name", BUNDLE_NAME};
+ }
+
+ public static void run(String name, String ext) throws Exception {
+ init(name, ext);
+
+ if (JPackageInstallerHelper.isVerifyInstall()) {
+ verifyInstall();
+ } else if (JPackageInstallerHelper.isVerifyUnInstall()) {
+ verifyUnInstall();
+ } else {
+ JPackageHelper.createHelloInstallerJar();
+ testCreateInstaller();
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/linux/base/JPLinuxInstallDirBase.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,96 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+public class JPLinuxInstallDirBase {
+
+ private static String TEST_NAME;
+ private static String EXT;
+ private static String OUTPUT;
+ private static String[] CMD;
+
+ private static void copyResults() throws Exception {
+ List<String> files = new ArrayList<>();
+ files.add(OUTPUT.toLowerCase());
+ JPackageInstallerHelper.copyTestResults(files);
+ }
+
+ private static void testCreateInstaller() throws Exception {
+ JPackageHelper.executeCLI(true, CMD);
+ JPackageInstallerHelper.validateOutput(OUTPUT);
+ copyResults();
+ }
+
+ private static void verifyInstall() throws Exception {
+ String app = JPackagePath.getLinuxInstalledApp("jpackage", TEST_NAME);
+ JPackageInstallerHelper.validateApp(app);
+ }
+
+ private static void verifyUnInstall() throws Exception {
+ String folderPath = JPackagePath.getLinuxInstallFolder("jpackage", TEST_NAME);
+ File folder = new File(folderPath);
+ if (folder.exists()) {
+ throw new AssertionError("Error: " + folder.getAbsolutePath() + " exist");
+ }
+
+ folderPath = JPackagePath.getLinuxInstallFolder("jpackage", null);
+ folder = new File(folderPath);
+ if (folder.exists()) {
+ throw new AssertionError("Error: " + folder.getAbsolutePath() + " exist");
+ }
+ }
+
+ private static void init(String name, String ext) {
+ TEST_NAME = name;
+ EXT = ext;
+ if (EXT.equals("rpm")) {
+ OUTPUT = "output" + File.separator + TEST_NAME + "-1.0-1.x86_64." + EXT;
+ } else {
+ OUTPUT = "output" + File.separator + TEST_NAME + "-1.0." + EXT;
+ }
+ CMD = new String[]{
+ "--package-type", EXT,
+ "--input", "input",
+ "--output", "output",
+ "--name", TEST_NAME,
+ "--main-jar", "hello.jar",
+ "--main-class", "Hello",
+ "--install-dir", "/opt/jpackage"};
+ }
+
+ public static void run(String name, String ext) throws Exception {
+ init(name, ext);
+
+ if (JPackageInstallerHelper.isVerifyInstall()) {
+ verifyInstall();
+ } else if (JPackageInstallerHelper.isVerifyUnInstall()) {
+ verifyUnInstall();
+ } else {
+ JPackageHelper.createHelloInstallerJar();
+ testCreateInstaller();
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/linux/base/JPLinuxLicenseBase.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,91 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+public class JPLinuxLicenseBase {
+
+ private static String TEST_NAME;
+ private static String EXT;
+ private static String OUTPUT;
+ private static String[] CMD;
+
+ private static void copyResults() throws Exception {
+ List<String> files = new ArrayList<>();
+ files.add(OUTPUT.toLowerCase());
+ JPackageInstallerHelper.copyTestResults(files);
+ }
+
+ private static void testCreateInstaller() throws Exception {
+ JPackageHelper.executeCLI(true, CMD);
+ JPackageInstallerHelper.validateOutput(OUTPUT);
+ copyResults();
+ }
+
+ private static void verifyInstall() throws Exception {
+ String app = JPackagePath.getLinuxInstalledApp(TEST_NAME);
+ JPackageInstallerHelper.validateApp(app);
+
+ }
+
+ private static void verifyUnInstall() throws Exception {
+ String folderPath = JPackagePath.getLinuxInstallFolder(TEST_NAME);
+ File folder = new File(folderPath);
+ if (folder.exists()) {
+ throw new AssertionError("Error: " + folder.getAbsolutePath() + " exist");
+ }
+ }
+
+ private static void init(String name, String ext) {
+ TEST_NAME = name;
+ EXT = ext;
+ if (EXT.equals("rpm")) {
+ OUTPUT = "output" + File.separator + TEST_NAME + "-1.0-1.x86_64." + EXT;
+ } else {
+ OUTPUT = "output" + File.separator + TEST_NAME + "-1.0." + EXT;
+ }
+ CMD = new String [] {
+ "--package-type", EXT,
+ "--input", "input",
+ "--output", "output",
+ "--name", TEST_NAME,
+ "--main-jar", "hello.jar",
+ "--main-class", "Hello",
+ "--license-file", JPackagePath.getLicenseFilePath()};
+ }
+
+ public static void run(String name, String ext) throws Exception {
+ init(name, ext);
+
+ if (JPackageInstallerHelper.isVerifyInstall()) {
+ verifyInstall();
+ } else if (JPackageInstallerHelper.isVerifyUnInstall()) {
+ verifyUnInstall();
+ } else {
+ JPackageHelper.createHelloInstallerJar();
+ testCreateInstaller();
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/linux/base/JPLinuxLicenseTypeBase.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,113 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+import java.io.File;
+import java.nio.file.Files;
+import java.util.ArrayList;
+import java.util.List;
+
+public class JPLinuxLicenseTypeBase {
+
+ private static String TEST_NAME;
+ private static String EXT;
+ private static String JP_LICENSE_TYPE;
+ private static String OUTPUT;
+ private static String[] CMD;
+
+ private static void copyResults() throws Exception {
+ List<String> files = new ArrayList<>();
+ files.add(OUTPUT.toLowerCase());
+ JPackageInstallerHelper.copyTestResults(files);
+ }
+
+ private static final String infoResult = "infoResult.txt";
+ private static void validatePackage() throws Exception {
+ int retVal = JPackageHelper.execute(new File(infoResult),"rpm",
+ "--query", "--package", "--info", OUTPUT.toLowerCase());
+ if (retVal != 0) {
+ throw new AssertionError("rpm exited with error: " + retVal);
+ }
+
+ File outfile = new File(infoResult);
+ if (!outfile.exists()) {
+ throw new AssertionError(infoResult + " was not created");
+ }
+
+ String output = Files.readString(outfile.toPath());
+ if (!output.contains(JP_LICENSE_TYPE)) {
+ throw new AssertionError("Unexpected result: " + output);
+ }
+ }
+
+ private static void testCreateInstaller() throws Exception {
+ JPackageHelper.executeCLI(true, CMD);
+ JPackageInstallerHelper.validateOutput(OUTPUT);
+ validatePackage();
+ copyResults();
+ }
+
+ private static void verifyInstall() throws Exception {
+ String app = JPackagePath.getLinuxInstalledApp(TEST_NAME);
+ JPackageInstallerHelper.validateApp(app);
+ }
+
+ private static void verifyUnInstall() throws Exception {
+ String folderPath = JPackagePath.getLinuxInstallFolder(TEST_NAME);
+ File folder = new File(folderPath);
+ if (folder.exists()) {
+ throw new AssertionError("Error: " + folder.getAbsolutePath() + " exist");
+ }
+ }
+
+ private static void init(String name, String ext) {
+ TEST_NAME = name;
+ EXT = ext;
+ JP_LICENSE_TYPE = "JP_LICENSE_TYPE";
+ if (EXT.equals("rpm")) {
+ OUTPUT = "output" + File.separator + TEST_NAME + "-1.0-1.x86_64." + EXT;
+ } else {
+ OUTPUT = "output" + File.separator + TEST_NAME + "-1.0." + EXT;
+ }
+ CMD = new String[]{
+ "--package-type", EXT,
+ "--input", "input",
+ "--output", "output",
+ "--name", TEST_NAME,
+ "--main-jar", "hello.jar",
+ "--main-class", "Hello",
+ "--linux-rpm-license-type", JP_LICENSE_TYPE};
+ }
+
+ public static void run(String name, String ext) throws Exception {
+ init(name, ext);
+
+ if (JPackageInstallerHelper.isVerifyInstall()) {
+ verifyInstall();
+ } else if (JPackageInstallerHelper.isVerifyUnInstall()) {
+ verifyUnInstall();
+ } else {
+ JPackageHelper.createHelloInstallerJar();
+ testCreateInstaller();
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/linux/base/JPLinuxMaintainerBase.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,109 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+import java.io.File;
+import java.nio.file.Files;
+import java.util.ArrayList;
+import java.util.List;
+
+public class JPLinuxMaintainerBase {
+
+ private static String TEST_NAME;
+ private static String EMAIL;
+ private static String EXT;
+ private static String OUTPUT;
+ private static String[] CMD;
+
+ private static void copyResults() throws Exception {
+ List<String> files = new ArrayList<>();
+ files.add(OUTPUT.toLowerCase());
+ JPackageInstallerHelper.copyTestResults(files);
+ }
+
+ private static final String infoResult = "infoResult.txt";
+ private static void validatePackage() throws Exception {
+ int retVal = JPackageHelper.execute(new File(infoResult), "dpkg",
+ "--info", OUTPUT.toLowerCase());
+ if (retVal != 0) {
+ throw new AssertionError("dpkg exited with error: " + retVal);
+ }
+
+ File outfile = new File(infoResult);
+ if (!outfile.exists()) {
+ throw new AssertionError(infoResult + " was not created");
+ }
+
+ String output = Files.readString(outfile.toPath());
+ if (!output.contains("Maintainer: " + EMAIL)) {
+ throw new AssertionError("Unexpected result: " + output);
+ }
+ }
+
+ private static void testCreateInstaller() throws Exception {
+ JPackageHelper.executeCLI(true, CMD);
+ JPackageInstallerHelper.validateOutput(OUTPUT);
+ validatePackage();
+ copyResults();
+ }
+
+ private static void verifyInstall() throws Exception {
+ String app = JPackagePath.getLinuxInstalledApp(TEST_NAME);
+ JPackageInstallerHelper.validateApp(app);
+ }
+
+ private static void verifyUnInstall() throws Exception {
+ String folderPath = JPackagePath.getLinuxInstallFolder(TEST_NAME);
+ File folder = new File(folderPath);
+ if (folder.exists()) {
+ throw new AssertionError("Error: " + folder.getAbsolutePath() + " exist");
+ }
+ }
+
+ private static void init(String name, String ext) {
+ TEST_NAME = name;
+ EMAIL = "jpackage-test@java.com";
+ EXT = ext;
+ OUTPUT = "output" + File.separator + TEST_NAME + "-1.0." + EXT;
+ CMD = new String[]{
+ "--package-type", EXT,
+ "--input", "input",
+ "--output", "output",
+ "--name", TEST_NAME,
+ "--main-jar", "hello.jar",
+ "--main-class", "Hello",
+ "--linux-deb-maintainer", EMAIL};
+ }
+
+ public static void run(String name, String ext) throws Exception {
+ init(name, ext);
+
+ if (JPackageInstallerHelper.isVerifyInstall()) {
+ verifyInstall();
+ } else if (JPackageInstallerHelper.isVerifyUnInstall()) {
+ verifyUnInstall();
+ } else {
+ JPackageHelper.createHelloInstallerJar();
+ testCreateInstaller();
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/linux/base/JPLinuxPackageDepsBase.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,144 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+import java.io.File;
+import java.nio.file.Files;
+import java.util.ArrayList;
+import java.util.List;
+
+public class JPLinuxPackageDepsBase {
+
+ private static String TEST_NAME;
+ private static String DEP_NAME;
+ private static String EXT;
+ private static String OUTPUT;
+ private static String OUTPUT_DEP;
+ private static String[] CMD;
+ private static String[] CMD_DEP;
+
+ private static void copyResults() throws Exception {
+ List<String> files = new ArrayList<>();
+ files.add(OUTPUT.toLowerCase());
+ files.add(OUTPUT_DEP.toLowerCase());
+ JPackageInstallerHelper.copyTestResults(files);
+ }
+
+ private static final String infoResult = "infoResult.txt";
+ private static void validatePackage() throws Exception {
+ if (EXT.equals("rpm")) {
+ int retVal = JPackageHelper.execute(new File(infoResult),"rpm",
+ "--query", "--package", "--requires", OUTPUT.toLowerCase());
+ if (retVal != 0) {
+ throw new AssertionError("rpm exited with error: " + retVal);
+ }
+ } else {
+ int retVal = JPackageHelper.execute(new File(infoResult), "dpkg",
+ "--info", OUTPUT.toLowerCase());
+ if (retVal != 0) {
+ throw new AssertionError("dpkg exited with error: " + retVal);
+ }
+ }
+
+ File outfile = new File(infoResult);
+ if (!outfile.exists()) {
+ throw new AssertionError(infoResult + " was not created");
+ }
+
+ String output = Files.readString(outfile.toPath());
+ if (!output.contains(DEP_NAME.toLowerCase())) {
+ throw new AssertionError("Unexpected result: " + output);
+ }
+ }
+
+ private static void testCreateInstaller() throws Exception {
+ JPackageHelper.executeCLI(true, CMD);
+ JPackageHelper.executeCLI(true, CMD_DEP);
+ JPackageInstallerHelper.validateOutput(OUTPUT);
+ JPackageInstallerHelper.validateOutput(OUTPUT_DEP);
+ validatePackage();
+ copyResults();
+ }
+
+ private static void verifyInstall() throws Exception {
+ String app = JPackagePath.getLinuxInstalledApp(TEST_NAME);
+ JPackageInstallerHelper.validateApp(app);
+
+ app = JPackagePath.getLinuxInstalledApp(DEP_NAME);
+ JPackageInstallerHelper.validateApp(app);
+ }
+
+ private static void verifyUnInstall() throws Exception {
+ String folderPath = JPackagePath.getLinuxInstallFolder(TEST_NAME);
+ File folder = new File(folderPath);
+ if (folder.exists()) {
+ throw new AssertionError("Error: " + folder.getAbsolutePath() + " exist");
+ }
+
+ folderPath = JPackagePath.getLinuxInstallFolder(DEP_NAME);
+ folder = new File(folderPath);
+ if (folder.exists()) {
+ throw new AssertionError("Error: " + folder.getAbsolutePath() + " exist");
+ }
+ }
+
+ private static void init(String name, String ext) {
+ TEST_NAME = name;
+ DEP_NAME = name + "Dep";
+ EXT = ext;
+ if (EXT.equals("rpm")) {
+ OUTPUT = "output" + File.separator + TEST_NAME + "-1.0-1.x86_64." + EXT;
+ OUTPUT_DEP = "output" + File.separator + DEP_NAME + "-1.0-1.x86_64." + EXT;
+ } else {
+ OUTPUT = "output" + File.separator + TEST_NAME + "-1.0." + EXT;
+ OUTPUT_DEP = "output" + File.separator + DEP_NAME + "-1.0." + EXT;
+ }
+ CMD = new String[]{
+ "--package-type", EXT,
+ "--input", "input",
+ "--output", "output",
+ "--name", TEST_NAME,
+ "--main-jar", "hello.jar",
+ "--main-class", "Hello",
+ "--linux-package-deps", DEP_NAME.toLowerCase()};
+ CMD_DEP = new String[]{
+ "--package-type", EXT,
+ "--input", "input",
+ "--output", "output",
+ "--name", DEP_NAME,
+ "--main-jar", "hello.jar",
+ "--main-class", "Hello"};
+ }
+
+ public static void run(String name, String ext) throws Exception {
+ init(name, ext);
+
+ if (JPackageInstallerHelper.isVerifyInstall()) {
+ verifyInstall();
+ } else if (JPackageInstallerHelper.isVerifyUnInstall()) {
+ verifyUnInstall();
+ } else {
+ JPackageHelper.createHelloInstallerJar();
+ testCreateInstaller();
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/linux/deb/JPLinuxDebAssociationsTest.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,47 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+/*
+ * @test
+ * @summary jpackage create installer test
+ * @library ../../../helpers
+ * @library ../base
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageInstallerHelper
+ * @build JPLinuxAssociationsBase
+ * @requires (os.family == "linux")
+ * @modules jdk.jpackage
+ * @modules jdk.jpackage/jdk.jpackage.internal
+ * @run main/othervm -Xmx512m JPLinuxDebAssociationsTest
+ */
+public class JPLinuxDebAssociationsTest {
+ private static final String TEST_NAME = "JPLinuxDebAssociationsTest";
+ private static final String EXT = "deb";
+
+ public static void main(String[] args) throws Exception {
+ if (jdk.jpackage.internal.LinuxDebBundler.isSupported()) {
+ JPLinuxAssociationsBase.run(TEST_NAME, EXT);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/linux/deb/JPLinuxDebBundleNameTest.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,47 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+/*
+ * @test
+ * @summary jpackage create installer test
+ * @library ../../../helpers
+ * @library ../base
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageInstallerHelper
+ * @build JPLinuxBundleNameBase
+ * @requires (os.family == "linux")
+ * @modules jdk.jpackage
+ * @modules jdk.jpackage/jdk.jpackage.internal
+ * @run main/othervm -Xmx512m JPLinuxDebBundleNameTest
+ */
+public class JPLinuxDebBundleNameTest {
+ private static final String TEST_NAME = "JPLinuxDebBundleNameTest";
+ private static final String EXT = "deb";
+
+ public static void main(String[] args) throws Exception {
+ if (jdk.jpackage.internal.LinuxDebBundler.isSupported()) {
+ JPLinuxBundleNameBase.run(TEST_NAME, EXT);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/linux/deb/JPLinuxDebInstallDirTest.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,47 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+/*
+ * @test
+ * @summary jpackage create installer test
+ * @library ../../../helpers
+ * @library ../base
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageInstallerHelper
+ * @build JPLinuxInstallDirBase
+ * @requires (os.family == "linux")
+ * @modules jdk.jpackage
+ * @modules jdk.jpackage/jdk.jpackage.internal
+ * @run main/othervm -Xmx512m JPLinuxDebInstallDirTest
+ */
+public class JPLinuxDebInstallDirTest {
+ private static final String TEST_NAME = "JPLinuxDebInstallDirTest";
+ private static final String EXT = "deb";
+
+ public static void main(String[] args) throws Exception {
+ if (jdk.jpackage.internal.LinuxDebBundler.isSupported()) {
+ JPLinuxInstallDirBase.run(TEST_NAME, EXT);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/linux/deb/JPLinuxDebLicenseTest.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,47 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+/*
+ * @test
+ * @summary jpackage create installer test
+ * @library ../../../helpers
+ * @library ../base
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageInstallerHelper
+ * @build JPLinuxLicenseBase
+ * @requires (os.family == "linux")
+ * @modules jdk.jpackage
+ * @modules jdk.jpackage/jdk.jpackage.internal
+ * @run main/othervm -Xmx512m JPLinuxDebLicenseTest
+ */
+public class JPLinuxDebLicenseTest {
+ private static final String TEST_NAME = "JPLinuxDebLicenseTest";
+ private static final String EXT = "deb";
+
+ public static void main(String[] args) throws Exception {
+ if (jdk.jpackage.internal.LinuxDebBundler.isSupported()) {
+ JPLinuxLicenseBase.run(TEST_NAME, EXT);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/linux/deb/JPLinuxDebMaintainerTest.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,47 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+/*
+ * @test
+ * @summary jpackage create installer test
+ * @library ../../../helpers
+ * @library ../base
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageInstallerHelper
+ * @build JPLinuxMaintainerBase
+ * @requires (os.family == "linux")
+ * @modules jdk.jpackage
+ * @modules jdk.jpackage/jdk.jpackage.internal
+ * @run main/othervm -Xmx512m JPLinuxDebMaintainerTest
+ */
+public class JPLinuxDebMaintainerTest {
+ private static final String TEST_NAME = "JPLinuxDebMaintainerTest";
+ private static final String EXT = "deb";
+
+ public static void main(String[] args) throws Exception {
+ if (jdk.jpackage.internal.LinuxDebBundler.isSupported()) {
+ JPLinuxMaintainerBase.run(TEST_NAME, EXT);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/linux/deb/JPLinuxDebPackageDepsTest.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,47 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+/*
+ * @test
+ * @summary jpackage create installer test
+ * @library ../../../helpers
+ * @library ../base
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageInstallerHelper
+ * @build JPLinuxPackageDepsBase
+ * @requires (os.family == "linux")
+ * @modules jdk.jpackage
+ * @modules jdk.jpackage/jdk.jpackage.internal
+ * @run main/othervm/timeout=240 -Xmx512m JPLinuxDebPackageDepsTest
+ */
+public class JPLinuxDebPackageDepsTest {
+ private static final String TEST_NAME = "JPLinuxDebPackageDepsTest";
+ private static final String EXT = "deb";
+
+ public static void main(String[] args) throws Exception {
+ if (jdk.jpackage.internal.LinuxDebBundler.isSupported()) {
+ JPLinuxPackageDepsBase.run(TEST_NAME, EXT);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/linux/deb/JPLinuxDebTest.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,47 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+/*
+ * @test
+ * @summary jpackage create installer test
+ * @library ../../../helpers
+ * @library ../base
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageInstallerHelper
+ * @build JPLinuxBase
+ * @requires (os.family == "linux")
+ * @modules jdk.jpackage
+ * @modules jdk.jpackage/jdk.jpackage.internal
+ * @run main/othervm -Xmx512m JPLinuxDebTest
+ */
+public class JPLinuxDebTest {
+ private static final String TEST_NAME = "JPLinuxDebTest";
+ private static final String EXT = "deb";
+
+ public static void main(String[] args) throws Exception {
+ if (jdk.jpackage.internal.LinuxDebBundler.isSupported()) {
+ JPLinuxBase.run(TEST_NAME, EXT);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/linux/deb/install.sh Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,8 @@
+sudo dpkg -i jplinuxdebtest-1.0.deb
+sudo dpkg -i jplinuxdebassociationstest-1.0.deb
+sudo dpkg -i jplinuxdeblicensetest-1.0.deb
+sudo dpkg -i jplinuxdebinstalldirtest-1.0.deb
+sudo dpkg -i jpackage-test-bundle-name-1.0.deb
+sudo dpkg -i jplinuxdebmaintainertest-1.0.deb
+sudo dpkg -i jplinuxdebpackagedepstestdep-1.0.deb
+sudo dpkg -i jplinuxdebpackagedepstest-1.0.deb
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/linux/deb/uninstall.sh Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,8 @@
+sudo dpkg -r jplinuxdebtest
+sudo dpkg -r jplinuxdebassociationstest
+sudo dpkg -r jplinuxdeblicensetest
+sudo dpkg -r jplinuxdebinstalldirtest
+sudo dpkg -r jpackage-test-bundle-name
+sudo dpkg -r jplinuxdebmaintainertest
+sudo dpkg -r jplinuxdebpackagedepstest
+sudo dpkg -r jplinuxdebpackagedepstestdep
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/linux/rpm/JPLinuxRpmAssociationsTest.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+/*
+ * @test
+ * @summary jpackage create installer test
+ * @library ../../../helpers
+ * @library ../base
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageInstallerHelper
+ * @build JPLinuxAssociationsBase
+ * @requires (os.family == "linux")
+ * @modules jdk.jpackage
+ * @run main/othervm -Xmx512m JPLinuxRpmAssociationsTest
+ */
+public class JPLinuxRpmAssociationsTest {
+ private static final String TEST_NAME = "JPLinuxRpmAssociationsTest";
+ private static final String EXT = "rpm";
+
+ public static void main(String[] args) throws Exception {
+ JPLinuxAssociationsBase.run(TEST_NAME, EXT);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/linux/rpm/JPLinuxRpmBundleNameTest.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+/*
+ * @test
+ * @summary jpackage create installer test
+ * @library ../../../helpers
+ * @library ../base
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageInstallerHelper
+ * @build JPLinuxBundleNameBase
+ * @requires (os.family == "linux")
+ * @modules jdk.jpackage
+ * @run main/othervm -Xmx512m JPLinuxRpmBundleNameTest
+ */
+public class JPLinuxRpmBundleNameTest {
+ private static final String TEST_NAME = "JPLinuxRpmBundleNameTest";
+ private static final String EXT = "rpm";
+
+ public static void main(String[] args) throws Exception {
+ JPLinuxBundleNameBase.run(TEST_NAME, EXT);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/linux/rpm/JPLinuxRpmInstallDirTest.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+/*
+ * @test
+ * @summary jpackage create installer test
+ * @library ../../../helpers
+ * @library ../base
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageInstallerHelper
+ * @build JPLinuxInstallDirBase
+ * @requires (os.family == "linux")
+ * @modules jdk.jpackage
+ * @run main/othervm -Xmx512m JPLinuxRpmInstallDirTest
+ */
+public class JPLinuxRpmInstallDirTest {
+ private static final String TEST_NAME = "JPLinuxRpmInstallDirTest";
+ private static final String EXT = "rpm";
+
+ public static void main(String[] args) throws Exception {
+ JPLinuxInstallDirBase.run(TEST_NAME, EXT);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/linux/rpm/JPLinuxRpmLicenseTest.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+/*
+ * @test
+ * @summary jpackage create installer test
+ * @library ../../../helpers
+ * @library ../base
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageInstallerHelper
+ * @build JPLinuxLicenseBase
+ * @requires (os.family == "linux")
+ * @modules jdk.jpackage
+ * @run main/othervm -Xmx512m JPLinuxRpmLicenseTest
+ */
+public class JPLinuxRpmLicenseTest {
+ private static final String TEST_NAME = "JPLinuxRpmLicenseTest";
+ private static final String EXT = "rpm";
+
+ public static void main(String[] args) throws Exception {
+ JPLinuxLicenseBase.run(TEST_NAME, EXT);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/linux/rpm/JPLinuxRpmLicenseTypeTest.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+/*
+ * @test
+ * @summary jpackage create installer test
+ * @library ../../../helpers
+ * @library ../base
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageInstallerHelper
+ * @build JPLinuxLicenseTypeBase
+ * @requires (os.family == "linux")
+ * @modules jdk.jpackage
+ * @run main/othervm -Xmx512m JPLinuxRpmLicenseTypeTest
+ */
+public class JPLinuxRpmLicenseTypeTest {
+ private static final String TEST_NAME = "JPLinuxRpmLicenseTypeTest";
+ private static final String EXT = "rpm";
+
+ public static void main(String[] args) throws Exception {
+ JPLinuxLicenseTypeBase.run(TEST_NAME, EXT);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/linux/rpm/JPLinuxRpmPackageDepsTest.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+/*
+ * @test
+ * @summary jpackage create installer test
+ * @library ../../../helpers
+ * @library ../base
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageInstallerHelper
+ * @build JPLinuxPackageDepsBase
+ * @requires (os.family == "linux")
+ * @modules jdk.jpackage
+ * @run main/othervm/timeout=240 -Xmx512m JPLinuxRpmPackageDepsTest
+ */
+public class JPLinuxRpmPackageDepsTest {
+ private static final String TEST_NAME = "JPLinuxRpmPackageDepsTest";
+ private static final String EXT = "rpm";
+
+ public static void main(String[] args) throws Exception {
+ JPLinuxPackageDepsBase.run(TEST_NAME, EXT);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/linux/rpm/JPLinuxRpmTest.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+/*
+ * @test
+ * @summary jpackage create installer test
+ * @library ../../../helpers
+ * @library ../base
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageInstallerHelper
+ * @build JPLinuxBase
+ * @requires (os.family == "linux")
+ * @modules jdk.jpackage
+ * @run main/othervm -Xmx512m JPLinuxRpmTest
+ */
+public class JPLinuxRpmTest {
+ private static final String TEST_NAME = "JPLinuxRpmTest";
+ private static final String EXT = "rpm";
+
+ public static void main(String[] args) throws Exception {
+ JPLinuxBase.run(TEST_NAME, EXT);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/linux/rpm/install.sh Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,8 @@
+sudo rpm --install jplinuxrpmassociationstest-1.0-1.x86_64.rpm
+sudo rpm --install jplinuxrpminstalldirtest-1.0-1.x86_64.rpm
+sudo rpm --install jplinuxrpmlicensetest-1.0-1.x86_64.rpm
+sudo rpm --install jplinuxrpmlicensetypetest-1.0-1.x86_64.rpm
+sudo rpm --install jplinuxrpmpackagedepstestdep-1.0-1.x86_64.rpm
+sudo rpm --install jplinuxrpmpackagedepstest-1.0-1.x86_64.rpm
+sudo rpm --install jplinuxrpmtest-1.0-1.x86_64.rpm
+sudo rpm --install jpackage-test-bundle-name-1.0-1.x86_64.rpm
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/linux/rpm/uninstall.sh Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,8 @@
+sudo rpm -e jplinuxrpmassociationstest
+sudo rpm -e jplinuxrpminstalldirtest
+sudo rpm -e jplinuxrpmlicensetest
+sudo rpm -e jplinuxrpmlicensetypetest
+sudo rpm -e jplinuxrpmpackagedepstest
+sudo rpm -e jplinuxrpmpackagedepstestdep
+sudo rpm -e jplinuxrpmtest
+sudo rpm -e jpackage-test-bundle-name
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/macosx/base/JPMacAssociationsBase.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,149 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+import java.awt.Desktop;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.PrintWriter;
+import java.nio.file.Files;
+import java.util.ArrayList;
+import java.util.List;
+
+public class JPMacAssociationsBase {
+
+ private static String TEST_NAME;
+ private static String EXT;
+ private static String TEST_EXT;
+ private static String OUTPUT;
+ private static String[] CMD;
+
+ private static void copyResults() throws Exception {
+ List<String> files = new ArrayList<>();
+ files.add(OUTPUT);
+ JPackageInstallerHelper.copyTestResults(files);
+ }
+
+ private static void testCreateInstaller() throws Exception {
+ JPackageHelper.executeCLI(true, CMD);
+ JPackageInstallerHelper.validateOutput(OUTPUT);
+ copyResults();
+ }
+
+ private static void validateAppOutput() throws Exception {
+ File outFile = new File("appOutput.txt");
+ int count = 10;
+ boolean bOutputCreated = false;
+ while (count > 0) {
+ if (!outFile.exists()) {
+ Thread.sleep(3000);
+ count--;
+ } else {
+ bOutputCreated = true;
+ break;
+ }
+ }
+
+ if (!bOutputCreated) {
+ throw new AssertionError(outFile.getAbsolutePath() + " was not created");
+ }
+
+ String output = Files.readString(outFile.toPath());
+ String[] result = output.split("\n");
+ if (result.length != 3) {
+ System.err.println(output);
+ throw new AssertionError(
+ "Unexpected number of lines: " + result.length);
+ }
+
+ if (!result[0].trim().equals("jpackage test application")) {
+ throw new AssertionError("Unexpected result[0]: " + result[0]);
+ }
+
+ if (!result[1].trim().equals("args.length: 1")) {
+ throw new AssertionError("Unexpected result[1]: " + result[1]);
+ }
+
+ File faFile = new File(TEST_NAME + "." + TEST_EXT);
+ if (!result[2].trim().equals(faFile.getAbsolutePath())) {
+ throw new AssertionError("Unexpected result[2]: " + result[2]);
+ }
+ }
+
+ private static void verifyInstall() throws Exception {
+ createAssociationsTestFile();
+ Desktop.getDesktop().open(new File(TEST_NAME + "." + TEST_EXT));
+ validateAppOutput();
+ }
+
+ private static void verifyUnInstall() throws Exception {
+ // Not needed on OS X, since we just deleting installed application
+ // without using generated installer. We keeping this for consistnency
+ // between platforms.
+ }
+
+ private static void createAssociationsTestFile() throws Exception {
+ try (PrintWriter out = new PrintWriter(new BufferedWriter(
+ new FileWriter(TEST_NAME + "." + TEST_EXT)))) {
+ out.println(TEST_NAME);
+ }
+ }
+
+ private static void createAssociationsProperties() throws Exception {
+ try (PrintWriter out = new PrintWriter(new BufferedWriter(
+ new FileWriter("fa.properties")))) {
+ out.println("extension=" + TEST_EXT);
+ out.println("mime-type=application/" + TEST_EXT);
+ out.println("description=jpackage test extention");
+ }
+ }
+
+ private static void init(String name, String ext) {
+ TEST_NAME = name;
+ EXT = ext;
+ TEST_EXT = "jptest1";
+ OUTPUT = "output" + File.separator + TEST_NAME + "-1.0." + EXT;
+ CMD = new String[]{
+ "--package-type", EXT,
+ "--input", "input",
+ "--output", "output",
+ "--name", TEST_NAME,
+ "--main-jar", "hello.jar",
+ "--main-class", "Hello",
+ "--file-associations", "fa.properties"};
+ }
+
+ public static void run(String name, String ext) throws Exception {
+ init(name, ext);
+
+ if (JPackageInstallerHelper.isVerifyInstall()) {
+ verifyInstall();
+ } else if (JPackageInstallerHelper.isVerifyUnInstall()) {
+ verifyUnInstall();
+ } else {
+ JPackageHelper.createHelloInstallerJar();
+ createAssociationsProperties();
+ testCreateInstaller();
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/macosx/base/JPMacBase.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,83 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+public class JPMacBase {
+
+ private static String TEST_NAME;
+ private static String EXT;
+ private static String OUTPUT;
+ private static String[] CMD;
+
+ private static void copyResults() throws Exception {
+ List<String> files = new ArrayList<>();
+ files.add(OUTPUT);
+ JPackageInstallerHelper.copyTestResults(files);
+ }
+
+ private static void testCreateInstaller() throws Exception {
+ JPackageHelper.executeCLI(true, CMD);
+ JPackageInstallerHelper.validateOutput(OUTPUT);
+ copyResults();
+ }
+
+ private static void verifyInstall() throws Exception {
+ String app = JPackagePath.getOSXInstalledApp(TEST_NAME);
+ JPackageInstallerHelper.validateApp(app);
+ }
+
+ private static void verifyUnInstall() throws Exception {
+ // Not needed on OS X, since we just deleting installed application
+ // without using generated installer. We keeping this for consistnency
+ // between platforms.
+ }
+
+ private static void init(String name, String ext) {
+ TEST_NAME = name;
+ EXT = ext;
+ OUTPUT = "output" + File.separator + TEST_NAME + "-1.0." + EXT;
+ CMD = new String[]{
+ "--package-type", EXT,
+ "--input", "input",
+ "--output", "output",
+ "--name", TEST_NAME,
+ "--main-jar", "hello.jar",
+ "--main-class", "Hello"};
+ }
+
+ public static void run(String name, String ext) throws Exception {
+ init(name, ext);
+
+ if (JPackageInstallerHelper.isVerifyInstall()) {
+ verifyInstall();
+ } else if (JPackageInstallerHelper.isVerifyUnInstall()) {
+ verifyUnInstall();
+ } else {
+ JPackageHelper.createHelloInstallerJar();
+ testCreateInstaller();
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/macosx/base/JPMacInstallDirBase.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,84 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+public class JPMacInstallDirBase {
+
+ private static String TEST_NAME;
+ private static String EXT;
+ private static String OUTPUT;
+ private static String[] CMD;
+
+ private static void copyResults() throws Exception {
+ List<String> files = new ArrayList<>();
+ files.add(OUTPUT);
+ JPackageInstallerHelper.copyTestResults(files);
+ }
+
+ private static void testCreateInstaller() throws Exception {
+ JPackageHelper.executeCLI(true, CMD);
+ JPackageInstallerHelper.validateOutput(OUTPUT);
+ copyResults();
+ }
+
+ private static void verifyInstall() throws Exception {
+ String app = JPackagePath.getOSXInstalledApp("jpackage", TEST_NAME);
+ JPackageInstallerHelper.validateApp(app);
+ }
+
+ private static void verifyUnInstall() throws Exception {
+ // Not needed on OS X, since we just deleting installed application
+ // without using generated installer. We keeping this for consistnency
+ // between platforms.
+ }
+
+ private static void init(String name, String ext) {
+ TEST_NAME = name;
+ EXT = ext;
+ OUTPUT = "output" + File.separator + TEST_NAME + "-1.0." + EXT;
+ CMD = new String[]{
+ "--package-type", EXT,
+ "--input", "input",
+ "--output", "output",
+ "--name", TEST_NAME,
+ "--main-jar", "hello.jar",
+ "--main-class", "Hello",
+ "--install-dir", "/Applications/jpackage"};
+ }
+
+ public static void run(String name, String ext) throws Exception {
+ init(name, ext);
+
+ if (JPackageInstallerHelper.isVerifyInstall()) {
+ verifyInstall();
+ } else if (JPackageInstallerHelper.isVerifyUnInstall()) {
+ verifyUnInstall();
+ } else {
+ JPackageHelper.createHelloInstallerJar();
+ testCreateInstaller();
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/macosx/base/JPMacLicenseBase.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,85 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+public class JPMacLicenseBase {
+
+ private static String TEST_NAME;
+ private static String EXT;
+ private static String OUTPUT;
+ private static String[] CMD;
+
+ private static void copyResults() throws Exception {
+ List<String> files = new ArrayList<>();
+ files.add(OUTPUT);
+ JPackageInstallerHelper.copyTestResults(files);
+ }
+
+ private static void testCreateInstaller() throws Exception {
+ JPackageHelper.executeCLI(true, CMD);
+ JPackageInstallerHelper.validateOutput(OUTPUT);
+ copyResults();
+ }
+
+ private static void verifyInstall() throws Exception {
+ String app = JPackagePath.getOSXInstalledApp(TEST_NAME);
+ JPackageInstallerHelper.validateApp(app);
+
+ }
+
+ private static void verifyUnInstall() throws Exception {
+ // Not needed on OS X, since we just deleting installed application
+ // without using generated installer. We keeping this for consistnency
+ // between platforms.
+ }
+
+ private static void init(String name, String ext) {
+ TEST_NAME = name;
+ EXT = ext;
+ OUTPUT = "output" + File.separator + TEST_NAME + "-1.0." + EXT;
+ CMD = new String [] {
+ "--package-type", EXT,
+ "--input", "input",
+ "--output", "output",
+ "--name", TEST_NAME,
+ "--main-jar", "hello.jar",
+ "--main-class", "Hello",
+ "--license-file", JPackagePath.getLicenseFilePath()};
+ }
+
+ public static void run(String name, String ext) throws Exception {
+ init(name, ext);
+
+ if (JPackageInstallerHelper.isVerifyInstall()) {
+ verifyInstall();
+ } else if (JPackageInstallerHelper.isVerifyUnInstall()) {
+ verifyUnInstall();
+ } else {
+ JPackageHelper.createHelloInstallerJar();
+ testCreateInstaller();
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/macosx/base/JPMacOptionsBase.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,174 @@
+/*
+ * Copyright (c) 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.
+ *
+ * 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.
+ */
+
+import javax.xml.stream.XMLInputFactory;
+import javax.xml.stream.XMLStreamConstants;
+import javax.xml.stream.XMLStreamReader;
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileReader;
+
+public class JPMacOptionsBase {
+
+ static final String TEST_BUNDLE_NAME = "TestBundleName";
+ static final String TEST_BUNDLE_IDENTIFIER = "net.java.openjdk.packagerTest";
+ static final String TEST_CATECORY = "public.app-category.test";
+ private static String TEST_NAME;
+ private static String EXT;
+ private static String OUTPUT;
+ private static String[] CMD;
+
+ private static void testCreateInstaller() throws Exception {
+ JPackageHelper.executeCLI(true, CMD);
+
+ if (EXT.equals("dmg")) {
+ String disk = null;
+ try {
+ var log = new File("hdiutil.log");
+ JPackageHelper.execute(log, "/usr/bin/hdiutil",
+ "attach", OUTPUT);
+ try(var br = new BufferedReader(new FileReader(log))) {
+ var line = br.lines().reduce((a, b) -> b).orElse(null)
+ .split("\t");
+ disk = line[0].trim();
+ testPkg(line[2].trim() + File.separator + TEST_NAME +
+ "-1.0.pkg");
+ }
+ } finally {
+ if (disk != null) {
+ JPackageHelper.execute(null,
+ "/usr/bin/hdiutil", "detach", disk);
+ }
+ }
+ } else {
+ testPkg(OUTPUT);
+ }
+ }
+
+ private static void testPkg(String path) throws Exception {
+ JPackageHelper.execute(null, "/usr/sbin/pkgutil",
+ "--expand-full", path, "expand");
+ var info = new File("expand/" + TEST_NAME + "-app.pkg/Payload/"
+ + TEST_NAME + ".app/Contents/Info.plist");
+ if (!info.exists()) {
+ throw new AssertionError("Info.plist not found");
+ }
+
+ String bundleName = null;
+ String bundleIdentifier = null;
+ String categoryType = null;
+ try (FileInputStream fis = new FileInputStream(info)) {
+ var xmlInFact = XMLInputFactory.newInstance();
+ xmlInFact.setProperty(XMLInputFactory.SUPPORT_DTD, false);
+ xmlInFact.setProperty(
+ XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, false);
+ var reader = xmlInFact.createXMLStreamReader(fis);
+ while (reader.hasNext()) {
+ if (reader.next() == XMLStreamConstants.CHARACTERS) {
+ switch (reader.getText()) {
+ case "CFBundleName": {
+ bundleName = readValue(reader);
+ break;
+ }
+ case "CFBundleIdentifier" : {
+ bundleIdentifier = readValue(reader);
+ break;
+ }
+ case "LSApplicationCategoryType" : {
+ categoryType = readValue(reader);
+ break;
+ }
+ }
+ }
+ }
+ }
+ boolean passed = true;
+ if (!TEST_BUNDLE_NAME.equals(bundleName)) {
+ passed = false;
+ System.err.println("Wrong bundle name [" + bundleName +
+ "] expected [" + TEST_BUNDLE_NAME + "]" );
+ }
+ if (!TEST_BUNDLE_IDENTIFIER.equals(bundleIdentifier)) {
+ passed = false;
+ System.err.println("Wrong bundle identifier [" +
+ bundleIdentifier + "] expected [" + TEST_BUNDLE_IDENTIFIER
+ + "]" );
+ }
+ if (!TEST_CATECORY.equals(categoryType)) {
+ passed = false;
+ System.err.println("Wrong appstore category [" + categoryType +
+ "] expected [" + TEST_CATECORY + "]" );
+ }
+
+ if (!passed) {
+ throw new AssertionError("Test failed");
+ }
+ }
+
+ static private String readValue(XMLStreamReader reader) throws Exception {
+ while (reader.hasNext() && reader.next() != XMLStreamConstants.START_ELEMENT);
+ return reader.hasNext() ? reader.getElementText() : null;
+ }
+
+ private static void verifyInstall() throws Exception {
+ String app = JPackagePath.getOSXInstalledApp("jpackage", TEST_NAME);
+ JPackageInstallerHelper.validateApp(app);
+ }
+
+ private static void verifyUnInstall() throws Exception {
+ // Not needed on OS X, since we just deleting installed application
+ // without using generated installer. We keeping this for consistnency
+ // between platforms.
+ }
+
+ private static void init(String name, String ext) {
+ TEST_NAME = name;
+ EXT = ext;
+ OUTPUT = "output" + File.separator + TEST_NAME + "-1.0." + EXT;
+ CMD = new String[] {
+ "--package-type", EXT,
+ "--input", "input",
+ "--output", "output",
+ "--name", TEST_NAME,
+ "--main-jar", "hello.jar",
+ "--main-class", "Hello",
+ "--mac-bundle-name", TEST_BUNDLE_NAME,
+ "--mac-bundle-identifier", TEST_BUNDLE_IDENTIFIER,
+ "--mac-app-store-category", TEST_CATECORY
+ };
+ }
+
+ public static void run(String name, String ext) throws Exception {
+ init(name, ext);
+
+ if (JPackageInstallerHelper.isVerifyInstall()) {
+ verifyInstall();
+ } else if (JPackageInstallerHelper.isVerifyUnInstall()) {
+ verifyUnInstall();
+ } else {
+ JPackageHelper.createHelloInstallerJar();
+ testCreateInstaller();
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/macosx/dmg/JPMacDmgAssociationsTest.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,47 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+/*
+ * @test
+ * @summary jpackage create installer test
+ * @library ../../../helpers
+ * @library ../base
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageInstallerHelper
+ * @build JPMacAssociationsBase
+ * @requires (os.family == "mac")
+ * @modules jdk.jpackage
+ * @modules jdk.jpackage/jdk.jpackage.internal
+ * @run main/othervm -Xmx512m JPMacDmgAssociationsTest
+ */
+public class JPMacDmgAssociationsTest {
+ private static final String TEST_NAME = "JPMacDmgAssociationsTest";
+ private static final String EXT = "dmg";
+
+ public static void main(String[] args) throws Exception {
+ if (jdk.jpackage.internal.MacDmgBundler.isSupported()) {
+ JPMacAssociationsBase.run(TEST_NAME, EXT);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/macosx/dmg/JPMacDmgInstallDirTest.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,47 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+/*
+ * @test
+ * @summary jpackage create installer test
+ * @library ../../../helpers
+ * @library ../base
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageInstallerHelper
+ * @build JPMacInstallDirBase
+ * @requires (os.family == "mac")
+ * @modules jdk.jpackage
+ * @modules jdk.jpackage/jdk.jpackage.internal
+ * @run main/othervm -Xmx512m JPMacDmgInstallDirTest
+ */
+public class JPMacDmgInstallDirTest {
+ private static final String TEST_NAME = "JPMacDmgInstallDirTest";
+ private static final String EXT = "dmg";
+
+ public static void main(String[] args) throws Exception {
+ if (jdk.jpackage.internal.MacDmgBundler.isSupported()) {
+ JPMacInstallDirBase.run(TEST_NAME, EXT);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/macosx/dmg/JPMacDmgLicenseTest.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,47 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+/*
+ * @test
+ * @summary jpackage create installer test
+ * @library ../../../helpers
+ * @library ../base
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageInstallerHelper
+ * @build JPMacLicenseBase
+ * @requires (os.family == "mac")
+ * @modules jdk.jpackage
+ * @modules jdk.jpackage/jdk.jpackage.internal
+ * @run main/othervm -Xmx512m JPMacDmgLicenseTest
+ */
+public class JPMacDmgLicenseTest {
+ private static final String TEST_NAME = "JPMacDmgLicenseTest";
+ private static final String EXT = "dmg";
+
+ public static void main(String[] args) throws Exception {
+ if (jdk.jpackage.internal.MacDmgBundler.isSupported()) {
+ JPMacLicenseBase.run(TEST_NAME, EXT);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/macosx/dmg/JPMacDmgOptionsTest.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 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.
+ *
+ * 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.
+ */
+
+/*
+ * @test
+ * @summary jpackage create installer test
+ * @library ../../../helpers
+ * @library ../base
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageInstallerHelper
+ * @build JPMacOptionsBase
+ * @requires (os.family == "mac")
+ * @modules jdk.jpackage
+ * @modules jdk.jpackage/jdk.jpackage.internal
+ * @run main/othervm -Xmx512m JPMacDmgOptionsTest
+ */
+public class JPMacDmgOptionsTest {
+ private static final String TEST_NAME = "JPMacDmgOptionsTest";
+ private static final String EXT = "dmg";
+
+ public static void main(String[] args) throws Exception {
+ if (jdk.jpackage.internal.MacDmgBundler.isSupported()) {
+ JPMacOptionsBase.run(TEST_NAME, EXT);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/macosx/dmg/JPMacDmgTest.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,47 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+/*
+ * @test
+ * @summary jpackage create installer test
+ * @library ../../../helpers
+ * @library ../base
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageInstallerHelper
+ * @build JPMacBase
+ * @requires (os.family == "mac")
+ * @modules jdk.jpackage
+ * @modules jdk.jpackage/jdk.jpackage.internal
+ * @run main/othervm -Xmx512m JPMacDmgTest
+ */
+public class JPMacDmgTest {
+ private static final String TEST_NAME = "JPMacDmgTest";
+ private static final String EXT = "dmg";
+
+ public static void main(String[] args) throws Exception {
+ if (jdk.jpackage.internal.MacDmgBundler.isSupported()) {
+ JPMacBase.run(TEST_NAME, EXT);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/macosx/dmg/install.sh Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,26 @@
+echo "Note: This script will install DMG files silently. In order to verify UI, each .dmg needs to launched manually via Finder."
+
+# JPMacDmgTest
+hdiutil attach JPMacDmgTest-1.0.dmg
+sudo /usr/sbin/installer -pkg /Volumes/JPMacDmgTest/JPMacDmgTest-1.0.pkg -target /
+hdiutil detach /Volumes/JPMacDmgTest/
+
+# JPMacDmgLicenseTest
+hdiutil attach JPMacDmgLicenseTest-1.0.dmg
+sudo /usr/sbin/installer -pkg /Volumes/JPMacDmgLicenseTest/JPMacDmgLicenseTest-1.0.pkg -target /
+hdiutil detach /Volumes/JPMacDmgLicenseTest/
+
+# JPMacDmgAssociationsTest
+hdiutil attach JPMacDmgAssociationsTest-1.0.dmg
+sudo /usr/sbin/installer -pkg /Volumes/JPMacDmgAssociationsTest/JPMacDmgAssociationsTest-1.0.pkg -target /
+hdiutil detach /Volumes/JPMacDmgAssociationsTest/
+
+# JPMacDmgOptionsTest
+hdiutil attach JPMacDmgOptionsTest-1.0.dmg
+sudo /usr/sbin/installer -pkg /Volumes/JPMacDmgOptionsTest/JPMacDmgOptionsTest-1.0.pkg -target /
+hdiutil detach /Volumes/JPMacDmgOptionsTest/
+
+# JPMacDmgInstallDirTest
+hdiutil attach JPMacDmgInstallDirTest-1.0.dmg
+sudo /usr/sbin/installer -pkg /Volumes/JPMacDmgInstallDirTest/JPMacDmgInstallDirTest-1.0.pkg -target /
+hdiutil detach /Volumes/JPMacDmgInstallDirTest/
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/macosx/dmg/uninstall.sh Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,5 @@
+sudo rm -rf /Applications/JPMacDmgTest.app
+sudo rm -rf /Applications/JPMacDmgLicenseTest.app
+sudo rm -rf /Applications/JPMacDmgAssociationsTest.app
+sudo rm -rf /Applications/JPMacDmgOptionsTest.app
+sudo rm -rf /Applications/jpackage/JPMacDmgInstallDirTest.app
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/macosx/pkg/JPMacPkgAssociationsTest.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+/*
+ * @test
+ * @summary jpackage create installer test
+ * @library ../../../helpers
+ * @library ../base
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageInstallerHelper
+ * @build JPMacAssociationsBase
+ * @requires (os.family == "mac")
+ * @modules jdk.jpackage
+ * @run main/othervm -Xmx512m JPMacPkgAssociationsTest
+ */
+public class JPMacPkgAssociationsTest {
+ private static final String TEST_NAME = "JPMacPkgAssociationsTest";
+ private static final String EXT = "pkg";
+
+ public static void main(String[] args) throws Exception {
+ JPMacAssociationsBase.run(TEST_NAME, EXT);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/macosx/pkg/JPMacPkgInstallDirTest.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+/*
+ * @test
+ * @summary jpackage create installer test
+ * @library ../../../helpers
+ * @library ../base
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageInstallerHelper
+ * @build JPMacInstallDirBase
+ * @requires (os.family == "mac")
+ * @modules jdk.jpackage
+ * @run main/othervm -Xmx512m JPMacPkgInstallDirTest
+ */
+public class JPMacPkgInstallDirTest {
+ private static final String TEST_NAME = "JPMacPkgInstallDirTest";
+ private static final String EXT = "pkg";
+
+ public static void main(String[] args) throws Exception {
+ JPMacInstallDirBase.run(TEST_NAME, EXT);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/macosx/pkg/JPMacPkgLicenseTest.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+/*
+ * @test
+ * @summary jpackage create installer test
+ * @library ../../../helpers
+ * @library ../base
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageInstallerHelper
+ * @build JPMacLicenseBase
+ * @requires (os.family == "mac")
+ * @modules jdk.jpackage
+ * @run main/othervm -Xmx512m JPMacPkgLicenseTest
+ */
+public class JPMacPkgLicenseTest {
+ private static final String TEST_NAME = "JPMacPkgLicenseTest";
+ private static final String EXT = "pkg";
+
+ public static void main(String[] args) throws Exception {
+ JPMacLicenseBase.run(TEST_NAME, EXT);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/macosx/pkg/JPMacPkgOptionsTest.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 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.
+ *
+ * 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.
+ */
+
+/*
+ * @test
+ * @summary jpackage create installer test
+ * @library ../../../helpers
+ * @library ../base
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageInstallerHelper
+ * @build JPMacOptionsBase
+ * @requires (os.family == "mac")
+ * @modules jdk.jpackage
+ * @run main/othervm -Xmx512m JPMacPkgOptionsTest
+ */
+public class JPMacPkgOptionsTest {
+ private static final String TEST_NAME = "JPMacPkgOptionsTest";
+ private static final String EXT = "pkg";
+
+ public static void main(String[] args) throws Exception {
+ JPMacOptionsBase.run(TEST_NAME, EXT);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/macosx/pkg/JPMacPkgTest.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+/*
+ * @test
+ * @summary jpackage create installer test
+ * @library ../../../helpers
+ * @library ../base
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageInstallerHelper
+ * @build JPMacBase
+ * @requires (os.family == "mac")
+ * @modules jdk.jpackage
+ * @run main/othervm -Xmx512m JPMacPkgTest
+ */
+public class JPMacPkgTest {
+ private static final String TEST_NAME = "JPMacPkgTest";
+ private static final String EXT = "pkg";
+
+ public static void main(String[] args) throws Exception {
+ JPMacBase.run(TEST_NAME, EXT);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/macosx/pkg/install.sh Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,6 @@
+echo Note: This script will install packages silently. In order to verify UI, each .pkg needs to launched manually via Finder.
+sudo /usr/sbin/installer -pkg JPMacPkgTest-1.0.pkg -target /
+sudo /usr/sbin/installer -pkg JPMacPkgLicenseTest-1.0.pkg -target /
+sudo /usr/sbin/installer -pkg JPMacPkgAssociationsTest-1.0.pkg -target /
+sudo /usr/sbin/installer -pkg JPMacOptionsTest-1.0.pkg -target /
+sudo /usr/sbin/installer -pkg JPMacPkgInstallDirTest-1.0.pkg -target /
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/macosx/pkg/uninstall.sh Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,5 @@
+sudo rm -rf /Applications/JPMacPkgTest.app
+sudo rm -rf /Applications/JPMacPkgLicenseTest.app
+sudo rm -rf /Applications/JPMacPkgAssociationsTest.app
+sudo rm -rf /Applications/JPMacPkgOptionsDirTest.app
+sudo rm -rf /Applications/jpackage/JPMacPkgInstallDirTest.app
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/windows/base/JPackageCreateInstallerBase.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,91 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+public class JPackageCreateInstallerBase {
+
+ private static String TEST_NAME;
+ private static String EXT;
+ private static String OUTPUT;
+ private static String[] CMD;
+
+ private static void copyResults() throws Exception {
+ List<String> files = new ArrayList<>();
+ files.add(OUTPUT);
+ JPackageInstallerHelper.copyTestResults(files);
+ }
+
+ private static void testCreateInstaller() throws Exception {
+ JPackageHelper.executeCLI(true, CMD);
+ JPackageInstallerHelper.validateOutput(OUTPUT);
+ copyResults();
+ }
+
+ private static void verifyInstall() throws Exception {
+ String app = JPackagePath.getWinInstalledApp(TEST_NAME);
+ JPackageInstallerHelper.validateApp(app);
+
+ // Validate start menu
+ JPackageInstallerHelper.validateStartMenu("Unknown", TEST_NAME, true);
+ }
+
+ private static void verifyUnInstall() throws Exception {
+ String folderPath = JPackagePath.getWinInstallFolder(TEST_NAME);
+ File folder = new File(folderPath);
+ if (folder.exists()) {
+ throw new AssertionError("Error: " + folder.getAbsolutePath() + " exist");
+ }
+
+ // Validate start menu
+ JPackageInstallerHelper.validateStartMenu("Unknown", TEST_NAME, false);
+ }
+
+ private static void init(String name, String ext) {
+ TEST_NAME = name;
+ EXT = ext;
+ OUTPUT = "output" + File.separator + TEST_NAME + "-1.0." + EXT;
+ CMD = new String[]{
+ "--package-type", EXT,
+ "--input", "input",
+ "--output", "output",
+ "--name", TEST_NAME,
+ "--main-jar", "hello.jar",
+ "--main-class", "Hello"};
+ }
+
+ public static void run(String name, String ext) throws Exception {
+ init(name, ext);
+
+ if (JPackageInstallerHelper.isVerifyInstall()) {
+ verifyInstall();
+ } else if (JPackageInstallerHelper.isVerifyUnInstall()) {
+ verifyUnInstall();
+ } else {
+ JPackageHelper.createHelloInstallerJar();
+ testCreateInstaller();
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/windows/base/JPackageCreateInstallerFileAssociationsBase.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,179 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+import java.awt.Desktop;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.PrintWriter;
+import java.nio.file.Files;
+import java.util.ArrayList;
+import java.util.List;
+
+public class JPackageCreateInstallerFileAssociationsBase {
+
+ private static String TEST_NAME;
+ private static String EXT;
+ private static String TEST_EXT;
+ private static String OUTPUT;
+ private static String[] CMD;
+
+ private static void copyResults() throws Exception {
+ List<String> files = new ArrayList<>();
+ files.add(OUTPUT);
+ JPackageInstallerHelper.copyTestResults(files);
+ }
+
+ private static void testCreateInstaller() throws Exception {
+ JPackageHelper.executeCLI(true, CMD);
+ JPackageInstallerHelper.validateOutput(OUTPUT);
+ copyResults();
+ }
+
+ private static void validateAppOutput() throws Exception {
+ File outFile = new File("appOutput.txt");
+ int count = 10;
+ boolean bOutputCreated = false;
+ while (count > 0) {
+ if (!outFile.exists()) {
+ Thread.sleep(3000);
+ count--;
+ } else {
+ bOutputCreated = true;
+ break;
+ }
+ }
+
+ if (!bOutputCreated) {
+ throw new AssertionError(outFile.getAbsolutePath() + " was not created");
+ }
+
+ String output = Files.readString(outFile.toPath());
+ String[] result = output.split("\n");
+ if (result.length != 3) {
+ System.err.println(output);
+ throw new AssertionError(
+ "Unexpected number of lines: " + result.length);
+ }
+
+ if (!result[0].trim().equals("jpackage test application")) {
+ throw new AssertionError("Unexpected result[0]: " + result[0]);
+ }
+
+ if (!result[1].trim().equals("args.length: 1")) {
+ throw new AssertionError("Unexpected result[1]: " + result[1]);
+ }
+
+ File faFile = new File(TEST_NAME + "." + TEST_EXT);
+ if (!result[2].trim().equals(faFile.getAbsolutePath())) {
+ throw new AssertionError("Unexpected result[2]: " + result[2]);
+ }
+ }
+
+ private static void verifyInstall() throws Exception {
+ createFileAssociationsTestFile();
+ Desktop.getDesktop().open(new File(TEST_NAME + "." + TEST_EXT));
+ validateAppOutput();
+
+ // Validate start menu
+ JPackageInstallerHelper.validateStartMenu("Unknown", TEST_NAME, true);
+
+ // Validate registry
+ String[] values1 = {TEST_NAME};
+ JPackageInstallerHelper.validateWinRegistry("HKLM\\Software\\Classes\\." + TEST_EXT, values1, true);
+ String[] values2 = {TEST_EXT};
+ JPackageInstallerHelper.validateWinRegistry("HKLM\\Software\\Classes\\MIME\\Database\\Content Type\\application/" + TEST_EXT, values2, true);
+ }
+
+ private static void verifyUnInstall() throws Exception {
+ String folderPath = JPackagePath.getWinInstallFolder(TEST_NAME);
+ File folder = new File(folderPath);
+ if (folder.exists()) {
+ throw new AssertionError("Error: " + folder.getAbsolutePath() + " exist");
+ }
+
+ // Validate start menu
+ JPackageInstallerHelper.validateStartMenu("Unknown", TEST_NAME, false);
+
+ // Validate registry
+ JPackageInstallerHelper.validateWinRegistry("HKLM\\Software\\Classes\\." + TEST_EXT, null, false);
+ JPackageInstallerHelper.validateWinRegistry("HKLM\\Software\\Classes\\MIME\\Database\\Content Type\\application/" + TEST_EXT, null, false);
+ }
+
+ private static void createFileAssociationsTestFile() throws Exception {
+ try (PrintWriter out = new PrintWriter(new BufferedWriter(
+ new FileWriter(TEST_NAME + "." + TEST_EXT)))) {
+ out.println(TEST_NAME);
+ }
+ }
+
+ private static void createFileAssociationsProperties() throws Exception {
+ try (PrintWriter out = new PrintWriter(new BufferedWriter(
+ new FileWriter("fa.properties")))) {
+ out.println("extension=" + TEST_EXT);
+ out.println("mime-type=application/" + TEST_EXT);
+ out.println("description=jpackage test extention");
+ }
+ }
+
+ private static void init(String name, String ext, String installDir, String testExt) {
+ TEST_NAME = name;
+ EXT = ext;
+ TEST_EXT = testExt;
+ OUTPUT = "output" + File.separator + TEST_NAME + "-1.0." + EXT;
+ if (installDir == null) {
+ CMD = new String[]{
+ "--package-type", EXT,
+ "--input", "input",
+ "--output", "output",
+ "--name", TEST_NAME,
+ "--main-jar", "hello.jar",
+ "--main-class", "Hello",
+ "--file-associations", "fa.properties"};
+ } else {
+ CMD = new String[]{
+ "--package-type", EXT,
+ "--input", "input",
+ "--output", "output",
+ "--name", TEST_NAME,
+ "--main-jar", "hello.jar",
+ "--main-class", "Hello",
+ "--file-associations", "fa.properties",
+ "--install-dir", installDir};
+ }
+ }
+
+ public static void run(String name, String ext, String installDir, String testExt) throws Exception {
+ init(name, ext, installDir, testExt);
+
+ if (JPackageInstallerHelper.isVerifyInstall()) {
+ verifyInstall();
+ } else if (JPackageInstallerHelper.isVerifyUnInstall()) {
+ verifyUnInstall();
+ } else {
+ JPackageHelper.createHelloInstallerJar();
+ createFileAssociationsProperties();
+ testCreateInstaller();
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/windows/base/JPackageCreateInstallerInstallDirBase.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 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.
+ *
+ * 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.
+ */
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+public class JPackageCreateInstallerInstallDirBase {
+
+ private static String TEST_NAME;
+ private static String EXT;
+ private static String OUTPUT;
+ private static String[] CMD;
+ private static String INSTALL_DIR;
+
+ private static void copyResults() throws Exception {
+ List<String> files = new ArrayList<>();
+ files.add(OUTPUT);
+ JPackageInstallerHelper.copyTestResults(files);
+ }
+
+ private static void testCreateInstaller() throws Exception {
+ JPackageHelper.executeCLI(true, CMD);
+ JPackageInstallerHelper.validateOutput(OUTPUT);
+ copyResults();
+ }
+
+ private static void verifyInstall() throws Exception {
+ String app = JPackagePath.getWinInstalledApp(INSTALL_DIR, TEST_NAME);
+ JPackageInstallerHelper.validateApp(app);
+
+ // Validate start menu
+ JPackageInstallerHelper.validateStartMenu("Unknown", TEST_NAME, false);
+
+ // Validate desktop shortcut
+ JPackageInstallerHelper.validateDesktopShortcut(TEST_NAME, true);
+ }
+
+ private static void verifyUnInstall() throws Exception {
+ String folderPath = JPackagePath.getWinInstallFolder(INSTALL_DIR);
+ File folder = new File(folderPath);
+ if (folder.exists()) {
+ throw new AssertionError("Error: " + folder.getAbsolutePath() + " exist");
+ }
+
+ // Validate start menu
+ JPackageInstallerHelper.validateStartMenu("Unknown", TEST_NAME, false);
+
+ // Validate desktop shortcut
+ JPackageInstallerHelper.validateDesktopShortcut(TEST_NAME, false);
+ }
+
+ private static void init(String name, String ext) {
+ TEST_NAME = name;
+ EXT = ext;
+ OUTPUT = "output" + File.separator + TEST_NAME + "-1.0." + EXT;
+ INSTALL_DIR = "TestVendor\\JPackageCreateInstallerInstallDirTestDir";
+ CMD = new String[]{
+ "--package-type", EXT,
+ "--input", "input",
+ "--output", "output",
+ "--name", TEST_NAME,
+ "--main-jar", "hello.jar",
+ "--main-class", "Hello",
+ "--install-dir", INSTALL_DIR,
+ "--win-shortcut"};
+ }
+
+ public static void run(String name, String ext) throws Exception {
+ init(name, ext);
+
+ if (JPackageInstallerHelper.isVerifyInstall()) {
+ verifyInstall();
+ } else if (JPackageInstallerHelper.isVerifyUnInstall()) {
+ verifyUnInstall();
+ } else {
+ JPackageHelper.createHelloInstallerJar();
+ testCreateInstaller();
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/windows/base/JPackageCreateInstallerLicenseBase.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,92 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+public class JPackageCreateInstallerLicenseBase {
+
+ private static String TEST_NAME;
+ private static String EXT;
+ private static String OUTPUT;
+ private static String[] CMD;
+
+ private static void copyResults() throws Exception {
+ List<String> files = new ArrayList<>();
+ files.add(OUTPUT);
+ JPackageInstallerHelper.copyTestResults(files);
+ }
+
+ private static void testCreateInstaller() throws Exception {
+ JPackageHelper.executeCLI(true, CMD);
+ JPackageInstallerHelper.validateOutput(OUTPUT);
+ copyResults();
+ }
+
+ private static void verifyInstall() throws Exception {
+ String app = JPackagePath.getWinInstalledApp(TEST_NAME);
+ JPackageInstallerHelper.validateApp(app);
+
+ // Validate start menu
+ JPackageInstallerHelper.validateStartMenu("Unknown", TEST_NAME, true);
+ }
+
+ private static void verifyUnInstall() throws Exception {
+ String folderPath = JPackagePath.getWinInstallFolder(TEST_NAME);
+ File folder = new File(folderPath);
+ if (folder.exists()) {
+ throw new AssertionError("Error: " + folder.getAbsolutePath() + " exist");
+ }
+
+ // Validate start menu
+ JPackageInstallerHelper.validateStartMenu("Unknown", TEST_NAME, false);
+ }
+
+ private static void init(String name, String ext) {
+ TEST_NAME = name;
+ EXT = ext;
+ OUTPUT = "output" + File.separator + TEST_NAME + "-1.0." + EXT;
+ CMD = new String [] {
+ "--package-type", EXT,
+ "--input", "input",
+ "--output", "output",
+ "--name", TEST_NAME,
+ "--main-jar", "hello.jar",
+ "--main-class", "Hello",
+ "--license-file", JPackagePath.getLicenseFilePath()};
+ }
+
+ public static void run(String name, String ext) throws Exception {
+ init(name, ext);
+
+ if (JPackageInstallerHelper.isVerifyInstall()) {
+ verifyInstall();
+ } else if (JPackageInstallerHelper.isVerifyUnInstall()) {
+ verifyUnInstall();
+ } else {
+ JPackageHelper.createHelloInstallerJar();
+ testCreateInstaller();
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/windows/base/JPackageCreateInstallerRuntimeBase.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 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.
+ *
+ * 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.
+ */
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+public class JPackageCreateInstallerRuntimeBase {
+
+ private static String TEST_NAME;
+ private static String EXT;
+ private static String OUTPUT;
+ private static String[] CMD;
+
+ private static void copyResults() throws Exception {
+ List<String> files = new ArrayList<>();
+ files.add(OUTPUT);
+ JPackageInstallerHelper.copyTestResults(files);
+ }
+
+ private static void testCreateInstaller() throws Exception {
+ JPackageHelper.executeCLI(true, CMD);
+ JPackageInstallerHelper.validateOutput(OUTPUT);
+ copyResults();
+ }
+
+ private static void verifyInstall() throws Exception {
+ // NOP by design.
+ }
+
+ private static void verifyUnInstall() throws Exception {
+ String folderPath = JPackagePath.getWinInstallFolder(TEST_NAME);
+ File folder = new File(folderPath);
+ if (folder.exists()) {
+ throw new AssertionError("Error: " + folder.getAbsolutePath() + " exist");
+ }
+ }
+
+ private static void init(String name, String ext) {
+ TEST_NAME = name;
+ EXT = ext;
+ OUTPUT = "output" + File.separator + TEST_NAME + "-1.0." + EXT;
+ CMD = new String[]{
+ "--package-type", EXT,
+ "--output", "output",
+ "--name", TEST_NAME,
+ "--runtime-image", System.getProperty("java.home")};
+ }
+
+ public static void run(String name, String ext) throws Exception {
+ init(name, ext);
+
+ if (JPackageInstallerHelper.isVerifyInstall()) {
+ verifyInstall();
+ } else if (JPackageInstallerHelper.isVerifyUnInstall()) {
+ verifyUnInstall();
+ } else {
+ testCreateInstaller();
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/windows/base/JPackageCreateInstallerWinDirChooserBase.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,92 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+public class JPackageCreateInstallerWinDirChooserBase {
+
+ private static String TEST_NAME;
+ private static String EXT;
+ private static String OUTPUT;
+ private static String[] CMD;
+
+ private static void copyResults() throws Exception {
+ List<String> files = new ArrayList<>();
+ files.add(OUTPUT);
+ JPackageInstallerHelper.copyTestResults(files);
+ }
+
+ private static void testCreateInstaller() throws Exception {
+ JPackageHelper.executeCLI(true, CMD);
+ JPackageInstallerHelper.validateOutput(OUTPUT);
+ copyResults();
+ }
+
+ private static void verifyInstall() throws Exception {
+ String app = JPackagePath.getWinInstalledApp(TEST_NAME);
+ JPackageInstallerHelper.validateApp(app);
+
+ // Validate start menu
+ JPackageInstallerHelper.validateStartMenu("Unknown", TEST_NAME, true);
+ }
+
+ private static void verifyUnInstall() throws Exception {
+ String folderPath = JPackagePath.getWinInstallFolder(TEST_NAME);
+ File folder = new File(folderPath);
+ if (folder.exists()) {
+ throw new AssertionError("Error: " + folder.getAbsolutePath() + " exist");
+ }
+
+ // Validate start menu
+ JPackageInstallerHelper.validateStartMenu("Unknown", TEST_NAME, false);
+ }
+
+ private static void init(String name, String ext) {
+ TEST_NAME = name;
+ EXT = ext;
+ OUTPUT = "output" + File.separator + TEST_NAME + "-1.0." + EXT;
+ CMD = new String[]{
+ "--package-type", EXT,
+ "--input", "input",
+ "--output", "output",
+ "--name", TEST_NAME,
+ "--main-jar", "hello.jar",
+ "--main-class", "Hello",
+ "--win-dir-chooser"};
+ }
+
+ public static void run(String name, String ext) throws Exception {
+ init(name, ext);
+
+ if (JPackageInstallerHelper.isVerifyInstall()) {
+ verifyInstall();
+ } else if (JPackageInstallerHelper.isVerifyUnInstall()) {
+ verifyUnInstall();
+ } else {
+ JPackageHelper.createHelloInstallerJar();
+ testCreateInstaller();
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/windows/base/JPackageCreateInstallerWinMenuBase.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,92 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+public class JPackageCreateInstallerWinMenuBase {
+
+ private static String TEST_NAME;
+ private static String EXT;
+ private static String OUTPUT;
+ private static String[] CMD;
+
+ private static void copyResults() throws Exception {
+ List<String> files = new ArrayList<>();
+ files.add(OUTPUT);
+ JPackageInstallerHelper.copyTestResults(files);
+ }
+
+ private static void testCreateInstaller() throws Exception {
+ JPackageHelper.executeCLI(true, CMD);
+ JPackageInstallerHelper.validateOutput(OUTPUT);
+ copyResults();
+ }
+
+ private static void verifyInstall() throws Exception {
+ String app = JPackagePath.getWinInstalledApp(TEST_NAME);
+ JPackageInstallerHelper.validateApp(app);
+
+ // Validate start menu
+ JPackageInstallerHelper.validateStartMenu("Unknown", TEST_NAME, true);
+ }
+
+ private static void verifyUnInstall() throws Exception {
+ String folderPath = JPackagePath.getWinInstallFolder(TEST_NAME);
+ File folder = new File(folderPath);
+ if (folder.exists()) {
+ throw new AssertionError("Error: " + folder.getAbsolutePath() + " exist");
+ }
+
+ // Validate start menu
+ JPackageInstallerHelper.validateStartMenu("Unknown", TEST_NAME, false);
+ }
+
+ private static void init(String name, String ext) {
+ TEST_NAME = name;
+ EXT = ext;
+ OUTPUT = "output" + File.separator + TEST_NAME + "-1.0." + EXT;
+ CMD = new String[]{
+ "--package-type", EXT,
+ "--input", "input",
+ "--output", "output",
+ "--name", TEST_NAME,
+ "--main-jar", "hello.jar",
+ "--main-class", "Hello",
+ "--win-menu"};
+ }
+
+ public static void run(String name, String ext) throws Exception {
+ init(name, ext);
+
+ if (JPackageInstallerHelper.isVerifyInstall()) {
+ verifyInstall();
+ } else if (JPackageInstallerHelper.isVerifyUnInstall()) {
+ verifyUnInstall();
+ } else {
+ JPackageHelper.createHelloInstallerJar();
+ testCreateInstaller();
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/windows/base/JPackageCreateInstallerWinMenuGroupBase.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,93 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+public class JPackageCreateInstallerWinMenuGroupBase {
+
+ private static String TEST_NAME;
+ private static String EXT;
+ private static String OUTPUT;
+ private static String[] CMD;
+
+ private static void copyResults() throws Exception {
+ List<String> files = new ArrayList<>();
+ files.add(OUTPUT);
+ JPackageInstallerHelper.copyTestResults(files);
+ }
+
+ private static void testCreateInstaller() throws Exception {
+ JPackageHelper.executeCLI(true, CMD);
+ JPackageInstallerHelper.validateOutput(OUTPUT);
+ copyResults();
+ }
+
+ private static void verifyInstall() throws Exception {
+ String app = JPackagePath.getWinInstalledApp(TEST_NAME);
+ JPackageInstallerHelper.validateApp(app);
+
+ // Validate start menu
+ JPackageInstallerHelper.validateStartMenu(TEST_NAME, TEST_NAME, true);
+ }
+
+ private static void verifyUnInstall() throws Exception {
+ String folderPath = JPackagePath.getWinInstallFolder(TEST_NAME);
+ File folder = new File(folderPath);
+ if (folder.exists()) {
+ throw new AssertionError("Error: " + folder.getAbsolutePath() + " exist");
+ }
+
+ // Validate start menu
+ JPackageInstallerHelper.validateStartMenu(TEST_NAME, TEST_NAME, false);
+ }
+
+ private static void init(String name, String ext) {
+ TEST_NAME = name;
+ EXT = ext;
+ OUTPUT = "output" + File.separator + TEST_NAME + "-1.0." + EXT;
+ CMD = new String[]{
+ "--package-type", EXT,
+ "--input", "input",
+ "--output", "output",
+ "--name", TEST_NAME,
+ "--main-jar", "hello.jar",
+ "--main-class", "Hello",
+ "--win-menu",
+ "--win-menu-group", TEST_NAME};
+ }
+
+ public static void run(String name, String ext) throws Exception {
+ init(name, ext);
+
+ if (JPackageInstallerHelper.isVerifyInstall()) {
+ verifyInstall();
+ } else if (JPackageInstallerHelper.isVerifyUnInstall()) {
+ verifyUnInstall();
+ } else {
+ JPackageHelper.createHelloInstallerJar();
+ testCreateInstaller();
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/windows/base/JPackageCreateInstallerWinPerUserInstallBase.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,94 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+public class JPackageCreateInstallerWinPerUserInstallBase {
+
+ private static String TEST_NAME;
+ private static String EXT;
+ private static String OUTPUT;
+ private static String[] CMD;
+
+ private static void copyResults() throws Exception {
+ List<String> files = new ArrayList<>();
+ files.add(OUTPUT);
+ JPackageInstallerHelper.copyTestResults(files);
+ }
+
+ private static void testCreateInstaller() throws Exception {
+ JPackageHelper.executeCLI(true, CMD);
+ JPackageInstallerHelper.validateOutput(OUTPUT);
+ copyResults();
+ }
+
+ private static void verifyInstall() throws Exception {
+ String app = JPackagePath.getWinUserLocalInstalledApp(TEST_NAME);
+ JPackageInstallerHelper.validateApp(app);
+
+ // Validate start menu
+ JPackageInstallerHelper.validateUserLocalStartMenu(TEST_NAME, TEST_NAME, true);
+ }
+
+ private static void verifyUnInstall() throws Exception {
+ String folderPath = JPackagePath.getWinUserLocalInstallFolder(TEST_NAME);
+ File folder = new File(folderPath);
+ if (folder.exists()) {
+ throw new AssertionError("Error: " + folder.getAbsolutePath() + " exist");
+ }
+
+ // Validate start menu
+ JPackageInstallerHelper.validateUserLocalStartMenu(TEST_NAME, TEST_NAME, false);
+ }
+
+ private static void init(String name, String ext) {
+ TEST_NAME = name;
+ EXT = ext;
+ OUTPUT = "output" + File.separator + TEST_NAME + "-1.0." + EXT;
+ CMD = new String[]{
+ "--package-type", EXT,
+ "--input", "input",
+ "--output", "output",
+ "--name", TEST_NAME,
+ "--main-jar", "hello.jar",
+ "--main-class", "Hello",
+ "--win-per-user-install",
+ "--win-menu",
+ "--win-menu-group", TEST_NAME};
+ }
+
+ public static void run(String name, String ext) throws Exception {
+ init(name, ext);
+
+ if (JPackageInstallerHelper.isVerifyInstall()) {
+ verifyInstall();
+ } else if (JPackageInstallerHelper.isVerifyUnInstall()) {
+ verifyUnInstall();
+ } else {
+ JPackageHelper.createHelloInstallerJar();
+ testCreateInstaller();
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/windows/base/JPackageCreateInstallerWinRegistryNameBase.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,170 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+import java.awt.Desktop;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.PrintWriter;
+import java.nio.file.Files;
+import java.util.ArrayList;
+import java.util.List;
+
+public class JPackageCreateInstallerWinRegistryNameBase {
+
+ private static String TEST_NAME;
+ private static String EXT;
+ private static String TEST_EXT;
+ private static String WIN_REGISTRY_NAME;
+ private static String OUTPUT;
+ private static String[] CMD;
+
+ private static void copyResults() throws Exception {
+ List<String> files = new ArrayList<>();
+ files.add(OUTPUT);
+ JPackageInstallerHelper.copyTestResults(files);
+ }
+
+ private static void testCreateInstaller() throws Exception {
+ JPackageHelper.executeCLI(true, CMD);
+ JPackageInstallerHelper.validateOutput(OUTPUT);
+ copyResults();
+ }
+
+ private static void validateAppOutput() throws Exception {
+ File outFile = new File("appOutput.txt");
+ int count = 10;
+ boolean bOutputCreated = false;
+ while (count > 0) {
+ if (!outFile.exists()) {
+ Thread.sleep(3000);
+ count--;
+ } else {
+ bOutputCreated = true;
+ break;
+ }
+ }
+
+ if (!bOutputCreated) {
+ throw new AssertionError(outFile.getAbsolutePath() + " was not created");
+ }
+
+ String output = Files.readString(outFile.toPath());
+ String[] result = output.split("\n");
+ if (result.length != 3) {
+ System.err.println(output);
+ throw new AssertionError(
+ "Unexpected number of lines: " + result.length);
+ }
+
+ if (!result[0].trim().equals("jpackage test application")) {
+ throw new AssertionError("Unexpected result[0]: " + result[0]);
+ }
+
+ if (!result[1].trim().equals("args.length: 1")) {
+ throw new AssertionError("Unexpected result[1]: " + result[1]);
+ }
+
+ File faFile = new File(TEST_NAME + "." + TEST_EXT);
+ if (!result[2].trim().equals(faFile.getAbsolutePath())) {
+ throw new AssertionError("Unexpected result[2]: " + result[2]);
+ }
+ }
+
+ private static void verifyInstall() throws Exception {
+ createFileAssociationsTestFile();
+ Desktop.getDesktop().open(new File(TEST_NAME + "." + TEST_EXT));
+ validateAppOutput();
+
+ // Validate start menu
+ JPackageInstallerHelper.validateStartMenu("Unknown", TEST_NAME, true);
+
+ // Validate registry
+ String[] values1 = {WIN_REGISTRY_NAME};
+ JPackageInstallerHelper.validateWinRegistry("HKLM\\Software\\Classes\\." + TEST_EXT, values1, true);
+ String[] values2 = {TEST_EXT};
+ JPackageInstallerHelper.validateWinRegistry("HKLM\\Software\\Classes\\MIME\\Database\\Content Type\\application/" + TEST_EXT, values2, true);
+ }
+
+ private static void verifyUnInstall() throws Exception {
+ String folderPath = JPackagePath.getWinInstallFolder(TEST_NAME);
+ File folder = new File(folderPath);
+ if (folder.exists()) {
+ throw new AssertionError("Error: " + folder.getAbsolutePath() + " exist");
+ }
+
+ // Validate start menu
+ JPackageInstallerHelper.validateStartMenu("Unknown", TEST_NAME, false);
+
+ // Validate registry
+ JPackageInstallerHelper.validateWinRegistry("HKLM\\Software\\Classes\\." + TEST_EXT, null, false);
+ JPackageInstallerHelper.validateWinRegistry("HKLM\\Software\\Classes\\MIME\\Database\\Content Type\\application/" + TEST_EXT, null, false);
+ }
+
+ private static void createFileAssociationsTestFile() throws Exception {
+ try (PrintWriter out = new PrintWriter(new BufferedWriter(
+ new FileWriter(TEST_NAME + "." + TEST_EXT)))) {
+ out.println(TEST_NAME);
+ }
+ }
+
+ private static void createFileAssociationsProperties() throws Exception {
+ try (PrintWriter out = new PrintWriter(new BufferedWriter(
+ new FileWriter("fa.properties")))) {
+ out.println("extension=" + TEST_EXT);
+ out.println("mime-type=application/" + TEST_EXT);
+ out.println("description=jpackage test extention");
+ }
+ }
+
+ private static void init(String name, String ext) {
+ TEST_NAME = name;
+ EXT = ext;
+ TEST_EXT = "jptest2";
+ WIN_REGISTRY_NAME = "JPWINTESTREGISTRYNAME";
+ OUTPUT = "output" + File.separator + TEST_NAME + "-1.0." + EXT;
+ CMD = new String[]{
+ "--package-type", EXT,
+ "--input", "input",
+ "--output", "output",
+ "--name", TEST_NAME,
+ "--main-jar", "hello.jar",
+ "--main-class", "Hello",
+ "--file-associations", "fa.properties",
+ "--win-registry-name", WIN_REGISTRY_NAME};
+ }
+
+ public static void run(String name, String ext) throws Exception {
+ init(name, ext);
+
+ if (JPackageInstallerHelper.isVerifyInstall()) {
+ verifyInstall();
+ } else if (JPackageInstallerHelper.isVerifyUnInstall()) {
+ verifyUnInstall();
+ } else {
+ JPackageHelper.createHelloInstallerJar();
+ createFileAssociationsProperties();
+ testCreateInstaller();
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/windows/base/JPackageCreateInstallerWinShortcutBase.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,98 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+public class JPackageCreateInstallerWinShortcutBase {
+
+ private static String TEST_NAME;
+ private static String EXT;
+ private static String OUTPUT;
+ private static String[] CMD;
+
+ private static void copyResults() throws Exception {
+ List<String> files = new ArrayList<>();
+ files.add(OUTPUT);
+ JPackageInstallerHelper.copyTestResults(files);
+ }
+
+ private static void testCreateInstaller() throws Exception {
+ JPackageHelper.executeCLI(true, CMD);
+ JPackageInstallerHelper.validateOutput(OUTPUT);
+ copyResults();
+ }
+
+ private static void verifyInstall() throws Exception {
+ String app = JPackagePath.getWinInstalledApp(TEST_NAME);
+ JPackageInstallerHelper.validateApp(app);
+
+ // Validate start menu
+ JPackageInstallerHelper.validateStartMenu("Unknown", TEST_NAME, false);
+
+ // Validate desktop shortcut
+ JPackageInstallerHelper.validateDesktopShortcut(TEST_NAME, true);
+ }
+
+ private static void verifyUnInstall() throws Exception {
+ String folderPath = JPackagePath.getWinInstallFolder(TEST_NAME);
+ File folder = new File(folderPath);
+ if (folder.exists()) {
+ throw new AssertionError("Error: " + folder.getAbsolutePath() + " exist");
+ }
+
+ // Validate start menu
+ JPackageInstallerHelper.validateStartMenu("Unknown", TEST_NAME, false);
+
+ // Validate desktop shortcut
+ JPackageInstallerHelper.validateDesktopShortcut(TEST_NAME, false);
+ }
+
+ private static void init(String name, String ext) {
+ TEST_NAME = name;
+ EXT = ext;
+ OUTPUT = "output" + File.separator + TEST_NAME + "-1.0." + EXT;
+ CMD = new String[]{
+ "--package-type", EXT,
+ "--input", "input",
+ "--output", "output",
+ "--name", TEST_NAME,
+ "--main-jar", "hello.jar",
+ "--main-class", "Hello",
+ "--win-shortcut"};
+ }
+
+ public static void run(String name, String ext) throws Exception {
+ init(name, ext);
+
+ if (JPackageInstallerHelper.isVerifyInstall()) {
+ verifyInstall();
+ } else if (JPackageInstallerHelper.isVerifyUnInstall()) {
+ verifyUnInstall();
+ } else {
+ JPackageHelper.createHelloInstallerJar();
+ testCreateInstaller();
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/windows/base/JPackageCreateInstallerWinUpgradeUUIDBase.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,139 @@
+/*
+ * Copyright (c) 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.
+ *
+ * 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.
+ */
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+public class JPackageCreateInstallerWinUpgradeUUIDBase {
+
+ private static String TEST_NAME;
+ private static String EXT;
+ private static String OUTPUT_1;
+ private static String[] CMD_1;
+ private static String OUTPUT_2;
+ private static String[] CMD_2;
+ private static final String FILE_1 = "file1.txt";
+ private static final String FILE_2 = "file2.txt";
+
+ private static void copyResults() throws Exception {
+ List<String> files = new ArrayList<>();
+ files.add(OUTPUT_1);
+ files.add(OUTPUT_2);
+ JPackageInstallerHelper.copyTestResults(files);
+ }
+
+ private static void testCreateInstaller() throws Exception {
+ JPackageHelper.executeCLI(true, CMD_1);
+ JPackageInstallerHelper.validateOutput(OUTPUT_1);
+ JPackageHelper.executeCLI(true, CMD_2);
+ JPackageInstallerHelper.validateOutput(OUTPUT_2);
+ copyResults();
+ }
+
+ private static void verifyInstall() throws Exception {
+ String app = JPackagePath.getWinInstalledApp(TEST_NAME);
+ JPackageInstallerHelper.validateApp(app);
+
+ String file1Path = JPackagePath.getWinInstalledAppFolder(TEST_NAME) + File.separator + FILE_1;
+ File file1 = new File(file1Path);
+ if (EXT.equals("msi")) {
+ if (file1.exists()) {
+ throw new AssertionError("Unexpected file does exist: "
+ + file1.getAbsolutePath());
+ }
+ } else if (EXT.equals("exe")) {
+ if (!file1.exists()) {
+ throw new AssertionError("Unexpected file does not exist: "
+ + file1.getAbsolutePath());
+ }
+ } else {
+ throw new AssertionError("Unknown installer type: " + EXT);
+ }
+
+ String file2Path = JPackagePath.getWinInstalledAppFolder(TEST_NAME) + File.separator + FILE_2;
+ File file2 = new File(file2Path);
+ if (!file2.exists()) {
+ throw new AssertionError("Unexpected file does not exist: "
+ + file2.getAbsolutePath());
+ }
+ }
+
+ private static void verifyUnInstall() throws Exception {
+ String folderPath = JPackagePath.getWinInstallFolder(TEST_NAME);
+ File folder = new File(folderPath);
+ if (folder.exists()) {
+ throw new AssertionError("Error: " + folder.getAbsolutePath() + " exist");
+ }
+ }
+
+ private static void init(String name, String ext) {
+ TEST_NAME = name;
+ EXT = ext;
+ OUTPUT_1 = "output" + File.separator + TEST_NAME + "-1.0." + EXT;
+ CMD_1 = new String[]{
+ "--package-type", EXT,
+ "--input", "input",
+ "--output", "output",
+ "--name", TEST_NAME,
+ "--main-jar", "hello.jar",
+ "--main-class", "Hello",
+ "--app-version", "1.0",
+ "--win-upgrade-uuid", "F0B18E75-52AD-41A2-BC86-6BE4FCD50BEB"};
+ OUTPUT_2 = "output" + File.separator + TEST_NAME + "-2.0." + EXT;
+ CMD_2 = new String[]{
+ "--package-type", EXT,
+ "--input", "input",
+ "--output", "output",
+ "--name", TEST_NAME,
+ "--main-jar", "hello.jar",
+ "--main-class", "Hello",
+ "--app-version", "2.0",
+ "--win-upgrade-uuid", "F0B18E75-52AD-41A2-BC86-6BE4FCD50BEB"};
+ }
+
+ private static void createInputFile(String name, String context) throws Exception {
+ try (PrintWriter out = new PrintWriter(
+ new BufferedWriter(new FileWriter("input" + File.separator + name)))) {
+ out.println(context);
+ }
+ }
+
+ public static void run(String name, String ext) throws Exception {
+ init(name, ext);
+
+ if (JPackageInstallerHelper.isVerifyInstall()) {
+ verifyInstall();
+ } else if (JPackageInstallerHelper.isVerifyUnInstall()) {
+ verifyUnInstall();
+ } else {
+ JPackageHelper.createHelloInstallerJar();
+ createInputFile(FILE_1, FILE_1);
+ createInputFile(FILE_2, FILE_2);
+ testCreateInstaller();
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/windows/exe/JPackageCreateInstallerFileAssociationsInstallDirTest.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 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.
+ *
+ * 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.
+ */
+
+/*
+ * @test
+ * @summary jpackage create installer test
+ * @library ../../../helpers
+ * @library ../base
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageInstallerHelper
+ * @build JPackageCreateInstallerFileAssociationsBase
+ * @requires (os.family == "windows")
+ * @modules jdk.jpackage
+ * @modules jdk.jpackage/jdk.jpackage.internal
+ * @run main/othervm -Xmx512m JPackageCreateInstallerFileAssociationsInstallDirTest
+ */
+public class JPackageCreateInstallerFileAssociationsInstallDirTest {
+ private static final String TEST_NAME = "JPackageCreateInstallerFileAssociationsInstallDirTest";
+ private static final String EXT = "exe";
+ private static final String INSTALL_DIR
+ = "TestVendor\\JPackageCreateInstallerFileAssociationsInstallDirTestDir";
+ private static final String TEST_EXT = "jptest3";
+
+ public static void main(String[] args) throws Exception {
+ if (jdk.jpackage.internal.WinMsiBundler.isSupported()) {
+ JPackageCreateInstallerFileAssociationsBase.run(TEST_NAME, EXT, INSTALL_DIR, TEST_EXT);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/windows/exe/JPackageCreateInstallerFileAssociationsTest.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,48 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+/*
+ * @test
+ * @summary jpackage create installer test
+ * @library ../../../helpers
+ * @library ../base
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageInstallerHelper
+ * @build JPackageCreateInstallerFileAssociationsBase
+ * @requires (os.family == "windows")
+ * @modules jdk.jpackage
+ * @modules jdk.jpackage/jdk.jpackage.internal
+ * @run main/othervm -Xmx512m JPackageCreateInstallerFileAssociationsTest
+ */
+public class JPackageCreateInstallerFileAssociationsTest {
+ private static final String TEST_NAME = "JPackageCreateInstallerFileAssociationsTest";
+ private static final String EXT = "exe";
+ private static final String TEST_EXT = "jptest1";
+
+ public static void main(String[] args) throws Exception {
+ if (jdk.jpackage.internal.WinMsiBundler.isSupported()) {
+ JPackageCreateInstallerFileAssociationsBase.run(TEST_NAME, EXT, null, TEST_EXT);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/windows/exe/JPackageCreateInstallerInstallDirTest.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 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.
+ *
+ * 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.
+ */
+
+/*
+ * @test
+ * @summary jpackage create installer install dir test
+ * @library ../../../helpers
+ * @library ../base
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageInstallerHelper
+ * @build JPackageCreateInstallerInstallDirBase
+ * @requires (os.family == "windows")
+ * @modules jdk.jpackage
+ * @modules jdk.jpackage/jdk.jpackage.internal
+ * @run main/othervm -Xmx512m JPackageCreateInstallerInstallDirTest
+ */
+public class JPackageCreateInstallerInstallDirTest {
+ private static final String TEST_NAME = "JPackageCreateInstallerInstallDirTest";
+ private static final String EXT = "exe";
+
+ public static void main(String[] args) throws Exception {
+ if (jdk.jpackage.internal.WinMsiBundler.isSupported()) {
+ JPackageCreateInstallerInstallDirBase.run(TEST_NAME, EXT);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/windows/exe/JPackageCreateInstallerLicenseTest.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,47 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+/*
+ * @test
+ * @summary jpackage create installer test
+ * @library ../../../helpers
+ * @library ../base
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageInstallerHelper
+ * @build JPackageCreateInstallerLicenseBase
+ * @requires (os.family == "windows")
+ * @modules jdk.jpackage
+ * @modules jdk.jpackage/jdk.jpackage.internal
+ * @run main/othervm -Xmx512m JPackageCreateInstallerLicenseTest
+ */
+public class JPackageCreateInstallerLicenseTest {
+ private static final String TEST_NAME = "JPackageCreateInstallerLicenseTest";
+ private static final String EXT = "exe";
+
+ public static void main(String[] args) throws Exception {
+ if (jdk.jpackage.internal.WinMsiBundler.isSupported()) {
+ JPackageCreateInstallerLicenseBase.run(TEST_NAME, EXT);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/windows/exe/JPackageCreateInstallerRuntimeTest.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,50 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+import java.lang.invoke.MethodHandles;
+
+/*
+ * @test
+ * @summary jpackage create installer test
+ * @library ../../../helpers
+ * @library ../base
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageInstallerHelper
+ * @build JPackageCreateInstallerRuntimeBase
+ * @requires (os.family == "windows")
+ * @modules jdk.jpackage
+ * @modules jdk.jpackage/jdk.jpackage.internal
+ * @run main/othervm -Xmx512m JPackageCreateInstallerRuntimeTest
+ */
+public class JPackageCreateInstallerRuntimeTest {
+
+ private static final String TEST_NAME = MethodHandles.lookup().lookupClass().getName();
+ private static final String EXT = "exe";
+
+ public static void main(String[] args) throws Exception {
+ if (jdk.jpackage.internal.WinMsiBundler.isSupported()) {
+ JPackageCreateInstallerRuntimeBase.run(TEST_NAME, EXT);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/windows/exe/JPackageCreateInstallerTest.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,47 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+/*
+ * @test
+ * @summary jpackage create installer test
+ * @library ../../../helpers
+ * @library ../base
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageInstallerHelper
+ * @build JPackageCreateInstallerBase
+ * @requires (os.family == "windows")
+ * @modules jdk.jpackage
+ * @modules jdk.jpackage/jdk.jpackage.internal
+ * @run main/othervm -Xmx512m JPackageCreateInstallerTest
+ */
+public class JPackageCreateInstallerTest {
+ private static final String TEST_NAME = "JPackageCreateInstallerTest";
+ private static final String EXT = "exe";
+
+ public static void main(String[] args) throws Exception {
+ if (jdk.jpackage.internal.WinMsiBundler.isSupported()) {
+ JPackageCreateInstallerBase.run(TEST_NAME, EXT);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/windows/exe/JPackageCreateInstallerWinDirChooserTest.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,47 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+/*
+ * @test
+ * @summary jpackage create installer test
+ * @library ../../../helpers
+ * @library ../base
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageInstallerHelper
+ * @build JPackageCreateInstallerWinDirChooserBase
+ * @requires (os.family == "windows")
+ * @modules jdk.jpackage
+ * @modules jdk.jpackage/jdk.jpackage.internal
+ * @run main/othervm -Xmx512m JPackageCreateInstallerWinDirChooserTest
+ */
+public class JPackageCreateInstallerWinDirChooserTest {
+ private static final String TEST_NAME = "JPackageCreateInstallerWinDirChooserTest";
+ private static final String EXT = "exe";
+
+ public static void main(String[] args) throws Exception {
+ if (jdk.jpackage.internal.WinMsiBundler.isSupported()) {
+ JPackageCreateInstallerWinDirChooserBase.run(TEST_NAME, EXT);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/windows/exe/JPackageCreateInstallerWinMenuGroupTest.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,47 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+/*
+ * @test
+ * @summary jpackage create installer test
+ * @library ../../../helpers
+ * @library ../base
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageInstallerHelper
+ * @build JPackageCreateInstallerWinMenuGroupBase
+ * @requires (os.family == "windows")
+ * @modules jdk.jpackage
+ * @modules jdk.jpackage/jdk.jpackage.internal
+ * @run main/othervm -Xmx512m JPackageCreateInstallerWinMenuGroupTest
+ */
+public class JPackageCreateInstallerWinMenuGroupTest {
+ private static final String TEST_NAME = "JPackageCreateInstallerWinMenuGroupTest";
+ private static final String EXT = "exe";
+
+ public static void main(String[] args) throws Exception {
+ if (jdk.jpackage.internal.WinMsiBundler.isSupported()) {
+ JPackageCreateInstallerWinMenuGroupBase.run(TEST_NAME, EXT);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/windows/exe/JPackageCreateInstallerWinMenuTest.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,47 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+/*
+ * @test
+ * @summary jpackage create installer test
+ * @library ../../../helpers
+ * @library ../base
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageInstallerHelper
+ * @build JPackageCreateInstallerWinMenuBase
+ * @requires (os.family == "windows")
+ * @modules jdk.jpackage
+ * @modules jdk.jpackage/jdk.jpackage.internal
+ * @run main/othervm -Xmx512m JPackageCreateInstallerWinMenuTest
+ */
+public class JPackageCreateInstallerWinMenuTest {
+ private static final String TEST_NAME = "JPackageCreateInstallerWinMenuTest";
+ private static final String EXT = "exe";
+
+ public static void main(String[] args) throws Exception {
+ if (jdk.jpackage.internal.WinMsiBundler.isSupported()) {
+ JPackageCreateInstallerWinMenuBase.run(TEST_NAME, EXT);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/windows/exe/JPackageCreateInstallerWinPerUserInstallTest.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,47 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+/*
+ * @test
+ * @summary jpackage create installer test
+ * @library ../../../helpers
+ * @library ../base
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageInstallerHelper
+ * @build JPackageCreateInstallerWinPerUserInstallBase
+ * @requires (os.family == "windows")
+ * @modules jdk.jpackage
+ * @modules jdk.jpackage/jdk.jpackage.internal
+ * @run main/othervm -Xmx512m JPackageCreateInstallerWinPerUserInstallTest
+ */
+public class JPackageCreateInstallerWinPerUserInstallTest {
+ private static final String TEST_NAME = "JPackageCreateInstallerWinPerUserInstallTest";
+ private static final String EXT = "exe";
+
+ public static void main(String[] args) throws Exception {
+ if (jdk.jpackage.internal.WinMsiBundler.isSupported()) {
+ JPackageCreateInstallerWinPerUserInstallBase.run(TEST_NAME, EXT);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/windows/exe/JPackageCreateInstallerWinRegistryNameTest.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,47 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+/*
+ * @test
+ * @summary jpackage create installer test
+ * @library ../../../helpers
+ * @library ../base
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageInstallerHelper
+ * @build JPackageCreateInstallerWinRegistryNameBase
+ * @requires (os.family == "windows")
+ * @modules jdk.jpackage
+ * @modules jdk.jpackage/jdk.jpackage.internal
+ * @run main/othervm -Xmx512m JPackageCreateInstallerWinRegistryNameTest
+ */
+public class JPackageCreateInstallerWinRegistryNameTest {
+ private static final String TEST_NAME = "JPackageCreateInstallerWinRegistryNameTest";
+ private static final String EXT = "exe";
+
+ public static void main(String[] args) throws Exception {
+ if (jdk.jpackage.internal.WinMsiBundler.isSupported()) {
+ JPackageCreateInstallerWinRegistryNameBase.run(TEST_NAME, EXT);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/windows/exe/JPackageCreateInstallerWinShortcutTest.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,47 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+/*
+ * @test
+ * @summary jpackage create installer test
+ * @library ../../../helpers
+ * @library ../base
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageInstallerHelper
+ * @build JPackageCreateInstallerWinShortcutBase
+ * @requires (os.family == "windows")
+ * @modules jdk.jpackage
+ * @modules jdk.jpackage/jdk.jpackage.internal
+ * @run main/othervm -Xmx512m JPackageCreateInstallerWinShortcutTest
+ */
+public class JPackageCreateInstallerWinShortcutTest {
+ private static final String TEST_NAME = "JPackageCreateInstallerWinShortcutTest";
+ private static final String EXT = "exe";
+
+ public static void main(String[] args) throws Exception {
+ if (jdk.jpackage.internal.WinMsiBundler.isSupported()) {
+ JPackageCreateInstallerWinShortcutBase.run(TEST_NAME, EXT);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/windows/exe/JPackageCreateInstallerWinUpgradeUUIDTest.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 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.
+ *
+ * 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.
+ */
+
+/*
+ * @test
+ * @summary jpackage create installer test
+ * @library ../../../helpers
+ * @library ../base
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageInstallerHelper
+ * @build JPackageCreateInstallerWinUpgradeUUIDBase
+ * @requires (os.family == "windows")
+ * @modules jdk.jpackage
+ * @modules jdk.jpackage/jdk.jpackage.internal
+ * @run main/othervm -Xmx512m JPackageCreateInstallerWinUpgradeUUIDTest
+ */
+public class JPackageCreateInstallerWinUpgradeUUIDTest {
+ private static final String TEST_NAME = "JPackageCreateInstallerWinUpgradeUUIDTest";
+ private static final String EXT = "exe";
+
+ public static void main(String[] args) throws Exception {
+ if (jdk.jpackage.internal.WinMsiBundler.isSupported()) {
+ JPackageCreateInstallerWinUpgradeUUIDBase.run(TEST_NAME, EXT);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/windows/exe/install.bat Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,17 @@
+JPackageCreateInstallerTest-1.0.exe
+JPackageCreateInstallerWinMenuTest-1.0.exe
+JPackageCreateInstallerWinMenuGroupTest-1.0.exe
+JPackageCreateInstallerWinPerUserInstallTest-1.0.exe
+JPackageCreateInstallerFileAssociationsTest-1.0.exe
+ECHO Make sure that installer asks to select installation location. Install to default one.
+JPackageCreateInstallerWinDirChooserTest-1.0.exe
+JPackageCreateInstallerWinRegistryNameTest-1.0.exe
+JPackageCreateInstallerWinShortcutTest-1.0.exe
+ECHO Make sure that installer shows license
+JPackageCreateInstallerLicenseTest-1.0.exe
+JPackageCreateInstallerWinUpgradeUUIDTest-1.0.exe
+JPackageCreateInstallerWinUpgradeUUIDTest-2.0.exe
+JPackageCreateInstallerInstallDirTest-1.0.exe
+JPackageCreateInstallerFileAssociationsInstallDirTest-1.0.exe
+JPackageCreateInstallerRuntimeTest-1.0.exe
+PAUSE
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/windows/exe/uninstall.bat Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,14 @@
+"%ProgramFiles%\JPackageCreateInstallerTest\unins000.exe"
+"%ProgramFiles%\JPackageCreateInstallerWinMenuTest\unins000.exe"
+"%ProgramFiles%\JPackageCreateInstallerWinMenuGroupTest\unins000.exe"
+"%localappdata%\JPackageCreateInstallerWinPerUserInstallTest\unins000.exe"
+"%ProgramFiles%\JPackageCreateInstallerFileAssociationsTest\unins000.exe"
+"%ProgramFiles%\JPackageCreateInstallerWinDirChooserTest\unins000.exe"
+"%ProgramFiles%\JPackageCreateInstallerWinRegistryNameTest\unins000.exe"
+"%ProgramFiles%\JPackageCreateInstallerWinShortcutTest\unins000.exe"
+"%ProgramFiles%\JPackageCreateInstallerLicenseTest\unins000.exe"
+"%ProgramFiles%\JPackageCreateInstallerWinUpgradeUUIDTest\unins000.exe"
+"%ProgramFiles%\TestVendor\JPackageCreateInstallerInstallDirTestDir\unins000.exe"
+"%ProgramFiles%\TestVendor\JPackageCreateInstallerFileAssociationsInstallDirTestDir\unins000.exe"
+"%ProgramFiles%\JPackageCreateInstallerRuntimeTest\unins000.exe"
+PAUSE
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/windows/msi/JPackageCreateInstallerFileAssociationsInstallDirTest.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 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.
+ *
+ * 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.
+ */
+
+/*
+ * @test
+ * @summary jpackage create installer test
+ * @library ../../../helpers
+ * @library ../base
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageInstallerHelper
+ * @build JPackageCreateInstallerFileAssociationsBase
+ * @requires (os.family == "windows")
+ * @modules jdk.jpackage
+ * @modules jdk.jpackage/jdk.jpackage.internal
+ * @run main/othervm -Xmx512m JPackageCreateInstallerFileAssociationsInstallDirTest
+ */
+public class JPackageCreateInstallerFileAssociationsInstallDirTest {
+ private static final String TEST_NAME = "JPackageCreateInstallerFileAssociationsInstallDirTest";
+ private static final String EXT = "msi";
+ private static final String INSTALL_DIR
+ = "TestVendor\\JPackageCreateInstallerFileAssociationsInstallDirTestDir";
+ private static final String TEST_EXT = "jptest3";
+
+ public static void main(String[] args) throws Exception {
+ if (jdk.jpackage.internal.WinMsiBundler.isSupported()) {
+ JPackageCreateInstallerFileAssociationsBase.run(TEST_NAME,
+ EXT, INSTALL_DIR, TEST_EXT);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/windows/msi/JPackageCreateInstallerFileAssociationsTest.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+/*
+ * @test
+ * @summary jpackage create installer test
+ * @library ../../../helpers
+ * @library ../base
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageInstallerHelper
+ * @build JPackageCreateInstallerFileAssociationsBase
+ * @requires (os.family == "windows")
+ * @modules jdk.jpackage
+ * @modules jdk.jpackage/jdk.jpackage.internal
+ * @run main/othervm -Xmx512m JPackageCreateInstallerFileAssociationsTest
+ */
+public class JPackageCreateInstallerFileAssociationsTest {
+ private static final String TEST_NAME = "JPackageCreateInstallerFileAssociationsTest";
+ private static final String EXT = "msi";
+ private static final String TEST_EXT = "jptest1";
+
+ public static void main(String[] args) throws Exception {
+ if (jdk.jpackage.internal.WinMsiBundler.isSupported()) {
+ JPackageCreateInstallerFileAssociationsBase.run(TEST_NAME,
+ EXT, null, TEST_EXT);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/windows/msi/JPackageCreateInstallerInstallDirTest.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 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.
+ *
+ * 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.
+ */
+
+/*
+ * @test
+ * @summary jpackage create installer install dir test
+ * @library ../../../helpers
+ * @library ../base
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageInstallerHelper
+ * @build JPackageCreateInstallerInstallDirBase
+ * @requires (os.family == "windows")
+ * @modules jdk.jpackage
+ * @modules jdk.jpackage/jdk.jpackage.internal
+ * @run main/othervm -Xmx512m JPackageCreateInstallerInstallDirTest
+ */
+public class JPackageCreateInstallerInstallDirTest {
+ private static final String TEST_NAME = "JPackageCreateInstallerInstallDirTest";
+ private static final String EXT = "msi";
+
+ public static void main(String[] args) throws Exception {
+ if (jdk.jpackage.internal.WinMsiBundler.isSupported()) {
+ JPackageCreateInstallerInstallDirBase.run(TEST_NAME, EXT);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/windows/msi/JPackageCreateInstallerLicenseTest.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,47 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+/*
+ * @test
+ * @summary jpackage create installer test
+ * @library ../../../helpers
+ * @library ../base
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageInstallerHelper
+ * @build JPackageCreateInstallerLicenseBase
+ * @requires (os.family == "windows")
+ * @modules jdk.jpackage
+ * @modules jdk.jpackage/jdk.jpackage.internal
+ * @run main/othervm -Xmx512m JPackageCreateInstallerLicenseTest
+ */
+public class JPackageCreateInstallerLicenseTest {
+ private static final String TEST_NAME = "JPackageCreateInstallerLicenseTest";
+ private static final String EXT = "msi";
+
+ public static void main(String[] args) throws Exception {
+ if (jdk.jpackage.internal.WinMsiBundler.isSupported()) {
+ JPackageCreateInstallerLicenseBase.run(TEST_NAME, EXT);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/windows/msi/JPackageCreateInstallerRuntimeTest.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,50 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+import java.lang.invoke.MethodHandles;
+
+/*
+ * @test
+ * @summary jpackage create installer test
+ * @library ../../../helpers
+ * @library ../base
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageInstallerHelper
+ * @build JPackageCreateInstallerRuntimeBase
+ * @requires (os.family == "windows")
+ * @modules jdk.jpackage
+ * @modules jdk.jpackage/jdk.jpackage.internal
+ * @run main/othervm -Xmx512m JPackageCreateInstallerRuntimeTest
+ */
+public class JPackageCreateInstallerRuntimeTest {
+
+ private static final String TEST_NAME = MethodHandles.lookup().lookupClass().getName();
+ private static final String EXT = "msi";
+
+ public static void main(String[] args) throws Exception {
+ if (jdk.jpackage.internal.WinMsiBundler.isSupported()) {
+ JPackageCreateInstallerRuntimeBase.run(TEST_NAME, EXT);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/windows/msi/JPackageCreateInstallerTest.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,47 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+/*
+ * @test
+ * @summary jpackage create installer test
+ * @library ../../../helpers
+ * @library ../base
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageInstallerHelper
+ * @build JPackageCreateInstallerBase
+ * @requires (os.family == "windows")
+ * @modules jdk.jpackage
+ * @modules jdk.jpackage/jdk.jpackage.internal
+ * @run main/othervm -Xmx512m JPackageCreateInstallerTest
+ */
+public class JPackageCreateInstallerTest {
+ private static final String TEST_NAME = "JPackageCreateInstallerTest";
+ private static final String EXT = "msi";
+
+ public static void main(String[] args) throws Exception {
+ if (jdk.jpackage.internal.WinMsiBundler.isSupported()) {
+ JPackageCreateInstallerBase.run(TEST_NAME, EXT);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/windows/msi/JPackageCreateInstallerWinDirChooserTest.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,47 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+/*
+ * @test
+ * @summary jpackage create installer test
+ * @library ../../../helpers
+ * @library ../base
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageInstallerHelper
+ * @build JPackageCreateInstallerWinDirChooserBase
+ * @requires (os.family == "windows")
+ * @modules jdk.jpackage
+ * @modules jdk.jpackage/jdk.jpackage.internal
+ * @run main/othervm -Xmx512m JPackageCreateInstallerWinDirChooserTest
+ */
+public class JPackageCreateInstallerWinDirChooserTest {
+ private static final String TEST_NAME = "JPackageCreateInstallerWinDirChooserTest";
+ private static final String EXT = "msi";
+
+ public static void main(String[] args) throws Exception {
+ if (jdk.jpackage.internal.WinMsiBundler.isSupported()) {
+ JPackageCreateInstallerWinDirChooserBase.run(TEST_NAME, EXT);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/windows/msi/JPackageCreateInstallerWinMenuGroupTest.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,47 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+/*
+ * @test
+ * @summary jpackage create installer test
+ * @library ../../../helpers
+ * @library ../base
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageInstallerHelper
+ * @build JPackageCreateInstallerWinMenuGroupBase
+ * @requires (os.family == "windows")
+ * @modules jdk.jpackage
+ * @modules jdk.jpackage/jdk.jpackage.internal
+ * @run main/othervm -Xmx512m JPackageCreateInstallerWinMenuGroupTest
+ */
+public class JPackageCreateInstallerWinMenuGroupTest {
+ private static final String TEST_NAME = "JPackageCreateInstallerWinMenuGroupTest";
+ private static final String EXT = "msi";
+
+ public static void main(String[] args) throws Exception {
+ if (jdk.jpackage.internal.WinMsiBundler.isSupported()) {
+ JPackageCreateInstallerWinMenuGroupBase.run(TEST_NAME, EXT);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/windows/msi/JPackageCreateInstallerWinMenuTest.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,47 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+/*
+ * @test
+ * @summary jpackage create installer test
+ * @library ../../../helpers
+ * @library ../base
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageInstallerHelper
+ * @build JPackageCreateInstallerWinMenuBase
+ * @requires (os.family == "windows")
+ * @modules jdk.jpackage
+ * @modules jdk.jpackage/jdk.jpackage.internal
+ * @run main/othervm -Xmx512m JPackageCreateInstallerWinMenuTest
+ */
+public class JPackageCreateInstallerWinMenuTest {
+ private static final String TEST_NAME = "JPackageCreateInstallerWinMenuTest";
+ private static final String EXT = "msi";
+
+ public static void main(String[] args) throws Exception {
+ if (jdk.jpackage.internal.WinMsiBundler.isSupported()) {
+ JPackageCreateInstallerWinMenuBase.run(TEST_NAME, EXT);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/windows/msi/JPackageCreateInstallerWinPerUserInstallTest.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,47 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+/*
+ * @test
+ * @summary jpackage create installer test
+ * @library ../../../helpers
+ * @library ../base
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageInstallerHelper
+ * @build JPackageCreateInstallerWinPerUserInstallBase
+ * @requires (os.family == "windows")
+ * @modules jdk.jpackage
+ * @modules jdk.jpackage/jdk.jpackage.internal
+ * @run main/othervm -Xmx512m JPackageCreateInstallerWinPerUserInstallTest
+ */
+public class JPackageCreateInstallerWinPerUserInstallTest {
+ private static final String TEST_NAME = "JPackageCreateInstallerWinPerUserInstallTest";
+ private static final String EXT = "msi";
+
+ public static void main(String[] args) throws Exception {
+ if (jdk.jpackage.internal.WinMsiBundler.isSupported()) {
+ JPackageCreateInstallerWinPerUserInstallBase.run(TEST_NAME, EXT);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/windows/msi/JPackageCreateInstallerWinRegistryNameTest.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,47 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+/*
+ * @test
+ * @summary jpackage create installer test
+ * @library ../../../helpers
+ * @library ../base
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageInstallerHelper
+ * @build JPackageCreateInstallerWinRegistryNameBase
+ * @requires (os.family == "windows")
+ * @modules jdk.jpackage
+ * @modules jdk.jpackage/jdk.jpackage.internal
+ * @run main/othervm -Xmx512m JPackageCreateInstallerWinRegistryNameTest
+ */
+public class JPackageCreateInstallerWinRegistryNameTest {
+ private static final String TEST_NAME = "JPackageCreateInstallerWinRegistryNameTest";
+ private static final String EXT = "msi";
+
+ public static void main(String[] args) throws Exception {
+ if (jdk.jpackage.internal.WinMsiBundler.isSupported()) {
+ JPackageCreateInstallerWinRegistryNameBase.run(TEST_NAME, EXT);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/windows/msi/JPackageCreateInstallerWinShortcutTest.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,47 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+/*
+ * @test
+ * @summary jpackage create installer test
+ * @library ../../../helpers
+ * @library ../base
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageInstallerHelper
+ * @build JPackageCreateInstallerWinShortcutBase
+ * @requires (os.family == "windows")
+ * @modules jdk.jpackage
+ * @modules jdk.jpackage/jdk.jpackage.internal
+ * @run main/othervm -Xmx512m JPackageCreateInstallerWinShortcutTest
+ */
+public class JPackageCreateInstallerWinShortcutTest {
+ private static final String TEST_NAME = "JPackageCreateInstallerWinShortcutTest";
+ private static final String EXT = "msi";
+
+ public static void main(String[] args) throws Exception {
+ if (jdk.jpackage.internal.WinMsiBundler.isSupported()) {
+ JPackageCreateInstallerWinShortcutBase.run(TEST_NAME, EXT);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/windows/msi/JPackageCreateInstallerWinUpgradeUUIDTest.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 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.
+ *
+ * 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.
+ */
+
+/*
+ * @test
+ * @summary jpackage create installer test
+ * @library ../../../helpers
+ * @library ../base
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageInstallerHelper
+ * @build JPackageCreateInstallerWinUpgradeUUIDBase
+ * @requires (os.family == "windows")
+ * @modules jdk.jpackage
+ * @modules jdk.jpackage/jdk.jpackage.internal
+ * @run main/othervm -Xmx512m JPackageCreateInstallerWinUpgradeUUIDTest
+ */
+public class JPackageCreateInstallerWinUpgradeUUIDTest {
+ private static final String TEST_NAME = "JPackageCreateInstallerWinUpgradeUUIDTest";
+ private static final String EXT = "msi";
+
+ public static void main(String[] args) throws Exception {
+ if (jdk.jpackage.internal.WinMsiBundler.isSupported()) {
+ JPackageCreateInstallerWinUpgradeUUIDBase.run(TEST_NAME, EXT);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/windows/msi/install.bat Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,17 @@
+JPackageCreateInstallerTest-1.0.msi
+JPackageCreateInstallerWinMenuTest-1.0.msi
+JPackageCreateInstallerWinMenuGroupTest-1.0.msi
+JPackageCreateInstallerWinPerUserInstallTest-1.0.msi
+JPackageCreateInstallerFileAssociationsTest-1.0.msi
+ECHO Make sure that installer asks to select installation location. Install to default one.
+JPackageCreateInstallerWinDirChooserTest-1.0.msi
+JPackageCreateInstallerWinRegistryNameTest-1.0.msi
+JPackageCreateInstallerWinShortcutTest-1.0.msi
+ECHO Make sure that installer shows license
+JPackageCreateInstallerLicenseTest-1.0.msi
+JPackageCreateInstallerWinUpgradeUUIDTest-1.0.msi
+JPackageCreateInstallerWinUpgradeUUIDTest-2.0.msi
+JPackageCreateInstallerInstallDirTest-1.0.msi
+JPackageCreateInstallerFileAssociationsInstallDirTest-1.0.msi
+JPackageCreateInstallerRuntimeTest-1.0.msi
+PAUSE
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/windows/msi/uninstall.bat Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,14 @@
+MSIEXEC /uninstall JPackageCreateInstallerTest-1.0.msi
+MSIEXEC /uninstall JPackageCreateInstallerWinMenuTest-1.0.msi
+MSIEXEC /uninstall JPackageCreateInstallerWinMenuGroupTest-1.0.msi
+MSIEXEC /uninstall JPackageCreateInstallerWinPerUserInstallTest-1.0.msi
+MSIEXEC /uninstall JPackageCreateInstallerFileAssociationsTest-1.0.msi
+MSIEXEC /uninstall JPackageCreateInstallerWinDirChooserTest-1.0.msi
+MSIEXEC /uninstall JPackageCreateInstallerWinRegistryNameTest-1.0.msi
+MSIEXEC /uninstall JPackageCreateInstallerWinShortcutTest-1.0.msi
+MSIEXEC /uninstall JPackageCreateInstallerLicenseTest-1.0.msi
+MSIEXEC /uninstall JPackageCreateInstallerWinUpgradeUUIDTest-2.0.msi
+MSIEXEC /uninstall JPackageCreateInstallerInstallDirTest-1.0.msi
+MSIEXEC /uninstall JPackageCreateInstallerFileAssociationsInstallDirTest-1.0.msi
+MSIEXEC /uninstall JPackageCreateInstallerRuntimeTest-1.0.msi
+PAUSE
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/helpers/JPackageHelper.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,657 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.io.BufferedWriter;
+import java.nio.file.FileVisitResult;
+
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.SimpleFileVisitor;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Arrays;
+
+import java.util.spi.ToolProvider;
+
+public class JPackageHelper {
+
+ private static final boolean VERBOSE = false;
+ private static final String OS = System.getProperty("os.name").toLowerCase();
+ private static final String JAVA_HOME = System.getProperty("java.home");
+ public static final String TEST_SRC_ROOT;
+ public static final String TEST_SRC;
+ private static final Path BIN_DIR = Path.of(JAVA_HOME, "bin");
+ private static final Path JPACKAGE;
+ private static final Path JAVAC;
+ private static final Path JAR;
+ private static final Path JLINK;
+
+ static {
+ if (OS.startsWith("win")) {
+ JPACKAGE = BIN_DIR.resolve("jpackage.exe");
+ JAVAC = BIN_DIR.resolve("javac.exe");
+ JAR = BIN_DIR.resolve("jar.exe");
+ JLINK = BIN_DIR.resolve("jlink.exe");
+ } else {
+ JPACKAGE = BIN_DIR.resolve("jpackage");
+ JAVAC = BIN_DIR.resolve("javac");
+ JAR = BIN_DIR.resolve("jar");
+ JLINK = BIN_DIR.resolve("jlink");
+ }
+
+ // Figure out test src based on where we called
+ TEST_SRC = System.getProperty("test.src");
+ Path root = Path.of(TEST_SRC);
+ Path apps = Path.of(TEST_SRC, "apps");
+ if (apps.toFile().exists()) {
+ // fine - test is at root
+ } else {
+ apps = Path.of(TEST_SRC, "..", "apps");
+ if (apps.toFile().exists()) {
+ root = apps.getParent().normalize(); // test is 1 level down
+ } else {
+ apps = Path.of(TEST_SRC, "..", "..", "apps");
+ if (apps.toFile().exists()) {
+ root = apps.getParent().normalize(); // 2 levels down
+ } else {
+ apps = Path.of(TEST_SRC, "..", "..", "..", "apps");
+ if (apps.toFile().exists()) {
+ root = apps.getParent().normalize(); // 3 levels down
+ } else {
+ // if we ever have tests more than three levels
+ // down we need to add code here
+ throw new RuntimeException("we should never get here");
+ }
+ }
+ }
+ }
+ TEST_SRC_ROOT = root.toString();
+ }
+
+ static final ToolProvider JPACKAGE_TOOL =
+ ToolProvider.findFirst("jpackage").orElseThrow(
+ () -> new RuntimeException("jpackage tool not found"));
+
+ public static int execute(File out, String... command) throws Exception {
+ if (VERBOSE) {
+ System.out.print("Execute command: ");
+ for (String c : command) {
+ System.out.print(c);
+ System.out.print(" ");
+ }
+ System.out.println();
+ }
+
+ ProcessBuilder builder = new ProcessBuilder(command);
+ if (out != null) {
+ builder.redirectErrorStream(true);
+ builder.redirectOutput(out);
+ }
+
+ Process process = builder.start();
+ return process.waitFor();
+ }
+
+ public static Process executeNoWait(File out, String... command) throws Exception {
+ if (VERBOSE) {
+ System.out.print("Execute command: ");
+ for (String c : command) {
+ System.out.print(c);
+ System.out.print(" ");
+ }
+ System.out.println();
+ }
+
+ ProcessBuilder builder = new ProcessBuilder(command);
+ if (out != null) {
+ builder.redirectErrorStream(true);
+ builder.redirectOutput(out);
+ }
+
+ return builder.start();
+ }
+
+ private static String[] getCommand(String... args) {
+ String[] command;
+ if (args == null) {
+ command = new String[1];
+ } else {
+ command = new String[args.length + 1];
+ }
+
+ int index = 0;
+ command[index] = JPACKAGE.toString();
+
+ if (args != null) {
+ for (String arg : args) {
+ index++;
+ command[index] = arg;
+ }
+ }
+
+ return command;
+ }
+
+ public static void deleteRecursive(File path) throws IOException {
+ if (!path.exists()) {
+ return;
+ }
+
+ Path directory = path.toPath();
+ Files.walkFileTree(directory, new SimpleFileVisitor<Path>() {
+ @Override
+ public FileVisitResult visitFile(Path file,
+ BasicFileAttributes attr) throws IOException {
+ file.toFile().setWritable(true);
+ if (OS.startsWith("win")) {
+ try {
+ Files.setAttribute(file, "dos:readonly", false);
+ } catch (Exception ioe) {
+ // just report and try to contune
+ System.err.println("IOException: " + ioe);
+ ioe.printStackTrace(System.err);
+ }
+ }
+ Files.delete(file);
+ return FileVisitResult.CONTINUE;
+ }
+
+ @Override
+ public FileVisitResult preVisitDirectory(Path dir,
+ BasicFileAttributes attr) throws IOException {
+ if (OS.startsWith("win")) {
+ Files.setAttribute(dir, "dos:readonly", false);
+ }
+ return FileVisitResult.CONTINUE;
+ }
+
+ @Override
+ public FileVisitResult postVisitDirectory(Path dir, IOException e)
+ throws IOException {
+ Files.delete(dir);
+ return FileVisitResult.CONTINUE;
+ }
+ });
+ }
+
+ public static void deleteOutputFolder(String output) throws IOException {
+ File outputFolder = new File(output);
+ System.out.println("deleteOutputFolder: " + outputFolder.getAbsolutePath());
+ try {
+ deleteRecursive(outputFolder);
+ } catch (IOException ioe) {
+ System.err.println("IOException: " + ioe);
+ ioe.printStackTrace(System.err);
+ deleteRecursive(outputFolder);
+ }
+ }
+
+ public static String executeCLI(boolean retValZero, String... args) throws Exception {
+ int retVal;
+ File outfile = new File("output.log");
+ String[] command = getCommand(args);
+ try {
+ retVal = execute(outfile, command);
+ } catch (Exception ex) {
+ if (outfile.exists()) {
+ System.err.println(Files.readString(outfile.toPath()));
+ }
+ throw ex;
+ }
+
+ String output = Files.readString(outfile.toPath());
+ if (retValZero) {
+ if (retVal != 0) {
+ System.err.println("command run:");
+ for (String s : command) { System.err.println(s); }
+ System.err.println("command output:");
+ System.err.println(output);
+ throw new AssertionError("jpackage exited with error: " + retVal);
+ }
+ } else {
+ if (retVal == 0) {
+ System.err.println(output);
+ throw new AssertionError("jpackage exited without error: " + retVal);
+ }
+ }
+
+ if (VERBOSE) {
+ System.out.println("output =");
+ System.out.println(output);
+ }
+
+ return output;
+ }
+
+ public static String executeToolProvider(boolean retValZero, String... args) throws Exception {
+ StringWriter writer = new StringWriter();
+ PrintWriter pw = new PrintWriter(writer);
+ int retVal = JPACKAGE_TOOL.run(pw, pw, args);
+ String output = writer.toString();
+
+ if (retValZero) {
+ if (retVal != 0) {
+ System.err.println(output);
+ throw new AssertionError("jpackage exited with error: " + retVal);
+ }
+ } else {
+ if (retVal == 0) {
+ System.err.println(output);
+ throw new AssertionError("jpackage exited without error");
+ }
+ }
+
+ if (VERBOSE) {
+ System.out.println("output =");
+ System.out.println(output);
+ }
+
+ return output;
+ }
+
+ public static boolean isWindows() {
+ return (OS.contains("win"));
+ }
+
+ public static boolean isOSX() {
+ return (OS.contains("mac"));
+ }
+
+ public static boolean isLinux() {
+ return ((OS.contains("nix") || OS.contains("nux")));
+ }
+
+ public static void createHelloImageJar(String inputDir) throws Exception {
+ createJar(false, "Hello", "image", inputDir);
+ }
+
+ public static void createHelloImageJar() throws Exception {
+ createJar(false, "Hello", "image", "input");
+ }
+
+ public static void createHelloImageJarWithMainClass() throws Exception {
+ createJar(true, "Hello", "image", "input");
+ }
+
+ public static void createHelloInstallerJar() throws Exception {
+ createJar(false, "Hello", "installer", "input");
+ }
+
+ public static void createHelloInstallerJarWithMainClass() throws Exception {
+ createJar(true, "Hello", "installer", "input");
+ }
+
+ private static void createJar(boolean mainClassAttribute, String name,
+ String testType, String inputDir) throws Exception {
+ int retVal;
+
+ File input = new File(inputDir);
+ if (!input.exists()) {
+ input.mkdirs();
+ }
+
+ Path src = Path.of(TEST_SRC_ROOT + File.separator + "apps"
+ + File.separator + testType + File.separator + name + ".java");
+ Path dst = Path.of(name + ".java");
+
+ if (dst.toFile().exists()) {
+ Files.delete(dst);
+ }
+ Files.copy(src, dst);
+
+
+ File javacLog = new File("javac.log");
+ try {
+ retVal = execute(javacLog, JAVAC.toString(), name + ".java");
+ } catch (Exception ex) {
+ if (javacLog.exists()) {
+ System.err.println(Files.readString(javacLog.toPath()));
+ }
+ throw ex;
+ }
+
+ if (retVal != 0) {
+ if (javacLog.exists()) {
+ System.err.println(Files.readString(javacLog.toPath()));
+ }
+ throw new AssertionError("javac exited with error: " + retVal);
+ }
+
+ File jarLog = new File("jar.log");
+ try {
+ List<String> args = new ArrayList<>();
+ args.add(JAR.toString());
+ args.add("-c");
+ args.add("-v");
+ args.add("-f");
+ args.add(inputDir + File.separator + name.toLowerCase() + ".jar");
+ if (mainClassAttribute) {
+ args.add("-e");
+ args.add(name);
+ }
+ args.add(name + ".class");
+ retVal = execute(jarLog, args.stream().toArray(String[]::new));
+ } catch (Exception ex) {
+ if (jarLog.exists()) {
+ System.err.println(Files.readString(jarLog.toPath()));
+ }
+ throw ex;
+ }
+
+ if (retVal != 0) {
+ if (jarLog.exists()) {
+ System.err.println(Files.readString(jarLog.toPath()));
+ }
+ throw new AssertionError("jar exited with error: " + retVal);
+ }
+ }
+
+ public static void createHelloModule() throws Exception {
+ createModule("Hello.java", "input", "hello", true);
+ }
+
+ public static void createOtherModule() throws Exception {
+ createModule("Other.java", "input-other", "other", false);
+ }
+
+ private static void createModule(String javaFile, String inputDir,
+ String aName, boolean createModularJar) throws Exception {
+ int retVal;
+
+ File input = new File(inputDir);
+ if (!input.exists()) {
+ input.mkdir();
+ }
+
+ File module = new File("module" + File.separator + "com." + aName);
+ if (!module.exists()) {
+ module.mkdirs();
+ }
+
+ File javacLog = new File("javac.log");
+ try {
+ List<String> args = new ArrayList<>();
+ args.add(JAVAC.toString());
+ args.add("-d");
+ args.add("module" + File.separator + "com." + aName);
+ args.add(TEST_SRC_ROOT + File.separator + "apps" + File.separator
+ + "com." + aName + File.separator + "module-info.java");
+ args.add(TEST_SRC_ROOT + File.separator + "apps"
+ + File.separator + "com." + aName + File.separator + "com"
+ + File.separator + aName + File.separator + javaFile);
+ retVal = execute(javacLog, args.stream().toArray(String[]::new));
+ } catch (Exception ex) {
+ if (javacLog.exists()) {
+ System.err.println(Files.readString(javacLog.toPath()));
+ }
+ throw ex;
+ }
+
+ if (retVal != 0) {
+ if (javacLog.exists()) {
+ System.err.println(Files.readString(javacLog.toPath()));
+ }
+ throw new AssertionError("javac exited with error: " + retVal);
+ }
+
+ if (createModularJar) {
+ File jarLog = new File("jar.log");
+ try {
+ List<String> args = new ArrayList<>();
+ args.add(JAR.toString());
+ args.add("--create");
+ args.add("--file");
+ args.add(inputDir + File.separator + "com." + aName + ".jar");
+ args.add("-C");
+ args.add("module" + File.separator + "com." + aName);
+ args.add(".");
+
+ retVal = execute(jarLog, args.stream().toArray(String[]::new));
+ } catch (Exception ex) {
+ if (jarLog.exists()) {
+ System.err.println(Files.readString(jarLog.toPath()));
+ }
+ throw ex;
+ }
+
+ if (retVal != 0) {
+ if (jarLog.exists()) {
+ System.err.println(Files.readString(jarLog.toPath()));
+ }
+ throw new AssertionError("jar exited with error: " + retVal);
+ }
+ }
+ }
+
+ public static void createRuntime() throws Exception {
+ List<String> moreArgs = new ArrayList<>();
+ createRuntime(moreArgs);
+ }
+
+ public static void createRuntime(List<String> moreArgs) throws Exception {
+ int retVal;
+
+ File jlinkLog = new File("jlink.log");
+ try {
+ List<String> args = new ArrayList<>();
+ args.add(JLINK.toString());
+ args.add("--output");
+ args.add("runtime");
+ args.add("--add-modules");
+ args.add("java.base");
+ args.addAll(moreArgs);
+
+ retVal = execute(jlinkLog, args.stream().toArray(String[]::new));
+ } catch (Exception ex) {
+ if (jlinkLog.exists()) {
+ System.err.println(Files.readString(jlinkLog.toPath()));
+ }
+ throw ex;
+ }
+
+ if (retVal != 0) {
+ if (jlinkLog.exists()) {
+ System.err.println(Files.readString(jlinkLog.toPath()));
+ }
+ throw new AssertionError("jlink exited with error: " + retVal);
+ }
+ }
+
+ public static String listToArgumentsMap(List<String> arguments, boolean toolProvider) {
+ if (arguments.isEmpty()) {
+ return "";
+ }
+
+ String argsStr = "";
+ for (int i = 0; i < arguments.size(); i++) {
+ String arg = arguments.get(i);
+ argsStr += quote(arg, toolProvider);
+ if ((i + 1) != arguments.size()) {
+ argsStr += " ";
+ }
+ }
+
+ if (!toolProvider && isWindows()) {
+ if (argsStr.contains(" ")) {
+ if (argsStr.contains("\"")) {
+ argsStr = escapeQuote(argsStr, toolProvider);
+ }
+ argsStr = "\"" + argsStr + "\"";
+ }
+ }
+ return argsStr;
+ }
+
+ public static String[] cmdWithAtFilename(String [] cmd, int ndx, int len)
+ throws IOException {
+ ArrayList<String> newAList = new ArrayList<>();
+ String fileString = null;
+ for (int i=0; i<cmd.length; i++) {
+ if (i == ndx) {
+ newAList.add("@argfile.cmds");
+ fileString = cmd[i];
+ } else if (i > ndx && i < ndx + len) {
+ fileString += " " + cmd[i];
+ } else {
+ newAList.add(cmd[i]);
+ }
+ }
+ if (fileString != null) {
+ Path path = new File("argfile.cmds").toPath();
+ try (BufferedWriter bw = Files.newBufferedWriter(path);
+ PrintWriter out = new PrintWriter(bw)) {
+ out.println(fileString);
+ }
+ }
+ return newAList.toArray(new String[0]);
+ }
+
+ public static String [] splitAndFilter(String output) {
+ if (output == null) {
+ return null;
+ }
+
+ String[] result = output.split("\n");
+ if (result == null || result.length == 0) {
+ return result;
+ }
+
+ List<String> origList = new ArrayList(Arrays.asList(result));
+ List<String> newlist = new ArrayList();
+ origList.stream().filter((str) ->
+ (!str.startsWith("Picked up"))).forEachOrdered((str) -> {
+ newlist.add(str);
+ });
+
+ return newlist.toArray(new String[newlist.size()]);
+ }
+
+ private static String quote(String in, boolean toolProvider) {
+ if (in == null) {
+ return null;
+ }
+
+ if (in.isEmpty()) {
+ return "";
+ }
+
+ if (!in.contains("=")) {
+ // Not a property
+ if (in.contains(" ")) {
+ in = escapeQuote(in, toolProvider);
+ return "\"" + in + "\"";
+ }
+ return in;
+ }
+
+ if (!in.contains(" ")) {
+ return in; // No need to quote
+ }
+
+ int paramIndex = in.indexOf("=");
+ if (paramIndex <= 0) {
+ return in; // Something wrong, just skip quoting
+ }
+
+ String param = in.substring(0, paramIndex);
+ String value = in.substring(paramIndex + 1);
+
+ if (value.length() == 0) {
+ return in; // No need to quote
+ }
+
+ value = escapeQuote(value, toolProvider);
+
+ return param + "=" + "\"" + value + "\"";
+ }
+
+ private static String escapeQuote(String in, boolean toolProvider) {
+ if (in == null) {
+ return null;
+ }
+
+ if (in.isEmpty()) {
+ return "";
+ }
+
+ if (in.contains("\"")) {
+ // Use code points to preserve non-ASCII chars
+ StringBuilder sb = new StringBuilder();
+ int codeLen = in.codePointCount(0, in.length());
+ for (int i = 0; i < codeLen; i++) {
+ int code = in.codePointAt(i);
+ // Note: No need to escape '\' on Linux or OS X
+ // jpackage expects us to pass arguments and properties with
+ // quotes and spaces as a map
+ // with quotes being escaped with additional \ for
+ // internal quotes.
+ // So if we want two properties below:
+ // -Djnlp.Prop1=Some "Value" 1
+ // -Djnlp.Prop2=Some Value 2
+ // jpackage will need:
+ // "-Djnlp.Prop1=\"Some \\"Value\\" 1\" -Djnlp.Prop2=\"Some Value 2\""
+ // but since we using ProcessBuilder to run jpackage we will need to escape
+ // our escape symbols as well, so we will need to pass string below to ProcessBuilder:
+ // "-Djnlp.Prop1=\\\"Some \\\\\\\"Value\\\\\\\" 1\\\" -Djnlp.Prop2=\\\"Some Value 2\\\""
+ switch (code) {
+ case '"':
+ // " -> \" -> \\\"
+ if (i == 0 || in.codePointAt(i - 1) != '\\') {
+ sb.appendCodePoint('\\');
+ sb.appendCodePoint(code);
+ }
+ break;
+ case '\\':
+ // We need to escape already escaped symbols as well
+ if ((i + 1) < codeLen) {
+ int nextCode = in.codePointAt(i + 1);
+ if (nextCode == '"') {
+ // \" -> \\\"
+ sb.appendCodePoint('\\');
+ sb.appendCodePoint('\\');
+ sb.appendCodePoint('\\');
+ sb.appendCodePoint(nextCode);
+ } else {
+ sb.appendCodePoint('\\');
+ sb.appendCodePoint(code);
+ }
+ } else {
+ sb.appendCodePoint(code);
+ }
+ break;
+ default:
+ sb.appendCodePoint(code);
+ break;
+ }
+ }
+ return sb.toString();
+ }
+
+ return in;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/helpers/JPackageInstallerHelper.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,243 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+import java.io.File;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardCopyOption;
+import java.util.List;
+
+public class JPackageInstallerHelper {
+ private static final String JPACKAGE_TEST_OUTPUT = "jpackage.test.output";
+ private static final String JPACKAGE_VERIFY_INSTALL = "jpackage.verify.install";
+ private static final String JPACKAGE_VERIFY_UNINSTALL = "jpackage.verify.uninstall";
+ private static String testOutput;
+ private static final boolean isTestOutputSet;
+ private static final boolean isVerifyInstall;
+ private static final boolean isVerifyUnInstall;
+
+ static {
+ String out = System.getProperty(JPACKAGE_TEST_OUTPUT);
+ isTestOutputSet = (out != null);
+ if (isTestOutputSet) {
+ File file = new File(out);
+ if (!file.exists()) {
+ throw new AssertionError(file.getAbsolutePath() + " does not exist");
+ }
+
+ if (!file.isDirectory()) {
+ throw new AssertionError(file.getAbsolutePath() + " is not a directory");
+ }
+
+ if (!file.canWrite()) {
+ throw new AssertionError(file.getAbsolutePath() + " is not writable");
+ }
+
+ if (out.endsWith(File.separator)) {
+ out = out.substring(0, out.length() - 2);
+ }
+
+ testOutput = out;
+ }
+
+ isVerifyInstall = (System.getProperty(JPACKAGE_VERIFY_INSTALL) != null);
+ isVerifyUnInstall = (System.getProperty(JPACKAGE_VERIFY_UNINSTALL) != null);
+ }
+
+ public static boolean isTestOutputSet() {
+ return isTestOutputSet;
+ }
+
+ public static boolean isVerifyInstall() {
+ return isVerifyInstall;
+ }
+
+ public static boolean isVerifyUnInstall() {
+ return isVerifyUnInstall;
+ }
+
+ public static void copyTestResults(List<String> files) throws Exception {
+ if (!isTestOutputSet()) {
+ return;
+ }
+
+ File dest = new File(testOutput);
+ if (!dest.exists()) {
+ dest.mkdirs();
+ }
+
+ if (JPackageHelper.isWindows()) {
+ files.add(JPackagePath.getTestSrc() + File.separator + "install.bat");
+ files.add(JPackagePath.getTestSrc() + File.separator + "uninstall.bat");
+ } else {
+ files.add(JPackagePath.getTestSrc() + File.separator + "install.sh");
+ files.add(JPackagePath.getTestSrc() + File.separator + "uninstall.sh");
+ }
+
+ for (String file : files) {
+ Path source = Path.of(file);
+ Path target = Path.of(dest.toPath() + File.separator + source.getFileName());
+ Files.copy(source, target, StandardCopyOption.REPLACE_EXISTING);
+ }
+ }
+
+ public static void validateApp(String app) throws Exception {
+ File outFile = new File("appOutput.txt");
+ if (outFile.exists()) {
+ outFile.delete();
+ }
+
+ int retVal = JPackageHelper.execute(outFile, app);
+ if (retVal != 0) {
+ throw new AssertionError(
+ "Test application exited with error: " + retVal);
+ }
+
+ if (!outFile.exists()) {
+ throw new AssertionError(outFile.getAbsolutePath() + " was not created");
+ }
+
+ String output = Files.readString(outFile.toPath());
+ String[] result = output.split("\n");
+ if (result.length != 2) {
+ System.err.println(output);
+ throw new AssertionError(
+ "Unexpected number of lines: " + result.length);
+ }
+
+ if (!result[0].trim().equals("jpackage test application")) {
+ throw new AssertionError("Unexpected result[0]: " + result[0]);
+ }
+
+ if (!result[1].trim().equals("args.length: 0")) {
+ throw new AssertionError("Unexpected result[1]: " + result[1]);
+ }
+ }
+
+ public static void validateOutput(String output) throws Exception {
+ File file = new File(output);
+ if (!file.exists()) {
+ // Try lower case in case of OS is case sensitive
+ file = new File(output.toLowerCase());
+ if (!file.exists()) {
+ throw new AssertionError("Cannot find " + file.getAbsolutePath());
+ }
+ }
+ }
+
+ public static void validateStartMenu(String menuGroup, String menu, boolean exist)
+ throws Exception {
+ String startMenuLink = JPackagePath.getWinStartMenu() +
+ File.separator + menuGroup + File.separator +
+ menu + ".lnk";
+
+ File link = new File(startMenuLink);
+ if (exist) {
+ if (!link.exists()) {
+ throw new AssertionError("Cannot find " + link.getAbsolutePath());
+ }
+ } else {
+ if (link.exists()) {
+ throw new AssertionError("Error: " + link.getAbsolutePath() + " exist");
+ }
+ }
+ }
+
+ public static void validateDesktopShortcut(String name, boolean exist)
+ throws Exception {
+ String shortcutLink = JPackagePath.getWinPublicDesktop() +
+ File.separator + name + ".lnk";
+
+ File link = new File(shortcutLink);
+ if (exist) {
+ if (!link.exists()) {
+ throw new AssertionError("Cannot find " + link.getAbsolutePath());
+ }
+ } else {
+ if (link.exists()) {
+ throw new AssertionError("Error: " + link.getAbsolutePath() + " exist");
+ }
+ }
+ }
+
+ public static void validateUserLocalStartMenu(String menuGroup, String menu, boolean exist)
+ throws Exception {
+ String startMenuLink = JPackagePath.getWinUserLocalStartMenu() +
+ File.separator + menuGroup + File.separator +
+ menu + ".lnk";
+
+ File link = new File(startMenuLink);
+ if (exist) {
+ if (!link.exists()) {
+ throw new AssertionError("Cannot find " + link.getAbsolutePath());
+ }
+ } else {
+ if (link.exists()) {
+ throw new AssertionError("Error: " + link.getAbsolutePath() + " exist");
+ }
+ }
+ }
+
+ public static void validateWinRegistry(String key, String [] values, boolean retValZero)
+ throws Exception {
+ File outFile = new File("regOutput.txt");
+ if (outFile.exists()) {
+ outFile.delete();
+ }
+
+ int retVal = JPackageHelper.execute(outFile, "reg.exe", "query", key);
+ if (retValZero) {
+ if (retVal != 0) {
+ System.out.println("validateWinRegistry() key=" + key);
+ if (outFile.exists()) {
+ System.err.println(Files.readString(outFile.toPath()));
+ }
+ throw new AssertionError(
+ "Reg.exe application exited with error: " + retVal);
+ }
+ } else {
+ if (retVal == 0) {
+ System.out.println("validateWinRegistry() key=" + key);
+ if (outFile.exists()) {
+ System.err.println(Files.readString(outFile.toPath()));
+ }
+ throw new AssertionError(
+ "Reg.exe application exited without error: " + retVal);
+ } else {
+ return; // Done
+ }
+ }
+
+ if (!outFile.exists()) {
+ throw new AssertionError(outFile.getAbsolutePath() + " was not created");
+ }
+
+ String output = Files.readString(outFile.toPath());
+ for (String value : values) {
+ if (!output.contains(value)) {
+ System.err.println(output);
+ throw new AssertionError("Cannot find in registry: " + value);
+ }
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/helpers/JPackagePath.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,300 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+import java.io.File;
+import java.nio.file.Path;
+
+/**
+ * Helper class which contains functions to get different system
+ * dependent paths used by tests
+ */
+public class JPackagePath {
+
+ // Path to Windows "Program Files" folder
+ // Probably better to figure this out programattically
+ 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";
+
+ // Path to Windows public desktop location
+ 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() {
+ return JPackageHelper.TEST_SRC_ROOT;
+ }
+
+ // Return path to calling test
+ public static String getTestSrc() {
+ return JPackageHelper.TEST_SRC;
+ }
+
+ // Returns path to generate test application
+ public static String getApp() {
+ return getApp("test");
+ }
+
+ public static String getApp(String name) {
+ if (JPackageHelper.isWindows()) {
+ return Path.of("output", name, "bin", name + ".exe").toString();
+ } else if (JPackageHelper.isOSX()) {
+ return Path.of("output", name + ".app",
+ "Contents", "MacOS", name).toString();
+ } else if (JPackageHelper.isLinux()) {
+ return Path.of("output", name, "bin", name).toString();
+ } else {
+ throw new AssertionError("Cannot detect platform");
+ }
+ }
+
+ // Returns path to generate test application icon
+ public static String getAppIcon() {
+ return getAppIcon("test");
+ }
+
+ public static String getAppIcon(String name) {
+ if (JPackageHelper.isWindows()) {
+ return Path.of("output", name, "bin", name + ".ico").toString();
+ } else if (JPackageHelper.isOSX()) {
+ return Path.of("output", name + ".app",
+ "Contents", "Resources", name + ".icns").toString();
+ } else if (JPackageHelper.isLinux()) {
+ return Path.of("output", name, "bin", name + ".png").toString();
+ } else {
+ throw new AssertionError("Cannot detect platform");
+ }
+ }
+
+ // 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 Path.of("output", app, "bin", sl + ".exe").toString();
+ } else if (JPackageHelper.isOSX()) {
+ return Path.of("output", app + ".app",
+ "Contents", "MacOS", sl).toString();
+ } else if (JPackageHelper.isLinux()) {
+ return Path.of("output", app, "bin", sl).toString();
+ } else {
+ throw new AssertionError("Cannot detect platform");
+ }
+ }
+
+ // 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 Path.of("output", name, "app").toString();
+ } else if (JPackageHelper.isOSX()) {
+ return Path.of("output", name + ".app",
+ "Contents", "Java").toString();
+ } else if (JPackageHelper.isLinux()) {
+ return Path.of("output", name, "app").toString();
+ } else {
+ throw new AssertionError("Cannot detect platform");
+ }
+ }
+
+ // Returns path to test application cfg file
+ public static String getAppCfg() {
+ return getAppCfg("test");
+ }
+
+ public static String getAppCfg(String name) {
+ if (JPackageHelper.isWindows()) {
+ return Path.of("output", name, "app", name + ".cfg").toString();
+ } else if (JPackageHelper.isOSX()) {
+ return Path.of("output", name + ".app",
+ "Contents", "Java", name + ".cfg").toString();
+ } else if (JPackageHelper.isLinux()) {
+ return Path.of("output", name, "app", name + ".cfg").toString();
+ } else {
+ throw new AssertionError("Cannot detect platform");
+ }
+ }
+
+ // 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 Path.of("output", name,
+ "runtime", "bin", "java.exe").toString();
+ } else if (JPackageHelper.isOSX()) {
+ return Path.of("output", name + ".app", "Contents",
+ "runtime", "Contents", "Home", "bin", "java").toString();
+ } else if (JPackageHelper.isLinux()) {
+ return Path.of("output", name,
+ "runtime", "bin", "java").toString();
+ } else {
+ throw new AssertionError("Cannot detect platform");
+ }
+ }
+
+ // Returns output file name generate by test application
+ public static String getAppOutputFile() {
+ return "appOutput.txt";
+ }
+
+ // 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 Path.of("output", name, "runtime", "bin").toString();
+ } else if (JPackageHelper.isOSX()) {
+ return Path.of("output", name + ".app",
+ "Contents", "runtime",
+ "Contents", "Home", "bin").toString();
+ } else if (JPackageHelper.isLinux()) {
+ return Path.of("output", name, "runtime", "bin").toString();
+ } else {
+ throw new AssertionError("Cannot detect platform");
+ }
+ }
+
+ public static String getWinProgramFiles() {
+ return WIN_PROGRAM_FILES;
+ }
+
+ public static String getWinUserLocal() {
+ return Path.of(System.getProperty("user.home"),
+ "AppData", "Local").toString();
+ }
+
+ public static String getWinStartMenu() {
+ return WIN_START_MENU;
+ }
+
+ public static String getWinPublicDesktop() {
+ return WIN_PUBLIC_DESKTOP;
+ }
+
+ public static String getWinUserLocalStartMenu() {
+ return Path.of(System.getProperty("user.home"), "AppData", "Roaming",
+ "Microsoft", "Windows", "Start Menu", "Programs").toString();
+ }
+
+ public static String getWinInstalledApp(String testName) {
+ return Path.of(getWinProgramFiles(), testName,
+ testName + ".exe").toString();
+ }
+
+ 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;
+ }
+
+ public static String getLinuxInstalledApp(String 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;
+ }
+
+ public static String getLinuxInstalledApp(String subDir, String 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;
+ }
+
+ public static String getLinuxInstallFolder(String 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;
+ } else {
+ return File.separator + "opt"
+ + File.separator + subDir
+ + File.separator + testName;
+ }
+ }
+
+ public static String getWinUserLocalInstalledApp(String testName) {
+ return getWinUserLocal()
+ + File.separator + testName
+ + File.separator + "bin"
+ + File.separator + testName + ".exe";
+ }
+
+ public static String getWinUserLocalInstallFolder(String testName) {
+ return getWinUserLocal() + File.separator + testName;
+ }
+
+ // Returs path to test license file
+ public static String getLicenseFilePath() {
+ String path = JPackagePath.getTestSrcRoot()
+ + File.separator + "resources"
+ + File.separator + "license.txt";
+
+ return path;
+ }
+
+ // Returns path to app folder of installed application
+ public static String getWinInstalledAppFolder(String testName) {
+ return getWinProgramFiles()
+ + File.separator + testName
+ + File.separator + "app";
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/jdk/jpackage/internal/DeployParamsTest.java Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,125 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+import jdk.jpackage.internal.Arguments;
+import jdk.jpackage.internal.DeployParams;
+import jdk.jpackage.internal.PackagerException;
+import java.io.File;
+
+/*
+ * @test
+ * @bug 8211285
+ * @summary DeployParamsTest
+ * @modules jdk.jpackage
+ * @modules jdk.jpackage/jdk.jpackage.internal
+ * @run main/othervm -Xmx512m DeployParamsTest
+ */
+public class DeployParamsTest {
+
+ private static File testRoot = null;
+
+ private static void setUp() {
+ testRoot = new File("deployParamsTest");
+ System.out.println("DeployParamsTest: " + testRoot.getAbsolutePath());
+ testRoot.mkdir();
+ }
+
+ private static void tearDown() {
+ if (testRoot != null) {
+ testRoot.delete();
+ }
+ }
+
+ private static void testValidateAppName1() throws Exception {
+ DeployParams params = getParamsAppName();
+
+ setAppName(params, "Test");
+ params.validate();
+
+ setAppName(params, "Test Name");
+ params.validate();
+
+ setAppName(params, "Test - Name !!!");
+ params.validate();
+ }
+
+ private static void testValidateAppName2() throws Exception {
+ DeployParams params = getParamsAppName();
+
+ setAppName(params, "Test\nName");
+ appName2TestHelper(params);
+
+ setAppName(params, "Test\rName");
+ appName2TestHelper(params);
+
+ setAppName(params, "TestName\\");
+ appName2TestHelper(params);
+
+ setAppName(params, "Test \" Name");
+ appName2TestHelper(params);
+ }
+
+ private static void appName2TestHelper(DeployParams params) throws Exception {
+ try {
+ params.validate();
+ } catch (PackagerException pe) {
+ if (!pe.getMessage().startsWith("Error: Invalid Application name")) {
+ throw new Exception("Unexpected PackagerException received: " + pe);
+ }
+
+ return; // Done
+ }
+
+ throw new Exception("Expecting PackagerException");
+ }
+
+ // Returns deploy params initialized to pass all validation, except for
+ // app name
+ private static DeployParams getParamsAppName() {
+ DeployParams 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(), "test.jar");
+ params.addBundleArgument(Arguments.CLIOptions.INPUT.getId(), "input");
+
+ return params;
+ }
+
+ private static void setAppName(DeployParams params, String appName) {
+ params.addBundleArgument(Arguments.CLIOptions.NAME.getId(), appName);
+ }
+
+ public static void main(String[] args) throws Exception {
+ setUp();
+
+ try {
+ testValidateAppName1();
+ testValidateAppName2();
+ } finally {
+ tearDown();
+ }
+ }
+
+}
Binary file test/jdk/tools/jpackage/resources/icon.icns has changed
Binary file test/jdk/tools/jpackage/resources/icon.ico has changed
Binary file test/jdk/tools/jpackage/resources/icon.png has changed
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/resources/license.txt Thu Jun 27 19:14:42 2019 -0400
@@ -0,0 +1,1 @@
+jpackage test license file (just some sample text).
--- a/test/jdk/tools/launcher/HelpFlagsTest.java Thu Jun 27 22:03:19 2019 +0200
+++ b/test/jdk/tools/launcher/HelpFlagsTest.java Thu Jun 27 19:14:42 2019 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018 SAP SE. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
@@ -156,13 +156,12 @@
new ToolHelpSpec("jstatd", 1, 1, 1, 0, 0, 0, 1), // -?, -h, --help
new ToolHelpSpec("keytool", 1, 1, 1, 0, 1, 0, 1), // none, prints help message anyways.
new ToolHelpSpec("pack200", 1, 1, 1, 0, 1, 0, 2), // -?, -h, --help, -help accepted but not documented.
- new ToolHelpSpec("rmic", 0, 0, 0, 0, 0, 0, 1), // none, pirnts help message anyways.
+ new ToolHelpSpec("rmic", 0, 0, 0, 0, 0, 0, 1), // none, prints help message anyways.
new ToolHelpSpec("rmid", 0, 0, 0, 0, 0, 0, 1), // none, prints help message anyways.
new ToolHelpSpec("rmiregistry", 0, 0, 0, 0, 0, 0, 1), // none, prints help message anyways.
new ToolHelpSpec("serialver", 0, 0, 0, 0, 0, 0, 1), // none, prints help message anyways.
new ToolHelpSpec("unpack200", 1, 1, 1, 0, 1, 0, 2), // -?, -h, --help, -help accepted but not documented.
- // Oracle proprietary tools:
- new ToolHelpSpec("javapackager",0, 0, 0, 0, 1, 0, 255), // -help accepted but not documented.
+ new ToolHelpSpec("jpackage", 0, 1, 1, 0, 0, 1, 1), // -h, --help,
};
// Returns true if the file is not a tool.
--- a/test/jdk/tools/launcher/VersionCheck.java Thu Jun 27 22:03:19 2019 +0200
+++ b/test/jdk/tools/launcher/VersionCheck.java Thu Jun 27 19:14:42 2019 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 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
@@ -62,7 +62,7 @@
"jmc",
"jmc.ini",
"jweblauncher",
- "packager",
+ "jpackage",
"ssvagent",
"unpack200",
};
@@ -108,7 +108,7 @@
"klist",
"ktab",
"pack200",
- "packager",
+ "jpackage",
"rmic",
"rmid",
"rmiregistry",