# HG changeset patch # User herrick # Date 1547144036 18000 # Node ID 3409e81fb3cbab9a72fdcecfae308ddee3a8ab08 # Parent 57fead34cce2c4591af87a5297275e3c00425e32# Parent f6ab4cc4c70e3b9ad34a38c39bf47aec12c6b3a6 Merge diff -r f6ab4cc4c70e -r 3409e81fb3cb make/CompileDemos.gmk --- a/make/CompileDemos.gmk Thu Jan 10 09:11:56 2019 -0800 +++ b/make/CompileDemos.gmk Thu Jan 10 13:13:56 2019 -0500 @@ -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. diff -r f6ab4cc4c70e -r 3409e81fb3cb make/CompileJavaModules.gmk --- a/make/CompileJavaModules.gmk Thu Jan 10 09:11:56 2019 -0800 +++ b/make/CompileJavaModules.gmk Thu Jan 10 13:13:56 2019 -0500 @@ -384,6 +384,15 @@ ################################################################################ +jdk.jpackage_ADD_JAVAC_FLAGS += -parameters -XDstringConcat=inline + +jdk.jpackage_COPY += .gif .png .txt .spec .script .prerm .preinst .postrm .postinst .list \ + .desktop .copyright .control .plist .template .icns .scpt .entitlements .wxs .iss .ico .bmp + +jdk.jpackage_CLEAN += .properties + +################################################################################ + jdk.jconsole_COPY += .gif .png jdk.jconsole_CLEAN_FILES += $(wildcard \ diff -r f6ab4cc4c70e -r 3409e81fb3cb make/common/Modules.gmk --- a/make/common/Modules.gmk Thu Jan 10 09:11:56 2019 -0800 +++ b/make/common/Modules.gmk Thu Jan 10 13:13:56 2019 -0500 @@ -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 ($(filter $(OPENJDK_TARGET_OS), windows macosx linux), ) + MODULES_FILTER += jdk.jpackage +endif + +################################################################################ # Module list macros # Use append so that the custom extension may add to these variables diff -r f6ab4cc4c70e -r 3409e81fb3cb make/common/NativeCompilation.gmk --- a/make/common/NativeCompilation.gmk Thu Jan 10 09:11:56 2019 -0800 +++ b/make/common/NativeCompilation.gmk Thu Jan 10 13:13:56 2019 -0500 @@ -390,6 +390,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! @@ -500,8 +501,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))) @@ -842,30 +841,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 ($(OPENJDK_TARGET_OS), windows) - $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 ifneq ($(findstring $(OPENJDK_TARGET_OS), linux solaris), ) - $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 ($(OPENJDK_TARGET_OS), macosx) $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 # OPENJDK_TARGET_OS # Since the link rule creates more than one file that we want to track, @@ -887,14 +887,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 @@ -930,6 +930,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)) @@ -1032,6 +1033,7 @@ # 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 ($(OPENJDK_TARGET_OS), windows) $$(call ExecuteWithLog, $$($1_OBJECT_DIR)/$$($1_SAFE_NAME)_link, \ $$($1_LD) $$($1_LDFLAGS) $$($1_EXTRA_LDFLAGS) $$($1_SYSROOT_LDFLAGS) \ diff -r f6ab4cc4c70e -r 3409e81fb3cb make/launcher/Launcher-jdk.jpackage.gmk --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/make/launcher/Launcher-jdk.jpackage.gmk Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,56 @@ +# +# Copyright (c) 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. +# + +include LauncherCommon.gmk + + +################################################################################ + +ifeq ($(OPENJDK_TARGET_OS), windows) + + JPACKAGEEXE_SRC := $(TOPDIR)/src/jdk.jpackage/windows/native/jpackage + + $(eval $(call SetupJdkExecutable, BUILD_JPACKAGEEXE, \ + NAME := jpackage, \ + SRC := $(JPACKAGEEXE_SRC), \ + OPTIMIZATION := LOW, \ + CFLAGS := $(CXXFLAGS_JDKEXE), \ + CFLAGS_windows := -DFULL -EHsc -DUNICODE -D_UNICODE, \ + CFLAGS_release := -DPRODUCT, \ + LDFLAGS := $(LDFLAGS_JDKEXE), \ + LIBS := $(LIBCXX) user32.lib shell32.lib advapi32.lib ole32.lib, \ + )) + + TARGETS += $(BUILD_JPACKAGEEXE) + +else + +$(eval $(call SetupBuildLauncher, jpackage, \ + MAIN_CLASS := jdk.jpackage.main.Main, \ +)) + +endif + +################################################################################ diff -r f6ab4cc4c70e -r 3409e81fb3cb make/lib/Lib-jdk.jpackage.gmk --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/make/lib/Lib-jdk.jpackage.gmk Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,100 @@ +# +# Copyright (c) 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. +# + +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) + +################################################################################ + +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_linux := -fPIC, \ + CFLAGS_solaris := -KPIC, \ + CFLAGS_macosx := -fPIC, \ + CFLAGS_windows := -EHsc -DLAUNCHERC -DUNICODE -D_UNICODE, \ + LDFLAGS_windows := -nologo, \ + LIBS_macosx := -framework Cocoa, \ + LIBS := $(LIBCXX), \ + LIBS_solaris := -lc, \ + LIBS_linux := -ldl, \ + LIBS_windows := user32.lib shell32.lib advapi32.lib, \ + VERSIONINFO_RESOURCE := $(GLOBAL_VERSION_INFO_RESOURCE), \ +)) + +TARGETS += $(BUILD_JPACKAGE_APPLAUNCHEREXE) + +# Build non-console version of launcher +ifeq ($(OPENJDK_TARGET_OS), windows) + + $(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_windows := -nologo, \ + LIBS := $(LIBCXX), \ + LIBS_windows := user32.lib shell32.lib advapi32.lib, \ + VERSIONINFO_RESOURCE := $(GLOBAL_VERSION_INFO_RESOURCE), \ + )) + + TARGETS += $(BUILD_JPACKAGE_APPLAUNCHERWEXE) +endif + diff -r f6ab4cc4c70e -r 3409e81fb3cb src/demo/share/jpackage/JNLPConverter/README.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/demo/share/jpackage/JNLPConverter/README.txt Thu Jan 10 13:13:56 2019 -0500 @@ -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 + +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. diff -r f6ab4cc4c70e -r 3409e81fb3cb src/demo/share/jpackage/JNLPConverter/src/jnlp/converter/HTTPHelper.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/demo/share/jpackage/JNLPConverter/src/jnlp/converter/HTTPHelper.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,193 @@ +/* + * Copyright (c) 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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."); + } + } +} diff -r f6ab4cc4c70e -r 3409e81fb3cb src/demo/share/jpackage/JNLPConverter/src/jnlp/converter/HTTPHelperException.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/demo/share/jpackage/JNLPConverter/src/jnlp/converter/HTTPHelperException.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,42 @@ +/* + * Copyright (c) 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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; + } +} diff -r f6ab4cc4c70e -r 3409e81fb3cb src/demo/share/jpackage/JNLPConverter/src/jnlp/converter/JNLPConverter.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/demo/share/jpackage/JNLPConverter/src/jnlp/converter/JNLPConverter.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,865 @@ +/* + * Copyright (c) 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 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.getLocalizedMessage()); + } + } + + 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 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 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 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.createImage()) { + addLaunchArg("create-image", launchArgs); + } else if (options.createInstaller()) { + if (options.getInstallerType() == null) { + addLaunchArg("create-installer", launchArgs); + } else { + addLaunchArg("create-installer", 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("--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("--class", jnlpd.getMainClass(), launchArgs); + + addFiles(launchArgs); + addArguments(launchArgs); + addJVMArgs(launchArgs); + + 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 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 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 addFileList(String arg, List filesToAdd, List launchArgs) { + if (filesToAdd.isEmpty()) { + return; + } + + String filesArg = ""; + for (int i = 0; i < filesToAdd.size(); i++) { + filesArg += quote(filesToAdd.get(i)); + if ((i + 1) != filesToAdd.size()) { + filesArg += File.pathSeparator; + } + } + + launchArgs.add(arg); + launchArgs.add(filesArg); + } + + private void addFiles(List launchArgs) { + addFileList("--files", jnlpd.getFiles(), launchArgs); + } + + private void addArguments(List launchArgs) { + List 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 launchArgs) { + List 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("--jvm-args"); + 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) != '\\') { + if (Platform.isWindows()) { + sb.appendCodePoint('\\'); + sb.appendCodePoint('\\'); + } + 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 { + if (Platform.isWindows()) { + sb.appendCodePoint('\\'); + } + 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.getLocalizedMessage()); + } + } + + 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; + } +} diff -r f6ab4cc4c70e -r 3409e81fb3cb src/demo/share/jpackage/JNLPConverter/src/jnlp/converter/Log.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/demo/share/jpackage/JNLPConverter/src/jnlp/converter/Log.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2018, 2018, 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); + } +} diff -r f6ab4cc4c70e -r 3409e81fb3cb src/demo/share/jpackage/JNLPConverter/src/jnlp/converter/Main.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/demo/share/jpackage/JNLPConverter/src/jnlp/converter/Main.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,71 @@ +/* + * Copyright (c) 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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); + } +} diff -r f6ab4cc4c70e -r 3409e81fb3cb src/demo/share/jpackage/JNLPConverter/src/jnlp/converter/Options.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/demo/share/jpackage/JNLPConverter/src/jnlp/converter/Options.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,370 @@ +/* + * Copyright (c) 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 createImage = 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 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", "rpm", "deb", + "dmg", "pkg", "pkg-app-store"}; + + // --output, -o, --input, -i, --files, -f, --main-jar, -j, --class, -c + private static final String [] BLOCKED_JPACKAGE_OPTIONS = {"--output", "-o", "--input", "-i", + "--files", "-f", "--main-jar", + "-j", "--class", "-c"}; + + 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-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 createImage() { + return createImage; + } + + 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 getJPackageOptions() { + return jpackageOptions; + } + + public boolean isRuntimeImageSet() { + return isRuntimeImageSet; + } + + // Helper method to dump all options + private void display() { + System.out.println("Options:"); + System.out.println("createImage: " + createImage); + 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 (!createImage && !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 "); + System.out.println(""); + System.out.println("where mode is one of:"); + System.out.println(" create-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(" Valid values for \"type\" are \"msi\", \"rpm\", \"deb\", \"dmg\", \"pkg\","); + System.out.println(" \"pkg-app-store\". If \"type\" is omitted, all supported types of installable"); + System.out.println(" packages for current platform will be generated."); + System.out.println(""); + System.out.println("Possible options include:"); + System.out.println(" -j, --jnlp "); + System.out.println(" Full path to JNLP file. Supported protocols are HTTP/HTTPS/FILE."); + System.out.println(" -o, --output "); + System.out.println(" Name of the directory where generated output files are placed."); + System.out.println(" -k, --keep "); + System.out.println(" Keep JNLP, JARs and command line arguments for jpackage"); + System.out.println(" in directory provided."); + System.out.println(" --jpackage-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(" --files -f, --main-jar -j and --class -c."); + 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 --= or"); + System.out.println("-- ."); + 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-image": + options.createImage = true; + index = 1; + break; + case "create-installer": + options.createInstaller = true; + index = 1; + if (args.length >= 2) { + if (isInstallerType(args[1])) { + options.installerType = args[1]; + index = 2; + } + } + 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 { + 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); + } +} diff -r f6ab4cc4c70e -r 3409e81fb3cb src/demo/share/jpackage/JNLPConverter/src/jnlp/converter/Platform.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/demo/share/jpackage/JNLPConverter/src/jnlp/converter/Platform.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,82 @@ +/* + * Copyright (c) 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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; + } +} diff -r f6ab4cc4c70e -r 3409e81fb3cb src/demo/share/jpackage/JNLPConverter/src/jnlp/converter/parser/GeneralUtil.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/demo/share/jpackage/JNLPConverter/src/jnlp/converter/parser/GeneralUtil.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,415 @@ +/* + * Copyright (c) 2006, 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 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; + } +} diff -r f6ab4cc4c70e -r 3409e81fb3cb src/demo/share/jpackage/JNLPConverter/src/jnlp/converter/parser/JNLPDesc.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/demo/share/jpackage/JNLPConverter/src/jnlp/converter/parser/JNLPDesc.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,617 @@ +/* + * Copyright (c) 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 arguments = new ArrayList<>(); + private final List files = new ArrayList<>(); + private final List resources = new ArrayList<>(); + private final List 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 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 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 getResources() { + return resources; + } + + public void addVMArg(String arg) { + if (arg != null) { + vmArgs.add(arg); + } + } + + public List 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; + } + } +} diff -r f6ab4cc4c70e -r 3409e81fb3cb src/demo/share/jpackage/JNLPConverter/src/jnlp/converter/parser/ResourceType.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/demo/share/jpackage/JNLPConverter/src/jnlp/converter/parser/ResourceType.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2006, 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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; +} diff -r f6ab4cc4c70e -r 3409e81fb3cb src/demo/share/jpackage/JNLPConverter/src/jnlp/converter/parser/ResourceVisitor.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/demo/share/jpackage/JNLPConverter/src/jnlp/converter/parser/ResourceVisitor.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2006, 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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) { + } +} diff -r f6ab4cc4c70e -r 3409e81fb3cb src/demo/share/jpackage/JNLPConverter/src/jnlp/converter/parser/ResourcesDesc.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/demo/share/jpackage/JNLPConverter/src/jnlp/converter/parser/ResourcesDesc.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,665 @@ +/* + * Copyright (c) 2006, 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 _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 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 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 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 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 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 getResourcePropertyList() throws Exception { + final LinkedList 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 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); + } + } +} diff -r f6ab4cc4c70e -r 3409e81fb3cb src/demo/share/jpackage/JNLPConverter/src/jnlp/converter/parser/VersionID.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/demo/share/jpackage/JNLPConverter/src/jnlp/converter/parser/VersionID.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,313 @@ +/* + * Copyright (c) 2006, 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 { + 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 String. + * @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 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(); + } +} diff -r f6ab4cc4c70e -r 3409e81fb3cb src/demo/share/jpackage/JNLPConverter/src/jnlp/converter/parser/VersionString.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/demo/share/jpackage/JNLPConverter/src/jnlp/converter/parser/VersionString.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2006, 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 _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 getAllVersionIDs() { + return _versionIds; + } +} diff -r f6ab4cc4c70e -r 3409e81fb3cb src/demo/share/jpackage/JNLPConverter/src/jnlp/converter/parser/XMLFormat.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/demo/share/jpackage/JNLPConverter/src/jnlp/converter/parser/XMLFormat.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,659 @@ +/* + * Copyright (c) 2006, 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 tag + if (!root.getName().equals("jnlp")) { + throw (new MissingFieldException(source, "")); + } + + // Read 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 attributes + if (XMLUtils.isElementPath(root, "")) { + jnlpd.setIsSandbox(false); + } else if (XMLUtils.isElementPath(root, + "")) { + 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, "")) { + // no new type for javafx-desc - needs one of the others + buildFXAppDesc(source, root, "", 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, "")) { + buildApplicationDesc(source, root, jnlpd); + } else if (XMLUtils.isElementPath(root, "")) { + jnlpd.setIsLibrary(true); + } else if (XMLUtils.isElementPath(root, "")) { + Log.warning(" is not supported and will be ignored in " + jnlp); + jnlpd.setIsInstaller(true); + } else if (XMLUtils.isElementPath(root, "")) { + isApplet = true; + } else { + if (!isFXApp) { + throw (new MissingFieldException(source, + "(||" + + "|)")); + } + } + + if (isApplet && !isFXApp) { + Log.error("Applet based applications deployed with 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 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 tag */ + private static void buildInformationDesc(final String source, final URL codebase, XMLNode root, JNLPDesc jnlpd) + throws MissingFieldException, BadFieldException { + final ArrayList list = new ArrayList<>(); + + // Iterates over all nodes ignoring the type + XMLUtils.visitElements(root, + "", 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, ""); + 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); + } +} diff -r f6ab4cc4c70e -r 3409e81fb3cb src/demo/share/jpackage/JNLPConverter/src/jnlp/converter/parser/XMLUtils.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/demo/share/jpackage/JNLPConverter/src/jnlp/converter/parser/XMLUtils.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,328 @@ +/* + * Copyright (c) 2006, 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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(); + } + } +} diff -r f6ab4cc4c70e -r 3409e81fb3cb src/demo/share/jpackage/JNLPConverter/src/jnlp/converter/parser/exception/BadFieldException.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/demo/share/jpackage/JNLPConverter/src/jnlp/converter/parser/exception/BadFieldException.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2006, 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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() + "]"; + } +} diff -r f6ab4cc4c70e -r 3409e81fb3cb src/demo/share/jpackage/JNLPConverter/src/jnlp/converter/parser/exception/JNLParseException.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/demo/share/jpackage/JNLPConverter/src/jnlp/converter/parser/exception/JNLParseException.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2006, 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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(); + } +} diff -r f6ab4cc4c70e -r 3409e81fb3cb src/demo/share/jpackage/JNLPConverter/src/jnlp/converter/parser/exception/MissingFieldException.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/demo/share/jpackage/JNLPConverter/src/jnlp/converter/parser/exception/MissingFieldException.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2006, 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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() + "]"; + } +} diff -r f6ab4cc4c70e -r 3409e81fb3cb src/demo/share/jpackage/JNLPConverter/src/jnlp/converter/parser/xml/XMLAttribute.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/demo/share/jpackage/JNLPConverter/src/jnlp/converter/parser/xml/XMLAttribute.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2006, 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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; + } +} diff -r f6ab4cc4c70e -r 3409e81fb3cb src/demo/share/jpackage/JNLPConverter/src/jnlp/converter/parser/xml/XMLEncoding.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/demo/share/jpackage/JNLPConverter/src/jnlp/converter/parser/xml/XMLEncoding.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,342 @@ +/* + * Copyright (c) 2006, 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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"; +} diff -r f6ab4cc4c70e -r 3409e81fb3cb src/demo/share/jpackage/JNLPConverter/src/jnlp/converter/parser/xml/XMLNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/demo/share/jpackage/JNLPConverter/src/jnlp/converter/parser/xml/XMLNode.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,201 @@ +/* + * Copyright (c) 2006, 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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(); + } +} diff -r f6ab4cc4c70e -r 3409e81fb3cb src/demo/share/jpackage/JNLPConverter/src/jnlp/converter/parser/xml/XMLParser.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/demo/share/jpackage/JNLPConverter/src/jnlp/converter/parser/xml/XMLParser.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,179 @@ +/* + * Copyright (c) 2014, 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 + // Vendor "Some whitespaces" + // + // In example above when we receive end of we will + // have _characters set to whitespace and new line and it will be + // added as child node to . 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. + // + // + // test with whitespaces + // + // From example above we want to include whitespaces for . + // + // + // abc + // xyz (might be whitespaces) + // + // 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 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 + } + } +} diff -r f6ab4cc4c70e -r 3409e81fb3cb src/demo/share/nbproject/jpackage/JNLPConverter/build.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/demo/share/nbproject/jpackage/JNLPConverter/build.xml Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,80 @@ + + + + + + + + + + + Builds, tests, and runs the project JNLPConverter. + + + + + + + + + + diff -r f6ab4cc4c70e -r 3409e81fb3cb src/demo/share/nbproject/jpackage/JNLPConverter/manifest.mf --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/demo/share/nbproject/jpackage/JNLPConverter/manifest.mf Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +X-COMMENT: Main-Class will be added automatically by build + diff -r f6ab4cc4c70e -r 3409e81fb3cb src/demo/share/nbproject/jpackage/JNLPConverter/nbproject/build-impl.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/demo/share/nbproject/jpackage/JNLPConverter/nbproject/build-impl.xml Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,1403 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set src.src.dir + Must set build.dir + Must set dist.dir + Must set build.classes.dir + Must set dist.javadoc.dir + Must set build.test.classes.dir + Must set build.test.results.dir + Must set build.classes.excludes + Must set dist.jar + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set javac.includes + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + No tests executed. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set JVM to use for profiling in profiler.info.jvm + Must set profiler agent JVM arguments in profiler.info.jvmargs.agent + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select some files in the IDE or set javac.includes + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + To run this application from the command line without Ant, try: + + java -jar "${dist.jar.resolved}" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set run.class + + + + Must select one file in the IDE or set run.class + + + + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set debug.class + + + + + Must select one file in the IDE or set debug.class + + + + + Must set fix.includes + + + + + + + + + + This target only works when run from inside the NetBeans IDE. + + + + + + + + + Must select one file in the IDE or set profile.class + This target only works when run from inside the NetBeans IDE. + + + + + + + + + This target only works when run from inside the NetBeans IDE. + + + + + + + + + + + + + This target only works when run from inside the NetBeans IDE. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set run.class + + + + + + Must select some files in the IDE or set test.includes + + + + + Must select one file in the IDE or set run.class + + + + + Must select one file in the IDE or set applet.url + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select some files in the IDE or set javac.includes + + + + + + + + + + + + + + + + + + Some tests failed; see details above. + + + + + + + + + Must select some files in the IDE or set test.includes + + + + Some tests failed; see details above. + + + + Must select some files in the IDE or set test.class + Must select some method in the IDE or set test.method + + + + Some tests failed; see details above. + + + + + Must select one file in the IDE or set test.class + + + + Must select one file in the IDE or set test.class + Must select some method in the IDE or set test.method + + + + + + + + + + + + + + Must select one file in the IDE or set applet.url + + + + + + + + + Must select one file in the IDE or set applet.url + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff -r f6ab4cc4c70e -r 3409e81fb3cb src/demo/share/nbproject/jpackage/JNLPConverter/nbproject/genfiles.properties --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/demo/share/nbproject/jpackage/JNLPConverter/nbproject/genfiles.properties Thu Jan 10 13:13:56 2019 -0500 @@ -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 diff -r f6ab4cc4c70e -r 3409e81fb3cb src/demo/share/nbproject/jpackage/JNLPConverter/nbproject/project.properties --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/demo/share/nbproject/jpackage/JNLPConverter/nbproject/project.properties Thu Jan 10 13:13:56 2019 -0500 @@ -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} diff -r f6ab4cc4c70e -r 3409e81fb3cb src/demo/share/nbproject/jpackage/JNLPConverter/nbproject/project.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/demo/share/nbproject/jpackage/JNLPConverter/nbproject/project.xml Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,13 @@ + + + org.netbeans.modules.java.j2seproject + + + JNLPConverter + + + + + + + diff -r f6ab4cc4c70e -r 3409e81fb3cb src/java.base/macosx/native/libjli/java_md_macosx.m diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jlink/linux/classes/module-info.java.extra --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jlink/linux/classes/module-info.java.extra Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2015, 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. + */ + + exports jdk.tools.jlink.internal.packager to + jdk.jpackage; diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jlink/macosx/classes/module-info.java.extra --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jlink/macosx/classes/module-info.java.extra Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2015, 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. + */ + + exports jdk.tools.jlink.internal.packager to + jdk.jpackage; diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jlink/share/classes/module-info.java --- a/src/jdk.jlink/share/classes/module-info.java Thu Jan 10 09:11:56 2019 -0800 +++ b/src/jdk.jlink/share/classes/module-info.java Thu Jan 10 13:13:56 2019 -0500 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 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 @@ -76,4 +76,4 @@ jdk.tools.jlink.internal.plugins.IncludeLocalesPlugin, jdk.tools.jlink.internal.plugins.GenerateJLIClassesPlugin, jdk.tools.jlink.internal.plugins.ReleaseInfoPlugin; - } +} diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jlink/windows/classes/module-info.java.extra --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jlink/windows/classes/module-info.java.extra Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2015, 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. + */ + + exports jdk.tools.jlink.internal.packager to + jdk.jpackage; diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxAppBundler.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxAppBundler.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,232 @@ +/* + * Copyright (c) 2012, 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.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"); + + public static final BundlerParamInfo ICON_PNG = + new StandardBundlerParam<>( + I18N.getString("param.icon-png.name"), + I18N.getString("param.icon-png.description"), + "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 static final BundlerParamInfo LINUX_INSTALL_DIR = + new StandardBundlerParam<>( + I18N.getString("param.linux-install-dir.name"), + I18N.getString("param.linux-install-dir.description"), + "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 + ); + + public static final BundlerParamInfo LINUX_PACKAGE_DEPENDENCIES = + new StandardBundlerParam<>( + I18N.getString("param.linux-package-dependencies.name"), + I18N.getString("param.linux-package-dependencies.description"), + Arguments.CLIOptions.LINUX_PACKAGE_DEPENDENCIES.getId(), + String.class, + params -> { + return ""; + }, + (s, p) -> s + ); + + @Override + public boolean validate(Map p) + throws UnsupportedPlatformException, ConfigException { + try { + if (p == null) throw new ConfigException( + I18N.getString("error.parameters-null"), + I18N.getString("error.parameters-null.advice")); + + return doValidate(p); + } catch (RuntimeException re) { + if (re.getCause() instanceof ConfigException) { + throw (ConfigException) re.getCause(); + } else { + throw new ConfigException(re); + } + } + } + + private boolean doValidate(Map p) + throws UnsupportedPlatformException, ConfigException { + if (Platform.getPlatform() != Platform.LINUX) { + throw new UnsupportedPlatformException(); + } + + imageBundleValidation(p); + + return true; + } + + // 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 p) { + return new File(outDir, APP_FS_NAME.fetchFrom(p)); + } + + public static String getLauncherCfgName(Map p) { + return "app/" + APP_FS_NAME.fetchFrom(p) +".cfg"; + } + + File doBundle(Map p, File outputDirectory, + boolean dependentTask) { + if (Arguments.CREATE_JRE_INSTALLER.fetchFrom(p)) { + return doJreBundle(p, outputDirectory, dependentTask); + } else { + return doAppBundle(p, outputDirectory, dependentTask); + } + } + + private File doJreBundle(Map p, + File outputDirectory, boolean dependentTask) { + try { + File rootDirectory = createRoot(p, outputDirectory, dependentTask, + APP_FS_NAME.fetchFrom(p), "linuxapp-image-builder"); + AbstractAppImageBuilder appBuilder = new LinuxAppImageBuilder( + APP_NAME.fetchFrom(p), outputDirectory.toPath()); + File predefined = PREDEFINED_RUNTIME_IMAGE.fetchFrom(p); + if (predefined == null ) { + JLinkBundlerHelper.generateJre(p, appBuilder); + } else { + return predefined; + } + return rootDirectory; + } catch (Exception ex) { + Log.error("Exception: "+ex); + Log.debug(ex); + return null; + } + } + + private File doAppBundle(Map p, + File outputDirectory, boolean dependentTask) { + try { + File rootDirectory = createRoot(p, outputDirectory, dependentTask, + APP_FS_NAME.fetchFrom(p), "linuxapp-image-builder"); + AbstractAppImageBuilder appBuilder = new LinuxAppImageBuilder(p, + outputDirectory.toPath()); + if (PREDEFINED_RUNTIME_IMAGE.fetchFrom(p) == null ) { + JLinkBundlerHelper.execute(p, appBuilder); + } else { + StandardBundlerParam.copyPredefinedRuntimeImage(p, appBuilder); + } + return rootDirectory; + } catch (Exception ex) { + Log.error("Exception: "+ex); + Log.debug(ex); + return null; + } + } + + @Override + public String getName() { + return I18N.getString("app.bundler.name"); + } + + @Override + public String getDescription() { + return I18N.getString("app.bundler.description"); + } + + @Override + public String getID() { + return "linux.app"; + } + + @Override + public String getBundleType() { + return "IMAGE"; + } + + @Override + public Collection> getBundleParameters() { + return getAppBundleParameters(); + } + + public static Collection> getAppBundleParameters() { + return Arrays.asList( + APP_NAME, + APP_RESOURCES, + ARGUMENTS, + CLASSPATH, + JVM_OPTIONS, + MAIN_CLASS, + MAIN_JAR, + PREFERENCES_ID, + VERSION, + VERBOSE + ); + } + + @Override + public File execute(Map params, + File outputParentDir) { + return doBundle(params, outputParentDir, false); + } + + @Override + public boolean supported() { + return (Platform.getPlatform() == Platform.LINUX); + } +} diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxAppImageBuilder.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxAppImageBuilder.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,254 @@ +/* + * Copyright (c) 2015, 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.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 resourcesDir; + private final Path mdir; + + private final Map params; + + public static final BundlerParamInfo ICON_PNG = + new StandardBundlerParam<>( + I18N.getString("param.icon-png.name"), + I18N.getString("param.icon-png.description"), + "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 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.resourcesDir = root.resolve("resources"); + this.mdir = runtimeDir.resolve("lib"); + this.params = new HashMap<>(); + config.entrySet().stream().forEach(e -> params.put( + e.getKey().toString(), e.getValue())); + Files.createDirectories(appDir); + Files.createDirectories(runtimeDir); + Files.createDirectories(resourcesDir); + } + + public LinuxAppImageBuilder(String appName, Path imageOutDir) + 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.resourcesDir = null; + this.mdir = null; + this.params = new HashMap<>(); + } + + private Path destFile(String dir, String filename) { + return runtimeDir.resolve(dir).resolve(filename); + } + + private void writeEntry(InputStream in, Path dstFile) throws IOException { + Files.createDirectories(dstFile.getParent()); + Files.copy(in, dstFile); + } + + private void writeSymEntry(Path dstFile, Path target) throws IOException { + Files.createDirectories(dstFile.getParent()); + Files.createLink(dstFile, target); + } + + /** + * chmod ugo+x file + */ + private void setExecutable(Path file) { + try { + Set perms = + Files.getPosixFilePermissions(file); + perms.add(PosixFilePermission.OWNER_EXECUTE); + perms.add(PosixFilePermission.GROUP_EXECUTE); + perms.add(PosixFilePermission.OTHERS_EXECUTE); + Files.setPosixFilePermissions(file, perms); + } catch (IOException ioe) { + throw new UncheckedIOException(ioe); + } + } + + private static void createUtf8File(File file, String content) + throws IOException { + try (OutputStream fout = new FileOutputStream(file); + Writer output = new OutputStreamWriter(fout, "UTF-8")) { + output.write(content); + } + } + + + // 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 p) { + return new File(outDir, APP_FS_NAME.fetchFrom(p)); + } + + public static String getLauncherName(Map p) { + return APP_FS_NAME.fetchFrom(p); + } + + public static String getLauncherCfgName(Map p) { + return "app/" + APP_FS_NAME.fetchFrom(p) + ".cfg"; + } + + @Override + public Path getAppDir() { + return appDir; + } + + @Override + public Path getAppModsDir() { + return appModsDir; + } + + @Override + public void prepareApplicationFiles() throws IOException { + Map originalParams = new HashMap<>(params); + + // create the primary launcher + createLauncherForEntryPoint(params, root); + + // Copy library to the launcher folder + try (InputStream is_lib = getResourceAsStream(LIBRARY_NAME)) { + writeEntry(is_lib, root.resolve(LIBRARY_NAME)); + } + + // create the secondary launchers, if any + List> entryPoints + = StandardBundlerParam.SECONDARY_LAUNCHERS.fetchFrom(params); + for (Map entryPoint : entryPoints) { + Map tmp = new HashMap<>(originalParams); + tmp.putAll(entryPoint); + // remove name.fs that was calculated for main launcher. + // otherwise, wrong launcher name will be selected. + tmp.remove(APP_FS_NAME.getID()); + createLauncherForEntryPoint(tmp, root); + } + + // Copy class path entries to Java folder + copyApplication(); + + // Copy icon to Resources folder + copyIcon(); + } + + @Override + public void prepareJreFiles() throws IOException {} + + private void createLauncherForEntryPoint(Map p, + Path rootDir) throws IOException { + // Copy executable to Linux folder + Path executableFile = root.resolve(getLauncherName(p)); + try (InputStream is_launcher = + getResourceAsStream("jpackageapplauncher")) { + writeEntry(is_launcher, executableFile); + } + + executableFile.toFile().setExecutable(true, false); + executableFile.toFile().setWritable(true, true); + + writeCfgFile(p, root.resolve(getLauncherCfgName(p)).toFile(), + "$APPDIR/runtime"); + } + + private void copyIcon() throws IOException { + File icon = ICON_PNG.fetchFrom(params); + if (icon != null) { + File iconTarget = new File(resourcesDir.toFile(), + APP_FS_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); + } + } + } + +} diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxDebBundler.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxDebBundler.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,902 @@ +/* + * Copyright (c) 2012, 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.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.logging.Level; +import java.util.logging.Logger; +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 APP_BUNDLER = + new StandardBundlerParam<>( + I18N.getString("param.deb-app-bundler.name"), + I18N.getString("param.deb-app-bundler.description"), + "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 BUNDLE_NAME = + new StandardBundlerParam<> ( + I18N.getString("param.bundle-name.name"), + I18N.getString("param.bundle-name.description"), + 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 FULL_PACKAGE_NAME = + new StandardBundlerParam<> ( + I18N.getString("param.full-package-name.name"), + I18N.getString("param.full-package-name.description"), + "linux.deb.fullPackageName", + String.class, + params -> BUNDLE_NAME.fetchFrom(params) + "-" + + VERSION.fetchFrom(params), + (s, p) -> s); + + public static final BundlerParamInfo DEB_IMAGE_DIR = + new StandardBundlerParam<>( + I18N.getString("param.image-dir.name"), + I18N.getString("param.image-dir.description"), + "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 APP_IMAGE_ROOT = + new StandardBundlerParam<>( + I18N.getString("param.app-image-root.name"), + I18N.getString("param.app-image-root.description"), + "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 CONFIG_DIR = + new StandardBundlerParam<>( + I18N.getString("param.config-dir.name"), + I18N.getString("param.config-dir.description"), + "linux.deb.configDir", + File.class, + params -> new File(DEB_IMAGE_DIR.fetchFrom(params), "DEBIAN"), + (s, p) -> new File(s)); + + public static final BundlerParamInfo EMAIL = + new StandardBundlerParam<> ( + I18N.getString("param.maintainer-email.name"), + I18N.getString("param.maintainer-email.description"), + BundleParams.PARAM_EMAIL, + String.class, + params -> "Unknown", + (s, p) -> s); + + public static final BundlerParamInfo MAINTAINER = + new StandardBundlerParam<> ( + I18N.getString("param.maintainer-name.name"), + I18N.getString("param.maintainer-name.description"), + Arguments.CLIOptions.LINUX_DEB_MAINTAINER.getId(), + String.class, + params -> VENDOR.fetchFrom(params) + " <" + + EMAIL.fetchFrom(params) + ">", + (s, p) -> s); + + public static final BundlerParamInfo LICENSE_TEXT = + new StandardBundlerParam<> ( + I18N.getString("param.license-text.name"), + I18N.getString("param.license-text.description"), + "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) { + if (Log.isDebug()) { + e.printStackTrace(); + } + } + return "Unknown"; + }, + (s, p) -> s); + + public static final BundlerParamInfo XDG_FILE_PREFIX = + new StandardBundlerParam<> ( + I18N.getString("param.xdg-prefix.name"), + I18N.getString("param.xdg-prefix.description"), + "linux.xdg-prefix", + String.class, + params -> { + try { + String vendor; + if (params.containsKey(VENDOR.getID())) { + vendor = VENDOR.fetchFrom(params); + } else { + vendor = "jpackage"; + } + String appName = APP_FS_NAME.fetchFrom(params); + + return (appName + "-" + vendor).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_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, Log.isDebug(), true); + } 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 p) + throws UnsupportedPlatformException, ConfigException { + try { + if (p == 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(p).validate(p); + + // 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(p); + if (licenseFile == null) { + Log.verbose(I18N.getString("message.debs-like-licenses")); + } + + // only one mime type per association, at least one file extention + List> associations = + FILE_ASSOCIATIONS.fetchFrom(p); + if (associations != null) { + for (int i = 0; i < associations.size(); i++) { + Map assoc = associations.get(i); + List 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(p), p); + + return true; + } catch (RuntimeException re) { + if (re.getCause() instanceof ConfigException) { + throw (ConfigException) re.getCause(); + } else { + throw new ConfigException(re); + } + } + } + + private boolean prepareProto(Map p) + throws IOException { + File appImage = StandardBundlerParam.getPredefinedAppImage(p); + File appDir = null; + + // we either have an application image or need to build one + if (appImage != null) { + appDir = new File(APP_IMAGE_ROOT.fetchFrom(p), + APP_NAME.fetchFrom(p)); + // copy everything from appImage dir into appDir/name + IOUtils.copyRecursive(appImage.toPath(), appDir.toPath()); + } else { + appDir = APP_BUNDLER.fetchFrom(p).doBundle(p, + APP_IMAGE_ROOT.fetchFrom(p), true); + } + return appDir != null; + } + + //@Override + public File bundle(Map p, File outdir) { + if (!outdir.isDirectory() && !outdir.mkdirs()) { + throw new RuntimeException(MessageFormat.format( + I18N.getString("error.cannot-create-output-dir"), + outdir.getAbsolutePath())); + } + if (!outdir.canWrite()) { + throw new RuntimeException(MessageFormat.format( + I18N.getString("error.cannot-write-to-output-dir"), + outdir.getAbsolutePath())); + } + + // we want to create following structure + // + // 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(p); + File configDir = CONFIG_DIR.fetchFrom(p); + + try { + + imageDir.mkdirs(); + configDir.mkdirs(); + if (prepareProto(p) && prepareProjectConfig(p)) { + return buildDeb(p, outdir); + } + return null; + } catch (IOException ex) { + ex.printStackTrace(); + return null; + } + } + + /* + * 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 filePermissions = + PosixFilePermissions.fromString(permissions); + try { + if (file.exists()) { + Files.setPosixFilePermissions(file.toPath(), filePermissions); + } + } catch (IOException ex) { + Logger.getLogger(LinuxDebBundler.class.getName()).log( + Level.SEVERE, null, ex); + } + + } + + private String getArch() { + String arch = System.getProperty("os.arch"); + if ("i386".equals(arch)) + return "i386"; + else + return "amd64"; + } + + private long getInstalledSizeKB(Map 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 params) + throws IOException { + Map data = createReplacementData(params); + File rootDir = LinuxAppBundler.getRootDir(APP_IMAGE_ROOT.fetchFrom( + params), params); + + File iconTarget = getConfig_IconFile(rootDir, params); + File icon = ICON_PNG.fetchFrom(params); + if (!Arguments.CREATE_JRE_INSTALLER.fetchFrom(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 secondaryLauncher : + SECONDARY_LAUNCHERS.fetchFrom(params)) { + Map secondaryLauncherData = + createReplacementData(secondaryLauncher); + secondaryLauncherData.put("APPLICATION_FS_NAME", + data.get("APPLICATION_FS_NAME")); + secondaryLauncherData.put("DESKTOP_MIMES", ""); + + if (!Arguments.CREATE_JRE_INSTALLER.fetchFrom(params)) { + // prepare desktop shortcut + Writer w = new BufferedWriter(new FileWriter( + getConfig_DesktopShortcutFile( + rootDir, secondaryLauncher))); + String content = preprocessTextResource( + getConfig_DesktopShortcutFile(rootDir, + secondaryLauncher).getName(), + I18N.getString("resource.menu-shortcut-descriptor"), + DEFAULT_DESKTOP_FILE_TEMPLATE, + secondaryLauncherData, + VERBOSE.fetchFrom(params), + RESOURCE_DIR.fetchFrom(params)); + w.write(content); + w.close(); + } + + // prepare installer icon + iconTarget = getConfig_IconFile(rootDir, secondaryLauncher); + icon = ICON_PNG.fetchFrom(secondaryLauncher); + 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( + secondaryLauncherData.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( + secondaryLauncherData.get("APPLICATION_LAUNCHER_FILENAME")); + removeScripts.append(".desktop\n"); + } + data.put("SECONDARY_LAUNCHERS_INSTALL", installScripts.toString()); + data.put("SECONDARY_LAUNCHERS_REMOVE", removeScripts.toString()); + + List> 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( + "\n\n"); + StringBuilder registrations = new StringBuilder(); + StringBuilder deregistrations = new StringBuilder(); + StringBuilder desktopMimes = new StringBuilder("MimeType="); + boolean addedEntry = false; + + for (Map assoc : associations) { + // + // Awesome document + // + // + // + + if (assoc == null) { + continue; + } + + String description = FA_DESCRIPTION.fetchFrom(assoc); + File faIcon = FA_ICON.fetchFrom(assoc); + List extensions = FA_EXTENSIONS.fetchFrom(assoc); + if (extensions == null) { + Log.error(I18N.getString( + "message.creating-association-with-null-extension")); + } + + List mimes = FA_CONTENT_TYPE.fetchFrom(assoc); + if (mimes == null || mimes.isEmpty()) { + continue; + } + String thisMime = mimes.get(0); + String dashMime = thisMime.replace('/', '-'); + + mimeInfo.append(" \n"); + if (description != null && !description.isEmpty()) { + mimeInfo.append(" ") + .append(description) + .append("\n"); + } + + if (extensions != null) { + for (String ext : extensions) { + mimeInfo.append(" \n"); + } + } + + mimeInfo.append(" \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(rootDir, + APP_FS_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(""); + + if (addedEntry) { + Writer w = new BufferedWriter(new FileWriter( + new File(rootDir, mimeInfoFile))); + w.write(mimeInfo.toString()); + w.close(); + data.put("FILE_ASSOCIATION_INSTALL", registrations.toString()); + data.put("FILE_ASSOCIATION_REMOVE", deregistrations.toString()); + data.put("DESKTOP_MIMES", desktopMimes.toString()); + } + } + + if (!Arguments.CREATE_JRE_INSTALLER.fetchFrom(params)) { + //prepare desktop shortcut + Writer w = new BufferedWriter(new FileWriter( + getConfig_DesktopShortcutFile(rootDir, params))); + String content = preprocessTextResource( + getConfig_DesktopShortcutFile( + rootDir, params).getName(), + I18N.getString("resource.menu-shortcut-descriptor"), + DEFAULT_DESKTOP_FILE_TEMPLATE, + data, + VERBOSE.fetchFrom(params), + RESOURCE_DIR.fetchFrom(params)); + w.write(content); + w.close(); + } + // prepare control file + Writer w = new BufferedWriter(new FileWriter( + getConfig_ControlFile(params))); + 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); + w.close(); + + w = new BufferedWriter(new FileWriter( + getConfig_PreinstallFile(params))); + 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); + w.close(); + setPermissions(getConfig_PreinstallFile(params), "rwxr-xr-x"); + + w = new BufferedWriter(new FileWriter(getConfig_PrermFile(params))); + 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); + w.close(); + setPermissions(getConfig_PrermFile(params), "rwxr-xr-x"); + + w = new BufferedWriter(new FileWriter( + getConfig_PostinstallFile(params))); + 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); + w.close(); + setPermissions(getConfig_PostinstallFile(params), "rwxr-xr-x"); + + w = new BufferedWriter(new FileWriter(getConfig_PostrmFile(params))); + 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); + w.close(); + setPermissions(getConfig_PostrmFile(params), "rwxr-xr-x"); + + w = new BufferedWriter(new FileWriter(getConfig_CopyrightFile(params))); + 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); + w.close(); + + return true; + } + + private Map createReplacementData( + Map params) { + Map data = new HashMap<>(); + + data.put("APPLICATION_NAME", APP_NAME.fetchFrom(params)); + data.put("APPLICATION_FS_NAME", APP_FS_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", + APP_FS_NAME.fetchFrom(params)); + data.put("INSTALLATION_DIRECTORY", LINUX_INSTALL_DIR.fetchFrom(params)); + data.put("XDG_PREFIX", XDG_FILE_PREFIX.fetchFrom(params)); + data.put("DEPLOY_BUNDLE_CATEGORY", CATEGORY.fetchFrom(params)); + data.put("APPLICATION_DESCRIPTION", DESCRIPTION.fetchFrom(params)); + data.put("APPLICATION_SUMMARY", TITLE.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("CREATE_JRE_INSTALLER", + Arguments.CREATE_JRE_INSTALLER.fetchFrom(params).toString()); + + return data; + } + + private File getConfig_DesktopShortcutFile(File rootDir, + Map params) { + return new File(rootDir, + APP_FS_NAME.fetchFrom(params) + ".desktop"); + } + + private File getConfig_IconFile(File rootDir, + Map params) { + return new File(rootDir, + APP_FS_NAME.fetchFrom(params) + ".png"); + } + + private File getConfig_InitScriptFile(Map params) { + return new File(LinuxAppBundler.getRootDir( + APP_IMAGE_ROOT.fetchFrom(params), params), + BUNDLE_NAME.fetchFrom(params) + ".init"); + } + + private File getConfig_ControlFile(Map params) { + return new File(CONFIG_DIR.fetchFrom(params), "control"); + } + + private File getConfig_PreinstallFile(Map params) { + return new File(CONFIG_DIR.fetchFrom(params), "preinst"); + } + + private File getConfig_PrermFile(Map params) { + return new File(CONFIG_DIR.fetchFrom(params), "prerm"); + } + + private File getConfig_PostinstallFile(Map params) { + return new File(CONFIG_DIR.fetchFrom(params), "postinst"); + } + + private File getConfig_PostrmFile(Map params) { + return new File(CONFIG_DIR.fetchFrom(params), "postrm"); + } + + private File getConfig_CopyrightFile(Map params) { + return new File(CONFIG_DIR.fetchFrom(params), "copyright"); + } + + private File buildDeb(Map 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, false); + + 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 getDescription() { + return I18N.getString("deb.bundler.description"); + } + + @Override + public String getID() { + return "deb"; + } + + @Override + public String getBundleType() { + return "INSTALLER"; + } + + @Override + public Collection> getBundleParameters() { + Collection> results = new LinkedHashSet<>(); + results.addAll(LinuxAppBundler.getAppBundleParameters()); + results.addAll(getDebBundleParameters()); + return results; + } + + public static Collection> getDebBundleParameters() { + return Arrays.asList( + BUNDLE_NAME, + COPYRIGHT, + CATEGORY, + DESCRIPTION, + EMAIL, + ICON_PNG, + LICENSE_FILE, + TITLE, + VENDOR + ); + } + + @Override + public File execute(Map params, + File outputParentDir) { + return bundle(params, outputParentDir); + } + + @Override + public boolean supported() { + return (Platform.getPlatform() == Platform.LINUX); + } + + 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; + } + } +} diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxRpmBundler.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxRpmBundler.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,730 @@ +/* + * Copyright (c) 2012, 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.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.logging.Level; +import java.util.logging.Logger; +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 APP_BUNDLER = + new StandardBundlerParam<>( + I18N.getString("param.rpm-app-bundler.name"), + I18N.getString("param.rpm-app-bundler.description"), + "linux.app.bundler", + LinuxAppBundler.class, + params -> new LinuxAppBundler(), + null); + + public static final BundlerParamInfo RPM_IMAGE_DIR = + new StandardBundlerParam<>( + I18N.getString("param.image-dir.name"), + I18N.getString("param.image-dir.description"), + "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 BUNDLE_NAME = + new StandardBundlerParam<> ( + I18N.getString("param.bundle-name.name"), + I18N.getString("param.bundle-name.description"), + 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 LICENSE_TYPE = + new StandardBundlerParam<>( + I18N.getString("param.license-type.name"), + I18N.getString("param.license-type.description"), + Arguments.CLIOptions.LINUX_RPM_LICENSE_TYPE.getId(), + String.class, + params -> I18N.getString("param.license-type.default"), + (s, p) -> s + ); + + public static final BundlerParamInfo XDG_FILE_PREFIX = + new StandardBundlerParam<> ( + I18N.getString("param.xdg-prefix.name"), + I18N.getString("param.xdg-prefix.description"), + "linux.xdg-prefix", + String.class, + params -> { + try { + String vendor; + if (params.containsKey(VENDOR.getID())) { + vendor = VENDOR.fetchFrom(params); + } else { + vendor = "jpackage"; + } + String appName = APP_FS_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, Log.isDebug(), 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 p) + throws UnsupportedPlatformException, ConfigException { + try { + if (p == 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(p).validate(p); + + // 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> associations = + FILE_ASSOCIATIONS.fetchFrom(p); + if (associations != null) { + for (int i = 0; i < associations.size(); i++) { + Map assoc = associations.get(i); + List 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(p), p); + + return true; + } catch (RuntimeException re) { + if (re.getCause() instanceof ConfigException) { + throw (ConfigException) re.getCause(); + } else { + throw new ConfigException(re); + } + } + } + + private boolean prepareProto(Map p) + throws IOException { + File appImage = StandardBundlerParam.getPredefinedAppImage(p); + File appDir = null; + + // we either have an application image or need to build one + if (appImage != null) { + appDir = new File(RPM_IMAGE_DIR.fetchFrom(p), + APP_NAME.fetchFrom(p)); + // copy everything from appImage dir into appDir/name + IOUtils.copyRecursive(appImage.toPath(), appDir.toPath()); + } else { + appDir = APP_BUNDLER.fetchFrom(p).doBundle(p, + RPM_IMAGE_DIR.fetchFrom(p), true); + } + return appDir != null; + } + + public File bundle(Map p, File outdir) { + if (!outdir.isDirectory() && !outdir.mkdirs()) { + throw new RuntimeException(MessageFormat.format( + I18N.getString("error.cannot-create-output-dir"), + outdir.getAbsolutePath())); + } + if (!outdir.canWrite()) { + throw new RuntimeException(MessageFormat.format( + I18N.getString("error.cannot-write-to-output-dir"), + outdir.getAbsolutePath())); + } + + File imageDir = RPM_IMAGE_DIR.fetchFrom(p); + try { + + imageDir.mkdirs(); + + if (prepareProto(p) && prepareProjectConfig(p)) { + return buildRPM(p, outdir); + } + return null; + } catch (IOException ex) { + ex.printStackTrace(); + return null; + } + } + + private String getLicenseFileString(Map 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_FS_NAME.fetchFrom(params)); + sb.append("/app/"); + sb.append(licenseFile.getName()); + } + + return sb.toString(); + } + + private boolean prepareProjectConfig(Map params) + throws IOException { + Map data = createReplacementData(params); + File rootDir = + LinuxAppBundler.getRootDir(RPM_IMAGE_DIR.fetchFrom(params), params); + + // prepare installer icon + File iconTarget = getConfig_IconFile(rootDir, params); + File icon = LinuxAppBundler.ICON_PNG.fetchFrom(params); + if (!Arguments.CREATE_JRE_INSTALLER.fetchFrom(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 secondaryLauncher : + SECONDARY_LAUNCHERS.fetchFrom(params)) { + Map secondaryLauncherData = + createReplacementData(secondaryLauncher); + secondaryLauncherData.put("APPLICATION_FS_NAME", + data.get("APPLICATION_FS_NAME")); + secondaryLauncherData.put("DESKTOP_MIMES", ""); + + // prepare desktop shortcut + Writer w = new BufferedWriter(new FileWriter( + getConfig_DesktopShortcutFile(rootDir, secondaryLauncher))); + String content = preprocessTextResource( + getConfig_DesktopShortcutFile(rootDir, + secondaryLauncher).getName(), + I18N.getString("resource.menu-shortcut-descriptor"), + DEFAULT_DESKTOP_FILE_TEMPLATE, secondaryLauncherData, + VERBOSE.fetchFrom(params), + RESOURCE_DIR.fetchFrom(params)); + w.write(content); + w.close(); + + // prepare installer icon + iconTarget = getConfig_IconFile(rootDir, secondaryLauncher); + icon = LinuxAppBundler.ICON_PNG.fetchFrom(secondaryLauncher); + 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(secondaryLauncherData.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(secondaryLauncherData.get( + "APPLICATION_LAUNCHER_FILENAME")); + removeScripts.append(".desktop\n"); + + } + data.put("SECONDARY_LAUNCHERS_INSTALL", installScripts.toString()); + data.put("SECONDARY_LAUNCHERS_REMOVE", removeScripts.toString()); + + StringBuilder cdsScript = new StringBuilder(); + + data.put("APP_CDS_CACHE", cdsScript.toString()); + + List> 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( + "\n\n"); + StringBuilder registrations = new StringBuilder(); + StringBuilder deregistrations = new StringBuilder(); + StringBuilder desktopMimes = new StringBuilder("MimeType="); + boolean addedEntry = false; + + for (Map assoc : associations) { + // + // Awesome document + // + // + // + + if (assoc == null) { + continue; + } + + String description = FA_DESCRIPTION.fetchFrom(assoc); + File faIcon = FA_ICON.fetchFrom(assoc); //TODO FA_ICON_PNG + List extensions = FA_EXTENSIONS.fetchFrom(assoc); + if (extensions == null) { + Log.verbose(I18N.getString( + "message.creating-association-with-null-extension")); + } + + List mimes = FA_CONTENT_TYPE.fetchFrom(assoc); + if (mimes == null || mimes.isEmpty()) { + continue; + } + String thisMime = mimes.get(0); + String dashMime = thisMime.replace('/', '-'); + + mimeInfo.append(" \n"); + if (description != null && !description.isEmpty()) { + mimeInfo.append(" ") + .append(description) + .append("\n"); + } + + if (extensions != null) { + for (String ext : extensions) { + mimeInfo.append(" \n"); + } + } + + mimeInfo.append(" \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(rootDir, + APP_FS_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(""); + + if (addedEntry) { + Writer w = new BufferedWriter(new FileWriter( + new File(rootDir, mimeInfoFile))); + w.write(mimeInfo.toString()); + w.close(); + data.put("FILE_ASSOCIATION_INSTALL", registrations.toString()); + data.put("FILE_ASSOCIATION_REMOVE", deregistrations.toString()); + data.put("DESKTOP_MIMES", desktopMimes.toString()); + } + } + + if (!Arguments.CREATE_JRE_INSTALLER.fetchFrom(params)) { + //prepare desktop shortcut + Writer w = new BufferedWriter(new FileWriter( + getConfig_DesktopShortcutFile(rootDir, params))); + String content = preprocessTextResource( + getConfig_DesktopShortcutFile(rootDir, params).getName(), + I18N.getString("resource.menu-shortcut-descriptor"), + DEFAULT_DESKTOP_FILE_TEMPLATE, data, + VERBOSE.fetchFrom(params), + RESOURCE_DIR.fetchFrom(params)); + w.write(content); + w.close(); + } + + // prepare spec file + Writer w = new BufferedWriter( + new FileWriter(getConfig_SpecFile(params))); + 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); + w.close(); + + return true; + } + + private Map createReplacementData( + Map params) throws IOException { + Map data = new HashMap<>(); + + data.put("APPLICATION_NAME", APP_NAME.fetchFrom(params)); + data.put("APPLICATION_FS_NAME", APP_FS_NAME.fetchFrom(params)); + data.put("APPLICATION_PACKAGE", BUNDLE_NAME.fetchFrom(params)); + data.put("APPLICATION_VENDOR", VENDOR.fetchFrom(params)); + data.put("APPLICATION_VERSION", VERSION.fetchFrom(params)); + data.put("APPLICATION_LAUNCHER_FILENAME", + APP_FS_NAME.fetchFrom(params)); + data.put("INSTALLATION_DIRECTORY", LINUX_INSTALL_DIR.fetchFrom(params)); + data.put("XDG_PREFIX", XDG_FILE_PREFIX.fetchFrom(params)); + data.put("DEPLOY_BUNDLE_CATEGORY", CATEGORY.fetchFrom(params)); + // TODO rpm categories + data.put("APPLICATION_DESCRIPTION", DESCRIPTION.fetchFrom(params)); + data.put("APPLICATION_SUMMARY", TITLE.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("CREATE_JRE_INSTALLER", + Arguments.CREATE_JRE_INSTALLER.fetchFrom(params).toString()); + return data; + } + + private File getConfig_DesktopShortcutFile(File rootDir, + Map params) { + return new File(rootDir, + APP_FS_NAME.fetchFrom(params) + ".desktop"); + } + + private File getConfig_IconFile(File rootDir, + Map params) { + return new File(rootDir, + APP_FS_NAME.fetchFrom(params) + ".png"); + } + + private File getConfig_SpecFile(Map params) { + return new File(RPM_IMAGE_DIR.fetchFrom(params), + APP_FS_NAME.fetchFrom(params) + ".spec"); + } + + private File buildRPM(Map params, + File outdir) throws IOException { + Log.verbose(MessageFormat.format(I18N.getString( + "message.outputting-bundle-location"), + outdir.getAbsolutePath())); + + File broot = new File(BUILD_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, false); + + 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 getDescription() { + return I18N.getString("rpm.bundler.description"); + } + + @Override + public String getID() { + return "rpm"; + } + + @Override + public String getBundleType() { + return "INSTALLER"; + } + + @Override + public Collection> getBundleParameters() { + Collection> results = new LinkedHashSet<>(); + results.addAll(LinuxAppBundler.getAppBundleParameters()); + results.addAll(getRpmBundleParameters()); + return results; + } + + public static Collection> getRpmBundleParameters() { + return Arrays.asList( + BUNDLE_NAME, + CATEGORY, + DESCRIPTION, + LinuxAppBundler.ICON_PNG, + LICENSE_FILE, + LICENSE_TYPE, + TITLE, + VENDOR + ); + } + + @Override + public File execute( + Map params, File outputParentDir) { + return bundle(params, outputParentDir); + } + + @Override + public boolean supported() { + return (Platform.getPlatform() == Platform.LINUX); + } + + 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; + } + } +} diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/LinuxResources.properties --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/LinuxResources.properties Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,109 @@ +# +# Copyright (c) 2017, 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. +# +# + +app.bundler.name=Linux Application Image +app.bundler.description=A Directory based image of a linux Application with an optionally co-bundled JRE. Used as a base for the Installer bundlers. +deb.bundler.name=DEB Installer +deb.bundler.description=Linux Debian Bundle. +rpm.bundler.name=RPM Bundle +rpm.bundler.description=Redhat Package Manager (RPM) bundler. + +param.icon-png.name=.png Icon +param.icon-png.description=Icon for the application, in PNG format. +param.linux-install-dir.name=Linux Installation Directory +param.linux-install-dir.description=Installation directory of the application on Linux. +param.linux-package-dependencies.name=Linux Package Dependencies +param.linux-package-dependencies.description=Required packages or capabilities for the application. +param.deb-app-bundler.name=DEB Bundler Name +param.deb-app-bundler.description=DEB Bundler Name +param.full-package-name.name=Deb Package Name +param.full-package-name.description=Deb Package Name +param.image-dir.name=Image Dir +param.image-dir.description=Image Dir +param.app-image-root.name=Image Root Dir +param.app-image-root.description=Image Root Dir +param.config-dir.name=Config Dir +param.config-dir.description=Config Dir +param.maintainer-email.name=DEB Maintainer Email +param.maintainer-email.description=DEB Maintainer Email +param.maintainer-name.name=DEB Maintainer Name +param.maintainer-name.description=DEB Maintainer Name +param.license-type.name=License Type +param.license-type.description=License Type +param.license-type.default=Unknown +param.license-text.name=License Content +param.license-text.description=License Content +param.xdg-prefix.name=Prefix for XDG files (mime, desktop) +param.xdg-prefix.description=Prefix for XDG MimeInfo and Desktop Files. Defaults to -, with spaces dropped. +param.rpm-app-bundler.name=RPM Bundler +param.rpm-app-bundler.description=RPM Bundler +param.bundle-name.name=RPM Bundle +param.bundle-name.description=RPM Bundle + +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.deb-init-script=DEB init script +resource.menu-shortcut-descriptor=Menu shortcut descriptor +resource.menu-icon=menu icon +resource.rpm-spec-file=RPM spec file +resource.rpm-init-script=RPM init script + + +error.parameters-null=Parameters map is null. +error.parameters-null.advice=Pass in a non-null parameters map. +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.launcher-name-too-long=The bundle name "{0}" is too long for a daemon. +error.launcher-name-too-long.advice=Set a bundler argument "{0}" to a bundle name that is shorter than 16 characters. +error.cannot-create-output-dir=Output directory {0} cannot be created. +error.cannot-write-to-output-dir=Output directory {0} is not writable. +error.no-content-types-for-file-association=No MIME types were specified for File Association number {0}. +error.no-content-types-for-file-association.advice=For Linux Bundling specify one and only one MIME type for each file association. +error.too-many-content-types-for-file-association=More than one MIME types was specified for File Association number {0}. +error.too-many-content-types-for-file-association.advice=For Linux Bundling specify one and only one MIME type for each file association. +error.no-support-for-peruser-daemons=Bundler doesn't support per-user daemons. +error.no-support-for-peruser-daemons.advice=Make sure that the system wide hint is set to true. +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.one-shortcut-required=At least one type of shortcut is required. Enabling menu shortcut. +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. diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/LinuxResources_ja.properties --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/LinuxResources_ja.properties Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,109 @@ +# +# Copyright (c) 2017, 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. +# +# + +app.bundler.name=Linux Application Image +app.bundler.description=A Directory based image of a linux Application with an optionally co-bundled JRE. Used as a base for the Installer bundlers. +deb.bundler.name=DEB Installer +deb.bundler.description=Linux Debian Bundle. +rpm.bundler.name=RPM Bundle +rpm.bundler.description=Redhat Package Manager (RPM) bundler. + +param.icon-png.name=.png Icon +param.icon-png.description=Icon for the application, in PNG format. +param.linux-install-dir.name=Linux Installation Directory +param.linux-install-dir.description=Installation directory of the application on Linux. +param.linux-package-dependencies.name=Linux Package Dependencies +param.linux-package-dependencies.description=Required packages or capabilities for the application. +param.deb-app-bundler.name=DEB Bundler Name +param.deb-app-bundler.description=DEB Bundler Name +param.full-package-name.name=Deb Package Name +param.full-package-name.description=Deb Package Name +param.image-dir.name=Image Dir +param.image-dir.description=Image Dir +param.app-image-root.name=Image Root Dir +param.app-image-root.description=Image Root Dir +param.config-dir.name=Config Dir +param.config-dir.description=Config Dir +param.maintainer-email.name=DEB Maintainer Email +param.maintainer-email.description=DEB Maintainer Email +param.maintainer-name.name=DEB Maintainer Name +param.maintainer-name.description=DEB Maintainer Name +param.license-type.name=License Type +param.license-type.description=License Type +param.license-type.default=Unknown +param.license-text.name=License Content +param.license-text.description=License Content +param.xdg-prefix.name=Prefix for XDG files (mime, desktop) +param.xdg-prefix.description=Prefix for XDG MimeInfo and Desktop Files. Defaults to -, with spaces dropped. +param.rpm-app-bundler.name=RPM Bundler +param.rpm-app-bundler.description=RPM Bundler +param.bundle-name.name=RPM Bundle +param.bundle-name.description=RPM Bundle + +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.deb-init-script=DEB init script +resource.menu-shortcut-descriptor=Menu shortcut descriptor +resource.menu-icon=menu icon +resource.rpm-spec-file=RPM spec file +resource.rpm-init-script=RPM init script + + +error.parameters-null=Parameters map is null. +error.parameters-null.advice=Pass in a non-null parameters map. +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.launcher-name-too-long=The bundle name "{0}" is too long for a daemon. +error.launcher-name-too-long.advice=Set a bundler argument "{0}" to a bundle name that is shorter than 16 characters. +error.cannot-create-output-dir=Output directory {0} cannot be created. +error.cannot-write-to-output-dir=Output directory {0} is not writable. +error.no-content-types-for-file-association=No MIME types were specified for File Association number {0}. +error.no-content-types-for-file-association.advice=For Linux Bundling specify one and only one MIME type for each file association. +error.too-many-content-types-for-file-association=More than one MIME types was specified for File Association number {0}. +error.too-many-content-types-for-file-association.advice=For Linux Bundling specify one and only one MIME type for each file association. +error.no-support-for-peruser-daemons=Bundler doesn't support per-user daemons. +error.no-support-for-peruser-daemons.advice=Make sure that the system wide hint is set to true. +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.one-shortcut-required=At least one type of shortcut is required. Enabling menu shortcut. +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. diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/LinuxResources_zh_CN.properties --- /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 Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,109 @@ +# +# Copyright (c) 2017, 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. +# +# + +app.bundler.name=Linux Application Image +app.bundler.description=A Directory based image of a linux Application with an optionally co-bundled JRE. Used as a base for the Installer bundlers. +deb.bundler.name=DEB Installer +deb.bundler.description=Linux Debian Bundle. +rpm.bundler.name=RPM Bundle +rpm.bundler.description=Redhat Package Manager (RPM) bundler. + +param.icon-png.name=.png Icon +param.icon-png.description=Icon for the application, in PNG format. +param.linux-install-dir.name=Linux Installation Directory +param.linux-install-dir.description=Installation directory of the application on Linux. +param.linux-package-dependencies.name=Linux Package Dependencies +param.linux-package-dependencies.description=Required packages or capabilities for the application. +param.deb-app-bundler.name=DEB Bundler Name +param.deb-app-bundler.description=DEB Bundler Name +param.full-package-name.name=Deb Package Name +param.full-package-name.description=Deb Package Name +param.image-dir.name=Image Dir +param.image-dir.description=Image Dir +param.app-image-root.name=Image Root Dir +param.app-image-root.description=Image Root Dir +param.config-dir.name=Config Dir +param.config-dir.description=Config Dir +param.maintainer-email.name=DEB Maintainer Email +param.maintainer-email.description=DEB Maintainer Email +param.maintainer-name.name=DEB Maintainer Name +param.maintainer-name.description=DEB Maintainer Name +param.license-type.name=License Type +param.license-type.description=License Type +param.license-type.default=Unknown +param.license-text.name=License Content +param.license-text.description=License Content +param.xdg-prefix.name=Prefix for XDG files (mime, desktop) +param.xdg-prefix.description=Prefix for XDG MimeInfo and Desktop Files. Defaults to -, with spaces dropped. +param.rpm-app-bundler.name=RPM Bundler +param.rpm-app-bundler.description=RPM Bundler +param.bundle-name.name=RPM Bundle +param.bundle-name.description=RPM Bundle + +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.deb-init-script=DEB init script +resource.menu-shortcut-descriptor=Menu shortcut descriptor +resource.menu-icon=menu icon +resource.rpm-spec-file=RPM spec file +resource.rpm-init-script=RPM init script + + +error.parameters-null=Parameters map is null. +error.parameters-null.advice=Pass in a non-null parameters map. +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.launcher-name-too-long=The bundle name "{0}" is too long for a daemon. +error.launcher-name-too-long.advice=Set a bundler argument "{0}" to a bundle name that is shorter than 16 characters. +error.cannot-create-output-dir=Output directory {0} cannot be created. +error.cannot-write-to-output-dir=Output directory {0} is not writable. +error.no-content-types-for-file-association=No MIME types were specified for File Association number {0}. +error.no-content-types-for-file-association.advice=For Linux Bundling specify one and only one MIME type for each file association. +error.too-many-content-types-for-file-association=More than one MIME types was specified for File Association number {0}. +error.too-many-content-types-for-file-association.advice=For Linux Bundling specify one and only one MIME type for each file association. +error.no-support-for-peruser-daemons=Bundler doesn't support per-user daemons. +error.no-support-for-peruser-daemons.advice=Make sure that the system wide hint is set to true. +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.one-shortcut-required=At least one type of shortcut is required. Enabling menu shortcut. +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. diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/javalogo_white_32.png Binary file src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/javalogo_white_32.png has changed diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/template.control --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/template.control Thu Jan 10 13:13:56 2019 -0500 @@ -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_SUMMARY +Installed-Size: APPLICATION_INSTALLED_SIZE +PACKAGE_DEPENDENCIES diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/template.copyright --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/template.copyright Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,8 @@ + +Copyright: + + APPLICATION_COPYRIGHT + +License: + + APPLICATION_LICENSE_TEXT diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/template.desktop --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/template.desktop Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,9 @@ +[Desktop Entry] +Name=APPLICATION_NAME +Comment=APPLICATION_SUMMARY +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 diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/template.postinst --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/template.postinst Thu Jan 10 13:13:56 2019 -0500 @@ -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: +# * `configure' +# * `abort-upgrade' +# * `abort-remove' `in-favour' +# +# * `abort-remove' +# * `abort-deconfigure' `in-favour' +# `removing' +# +# for details, see http://www.debian.org/doc/debian-policy/ or +# the debian-policy package + +case "$1" in + configure) + if [ "CREATE_JRE_INSTALLER" != "true" ]; then + echo Adding shortcut to the menu +SECONDARY_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 diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/template.postrm --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/template.postrm Thu Jan 10 13:13:56 2019 -0500 @@ -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: +# * `remove' +# * `purge' +# * `upgrade' +# * `failed-upgrade' +# * `abort-install' +# * `abort-install' +# * `abort-upgrade' +# * `disappear' +# +# 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 diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/template.preinst --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/template.preinst Thu Jan 10 13:13:56 2019 -0500 @@ -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: +# * `install' +# * `install' +# * `upgrade' +# * `abort-upgrade' +# 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 diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/template.prerm --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/template.prerm Thu Jan 10 13:13:56 2019 -0500 @@ -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: +# * `remove' +# * `upgrade' +# * `failed-upgrade' +# * `remove' `in-favour' +# * `deconfigure' `in-favour' +# `removing' +# +# for details, see http://www.debian.org/doc/debian-policy/ or +# the debian-policy package + + +case "$1" in + remove|upgrade|deconfigure) + if [ "CREATE_JRE_INSTALLER" != "true" ]; then + echo Removing shortcut +SECONDARY_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 + diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/template.spec --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/template.spec Thu Jan 10 13:13:56 2019 -0500 @@ -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 [ "CREATE_JRE_INSTALLER" != "true" ]; then +SECONDARY_LAUNCHERS_INSTALL + xdg-desktop-menu install --novendor INSTALLATION_DIRECTORY/APPLICATION_FS_NAME/APPLICATION_LAUNCHER_FILENAME.desktop +FILE_ASSOCIATION_INSTALL +fi + +%preun +if [ "CREATE_JRE_INSTALLER" != "true" ]; then +SECONDARY_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 diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/linux/classes/module-info.java.extra --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/linux/classes/module-info.java.extra Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,30 @@ +/* + * Copyright (c) 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. + */ + +provides jdk.jpackage.internal.Bundler with + jdk.jpackage.internal.LinuxAppBundler, + jdk.jpackage.internal.LinuxDebBundler, + jdk.jpackage.internal.LinuxRpmBundler; + diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/linux/native/jpackageapplauncher/launcher.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/linux/native/jpackageapplauncher/launcher.cpp Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2014, 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. + */ + +#include +#include +#include +#include +#include +#include + + +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; +} diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacAppBundler.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacAppBundler.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,421 @@ +/* + * Copyright (c) 2012, 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.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.*; +import jdk.jpackage.internal.AbstractAppImageBuilder; + +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"; + + private static Map getMacCategories() { + Map 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 MAC_CATEGORY = + new EnumeratedBundlerParam<>( + I18N.getString("param.category-name"), + I18N.getString("param.category-name.description"), + Arguments.CLIOptions.MAC_APP_STORE_CATEGORY.getId(), + String.class, + params -> params.containsKey(CATEGORY.getID()) + ? CATEGORY.fetchFrom(params) + : "Unknown", + (s, p) -> s, + getMacCategories(), + false //strict - for MacStoreBundler this should be strict + ); + + public static final BundlerParamInfo MAC_CF_BUNDLE_NAME = + new StandardBundlerParam<>( + I18N.getString("param.cfbundle-name.name"), + I18N.getString("param.cfbundle-name.description"), + Arguments.CLIOptions.MAC_BUNDLE_NAME.getId(), + String.class, + params -> null, + (s, p) -> s); + + public static final BundlerParamInfo MAC_CF_BUNDLE_IDENTIFIER = + new StandardBundlerParam<>( + I18N.getString("param.cfbundle-identifier.name"), + I18N.getString("param.cfbundle-identifier.description"), + Arguments.CLIOptions.MAC_BUNDLE_IDENTIFIER.getId(), + String.class, + IDENTIFIER::fetchFrom, + (s, p) -> s); + + public static final BundlerParamInfo MAC_CF_BUNDLE_VERSION = + new StandardBundlerParam<>( + I18N.getString("param.cfbundle-version.name"), + I18N.getString("param.cfbundle-version.description"), + "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 DEFAULT_ICNS_ICON = + new StandardBundlerParam<>( + I18N.getString("param.default-icon-icns"), + I18N.getString("param.default-icon-icns.description"), + ".mac.default.icns", + String.class, + params -> TEMPLATE_BUNDLE_ICON, + (s, p) -> s); + + public static final BundlerParamInfo DEVELOPER_ID_APP_SIGNING_KEY = + new StandardBundlerParam<>( + I18N.getString("param.signing-key-developer-id-app.name"), + I18N.getString("param.signing-key-developer-id-app.description"), + "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 BUNDLE_ID_SIGNING_PREFIX = + new StandardBundlerParam<>( + I18N.getString("param.bundle-id-signing-prefix.name"), + I18N.getString("param.bundle-id-signing-prefix.description"), + Arguments.CLIOptions.MAC_BUNDLE_SIGNING_PREFIX.getId(), + String.class, + params -> IDENTIFIER.fetchFrom(params) + ".", + (s, p) -> s); + + public static final BundlerParamInfo ICON_ICNS = + new StandardBundlerParam<>( + I18N.getString("param.icon-icns.name"), + I18N.getString("param.icon-icns.description"), + "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 params) + throws UnsupportedPlatformException, 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 p) + throws UnsupportedPlatformException, ConfigException { + if (Platform.getPlatform() != Platform.MAC) { + throw new UnsupportedPlatformException(); + } + + imageBundleValidation(p); + + if (StandardBundlerParam.getPredefinedAppImage(p) != null) { + return true; + } + + // validate short version + if (!validCFBundleVersion(MAC_CF_BUNDLE_VERSION.fetchFrom(p))) { + 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(p)).orElse(Boolean.FALSE)) { + String signingIdentity = DEVELOPER_ID_APP_SIGNING_KEY.fetchFrom(p); + 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 p, File outputDirectory, + boolean dependentTask) { + if (Arguments.CREATE_JRE_INSTALLER.fetchFrom(p)) { + return doJreBundle(p, outputDirectory, dependentTask); + } else { + return doAppBundle(p, outputDirectory, dependentTask); + } + } + + File doJreBundle(Map p, + File outputDirectory, boolean dependentTask) { + try { + File rootDirectory = createRoot(p, outputDirectory, dependentTask, + APP_NAME.fetchFrom(p), "macapp-image-builder"); + AbstractAppImageBuilder appBuilder = new MacAppImageBuilder(p, + APP_NAME.fetchFrom(p), outputDirectory.toPath()); + File predefined = PREDEFINED_RUNTIME_IMAGE.fetchFrom(p); + if (predefined == null ) { + JLinkBundlerHelper.generateJre(p, appBuilder); + } else { + return predefined; + } + return rootDirectory; + } catch (Exception ex) { + Log.error("Exception: "+ex); + Log.verbose(ex); + return null; + } + } + + File doAppBundle(Map p, File outputDirectory, + boolean dependentTask) { + try { + File rootDirectory = createRoot(p, outputDirectory, dependentTask, + APP_NAME.fetchFrom(p) + ".app", "macapp-image-builder"); + AbstractAppImageBuilder appBuilder = + new MacAppImageBuilder(p, outputDirectory.toPath()); + if (PREDEFINED_RUNTIME_IMAGE.fetchFrom(p) == null ) { + JLinkBundlerHelper.execute(p, appBuilder); + } else { + StandardBundlerParam.copyPredefinedRuntimeImage(p, appBuilder); + } + return rootDirectory; + } catch (Exception ex) { + Log.error("Exception: "+ex); + Log.verbose(ex); + return null; + } + } + + ///////////////////////////////////////////////////////////////////////// + // Implement Bundler + ///////////////////////////////////////////////////////////////////////// + + @Override + public String getName() { + return I18N.getString("app.bundler.name"); + } + + @Override + public String getDescription() { + return I18N.getString("app.bundler.description"); + } + + @Override + public String getID() { + return "mac.app"; + } + + @Override + public String getBundleType() { + return "IMAGE"; + } + + @Override + public Collection> getBundleParameters() { + return getAppBundleParameters(); + } + + public static Collection> getAppBundleParameters() { + return Arrays.asList( + APP_NAME, + APP_RESOURCES, + ARGUMENTS, + BUNDLE_ID_SIGNING_PREFIX, + CLASSPATH, + DEVELOPER_ID_APP_SIGNING_KEY, + ICON_ICNS, + JVM_OPTIONS, + MAC_CATEGORY, + MAC_CF_BUNDLE_IDENTIFIER, + MAC_CF_BUNDLE_NAME, + MAC_CF_BUNDLE_VERSION, + MAIN_CLASS, + MAIN_JAR, + PREFERENCES_ID, + SIGNING_KEYCHAIN, + VERSION, + VERBOSE + ); + } + + + @Override + public File execute(Map params, + File outputParentDir) { + return doBundle(params, outputParentDir, false); + } + + @Override + public boolean supported() { + return Platform.getPlatform() == Platform.MAC; + } + +} diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacAppImageBuilder.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacAppImageBuilder.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,975 @@ +/* + * Copyright (c) 2015, 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.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 params; + + private static List keyChains; + + public static final BundlerParamInfo + MAC_CONFIGURE_LAUNCHER_IN_PLIST = new StandardBundlerParam<>( + I18N.getString("param.configure-launcher-in-plist"), + I18N.getString( + "param.configure-launcher-in-plist.description"), + "mac.configure-launcher-in-plist", + Boolean.class, + params -> Boolean.FALSE, + (s, p) -> Boolean.valueOf(s)); + + public static final BundlerParamInfo MAC_CATEGORY = + new StandardBundlerParam<>( + I18N.getString("param.category-name"), + I18N.getString("param.category-name.description"), + "mac.category", + String.class, + CATEGORY::fetchFrom, + (s, p) -> s + ); + + public static final BundlerParamInfo MAC_CF_BUNDLE_NAME = + new StandardBundlerParam<>( + I18N.getString("param.cfbundle-name.name"), + I18N.getString("param.cfbundle-name.description"), + "mac.CFBundleName", + String.class, + params -> null, + (s, p) -> s); + + public static final BundlerParamInfo MAC_CF_BUNDLE_IDENTIFIER = + new StandardBundlerParam<>( + I18N.getString("param.cfbundle-identifier.name"), + I18N.getString("param.cfbundle-identifier.description"), + Arguments.CLIOptions.MAC_BUNDLE_IDENTIFIER.getId(), + String.class, + IDENTIFIER::fetchFrom, + (s, p) -> s); + + public static final BundlerParamInfo MAC_CF_BUNDLE_VERSION = + new StandardBundlerParam<>( + I18N.getString("param.cfbundle-version.name"), + I18N.getString("param.cfbundle-version.description"), + "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 DEFAULT_ICNS_ICON = + new StandardBundlerParam<>( + I18N.getString("param.default-icon-icns"), + I18N.getString("param.default-icon-icns.description"), + ".mac.default.icns", + String.class, + params -> TEMPLATE_BUNDLE_ICON, + (s, p) -> s); + + public static final BundlerParamInfo ICON_ICNS = + new StandardBundlerParam<>( + I18N.getString("param.icon-icns.name"), + I18N.getString("param.icon-icns.description"), + "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 SIGN_BUNDLE = + new StandardBundlerParam<>( + I18N.getString("param.sign-bundle.name"), + I18N.getString("param.sign-bundle.description"), + 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 config, Path imageOutDir) + throws IOException { + super(config, imageOutDir.resolve(APP_NAME.fetchFrom(config) + + ".app/Contents/PlugIns/Java.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("PlugIns/Java.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 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); + } + + // chmod ugo+x file + private void setExecutable(Path file) { + try { + Set perms = + Files.getPosixFilePermissions(file); + perms.add(PosixFilePermission.OWNER_EXECUTE); + perms.add(PosixFilePermission.GROUP_EXECUTE); + perms.add(PosixFilePermission.OTHERS_EXECUTE); + Files.setPosixFilePermissions(file, perms); + } catch (IOException ioe) { + throw new UncheckedIOException(ioe); + } + } + + private static void createUtf8File(File file, String content) + throws IOException { + try (OutputStream fout = new FileOutputStream(file); + Writer output = new OutputStreamWriter(fout, "UTF-8")) { + output.write(content); + } + } + + 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 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/PlugIns/Java.runtime"); + + // create secondary app launcher(s) and config file(s) + List> entryPoints = + StandardBundlerParam.SECONDARY_LAUNCHERS.fetchFrom(params); + for (Map entryPoint : entryPoints) { + Map tmp = new HashMap<>(originalParams); + tmp.putAll(entryPoint); + + // add executable for secondary launcher + Path secondaryExecutable = macOSDir.resolve(getLauncherName(tmp)); + try (InputStream is = getResourceAsStream("jpackageapplauncher");) { + writeEntry(is, secondaryExecutable); + } + secondaryExecutable.toFile().setExecutable(true, false); + + // add config file for secondary launcher + cfg = new File(root.toFile(), getLauncherCfgName(tmp)); + writeCfgFile(tmp, cfg, "$APPDIR/PlugIns/Java.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 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 params) { + if (APP_NAME.fetchFrom(params) != null) { + return APP_NAME.fetchFrom(params); + } else { + return MAIN_CLASS.fetchFrom(params); + } + } + + public static String getLauncherCfgName(Map p) { + return "Contents/Java/" + APP_NAME.fetchFrom(p) + ".cfg"; + } + + private void copyClassPathEntries(Path javaDirectory) throws IOException { + List 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 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 data = new HashMap<>(); + String identifier = Arguments.CREATE_JRE_INSTALLER.fetchFrom(params) ? + MAC_CF_BUNDLE_IDENTIFIER.fetchFrom(params) : + "com.oracle.java." + MAC_CF_BUNDLE_IDENTIFIER.fetchFrom(params); + data.put("CF_BUNDLE_IDENTIFIER", identifier); + String name = Arguments.CREATE_JRE_INSTALLER.fetchFrom(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)); + + Writer w = new BufferedWriter(new FileWriter(file)); + w.write(preprocessTextResource("Runtime-Info.plist", + I18N.getString("resource.runtime-info-plist"), + TEMPLATE_RUNTIME_INFO_PLIST, + data, + VERBOSE.fetchFrom(params), + RESOURCE_DIR.fetchFrom(params))); + w.close(); + } + + 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 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/PlugIns/Java.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)); + } + + data.put("DEPLOY_PREFERENCES_ID", + PREFERENCES_ID.fetchFrom(params).toLowerCase()); + + StringBuilder sb = new StringBuilder(); + List jvmOptions = JVM_OPTIONS.fetchFrom(params); + + String newline = ""; //So we don't add extra line after last append + for (String o : jvmOptions) { + sb.append(newline).append( + " ").append(o).append(""); + newline = "\n"; + } + + data.put("DEPLOY_JVM_OPTIONS", sb.toString()); + + sb = new StringBuilder(); + List args = ARGUMENTS.fetchFrom(params); + newline = ""; + // So we don't add unneccessary extra line after last append + + for (String o : args) { + sb.append(newline).append(" ").append(o).append( + ""); + 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 + fileAssociation : FILE_ASSOCIATIONS.fetchFrom(params)) { + + List extensions = FA_EXTENSIONS.fetchFrom(fileAssociation); + + if (extensions == null) { + Log.verbose(I18N.getString( + "message.creating-association-with-null-extension")); + } + + List 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); //TODO FA_ICON_ICNS + + bundleDocumentTypes.append(" \n") + .append(" LSItemContentTypes\n") + .append(" \n") + .append(" ") + .append(itemContentType) + .append("\n") + .append(" \n") + .append("\n") + .append(" CFBundleTypeName\n") + .append(" ") + .append(description) + .append("\n") + .append("\n") + .append(" LSHandlerRank\n") + .append(" Owner\n") + // TODO make a bundler arg + .append("\n") + .append(" CFBundleTypeRole\n") + .append(" Editor\n") + // TODO make a bundler arg + .append("\n") + .append(" LSIsAppleDefaultForType\n") + .append(" \n") + // TODO make a bundler arg + .append("\n"); + + if (icon != null && icon.exists()) { + bundleDocumentTypes + .append(" CFBundleTypeIconFile\n") + .append(" ") + .append(icon.getName()) + .append("\n"); + } + bundleDocumentTypes.append(" \n"); + + exportedTypes.append(" \n") + .append(" UTTypeIdentifier\n") + .append(" ") + .append(itemContentType) + .append("\n") + .append("\n") + .append(" UTTypeDescription\n") + .append(" ") + .append(description) + .append("\n") + .append(" UTTypeConformsTo\n") + .append(" \n") + .append(" public.data\n") + //TODO expose this? + .append(" \n") + .append("\n"); + + if (icon != null && icon.exists()) { + exportedTypes.append(" UTTypeIconFile\n") + .append(" ") + .append(icon.getName()) + .append("\n") + .append("\n"); + } + + exportedTypes.append("\n") + .append(" UTTypeTagSpecification\n") + .append(" \n") + // TODO expose via param? .append( + // " com.apple.ostype\n"); + // TODO expose via param? .append( + // " ABCD\n") + .append("\n"); + + if (extensions != null && !extensions.isEmpty()) { + exportedTypes.append( + " public.filename-extension\n") + .append(" \n"); + + for (String ext : extensions) { + exportedTypes.append(" ") + .append(ext) + .append("\n"); + } + exportedTypes.append(" \n"); + } + if (mimeTypes != null && !mimeTypes.isEmpty()) { + exportedTypes.append(" public.mime-type\n") + .append(" \n"); + + for (String mime : mimeTypes) { + exportedTypes.append(" ") + .append(mime) + .append("\n"); + } + exportedTypes.append(" \n"); + } + exportedTypes.append(" \n") + .append(" \n"); + } + String associationData; + if (bundleDocumentTypes.length() > 0) { + associationData = + "\n CFBundleDocumentTypes\n \n" + + bundleDocumentTypes.toString() + + " \n\n" + + " UTExportedTypeDeclarations\n \n" + + exportedTypes.toString() + + " \n"; + } else { + associationData = ""; + } + data.put("DEPLOY_FILE_ASSOCIATIONS", associationData); + + + Writer w = new BufferedWriter(new FileWriter(file)); + 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))); + w.close(); + } + + private void writePkgInfo(File file) throws IOException { + //hardcoded as it does not seem we need to change it ever + String signature = "????"; + + try (Writer out = new BufferedWriter(new FileWriter(file))) { + out.write(OS_TYPE_CODE + signature); + out.flush(); + } + } + + public static void addNewKeychain(Map 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 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 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, false); + } + + public static void restoreKeychainList(Map 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 args = new ArrayList<>(); + args.add("security"); + args.add("list-keychains"); + args.add("-s"); + + args.addAll(keyChains); + + ProcessBuilder pb = new ProcessBuilder(args); + IOUtils.exec(pb, false); + } + + public static void signAppBundle( + Map params, Path appLocation, + String signingIdentity, String identifierPrefix, + String entitlementsFile, String inheritedEntitlements) + throws IOException { + AtomicReference 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 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().contains( + "/Contents/MacOS/JavaAppletPlugin") + || 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 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 oldPermissions = + Files.getPosixFilePermissions(p); + File f = p.toFile(); + f.setWritable(true, true); + + ProcessBuilder pb = new ProcessBuilder(args); + IOUtils.exec(pb, false); + + Files.setPosixFilePermissions(p, oldPermissions); + } catch (IOException ioe) { + toThrow.set(ioe); + } + } + }); + + IOException ioe = toThrow.get(); + if (ioe != null) { + throw ioe; + } + + // sign all plugins and frameworks + Consumer signIdentifiedByPList = path -> { + //noinspection ThrowableResultOfMethodCallIgnored + if (toThrow.get() != null) return; + + try { + List 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, false); + + 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, false); + } catch (IOException e) { + toThrow.set(e); + } + }; + + Path pluginsPath = appLocation.resolve("Contents/PlugIns"); + if (Files.isDirectory(pluginsPath)) { + Files.list(pluginsPath) + .forEach(signIdentifiedByPList); + + 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 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, false); + } + +} diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacAppStoreBundler.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacAppStoreBundler.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,373 @@ +/* + * Copyright (c) 2014, 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.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 MAC_APP_STORE_APP_SIGNING_KEY = + new StandardBundlerParam<>( + I18N.getString("param.signing-key-app.name"), + I18N.getString("param.signing-key-app.description"), + "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 MAC_APP_STORE_PKG_SIGNING_KEY = + new StandardBundlerParam<>( + I18N.getString("param.signing-key-pkg.name"), + I18N.getString("param.signing-key-pkg.description"), + "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 MAC_APP_STORE_ENTITLEMENTS = + new StandardBundlerParam<>( + I18N.getString("param.mac-app-store-entitlements.name"), + I18N.getString("param.mac-app-store-entitlements.description"), + Arguments.CLIOptions.MAC_APP_STORE_ENTITLEMENTS.getId(), + File.class, + params -> null, + (s, p) -> new File(s)); + + public static final BundlerParamInfo INSTALLER_SUFFIX = + new StandardBundlerParam<> ( + I18N.getString("param.installer-suffix.name"), + I18N.getString("param.installer-suffix.description"), + "mac.app-store.installerName.suffix", + String.class, + params -> "-MacAppStore", + (s, p) -> s); + + //@Override + public File bundle(Map p, File outdir) { + Log.verbose(MessageFormat.format(I18N.getString( + "message.building-bundle"), APP_NAME.fetchFrom(p))); + if (!outdir.isDirectory() && !outdir.mkdirs()) { + throw new RuntimeException(MessageFormat.format(I18N.getString( + "error.cannot-create-output-dir"), + outdir.getAbsolutePath())); + } + if (!outdir.canWrite()) { + throw new RuntimeException(MessageFormat.format(I18N.getString( + "error.cannot-write-to-output-dir"), + outdir.getAbsolutePath())); + } + + // first, load in some overrides + // icns needs @2 versions, so load in the @2 default + p.put(DEFAULT_ICNS_ICON.getID(), TEMPLATE_BUNDLE_ICON_HIDPI); + + // now we create the app + File appImageDir = APP_IMAGE_BUILD_ROOT.fetchFrom(p); + try { + appImageDir.mkdirs(); + + try { + MacAppImageBuilder.addNewKeychain(p); + } catch (InterruptedException e) { + Log.error(e.getMessage()); + } + // first, make sure we don't use the local signing key + p.put(DEVELOPER_ID_APP_SIGNING_KEY.getID(), null); + File appLocation = prepareAppBundle(p, false); + + prepareEntitlements(p); + + String signingIdentity = MAC_APP_STORE_APP_SIGNING_KEY.fetchFrom(p); + String identifierPrefix = BUNDLE_ID_SIGNING_PREFIX.fetchFrom(p); + String entitlementsFile = getConfig_Entitlements(p).toString(); + String inheritEntitlements = + getConfig_Inherit_Entitlements(p).toString(); + + MacAppImageBuilder.signAppBundle(p, appLocation.toPath(), + signingIdentity, identifierPrefix, + entitlementsFile, inheritEntitlements); + MacAppImageBuilder.restoreKeychainList(p); + + ProcessBuilder pb; + + // create the final pkg file + File finalPKG = new File(outdir, INSTALLER_NAME.fetchFrom(p) + + INSTALLER_SUFFIX.fetchFrom(p) + + ".pkg"); + outdir.mkdirs(); + + String installIdentify = + MAC_APP_STORE_PKG_SIGNING_KEY.fetchFrom(p); + + List 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(p); + if (keychainName != null && !keychainName.isEmpty()) { + buildOptions.add("--keychain"); + buildOptions.add(keychainName); + } + buildOptions.add(finalPKG.getAbsolutePath()); + + pb = new ProcessBuilder(buildOptions); + + IOUtils.exec(pb, false); + return finalPKG; + } catch (Exception ex) { + Log.error("App Store Ready Bundle failed : " + ex.getMessage()); + Log.verbose(ex); + return null; + } + } + + private File getConfig_Entitlements(Map params) { + return new File(CONFIG_ROOT.fetchFrom(params), + APP_NAME.fetchFrom(params) + ".entitlements"); + } + + private File getConfig_Inherit_Entitlements( + Map params) { + return new File(CONFIG_ROOT.fetchFrom(params), + APP_NAME.fetchFrom(params) + "_Inherit.entitlements"); + } + + private void prepareEntitlements(Map 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 params) { + return APP_NAME.fetchFrom(params) + ".entitlements"; + } + + private String getInheritEntitlementsFileName( + Map params) { + return APP_NAME.fetchFrom(params) + "_Inherit.entitlements"; + } + + + /////////////////////////////////////////////////////////////////////// + // Implement Bundler + /////////////////////////////////////////////////////////////////////// + + @Override + public String getName() { + return I18N.getString("store.bundler.name"); + } + + @Override + public String getDescription() { + return I18N.getString("store.bundler.description"); + } + + @Override + public String getID() { + return "mac.appStore"; + } + + @Override + public Collection> getBundleParameters() { + Collection> results = new LinkedHashSet<>(); + results.addAll(getAppBundleParameters()); + results.addAll(getMacAppStoreBundleParameters()); + return results; + } + + public Collection> getMacAppStoreBundleParameters() { + Collection> results = new LinkedHashSet<>(); + + results.addAll(getAppBundleParameters()); + results.remove(DEVELOPER_ID_APP_SIGNING_KEY); + results.addAll(Arrays.asList( + INSTALLER_SUFFIX, + MAC_APP_STORE_APP_SIGNING_KEY, + MAC_APP_STORE_ENTITLEMENTS, + MAC_APP_STORE_PKG_SIGNING_KEY, + SIGNING_KEYCHAIN + )); + + return results; + } + + @Override + public boolean validate(Map params) + throws UnsupportedPlatformException, ConfigException { + try { + if (Platform.getPlatform() != Platform.MAC) { + throw new UnsupportedPlatformException(); + } + + 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 + + // TODO Mac App Store apps cannot use the system runtime + + // 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 params, + File outputParentDir) { + return bundle(params, outputParentDir); + } + + @Override + public boolean supported() { + return !Arguments.isJreInstaller() && + Platform.getPlatform() == Platform.MAC; + } +} diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacBaseInstallerBundler.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacBaseInstallerBundler.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,222 @@ +/* + * Copyright (c) 2014, 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.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 APP_BUNDLER = + new StandardBundlerParam<>( + I18N.getString("param.app-bundler.name"), + I18N.getString("param.app-bundle.description"), + "mac.app.bundler", + MacAppBundler.class, + params -> new MacAppBundler(), + (s, p) -> null); + + public final BundlerParamInfo APP_IMAGE_BUILD_ROOT = + new StandardBundlerParam<>( + I18N.getString("param.app-image-build-root.name"), + I18N.getString("param.app-image-build-root.description"), + "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 SIGNING_KEY_USER = + new StandardBundlerParam<>( + I18N.getString("param.signing-key-name.name"), + I18N.getString("param.signing-key-name.description"), + Arguments.CLIOptions.MAC_SIGNING_KEY_NAME.getId(), + String.class, + params -> "", + null); + + public static final BundlerParamInfo SIGNING_KEYCHAIN = + new StandardBundlerParam<>( + I18N.getString("param.signing-keychain.name"), + I18N.getString("param.signing-keychain.description"), + Arguments.CLIOptions.MAC_SIGNING_KEYCHAIN.getId(), + String.class, + params -> "", + null); + + public static final BundlerParamInfo INSTALLER_NAME = + new StandardBundlerParam<> ( + I18N.getString("param.installer-name.name"), + I18N.getString("param.installer-name.description"), + "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 params) + throws ConfigException, UnsupportedPlatformException { + 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 p, boolean pkg) { + File predefinedImage = StandardBundlerParam.getPredefinedAppImage(p); + if (predefinedImage != null) { + return predefinedImage; + } + File appImageRoot = APP_IMAGE_BUILD_ROOT.fetchFrom(p); + if (pkg) { + // create pkg in dmg + return new MacPkgBundler().bundle(p, appImageRoot); + } else { + return APP_BUNDLER.fetchFrom(p).doBundle(p, appImageRoot, true); + } + } + + @Override + public Collection> getBundleParameters() { + Collection> results = new LinkedHashSet<>(); + + results.addAll(MacAppBundler.getAppBundleParameters()); + results.addAll(Arrays.asList( + APP_BUNDLER, + CONFIG_ROOT, + APP_IMAGE_BUILD_ROOT, + PREDEFINED_APP_IMAGE + )); + + return results; + } + + @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 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, verbose, false, ps); + Pattern p = Pattern.compile("\"alis\"=\"([^\"]+)\""); + 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; + } + } +} diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacCertificate.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacCertificate.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2016, 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.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.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 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, verbose, false, ps); + + File output = File.createTempFile("tempfile", ".tmp"); + PrintStream p = new PrintStream( + new BufferedOutputStream( + new FileOutputStream(output, true))); + BufferedReader bfReader = new BufferedReader( + new InputStreamReader( + new ByteArrayInputStream(baos.toByteArray()))); + String line = null; + + while((line = bfReader.readLine()) != null){ + p.println(line); + } + + p.close(); + result = output; + } + catch (IOException ignored) {} + + return result; + } + + private static Date findCertificateDate(String filename, boolean verbose) { + Date result = null; + + List 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, verbose, 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; + } +} diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacDmgBundler.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacDmgBundler.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,484 @@ +/* + * Copyright (c) 2012, 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.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 INSTALLER_SUFFIX = + new StandardBundlerParam<> ( + I18N.getString("param.installer-suffix.name"), + I18N.getString("param.installer-suffix.description"), + "mac.dmg.installerName.suffix", + String.class, + params -> "", + (s, p) -> s); + + public File bundle(Map params, File outdir) { + Log.verbose(MessageFormat.format(I18N.getString("message.building-dmg"), + APP_NAME.fetchFrom(params))); + if (!outdir.isDirectory() && !outdir.mkdirs()) { + throw new RuntimeException(MessageFormat.format( + I18N.getString("error.cannot-create-output-dir"), + outdir.getAbsolutePath())); + } + if (!outdir.canWrite()) { + throw new RuntimeException(MessageFormat.format( + I18N.getString("error.cannot-write-to-output-dir"), + outdir.getAbsolutePath())); + } + + File appImageDir = APP_IMAGE_BUILD_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, false); + } + + return buildDMG(params, outdir); + } + return null; + } catch (IOException ex) { + Log.verbose(ex); + return null; + } + } + + private static final String hdiutil = "/usr/bin/hdiutil"; + + private void prepareDMGSetupScript(String volumeName, + Map p) throws IOException { + File dmgSetup = getConfig_VolumeScript(p); + Log.verbose(MessageFormat.format( + I18N.getString("message.preparing-dmg-setup"), + dmgSetup.getAbsolutePath())); + + //prepare config for exe + Map data = new HashMap<>(); + data.put("DEPLOY_ACTUAL_VOLUME_NAME", volumeName); + data.put("DEPLOY_APPLICATION_NAME", APP_NAME.fetchFrom(p)); + + data.put("DEPLOY_INSTALL_LOCATION", "(path to desktop folder)"); + data.put("DEPLOY_INSTALL_NAME", "Desktop"); + + Writer w = new BufferedWriter(new FileWriter(dmgSetup)); + w.write(preprocessTextResource(dmgSetup.getName(), + I18N.getString("resource.dmg-setup-script"), + DEFAULT_DMG_SETUP_SCRIPT, data, VERBOSE.fetchFrom(p), + RESOURCE_DIR.fetchFrom(p))); + w.close(); + } + + private File getConfig_VolumeScript(Map params) { + return new File(CONFIG_ROOT.fetchFrom(params), + APP_NAME.fetchFrom(params) + "-dmg-setup.scpt"); + } + + private File getConfig_VolumeBackground( + Map params) { + return new File(CONFIG_ROOT.fetchFrom(params), + APP_NAME.fetchFrom(params) + "-background.png"); + } + + private File getConfig_VolumeIcon(Map params) { + return new File(CONFIG_ROOT.fetchFrom(params), + APP_NAME.fetchFrom(params) + "-volume.icns"); + } + + private File getConfig_LicenseFile(Map params) { + return new File(CONFIG_ROOT.fetchFrom(params), + APP_NAME.fetchFrom(params) + "-license.plist"); + } + + private void prepareLicense(Map 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 data = new HashMap<>(); + data.put("APPLICATION_LICENSE_TEXT", licenseInBase64); + + Writer w = new BufferedWriter( + new FileWriter(getConfig_LicenseFile(params))); + w.write(preprocessTextResource( + getConfig_LicenseFile(params).getName(), + I18N.getString("resource.license-setup"), + DEFAULT_LICENSE_PLIST, data, VERBOSE.fetchFrom(params), + RESOURCE_DIR.fetchFrom(params))); + w.close(); + + } catch (IOException ex) { + Log.verbose(ex); + } + } + + private boolean prepareConfigFiles(Map 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 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 p, File outdir) + throws IOException { + File imagesRoot = IMAGES_ROOT.fetchFrom(p); + if (!imagesRoot.exists()) imagesRoot.mkdirs(); + + File protoDMG = new File(imagesRoot, APP_NAME.fetchFrom(p) +"-tmp.dmg"); + File finalDMG = new File(outdir, INSTALLER_NAME.fetchFrom(p) + + INSTALLER_SUFFIX.fetchFrom(p) + + ".dmg"); + + File srcFolder = APP_IMAGE_BUILD_ROOT.fetchFrom(p); + File predefinedImage = StandardBundlerParam.getPredefinedAppImage(p); + 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(p), + "-ov", protoDMG.getAbsolutePath(), + "-fs", "HFS+", + "-format", "UDRW"); + IOUtils.exec(pb, false); + + // mount temp image + pb = new ProcessBuilder( + hdiutil, + "attach", + protoDMG.getAbsolutePath(), + hdiUtilVerbosityFlag, + "-mountroot", imagesRoot.getAbsolutePath()); + IOUtils.exec(pb, false); + + File mountedRoot = + new File(imagesRoot.getAbsolutePath(), APP_NAME.fetchFrom(p)); + + // volume icon + File volumeIconFile = new File(mountedRoot, ".VolumeIcon.icns"); + IOUtils.copyFile(getConfig_VolumeIcon(p), + volumeIconFile); + + pb = new ProcessBuilder("osascript", + getConfig_VolumeScript(p).getAbsolutePath()); + IOUtils.exec(pb, false); + + // 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, false); + volumeIconFile.setReadOnly(); + + pb = new ProcessBuilder( + setFileUtility, + "-a", "C", + mountedRoot.getAbsolutePath()); + IOUtils.exec(pb, false); + } 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", + hdiUtilVerbosityFlag, + mountedRoot.getAbsolutePath()); + IOUtils.exec(pb, false); + + // Compress it to a new image + pb = new ProcessBuilder( + hdiutil, + "convert", + protoDMG.getAbsolutePath(), + hdiUtilVerbosityFlag, + "-format", "UDZO", + "-o", finalDMG.getAbsolutePath()); + IOUtils.exec(pb, false); + + //add license if needed + if (getConfig_LicenseFile(p).exists()) { + //hdiutil unflatten your_image_file.dmg + pb = new ProcessBuilder( + hdiutil, + "unflatten", + finalDMG.getAbsolutePath() + ); + IOUtils.exec(pb, false); + + //add license + pb = new ProcessBuilder( + hdiutil, + "udifrez", + finalDMG.getAbsolutePath(), + "-xml", + getConfig_LicenseFile(p).getAbsolutePath() + ); + IOUtils.exec(pb, false); + + //hdiutil flatten your_image_file.dmg + pb = new ProcessBuilder( + hdiutil, + "flatten", + finalDMG.getAbsolutePath() + ); + IOUtils.exec(pb, false); + + } + + //Delete the temporary image + protoDMG.delete(); + + Log.verbose(MessageFormat.format(I18N.getString( + "message.output-to-location"), + APP_NAME.fetchFrom(p), finalDMG.getAbsolutePath())); + + return finalDMG; + } + + + ////////////////////////////////////////////////////////////////////////// + // Implement Bundler + ////////////////////////////////////////////////////////////////////////// + + @Override + public String getName() { + return I18N.getString("dmg.bundler.name"); + } + + @Override + public String getDescription() { + return I18N.getString("dmg.bundler.description"); + } + + @Override + public String getID() { + return "dmg"; + } + + @Override + public Collection> getBundleParameters() { + Collection> results = new LinkedHashSet<>(); + results.addAll(MacAppBundler.getAppBundleParameters()); + results.addAll(getDMGBundleParameters()); + return results; + } + + public Collection> getDMGBundleParameters() { + Collection> results = new LinkedHashSet<>(); + + results.addAll(MacAppBundler.getAppBundleParameters()); + results.addAll(Arrays.asList( + INSTALLER_SUFFIX, + LICENSE_FILE + )); + + return results; + } + + + @Override + public boolean validate(Map params) + throws UnsupportedPlatformException, 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 params, File outputParentDir) { + return bundle(params, outputParentDir); + } + + @Override + public boolean supported() { + return Platform.getPlatform() == Platform.MAC; + } +} diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacPkgBundler.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacPkgBundler.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,501 @@ +/* + * Copyright (c) 2014, 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.internal; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.PrintStream; +import java.io.Writer; +import java.net.URLEncoder; +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 PACKAGES_ROOT = + new StandardBundlerParam<>( + I18N.getString("param.packages-root.name"), + I18N.getString("param.packages-root.description"), + "mac.pkg.packagesRoot", + File.class, + params -> { + File packagesRoot = + new File(BUILD_ROOT.fetchFrom(params), "packages"); + packagesRoot.mkdirs(); + return packagesRoot; + }, + (s, p) -> new File(s)); + + + protected final BundlerParamInfo SCRIPTS_DIR = + new StandardBundlerParam<>( + I18N.getString("param.scripts-dir.name"), + I18N.getString("param.scripts-dir.description"), + "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 DEVELOPER_ID_INSTALLER_SIGNING_KEY = + new StandardBundlerParam<>( + I18N.getString("param.signing-key-developer-id-installer.name"), + I18N.getString( + "param.signing-key-developer-id-installer.description"), + "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 MAC_INSTALL_DIR = + new StandardBundlerParam<>( + I18N.getString("param.mac-install-dir.name"), + I18N.getString("param.mac-install-dir.description"), + "mac-install-dir", + String.class, + params -> { + String dir = INSTALL_DIR.fetchFrom(params); + return (dir != null) ? dir : "/Applications"; + }, + (s, p) -> s + ); + + public static final BundlerParamInfo INSTALLER_SUFFIX = + new StandardBundlerParam<> ( + I18N.getString("param.installer-suffix.name"), + I18N.getString("param.installer-suffix.description"), + "mac.pkg.installerName.suffix", + String.class, + params -> "", + (s, p) -> s); + + public File bundle(Map params, File outdir) { + Log.verbose(MessageFormat.format(I18N.getString("message.building-pkg"), + APP_NAME.fetchFrom(params))); + if (!outdir.isDirectory() && !outdir.mkdirs()) { + throw new RuntimeException(MessageFormat.format( + I18N.getString("error.cannot-create-output-dir"), + outdir.getAbsolutePath())); + } + if (!outdir.canWrite()) { + throw new RuntimeException(MessageFormat.format( + I18N.getString("error.cannot-write-to-output-dir"), + outdir.getAbsolutePath())); + } + + 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, false); + } + + return createPKG(params, outdir, appImageDir); + } + return null; + } catch (IOException ex) { + Log.verbose(ex); + return null; + } + } + + private File getPackages_AppPackage(Map params) { + return new File(PACKAGES_ROOT.fetchFrom(params), + APP_FS_NAME.fetchFrom(params) + "-app.pkg"); + } + + private File getPackages_DaemonPackage(Map params) { + return new File(PACKAGES_ROOT.fetchFrom(params), + APP_FS_NAME.fetchFrom(params) + "-daemon.pkg"); + } + + private File getConfig_DistributionXMLFile( + Map params) { + return new File(CONFIG_ROOT.fetchFrom(params), "distribution.dist"); + } + + private File getConfig_BackgroundImage(Map params) { + return new File(CONFIG_ROOT.fetchFrom(params), + APP_NAME.fetchFrom(params) + "-background.png"); + } + + private File getScripts_PreinstallFile(Map params) { + return new File(SCRIPTS_DIR.fetchFrom(params), "preinstall"); + } + + private File getScripts_PostinstallFile( + Map params) { + return new File(SCRIPTS_DIR.fetchFrom(params), "postinstall"); + } + + private String getAppIdentifier(Map params) { + return IDENTIFIER.fetchFrom(params); + } + + private String getDaemonIdentifier(Map params) { + return IDENTIFIER.fetchFrom(params) + ".daemon"; + } + + private void preparePackageScripts(Map params) + throws IOException { + Log.verbose(I18N.getString("message.preparing-scripts")); + + Map data = new HashMap<>(); + + data.put("DEPLOY_DAEMON_IDENTIFIER", getDaemonIdentifier(params)); + data.put("DEPLOY_LAUNCHD_PLIST_FILE", + IDENTIFIER.fetchFrom(params).toLowerCase() + ".launchd.plist"); + + Writer w = new BufferedWriter( + new FileWriter(getScripts_PreinstallFile(params))); + 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); + w.close(); + getScripts_PreinstallFile(params).setExecutable(true, false); + + w = new BufferedWriter( + new FileWriter(getScripts_PostinstallFile(params))); + 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); + w.close(); + getScripts_PostinstallFile(params).setExecutable(true, false); + } + + private void prepareDistributionXMLFile(Map params) + throws IOException { + File f = getConfig_DistributionXMLFile(params); + + Log.verbose(MessageFormat.format(I18N.getString( + "message.preparing-distribution-dist"), f.getAbsolutePath())); + + PrintStream out = new PrintStream(f); + + out.println( + ""); + out.println(""); + + out.println("" + APP_NAME.fetchFrom(params) + ""); + out.println(""); + + String licFileStr = LICENSE_FILE.fetchFrom(params); + if (licFileStr != null) { + File licFile = new File(licFileStr); + out.println(""); + } + + /* + * Note that the content of the distribution file + * below is generated by productbuild --synthesize + */ + + String appId = getAppIdentifier(params); + + out.println(""); + + out.println(""); + out.println(""); + out.println(" "); + out.println(" "); + out.println(" "); + out.println(""); + out.println(""); + out.println(""); + out.println(" "); + out.println(""); + out.println("" + + URLEncoder.encode(getPackages_AppPackage(params).getName(), + "UTF-8") + ""); + + out.println(""); + + out.close(); + } + + private boolean prepareConfigFiles(Map 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 params) { + return new File(CONFIG_ROOT.fetchFrom(params), + APP_NAME.fetchFrom(params) + "-post-image.sh"); + } + + private File createPKG(Map params, + File outdir, File appLocation) { + // generic find attempt + try { + File appPKG = getPackages_AppPackage(params); + + // build application package + ProcessBuilder pb = new ProcessBuilder("pkgbuild", + "--component", + appLocation.toString(), + "--install-location", + MAC_INSTALL_DIR.fetchFrom(params), + appPKG.getAbsolutePath()); + IOUtils.exec(pb, false); + + // build final package + File finalPKG = new File(outdir, INSTALLER_NAME.fetchFrom(params) + + INSTALLER_SUFFIX.fetchFrom(params) + + ".pkg"); + outdir.mkdirs(); + + List 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, false); + + 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 getDescription() { + return I18N.getString("pkg.bundler.description"); + } + + @Override + public String getID() { + return "pkg"; + } + + @Override + public Collection> getBundleParameters() { + Collection> results = new LinkedHashSet<>(); + results.addAll(MacAppBundler.getAppBundleParameters()); + results.addAll(getPKGBundleParameters()); + return results; + } + + public Collection> getPKGBundleParameters() { + Collection> results = new LinkedHashSet<>(); + + results.addAll(MacAppBundler.getAppBundleParameters()); + results.addAll(Arrays.asList( + DEVELOPER_ID_INSTALLER_SIGNING_KEY, + // IDENTIFIER, + INSTALLER_SUFFIX, + LICENSE_FILE, + // SERVICE_HINT, + SIGNING_KEYCHAIN)); + + return results; + } + + @Override + public boolean validate(Map params) + throws UnsupportedPlatformException, 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 params, File outputParentDir) { + return bundle(params, outputParentDir); + } + + @Override + public boolean supported() { + return Platform.getPlatform() == Platform.MAC; + } + +} diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/DMGsetup.scpt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/DMGsetup.scpt Thu Jan 10 13:13:56 2019 -0500 @@ -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 + diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/GenericApp.icns Binary file src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/GenericApp.icns has changed diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/GenericAppHiDPI.icns Binary file src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/GenericAppHiDPI.icns has changed diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/Info-lite.plist.template --- /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 Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,38 @@ + + + + + LSMinimumSystemVersion + 10.9 + CFBundleDevelopmentRegion + English + CFBundleAllowMixedLocalizations + + CFBundleExecutable + DEPLOY_LAUNCHER_NAME + CFBundleIconFile + DEPLOY_ICON_FILE + CFBundleIdentifier + DEPLOY_BUNDLE_IDENTIFIER + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + DEPLOY_BUNDLE_NAME + CFBundlePackageType + APPL + CFBundleShortVersionString + DEPLOY_BUNDLE_SHORT_VERSION + CFBundleSignature + ???? + + LSApplicationCategoryType + DEPLOY_BUNDLE_CATEGORY + CFBundleVersion + DEPLOY_BUNDLE_CFBUNDLE_VERSION + NSHumanReadableCopyright + DEPLOY_BUNDLE_COPYRIGHTDEPLOY_FILE_ASSOCIATIONS + NSHighResolutionCapable + true + + diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/Info.plist.template --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/Info.plist.template Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,56 @@ + + + + + LSMinimumSystemVersion + 10.7.4 + CFBundleDevelopmentRegion + English + CFBundleAllowMixedLocalizations + + CFBundleExecutable + DEPLOY_LAUNCHER_NAME + CFBundleIconFile + DEPLOY_ICON_FILE + CFBundleIdentifier + DEPLOY_BUNDLE_IDENTIFIER + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + DEPLOY_BUNDLE_NAME + CFBundlePackageType + APPL + CFBundleShortVersionString + DEPLOY_BUNDLE_SHORT_VERSION + CFBundleSignature + ???? + + LSApplicationCategoryType + DEPLOY_BUNDLE_CATEGORY + CFBundleVersion + DEPLOY_BUNDLE_CFBUNDLE_VERSION + NSHumanReadableCopyright + DEPLOY_BUNDLE_COPYRIGHT + JVMRuntime + DEPLOY_JAVA_RUNTIME_NAME + JVMMainClassName + DEPLOY_LAUNCHER_CLASS + JVMAppClasspath + DEPLOY_APP_CLASSPATH + JVMMainJarName + DEPLOY_MAIN_JAR_NAME + JVMPreferencesID + DEPLOY_PREFERENCES_ID + JVMOptions + +DEPLOY_JVM_OPTIONS + + ArgOptions + +DEPLOY_ARGUMENTS + DEPLOY_FILE_ASSOCIATIONS + NSHighResolutionCapable + true + + diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacAppStore.entitlements --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacAppStore.entitlements Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,8 @@ + + + + + com.apple.security.app-sandbox + + + diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacAppStore_Inherit.entitlements --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacAppStore_Inherit.entitlements Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,10 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.inherit + + + diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacResources.properties --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacResources.properties Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,160 @@ +# +# Copyright (c) 2017, 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. +# +# + +app.bundler.name=Mac Application Image +app.bundler.description=A Directory based image of a mac Application with an optionally co-bundled JRE. Used as a base for the Installer bundlers +store.bundler.name=Mac App Store Ready Bundler +store.bundler.description=Creates a binary bundle ready for deployment into the Mac App Store." +dmg.bundler.name=DMG Installer +dmg.bundler.description=Mac DMG Installer Bundle +pkg.bundler.name=PKG Installer +pkg.bundler.description=Mac PKG Installer Bundle. + +param.signing-key-developer-id-app.name=Apple Developer ID Application Signing Key +param.signing-key-developer-id-app.description=The full name of the Apple Developer ID Application signing key. +param.icon-icns.name=.icns Icon +param.icon-icns.description=Icon for the application, in ICNS format. +param.configure-launcher-in-plist=Configure Launcher in Info.plist +param.configure-launcher-in-plist.description=Should the legacy method of configuring hte launcher in the Info.plist be used. +param.category-name=Category +param.category-name.description=Mac App Store Categories. Note that the key is the string to display to the user and the value is the id of the category. +param.cfbundle-name.name=CFBundleName +param.cfbundle-name.description=The name of the app as it appears in the Menu Bar. This can be different from the application name. This name should be less than 16 characters long and be suitable for displaying in the menu bar and the app's Info window. +param.cfbundle-identifier.name=CFBundleIdentifier +param.cfbundle-identifier.description=An identifier that uniquely identifies the application for MacOSX (and on the Mac App Store). May only use alphanumeric (A-Z,a-z,0-9), hyphen (-), and period (.) characters. +param.cfbundle-version.name=CFBundleVersion +param.cfbundle-version.description=An computer readable version for the CFBundle. May contain only digits and from zero to two dots, such as "1.8.1" or "100". +param.bundle-id-signing-prefix.name=Bundle Signing Prefix +param.bundle-id-signing-prefix.description=When signing the application bundle this value is prefixed to all components that need to be signed that don't have an existing CFBundleIdentifier. +param.raw-executable-url.name=Launcher URL +param.raw-executable-url.description=Override the jpackage default launcher with a custom launcher. +param.default-icon-icns=Default Icon +param.default-icon-icns.description=The Default Icon for when a user does not specify an icns file. +param.icon-icns.name=.icns Icon +param.icon-icns.description= Icon for the application, in ICNS format. +param.sign-bundle.name=Sign Bundle +param.sign-bundle.description=Request that the bundle be signed. +param.signing-key-app.name=Application Signing Key +param.signing-key-app.description=The full name of the signing key to sign the application with. +param.signing-key-pkg.name=Installer Signing Key +param.signing-key-pkg.description=The full name of the signing key to sign the PKG Installer with. +param.mac-app-store-entitlements.name=Entitlements File +param.mac-app-store-entitlements.description=File location of a custom mac app store entitlements file +param.installer-suffix.name=Installer Suffix +param.installer-suffix.description=The suffix for the installer this package. +param.app-bundler.name=Mac App Bundler +param.app-bundle.description=Creates a .app bundle for the Mac +param.app-image-build-root.name=Build Root Dir +param.app-image-build-root.description=This is temporary location built by jpackage that is the root of the image application +param.signing-keychain.name=Signing Keychain +param.signing-keychain.description=The location of the keychain to use. If not specified the standard keychains will be used. +param.signing-key-name.name=Signing Key User Name +param.signing-key-name.description=The user name portion of the typical "Mac Developer ID Application: " signing key. +param.installer-name.name=Installer Name +param.installer-name.description=The filename of the generated installer without the file type extension. Default is -. +param.simple-dmg.name=Simple DMG Generation +param.simple-dmg.description=Generate a DMG without AppleScript customizations. Recommended for continuous automated builds. +param.signing-key-developer-id-installer.name=Apple Developer ID Installer Signing Key +param.signing-key-developer-id-installer.description=The full name of the Apple Developer ID Installer signing key. +param.packages-root.name=PKG Root Dir +param.packages-root.description=This is temporary location for component packages (application and daemon). The packages are incorporated into final product package. +param.scripts-dir.name=Scripts Dir +param.scripts-dir.description=This is temporary location for package scripts +param.mac-install-dir.name=Mac Installation Directory +param.mac-install-dir.description=Installation directory of the application on Mac. + +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.non-existent-runtime=The file for the Runtime/JRE directory does not exist. +error.non-existent-runtime.advice=Point the runtime parameter to a directory that containes the JRE. +error.cannot-detect-runtime-in-directory=Cannot determine which JRE/JDK exists in the specified runtime directory. +error.cannot-detect-runtime-in-directory.advice=Point the runtime directory to one of the JDK/JRE root, the Contents/Home directory of that root, or the Contents/Home/jre directory of the JDK. +error.parameters-null=Parameters map is null. +error.parameters-null.advice=Pass in a non-null parameters map. +error.cannot-create-output-dir=Output directory {0} cannot be created. +error.cannot-write-to-output-dir=Output directory {0} is not writable. +error.no-system-runtime=Bundle Configured to use the System JRE +error.no-system-runtime.advice=Do not set 'runtime' to null, either don't set it or set it to a valid value. +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}. +error.dmg-does-not-do-daemons=DMG bundler doesn't support services. +error.dmg-does-not-do-daemons.advice=Make sure that the service hint is set to false. + +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.no-mac-jre-support=Currently Macs require a JDK to package +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} +mesasge.intermediate-bundle-location=Intermediate application bundle image\: {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.could-not-retrieve-name=Could not retrieve gecos name +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.intermediate-image-location=[DEBUG] Intermediate application bundle 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. + diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacResources_ja.properties --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacResources_ja.properties Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,160 @@ +# +# Copyright (c) 2017, 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. +# +# + +app.bundler.name=Mac Application Image +app.bundler.description=A Directory based image of a mac Application with an optionally co-bundled JRE. Used as a base for the Installer bundlers +store.bundler.name=Mac App Store Ready Bundler +store.bundler.description=Creates a binary bundle ready for deployment into the Mac App Store." +dmg.bundler.name=DMG Installer +dmg.bundler.description=Mac DMG Installer Bundle +pkg.bundler.name=PKG Installer +pkg.bundler.description=Mac PKG Installer Bundle. + +param.signing-key-developer-id-app.name=Apple Developer ID Application Signing Key +param.signing-key-developer-id-app.description=The full name of the Apple Developer ID Application signing key. +param.icon-icns.name=.icns Icon +param.icon-icns.description=Icon for the application, in ICNS format. +param.configure-launcher-in-plist=Configure Launcher in Info.plist +param.configure-launcher-in-plist.description=Should the legacy method of configuring hte launcher in the Info.plist be used. +param.category-name=Category +param.category-name.description=Mac App Store Categories. Note that the key is the string to display to the user and the value is the id of the category. +param.cfbundle-name.name=CFBundleName +param.cfbundle-name.description=The name of the app as it appears in the Menu Bar. This can be different from the application name. This name should be less than 16 characters long and be suitable for displaying in the menu bar and the app's Info window. +param.cfbundle-identifier.name=CFBundleIdentifier +param.cfbundle-identifier.description=An identifier that uniquely identifies the application for MacOSX (and on the Mac App Store). May only use alphanumeric (A-Z,a-z,0-9), hyphen (-), and period (.) characters. +param.cfbundle-version.name=CFBundleVersion +param.cfbundle-version.description=An computer readable version for the CFBundle. May contain only digits and from zero to two dots, such as "1.8.1" or "100". +param.bundle-id-signing-prefix.name=Bundle Signing Prefix +param.bundle-id-signing-prefix.description=When signing the application bundle this value is prefixed to all components that need to be signed that don't have an existing CFBundleIdentifier. +param.raw-executable-url.name=Launcher URL +param.raw-executable-url.description=Override the jpackage default launcher with a custom launcher. +param.default-icon-icns=Default Icon +param.default-icon-icns.description=The Default Icon for when a user does not specify an icns file. +param.icon-icns.name=.icns Icon +param.icon-icns.description= Icon for the application, in ICNS format. +param.sign-bundle.name=Sign Bundle +param.sign-bundle.description=Request that the bundle be signed. +param.signing-key-app.name=Application Signing Key +param.signing-key-app.description=The full name of the signing key to sign the application with. +param.signing-key-pkg.name=Installer Signing Key +param.signing-key-pkg.description=The full name of the signing key to sign the PKG Installer with. +param.mac-app-store-entitlements.name=Entitlements File +param.mac-app-store-entitlements.description=File location of a custom mac app store entitlements file +param.installer-suffix.name=Installer Suffix +param.installer-suffix.description=The suffix for the installer this package. +param.app-bundler.name=Mac App Bundler +param.app-bundle.description=Creates a .app bundle for the Mac +param.app-image-build-root.name=Build Root Dir +param.app-image-build-root.description=This is temporary location built by jpackage that is the root of the image application +param.signing-keychain.name=Signing Keychain +param.signing-keychain.description=The location of the keychain to use. If not specified the standard keychains will be used. +param.signing-key-name.name=Signing Key User Name +param.signing-key-name.description=The user name portion of the typical "Mac Developer ID Application: " signing key. +param.installer-name.name=Installer Name +param.installer-name.description=The filename of the generated installer without the file type extension. Default is -. +param.simple-dmg.name=Simple DMG Generation +param.simple-dmg.description=Generate a DMG without AppleScript customizations. Recommended for continuous automated builds. +param.signing-key-developer-id-installer.name=Apple Developer ID Installer Signing Key +param.signing-key-developer-id-installer.description=The full name of the Apple Developer ID Installer signing key. +param.packages-root.name=PKG Root Dir +param.packages-root.description=This is temporary location for component packages (application and daemon). The packages are incorporated into final product package. +param.scripts-dir.name=Scripts Dir +param.scripts-dir.description=This is temporary location for package scripts +param.mac-install-dir.name=Mac Installation Directory +param.mac-install-dir.description=Installation directory of the application on Mac. + +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.non-existent-runtime=The file for the Runtime/JRE directory does not exist. +error.non-existent-runtime.advice=Point the runtime parameter to a directory that containes the JRE. +error.cannot-detect-runtime-in-directory=Cannot determine which JRE/JDK exists in the specified runtime directory. +error.cannot-detect-runtime-in-directory.advice=Point the runtime directory to one of the JDK/JRE root, the Contents/Home directory of that root, or the Contents/Home/jre directory of the JDK. +error.parameters-null=Parameters map is null. +error.parameters-null.advice=Pass in a non-null parameters map. +error.cannot-create-output-dir=Output directory {0} cannot be created. +error.cannot-write-to-output-dir=Output directory {0} is not writable. +error.no-system-runtime=Bundle Configured to use the System JRE +error.no-system-runtime.advice=Do not set 'runtime' to null, either don't set it or set it to a valid value. +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}. +error.dmg-does-not-do-daemons=DMG bundler doesn't support services. +error.dmg-does-not-do-daemons.advice=Make sure that the service hint is set to false. + +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.no-mac-jre-support=Currently Macs require a JDK to package +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} +mesasge.intermediate-bundle-location=Intermediate application bundle image\: {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.could-not-retrieve-name=Could not retrieve gecos name +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.intermediate-image-location=[DEBUG] Intermediate application bundle 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. + diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacResources_zh_CN.properties --- /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 Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,160 @@ +# +# Copyright (c) 2017, 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. +# +# + +app.bundler.name=Mac Application Image +app.bundler.description=A Directory based image of a mac Application with an optionally co-bundled JRE. Used as a base for the Installer bundlers +store.bundler.name=Mac App Store Ready Bundler +store.bundler.description=Creates a binary bundle ready for deployment into the Mac App Store." +dmg.bundler.name=DMG Installer +dmg.bundler.description=Mac DMG Installer Bundle +pkg.bundler.name=PKG Installer +pkg.bundler.description=Mac PKG Installer Bundle. + +param.signing-key-developer-id-app.name=Apple Developer ID Application Signing Key +param.signing-key-developer-id-app.description=The full name of the Apple Developer ID Application signing key. +param.icon-icns.name=.icns Icon +param.icon-icns.description=Icon for the application, in ICNS format. +param.configure-launcher-in-plist=Configure Launcher in Info.plist +param.configure-launcher-in-plist.description=Should the legacy method of configuring hte launcher in the Info.plist be used. +param.category-name=Category +param.category-name.description=Mac App Store Categories. Note that the key is the string to display to the user and the value is the id of the category. +param.cfbundle-name.name=CFBundleName +param.cfbundle-name.description=The name of the app as it appears in the Menu Bar. This can be different from the application name. This name should be less than 16 characters long and be suitable for displaying in the menu bar and the app's Info window. +param.cfbundle-identifier.name=CFBundleIdentifier +param.cfbundle-identifier.description=An identifier that uniquely identifies the application for MacOSX (and on the Mac App Store). May only use alphanumeric (A-Z,a-z,0-9), hyphen (-), and period (.) characters. +param.cfbundle-version.name=CFBundleVersion +param.cfbundle-version.description=An computer readable version for the CFBundle. May contain only digits and from zero to two dots, such as "1.8.1" or "100". +param.bundle-id-signing-prefix.name=Bundle Signing Prefix +param.bundle-id-signing-prefix.description=When signing the application bundle this value is prefixed to all components that need to be signed that don't have an existing CFBundleIdentifier. +param.raw-executable-url.name=Launcher URL +param.raw-executable-url.description=Override the jpackage default launcher with a custom launcher. +param.default-icon-icns=Default Icon +param.default-icon-icns.description=The Default Icon for when a user does not specify an icns file. +param.icon-icns.name=.icns Icon +param.icon-icns.description= Icon for the application, in ICNS format. +param.sign-bundle.name=Sign Bundle +param.sign-bundle.description=Request that the bundle be signed. +param.signing-key-app.name=Application Signing Key +param.signing-key-app.description=The full name of the signing key to sign the application with. +param.signing-key-pkg.name=Installer Signing Key +param.signing-key-pkg.description=The full name of the signing key to sign the PKG Installer with. +param.mac-app-store-entitlements.name=Entitlements File +param.mac-app-store-entitlements.description=File location of a custom mac app store entitlements file +param.installer-suffix.name=Installer Suffix +param.installer-suffix.description=The suffix for the installer this package. +param.app-bundler.name=Mac App Bundler +param.app-bundle.description=Creates a .app bundle for the Mac +param.app-image-build-root.name=Build Root Dir +param.app-image-build-root.description=This is temporary location built by jpackage that is the root of the image application +param.signing-keychain.name=Signing Keychain +param.signing-keychain.description=The location of the keychain to use. If not specified the standard keychains will be used. +param.signing-key-name.name=Signing Key User Name +param.signing-key-name.description=The user name portion of the typical "Mac Developer ID Application: " signing key. +param.installer-name.name=Installer Name +param.installer-name.description=The filename of the generated installer without the file type extension. Default is -. +param.simple-dmg.name=Simple DMG Generation +param.simple-dmg.description=Generate a DMG without AppleScript customizations. Recommended for continuous automated builds. +param.signing-key-developer-id-installer.name=Apple Developer ID Installer Signing Key +param.signing-key-developer-id-installer.description=The full name of the Apple Developer ID Installer signing key. +param.packages-root.name=PKG Root Dir +param.packages-root.description=This is temporary location for component packages (application and daemon). The packages are incorporated into final product package. +param.scripts-dir.name=Scripts Dir +param.scripts-dir.description=This is temporary location for package scripts +param.mac-install-dir.name=Mac Installation Directory +param.mac-install-dir.description=Installation directory of the application on Mac. + +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.non-existent-runtime=The file for the Runtime/JRE directory does not exist. +error.non-existent-runtime.advice=Point the runtime parameter to a directory that containes the JRE. +error.cannot-detect-runtime-in-directory=Cannot determine which JRE/JDK exists in the specified runtime directory. +error.cannot-detect-runtime-in-directory.advice=Point the runtime directory to one of the JDK/JRE root, the Contents/Home directory of that root, or the Contents/Home/jre directory of the JDK. +error.parameters-null=Parameters map is null. +error.parameters-null.advice=Pass in a non-null parameters map. +error.cannot-create-output-dir=Output directory {0} cannot be created. +error.cannot-write-to-output-dir=Output directory {0} is not writable. +error.no-system-runtime=Bundle Configured to use the System JRE +error.no-system-runtime.advice=Do not set 'runtime' to null, either don't set it or set it to a valid value. +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}. +error.dmg-does-not-do-daemons=DMG bundler doesn't support services. +error.dmg-does-not-do-daemons.advice=Make sure that the service hint is set to false. + +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.no-mac-jre-support=Currently Macs require a JDK to package +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} +mesasge.intermediate-bundle-location=Intermediate application bundle image\: {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.could-not-retrieve-name=Could not retrieve gecos name +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.intermediate-image-location=[DEBUG] Intermediate application bundle 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. + diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/Runtime-Info.plist.template --- /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 Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,24 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + libjli.dylib + CFBundleIdentifier + CF_BUNDLE_IDENTIFIER + CFBundleInfoDictionaryVersion + 7.0 + CFBundleName + CF_BUNDLE_NAME + CFBundlePackageType + BNDL + CFBundleShortVersionString + CF_BUNDLE_SHORT_VERSION_STRING + CFBundleSignature + ???? + CFBundleVersion + CF_BUNDLE_VERSION + + diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/background_dmg.png Binary file src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/background_dmg.png has changed diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/background_pkg.png Binary file src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/background_pkg.png has changed diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/launchd.plist.template --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/launchd.plist.template Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,14 @@ + + + + + Label + DEPLOY_DAEMON_IDENTIFIER + ProgramArguments + + DEPLOY_DAEMON_LAUNCHER_PATH + + RunAtLoad + KeepAlive + + diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/lic_template.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 Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,244 @@ + + + + + LPic + + + Attributes + 0x0000 + Data + AAAAAgAAAAAAAAAAAAQAAA== + ID + 5000 + Name + + + + STR# + + + Attributes + 0x0000 + Data + AAYPRW5nbGlzaCBkZWZhdWx0BUFncmVlCERpc2FncmVlBVByaW50B1NhdmUuLi56SWYgeW91IGFncmVlIHdpdGggdGhlIHRlcm1zIG9mIHRoaXMgbGljZW5zZSwgY2xpY2sgIkFncmVlIiB0byBhY2Nlc3MgdGhlIHNvZnR3YXJlLiAgSWYgeW91IGRvIG5vdCBhZ3JlZSwgcHJlc3MgIkRpc2FncmVlLiI= + ID + 5000 + Name + English buttons + + + Attributes + 0x0000 + Data + AAYHRGV1dHNjaAtBa3plcHRpZXJlbghBYmxlaG5lbgdEcnVja2VuClNpY2hlcm4uLi7nS2xpY2tlbiBTaWUgaW4g0kFremVwdGllcmVu0ywgd2VubiBTaWUgbWl0IGRlbiBCZXN0aW1tdW5nZW4gZGVzIFNvZnR3YXJlLUxpemVuenZlcnRyYWdzIGVpbnZlcnN0YW5kZW4gc2luZC4gRmFsbHMgbmljaHQsIGJpdHRlINJBYmxlaG5lbtMgYW5rbGlja2VuLiBTaWUga5pubmVuIGRpZSBTb2Z0d2FyZSBudXIgaW5zdGFsbGllcmVuLCB3ZW5uIFNpZSDSQWt6ZXB0aWVyZW7TIGFuZ2VrbGlja3QgaGFiZW4u + ID + 5001 + Name + German + + + Attributes + 0x0000 + Data + AAYHRW5nbGlzaAVBZ3JlZQhEaXNhZ3JlZQVQcmludAdTYXZlLi4ue0lmIHlvdSBhZ3JlZSB3aXRoIHRoZSB0ZXJtcyBvZiB0aGlzIGxpY2Vuc2UsIHByZXNzICJBZ3JlZSIgdG8gaW5zdGFsbCB0aGUgc29mdHdhcmUuICBJZiB5b3UgZG8gbm90IGFncmVlLCBwcmVzcyAiRGlzYWdyZWUiLg== + ID + 5002 + Name + English + + + Attributes + 0x0000 + Data + AAYHRXNwYZZvbAdBY2VwdGFyCk5vIGFjZXB0YXIISW1wcmltaXIKR3VhcmRhci4uLsBTaSBlc3SHIGRlIGFjdWVyZG8gY29uIGxvcyB0jnJtaW5vcyBkZSBlc3RhIGxpY2VuY2lhLCBwdWxzZSAiQWNlcHRhciIgcGFyYSBpbnN0YWxhciBlbCBzb2Z0d2FyZS4gRW4gZWwgc3VwdWVzdG8gZGUgcXVlIG5vIGVzdI4gZGUgYWN1ZXJkbyBjb24gbG9zIHSOcm1pbm9zIGRlIGVzdGEgbGljZW5jaWEsIHB1bHNlICJObyBhY2VwdGFyLiI= + ID + 5003 + Name + Spanish + + + Attributes + 0x0000 + Data + AAYIRnJhbo1haXMIQWNjZXB0ZXIHUmVmdXNlcghJbXByaW1lcg5FbnJlZ2lzdHJlci4uLrpTaSB2b3VzIGFjY2VwdGV6IGxlcyB0ZXJtZXMgZGUgbGEgcHKOc2VudGUgbGljZW5jZSwgY2xpcXVleiBzdXIgIkFjY2VwdGVyIiBhZmluIGQnaW5zdGFsbGVyIGxlIGxvZ2ljaWVsLiBTaSB2b3VzIG4nkHRlcyBwYXMgZCdhY2NvcmQgYXZlYyBsZXMgdGVybWVzIGRlIGxhIGxpY2VuY2UsIGNsaXF1ZXogc3VyICJSZWZ1c2VyIi4= + ID + 5004 + Name + French + + + Attributes + 0x0000 + Data + AAYISXRhbGlhbm8HQWNjZXR0bwdSaWZpdXRvBlN0YW1wYQtSZWdpc3RyYS4uLn9TZSBhY2NldHRpIGxlIGNvbmRpemlvbmkgZGkgcXVlc3RhIGxpY2VuemEsIGZhaSBjbGljIHN1ICJBY2NldHRvIiBwZXIgaW5zdGFsbGFyZSBpbCBzb2Z0d2FyZS4gQWx0cmltZW50aSBmYWkgY2xpYyBzdSAiUmlmaXV0byIu + ID + 5005 + Name + Italian + + + Attributes + 0x0000 + Data + AAYISmFwYW5lc2UKk6+I04K1gtyCtwyTr4jTgrWC3IK5gvEIiPON/IK3gukHlduRti4uLrSWe4Ncg3SDZ4NFg0eDQY5nl3CLlpH4jF+W8YLMj/CMj4LJk6+I04KzguqC6Y/qjYeCyYLNgUGDXIN0g2eDRYNHg0GC8INDg5ODWINngVuDi4K3gumCvYLfgsmBdZOviNOCtYLcgreBdoLwiZ+CtYLEgq2CvoKzgqKBQoFAk6+I04KzguqCyIKij+qNh4LJgs2BQYF1k6+I04K1gtyCuYLxgXaC8ImfgrWCxIKtgr6Cs4KigUI= + ID + 5006 + Name + Japanese + + + Attributes + 0x0000 + Data + AAYKTmVkZXJsYW5kcwJKYQNOZWUFUHJpbnQJQmV3YWFyLi4upEluZGllbiB1IGFra29vcmQgZ2FhdCBtZXQgZGUgdm9vcndhYXJkZW4gdmFuIGRlemUgbGljZW50aWUsIGt1bnQgdSBvcCAnSmEnIGtsaWtrZW4gb20gZGUgcHJvZ3JhbW1hdHV1ciB0ZSBpbnN0YWxsZXJlbi4gSW5kaWVuIHUgbmlldCBha2tvb3JkIGdhYXQsIGtsaWt0IHUgb3AgJ05lZScu + ID + 5007 + Name + Dutch + + + Attributes + 0x0000 + Data + AAYGU3ZlbnNrCEdvZGuKbm5zBkF2YppqcwhTa3JpdiB1dAhTcGFyYS4uLpNPbSBEdSBnb2Rrim5uZXIgbGljZW5zdmlsbGtvcmVuIGtsaWNrYSBwjCAiR29ka4pubnMiIGaaciBhdHQgaW5zdGFsbGVyYSBwcm9ncmFtcHJvZHVrdGVuLiBPbSBEdSBpbnRlIGdvZGuKbm5lciBsaWNlbnN2aWxsa29yZW4sIGtsaWNrYSBwjCAiQXZimmpzIi4= + ID + 5008 + Name + Swedish + + + Attributes + 0x0000 + Data + AAYRUG9ydHVndZBzLCBCcmFzaWwJQ29uY29yZGFyCURpc2NvcmRhcghJbXByaW1pcglTYWx2YXIuLi6MU2UgZXN0hyBkZSBhY29yZG8gY29tIG9zIHRlcm1vcyBkZXN0YSBsaWNlbo1hLCBwcmVzc2lvbmUgIkNvbmNvcmRhciIgcGFyYSBpbnN0YWxhciBvIHNvZnR3YXJlLiBTZSBui28gZXN0hyBkZSBhY29yZG8sIHByZXNzaW9uZSAiRGlzY29yZGFyIi4= + ID + 5009 + Name + Brazilian Portuguese + + + Attributes + 0x0000 + Data + AAYSU2ltcGxpZmllZCBDaGluZXNlBM2s0uIGsrvNrNLiBLTy06EGtOa0oqGtVMjnufvE+s2s0uKxvtDtv8nQrdLptcTM9b/uo6zH67C0obDNrNLiobHAtLCy17C0y8jtvP6ho8jnufvE+rK7zazS4qOsx+uwtKGwsrvNrNLiobGhow== + ID + 5010 + Name + Simplified Chinese + + + Attributes + 0x0000 + Data + AAYTVHJhZGl0aW9uYWwgQ2hpbmVzZQSmULdOBqSjplC3TgSmQ6ZMBsB4pnOhS1CmcKpHsXqmULdOpbuzXKVpw9K4zKq6sfi02qFBvdCr9qGnplC3TqGopUimd7jLs27F6aFDpnCqR6SjplC3TqFBvdCr9qGnpKOmULdOoaihQw== + ID + 5011 + Name + Traditional Chinese + + + Attributes + 0x0000 + Data + AAYFRGFuc2sERW5pZwVVZW5pZwdVZHNrcml2CkFya2l2ZXIuLi6YSHZpcyBkdSBhY2NlcHRlcmVyIGJldGluZ2Vsc2VybmUgaSBsaWNlbnNhZnRhbGVuLCBza2FsIGR1IGtsaWtrZSBwjCDSRW5pZ9MgZm9yIGF0IGluc3RhbGxlcmUgc29mdHdhcmVuLiBLbGlrIHCMINJVZW5pZ9MgZm9yIGF0IGFubnVsbGVyZSBpbnN0YWxsZXJpbmdlbi4= + ID + 5012 + Name + Danish + + + Attributes + 0x0000 + Data + AAYFU3VvbWkISHl2imtzeW4KRW4gaHl2imtzeQdUdWxvc3RhCVRhbGxlbm5hyW9IeXaKa3N5IGxpc2Vuc3Npc29waW11a3NlbiBlaGRvdCBvc29pdHRhbWFsbGEg1Uh5doprc3nVLiBKb3MgZXQgaHl2imtzeSBzb3BpbXVrc2VuIGVodG9qYSwgb3NvaXRhINVFbiBoeXaKa3N51S4= + ID + 5013 + Name + Finnish + + + Attributes + 0x0000 + Data + AAYRRnJhbo1haXMgY2FuYWRpZW4IQWNjZXB0ZXIHUmVmdXNlcghJbXByaW1lcg5FbnJlZ2lzdHJlci4uLrpTaSB2b3VzIGFjY2VwdGV6IGxlcyB0ZXJtZXMgZGUgbGEgcHKOc2VudGUgbGljZW5jZSwgY2xpcXVleiBzdXIgIkFjY2VwdGVyIiBhZmluIGQnaW5zdGFsbGVyIGxlIGxvZ2ljaWVsLiBTaSB2b3VzIG4nkHRlcyBwYXMgZCdhY2NvcmQgYXZlYyBsZXMgdGVybWVzIGRlIGxhIGxpY2VuY2UsIGNsaXF1ZXogc3VyICJSZWZ1c2VyIi4= + ID + 5014 + Name + French Canadian + + + Attributes + 0x0000 + Data + AAYGS29yZWFuBLW/wMcJtb/AxyC+yMfUBsfBuLDGrgfA+sDlLi4ufrvnv+sgsOi+4LytwMcgs7u/67+hILW/wMfHz7jpLCAitb/AxyIgtNzD37imILStt68gvNLHwcauv/6+7rimILyzxKHHz73KvcO/wC4gtb/Ax8fPwfYgvsq0wrTZuOksICK1v8DHIL7Ix9QiILTcw9+4piC0qbijvcq9w7/ALg== + ID + 5015 + Name + Korean + + + Attributes + 0x0000 + Data + AAYFTm9yc2sERW5pZwlJa2tlIGVuaWcIU2tyaXYgdXQKQXJraXZlci4uLqNIdmlzIERlIGVyIGVuaWcgaSBiZXN0ZW1tZWxzZW5lIGkgZGVubmUgbGlzZW5zYXZ0YWxlbiwga2xpa2tlciBEZSBwjCAiRW5pZyIta25hcHBlbiBmb3IgjCBpbnN0YWxsZXJlIHByb2dyYW12YXJlbi4gSHZpcyBEZSBpa2tlIGVyIGVuaWcsIGtsaWtrZXIgRGUgcIwgIklra2UgZW5pZyIu + ID + 5016 + Name + Norwegian + + + TEXT + + + Attributes + 0x0000 + Data + APPLICATION_LICENSE_TEXT + ID + 5000 + Name + English SLA + + + TMPL + + + Attributes + 0x0000 + Data + E0RlZmF1bHQgTGFuZ3VhZ2UgSUREV1JEBUNvdW50T0NOVAQqKioqTFNUQwtzeXMgbGFuZyBJRERXUkQebG9jYWwgcmVzIElEIChvZmZzZXQgZnJvbSA1MDAwRFdSRBAyLWJ5dGUgbGFuZ3VhZ2U/RFdSRAQqKioqTFNURQ== + ID + 128 + Name + LPic + + + plst + + + Attributes + 0x0050 + Data + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + ID + 0 + Name + + + + styl + + + Attributes + 0x0000 + Data + AAMAAAAAAAwACQAUAAAAAAAAAAAAAAAAACcADAAJABQBAAAAAAAAAAAAAAAAKgAMAAkAFAAAAAAAAAAAAAA= + ID + 5000 + Name + English SLA + + + + diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/postinstall.template --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/postinstall.template Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,6 @@ +#!/usr/bin/env sh + +set -e +launchctl load "/Library/LaunchDaemons/DEPLOY_LAUNCHD_PLIST_FILE" + +exit 0 diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/preinstall.template --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/preinstall.template Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,8 @@ +#!/usr/bin/env sh + +set -e +if launchctl list "DEPLOY_DAEMON_IDENTIFIER" &> /dev/null; then + launchctl unload "/Library/LaunchDaemons/DEPLOY_LAUNCHD_PLIST_FILE" +fi + +exit 0 diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/macosx/classes/module-info.java.extra --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/macosx/classes/module-info.java.extra Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,31 @@ +/* + * Copyright (c) 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. + */ + +provides jdk.jpackage.internal.Bundler with + jdk.jpackage.internal.MacAppBundler, + jdk.jpackage.internal.MacAppStoreBundler, + jdk.jpackage.internal.MacDmgBundler, + jdk.jpackage.internal.MacPkgBundler; + diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/macosx/native/jpackageapplauncher/main.m --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/macosx/native/jpackageapplauncher/main.m Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2012, 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. + */ + +#import +#include +#include + +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; +} diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/macosx/native/libapplauncher/MacPlatform.mm --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/macosx/native/libapplauncher/MacPlatform.mm Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,406 @@ +/* + * Copyright (c) 2014, 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. + */ + +#include "Platform.h" + +#ifdef MAC + +#include "MacPlatform.h" +#include "Helpers.h" +#include "Package.h" +#include "PropertyFile.h" +#include "IniFile.h" + +#include +#include +#include + +#import +#import + +#include +#include + +#ifdef __OBJC__ +#import +#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; +} + +MacPlatform::MacPlatform(void) : Platform(), GenericPlatform(), PosixPlatform() { +} + +MacPlatform::~MacPlatform(void) { +} + +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::GetBundledJVMLibraryFileName(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 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) { + if (result->LoadFromFile(FileName) == false) { + // New property file format was not found, + // attempt to load old property file format. + Helpers::LoadOldConfigFile(FileName, result); + } + } + else { + NSBundle *mainBundle = [NSBundle mainBundle]; + NSDictionary *infoDictionary = [mainBundle infoDictionary]; + std::map keys = GetKeys(); + + // JPackage options. + AppendPListDictionaryToIniFile(infoDictionary, result, + keys[CONFIG_SECTION_APPLICATION], false); + + // jvmargs + AppendPListArrayToIniFile(infoDictionary, result, + keys[CONFIG_SECTION_JVMOPTIONS]); + + // Generate AppCDS Cache + AppendPListDictionaryToIniFile(infoDictionary, result, + keys[CONFIG_SECTION_APPCDSJVMOPTIONS]); + AppendPListDictionaryToIniFile(infoDictionary, result, + keys[CONFIG_SECTION_APPCDSGENERATECACHEJVMOPTIONS]); + + // args + AppendPListArrayToIniFile(infoDictionary, result, + keys[CONFIG_SECTION_ARGOPTIONS]); + } + + return result; +} + +TString GetModuleFileNameOSX() { + Dl_info module_info; + if (dladdr(reinterpret_cast(GetModuleFileNameOSX), + &module_info) == 0) { + // Failed to find the symbol we asked for. + return std::string(); + } + return TString(module_info.dli_fname); +} + +#include + +TString MacPlatform::GetModuleFileName() { + //return GetModuleFileNameOSX(); + + TString result; + DynamicBuffer 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 MacPlatform::GetKeys() { + std::map keys; + + if (UsePListForConfigFile() == false) { + return GenericPlatform::GetKeys(); + } + else { + keys.insert(std::map::value_type(CONFIG_VERSION, + _T("app.version"))); + keys.insert(std::map::value_type(CONFIG_MAINJAR_KEY, + _T("JVMMainJarName"))); + keys.insert(std::map::value_type(CONFIG_MAINMODULE_KEY, + _T("JVMMainModuleName"))); + keys.insert(std::map::value_type( + CONFIG_MAINCLASSNAME_KEY, _T("JVMMainClassName"))); + keys.insert(std::map::value_type( + CONFIG_CLASSPATH_KEY, _T("JVMAppClasspath"))); + keys.insert(std::map::value_type(APP_NAME_KEY, + _T("CFBundleName"))); + keys.insert(std::map::value_type(CONFIG_APP_ID_KEY, + _T("JVMPreferencesID"))); + keys.insert(std::map::value_type(JVM_RUNTIME_KEY, + _T("JVMRuntime"))); + keys.insert(std::map::value_type(JPACKAGE_APP_DATA_DIR, + _T("CFBundleIdentifier"))); + + keys.insert(std::map::value_type(CONFIG_SPLASH_KEY, + _T("app.splash"))); + keys.insert(std::map::value_type(CONFIG_APP_MEMORY, + _T("app.memory"))); + keys.insert(std::map::value_type(CONFIG_APP_DEBUG, + _T("app.debug"))); + keys.insert(std::map::value_type( + CONFIG_APPLICATION_INSTANCE, _T("app.application.instance"))); + + keys.insert(std::map::value_type( + CONFIG_SECTION_APPLICATION, _T("Application"))); + keys.insert(std::map::value_type( + CONFIG_SECTION_JVMOPTIONS, _T("JVMOptions"))); + keys.insert(std::map::value_type( + CONFIG_SECTION_APPCDSJVMOPTIONS, _T("AppCDSJVMOptions"))); + keys.insert(std::map::value_type( + CONFIG_SECTION_APPCDSGENERATECACHEJVMOPTIONS, + _T("AppCDSGenerateCacheJVMOptions"))); + keys.insert(std::map::value_type( + CONFIG_SECTION_ARGOPTIONS, _T("ArgOptions"))); + } + + return keys; +} + +#ifdef DEBUG +bool MacPlatform::IsNativeDebuggerPresent() { + int state; + int mib[4]; + struct kinfo_proc info; + size_t size; + + info.kp_proc.p_flag = 0; + + mib[0] = CTL_KERN; + mib[1] = KERN_PROC; + mib[2] = KERN_PROC_PID; + mib[3] = getpid(); + + size = sizeof(info); + state = sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, NULL, 0); + assert(state == 0); + return ((info.kp_proc.p_flag & P_TRACED) != 0); +} + +int MacPlatform::GetProcessID() { + int pid = [[NSProcessInfo processInfo] processIdentifier]; + return pid; +} +#endif //DEBUG + +#endif //MAC diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/share/classes/jdk/jpackage/internal/AbstractAppImageBuilder.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/AbstractAppImageBuilder.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,273 @@ +/* + * Copyright (c) 2015, 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.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.*; +import static jdk.jpackage.internal.StandardBundlerParam.ARGUMENTS; + +public abstract class AbstractAppImageBuilder { + + private static final ResourceBundle I18N = ResourceBundle.getBundle( + "jdk.jpackage.internal.resources.MainResources"); + + private final Map properties; + private final Path root; + protected List excludeFileList = new ArrayList<>(); + + public AbstractAppImageBuilder(Map properties, + Path root) throws IOException { + this.properties = properties; + this.root = root; + excludeFileList.add(".*\\.diz"); + } + + 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 Map getProperties() { + return this.properties; + } + + public Path getRoot() { + return this.root; + } + + public String getExcludeFileList() { + return String.join(",", excludeFileList); + } + + 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 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 e : pairs.entrySet()) { + if (e.getValue() != null) { + result = result.replace(e.getKey(), e.getValue()); + } + } + return result; + } + } + + public void writeCfgFile(Map 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); + + 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.preferences.id=" + PREFERENCES_ID.fetchFrom(params)); + out.println("app.runtime=" + runtimeLocation); + out.println("app.identifier=" + IDENTIFIER.fetchFrom(params)); + out.println("app.classpath=" + String.join(File.pathSeparator, + CLASSPATH.fetchFrom(params).split("[ :;]"))); + + // 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("\\.", "/")); + } + } + + String version = JLinkBundlerHelper.getJDKVersion(params); + + if (!version.isEmpty()) { + out.println("app.java.version=" + version); + } + + out.println("jpackage.java.version=" + + System.getProperty("java.version")); + + Integer port = JLinkBundlerHelper.DEBUG.fetchFrom(params); + + if (port != null) { + out.println( + "app.debug=-agentlib:jdwp=transport=dt_socket," + + "server=y,suspend=y,address=localhost:" + + port); + } + + out.println(); + out.println("[JVMOptions]"); + List jvmargs = JVM_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 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); + } + } + + + out.close(); + } + +} diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/share/classes/jdk/jpackage/internal/AbstractBundler.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/AbstractBundler.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,196 @@ +/* + * Copyright (c) 2014, 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.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"); + + public static final BundlerParamInfo IMAGES_ROOT = + new StandardBundlerParam<>( + I18N.getString("param.images-root.name"), + I18N.getString("param.images-root.description"), + "imagesRoot", + File.class, + params -> new File( + StandardBundlerParam.BUILD_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 { + + InputStream is = streamResource(publicName, category, + defaultName, verbose, publicRoot); + if (is != null) { + try { + Files.copy(is, result.toPath(), + StandardCopyOption.REPLACE_EXISTING); + } finally { + is.close(); + } + } 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 { + + InputStream is = streamResource(publicName, category, + null, verbose, publicRoot); + if (is != null) { + try { + Files.copy(is, result.toPath()); + } finally { + is.close(); + } + } 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 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 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 params) { + try { + IOUtils.deleteRecursive( + StandardBundlerParam.BUILD_ROOT.fetchFrom(params)); + } catch (IOException e) { + Log.debug(e.getMessage()); + } + } +} diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/share/classes/jdk/jpackage/internal/AbstractImageBundler.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/AbstractImageBundler.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2015, 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.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-image" mode, + * or as an intermeadiate step in "create-installer" mode. + * + * The concrete implementations are in the platform specific Bundlers. + */ +public abstract class AbstractImageBundler extends AbstractBundler { + + private final static String JAVA_VERSION_SPEC = + "java version \"((\\d+).(\\d+).(\\d+).(\\d+))(-(.*))?(\\+[^\"]*)?\""; + + private static final ResourceBundle I18N = ResourceBundle.getBundle( + "jdk.jpackage.internal.resources.MainResources"); + + public void imageBundleValidation(Map p) + throws ConfigException { + StandardBundlerParam.validateMainClassInfoFromAppResources(p); + + boolean hasMainJar = MAIN_JAR.fetchFrom(p) != null; + boolean hasMainModule = + StandardBundlerParam.MODULE.fetchFrom(p) != null; + boolean hasMainClass = MAIN_CLASS.fetchFrom(p) != null; + boolean jreInstaller = Arguments.CREATE_JRE_INSTALLER.fetchFrom(p); + + if (!hasMainJar && !hasMainModule && !hasMainClass && !jreInstaller) { + throw new ConfigException( + I18N.getString("error.no-application-class"), + I18N.getString("error.no-application-class.advice")); + } + } + + public static void extractFlagsFromVersion( + Map params, String versionOutput) { + Pattern bitArchPattern = Pattern.compile("(\\d*)[- ]?[bB]it"); + Matcher matcher = bitArchPattern.matcher(versionOutput); + if (matcher.find()) { + params.put(".runtime.bit-arch", matcher.group(1)); + } else { + // presume 32 bit on no match + params.put(".runtime.bit-arch", "32"); + } + + Pattern oldVersionMatcher = Pattern.compile( + "java version \"((\\d+.(\\d+).\\d+)(_(\\d+)))?(-(.*))?\""); + matcher = oldVersionMatcher.matcher(versionOutput); + if (matcher.find()) { + params.put(".runtime.version", matcher.group(1)); + params.put(".runtime.version.release", matcher.group(2)); + params.put(".runtime.version.major", matcher.group(3)); + params.put(".runtime.version.update", matcher.group(5)); + params.put(".runtime.version.minor", matcher.group(5)); + params.put(".runtime.version.security", matcher.group(5)); + params.put(".runtime.version.patch", "0"); + params.put(".runtime.version.modifiers", matcher.group(7)); + } else { + Pattern newVersionMatcher = Pattern.compile(JAVA_VERSION_SPEC); + matcher = newVersionMatcher.matcher(versionOutput); + if (matcher.find()) { + params.put(".runtime.version", matcher.group(1)); + params.put(".runtime.version.release", matcher.group(1)); + params.put(".runtime.version.major", matcher.group(2)); + params.put(".runtime.version.update", matcher.group(3)); + params.put(".runtime.version.minor", matcher.group(3)); + params.put(".runtime.version.security", matcher.group(4)); + params.put(".runtime.version.patch", matcher.group(5)); + params.put(".runtime.version.modifiers", matcher.group(7)); + } else { + params.put(".runtime.version", ""); + params.put(".runtime.version.release", ""); + params.put(".runtime.version.major", ""); + params.put(".runtime.version.update", ""); + params.put(".runtime.version.minor", ""); + params.put(".runtime.version.security", ""); + params.put(".runtime.version.patch", ""); + params.put(".runtime.version.modifiers", ""); + } + } + } + + protected File createRoot(Map p, + File outputDirectory, boolean dependentTask, + String name, String jlinkKey) throws IOException { + if (!outputDirectory.isDirectory() && !outputDirectory.mkdirs()) { + throw new RuntimeException(MessageFormat.format( + I18N.getString("error.cannot-create-output-dir"), + outputDirectory.getAbsolutePath())); + } + if (!outputDirectory.canWrite()) { + throw new RuntimeException(MessageFormat.format( + I18N.getString("error.cannot-write-to-output-dir"), + outputDirectory.getAbsolutePath())); + } + if (!dependentTask) { + Log.verbose(MessageFormat.format( + I18N.getString("message.creating-app-bundle"), + name, outputDirectory.getAbsolutePath())); + } + + // Create directory structure + File rootDirectory = new File(outputDirectory, name); + + if (rootDirectory.exists()) { + if (!(FORCE.fetchFrom(p))) { + throw new IOException(MessageFormat.format( + I18N.getString("error.root-exists-without-force"), + rootDirectory.getAbsolutePath())); + } + IOUtils.deleteRecursive(rootDirectory); + } + rootDirectory.mkdirs(); + + if (!p.containsKey(JLinkBundlerHelper.JLINK_BUILDER.getID())) { + p.put(JLinkBundlerHelper.JLINK_BUILDER.getID(), jlinkKey); + } + + return rootDirectory; + } + +} diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/share/classes/jdk/jpackage/internal/ArgAction.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ArgAction.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,31 @@ +/* + * Copyright (c) 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.internal; + +@FunctionalInterface +interface ArgAction { + void execute(); +} diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/share/classes/jdk/jpackage/internal/Arguments.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/Arguments.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,945 @@ +/* + * Copyright (c) 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.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 IMAGE_MODE = "image"; + private static final String INSTALLER_MODE = "installer"; + private static final String JRE_INSTALLER_MODE = "jre-installer"; + + 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 static final BundlerParamInfo CREATE_IMAGE = + new StandardBundlerParam<>( + I18N.getString("param.create-image.name"), + I18N.getString("param.create-image.description"), + IMAGE_MODE, + Boolean.class, + p -> Boolean.FALSE, + (s, p) -> (s == null || "null".equalsIgnoreCase(s)) ? + true : Boolean.valueOf(s)); + + public static final BundlerParamInfo CREATE_INSTALLER = + new StandardBundlerParam<>( + I18N.getString("param.create-installer.name"), + I18N.getString("param.create-installer.description"), + INSTALLER_MODE, + Boolean.class, + p -> Boolean.FALSE, + (s, p) -> (s == null || "null".equalsIgnoreCase(s)) ? + true : Boolean.valueOf(s)); + + public static final BundlerParamInfo CREATE_JRE_INSTALLER = + new StandardBundlerParam<>( + I18N.getString("param.create-jre-installer.name"), + I18N.getString("param.create-jre-installer.description"), + JRE_INSTALLER_MODE, + Boolean.class, + p -> Boolean.FALSE, + (s, p) -> (s == null || "null".equalsIgnoreCase(s)) ? + true : Boolean.valueOf(s)); + + // regexp for parsing args (for example, for secondary launchers) + private static Pattern pattern = Pattern.compile( + "(?:(?:([\"'])(?:\\\\\\1|.)*?(?:\\1|$))|(?:\\\\[\"'\\s]|[^\\s]))++"); + + private DeployParams deployParams = null; + private BundlerType bundleType = null; + + private int pos = 0; + private List argList = null; + + private List allOptions = null; + + private ArrayList files = null; + + private String input = null; + private String output = null; + + private boolean hasMainJar = false; + private boolean hasMainClass = false; + private boolean hasMainModule = false; + private boolean hasTargetFormat = false; + private boolean hasAppImage = false; + private boolean retainBuildRoot = false; + + private String buildRoot = null; + private String mainJarPath = null; + + private static boolean jreInstaller = false; + + private List platformBundlers = null; + + private List secondaryLaunchers = null; + + private static Map argIds = new HashMap<>(); + private static Map argShortIds = new HashMap<>(); + + { + // init maps for parsing arguments + EnumSet options = EnumSet.allOf(CLIOptions.class); + + options.forEach(option -> { + argIds.put(option.getIdWithPrefix(), option); + if (option.getShortIdWithPrefix() != null) { + argShortIds.put(option.getShortIdWithPrefix(), option); + } + }); + } + + public Arguments(String[] args) { + initArgumentList(args); + } + + // CLIOptions is public for DeployParamsTest + public enum CLIOptions { + CREATE_IMAGE(IMAGE_MODE, OptionCategories.MODE, () -> { + context().bundleType = BundlerType.IMAGE; + context().deployParams.setTargetFormat("image"); + setOptionValue(IMAGE_MODE, true); + }), + + CREATE_INSTALLER(INSTALLER_MODE, OptionCategories.MODE, () -> { + setOptionValue(INSTALLER_MODE, true); + context().bundleType = BundlerType.INSTALLER; + String format = "installer"; + if (hasNextArg()) { + String arg = popArg(); + if (!arg.startsWith("-")) { + format = arg.toLowerCase(); + context().hasTargetFormat = true; + } else { + prevArg(); + } + } + context().deployParams.setTargetFormat(format); + }), + + CREATE_JRE_INSTALLER(JRE_INSTALLER_MODE, OptionCategories.MODE, () -> { + setOptionValue(JRE_INSTALLER_MODE, true); + context().bundleType = BundlerType.INSTALLER; + String format = "installer"; + if (hasNextArg()) { + String arg = popArg(); + if (!arg.startsWith("-")) { + format = arg.toLowerCase(); + context().hasTargetFormat = true; + } else { + prevArg(); + } + } + jreInstaller = true; + context().deployParams.setTargetFormat(format); + context().deployParams.setJreInstaller(true); + }), + + 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 ("class", "c", OptionCategories.PROPERTY, () -> { + context().hasMainClass = true; + setOptionValue("class", popArg()); + }), + + NAME ("name", "n", OptionCategories.PROPERTY), + + IDENTIFIER ("identifier", OptionCategories.PROPERTY), + + VERBOSE ("verbose", OptionCategories.PROPERTY, () -> { + setOptionValue("verbose", true); + Log.setVerbose(true); + }), + + FORCE ("force", OptionCategories.PROPERTY, () -> { + setOptionValue("force", true); + }), + + RESOURCE_DIR("resource-dir", + OptionCategories.PROPERTY, () -> { + String resourceDir = popArg(); + setOptionValue("resource-dir", resourceDir); + }), + + FILES ("files", "f", OptionCategories.PROPERTY, () -> { + context().files = new ArrayList<>(); + String files = popArg(); + context().files.addAll( + Arrays.asList(files.split(File.pathSeparator))); + }), + + ARGUMENTS ("arguments", "a", OptionCategories.PROPERTY, () -> { + List arguments = getArgumentList(popArg()); + setOptionValue("arguments", arguments); + }), + + STRIP_NATIVE_COMMANDS ("strip-native-commands", + OptionCategories.PROPERTY, () -> { + setOptionValue("strip-native-commands", true); + }), + + ICON ("icon", OptionCategories.PROPERTY), + CATEGORY ("category", OptionCategories.PROPERTY), + COPYRIGHT ("copyright", OptionCategories.PROPERTY), + + LICENSE_FILE ("license-file", OptionCategories.PROPERTY), + + VERSION ("app-version", OptionCategories.PROPERTY), + + JVM_ARGS ("jvm-args", OptionCategories.PROPERTY, () -> { + List args = getArgumentList(popArg()); + args.forEach(a -> setOptionValue("jvm-args", a)); + }), + + FILE_ASSOCIATIONS ("file-associations", + OptionCategories.PROPERTY, () -> { + Map args = new HashMap<>(); + + // load .properties file + Map 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> associationList = + new ArrayList>(); + + associationList.add(args); + + // check that we really add _another_ value to the list + setOptionValue("file-associations", associationList); + + }), + + SECONDARY_LAUNCHER ("secondary-launcher", + OptionCategories.PROPERTY, () -> { + context().secondaryLaunchers.add( + new SecondaryLauncherArguments(popArg())); + }), + + BUILD_ROOT ("build-root", OptionCategories.PROPERTY, () -> { + context().buildRoot = popArg(); + context().retainBuildRoot = true; + setOptionValue("build-root", context().buildRoot); + }), + + INSTALL_DIR ("install-dir", OptionCategories.PROPERTY), + + PREDEFINED_APP_IMAGE ("app-image", OptionCategories.PROPERTY, ()-> { + setOptionValue("app-image", popArg()); + context().hasAppImage = true; + }), + + PREDEFINED_RUNTIME_IMAGE ("runtime-image", OptionCategories.PROPERTY), + + MAIN_JAR ("main-jar", "j", 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), + + LIMIT_MODULES ("limit-modules", 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); + + 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; + } + + private 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() { + String prefix = isMode() ? "create-" : "--"; + return prefix + this.id; + } + + String getShortIdWithPrefix() { + return this.shortId == null ? null : "-" + this.shortId; + } + + void execute() { + if (action != null) { + action.execute(); + } else { + defaultAction(); + } + } + + boolean isMode() { + return category == OptionCategories.MODE; + } + + OptionCategories getCategory() { + return category; + } + + 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 void prevArg() { + context().pos--; + } + + private static boolean hasNextArg() { + return context().pos < context().argList.size(); + } + } + + enum OptionCategories { + MODE, + MODULAR, + PROPERTY, + PLATFORM_MAC, + PLATFORM_WIN, + PLATFORM_LINUX; + } + + private void initArgumentList(String[] args) { + argList = new ArrayList(args.length); + for (String arg : args) { + if (arg.startsWith("@")) { + if (arg.length() > 1) { + String filename = arg.substring(1); + argList.addAll(extractArgList(filename)); + } else { + Log.error("invalid option ["+arg+"]"); + } + } else { + argList.add(arg); + } + } + Log.debug ("\nJPackage argument list: \n" + argList + "\n"); + pos = 0; + + deployParams = new DeployParams(); + bundleType = BundlerType.NONE; + + allOptions = new ArrayList<>(); + + secondaryLaunchers = new ArrayList<>(); + } + + private List extractArgList(String filename) { + List args = new ArrayList(); + try { + File f = new File(filename); + if (f.exists()) { + List lines = Files.readAllLines(f.toPath()); + for (String line : lines) { + String [] qsplit; + String quote = "\""; + if (line.contains("\"")) { + qsplit = line.split("\""); + } else { + qsplit = line.split("\'"); + quote = "\'"; + } + for (int i=0; i> launchersAsMap = + new ArrayList<>(); + + for (SecondaryLauncherArguments sl : secondaryLaunchers) { + launchersAsMap.add(sl.getLauncherMap()); + } + + deployParams.addBundleArgument( + StandardBundlerParam.SECONDARY_LAUNCHERS.getID(), + launchersAsMap); + + // at this point deployParams should be already configured + + deployParams.validate(); + + BundleParams bp = deployParams.getBundleParams(); + + // validate name(s) + ArrayList usedNames = new ArrayList(); + usedNames.add(bp.getName()); // add main app name + + for (SecondaryLauncherArguments sl : secondaryLaunchers) { + Map slMap = sl.getLauncherMap(); + String slName = + (String) slMap.get(Arguments.CLIOptions.NAME.getId()); + if (slName == null) { + throw new PackagerException("ERR_NoSecondaryLauncherName"); + } else { + for (String usedName : usedNames) { + if (slName.equals(usedName)) { + throw new PackagerException("ERR_NoUniqueName"); + } + } + } + usedNames.add(slName); + } + if (jreInstaller && bp.getName() == null) { + throw new PackagerException("ERR_NoJreInstallerName"); + } + + return generateBundle(bp.getBundleParamsAsMap()); + } catch (Exception e) { + if (Log.isVerbose()) { + throw e; + } else { + Log.error(e.getMessage()); + if (e.getCause() != null && e.getCause() != e) { + Log.error(e.getCause().getMessage()); + } + return false; + } + } + } + + private void validateArguments() { + CLIOptions mode = allOptions.get(0); + for (CLIOptions option : allOptions) { + if(!ValidOptions.checkIfSupported(mode, option)) { + System.out.println("WARNING: argument [" + + option.getId() + "] is not " + + "supported for current configuration."); + } + } + } + + private List getPlatformBundlers() { + + if (platformBundlers != null) { + return platformBundlers; + } + + platformBundlers = new ArrayList<>(); + for (jdk.jpackage.internal.Bundler bundler : + Bundlers.createBundlersInstance().getBundlers( + bundleType.toString())) { + if (hasTargetFormat && deployParams.getTargetFormat() != null && + !deployParams.getTargetFormat().equalsIgnoreCase( + bundler.getID())) { + continue; + } + if (bundler.supported()) { + platformBundlers.add(bundler); + } + } + + return platformBundlers; + } + + private boolean generateBundle(Map params) + throws PackagerException { + + boolean bundleCreated = false; + + // the build-root needs to be fetched from the params early, + // to prevent each copy of the params (such as may be used for + // secondary launchers) from generating a separate build-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.BUILD_ROOT.fetchFrom(params); + + for (jdk.jpackage.internal.Bundler bundler : getPlatformBundlers()) { + Map localParams = new HashMap<>(params); + try { + if (bundler.validate(localParams)) { + File result = + bundler.execute(localParams, deployParams.outdir); + if (!retainBuildRoot) { + bundler.cleanup(localParams); + } + if (result == null) { + throw new PackagerException("MSG_BundlerFailed", + bundler.getID(), bundler.getName()); + } + bundleCreated = true; // at least one bundle was created + } + } catch (UnsupportedPlatformException e) { + Log.debug(MessageFormat.format( + I18N.getString("MSG_BundlerPlatformException"), + bundler.getName())); + } catch (ConfigException e) { + Log.debug(e); + if (e.getAdvice() != null) { + Log.error(MessageFormat.format( + I18N.getString("MSG_BundlerConfigException"), + bundler.getName(), e.getMessage(), e.getAdvice())); + } else { + Log.error(MessageFormat.format(I18N.getString( + "MSG_BundlerConfigExceptionNoAdvice"), + bundler.getName(), e.getMessage())); + } + } catch (RuntimeException re) { + Log.error(MessageFormat.format( + I18N.getString("MSG_BundlerRuntimeException"), + bundler.getName(), re.toString())); + Log.debug(re); + } finally { + if (retainBuildRoot) { + Log.verbose(MessageFormat.format( + I18N.getString("message.debug-working-directory"), + (new File(buildRoot)).getAbsolutePath())); + } + } + } + + return bundleCreated; + } + + private void addResources(DeployParams deployParams, + String inputdir, List inputfiles) { + + if (inputdir == null || inputdir.isEmpty()) { + return; + } + + File baseDir = new File(inputdir); + + if (!baseDir.isDirectory()) { + Log.error( + "Unable to add resources: \"--input\" is not a directory."); + return; + } + + List fileNames; + if (inputfiles != null) { + fileNames = inputfiles; + } else { + // "-files" is omitted, all files in input cdir (which + // is a mandatory argument in this case) will be packaged. + fileNames = new ArrayList<>(); + try (Stream 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 boolean isCLIOption(String arg) { + return toCLIOption(arg) != null; + } + + static CLIOptions toCLIOption(String arg) { + CLIOptions option; + if ((option = argIds.get(arg)) == null) { + option = argShortIds.get(arg); + } + return option; + } + + static Map getArgumentMap(String inputString) { + Map map = new HashMap<>(); + List list = getArgumentList(inputString); + for (String pair : list) { + int equals = pair.indexOf("="); + if (equals != -1) { + String key = pair.substring(0, equals); + String value = pair.substring(equals+1, pair.length()); + map.put(key, value); + } + } + return map; + } + + static Map getPropertiesFromFile(String filename) { + Map 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 getArgumentList(String inputString) { + List 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; + } + + static boolean isJreInstaller() { + return jreInstaller; + } +} diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/share/classes/jdk/jpackage/internal/BasicBundlers.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/BasicBundlers.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2014, 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.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. + *
    + *
  • Windows file image
  • + *
  • Mac .app
  • + *
  • Linux file image
  • + *
  • Windows MSI
  • + *
  • Windows EXE
  • + *
  • Mac DMG
  • + *
  • Mac PKG
  • + *
  • Linux DEB
  • + *
  • Linux RPM
  • + * + *
+ */ +public class BasicBundlers implements Bundlers { + + boolean defaultsLoaded = false; + + private final Collection bundlers = new CopyOnWriteArrayList<>(); + + @Override + public Collection getBundlers() { + return Collections.unmodifiableCollection(bundlers); + } + + @Override + public Collection 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)); + } + } + + @Override + public void loadDefaultBundlers() { + // no-op. We now load all bundlers from module system. + } + + // Loads bundlers from the META-INF/services direct + @Override + public void loadBundlersFromServices(ClassLoader cl) { + ServiceLoader loader = ServiceLoader.load(Bundler.class, cl); + for (Bundler aLoader : loader) { + bundlers.add(aLoader); + } + } + + @Override + public void loadBundler(Bundler bundler) { + bundlers.add(bundler); + } +} diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/share/classes/jdk/jpackage/internal/BundleParams.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/BundleParams.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,414 @@ +/* + * Copyright (c) 2012, 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.internal; + +import jdk.jpackage.internal.*; +import jdk.jpackage.internal.BundlerType; +import jdk.jpackage.internal.JLinkBundlerHelper; + +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 params; + + // RelativeFileSet + public static final String PARAM_APP_RESOURCES = "appResources"; + + // BundlerType + public static final String PARAM_TYPE = "type"; + + // String + public static final String PARAM_BUNDLE_FORMAT = "bundleFormat"; + // String + 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 - Copyright. Used on Mac */ + public static final String PARAM_COPYRIGHT = "copyright"; + + // String - GUID on windows for MSI, CFBundleIdentifier on Mac + // If not compatible with requirements then bundler either do not bundle + // or autogenerate + public static final String PARAM_IDENTIFIER = "identifier"; + + /* boolean - shortcut preferences */ + public static final String PARAM_SHORTCUT = "shortcutHint"; + // boolean - menu shortcut preference + public static final String PARAM_MENU = "menuHint"; + + // String - Application version. Format may differ for different bundlers + public static final String PARAM_VERSION = "appVersion"; + + // String - Application category. Used at least on Mac/Linux. + // Value is platform specific + public static final String PARAM_CATEGORY = "applicationCategory"; + + // String - Optional short application + public static final String PARAM_TITLE = "title"; + + // 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 params) { + this.params = new HashMap<>(params); + } + + public void addAllBundleParams(Map p) { + params.putAll(p); + } + + public C fetchParam(BundlerParamInfo paramInfo) { + return paramInfo.fetchFrom(params); + } + + @SuppressWarnings("unchecked") + public C fetchParamWithDefault( + Class klass, C defaultValue, String... keys) { + for (String key : keys) { + Object o = params.get(key); + if (klass.isInstance(o)) { + return (C) o; + } else if (params.containsKey(key) && o == null) { + return null; + } else if (o != null) { + Log.debug("Bundle param " + key + " is not type " + klass); + } + } + return defaultValue; + } + + public C fetchParam(Class klass, String... keys) { + return fetchParamWithDefault(klass, null, keys); + } + + // 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 getBundleParamsAsMap() { + return new HashMap<>(params); + } + + public void setJvmargs(List jvmargs) { + putUnlessNullOrEmpty(JVM_OPTIONS.getID(), jvmargs); + } + + public void setArguments(List arguments) { + putUnlessNullOrEmpty(ARGUMENTS.getID(), arguments); + } + + public void setAddModules(String value) { + putUnlessNull(StandardBundlerParam.ADD_MODULES.getID(), value); + } + + public void setLimitModules(String value) { + putUnlessNull(StandardBundlerParam.LIMIT_MODULES.getID(), value); + } + + public void setStripNativeCommands(boolean value) { + putUnlessNull(StandardBundlerParam.STRIP_NATIVE_COMMANDS.getID(), + value); + } + + public void setModulePath(String value) { + putUnlessNull(StandardBundlerParam.MODULE_PATH.getID(), value); + } + + public void setMainModule(String value) { + putUnlessNull(StandardBundlerParam.MODULE.getID(), value); + } + + public void setDebug(String value) { + putUnlessNull(JLinkBundlerHelper.DEBUG.getID(), value); + } + + public String getApplicationID() { + return fetchParam(IDENTIFIER); + } + + public String getPreferencesID() { + return fetchParam(PREFERENCES_ID); + } + + public String getTitle() { + return fetchParam(TITLE); + } + + public void setTitle(String title) { + putUnlessNull(PARAM_TITLE, title); + } + + public String getApplicationClass() { + return fetchParam(MAIN_CLASS); + } + + public void setApplicationClass(String applicationClass) { + putUnlessNull(PARAM_APPLICATION_CLASS, applicationClass); + } + + public String getAppVersion() { + return fetchParam(VERSION); + } + + public void setAppVersion(String version) { + putUnlessNull(PARAM_VERSION, version); + } + + public String getDescription() { + return fetchParam(DESCRIPTION); + } + + public void setDescription(String s) { + putUnlessNull(PARAM_DESCRIPTION, s); + } + + public void setInstalldirChooser(Boolean b) { + putUnlessNull(PARAM_INSTALLDIR_CHOOSER, b); + } + + public String getName() { + return fetchParam(APP_NAME); + } + + public void setName(String name) { + putUnlessNull(PARAM_NAME, name); + } + + @SuppressWarnings("deprecation") + public BundlerType getType() { + return fetchParam(BundlerType.class, PARAM_TYPE); + } + + @SuppressWarnings("deprecation") + public void setType(BundlerType type) { + putUnlessNull(PARAM_TYPE, type); + } + + public String getBundleFormat() { + return fetchParam(String.class, PARAM_BUNDLE_FORMAT); + } + + public void setBundleFormat(String t) { + putUnlessNull(PARAM_BUNDLE_FORMAT, t); + } + + public boolean getVerbose() { + return fetchParam(VERBOSE); + } + + public List getJvmargs() { + return JVM_OPTIONS.fetchFrom(params); + } + + public List getArguments() { + return ARGUMENTS.fetchFrom(params); + } + + public jdk.jpackage.internal.RelativeFileSet getAppResource() { + return fetchParam(APP_RESOURCES); + } + + public void setAppResource(jdk.jpackage.internal.RelativeFileSet fs) { + putUnlessNull(PARAM_APP_RESOURCES, fs); + } + + public void setAppResourcesList( + List rfs) { + putUnlessNull(APP_RESOURCES_LIST.getID(), rfs); + } + + public String getApplicationCategory() { + return fetchParam(CATEGORY); + } + + public void setApplicationCategory(String category) { + putUnlessNull(PARAM_CATEGORY, category); + } + + public String getMainClassName() { + String applicationClass = getApplicationClass(); + + if (applicationClass == null) { + return null; + } + + int idx = applicationClass.lastIndexOf("."); + if (idx >= 0) { + return applicationClass.substring(idx+1); + } + return applicationClass; + } + + public String getCopyright() { + return fetchParam(COPYRIGHT); + } + + public void setCopyright(String c) { + putUnlessNull(PARAM_COPYRIGHT, c); + } + + public String getIdentifier() { + return fetchParam(IDENTIFIER); + } + + public void setIdentifier(String s) { + putUnlessNull(PARAM_IDENTIFIER, s); + } + + private String mainJar = null; + + // assuming that application was packaged according to the rules + // we must have application jar, i.e. jar where we embed launcher + // and have main application class listed as main class! + // If there are more than one, or none - it will be treated as + // deployment error + // + // Note we look for both JavaFX executable jars and regular executable jars + // As long as main "application" entry point is the same it is main class + // (i.e. for FX jar we will use JavaFX manifest entry ...) + public String getMainApplicationJar() { + jdk.jpackage.internal.RelativeFileSet appResources = getAppResource(); + if (mainJar != null) { + if (getApplicationClass() == null) try { + if (appResources != null) { + File srcdir = appResources.getBaseDirectory(); + JarFile jf = new JarFile(new File(srcdir, mainJar)); + Manifest m = jf.getManifest(); + Attributes attrs = (m != null) ? + m.getMainAttributes() : null; + if (attrs != null) { + setApplicationClass( + attrs.getValue(Attributes.Name.MAIN_CLASS)); + } + } + } catch (IOException ignore) { + } + return mainJar; + } + + String applicationClass = getApplicationClass(); + + if (appResources == null || applicationClass == null) { + return null; + } + File srcdir = appResources.getBaseDirectory(); + for (String fname : appResources.getIncludedFiles()) { + JarFile jf; + try { + jf = new JarFile(new File(srcdir, fname)); + Manifest m = jf.getManifest(); + Attributes attrs = (m != null) ? m.getMainAttributes() : null; + if (attrs != null) { + boolean javaMain = applicationClass.equals( + attrs.getValue(Attributes.Name.MAIN_CLASS)); + + if (javaMain) { + mainJar = fname; + return mainJar; + } + } + } catch (IOException ignore) { + } + } + return null; + } + + public String getVendor() { + return fetchParam(VENDOR); + } + + public void setVendor(String vendor) { + putUnlessNull(PARAM_VENDOR, vendor); + } + + public String getEmail() { + return fetchParam(String.class, PARAM_EMAIL); + } + + public void setEmail(String email) { + putUnlessNull(PARAM_EMAIL, email); + } + + public void putUnlessNull(String param, Object value) { + if (value != null) { + params.put(param, value); + } + } + + public void putUnlessNullOrEmpty(String param, Collection value) { + if (value != null && !value.isEmpty()) { + params.put(param, value); + } + } + + public void putUnlessNullOrEmpty(String param, Map value) { + if (value != null && !value.isEmpty()) { + params.put(param, value); + } + } + +} diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/share/classes/jdk/jpackage/internal/Bundler.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/Bundler.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2014, 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.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 A more verbose description of the bundler. + */ + String getDescription(); + + /** + * @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(); + + /** + * The parameters that this bundler uses to generate it's bundle. + * @return immutable collection + */ + Collection> getBundleParameters(); + + /** + * 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 UnsupportedPlatformException If the bundler cannot run on this + * platform (i.e. creating mac apps on windows) + * @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 params) + throws UnsupportedPlatformException, 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 parameters as specified by getBundleParameters. + * 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: + *
    + *
  • A required parameter is not found in the params list, for + * example missing the main class.
  • + *
  • A parameter has the wrong type of an object, for example a + * String where a File is required
  • + *
  • Bundler specific incompatibilities with the parameters, for + * example a bad version number format or an application id with + * forward slashes.
  • + *
+ */ + public File execute( + Map params, File outputParentDir); + + /** + * Removes temporary files that are used for bundling. + */ + public void cleanup(Map params); + + /** + * Returns "true" if this bundler is supported on current platform. + */ + public boolean supported(); +} diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/share/classes/jdk/jpackage/internal/BundlerParamInfo.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/BundlerParamInfo.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2014, 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.internal; + +import java.util.Map; +import java.util.function.BiFunction; +import java.util.function.Function; + +/** + * BundlerParamInfo + * + * A BundlerParamInfo encapsulates an individual bundler parameter of type . + */ +class BundlerParamInfo { + /** + * The user friendly name of the parameter + */ + String name; + + /** + * A more verbose description of the parameter + */ + String description; + + /** + * The command line and hashmap name of the parameter + */ + String id; + + /** + * Type of the parameter. + */ + Class valueType; + + /** + * If the value is not set, and no fallback value is found, + * the parameter uses the value returned by the producer. + */ + Function, T> defaultValueFunction; + + /** + * An optional string converter for command line arguments. + */ + BiFunction, T> stringConverter; + + String getName() { + return name; + } + + void setName(String name) { + this.name = name; + } + + String getDescription() { + return description; + } + + void setDescription(String description) { + this.description = description; + } + + String getID() { + return id; + } + + Class getValueType() { + return valueType; + } + + void setValueType(Class valueType) { + this.valueType = valueType; + } + + Function, T> getDefaultValueFunction() { + return defaultValueFunction; + } + + void setDefaultValueFunction( + Function, T> defaultValueFunction) { + this.defaultValueFunction = defaultValueFunction; + } + + BiFunction,T> + getStringConverter() { + return stringConverter; + } + + void setStringConverter(BiFunction, T> stringConverter) { + this.stringConverter = stringConverter; + } + + @SuppressWarnings("unchecked") + final T fetchFrom(Map params) { + return fetchFrom(params, true); + } + + @SuppressWarnings("unchecked") + final T fetchFrom(Map params, + boolean invokeDefault) { + Object o = params.get(getID()); + if (o instanceof String && getStringConverter() != null) { + return getStringConverter().apply((String)o, params); + } + + Class 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); + } + return result; + } + + // ultimate fallback + return null; + } +} diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/share/classes/jdk/jpackage/internal/BundlerType.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/BundlerType.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2012, 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.internal; + +enum BundlerType { + NONE, + IMAGE, // Generates app image only + INSTALLER // Generates installers +} diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/share/classes/jdk/jpackage/internal/Bundlers.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/Bundlers.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2014, 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.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 bundlersLoader = + ServiceLoader.load(Bundlers.class, servicesClassLoader); + Bundlers bundlers = null; + Iterator 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 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 getBundlers(String type); + + /** + * Loads the bundlers common to the JDK. A typical implementation + * would load: + *
    + *
  • Windows file image
  • + *
  • Mac .app
  • + *
  • Linux file image
  • + + *
  • Windows MSI
  • + *
  • Windows EXE
  • + *
  • Mac DMG
  • + *
  • Mac PKG
  • + *
  • Linux DEB
  • + *
  • Linux RPM
  • + * + *
+ * + * This method is called from the + * {@link #createBundlersInstance(ClassLoader)} + * and {@link #createBundlersInstance()} methods. + * NOTE: Because of the module system this method is now not used. + */ + void loadDefaultBundlers(); + + /** + * 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); + + /** + * Loads a specific bundler into the set of bundlers. + * Useful for a manually configured bundler. + * + * @param bundler the specific bundler to add + */ + void loadBundler(Bundler bundler); +} diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/share/classes/jdk/jpackage/internal/CLIHelp.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/CLIHelp.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,79 @@ +/* + * Copyright (c) 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.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) { + + Platform platform = Platform.getPlatform(); + if (noArgs) { + Log.info(MessageFormat.format( + I18N.getString("MSG_Help_no_args"), File.pathSeparator)); + } else { + Log.info(MessageFormat.format( + I18N.getString("MSG_Help_common"), File.pathSeparator)); + + switch (platform) { + case MAC: + Log.info(I18N.getString("MSG_Help_mac")); + if (Log.isDebug()) { + Log.info(I18N.getString("MSG_Help_win")); + Log.info(I18N.getString("MSG_Help_linux")); + } + break; + case LINUX: + Log.info(I18N.getString("MSG_Help_linux")); + if (Log.isDebug()) { + Log.info(I18N.getString("MSG_Help_win")); + Log.info(I18N.getString("MSG_Help_mac")); + } + break; + case WINDOWS: + Log.info(I18N.getString("MSG_Help_win")); + if (Log.isDebug()) { + Log.info(I18N.getString("MSG_Help_mac")); + Log.info(I18N.getString("MSG_Help_linux")); + } + break; + } + } + } +} diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/share/classes/jdk/jpackage/internal/ConfigException.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ConfigException.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2012, 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.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; + } +} diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/share/classes/jdk/jpackage/internal/DeployParams.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/DeployParams.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,617 @@ +/* + * Copyright (c) 2011, 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.internal; + +import java.io.File; +import java.nio.file.Files; +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 resources = new ArrayList<>(); + + String id; + String title; + String vendor; + String email; + String description; + String category; + String licenseType; + String copyright; + String version; + Boolean systemWide; + Boolean serviceHint; + Boolean signBundle; + Boolean installdirChooser; + + String applicationClass; + + List params; + List arguments; //unnamed arguments + + // Java 9 modules support + String addModules = null; + String limitModules = null; + Boolean stripNativeCommands = null; + String modulePath = null; + String module = null; + String debugPort = null; + + boolean jreInstaller = false; + + File outdir = null; + + String appId = null; + + // list of jvm args + // (in theory string can contain spaces and need to be escaped + List jvmargs = new LinkedList<>(); + + // raw arguments to the bundler + Map bundlerArguments = new LinkedHashMap<>(); + + void setCategory(String category) { + this.category = category; + } + + void setLicenseType(String licenseType) { + this.licenseType = licenseType; + } + + void setCopyright(String copyright) { + this.copyright = copyright; + } + + void setVersion(String version) { + this.version = version; + } + + void setSystemWide(Boolean systemWide) { + this.systemWide = systemWide; + } + + void setInstalldirChooser(Boolean installdirChooser) { + this.installdirChooser = installdirChooser; + } + + void setSignBundle(Boolean signBundle) { + this.signBundle = signBundle; + } + + void addJvmArg(String v) { + jvmargs.add(v); + } + + void setArguments(List args) { + this.arguments = args; + } + + List getArguments() { + return this.arguments; + } + + void addArgument(String arg) { + this.arguments.add(arg); + } + + void addAddModule(String value) { + if (addModules == null) { + addModules = value; + } + else { + addModules += "," + value; + } + } + + void addLimitModule(String value) { + if (limitModules == null) { + limitModules = value; + } + else { + limitModules += "," + value; + } + } + + String getModulePath() { + return this.modulePath; + } + + void setModulePath(String value) { + this.modulePath = value; + } + + void setModule(String value) { + this.module = value; + } + + void setDebug(String value) { + this.debugPort = value; + } + + void setStripNativeCommands(boolean value) { + this.stripNativeCommands = value; + } + + void setDescription(String description) { + this.description = description; + } + + public void setAppId(String id) { + appId = id; + } + + void setParams(List params) { + this.params = params; + } + + void setTitle(String title) { + this.title = title; + } + + void setVendor(String vendor) { + this.vendor = vendor; + } + + void setEmail(String email) { + this.email = email; + } + + void setApplicationClass(String applicationClass) { + this.applicationClass = applicationClass; + } + + void setJreInstaller(boolean value) { + jreInstaller = value; + } + + File getOutput() { + return outdir; + } + + 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 expandFileset(File root) { + List 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) { + File file = new File(baseDir, path); + // normalize top level dir + // 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)))); + } + + 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); + } + + private static File createFile(final File baseDir, final String path) { + final File testFile = new File(path); + return testFile.isAbsolute() ? + testFile : new File(baseDir == null ? + null : baseDir.getAbsolutePath(), path); + } + + static void validateAppName(String s) throws PackagerException { + if (s == null || s.length() == 0) { + // empty or null string - there is no unsupported char + return; + } + + int last = s.length() - 1; + + char fc = s.charAt(0); + char lc = s.charAt(last); + + // illegal to end in backslash escape char + if (lc == '\\') { + throw new PackagerException("ERR_InvalidCharacterInArgument", "--name"); + } + + 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 > '~' || a == '%') { + // Reject '%', whitespaces and ISO Control. + // 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 ignored. + if (Character.isISOControl(a) || + Character.isWhitespace(a) || a == '%') { + throw new PackagerException( + "ERR_InvalidCharacterInArgument", "--name"); + } + } + if (a == '"') { + throw new PackagerException( + "ERR_InvalidCharacterInArgument", "--name"); + } + } + } + + public void validate() throws PackagerException { + if (outdir == null) { + throw new PackagerException("ERR_MissingArgument", "--output"); + } + + boolean hasModule = (bundlerArguments.get( + Arguments.CLIOptions.MODULE.getId()) != null); + boolean hasImage = (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 hasAppImage = (bundlerArguments.get( + Arguments.CLIOptions.PREDEFINED_APP_IMAGE.getId()) != null); + + if (getBundleType() == BundlerType.IMAGE) { + // 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 (getBundleType() == BundlerType.INSTALLER) { + if (!Arguments.isJreInstaller()) { + 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 && !hasImage && !jreInstaller) { + if (resources.isEmpty()) { + throw new PackagerException("ERR_MissingAppResources"); + } + if (!hasClass) { + throw new PackagerException("ERR_MissingArgument", "--class"); + } + if (!hasMain) { + throw new PackagerException("ERR_MissingArgument", + "--main-jar"); + } + } + + String name = (String)bundlerArguments.get( + Arguments.CLIOptions.NAME.getId()); + validateAppName(name); + + // 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()) { + throw new PackagerException("ERR_AppImageNotExist", appImage); + } + + File appImageAppDir = new File(appImage + File.separator + "app"); + File appImageRuntimeDir = new File(appImage + + File.separator + "runtime"); + if (!appImageAppDir.exists() || !appImageRuntimeDir.exists()) { + throw new PackagerException("ERR_AppImageInvalid", appImage); + } + } + + // Validate build-root + String root = (String)bundlerArguments.get( + Arguments.CLIOptions.BUILD_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"); + } + } + } + + boolean validateForBundle() { + boolean result = false; + + // Success + if (((applicationClass != null && !applicationClass.isEmpty()) || + (module != null && !module.isEmpty()))) { + result = true; + } + + return result; + } + + BundlerType bundleType = BundlerType.NONE; + String targetFormat = null; //means any + + void setBundleType(BundlerType type) { + bundleType = type; + } + + BundlerType getBundleType() { + return bundleType; + } + + void setTargetFormat(String t) { + targetFormat = t; + } + + String getTargetFormat() { + return targetFormat; + } + + private String getArch() { + String arch = System.getProperty("os.arch").toLowerCase(); + + if ("x86".equals(arch) || "i386".equals(arch) || "i486".equals(arch) + || "i586".equals(arch) || "i686".equals(arch)) { + arch = "x86"; + } else if ("x86_64".equals(arch) || "amd64".equals("arch")) { + arch = "x86_64"; + } + + return arch; + } + + static final Set multi_args = new TreeSet<>(Arrays.asList( + StandardBundlerParam.JVM_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) { + bundlerArguments.put(key, existingValue + "\n\n" + 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! + String currentOS = System.getProperty("os.name").toLowerCase(); + String currentArch = getArch(); + + bundleParams.setAppResourcesList(resources); + + bundleParams.setIdentifier(id); + + bundleParams.setApplicationClass(applicationClass); + bundleParams.setAppVersion(version); + bundleParams.setType(bundleType); + bundleParams.setBundleFormat(targetFormat); + bundleParams.setVendor(vendor); + bundleParams.setEmail(email); + bundleParams.setInstalldirChooser(installdirChooser); + bundleParams.setCopyright(copyright); + bundleParams.setApplicationCategory(category); + bundleParams.setDescription(description); + bundleParams.setTitle(title); + + bundleParams.setJvmargs(jvmargs); + bundleParams.setArguments(arguments); + + if (addModules != null && !addModules.isEmpty()) { + bundleParams.setAddModules(addModules); + } + + if (limitModules != null && !limitModules.isEmpty()) { + bundleParams.setLimitModules(limitModules); + } + + if (stripNativeCommands != null) { + bundleParams.setStripNativeCommands(stripNativeCommands); + } + + if (modulePath != null && !modulePath.isEmpty()) { + bundleParams.setModulePath(modulePath); + } + + if (module != null && !module.isEmpty()) { + bundleParams.setMainModule(module); + } + + if (debugPort != null && !debugPort.isEmpty()) { + bundleParams.setDebug(debugPort); + } + + Map paramsMap = new TreeMap<>(); + if (params != null) { + for (Param p : params) { + paramsMap.put(p.name, p.value); + } + } + + Map unescapedHtmlParams = new TreeMap<>(); + Map escapedHtmlParams = new TreeMap<>(); + + // check for collisions + TreeSet 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; + } + + Map getBundlerArguments() { + return this.bundlerArguments; + } + + void putUnlessNull(String param, Object value) { + if (value != null) { + bundlerArguments.put(param, value); + } + } + + void putUnlessNullOrEmpty(String param, Map value) { + if (value != null && !value.isEmpty()) { + bundlerArguments.put(param, value); + } + } + + void putUnlessNullOrEmpty(String param, Collection value) { + if (value != null && !value.isEmpty()) { + bundlerArguments.put(param, value); + } + } + + @Override + public String toString() { + return "DeployParams {" + "output: " + outdir + + " resources: {" + resources + "}}"; + } + +} diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/share/classes/jdk/jpackage/internal/EnumeratedBundlerParam.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/EnumeratedBundlerParam.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2014, 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.internal; + +import java.util.*; +import java.util.function.BiFunction; +import java.util.function.Function; + +/** + * EnumeratedBundlerParams + * + * 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: + * + *
{@code
+ *     Set 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);
+ * }
+ * + */ +class EnumeratedBundlerParam extends BundlerParamInfo { + // 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 elements; + private final boolean strict; + + EnumeratedBundlerParam(String name, String description, + String id, Class valueType, + Function, T> defaultValueFunction, + BiFunction, T> stringConverter, + Map elements, boolean strict) { + this.name = name; + this.description = description; + 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 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(); + } + + T validatedFetchFrom(Map params) + throws InvalidBundlerParamException { + if (isStrict()) { + T value = fetchFrom(params); + if (!isInPossibleValues(value)) { + throw new InvalidBundlerParamException("Parameter " + + value.toString() + + " not in valid set of values for BundlerParam " + + name); + } + return value; + } + return fetchFrom(params); + } + +} diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/share/classes/jdk/jpackage/internal/IOUtils.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/IOUtils.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,312 @@ +/* + * Copyright (c) 2012, 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.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() { + @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() { + @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 excludes) throws IOException { + Files.walkFileTree(src, new SimpleFileVisitor() { + @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 copyFromURL(URL location, File file) throws IOException { + copyFromURL(location, file, false); + } + + public static void copyFromURL(URL location, File file, boolean append) + throws IOException { + if (location == null) { + throw new IOException("Missing input resource!"); + } + if (file.exists() && !append) { + file.delete(); + } + InputStream in = location.openStream(); + FileOutputStream out = new FileOutputStream(file, append); + byte[] buffer = new byte[1024]; + int len; + while ((len = in.read(buffer)) != -1) { + out.write(buffer, 0, len); + } + out.close(); + in.close(); + file.setReadOnly(); + file.setReadable(true, false); + } + + 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(); + + FileChannel source = null; + FileChannel destination = null; + source = new FileInputStream(sourceFile).getChannel(); + destination = new FileOutputStream(destFile).getChannel(); + if (destination != null && source != null) { + destination.transferFrom(source, 0, source.size()); + } + if (source != null) { + source.close(); + } + if (destination != null) { + destination.close(); + } + + //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, boolean verbose) + throws IOException { + if (paramFile != null && paramFile.exists()) { + ProcessBuilder pb = + new ProcessBuilder(launcher, paramFile.getName()); + pb = pb.directory(paramFile.getParentFile()); + exec(pb, verbose); + } + } + + public static void exec(ProcessBuilder pb, boolean verbose) + throws IOException { + exec(pb, verbose, false); + } + + public static void exec(ProcessBuilder pb, boolean verbose, + boolean testForPresenseOnly) throws IOException { + exec(pb, verbose, testForPresenseOnly, null); + } + + public static void exec(ProcessBuilder pb, boolean verbose, + 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) { + } + } + + @SuppressWarnings("unchecked") + private static Process startProcess(Object... args) throws IOException { + final ArrayList argsList = new ArrayList<>(); + for (Object a : args) { + if (a instanceof List) { + argsList.addAll((List)a); + } else if (a instanceof String) { + argsList.add((String)a); + } + } + + return Runtime.getRuntime().exec( + argsList.toArray(new String[argsList.size()])); + } + + private static void logErrorStream(Process p) { + final BufferedReader err = + new BufferedReader(new InputStreamReader(p.getErrorStream())); + Thread t = new Thread(() -> { + try { + String line; + while ((line = err.readLine()) != null) { + Log.error(line); + } + } catch (IOException ioe) { + Log.verbose(ioe); + } + }); + t.setDaemon(true); + t.start(); + } + + public static int getProcessOutput(List result, Object... args) + throws IOException, InterruptedException { + final Process p = startProcess(args); + + List list = new ArrayList<>(); + final BufferedReader in = + new BufferedReader(new InputStreamReader(p.getInputStream())); + Thread t = new Thread(() -> { + try { + String line; + while ((line = in.readLine()) != null) { + list.add(line); + } + } catch (IOException ioe) { + jdk.jpackage.internal.Log.verbose(ioe); + } + }); + t.setDaemon(true); + t.start(); + + logErrorStream(p); + + int ret = p.waitFor(); + + result.clear(); + result.addAll(list); + + return ret; + } +} diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/share/classes/jdk/jpackage/internal/InvalidBundlerParamException.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/InvalidBundlerParamException.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2014, 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.internal; + +public class InvalidBundlerParamException extends RuntimeException { + private static final long serialVersionUID = 1L; + public InvalidBundlerParamException(String message) { + super(message); + } +} diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/share/classes/jdk/jpackage/internal/JLinkBundlerHelper.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/JLinkBundlerHelper.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,520 @@ +/* + * Copyright (c) 2015, 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.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.text.MessageFormat; +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.lang.module.ModuleDescriptor; +import java.lang.module.ModuleFinder; +import java.lang.module.ModuleReference; + +import jdk.tools.jlink.internal.packager.AppRuntimeImageBuilder; + +final class JLinkBundlerHelper { + + private static final ResourceBundle I18N = ResourceBundle.getBundle( + "jdk.jpackage.internal.resources.MainResources"); + private static final String JRE_MODULES_FILENAME = + "jdk/jpackage/internal/resources/jre.list"; + private static final String SERVER_JRE_MODULES_FILENAME = + "jdk/jpackage/internal/resources/jre.module.list"; + + private JLinkBundlerHelper() {} + + @SuppressWarnings("unchecked") + static final BundlerParamInfo JLINK_BUILDER = + new StandardBundlerParam<>( + I18N.getString("param.jlink-builder.name"), + I18N.getString("param.jlink-builder.description"), + "jlink.builder", + String.class, + null, + (s, p) -> s); + + @SuppressWarnings("unchecked") + static final BundlerParamInfo DEBUG = + new StandardBundlerParam<>( + "", + "", + "-J-Xdebug", + Integer.class, + p -> null, + (s, p) -> { + return Integer.valueOf(s); + }); + + static String listOfPathToString(List value) { + String result = ""; + + for (Path path : value) { + if (result.length() > 0) { + result += File.pathSeparator; + } + + result += path.toString(); + } + + return result; + } + + static String setOfStringToString(Set value) { + String result = ""; + + for (String element : value) { + if (result.length() > 0) { + result += ","; + } + + result += element; + } + + return result; + } + + static File getMainJar(Map 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 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 params) { + String result = ""; + String mainModule = StandardBundlerParam.MODULE.fetchFrom(params); + + if (mainModule != null) { + int index = mainModule.indexOf("/"); + + if (index > 0) { + result = mainModule.substring(0, index); + } + else { + result = mainModule; + } + } + + return result; + } + + static String getJDKVersion(Map params) { + String result = ""; + List modulePath = + StandardBundlerParam.MODULE_PATH.fetchFrom(params); + Set limitModules = + StandardBundlerParam.LIMIT_MODULES.fetchFrom(params); + Path javaBasePath = findPathOfModule(modulePath, "java.base.jmod"); + Set addModules = getValidModules(modulePath, + StandardBundlerParam.ADD_MODULES.fetchFrom(params), + limitModules, true); + + + if (javaBasePath != null && javaBasePath.toFile().exists()) { + result = getModuleVersion(javaBasePath.toFile(), + modulePath, addModules, limitModules); + } + + return result; + } + + static Path getJDKHome(Map params) { + Path result = null; + List modulePath = + StandardBundlerParam.MODULE_PATH.fetchFrom(params); + Path javaBasePath = findPathOfModule(modulePath, "java.base.jmod"); + + if (javaBasePath != null && javaBasePath.toFile().exists()) { + result = javaBasePath.getParent(); + + // 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. + if (result != null) { + boolean found = false; + Path bin = result.resolve("bin"); + + if (Files.exists(bin)) { + final String exe = + (Platform.getPlatform() == Platform.WINDOWS) ? + ".exe" : ""; + Path javaExe = bin.resolve("java" + exe); + + if (Files.exists(javaExe)) { + found = true; + } + } + + if (!found) { + result = result.resolve(".." + File.separator + "jdk"); + } + } + } + + return result; + } + + private static Set getValidModules(List modulePath, + Set addModules, Set limitModules, + boolean forJRE) { + ModuleHelper moduleHelper = new ModuleHelper( + modulePath, addModules, limitModules, forJRE); + return removeInvalidModules(modulePath, moduleHelper.modules()); + } + + static void execute(Map params, + AbstractAppImageBuilder imageBuilder) + throws IOException, Exception { + List modulePath = + StandardBundlerParam.MODULE_PATH.fetchFrom(params); + Set addModules = + StandardBundlerParam.ADD_MODULES.fetchFrom(params); + Set limitModules = + StandardBundlerParam.LIMIT_MODULES.fetchFrom(params); + boolean stripNativeCommands = + StandardBundlerParam.STRIP_NATIVE_COMMANDS.fetchFrom(params); + Path outputDir = imageBuilder.getRoot(); + String excludeFileList = imageBuilder.getExcludeFileList(); + 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; + } + + // Modules + + // The default for an unnamed jar is ALL_DEFAULT with the + // non-valid modules removed. + if (mainJarType == ModFile.ModType.UnnamedJar) { + addModules.add(ModuleHelper.ALL_RUNTIME); + } else if (mainJarType == ModFile.ModType.Unknown || + mainJarType == ModFile.ModType.ModularJar) { + String mainModule = getMainModule(params); + addModules.add(mainModule); + + // Error if any of the srcfiles are modular jars. + Set modularJars = + getResourceFileJarList(params, ModFile.JarType.ModularJar); + + if (!modularJars.isEmpty()) { + throw new Exception(MessageFormat.format(I18N.getString( + "error.srcfiles.contain.modules"), + modularJars.toString())); + } + } + addModules.addAll(getValidModules( + modulePath, addModules, limitModules, false)); + + Log.verbose(MessageFormat.format( + I18N.getString("message.modules"), addModules.toString())); + + AppRuntimeImageBuilder appRuntimeBuilder = new AppRuntimeImageBuilder(); + appRuntimeBuilder.setOutputDir(outputDir); + appRuntimeBuilder.setModulePath(modulePath); + appRuntimeBuilder.setAddModules(addModules); + appRuntimeBuilder.setLimitModules(limitModules); + appRuntimeBuilder.setExcludeFileList(excludeFileList); + appRuntimeBuilder.setStripNativeCommands(stripNativeCommands); + appRuntimeBuilder.setUserArguments(new HashMap()); + + appRuntimeBuilder.build(); + imageBuilder.prepareApplicationFiles(); + } + + static void generateJre(Map params, + AbstractAppImageBuilder imageBuilder) + throws IOException, Exception { + List modulePath = + StandardBundlerParam.MODULE_PATH.fetchFrom(params); + Set addModules = + StandardBundlerParam.ADD_MODULES.fetchFrom(params); + Set limitModules = + StandardBundlerParam.LIMIT_MODULES.fetchFrom(params); + boolean stripNativeCommands = + StandardBundlerParam.STRIP_NATIVE_COMMANDS.fetchFrom(params); + Path outputDir = imageBuilder.getRoot(); + addModules.add(ModuleHelper.ALL_RUNTIME); + Set redistModules = getValidModules(modulePath, + addModules, limitModules, true); + addModules.addAll(redistModules); + + Log.verbose(MessageFormat.format( + I18N.getString("message.modules"), addModules.toString())); + + AppRuntimeImageBuilder appRuntimeBuilder = new AppRuntimeImageBuilder(); + appRuntimeBuilder.setOutputDir(outputDir); + appRuntimeBuilder.setModulePath(modulePath); + appRuntimeBuilder.setAddModules(addModules); + appRuntimeBuilder.setLimitModules(limitModules); + appRuntimeBuilder.setStripNativeCommands(stripNativeCommands); + appRuntimeBuilder.setExcludeFileList(""); + appRuntimeBuilder.setUserArguments(new HashMap()); + + appRuntimeBuilder.build(); + imageBuilder.prepareJreFiles(); + } + + // Returns the path to the JDK modules in the user defined module path. + static Path findPathOfModule( + List modulePath, String moduleName) { + Path result = null; + + for (Path path : modulePath) { + Path moduleNamePath = path.resolve(moduleName); + + if (Files.exists(moduleNamePath)) { + result = path; + break; + } + } + + return result; + } + + private static Set getResourceFileJarList( + Map params, ModFile.JarType Query) { + Set files = new LinkedHashSet(); + + String srcdir = StandardBundlerParam.SOURCE_DIR.fetchFrom(params); + + for (RelativeFileSet appResources : + StandardBundlerParam.APP_RESOURCES_LIST.fetchFrom(params)) { + for (String resource : appResources.getIncludedFiles()) { + if (resource.endsWith(".jar")) { + String filename = srcdir + File.separator + resource; + + switch (Query) { + case All: { + files.add(filename); + break; + } + case ModularJar: { + ModFile mod = new ModFile(new File(filename)); + if (mod.getModType() == ModFile.ModType.ModularJar) { + files.add(filename); + } + break; + } + case UnnamedJar: { + ModFile mod = new ModFile(new File(filename)); + if (mod.getModType() == ModFile.ModType.UnnamedJar) { + files.add(filename); + } + break; + } + } + } + } + } + + return files; + } + + private static Set removeInvalidModules( + List modulePath, Set modules) { + Set result = new LinkedHashSet(); + ModuleManager mm = new ModuleManager(modulePath); + List lmodfiles = + mm.getModules(EnumSet.of(ModuleManager.SearchType.ModularJar, + ModuleManager.SearchType.Jmod, + ModuleManager.SearchType.ExplodedModule)); + + HashMap 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 String getModuleVersion(File moduleFile, + List modulePath, Set addModules, + Set limitModules) { + String result = ""; + + ModFile modFile = new ModFile(moduleFile); + ModuleFinder finder = AppRuntimeImageBuilder.moduleFinder(modulePath, + addModules, limitModules); + Optional mref = finder.find(modFile.getModName()); + + if (mref.isPresent()) { + ModuleDescriptor descriptor = mref.get().descriptor(); + + if (descriptor != null) { + Optional version = + descriptor.version(); + + if (version.isPresent()) { + result = version.get().toString(); + } + } + } + + 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_RUNTIME = "ALL-RUNTIME"; + + private final Set modules = new HashSet<>(); + private enum Macros {None, AllModulePath, AllRuntime} + + ModuleHelper(List paths, Set roots, + Set limitMods, boolean forJRE) { + Macros macro = Macros.None; + + for (Iterator iterator = roots.iterator(); + iterator.hasNext();) { + String module = iterator.next(); + + switch (module) { + case ALL_MODULE_PATH: + iterator.remove(); + macro = Macros.AllModulePath; + break; + case ALL_RUNTIME: + iterator.remove(); + macro = Macros.AllRuntime; + break; + default: + this.modules.add(module); + } + } + + switch (macro) { + case AllModulePath: + this.modules.addAll(getModuleNamesFromPath(paths)); + break; + case AllRuntime: + Set runtimeModules = + ModuleLayer.boot().modules(); + for (Module m : runtimeModules) { + String name = m.getName(); + if (forJRE && isModuleExcludedFromJRE(name)) { + continue; // JRE does not include this module + } + this.modules.add(name); + } + break; + } + } + + Set modules() { + return modules; + } + + private boolean isModuleExcludedFromJRE(String name) { + return false; // not excluding any modules from JRE at this time + } + + private static Set getModuleNamesFromPath(List Value) { + Set result = new LinkedHashSet(); + ModuleManager mm = new ModuleManager(Value); + List modFiles = + mm.getModules( + EnumSet.of(ModuleManager.SearchType.ModularJar, + ModuleManager.SearchType.Jmod, + ModuleManager.SearchType.ExplodedModule)); + + for (ModFile modFile : modFiles) { + result.add(modFile.getModName()); + } + + return result; + } + } +} diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/share/classes/jdk/jpackage/internal/JPackageToolProvider.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/JPackageToolProvider.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2017, 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.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 jdk.jpackage.main.Main.run(out, err, args); + } catch (Exception ignored) { + return -1; + } + } +} diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/share/classes/jdk/jpackage/internal/Log.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/Log.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,195 @@ +/* + * Copyright (c) 2011, 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.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; + } +} diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/share/classes/jdk/jpackage/internal/ModFile.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ModFile.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2016, 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.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; + + try { + for (ZipEntry entry = zip.getNextEntry(); entry != null; + entry = zip.getNextEntry()) { + if (entry.getName().matches("module-info.class")) { + result = JarType.ModularJar; + break; + } + } + + zip.close(); + } catch (IOException ex) { + } + } catch (FileNotFoundException e) { + } + + return result; + } + + private static String getFileWithoutExtension(String FileName) { + return FileName.replaceFirst("[.][^.]+$", ""); + } +} diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/share/classes/jdk/jpackage/internal/ModuleManager.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ModuleManager.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2016, 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.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 folders = new ArrayList(); + + enum SearchType {UnnamedJar, ModularJar, Jmod, ExplodedModule} + + ModuleManager(String folders) { + super(); + String lfolders = folders.replaceAll("^\"|\"$", ""); + List paths = new ArrayList(); + + for (String folder : + Arrays.asList(lfolders.split(File.pathSeparator))) { + File file = new File(folder); + paths.add(file.toPath()); + } + + initialize(paths); + } + + ModuleManager(List Paths) { + super(); + initialize(Paths); + } + + private void initialize(List Paths) { + for (Path path : Paths) { + folders.add(path.toString().replaceAll("^\"|\"$", "")); + } + } + + List getModules() { + return getModules(EnumSet.of(SearchType.UnnamedJar, + SearchType.ModularJar, SearchType.Jmod, + SearchType.ExplodedModule)); + } + + List getModules(EnumSet Search) { + List result = new ArrayList(); + + for (String folder : folders) { + result.addAll(getAllModulesInDirectory(folder, Search)); + } + + return result; + } + + private static List getAllModulesInDirectory(String Folder, + EnumSet Search) { + List result = new ArrayList(); + File lfolder = new File(Folder); + File[] files = lfolder.listFiles(); + + 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; + } +} diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/share/classes/jdk/jpackage/internal/PackagerException.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/PackagerException.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2011, 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.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); + } + +} diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/share/classes/jdk/jpackage/internal/Param.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/Param.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2011, 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.internal; + +class Param { + String name; + String value; + + void setName(String name) { + this.name = name; + } + + void setValue(String value) { + this.value = value; + } + +} diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/share/classes/jdk/jpackage/internal/Platform.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/Platform.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2016, 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.internal; + +import java.util.regex.Pattern; + +/** + * Platform + * + * Use Platform 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; + } +} diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/share/classes/jdk/jpackage/internal/RelativeFileSet.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/RelativeFileSet.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2012, 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.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 files = new LinkedHashSet<>(); + + RelativeFileSet(RelativeFileSet copy) { + basedir = copy.basedir; + files = new LinkedHashSet<>(copy.files); + } + + RelativeFileSet(File base, Collection 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)); + } + } + } + + void upshift() { + String root = basedir.getName(); + basedir = basedir.getParentFile(); + Set newFiles = new LinkedHashSet<>(); + for (String s : files) { + newFiles.add(root + File.separator + s); + } + files = newFiles; + } + + RelativeFileSet(File base, Set files) { + this(base, (Collection) files); + } + + boolean contains(String[] requiredFiles) { + boolean result = true; + + for(String fname: requiredFiles) { + if (!files.contains(fname)) { + Log.debug(" RelativeFileSet does not contain [" + fname + "]"); + result = false; + } + } + + return result; + } + + boolean contains(String requiredFile) { + if (files.contains(requiredFile)) { + return true; + } else { + Log.debug("RelativeFileSet does not contain [" +requiredFile+ "]"); + return false; + } + } + + File getBaseDirectory() { + return basedir; + } + + Set getIncludedFiles() { + return files; + } + + @Override + public String toString() { + return "RelativeFileSet {basedir:" + basedir + + ", files: {" + files + ")}"; + } + +} diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/share/classes/jdk/jpackage/internal/SecondaryLauncherArguments.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/SecondaryLauncherArguments.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,164 @@ +/* + * Copyright (c) 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.internal; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.io.File; +import jdk.jpackage.internal.Arguments.CLIOptions; + +/* + * SecondaryLauncherArguments + * + * Processes a secondary launcher properties file to create the Map of + * bundle params applicable to the secondary launcher: + * + * BundlerParams p = (new SecondaryLauncherArguments(file)).getLauncherMap(); + * + * A secondary launcher is another executable program generated by either the + * create-image mode or the create-installer mode. + * The secondary launcher may be the same program with different configuration, + * or a completely different program created from the same files. + * + * There may be multiple secondary launchers, each created by using the + * command line arg "--secondary-launcher + * + * The secondary launcher properties file may have any of: + * + * name (required) + * version + * module + * class + * icon + * arguments + * jvm-args + * win-menu + * win-shortcut + * win-console + * + */ +class SecondaryLauncherArguments { + + private final String filename; + private Map allArgs; + private Map bundleParams; + + SecondaryLauncherArguments(String filename) { + this.filename = filename; + } + + private void initLauncherMap() { + if (bundleParams != null) { + return; + } + + allArgs = Arguments.getPropertiesFromFile(filename); + + bundleParams = new HashMap<>(); + String mainClass = getOptionValue(CLIOptions.APPCLASS); + String module = getOptionValue(CLIOptions.MODULE); + + if (module != null && mainClass != null) { + putUnlessNull(bundleParams, Arguments.CLIOptions.MODULE.getId(), + module + "/" + mainClass); + } else if (module != null) { + putUnlessNull(bundleParams, Arguments.CLIOptions.MODULE.getId(), + module); + } else if (mainClass != null) { + putUnlessNull(bundleParams, Arguments.CLIOptions.APPCLASS.getId(), + mainClass); + } + + putUnlessNull(bundleParams, Arguments.CLIOptions.NAME.getId(), + getOptionValue(CLIOptions.NAME)); + putUnlessNull(bundleParams, Arguments.CLIOptions.VERSION.getId(), + getOptionValue(CLIOptions.VERSION)); + + putUnlessNull(bundleParams, Arguments.CLIOptions.WIN_MENU_HINT.getId(), + getOptionValue(CLIOptions.WIN_MENU_HINT)); + putUnlessNull(bundleParams, + Arguments.CLIOptions.WIN_SHORTCUT_HINT.getId(), + getOptionValue(CLIOptions.WIN_SHORTCUT_HINT)); + putUnlessNull(bundleParams, + Arguments.CLIOptions.WIN_CONSOLE_HINT.getId(), + getOptionValue(CLIOptions.WIN_CONSOLE_HINT)); + + String value = getOptionValue(CLIOptions.ICON); + putUnlessNull(bundleParams, Arguments.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.JVM_ARGS); + putUnlessNullOrEmpty(bundleParams, + CLIOptions.JVM_ARGS.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 getLauncherMap() { + initLauncherMap(); + return bundleParams; + } + + private void putUnlessNull(Map params, + String param, Object value) { + if (value != null) { + params.put(param, value); + } + } + + private void putUnlessNullOrEmpty(Map params, + String param, Collection value) { + if (value != null && !value.isEmpty()) { + params.put(param, value); + } + } + + private void putUnlessNullOrEmpty(Map params, + String param, Map value) { + if (value != null && !value.isEmpty()) { + params.put(param, value); + } + } +} diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/share/classes/jdk/jpackage/internal/StandardBundlerParam.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/StandardBundlerParam.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,903 @@ +/* + * Copyright (c) 2014, 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.internal; + +import jdk.jpackage.internal.BundleParams; +import jdk.jpackage.internal.AbstractAppImageBuilder; + +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; + +/** + * StandardBundlerParams + * + * 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 extends BundlerParamInfo { + + private static final ResourceBundle I18N = ResourceBundle.getBundle( + "jdk.jpackage.internal.resources.MainResources"); + private static final String JAVABASEJMOD = "java.base.jmod"; + + StandardBundlerParam(String name, String description, String id, + Class valueType, + Function, T> defaultValueFunction, + BiFunction, T> stringConverter) + { + this.name = name; + this.description = description; + this.id = id; + this.valueType = valueType; + this.defaultValueFunction = defaultValueFunction; + this.stringConverter = stringConverter; + } + + static final StandardBundlerParam APP_RESOURCES = + new StandardBundlerParam<>( + I18N.getString("param.app-resources.name"), + I18N.getString("param.app-resource.description"), + 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> APP_RESOURCES_LIST = + new StandardBundlerParam<>( + I18N.getString("param.app-resources-list.name"), + I18N.getString("param.app-resource-list.description"), + BundleParams.PARAM_APP_RESOURCES + "List", + (Class>) (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 SOURCE_DIR = + new StandardBundlerParam<>( + I18N.getString("param.source-dir.name"), + I18N.getString("param.source-dir.description"), + 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 MAIN_JAR = + new StandardBundlerParam<>( + I18N.getString("param.main-jar.name"), + I18N.getString("param.main-jar.description"), + Arguments.CLIOptions.MAIN_JAR.getId(), + RelativeFileSet.class, + params -> { + extractMainClassInfoFromAppResources(params); + return (RelativeFileSet) params.get("mainJar"); + }, + (s, p) -> getMainJar(s, p) + ); + + // TODO: test CLASSPATH jar manifest Attributet + static final StandardBundlerParam CLASSPATH = + new StandardBundlerParam<>( + I18N.getString("param.classpath.name"), + I18N.getString("param.classpath.description"), + "classpath", + String.class, + params -> { + extractMainClassInfoFromAppResources(params); + String cp = (String) params.get("classpath"); + return cp == null ? "" : cp; + }, + (s, p) -> s.replace(File.pathSeparator, " ") + ); + + static final StandardBundlerParam MAIN_CLASS = + new StandardBundlerParam<>( + I18N.getString("param.main-class.name"), + I18N.getString("param.main-class.description"), + Arguments.CLIOptions.APPCLASS.getId(), + String.class, + params -> { + if (Arguments.CREATE_JRE_INSTALLER.fetchFrom(params)) { + return null; + } + extractMainClassInfoFromAppResources(params); + String s = (String) params.get( + BundleParams.PARAM_APPLICATION_CLASS); + if (s == null) { + s = JLinkBundlerHelper.getMainClass(params); + } + return s; + }, + (s, p) -> s + ); + + static final StandardBundlerParam APP_NAME = + new StandardBundlerParam<>( + I18N.getString("param.app-name.name"), + I18N.getString("param.app-name.description"), + Arguments.CLIOptions.NAME.getId(), + String.class, + params -> { + String s = MAIN_CLASS.fetchFrom(params); + if (s == null) return null; + + int idx = s.lastIndexOf("."); + if (idx >= 0) { + return s.substring(idx+1); + } + return s; + }, + (s, p) -> s + ); + + private static Pattern TO_FS_NAME = Pattern.compile("\\s|[\\\\/?:*<>|]"); + // keep out invalid/undesireable filename characters + + static final StandardBundlerParam APP_FS_NAME = + new StandardBundlerParam<>( + I18N.getString("param.app-fs-name.name"), + I18N.getString("param.app-fs-name.description"), + "name.fs", + String.class, + params -> TO_FS_NAME.matcher( + APP_NAME.fetchFrom(params)).replaceAll(""), + (s, p) -> s + ); + + static final StandardBundlerParam ICON = + new StandardBundlerParam<>( + I18N.getString("param.icon-file.name"), + I18N.getString("param.icon-file.description"), + Arguments.CLIOptions.ICON.getId(), + File.class, + params -> null, + (s, p) -> new File(s) + ); + + static final StandardBundlerParam VENDOR = + new StandardBundlerParam<>( + I18N.getString("param.vendor.name"), + I18N.getString("param.vendor.description"), + Arguments.CLIOptions.VENDOR.getId(), + String.class, + params -> I18N.getString("param.vendor.default"), + (s, p) -> s + ); + + static final StandardBundlerParam CATEGORY = + new StandardBundlerParam<>( + I18N.getString("param.category.name"), + I18N.getString("param.category.description"), + Arguments.CLIOptions.CATEGORY.getId(), + String.class, + params -> I18N.getString("param.category.default"), + (s, p) -> s + ); + + static final StandardBundlerParam DESCRIPTION = + new StandardBundlerParam<>( + I18N.getString("param.description.name"), + I18N.getString("param.description.description"), + 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 COPYRIGHT = + new StandardBundlerParam<>( + I18N.getString("param.copyright.name"), + I18N.getString("param.copyright.description"), + Arguments.CLIOptions.COPYRIGHT.getId(), + String.class, + params -> MessageFormat.format(I18N.getString( + "param.copyright.default"), new Date()), + (s, p) -> s + ); + + @SuppressWarnings("unchecked") + static final StandardBundlerParam> ARGUMENTS = + new StandardBundlerParam<>( + I18N.getString("param.arguments.name"), + I18N.getString("param.arguments.description"), + Arguments.CLIOptions.ARGUMENTS.getId(), + (Class>) (Object) List.class, + params -> Collections.emptyList(), + (s, p) -> splitStringWithEscapes(s) + ); + + @SuppressWarnings("unchecked") + static final StandardBundlerParam> JVM_OPTIONS = + new StandardBundlerParam<>( + I18N.getString("param.jvm-options.name"), + I18N.getString("param.jvm-options.description"), + Arguments.CLIOptions.JVM_ARGS.getId(), + (Class>) (Object) List.class, + params -> Collections.emptyList(), + (s, p) -> Arrays.asList(s.split("\n\n")) + ); + + static final StandardBundlerParam TITLE = + new StandardBundlerParam<>( + I18N.getString("param.title.name"), + I18N.getString("param.title.description"), + BundleParams.PARAM_TITLE, + String.class, + APP_NAME::fetchFrom, + (s, p) -> s + ); + + // note that each bundler is likely to replace this one with + // their own converter + static final StandardBundlerParam VERSION = + new StandardBundlerParam<>( + I18N.getString("param.version.name"), + I18N.getString("param.version.description"), + Arguments.CLIOptions.VERSION.getId(), + String.class, + params -> I18N.getString("param.version.default"), + (s, p) -> s + ); + + @SuppressWarnings("unchecked") + public static final StandardBundlerParam LICENSE_FILE = + new StandardBundlerParam<>( + I18N.getString("param.license-file.name"), + I18N.getString("param.license-file.description"), + Arguments.CLIOptions.LICENSE_FILE.getId(), + String.class, + params -> null, + (s, p) -> s + ); + + static final StandardBundlerParam BUILD_ROOT = + new StandardBundlerParam<>( + I18N.getString("param.build-root.name"), + I18N.getString("param.build-root.description"), + Arguments.CLIOptions.BUILD_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 CONFIG_ROOT = + new StandardBundlerParam<>( + I18N.getString("param.config-root.name"), + I18N.getString("param.config-root.description"), + "configRoot", + File.class, + params -> { + File root = + new File(BUILD_ROOT.fetchFrom(params), "config"); + root.mkdirs(); + return root; + }, + (s, p) -> null + ); + + static final StandardBundlerParam IDENTIFIER = + new StandardBundlerParam<>( + I18N.getString("param.identifier.name"), + I18N.getString("param.identifier.description"), + 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 PREFERENCES_ID = + new StandardBundlerParam<>( + I18N.getString("param.preferences-id.name"), + I18N.getString("param.preferences-id.description"), + "preferencesID", + String.class, + p -> Optional.ofNullable(IDENTIFIER.fetchFrom(p)). + orElse("").replace('.', '/'), + (s, p) -> s + ); + + static final StandardBundlerParam VERBOSE = + new StandardBundlerParam<>( + I18N.getString("param.verbose.name"), + I18N.getString("param.verbose.description"), + 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 FORCE = + new StandardBundlerParam<>( + I18N.getString("param.force.name"), + I18N.getString("param.force.description"), + Arguments.CLIOptions.FORCE.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 RESOURCE_DIR = + new StandardBundlerParam<>( + I18N.getString("param.resource-dir.name"), + I18N.getString("param.resource-dir.description"), + Arguments.CLIOptions.RESOURCE_DIR.getId(), + File.class, + params -> null, + (s, p) -> new File(s) + ); + + static final BundlerParamInfo INSTALL_DIR = + new StandardBundlerParam<>( + I18N.getString("param.install-dir.name"), + I18N.getString("param.install-dir.description"), + Arguments.CLIOptions.INSTALL_DIR.getId(), + String.class, + params -> null, + (s, p) -> s + ); + + static final StandardBundlerParam PREDEFINED_APP_IMAGE = + new StandardBundlerParam<>( + I18N.getString("param.predefined-app-image.name"), + I18N.getString("param.predefined-app-image.description"), + Arguments.CLIOptions.PREDEFINED_APP_IMAGE.getId(), + File.class, + params -> null, + (s, p) -> new File(s)); + + static final StandardBundlerParam PREDEFINED_RUNTIME_IMAGE = + new StandardBundlerParam<>( + I18N.getString("param.predefined-runtime-image.name"), + I18N.getString("param.predefined-runtime-image.description"), + Arguments.CLIOptions.PREDEFINED_RUNTIME_IMAGE.getId(), + File.class, + params -> null, + (s, p) -> new File(s)); + + @SuppressWarnings("unchecked") + static final StandardBundlerParam>> SECONDARY_LAUNCHERS = + new StandardBundlerParam<>( + I18N.getString("param.secondary-launchers.name"), + I18N.getString("param.secondary-launchers.description"), + Arguments.CLIOptions.SECONDARY_LAUNCHER.getId(), + (Class>>) (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 + >> FILE_ASSOCIATIONS = + new StandardBundlerParam<>( + I18N.getString("param.file-associations.name"), + I18N.getString("param.file-associations.description"), + Arguments.CLIOptions.FILE_ASSOCIATIONS.getId(), + (Class>>) (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> FA_EXTENSIONS = + new StandardBundlerParam<>( + I18N.getString("param.fa-extension.name"), + I18N.getString("param.fa-extension.description"), + "fileAssociation.extension", + (Class>) (Object) List.class, + params -> null, // null means not matched to an extension + (s, p) -> Arrays.asList(s.split("(,|\\s)+")) + ); + + @SuppressWarnings("unchecked") + static final StandardBundlerParam> FA_CONTENT_TYPE = + new StandardBundlerParam<>( + I18N.getString("param.fa-content-type.name"), + I18N.getString("param.fa-content-type.description"), + "fileAssociation.contentType", + (Class>) (Object) List.class, + params -> null, + // null means not matched to a content/mime type + (s, p) -> Arrays.asList(s.split("(,|\\s)+")) + ); + + static final StandardBundlerParam FA_DESCRIPTION = + new StandardBundlerParam<>( + I18N.getString("param.fa-description.name"), + I18N.getString("param.fa-description.description"), + "fileAssociation.description", + String.class, + params -> APP_NAME.fetchFrom(params) + " File", + null + ); + + static final StandardBundlerParam FA_ICON = + new StandardBundlerParam<>( + I18N.getString("param.fa-icon.name"), + I18N.getString("param.fa-icon.description"), + "fileAssociation.icon", + File.class, + ICON::fetchFrom, + (s, p) -> new File(s) + ); + + @SuppressWarnings("unchecked") + static final BundlerParamInfo> MODULE_PATH = + new StandardBundlerParam<>( + I18N.getString("param.module-path.name"), + I18N.getString("param.module-path.description"), + Arguments.CLIOptions.MODULE_PATH.getId(), + (Class>) (Object)List.class, + p -> { return getDefaultModulePath(); }, + (s, p) -> { + List 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(); + } + + // Add the default JDK module path to the module path. + if (javaBasePath == null) { + List 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 MODULE = + new StandardBundlerParam<>( + I18N.getString("param.main.module.name"), + I18N.getString("param.main.module.description"), + Arguments.CLIOptions.MODULE.getId(), + String.class, + p -> null, + (s, p) -> { + return String.valueOf(s); + }); + + @SuppressWarnings("unchecked") + static final BundlerParamInfo> ADD_MODULES = + new StandardBundlerParam<>( + I18N.getString("param.add-modules.name"), + I18N.getString("param.add-modules.description"), + Arguments.CLIOptions.ADD_MODULES.getId(), + (Class>) (Object) Set.class, + p -> new LinkedHashSet(), + (s, p) -> new LinkedHashSet<>(Arrays.asList(s.split(","))) + ); + + @SuppressWarnings("unchecked") + static final BundlerParamInfo> LIMIT_MODULES = + new StandardBundlerParam<>( + I18N.getString("param.limit-modules.name"), + I18N.getString("param.limit-modules.description"), + Arguments.CLIOptions.LIMIT_MODULES.getId(), + (Class>) (Object) Set.class, + p -> new LinkedHashSet(), + (s, p) -> new LinkedHashSet<>(Arrays.asList(s.split(","))) + ); + + static final BundlerParamInfo STRIP_NATIVE_COMMANDS = + new StandardBundlerParam<>( + I18N.getString("param.strip-executables.name"), + I18N.getString("param.strip-executables.description"), + Arguments.CLIOptions.STRIP_NATIVE_COMMANDS.getId(), + Boolean.class, + p -> Boolean.FALSE, + (s, p) -> Boolean.valueOf(s) + ); + + static File getPredefinedAppImage(Map p) { + File applicationImage = null; + if (PREDEFINED_APP_IMAGE.fetchFrom(p) != null) { + applicationImage = PREDEFINED_APP_IMAGE.fetchFrom(p); + 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 p, + AbstractAppImageBuilder appBuilder) + throws IOException , ConfigException { + File image = PREDEFINED_RUNTIME_IMAGE.fetchFrom(p); + 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 excludes = Arrays.asList("jmods", "src.zip"); + IOUtils.copyRecursive(image.toPath(), appBuilder.getRoot(), excludes); + + // if module-path given - copy modules to appDir/mods + List modulePath = + StandardBundlerParam.MODULE_PATH.fetchFrom(p); + List 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 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()); + boolean jreInstaller = + params.containsKey(Arguments.CREATE_JRE_INSTALLER.getID()); + + if (hasMainClass && hasMainJar && hasMainJarClassPath || hasModule || + jreInstaller) { + return; + } + + // it's a pair. + // The [0] is the srcdir [1] is the file relative to sourcedir + List 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 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 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()); + boolean jreInstaller = + params.containsKey(Arguments.CREATE_JRE_INSTALLER.getID()); + + if (hasMainClass && hasMainJar && hasMainJarClassPath || + hasModule || jreInstaller || hasAppImage) { + 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 splitStringWithEscapes(String s) { + List l = new ArrayList<>(); + StringBuilder current = new StringBuilder(); + boolean quoted = false; + boolean escaped = false; + for (char c : s.toCharArray()) { + if (escaped) { + current.append(c); + } else if ('"' == c) { + quoted = !quoted; + } else if (!quoted && Character.isWhitespace(c)) { + l.add(current.toString()); + current = new StringBuilder(); + } else { + current.append(c); + } + } + l.add(current.toString()); + return l; + } + + private static List + createAppResourcesListFromString(String s, + Map objectObjectMap) { + List 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 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 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 only legal if + // path is within the appResourceRoot directory + try { + return new RelativeFileSet(appResourcesRoot, + new LinkedHashSet<>(Collections.singletonList( + mainJarFile))); + } catch (Exception e) { + // if not within, RelativeFileSet constructor throws a + // RuntimeException, but the IllegalArgumentException + // below contains a more explicit error message. + } + } else { + List 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 getDefaultModulePath() { + List result = new ArrayList(); + 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 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; + } +} diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/share/classes/jdk/jpackage/internal/UnsupportedPlatformException.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/UnsupportedPlatformException.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2012, 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.internal; + +public class UnsupportedPlatformException extends Exception { + private static final long serialVersionUID = 1L; +} diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/share/classes/jdk/jpackage/internal/ValidOptions.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ValidOptions.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,221 @@ +/* + * Copyright (c) 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.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 mode, CLIOptions arg) + * Determine if the given arg is valid in the given mode. + */ +class ValidOptions { + + private ValidOptions() {}; + + // multimap that contains pairs of (mode, supported args) + private static final Map> options = + new HashMap<>(); + + private static boolean argsInitialized = false; + + // initializing list of mandatory arguments + private static void initArgs() { + if (argsInitialized) { + return; + } + + // add options for CREATE_IMAGE + add(CLIOptions.CREATE_IMAGE, CLIOptions.INPUT); + add(CLIOptions.CREATE_IMAGE, CLIOptions.OUTPUT); + add(CLIOptions.CREATE_IMAGE, CLIOptions.APPCLASS); + add(CLIOptions.CREATE_IMAGE, CLIOptions.NAME); + add(CLIOptions.CREATE_IMAGE, CLIOptions.IDENTIFIER); + add(CLIOptions.CREATE_IMAGE, CLIOptions.VERBOSE); + add(CLIOptions.CREATE_IMAGE, CLIOptions.FORCE); + add(CLIOptions.CREATE_IMAGE, CLIOptions.FILES); + add(CLIOptions.CREATE_IMAGE, CLIOptions.ARGUMENTS); + add(CLIOptions.CREATE_IMAGE, CLIOptions.STRIP_NATIVE_COMMANDS); + add(CLIOptions.CREATE_IMAGE, CLIOptions.ICON); + add(CLIOptions.CREATE_IMAGE, CLIOptions.VERSION); + add(CLIOptions.CREATE_IMAGE, CLIOptions.JVM_ARGS); + add(CLIOptions.CREATE_IMAGE, CLIOptions.SECONDARY_LAUNCHER); + add(CLIOptions.CREATE_IMAGE, CLIOptions.BUILD_ROOT); + add(CLIOptions.CREATE_IMAGE, CLIOptions.PREDEFINED_RUNTIME_IMAGE); + add(CLIOptions.CREATE_IMAGE, CLIOptions.MAIN_JAR); + add(CLIOptions.CREATE_IMAGE, CLIOptions.MODULE); + add(CLIOptions.CREATE_IMAGE, CLIOptions.ADD_MODULES); + add(CLIOptions.CREATE_IMAGE, CLIOptions.MODULE_PATH); + add(CLIOptions.CREATE_IMAGE, CLIOptions.LIMIT_MODULES); + add(CLIOptions.CREATE_IMAGE, CLIOptions.RESOURCE_DIR); + + if (Platform.getPlatform() == Platform.MAC) { + add(CLIOptions.CREATE_IMAGE, CLIOptions.MAC_SIGN); + add(CLIOptions.CREATE_IMAGE, CLIOptions.MAC_BUNDLE_NAME); + add(CLIOptions.CREATE_IMAGE, CLIOptions.MAC_BUNDLE_IDENTIFIER); + add(CLIOptions.CREATE_IMAGE, CLIOptions.MAC_BUNDLE_SIGNING_PREFIX); + add(CLIOptions.CREATE_IMAGE, CLIOptions.MAC_SIGNING_KEY_NAME); + add(CLIOptions.CREATE_IMAGE, CLIOptions.MAC_SIGNING_KEYCHAIN); + add(CLIOptions.CREATE_IMAGE, CLIOptions.CATEGORY); + add(CLIOptions.CREATE_IMAGE, CLIOptions.COPYRIGHT); + } + + if (Platform.getPlatform() == Platform.WINDOWS) { + add(CLIOptions.CREATE_IMAGE, CLIOptions.DESCRIPTION); + add(CLIOptions.CREATE_IMAGE, CLIOptions.VENDOR); + add(CLIOptions.CREATE_IMAGE, CLIOptions.COPYRIGHT); + add(CLIOptions.CREATE_IMAGE, CLIOptions.WIN_CONSOLE_HINT); + } + + // add options for CREATE_INSTALLER + + // add all CREATE_IMAGE options for CREATE_JRE_INSTALLER + Set imageOptions = options.get(CLIOptions.CREATE_IMAGE); + imageOptions.forEach(o -> add(CLIOptions.CREATE_INSTALLER, o)); + + add(CLIOptions.CREATE_INSTALLER, CLIOptions.LICENSE_FILE); + add(CLIOptions.CREATE_INSTALLER, CLIOptions.FILE_ASSOCIATIONS); + add(CLIOptions.CREATE_INSTALLER, CLIOptions.INSTALL_DIR); + add(CLIOptions.CREATE_INSTALLER, CLIOptions.PREDEFINED_APP_IMAGE); + + if (Platform.getPlatform() == Platform.MAC) { + add(CLIOptions.CREATE_INSTALLER, CLIOptions.MAC_APP_STORE_CATEGORY); + add(CLIOptions.CREATE_INSTALLER, + CLIOptions.MAC_APP_STORE_ENTITLEMENTS); + } + + if (Platform.getPlatform() == Platform.LINUX) { + add(CLIOptions.CREATE_INSTALLER, CLIOptions.LINUX_BUNDLE_NAME); + add(CLIOptions.CREATE_INSTALLER, CLIOptions.LINUX_DEB_MAINTAINER); + add(CLIOptions.CREATE_INSTALLER, CLIOptions.LINUX_RPM_LICENSE_TYPE); + add(CLIOptions.CREATE_INSTALLER, + CLIOptions.LINUX_PACKAGE_DEPENDENCIES); + add(CLIOptions.CREATE_INSTALLER, CLIOptions.DESCRIPTION); + add(CLIOptions.CREATE_INSTALLER, CLIOptions.VENDOR); + add(CLIOptions.CREATE_INSTALLER, CLIOptions.CATEGORY); + add(CLIOptions.CREATE_INSTALLER, CLIOptions.COPYRIGHT); + } + + if (Platform.getPlatform() == Platform.WINDOWS) { + add(CLIOptions.CREATE_INSTALLER, CLIOptions.WIN_MENU_HINT); + add(CLIOptions.CREATE_INSTALLER, CLIOptions.WIN_MENU_GROUP); + add(CLIOptions.CREATE_INSTALLER, CLIOptions.WIN_SHORTCUT_HINT); + add(CLIOptions.CREATE_INSTALLER, + CLIOptions.WIN_PER_USER_INSTALLATION); + add(CLIOptions.CREATE_INSTALLER, CLIOptions.WIN_DIR_CHOOSER); + add(CLIOptions.CREATE_INSTALLER, CLIOptions.WIN_REGISTRY_NAME); + add(CLIOptions.CREATE_INSTALLER, CLIOptions.WIN_UPGRADE_UUID); + add(CLIOptions.CREATE_INSTALLER, CLIOptions.CATEGORY); + add(CLIOptions.CREATE_INSTALLER, CLIOptions.WIN_CONSOLE_HINT); + } + + // add options for CREATE_JRE_INSTALLER + + add(CLIOptions.CREATE_JRE_INSTALLER, CLIOptions.INPUT); + add(CLIOptions.CREATE_JRE_INSTALLER, CLIOptions.OUTPUT); + add(CLIOptions.CREATE_JRE_INSTALLER, CLIOptions.NAME); + add(CLIOptions.CREATE_JRE_INSTALLER, CLIOptions.VERBOSE); + add(CLIOptions.CREATE_JRE_INSTALLER, CLIOptions.FILES); + add(CLIOptions.CREATE_JRE_INSTALLER, CLIOptions.STRIP_NATIVE_COMMANDS); + add(CLIOptions.CREATE_JRE_INSTALLER, CLIOptions.LICENSE_FILE); + add(CLIOptions.CREATE_JRE_INSTALLER, CLIOptions.VERSION); + add(CLIOptions.CREATE_JRE_INSTALLER, CLIOptions.BUILD_ROOT); + add(CLIOptions.CREATE_JRE_INSTALLER, CLIOptions.INSTALL_DIR); + add(CLIOptions.CREATE_JRE_INSTALLER, + CLIOptions.PREDEFINED_RUNTIME_IMAGE); + add(CLIOptions.CREATE_JRE_INSTALLER, CLIOptions.ADD_MODULES); + add(CLIOptions.CREATE_JRE_INSTALLER, CLIOptions.MODULE_PATH); + add(CLIOptions.CREATE_JRE_INSTALLER, CLIOptions.LIMIT_MODULES); + + if (Platform.getPlatform() == Platform.MAC) { + add(CLIOptions.CREATE_JRE_INSTALLER, CLIOptions.MAC_SIGN); + add(CLIOptions.CREATE_JRE_INSTALLER, CLIOptions.MAC_BUNDLE_NAME); + add(CLIOptions.CREATE_JRE_INSTALLER, + CLIOptions.MAC_BUNDLE_IDENTIFIER); + add(CLIOptions.CREATE_JRE_INSTALLER, + CLIOptions.MAC_BUNDLE_SIGNING_PREFIX); + add(CLIOptions.CREATE_JRE_INSTALLER, + CLIOptions.MAC_SIGNING_KEY_NAME); + add(CLIOptions.CREATE_JRE_INSTALLER, + CLIOptions.MAC_SIGNING_KEYCHAIN); + } + + if (Platform.getPlatform() == Platform.WINDOWS) { + add(CLIOptions.CREATE_JRE_INSTALLER, + CLIOptions.WIN_PER_USER_INSTALLATION); + add(CLIOptions.CREATE_JRE_INSTALLER, CLIOptions.WIN_DIR_CHOOSER); + add(CLIOptions.CREATE_JRE_INSTALLER, + CLIOptions.WIN_UPGRADE_UUID); + add(CLIOptions.CREATE_JRE_INSTALLER, CLIOptions.DESCRIPTION); + add(CLIOptions.CREATE_JRE_INSTALLER, CLIOptions.VENDOR); + } + + if (Platform.getPlatform() == Platform.LINUX) { + add(CLIOptions.CREATE_JRE_INSTALLER, CLIOptions.LINUX_BUNDLE_NAME); + add(CLIOptions.CREATE_JRE_INSTALLER, + CLIOptions.LINUX_DEB_MAINTAINER); + add(CLIOptions.CREATE_JRE_INSTALLER, + CLIOptions.LINUX_PACKAGE_DEPENDENCIES); + add(CLIOptions.CREATE_JRE_INSTALLER, CLIOptions.DESCRIPTION); + add(CLIOptions.CREATE_JRE_INSTALLER, CLIOptions.VENDOR); + add(CLIOptions.CREATE_JRE_INSTALLER, + CLIOptions.LINUX_RPM_LICENSE_TYPE); + } + + argsInitialized = true; + } + + static void add(CLIOptions mode, CLIOptions arg) { + if (mode.equals(arg)) { + return; + } + options.computeIfAbsent(mode, + k -> new HashSet<>()).add(arg); + } + + static boolean checkIfSupported(CLIOptions mode, CLIOptions arg) { + if (mode.equals(arg)) { + return true; + } + + initArgs(); + Set set = options.get(mode); + if (set != null) { + return set.contains(arg); + } + return false; + } +} diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/HelpResources.properties --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/HelpResources.properties Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,240 @@ +# +# Copyright (c) 2017, 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. +# +# + +MSG_Help_common=Usage: jpackage \n\ +\n\ +where mode is one of: \n\ +\ create-image\n\ +\ Generates a platform-specific application image.\n\ +\ create-installer \n\ +\ Generates a platform-specific installer for the application.\n\ +\ Valid values for "type" are "msi", "exe", "rpm", "deb", "dmg",\n\ +\ "pkg", and "pkg-app-store".\n\ +\ If "type" is omitted, all supported types of installable\n\ +\ packages for the current platform will be generated.\n\ +\ create-jre-installer \n\ +\ Generates a platform-specific installer for JRE.\n\ +\ Valid values for "type" are "msi", "exe", "rpm", "deb", "dmg", \n\ +\ and "pkg".\n\ +\ If "type" is omitted, all supported types of installable\n\ +\ packages for the current platform will be generated.\n\ +\ \n\ +Sample usages:\n\ +--------------\n\ +\ Generate an application image from a non-modular jar file:\n\ +\ jpackage create-image --input inputdir --output outputdir \\\n\ +\ --name AppName --class package.ClassName --main-jar MyJar.jar\n\ +\ jpackage create-image -i inputdir -o outputdir -n AppName \\\n\ +\ -c package.ClassName -j MyJar.jar\n\ +\ Generate an application image from a modular jar file:\n\ +\ jpackage create-image --output outputdir --name AppName \\\n\ +\ --class package.ClassName -module moduleName -p modulePath\n\ +\ jpackage create-image --o outputdir -n AppName \\\n\ +\ -c package.ClassName -m moduleName -p modulePath\n\ +\ Generate an application installer:\n\ +\ jpackage create-installer -i inputdir -o outputdir \\\n\ +\ -n "Installer Name" -c package.ClassName -j MyJar.jar\n\ +\ jpackage create-installer -o outputdir -n "Installer Name" \\\n\ + --app-image \n\ +\ Generate a JRE installer:\n\ +\ jpackage create-jre-installer -name -output outputdir\n\ +\ jpackage create-jre-installer -n -o outputdir \\\n\ +\ --runtime-image \n\ +\n\ +The following options are valid for all platforms:\n\ +\ @ \n\ +\ Read options and/or mode from a file \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\ +\ --version -v\n\ +\ Print the product version to the output stream and exit\n\ +\ --output -o \n\ +\ Path where generated output file is placed\n\ +\ (absolute path or relative to the current directory)\n\ +\ --input -i \n\ +\ Path of the input directory that contains the files to package\n\ +\ (absolute path or relative to the current directory)\n\ +\ --files -f \n\ +\ A {0} separated list of files in the input dir to be packaged\n\ +\ If omitted, all files in the input directory will be packaged.\n\ +\ --name -n \n\ +\ Name of the application\n\ +\ --main-jar -j
\n\ +\ The main JAR of the application\n\ +\ (specified as a path relative to the input path)\n\ +\ This JAR should contain the main-class.\n\ +\ --class -c \n\ +\ Qualified name of the application class to execute\n\ +\ --app-version \n\ +\ Version of the application\n\ +\ --arguments -a
\n\ +\ Command line arguments to pass to the main class if no\n\ +\ arguments are specified by the launcher\n\ +\ --icon \n\ +\ Path of the icon of the application bundle\n\ +\ (absolute path or relative to the current directory)\n\ +\ --identifier \n\ +\ Machine readable identifier of the application\n\ +\ The format must be a DNS name in reverse order,\n\ +\ such as com.example.myapplication.\n\ +\ --verbose\n\ +\ Enables verbose output\n\ +\ --strip-native-commands\n\ +\ Removes native executables from the custom run-time images\n\ +\ --jvm-args \n\ +\ JVM flags and options to pass to the application\n\ +\ --file-associations \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\ +\ --secondary-launcher \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 "name" (required), "version", "module", "class",\n\ +\ "icon", "arguments", "jvm-args", "win-menu",\n\ +\ "win-shortcut", and "win-console" can be used to describe\n\ +\ the secondary launcher.\n\ +\ --build-root \n\ +\ Path of a new or empty directory used to create temporary files\n\ +\ (absolute path or relative to the current directory)\n\ +\ If not specified, a temporary directory will be created and\n\ +\ removed upon the task completion.\n\ +\ --runtime-image \n\ +\ Path of the predefined runtime image that is used to build\n\ +\ an application image and installable package\n\ +\ (absolute path or relative to the current directory)\n\ +\ --app-image \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\ +\ --install-dir \n\ +\ Absolute path of the installation directory of the application\n\ +\ This option is ignored on Windows, use --win-dir-chooser to\n\ +\ provide user the ability to choose the installation directory.\n\ +\ --license-file \n\ +\ Path to the license file\n\ +\ (absolute path or relative to the current directory)\n\ +\ --copyright \n\ +\ Copyright for the application\n\ +\ --description \n\ +\ Description of the application\n\ +\ --category \n\ +\ Category or group of the application\n\ +\ --vendor \n\ +\ Vendor of the application\n\ +\ --force\n\ +\ Allow the deletion of any existing output root directory\n\ +\ when creating an Application image\n\ +\ --resource-dir \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\ +\n\ +Modular options:\n\ +\ --module -m \n\ +\ Main module of the application\n\ +\ This module must contain the main-class,\n\ +\ and be located on the module path.\n\ +\ --module-path -p \n\ +\ Path JLink looks in for modules when packaging the Java Runtime\n\ +\ (absolute path or relative to the current directory)\n\ +\ --add-modules \n\ +\ A {0} separated list of modules to add to JImage creation,\n\ +\ including possible services\n\ +\ --limit-modules \n\ +\ A {0} separated list of Modules to limit JImage creation to\n\ + +MSG_Help_mac=\nThe following options are valid for Mac OS X platforms:\n\ +\ --mac-sign\n\ +\ Request that the bundle be signed\n\ +\ --mac-bundle-name \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-identifier \n\ +\ An identifier that uniquely identifies the application for MacOSX\n\ +\ (and on the Mac App Store)\n\ +\ May only use alphanumeric (A-Z,a-z,0-9), hyphen (-),\n\ +\ and period (.) characters.\n\ +\ --mac-app-store-category \n\ +\ Mac App Store Categories\n\ +\ Note that the key is the string shown to\n\ +\ the user and the value is the ID of the category.\n\ +\ --mac-app-store-entitlements \n\ +\ Path of a custom mac app store entitlements file\n\ +\ (absolute path or relative to the current directory)\n\ +\ --mac-bundle-signing-prefix \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-signing-key-user-name \n\ +\ User name portion of the typical\n\ +\ "Mac Developer ID Application: " signing key\n\ +\ --mac-signing-keychain \n\ +\ Path of the keychain to use\n\ +\ (absolute path or relative to the current directory)\n\ +\ If not specified, the standard keychains are used.\n\ + +MSG_Help_linux=\nThe following options are valid for Linux platforms:\n\ +\ --linux-bundle-name \n\ +\ Name for Linux bundle, defaults to the application name\n\ +\ --linux-package-deps\n\ +\ Required packages or capabilities for the application\n\ +\ --linux-rpm-license-type \n\ +\ Type of the license ("License: " of the RPM .spec)\n\ +\ --linux-deb-maintainer \n\ +\ Maintainer for .deb bundle\n\ + +MSG_Help_win=\nThe following options are valid for Windows platforms:\n\ +\ --win-menu\n\ +\ Adds the application to the system menu\n\ +\ --win-menu-group \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-dir-chooser\n\ +\ Adds a dialog to enable the user to choose a directory in which\n\ +\ the product is installed\n\ +\ --win-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-upgrade-uuid \n\ +\ UUID associated with upgrades for this package\n\ +\ --win-shortcut\n\ +\ Creates a desktop shortcut for the application\n\ +\ --win-console\n\ +\ Creates a console launcher for the application, should be\n\ +\ specified for application which requires console interactions\n\ + +MSG_Help_no_args=Usage: jpackage \n\ +\Use jpackage --help (or -h) for a list of possible options\ + diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/HelpResources_ja.properties --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/HelpResources_ja.properties Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,240 @@ +# +# Copyright (c) 2017, 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. +# +# + +MSG_Help_common=Usage: jpackage \n\ +\n\ +where mode is one of: \n\ +\ create-image\n\ +\ Generates a platform-specific application image.\n\ +\ create-installer \n\ +\ Generates a platform-specific installer for the application.\n\ +\ Valid values for "type" are "msi", "exe", "rpm", "deb", "dmg",\n\ +\ "pkg", and "pkg-app-store".\n\ +\ If "type" is omitted, all supported types of installable\n\ +\ packages for the current platform will be generated.\n\ +\ create-jre-installer \n\ +\ Generates a platform-specific installer for JRE.\n\ +\ Valid values for "type" are "msi", "exe", "rpm", "deb", "dmg", \n\ +\ and "pkg".\n\ +\ If "type" is omitted, all supported types of installable\n\ +\ packages for the current platform will be generated.\n\ +\ \n\ +Sample usages:\n\ +--------------\n\ +\ Generate an application image from a non-modular jar file:\n\ +\ jpackage create-image --input inputdir --output outputdir \\\n\ +\ --name AppName --class package.ClassName --main-jar MyJar.jar\n\ +\ jpackage create-image -i inputdir -o outputdir -n AppName \\\n\ +\ -c package.ClassName -j MyJar.jar\n\ +\ Generate an application image from a modular jar file:\n\ +\ jpackage create-image --output outputdir --name AppName \\\n\ +\ --class package.ClassName -module moduleName -p modulePath\n\ +\ jpackage create-image --o outputdir -n AppName \\\n\ +\ -c package.ClassName -m moduleName -p modulePath\n\ +\ Generate an application installer:\n\ +\ jpackage create-installer -i inputdir -o outputdir \\\n\ +\ -n "Installer Name" -c package.ClassName -j MyJar.jar\n\ +\ jpackage create-installer -o outputdir -n "Installer Name" \\\n\ + --app-image \n\ +\ Generate a JRE installer:\n\ +\ jpackage create-jre-installer -name -output outputdir\n\ +\ jpackage create-jre-installer -n -o outputdir \\\n\ +\ --runtime-image \n\ +\n\ +The following options are valid for all platforms:\n\ +\ @ \n\ +\ Read options and/or mode from a file \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\ +\ --version -v\n\ +\ Print the product version to the output stream and exit\n\ +\ --output -o \n\ +\ Path where generated output file is placed\n\ +\ (absolute path or relative to the current directory)\n\ +\ --input -i \n\ +\ Path of the input directory that contains the files to package\n\ +\ (absolute path or relative to the current directory)\n\ +\ --files -f \n\ +\ A {0} separated list of files in the input dir to be packaged\n\ +\ If omitted, all files in the input directory will be packaged.\n\ +\ --name -n \n\ +\ Name of the application\n\ +\ --main-jar -j
\n\ +\ The main JAR of the application\n\ +\ (specified as a path relative to the input path)\n\ +\ This JAR should contain the main-class.\n\ +\ --class -c \n\ +\ Qualified name of the application class to execute\n\ +\ --app-version \n\ +\ Version of the application\n\ +\ --arguments -a
\n\ +\ Command line arguments to pass to the main class if no\n\ +\ arguments are specified by the launcher\n\ +\ --icon \n\ +\ Path of the icon of the application bundle\n\ +\ (absolute path or relative to the current directory)\n\ +\ --identifier \n\ +\ Machine readable identifier of the application\n\ +\ The format must be a DNS name in reverse order,\n\ +\ such as com.example.myapplication.\n\ +\ --verbose\n\ +\ Enables verbose output\n\ +\ --strip-native-commands\n\ +\ Removes native executables from the custom run-time images\n\ +\ --jvm-args \n\ +\ JVM flags and options to pass to the application\n\ +\ --file-associations \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\ +\ --secondary-launcher \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 "name" (required), "version", "module", "class",\n\ +\ "icon", "arguments", "jvm-args", "win-menu",\n\ +\ "win-shortcut", and "win-console" can be used to describe\n\ +\ the secondary launcher.\n\ +\ --build-root \n\ +\ Path of a new or empty directory used to create temporary files\n\ +\ (absolute path or relative to the current directory)\n\ +\ If not specified, a temporary directory will be created and\n\ +\ removed upon the task completion.\n\ +\ --runtime-image \n\ +\ Path of the predefined runtime image that is used to build\n\ +\ an application image and installable package\n\ +\ (absolute path or relative to the current directory)\n\ +\ --app-image \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\ +\ --install-dir \n\ +\ Absolute path of the installation directory of the application\n\ +\ This option is ignored on Windows, use --win-dir-chooser to\n\ +\ provide user the ability to choose the installation directory.\n\ +\ --license-file \n\ +\ Path to the license file\n\ +\ (absolute path or relative to the current directory)\n\ +\ --copyright \n\ +\ Copyright for the application\n\ +\ --description \n\ +\ Description of the application\n\ +\ --category \n\ +\ Category or group of the application\n\ +\ --vendor \n\ +\ Vendor of the application\n\ +\ --force\n\ +\ Allow the deletion of any existing output root directory\n\ +\ when creating an Application image\n\ +\ --resource-dir \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\ +\n\ +Modular options:\n\ +\ --module -m \n\ +\ Main module of the application\n\ +\ This module must contain the main-class,\n\ +\ and be located on the module path.\n\ +\ --module-path -p \n\ +\ Path JLink looks in for modules when packaging the Java Runtime\n\ +\ (absolute path or relative to the current directory)\n\ +\ --add-modules \n\ +\ A {0} separated list of modules to add to JImage creation,\n\ +\ including possible services\n\ +\ --limit-modules \n\ +\ A {0} separated list of Modules to limit JImage creation to\n\ + +MSG_Help_mac=\nThe following options are valid for Mac OS X platforms:\n\ +\ --mac-sign\n\ +\ Request that the bundle be signed\n\ +\ --mac-bundle-name \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-identifier \n\ +\ An identifier that uniquely identifies the application for MacOSX\n\ +\ (and on the Mac App Store)\n\ +\ May only use alphanumeric (A-Z,a-z,0-9), hyphen (-),\n\ +\ and period (.) characters.\n\ +\ --mac-app-store-category \n\ +\ Mac App Store Categories\n\ +\ Note that the key is the string shown to\n\ +\ the user and the value is the ID of the category.\n\ +\ --mac-app-store-entitlements \n\ +\ Path of a custom mac app store entitlements file\n\ +\ (absolute path or relative to the current directory)\n\ +\ --mac-bundle-signing-prefix \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-signing-key-user-name \n\ +\ User name portion of the typical\n\ +\ "Mac Developer ID Application: " signing key\n\ +\ --mac-signing-keychain \n\ +\ Path of the keychain to use\n\ +\ (absolute path or relative to the current directory)\n\ +\ If not specified, the standard keychains are used.\n\ + +MSG_Help_linux=\nThe following options are valid for Linux platforms:\n\ +\ --linux-bundle-name \n\ +\ Name for Linux bundle, defaults to the application name\n\ +\ --linux-package-deps\n\ +\ Required packages or capabilities for the application\n\ +\ --linux-rpm-license-type \n\ +\ Type of the license ("License: " of the RPM .spec)\n\ +\ --linux-deb-maintainer \n\ +\ Maintainer for .deb bundle\n\ + +MSG_Help_win=\nThe following options are valid for Windows platforms:\n\ +\ --win-menu\n\ +\ Adds the application to the system menu\n\ +\ --win-menu-group \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-dir-chooser\n\ +\ Adds a dialog to enable the user to choose a directory in which\n\ +\ the product is installed\n\ +\ --win-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-upgrade-uuid \n\ +\ UUID associated with upgrades for this package\n\ +\ --win-shortcut\n\ +\ Creates a desktop shortcut for the application\n\ +\ --win-console\n\ +\ Creates a console launcher for the application, should be\n\ +\ specified for application which requires console interactions\n\ + +MSG_Help_no_args=Usage: jpackage \n\ +\Use jpackage --help (or -h) for a list of possible options\ + diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/HelpResources_zh_CN.properties --- /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 Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,240 @@ +# +# Copyright (c) 2017, 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. +# +# + +MSG_Help_common=Usage: jpackage \n\ +\n\ +where mode is one of: \n\ +\ create-image\n\ +\ Generates a platform-specific application image.\n\ +\ create-installer \n\ +\ Generates a platform-specific installer for the application.\n\ +\ Valid values for "type" are "msi", "exe", "rpm", "deb", "dmg",\n\ +\ "pkg", and "pkg-app-store".\n\ +\ If "type" is omitted, all supported types of installable\n\ +\ packages for the current platform will be generated.\n\ +\ create-jre-installer \n\ +\ Generates a platform-specific installer for JRE.\n\ +\ Valid values for "type" are "msi", "exe", "rpm", "deb", "dmg", \n\ +\ and "pkg".\n\ +\ If "type" is omitted, all supported types of installable\n\ +\ packages for the current platform will be generated.\n\ +\ \n\ +Sample usages:\n\ +--------------\n\ +\ Generate an application image from a non-modular jar file:\n\ +\ jpackage create-image --input inputdir --output outputdir \\\n\ +\ --name AppName --class package.ClassName --main-jar MyJar.jar\n\ +\ jpackage create-image -i inputdir -o outputdir -n AppName \\\n\ +\ -c package.ClassName -j MyJar.jar\n\ +\ Generate an application image from a modular jar file:\n\ +\ jpackage create-image --output outputdir --name AppName \\\n\ +\ --class package.ClassName -module moduleName -p modulePath\n\ +\ jpackage create-image --o outputdir -n AppName \\\n\ +\ -c package.ClassName -m moduleName -p modulePath\n\ +\ Generate an application installer:\n\ +\ jpackage create-installer -i inputdir -o outputdir \\\n\ +\ -n "Installer Name" -c package.ClassName -j MyJar.jar\n\ +\ jpackage create-installer -o outputdir -n "Installer Name" \\\n\ + --app-image \n\ +\ Generate a JRE installer:\n\ +\ jpackage create-jre-installer -name -output outputdir\n\ +\ jpackage create-jre-installer -n -o outputdir \\\n\ +\ --runtime-image \n\ +\n\ +The following options are valid for all platforms:\n\ +\ @ \n\ +\ Read options and/or mode from a file \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\ +\ --version -v\n\ +\ Print the product version to the output stream and exit\n\ +\ --output -o \n\ +\ Path where generated output file is placed\n\ +\ (absolute path or relative to the current directory)\n\ +\ --input -i \n\ +\ Path of the input directory that contains the files to package\n\ +\ (absolute path or relative to the current directory)\n\ +\ --files -f \n\ +\ A {0} separated list of files in the input dir to be packaged\n\ +\ If omitted, all files in the input directory will be packaged.\n\ +\ --name -n \n\ +\ Name of the application\n\ +\ --main-jar -j
\n\ +\ The main JAR of the application\n\ +\ (specified as a path relative to the input path)\n\ +\ This JAR should contain the main-class.\n\ +\ --class -c \n\ +\ Qualified name of the application class to execute\n\ +\ --app-version \n\ +\ Version of the application\n\ +\ --arguments -a
\n\ +\ Command line arguments to pass to the main class if no\n\ +\ arguments are specified by the launcher\n\ +\ --icon \n\ +\ Path of the icon of the application bundle\n\ +\ (absolute path or relative to the current directory)\n\ +\ --identifier \n\ +\ Machine readable identifier of the application\n\ +\ The format must be a DNS name in reverse order,\n\ +\ such as com.example.myapplication.\n\ +\ --verbose\n\ +\ Enables verbose output\n\ +\ --strip-native-commands\n\ +\ Removes native executables from the custom run-time images\n\ +\ --jvm-args \n\ +\ JVM flags and options to pass to the application\n\ +\ --file-associations \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\ +\ --secondary-launcher \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 "name" (required), "version", "module", "class",\n\ +\ "icon", "arguments", "jvm-args", "win-menu",\n\ +\ "win-shortcut", and "win-console" can be used to describe\n\ +\ the secondary launcher.\n\ +\ --build-root \n\ +\ Path of a new or empty directory used to create temporary files\n\ +\ (absolute path or relative to the current directory)\n\ +\ If not specified, a temporary directory will be created and\n\ +\ removed upon the task completion.\n\ +\ --runtime-image \n\ +\ Path of the predefined runtime image that is used to build\n\ +\ an application image and installable package\n\ +\ (absolute path or relative to the current directory)\n\ +\ --app-image \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\ +\ --install-dir \n\ +\ Absolute path of the installation directory of the application\n\ +\ This option is ignored on Windows, use --win-dir-chooser to\n\ +\ provide user the ability to choose the installation directory.\n\ +\ --license-file \n\ +\ Path to the license file\n\ +\ (absolute path or relative to the current directory)\n\ +\ --copyright \n\ +\ Copyright for the application\n\ +\ --description \n\ +\ Description of the application\n\ +\ --category \n\ +\ Category or group of the application\n\ +\ --vendor \n\ +\ Vendor of the application\n\ +\ --force\n\ +\ Allow the deletion of any existing output root directory\n\ +\ when creating an Application image\n\ +\ --resource-dir \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\ +\n\ +Modular options:\n\ +\ --module -m \n\ +\ Main module of the application\n\ +\ This module must contain the main-class,\n\ +\ and be located on the module path.\n\ +\ --module-path -p \n\ +\ Path JLink looks in for modules when packaging the Java Runtime\n\ +\ (absolute path or relative to the current directory)\n\ +\ --add-modules \n\ +\ A {0} separated list of modules to add to JImage creation,\n\ +\ including possible services\n\ +\ --limit-modules \n\ +\ A {0} separated list of Modules to limit JImage creation to\n\ + +MSG_Help_mac=\nThe following options are valid for Mac OS X platforms:\n\ +\ --mac-sign\n\ +\ Request that the bundle be signed\n\ +\ --mac-bundle-name \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-identifier \n\ +\ An identifier that uniquely identifies the application for MacOSX\n\ +\ (and on the Mac App Store)\n\ +\ May only use alphanumeric (A-Z,a-z,0-9), hyphen (-),\n\ +\ and period (.) characters.\n\ +\ --mac-app-store-category \n\ +\ Mac App Store Categories\n\ +\ Note that the key is the string shown to\n\ +\ the user and the value is the ID of the category.\n\ +\ --mac-app-store-entitlements \n\ +\ Path of a custom mac app store entitlements file\n\ +\ (absolute path or relative to the current directory)\n\ +\ --mac-bundle-signing-prefix \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-signing-key-user-name \n\ +\ User name portion of the typical\n\ +\ "Mac Developer ID Application: " signing key\n\ +\ --mac-signing-keychain \n\ +\ Path of the keychain to use\n\ +\ (absolute path or relative to the current directory)\n\ +\ If not specified, the standard keychains are used.\n\ + +MSG_Help_linux=\nThe following options are valid for Linux platforms:\n\ +\ --linux-bundle-name \n\ +\ Name for Linux bundle, defaults to the application name\n\ +\ --linux-package-deps\n\ +\ Required packages or capabilities for the application\n\ +\ --linux-rpm-license-type \n\ +\ Type of the license ("License: " of the RPM .spec)\n\ +\ --linux-deb-maintainer \n\ +\ Maintainer for .deb bundle\n\ + +MSG_Help_win=\nThe following options are valid for Windows platforms:\n\ +\ --win-menu\n\ +\ Adds the application to the system menu\n\ +\ --win-menu-group \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-dir-chooser\n\ +\ Adds a dialog to enable the user to choose a directory in which\n\ +\ the product is installed\n\ +\ --win-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-upgrade-uuid \n\ +\ UUID associated with upgrades for this package\n\ +\ --win-shortcut\n\ +\ Creates a desktop shortcut for the application\n\ +\ --win-console\n\ +\ Creates a console launcher for the application, should be\n\ +\ specified for application which requires console interactions\n\ + +MSG_Help_no_args=Usage: jpackage \n\ +\Use jpackage --help (or -h) for a list of possible options\ + diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/MainResources.properties --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/MainResources.properties Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,176 @@ +# +# Copyright (c) 2017, 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. +# +# + +param.images-root.name=Image Root +param.images-root.description=Image Root +param.config-root.name=Config Root +param.config-root.description=Config root dir +param.create-image.name=Create Image +param.create-image.description=Creates platform-specific application image. +param.create-installer.name=Create Installer +param.create-installer.description=Creates platform-specific installer for the application. +param.create-jre-installer.name=Create JRE Installer +param.create-jre-installer.description=Creates platform-specific JRE installer. +param.jlink-builder.name=JLink Builder +param.jlink-builder.description=Name of the JLink Builder to build the applicaiton image with. +param.app-name.name=App Name +param.app-name.description=The name of the application. +param.app-fs-name.name=App File System Name +param.app-fs-name.description=The name of the application suitable for file system use. Typically this is just letters, numbers, dots, and dashes. +param.app-resource.description=All of the files to place in the resources directory. Including all needed jars as assets. +param.app-resources.name=Resources +param.app-resource-list.description=A List of RelativeFileSet objects containing all of the files to place in the resources directory. Including all needed jars as assets. +param.app-resources-list.name=Resources List +param.build-root.name=Build Root +param.build-root.description=The directory in which to use and place temporary files. +param.category.name=Category +param.category.description=The category oor group of the application. Generally speaking you will also want to specify application specific categories as well. +param.category.default=Unknown +param.copyright.name=Copyright +param.copyright.description=The copyright for the application. +param.copyright.default=Copyright (C) {0,date,YYYY} +param.description.name=Description +param.description.description=A longer description of the application +param.description.default=none +param.icon-file.name=Icon +param.icon-file.description=The main icon of the application bundle. +param.identifier.name=Identifier +param.identifier.description=What is the machine readable identifier of this application? The format should be a DNS name in reverse order, such as com.example.myapplication. +param.arguments.name=Command Line Arguments +param.arguments.description=Command Line Arguments to be passed to the main class if no arguments are specified by the launcher. +param.jvm-options.name=JVM Options +param.jvm-options.description=JVM flags and options to be passed in. +param.jvm-system-properties.name=JVM System Properties +param.jvm-system-properties.description=JVM System Properties (of the -Dname\=value variety). +param.license-file.name=License +param.license-file.description=The license file, relative to the assembled application directory. +param.main-class.name=Main Class +param.main-class.description=The main class for the application. Either a javafx.application.Application instance or a java class with a main method. +param.main-module.name=Main Module +param.main-module.description=The main module for the application. This is the module containing the main class. +param.classpath.name=Main Jar Classpath +param.classpath.description=The classpath from the main jar of the application, relative to the assembled application directory. +param.main-jar.name=Main Jar +param.main-jar.description=The main jar of the application. This jar should have the main-class, and is relative to the assembled application dir. +param.name.name=Name +param.name.description=The name of the application. +param.preferences-id.name=Preferences ID +param.preferences-id.description=The preferences node to search for User JVM Options. The format be a slash delimited version of the main package name, such as "com/example/myapplication". +param.title.name=Title +param.title.description=A title for the application. +param.vendor.name=Vendor +param.vendor.description=The vendor of the application. +param.vendor.default=Unknown +param.predefined-app-image.name=Predefined Application Image +param.predefined-app-image.description=Location of the predefined application image that is used to build an installable package. +param.predefined-runtime-image.name=Predefined Runtime Image +param.predefined-runtime-image.description=Location of the custom runtime image that is used to build an application image and installable packages. +param.version.name=Version +param.version.description=The version of this application. +param.version.default=1.0 +param.verbose.name=Verbose +param.verbose.description=Flag to print out more information and saves configuration files for bundlers. +param.force.name=Force +param.force.description=Flag to allow removal of existing Build Root contents +param.resource-dir.name=Resource Dir +param.resource-dir.description=The directory to look for bundler specific resources. +param.secondary-launchers.name=Secondary Launchers +param.secondary-launchers.description=A collection of bundle param info for secondary launchers +param.file-associations.name=File Associations +param.file-associations.description=A list of maps where each map describes a file association. Uses the "fileAssociation." series of bundle arguments in each map. +param.fa-extension.name=File Association Extension +param.fa-extension.description=The File Extension to be associated, just the extension no dots. +param.fa-content-type.name=File Association Content Type +param.fa-content-type.description=Content Type to be associated. Such as application/x-vnd.my-awesome-app. +param.fa-icon.name=File Association Icon +param.fa-icon.description=The Icon to be used for associated files. Defaults to the application icon. +param.fa-description.name=File Association Description +param.fa-description.description=The description to be used for associated files. The default is " File". +param.source-dir.name=Source Directory +param.source-dir.description=Path to the directory containing the files to be bundled. +param.module-path.name=Module Path +param.module-path.description=When packaging the Java Runtime, this is the path JLink will look in for modules. +param.add-modules.name=Add Modules +param.add-modules.description=List of Modules to add to JImage creation, including possible services. +param.limit-modules.name=Limit Modules +param.limit-modules.description=Modules to Limit JImage creation to. +param.strip-executables.name=Strip Native Executables +param.strip-executables.description=Removes native executables from the JImage creation. +param.main.module.name=Main Module +param.main.module.description=The main module of the application. This module should have the main-class, and is on the module path. +param.install-dir.name=Installation Directory +param.install-dir.description=Installation directory of the application. + +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.detected.modules="Automatically adding detected modules: {0}." +message.modules="Adding modules: {0} to runtime image." +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} + +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-without-force=Root Directory {0} already exists and --force is not specified" +error.no-application-class=Main application class is missing. +error.no-application-class.advice=Please specify main application class. +error.no-main-module=Main application module is missing. +error.no-main-module.advice=Make sure to use fx\:module task to create modular application. +error.srcfiles.contain.modules=Error: Modules are not allowed in srcfiles: {0}. +error.no-main-class-with-main-jar=An application class was not specified nor was one found in the jar {0} +error.no-main-class-with-main-jar.advice=Please specify a application class or ensure that the jar {0} specifies one in the manifest. +error.no-main-class=An application 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} +error.main-jar-does-not-exist.advice=The main jar must be specified relative to the app resources (not an absolute path), and must exist within those resources. + +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_BundlerPlatformException=Bundler {0} skipped because the bundler does not support bundling on this platform. +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_Version=jpackage version +MSG_BundlerFailed=Error: Bundler "{1}" ({0}) failed to produce a bundle. + +ERR_MissingArgument=Error: Missing argument: {0} +ERR_MissingAppResources=Error: No application jars found +ERR_AppImageNotExist=Error: App image directory "{0}" does not exist +ERR_AppImageInvalid=Error: App image directory "{0}" is invalid and does not contain "app" and/or "runtime" sub-directories +ERR_NoSecondaryLauncherName=Secondary Launchers require a name parameter. +ERR_NoUniqueName=Secondary Launchers require a unique name parameter. +ERR_NoJreInstallerName=Jre Installers require a name parameter. +ERR_InvalidCharacterInArgument=Error: Invalid character found in {0} argument +ERR_LicenseFileNotExit=Error: Specified license file does not exist. +ERR_BuildRootInvalid=Error: build-root ({0}) must be empty directory. diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/MainResources_ja.properties --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/MainResources_ja.properties Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,176 @@ +# +# Copyright (c) 2017, 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. +# +# + +param.images-root.name=Image Root +param.images-root.description=Image Root +param.config-root.name=Config Root +param.config-root.description=Config root dir +param.create-image.name=Create Image +param.create-image.description=Creates platform-specific application image. +param.create-installer.name=Create Installer +param.create-installer.description=Creates platform-specific installer for the application. +param.create-jre-installer.name=Create JRE Installer +param.create-jre-installer.description=Creates platform-specific JRE installer. +param.jlink-builder.name=JLink Builder +param.jlink-builder.description=Name of the JLink Builder to build the applicaiton image with. +param.app-name.name=App Name +param.app-name.description=The name of the application. +param.app-fs-name.name=App File System Name +param.app-fs-name.description=The name of the application suitable for file system use. Typically this is just letters, numbers, dots, and dashes. +param.app-resource.description=All of the files to place in the resources directory. Including all needed jars as assets. +param.app-resources.name=Resources +param.app-resource-list.description=A List of RelativeFileSet objects containing all of the files to place in the resources directory. Including all needed jars as assets. +param.app-resources-list.name=Resources List +param.build-root.name=Build Root +param.build-root.description=The directory in which to use and place temporary files. +param.category.name=Category +param.category.description=The category oor group of the application. Generally speaking you will also want to specify application specific categories as well. +param.category.default=Unknown +param.copyright.name=Copyright +param.copyright.description=The copyright for the application. +param.copyright.default=Copyright (C) {0,date,YYYY} +param.description.name=Description +param.description.description=A longer description of the application +param.description.default=none +param.icon-file.name=Icon +param.icon-file.description=The main icon of the application bundle. +param.identifier.name=Identifier +param.identifier.description=What is the machine readable identifier of this application? The format should be a DNS name in reverse order, such as com.example.myapplication. +param.arguments.name=Command Line Arguments +param.arguments.description=Command Line Arguments to be passed to the main class if no arguments are specified by the launcher. +param.jvm-options.name=JVM Options +param.jvm-options.description=JVM flags and options to be passed in. +param.jvm-system-properties.name=JVM System Properties +param.jvm-system-properties.description=JVM System Properties (of the -Dname\=value variety). +param.license-file.name=License +param.license-file.description=The license file, relative to the assembled application directory. +param.main-class.name=Main Class +param.main-class.description=The main class for the application. Either a javafx.application.Application instance or a java class with a main method. +param.main-module.name=Main Module +param.main-module.description=The main module for the application. This is the module containing the main class. +param.classpath.name=Main Jar Classpath +param.classpath.description=The classpath from the main jar of the application, relative to the assembled application directory. +param.main-jar.name=Main Jar +param.main-jar.description=The main jar of the application. This jar should have the main-class, and is relative to the assembled application dir. +param.name.name=Name +param.name.description=The name of the application. +param.preferences-id.name=Preferences ID +param.preferences-id.description=The preferences node to search for User JVM Options. The format be a slash delimited version of the main package name, such as "com/example/myapplication". +param.title.name=Title +param.title.description=A title for the application. +param.vendor.name=Vendor +param.vendor.description=The vendor of the application. +param.vendor.default=Unknown +param.predefined-app-image.name=Predefined Application Image +param.predefined-app-image.description=Location of the predefined application image that is used to build an installable package. +param.predefined-runtime-image.name=Predefined Runtime Image +param.predefined-runtime-image.description=Location of the custom runtime image that is used to build an application image and installable packages. +param.version.name=Version +param.version.description=The version of this application. +param.version.default=1.0 +param.verbose.name=Verbose +param.verbose.description=Flag to print out more information and saves configuration files for bundlers. +param.force.name=Force +param.force.description=Flag to allow removal of existing Build Root contents +param.resource-dir.name=Resource Dir +param.resource-dir.description=The directory to look for bundler specific resources. +param.secondary-launchers.name=Secondary Launchers +param.secondary-launchers.description=A collection of bundle param info for secondary launchers +param.file-associations.name=File Associations +param.file-associations.description=A list of maps where each map describes a file association. Uses the "fileAssociation." series of bundle arguments in each map. +param.fa-extension.name=File Association Extension +param.fa-extension.description=The File Extension to be associated, just the extension no dots. +param.fa-content-type.name=File Association Content Type +param.fa-content-type.description=Content Type to be associated. Such as application/x-vnd.my-awesome-app. +param.fa-icon.name=File Association Icon +param.fa-icon.description=The Icon to be used for associated files. Defaults to the application icon. +param.fa-description.name=File Association Description +param.fa-description.description=The description to be used for associated files. The default is " File". +param.source-dir.name=Source Directory +param.source-dir.description=Path to the directory containing the files to be bundled. +param.module-path.name=Module Path +param.module-path.description=When packaging the Java Runtime, this is the path JLink will look in for modules. +param.add-modules.name=Add Modules +param.add-modules.description=List of Modules to add to JImage creation, including possible services. +param.limit-modules.name=Limit Modules +param.limit-modules.description=Modules to Limit JImage creation to. +param.strip-executables.name=Strip Native Executables +param.strip-executables.description=Removes native executables from the JImage creation. +param.main.module.name=Main Module +param.main.module.description=The main module of the application. This module should have the main-class, and is on the module path. +param.install-dir.name=Installation Directory +param.install-dir.description=Installation directory of the application. + +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.detected.modules="Automatically adding detected modules: {0}." +message.modules="Adding modules: {0} to runtime image." +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} + +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-without-force=Root Directory {0} already exists and --force is not specified" +error.no-application-class=Main application class is missing. +error.no-application-class.advice=Please specify main application class. +error.no-main-module=Main application module is missing. +error.no-main-module.advice=Make sure to use fx\:module task to create modular application. +error.srcfiles.contain.modules=Error: Modules are not allowed in srcfiles: {0}. +error.no-main-class-with-main-jar=An application class was not specified nor was one found in the jar {0} +error.no-main-class-with-main-jar.advice=Please specify a application class or ensure that the jar {0} specifies one in the manifest. +error.no-main-class=An application 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} +error.main-jar-does-not-exist.advice=The main jar must be specified relative to the app resources (not an absolute path), and must exist within those resources. + +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_BundlerPlatformException=Bundler {0} skipped because the bundler does not support bundling on this platform. +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_Version=jpackage version +MSG_BundlerFailed=Error: Bundler "{1}" ({0}) failed to produce a bundle. + +ERR_MissingArgument=Error: Missing argument: {0} +ERR_MissingAppResources=Error: No application jars found +ERR_AppImageNotExist=Error: App image directory "{0}" does not exist +ERR_AppImageInvalid=Error: App image directory "{0}" is invalid and does not contain "app" and/or "runtime" sub-directories +ERR_NoSecondaryLauncherName=Secondary Launchers require a name parameter. +ERR_NoUniqueName=Secondary Launchers require a unique name parameter. +ERR_NoJreInstallerName=Jre Installers require a name parameter. +ERR_InvalidCharacterInArgument=Error: Invalid character found in {0} argument +ERR_LicenseFileNotExit=Error: Specified license file does not exist. +ERR_BuildRootInvalid=Error: build-root ({0}) must be empty directory. diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/MainResources_zh_CN.properties --- /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 Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,176 @@ +# +# Copyright (c) 2017, 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. +# +# + +param.images-root.name=Image Root +param.images-root.description=Image Root +param.config-root.name=Config Root +param.config-root.description=Config root dir +param.create-image.name=Create Image +param.create-image.description=Creates platform-specific application image. +param.create-installer.name=Create Installer +param.create-installer.description=Creates platform-specific installer for the application. +param.create-jre-installer.name=Create JRE Installer +param.create-jre-installer.description=Creates platform-specific JRE installer. +param.jlink-builder.name=JLink Builder +param.jlink-builder.description=Name of the JLink Builder to build the applicaiton image with. +param.app-name.name=App Name +param.app-name.description=The name of the application. +param.app-fs-name.name=App File System Name +param.app-fs-name.description=The name of the application suitable for file system use. Typically this is just letters, numbers, dots, and dashes. +param.app-resource.description=All of the files to place in the resources directory. Including all needed jars as assets. +param.app-resources.name=Resources +param.app-resource-list.description=A List of RelativeFileSet objects containing all of the files to place in the resources directory. Including all needed jars as assets. +param.app-resources-list.name=Resources List +param.build-root.name=Build Root +param.build-root.description=The directory in which to use and place temporary files. +param.category.name=Category +param.category.description=The category oor group of the application. Generally speaking you will also want to specify application specific categories as well. +param.category.default=Unknown +param.copyright.name=Copyright +param.copyright.description=The copyright for the application. +param.copyright.default=Copyright (C) {0,date,YYYY} +param.description.name=Description +param.description.description=A longer description of the application +param.description.default=none +param.icon-file.name=Icon +param.icon-file.description=The main icon of the application bundle. +param.identifier.name=Identifier +param.identifier.description=What is the machine readable identifier of this application? The format should be a DNS name in reverse order, such as com.example.myapplication. +param.arguments.name=Command Line Arguments +param.arguments.description=Command Line Arguments to be passed to the main class if no arguments are specified by the launcher. +param.jvm-options.name=JVM Options +param.jvm-options.description=JVM flags and options to be passed in. +param.jvm-system-properties.name=JVM System Properties +param.jvm-system-properties.description=JVM System Properties (of the -Dname\=value variety). +param.license-file.name=License +param.license-file.description=The license file, relative to the assembled application directory. +param.main-class.name=Main Class +param.main-class.description=The main class for the application. Either a javafx.application.Application instance or a java class with a main method. +param.main-module.name=Main Module +param.main-module.description=The main module for the application. This is the module containing the main class. +param.classpath.name=Main Jar Classpath +param.classpath.description=The classpath from the main jar of the application, relative to the assembled application directory. +param.main-jar.name=Main Jar +param.main-jar.description=The main jar of the application. This jar should have the main-class, and is relative to the assembled application dir. +param.name.name=Name +param.name.description=The name of the application. +param.preferences-id.name=Preferences ID +param.preferences-id.description=The preferences node to search for User JVM Options. The format be a slash delimited version of the main package name, such as "com/example/myapplication". +param.title.name=Title +param.title.description=A title for the application. +param.vendor.name=Vendor +param.vendor.description=The vendor of the application. +param.vendor.default=Unknown +param.predefined-app-image.name=Predefined Application Image +param.predefined-app-image.description=Location of the predefined application image that is used to build an installable package. +param.predefined-runtime-image.name=Predefined Runtime Image +param.predefined-runtime-image.description=Location of the custom runtime image that is used to build an application image and installable packages. +param.version.name=Version +param.version.description=The version of this application. +param.version.default=1.0 +param.verbose.name=Verbose +param.verbose.description=Flag to print out more information and saves configuration files for bundlers. +param.force.name=Force +param.force.description=Flag to allow removal of existing Build Root contents +param.resource-dir.name=Resource Dir +param.resource-dir.description=The directory to look for bundler specific resources. +param.secondary-launchers.name=Secondary Launchers +param.secondary-launchers.description=A collection of bundle param info for secondary launchers +param.file-associations.name=File Associations +param.file-associations.description=A list of maps where each map describes a file association. Uses the "fileAssociation." series of bundle arguments in each map. +param.fa-extension.name=File Association Extension +param.fa-extension.description=The File Extension to be associated, just the extension no dots. +param.fa-content-type.name=File Association Content Type +param.fa-content-type.description=Content Type to be associated. Such as application/x-vnd.my-awesome-app. +param.fa-icon.name=File Association Icon +param.fa-icon.description=The Icon to be used for associated files. Defaults to the application icon. +param.fa-description.name=File Association Description +param.fa-description.description=The description to be used for associated files. The default is " File". +param.source-dir.name=Source Directory +param.source-dir.description=Path to the directory containing the files to be bundled. +param.module-path.name=Module Path +param.module-path.description=When packaging the Java Runtime, this is the path JLink will look in for modules. +param.add-modules.name=Add Modules +param.add-modules.description=List of Modules to add to JImage creation, including possible services. +param.limit-modules.name=Limit Modules +param.limit-modules.description=Modules to Limit JImage creation to. +param.strip-executables.name=Strip Native Executables +param.strip-executables.description=Removes native executables from the JImage creation. +param.main.module.name=Main Module +param.main.module.description=The main module of the application. This module should have the main-class, and is on the module path. +param.install-dir.name=Installation Directory +param.install-dir.description=Installation directory of the application. + +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.detected.modules="Automatically adding detected modules: {0}." +message.modules="Adding modules: {0} to runtime image." +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} + +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-without-force=Root Directory {0} already exists and --force is not specified" +error.no-application-class=Main application class is missing. +error.no-application-class.advice=Please specify main application class. +error.no-main-module=Main application module is missing. +error.no-main-module.advice=Make sure to use fx\:module task to create modular application. +error.srcfiles.contain.modules=Error: Modules are not allowed in srcfiles: {0}. +error.no-main-class-with-main-jar=An application class was not specified nor was one found in the jar {0} +error.no-main-class-with-main-jar.advice=Please specify a application class or ensure that the jar {0} specifies one in the manifest. +error.no-main-class=An application 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} +error.main-jar-does-not-exist.advice=The main jar must be specified relative to the app resources (not an absolute path), and must exist within those resources. + +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_BundlerPlatformException=Bundler {0} skipped because the bundler does not support bundling on this platform. +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_Version=jpackage version +MSG_BundlerFailed=Error: Bundler "{1}" ({0}) failed to produce a bundle. + +ERR_MissingArgument=Error: Missing argument: {0} +ERR_MissingAppResources=Error: No application jars found +ERR_AppImageNotExist=Error: App image directory "{0}" does not exist +ERR_AppImageInvalid=Error: App image directory "{0}" is invalid and does not contain "app" and/or "runtime" sub-directories +ERR_NoSecondaryLauncherName=Secondary Launchers require a name parameter. +ERR_NoUniqueName=Secondary Launchers require a unique name parameter. +ERR_NoJreInstallerName=Jre Installers require a name parameter. +ERR_InvalidCharacterInArgument=Error: Invalid character found in {0} argument +ERR_LicenseFileNotExit=Error: Specified license file does not exist. +ERR_BuildRootInvalid=Error: build-root ({0}) must be empty directory. diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/ResourceLocator.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/ResourceLocator.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2011, 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.internal.resources; + +public class ResourceLocator { + +} diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/share/classes/jdk/jpackage/main/Main.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/main/Main.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2011, 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 jdk.jpackage.internal.Arguments; +import jdk.jpackage.internal.Log; +import jdk.jpackage.internal.CLIHelp; +import java.io.PrintWriter; +import java.util.ResourceBundle; + +public class Main { + + private static final ResourceBundle bundle = ResourceBundle.getBundle( + "jdk.jpackage.internal.resources.MainResources"); + + private static final String version = bundle.getString("MSG_Version") + + " " + System.getProperty("java.version"); + + /** + * 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 = run(args); + System.exit(status); + } + + /** + * run() - 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 static int run(PrintWriter out, PrintWriter err, String... args) + throws Exception { + // Create logger with provided streams + Log.Logger logger = new Log.Logger(false); + logger.setPrintWriter(out, err); + Log.setLogger(logger); + + int status = run(args); + Log.flush(); + return status; + } + + private static int run(String... args) throws Exception { + if (args.length == 0) { + CLIHelp.showHelp(true); + } else if (hasHelp(args)){ + if (hasVersion(args)) { + Log.info(version + "\n"); + } + CLIHelp.showHelp(false); + } else if (hasVersion(args)) { + Log.info(version); + } else { + try { + Arguments arguments = new Arguments(args); + if (!arguments.processArguments()) { + // processArguments() should log error message if failed. + return -1; + } + } catch (Exception e) { + if (Log.isVerbose()) { + Log.verbose(e); + } else { + Log.error(e.getMessage()); + if (e.getCause() != null && e.getCause() != e) { + Log.error(e.getCause().getMessage()); + } + } + return -1; + } + } + + return 0; + } + + private static boolean hasHelp(String[] args) { + for (String a : args) { + if ("--help".equals(a) || "-h".equals(a)) { + return true; + } + } + return false; + } + + private static boolean hasVersion(String[] args) { + for (String a : args) { + if ("--version".equals(a) || "-v".equals(a)) { + return true; + } + } + return false; + } + +} diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/share/classes/module-info.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/share/classes/module-info.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,57 @@ +/* + * Copyright (c) 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. + */ + +/** + * Defines the Java Packaging tool, jpackage. + * + *

jpackage is a tool for generating self-contained application bundles. + * + *

This module provides the equivalent of command-line access to jpackage + * 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"}. + * + * @moduleGraph + * @since 13 + */ + +module jdk.jpackage { + requires jdk.jlink; + + requires java.xml; + requires java.logging; + 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; +} diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/share/native/libapplauncher/FilePath.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/share/native/libapplauncher/FilePath.cpp Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,769 @@ +/* + * Copyright (c) 2014, 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. + */ + +#include "FilePath.h" + +#include +#include + +#ifdef WINDOWS +#include +#endif // WINDOWS + +#ifdef POSIX +#include +#endif // POSIX + + +bool FilePath::FileExists(const TString FileName) { + bool result = false; +#ifdef WINDOWS + 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); + } +#endif // WINDOWS +#ifdef POSIX + struct stat buf; + + if ((stat(StringToFileSystemString(FileName), &buf) == 0) && + (S_ISREG(buf.st_mode) != 0)) { + result = true; + } +#endif // POSIX + return result; +} + +bool FilePath::DirectoryExists(const TString DirectoryName) { + bool result = false; +#ifdef WINDOWS + 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); + } +#endif // WINDOWS +#ifdef POSIX + struct stat buf; + + if ((stat(StringToFileSystemString(DirectoryName), &buf) == 0) && + (S_ISDIR(buf.st_mode) != 0)) { + result = true; + } +#endif // POSIX + return result; +} + +#ifdef WINDOWS +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; +} +#endif // WINDOWS + +bool FilePath::DeleteFile(const TString FileName) { + bool result = false; + + if (FileExists(FileName) == true) { +#ifdef WINDOWS + TString lFileName = FixPathForPlatform(FileName); + FileAttributes attributes(lFileName); + + if (attributes.Contains(faReadOnly) == true) { + attributes.Remove(faReadOnly); + } + + result = ::DeleteFile(lFileName.data()) == TRUE; +#endif // WINDOWS +#ifdef POSIX + if (unlink(StringToFileSystemString(FileName)) == 0) { + result = true; + } +#endif // POSIX + } + + return result; +} + +bool FilePath::DeleteDirectory(const TString DirectoryName) { + bool result = false; + + if (DirectoryExists(DirectoryName) == true) { +#ifdef WINDOWS + SHFILEOPSTRUCTW fos = {0}; + TString directoryName = FixPathForPlatform(DirectoryName); + DynamicBuffer 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; +#endif // WINDOWS +#ifdef POSIX + if (unlink(StringToFileSystemString(DirectoryName)) == 0) { + result = true; + } +#endif // POSIX + } + + 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) { +#ifdef WINDOWS + TString result; + size_t slash = Path.find_last_of(TRAILING_PATHSEPARATOR); + if (slash != TString::npos) + result = Path.substr(0, slash); + return result; +#endif // WINDOWS +#ifdef POSIX + return dirname(StringToFileSystemString(Path)); +#endif // POSIX +} + +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) { +#ifdef WINDOWS + 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; +#endif // WINDOWS +#ifdef POSIX + return basename(StringToFileSystemString(Path)); +#endif // POSIX +} + +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); +#ifdef WINDOWS + // 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; + } + } +#endif // WINDOWS + 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 paths; + TString lpath = Path; + + while (lpath.empty() == false && DirectoryExists(lpath) == false) { + paths.push_front(lpath); + lpath = ExtractFilePath(lpath); + } + + for (std::list::iterator iterator = paths.begin(); + iterator != paths.end(); iterator++) { + lpath = *iterator; + +#ifdef WINDOWS + if (_wmkdir(lpath.data()) == 0) { +#endif // WINDOWS +#ifdef POSIX + mode_t mode = S_IRWXU; + if (!ownerOnly) { + mode |= S_IRWXG | S_IROTH | S_IXOTH; + } + if (mkdir(StringToFileSystemString(lpath), mode) == 0) { +#endif // POSIX + result = true; + } + else { + result = false; + break; + } + } + + return result; +} + +void FilePath::ChangePermissions(TString FileName, bool ownerOnly) { +#ifdef POSIX + mode_t mode = S_IRWXU; + if (!ownerOnly) { + mode |= S_IRWXG | S_IROTH | S_IXOTH; + } + chmod(FileName.data(), mode); +#endif // POSIX +} + +//---------------------------------------------------------------------------- + +#include + +FileAttributes::FileAttributes(const TString FileName, bool FollowLink) { + FFileName = FileName; + FFollowLink = FollowLink; + ReadAttributes(); +} + +bool FileAttributes::WriteAttributes() { + bool result = false; + +#ifdef WINDOWS + DWORD attributes = 0; + + for (std::vector::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; + } +#endif // WINDOWS +#ifdef POSIX + mode_t attributes = 0; + + for (std::vector::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; + } +#endif // POSIX + + 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; + +#ifdef WINDOWS + 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_INTEGRITY_STREAM) { + // FAttributes.push_back(faIntegrityStream); + // } + if (attributes | FILE_ATTRIBUTE_NORMAL) { + FAttributes.push_back(faNormal); + } + if (attributes | FILE_ATTRIBUTE_NOT_CONTENT_INDEXED) { + FAttributes.push_back(faNotContentIndexed); + } + // if (attributes | FILE_ATTRIBUTE_NO_SCRUB_DATA) { + // FAttributes.push_back(faNoScrubData); + // } + 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); + } + } +#endif // WINDOWS +#ifdef POSIX + 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); + } + } +#endif // POSIX + + return result; +} + +bool FileAttributes::Valid(const FileAttribute Value) { + bool result = false; + + switch (Value) { +#ifdef WINDOWS + case faHidden: +#endif // WINDOWS +#ifdef POSIX + case faReadWrite: + case faWriteOnly: + case faExecute: + + case faGroupReadWrite: + case faGroupWriteOnly: + case faGroupReadOnly: + case faGroupExecute: + + case faOthersReadWrite: + case faOthersWriteOnly: + case faOthersReadOnly: + case faOthersExecute: +#endif // POSIX + + case faReadOnly: { + result = true; + break; + } + default: + break; + } + + return result; +} + +void FileAttributes::Append(FileAttribute Value) { + if (Valid(Value) == true) { +#ifdef POSIX + if ((Value == faReadOnly && Contains(faWriteOnly) == true) || + (Value == faWriteOnly && Contains(faReadOnly) == true)) { + Value = faReadWrite; + } +#endif // POSIX + + FAttributes.push_back(Value); + WriteAttributes(); + } +} + +bool FileAttributes::Contains(FileAttribute Value) { + bool result = false; + + std::vector::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) { +#ifdef POSIX + if (Value == faReadOnly && Contains(faReadWrite) == true) { + Append(faWriteOnly); + Remove(faReadWrite); + } + else if (Value == faWriteOnly && Contains(faReadWrite) == true) { + Append(faReadOnly); + Remove(faReadWrite); + } +#endif // POSIX + + std::vector::iterator iterator = + std::find(FAttributes.begin(), FAttributes.end(), Value); + + if (iterator != FAttributes.end()) { + FAttributes.erase(iterator); + WriteAttributes(); + } + } +} diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/share/native/libapplauncher/FilePath.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/share/native/libapplauncher/FilePath.h Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2014, 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. + */ + +#ifndef FILEPATH_H +#define FILEPATH_H + +#include "Platform.h" +#include "PlatformString.h" + +#include + +enum FileAttribute { +#ifdef WINDOWS + faArchive = FILE_ATTRIBUTE_ARCHIVE, + faCompressed = FILE_ATTRIBUTE_COMPRESSED, + faDevice = FILE_ATTRIBUTE_DEVICE, + faDirectory = FILE_ATTRIBUTE_DIRECTORY, + faEncrypted = FILE_ATTRIBUTE_ENCRYPTED, + faHidden = FILE_ATTRIBUTE_HIDDEN, + //faIntegrityStream = FILE_ATTRIBUTE_INTEGRITY_STREAM, + faNormal = FILE_ATTRIBUTE_NORMAL, + faNotContentIndexed = FILE_ATTRIBUTE_NOT_CONTENT_INDEXED, + //faNoScrubData = FILE_ATTRIBUTE_NO_SCRUB_DATA, + 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 //WINDOWS +#ifdef POSIX + faBlockSpecial, + faCharacterSpecial, + faFIFOSpecial, + faNormal, + faDirectory, + faSymbolicLink, + faSocket, + + // Owner + faReadOnly, + faWriteOnly, + faReadWrite, + faExecute, + + // Group + faGroupReadOnly, + faGroupWriteOnly, + faGroupReadWrite, + faGroupExecute, + + // Others + faOthersReadOnly, + faOthersWriteOnly, + faOthersReadWrite, + faOthersExecute, + + faHidden +#endif //POSIX +}; + +class FileAttributes { +private: + TString FFileName; + bool FFollowLink; + std::vector 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 diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/share/native/libapplauncher/GenericPlatform.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/share/native/libapplauncher/GenericPlatform.cpp Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,235 @@ +/* + * Copyright (c) 2014, 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. + */ + +#include "GenericPlatform.h" + +#include +#include + +#ifdef WINDOWS +#include +#endif // WINDOWS + + +GenericPlatform::GenericPlatform(void) { +} + +GenericPlatform::~GenericPlatform(void) { +} + +TString GenericPlatform::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; +} + +TString GenericPlatform::GetPackageAppDirectory() { +#if defined(WINDOWS) || defined(LINUX) + return FilePath::IncludeTrailingSeparator( + GetPackageRootDirectory()) + _T("app"); +#endif // WINDOWS || LINUX +#ifdef MAC + return FilePath::IncludeTrailingSeparator( + GetPackageRootDirectory()) + _T("Java"); +#endif +} + +TString GenericPlatform::GetPackageLauncherDirectory() { +#if defined(WINDOWS) || defined(LINUX) + return GetPackageRootDirectory(); +#endif // WINDOWS || LINUX +#ifdef MAC + return FilePath::IncludeTrailingSeparator( + GetPackageRootDirectory()) + _T("MacOS"); +#endif +} + +TString GenericPlatform::GetPackageRuntimeBinDirectory() { +#ifdef WINDOWS + return FilePath::IncludeTrailingSeparator(GetPackageRootDirectory()) + _T("runtime\\bin"); +#endif // WINDOWS +#ifdef LINUX + return FilePath::IncludeTrailingSeparator(GetPackageRootDirectory()) + _T("runtime/bin"); +#endif // LINUX +#ifdef MAC + return FilePath::IncludeTrailingSeparator(GetPackageRootDirectory()) + _T("Plugins/Java.runtime/Contents/Home/bin"); +#endif +} + +std::list GenericPlatform::LoadFromFile(TString FileName) { + std::list result; + + if (FilePath::FileExists(FileName) == true) { + std::wifstream stream(FileName.data()); + +#ifdef WINDOWS + const std::locale empty_locale = std::locale::empty(); +#endif // WINDOWS +#ifdef POSIX + const std::locale empty_locale = std::locale::classic(); +#endif // POSIX +#if defined(WINDOWS) + const std::locale utf8_locale = + std::locale(empty_locale, new std::codecvt_utf8()); + stream.imbue(utf8_locale); +#endif // WINDOWS + + 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 GenericPlatform::SaveToFile(TString FileName, std::list Contents, bool ownerOnly) { + TString path = FilePath::ExtractFilePath(FileName); + + if (FilePath::DirectoryExists(path) == false) { + FilePath::CreateDirectory(path, ownerOnly); + } + + std::wofstream stream(FileName.data()); + + FilePath::ChangePermissions(FileName.data(), ownerOnly); + +#ifdef WINDOWS + const std::locale empty_locale = std::locale::empty(); +#endif // WINDOWS +#ifdef POSIX + const std::locale empty_locale = std::locale::classic(); +#endif // POSIX +#if defined(WINDOWS) + const std::locale utf8_locale = + std::locale(empty_locale, new std::codecvt_utf8()); + stream.imbue(utf8_locale); +#endif // WINDOWS || MAC + + if (stream.is_open() == true) { + for (std::list::const_iterator iterator = + Contents.begin(); iterator != Contents.end(); iterator++) { + TString line = *iterator; + stream << PlatformString(line).toUnicodeString() << std::endl; + } + } +} + +#if defined(WINDOWS) || defined(LINUX) +TString GenericPlatform::GetAppName() { + TString result = GetModuleFileName(); + result = FilePath::ExtractFileName(result); +#if defined(WINDOWS) + result = FilePath::ChangeFileExt(result, _T("")); +#endif + return result; +} +#endif // WINDOWS || LINUX + +std::map GenericPlatform::GetKeys() { + std::map keys; + keys.insert(std::map::value_type(CONFIG_VERSION, + _T("app.version"))); + keys.insert(std::map::value_type(CONFIG_MAINJAR_KEY, + _T("app.mainjar"))); + keys.insert(std::map::value_type(CONFIG_MAINMODULE_KEY, + _T("app.mainmodule"))); + keys.insert(std::map::value_type(CONFIG_MAINCLASSNAME_KEY, + _T("app.mainclass"))); + keys.insert(std::map::value_type(CONFIG_CLASSPATH_KEY, + _T("app.classpath"))); + keys.insert(std::map::value_type(CONFIG_MODULEPATH_KEY, + _T("app.modulepath"))); + keys.insert(std::map::value_type(APP_NAME_KEY, + _T("app.name"))); + keys.insert(std::map::value_type(CONFIG_APP_ID_KEY, + _T("app.preferences.id"))); + keys.insert(std::map::value_type(JVM_RUNTIME_KEY, + _T("app.runtime"))); + keys.insert(std::map::value_type(JPACKAGE_APP_DATA_DIR, + _T("app.identifier"))); + keys.insert(std::map::value_type(CONFIG_SPLASH_KEY, + _T("app.splash"))); + keys.insert(std::map::value_type(CONFIG_APP_MEMORY, + _T("app.memory"))); + keys.insert(std::map::value_type(CONFIG_APP_DEBUG, + _T("app.debug"))); + keys.insert(std::map::value_type(CONFIG_APPLICATION_INSTANCE, + _T("app.application.instance"))); + keys.insert(std::map::value_type(CONFIG_SECTION_APPLICATION, + _T("Application"))); + keys.insert(std::map::value_type(CONFIG_SECTION_JVMOPTIONS, + _T("JVMOptions"))); + keys.insert(std::map::value_type(CONFIG_SECTION_APPCDSJVMOPTIONS, + _T("AppCDSJVMOptions"))); + keys.insert(std::map::value_type(CONFIG_SECTION_APPCDSGENERATECACHEJVMOPTIONS, + _T("AppCDSGenerateCacheJVMOptions"))); + keys.insert(std::map::value_type(CONFIG_SECTION_ARGOPTIONS, + _T("ArgOptions"))); + + return keys; +} + +#ifdef DEBUG +DebugState GenericPlatform::GetDebugState() { + DebugState result = dsNone; + + if (IsNativeDebuggerPresent() == true) { + result = dsNative; + } + + return result; +} +#endif // DEBUG diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/share/native/libapplauncher/GenericPlatform.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/share/native/libapplauncher/GenericPlatform.h Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2014, 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. + */ + +#ifndef GENERICPLATFORM_H +#define GENERICPLATFORM_H + +#include "FilePath.h" +// #include "Platform.h" + +#ifdef WINDOWS +#pragma warning( push ) +// C4250 - 'class1' : inherits 'class2::member' via dominance +#pragma warning( disable : 4250 ) +#endif + +class GenericPlatform : virtual public Platform { +public: + GenericPlatform(void); + virtual ~GenericPlatform(void); + + virtual TString GetPackageAppDirectory(); + virtual TString GetPackageLauncherDirectory(); + virtual TString GetPackageRuntimeBinDirectory(); + + virtual TString GetConfigFileName(); + + virtual std::list LoadFromFile(TString FileName); + virtual void SaveToFile(TString FileName, + std::list Contents, bool ownerOnly); + +#if defined(WINDOWS) || defined(LINUX) + virtual TString GetAppName(); +#endif // WINDOWS || LINUX + + virtual std::map GetKeys(); + +#ifdef DEBUG + virtual DebugState GetDebugState(); +#endif // DEBUG +}; +#ifdef WINDOWS +#pragma warning( pop ) // C4250 +#endif +#endif // GENERICPLATFORM_H diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/share/native/libapplauncher/Helpers.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/share/native/libapplauncher/Helpers.cpp Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,296 @@ +/* + * Copyright (c) 2014, 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. + */ + +#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 + Helpers::GetJVMArgsFromConfig(IPropertyContainer* config) { + OrderedMap 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 Helpers::GetArgsFromConfig(IPropertyContainer* config) { + std::list 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; +} + +void AppendToIni(PropertyFile &Source, IniFile* Destination, TString Key) { + TString value; + + if (Source.GetValue(Key, value) == true) { + Platform& platform = Platform::GetInstance(); + std::map keys = platform.GetKeys(); + Destination->Append(keys[CONFIG_SECTION_APPLICATION], Key, value); + } +} + +void Helpers::LoadOldConfigFile(TString FileName, IniFile* Container) { + PropertyFile propertyFile; + + if (propertyFile.LoadFromFile(FileName) == true) { + Platform& platform = Platform::GetInstance(); + + std::map keys = platform.GetKeys(); + + // Application Section + AppendToIni(propertyFile, Container, keys[CONFIG_MAINJAR_KEY]); + AppendToIni(propertyFile, Container, keys[CONFIG_MAINMODULE_KEY]); + AppendToIni(propertyFile, Container, keys[CONFIG_MAINCLASSNAME_KEY]); + AppendToIni(propertyFile, Container, keys[CONFIG_CLASSPATH_KEY]); + AppendToIni(propertyFile, Container, keys[APP_NAME_KEY]); + AppendToIni(propertyFile, Container, keys[CONFIG_APP_ID_KEY]); + AppendToIni(propertyFile, Container, keys[JVM_RUNTIME_KEY]); + AppendToIni(propertyFile, Container, keys[JPACKAGE_APP_DATA_DIR]); + + AppendToIni(propertyFile, Container, keys[CONFIG_APP_MEMORY]); + AppendToIni(propertyFile, Container, keys[CONFIG_SPLASH_KEY]); + + // JVMOptions Section + OrderedMap JVMArgs = + Helpers::GetJVMArgsFromConfig(&propertyFile); + Container->AppendSection(keys[CONFIG_SECTION_JVMOPTIONS], JVMArgs); + + // ArgOptions Section + std::list args = Helpers::GetArgsFromConfig(&propertyFile); + OrderedMap convertedArgs; + + for (std::list::iterator iterator = args.begin(); + iterator != args.end(); iterator++) { + TString arg = *iterator; + TString name; + TString value; + + if (Helpers::SplitOptionIntoNameValue(arg, name, value) == true) { + convertedArgs.Append(name, value); + } + } + + Container->AppendSection(keys[CONFIG_SECTION_ARGOPTIONS], + convertedArgs); + } +} + +std::list + Helpers::MapToNameValueList(OrderedMap Map) { + std::list result; + std::vector keys = Map.GetKeys(); + + for (OrderedMap::const_iterator iterator = Map.begin(); + iterator != Map.end(); iterator++) { + pair *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 Helpers::StringToArray(TString Value) { + std::list 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; +} diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/share/native/libapplauncher/Helpers.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/share/native/libapplauncher/Helpers.h Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2014, 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. + */ + +#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: + // + 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 + GetJVMArgsFromConfig(IPropertyContainer* config); + static std::list GetArgsFromConfig(IPropertyContainer* config); + + static void LoadOldConfigFile(TString FileName, IniFile* Container); + + static std::list + MapToNameValueList(OrderedMap Map); + + static TString NameValueToString(TString name, TString value); + + static std::list StringToArray(TString Value); +}; + +#endif // HELPERS_H diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/share/native/libapplauncher/IniFile.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/share/native/libapplauncher/IniFile.cpp Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,261 @@ +/* + * Copyright (c) 2015, 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. + */ + +#include "IniFile.h" +#include "Helpers.h" + +#include + + +IniFile::IniFile() : ISectionalPropertyContainer() { +} + +IniFile::~IniFile() { + for (OrderedMap::iterator iterator = + FMap.begin(); iterator != FMap.end(); iterator++) { + pair *item = *iterator; + delete item->second; + } +} + +bool IniFile::LoadFromFile(const TString FileName) { + bool result = false; + Platform& platform = Platform::GetInstance(); + + std::list 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::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::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 contents; + std::vector 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 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 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 &Data) { + bool result = false; + + if (FMap.ContainsKey(SectionName) == true) { + IniSectionData* section; + + if (FMap.GetValue(SectionName, section) == true && section != NULL) { + OrderedMap 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 Values) { + FMap = Values; +} + +std::vector IniSectionData::GetKeys() { + return FMap.GetKeys(); +} + +std::list IniSectionData::GetLines() { + std::list result; + std::vector 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 IniSectionData::GetData() { + OrderedMap 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 Values) { + FMap.Append(Values); +} + +size_t IniSectionData::GetCount() { + return FMap.Count(); +} diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/share/native/libapplauncher/IniFile.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/share/native/libapplauncher/IniFile.h Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2015, 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. + */ + +#ifndef INIFILE_H +#define INIFILE_H + +#include "Platform.h" +#include "OrderedMap.h" + +#include + + +class IniSectionData : public IPropertyContainer { +private: + OrderedMap FMap; + +public: + IniSectionData(); + IniSectionData(OrderedMap Values); + + std::vector GetKeys(); + std::list GetLines(); + OrderedMap GetData(); + + bool SetValue(const TString Key, TString Value); + void Append(OrderedMap Values); + + virtual bool GetValue(const TString Key, TString& Value); + virtual size_t GetCount(); +}; + + +class IniFile : public ISectionalPropertyContainer { +private: + OrderedMap 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 Values); + bool SetValue(const TString SectionName, + const TString Key, TString Value); + + // ISectionalPropertyContainer + virtual bool GetSection(const TString SectionName, + OrderedMap &Data); + virtual bool ContainsSection(const TString SectionName); + virtual bool GetValue(const TString SectionName, + const TString Key, TString& Value); +}; + +#endif // INIFILE_H diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/share/native/libapplauncher/JavaTypes.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/share/native/libapplauncher/JavaTypes.cpp Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,161 @@ +/* + * Copyright (c) 2014, 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. + */ + +#include "JavaTypes.h" +#include "PlatformString.h" + +#include + + +#ifdef DEBUG +TString JavaException::CreateExceptionMessage(JNIEnv* Env, + jthrowable Exception, jmethodID GetCauseMethod, + jmethodID GetStackTraceMethod, jmethodID ThrowableToTStringMethod, + jmethodID FrameToTStringMethod) { + + TString result; + jobjectArray frames = + (jobjectArray)Env->CallObjectMethod(Exception, GetStackTraceMethod); + + // Append Throwable.toTString(). + if (0 != frames) { + jstring jstr = (jstring)Env->CallObjectMethod(Exception, + ThrowableToTStringMethod); + const char* str = Env->GetStringUTFChars(jstr, 0); + result += PlatformString(str).toPlatformString(); + Env->ReleaseStringUTFChars(jstr, str); + Env->DeleteLocalRef(jstr); + } + + // Append stack trace if one exists. + if (Env->GetArrayLength(frames) > 0) { + jsize i = 0; + + for (i = 0; i < Env->GetArrayLength(frames); i++) { + // Get the string from the next frame and append it to + // the error message. + jobject frame = Env->GetObjectArrayElement(frames, i); + jstring obj = (jstring)Env->CallObjectMethod(frame, + FrameToTStringMethod); + const char* str = Env->GetStringUTFChars(obj, 0); + result += _T("\n "); + result += PlatformString(str).toPlatformString(); + Env->ReleaseStringUTFChars(obj, str); + Env->DeleteLocalRef(obj); + Env->DeleteLocalRef(frame); + } + } + + // If Exception has a cause then append the stack trace messages. + if (0 != frames) { + jthrowable cause = + (jthrowable)Env->CallObjectMethod(Exception, GetCauseMethod); + + if (cause != NULL) { + result += CreateExceptionMessage(Env, cause, GetCauseMethod, + GetStackTraceMethod, ThrowableToTStringMethod, + FrameToTStringMethod); + } + } + + return result; +} +#endif //DEBUG + +JavaException::JavaException() : Exception() {} + +//#ifdef WINDOWS +JavaException::JavaException(JNIEnv *Env, + const TString Message) : Exception(Message) { +//#endif //WINDOWS +//#ifdef POSIX +//JavaException::JavaException(JNIEnv *Env, TString message) { +//#endif //POSIX + + FEnv = Env; + FException = Env->ExceptionOccurred(); + Env->ExceptionClear(); + +#ifdef DEBUG + Platform& platform = Platform::GetInstance(); + + if (platform.GetDebugState() == dsNone) { + jclass ThrowableClass = Env->FindClass("java/lang/Throwable"); + + if (FEnv->ExceptionCheck() == JNI_TRUE) { + Env->ExceptionClear(); + return; + } + + jmethodID GetCauseMethod = Env->GetMethodID(ThrowableClass, + "getCause", "()Ljava/lang/Throwable;"); + + if (FEnv->ExceptionCheck() == JNI_TRUE) { + Env->ExceptionClear(); + return; + } + + jmethodID GetStackTraceMethod = Env->GetMethodID(ThrowableClass, + "getStackTrace", "()[Ljava/lang/StackTraceElement;"); + + if (FEnv->ExceptionCheck() == JNI_TRUE) { + Env->ExceptionClear(); + return; + } + + jmethodID ThrowableToTStringMethod = Env->GetMethodID(ThrowableClass, + "toString", "()Ljava/lang/String;"); + + if (FEnv->ExceptionCheck() == JNI_TRUE) { + Env->ExceptionClear(); + return; + } + + jclass FrameClass = Env->FindClass("java/lang/StackTraceElement"); + + if (FEnv->ExceptionCheck() == JNI_TRUE) { + Env->ExceptionClear(); + return; + } + + jmethodID FrameToTStringMethod = Env->GetMethodID(FrameClass, + "toString", "()Ljava/lang/String;"); + + if (FEnv->ExceptionCheck() == JNI_TRUE) { + Env->ExceptionClear(); + return; + } + + TString lmessage = CreateExceptionMessage(Env, FException, + GetCauseMethod, GetStackTraceMethod, ThrowableToTStringMethod, + FrameToTStringMethod); + SetMessage(lmessage); + } +#endif //DEBUG +} + +void JavaException::Rethrow() { + FEnv->Throw(FException); +} diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/share/native/libapplauncher/JavaTypes.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/share/native/libapplauncher/JavaTypes.h Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2014, 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. + */ + +#ifndef JAVATYPES_H +#define JAVATYPES_H + +#include "Platform.h" +#include "Messages.h" + +#include "jni.h" + +class JavaException : public Exception { +// Prohibit Heap-Based Classes. +private: + static void *operator new(size_t size); + +private: +#ifdef DEBUG + static TString CreateExceptionMessage(JNIEnv* Env, jthrowable Exception, + jmethodID GetCauseMethod, jmethodID GetStackTraceMethod, + jmethodID ThrowableToStringMethod, jmethodID FrameToStringMethod); +#endif // DEBUG + + jthrowable FException; + JNIEnv *FEnv; + +public: + explicit JavaException(); + explicit JavaException(JNIEnv *Env, const TString message); + virtual ~JavaException() throw() {} + + void Rethrow(); +}; + +#endif // JAVATYPES_H diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/share/native/libapplauncher/JavaVirtualMachine.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/share/native/libapplauncher/JavaVirtualMachine.cpp Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,349 @@ +/* + * Copyright (c) 2014, 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. + */ + +#include "JavaVirtualMachine.h" +#include "Platform.h" +#include "PlatformString.h" +#include "FilePath.h" +#include "Package.h" +#include "JavaTypes.h" +#include "Helpers.h" +#include "Messages.h" +#include "Macros.h" +#include "PlatformThread.h" + +#include "jni.h" + +#include +#include +#include + + +bool RunVM() { + JavaVirtualMachine javavm; + + bool result = javavm.StartJVM(); + + if (!result) { + Platform& platform = Platform::GetInstance(); + platform.ShowMessage(_T("Failed to launch JVM\n")); + } + + return result; +} + +JavaLibrary::JavaLibrary() : Library(), FCreateProc(NULL) { +} + +bool JavaLibrary::JavaVMCreate(size_t argc, char *argv[]) { + if (FCreateProc == NULL) { + FCreateProc = (JVM_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; +} + +//---------------------------------------------------------------------------- + +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 Values) { + std::vector orderedKeys = Values.GetKeys(); + + for (std::vector::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::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 JavaOptions::ToList() { + std::list result; + Macros& macros = Macros::GetInstance(); + + for (std::list::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.AppendValue(_T("-Dapp.preferences.id"), package.GetAppID()); + options.AppendValues(package.GetJVMArgs()); + +#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 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(); + // TODO: Clean this up. Because of bug JDK-8131321 the opening of the + // PE file ails in WindowsPlatform.cpp on the check to + // if (pNTHeader->Signature == IMAGE_NT_SIGNATURE) + TString libName = package.GetJVMLibraryFileName(); +#ifdef _WIN64 + if (FilePath::FileExists(_T("msvcr100.dll")) == true) { + javaLibrary.AddDependency(_T("msvcr100.dll")); + } + + TString runtimeBin = platform.GetPackageRuntimeBinDirectory(); + SetDllDirectory(runtimeBin.c_str()); +#else + javaLibrary.AddDependencies( + platform.FilterOutRuntimeDependenciesForPlatform( + platform.GetLibraryImports(libName))); +#endif + javaLibrary.Load(libName); +} + +bool JavaVirtualMachine::launchVM(JavaOptions& options, + std::list& 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 loptions = options.ToList(); + vmargs.splice(vmargs.end(), loptions, + loptions.begin(), loptions.end()); + } +#else + std::list loptions = options.ToList(); + vmargs.splice(vmargs.end(), loptions, loptions.begin(), loptions.end()); +#endif + + std::list largs = package.GetArgs(); + vmargs.splice(vmargs.end(), largs, largs.begin(), largs.end()); + + size_t argc = vmargs.size(); + DynamicBuffer argv(argc + 1); + if (argv.GetData() == NULL) { + return false; + } + + unsigned int index = 0; + for (std::list::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; +} diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/share/native/libapplauncher/JavaVirtualMachine.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/share/native/libapplauncher/JavaVirtualMachine.h Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2014, 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. + */ + +#ifndef JAVAVIRTUALMACHINE_H +#define JAVAVIRTUALMACHINE_H + + +#include "jni.h" +#include "Platform.h" + +struct JavaOptionItem { + TString name; + TString value; + void* extraInfo; +}; + +class JavaOptions { +private: + std::list 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 Values); + void ReplaceValue(const TString Key, TString Value); + std::list ToList(); + size_t GetCount(); +}; + +// Private typedef for function pointer casting +#define LAUNCH_FUNC "JLI_Launch" + +typedef int (JNICALL *JVM_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 JavaLibrary : public Library { + JVM_CREATE FCreateProc; + JavaLibrary(const TString &FileName); +public: + JavaLibrary(); + bool JavaVMCreate(size_t argc, char *argv[]); +}; + +class JavaVirtualMachine { +private: + JavaLibrary javaLibrary; + + void configureLibrary(); + bool launchVM(JavaOptions& options, std::list& vmargs); +public: + JavaVirtualMachine(); + ~JavaVirtualMachine(void); + + bool StartJVM(); +}; + +bool RunVM(); + +#endif // JAVAVIRTUALMACHINE_H diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/share/native/libapplauncher/LinuxPlatform.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/share/native/libapplauncher/LinuxPlatform.cpp Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,1181 @@ +/* + * Copyright (c) 2014, 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. + */ + +#include "Platform.h" + +#ifdef LINUX + +#include "JavaVirtualMachine.h" +#include "LinuxPlatform.h" +#include "PlatformString.h" +#include "IniFile.h" +#include "Helpers.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#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(), + GenericPlatform(), PosixPlatform() { + FMainThread = pthread_self(); +} + +LinuxPlatform::~LinuxPlatform(void) { +} + +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 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 filename = GetModuleFileName(); + return FilePath::ExtractFilePath(filename); +} + +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; + } + + if (result->LoadFromFile(FileName) == false) { + // New property file format was not found, + // attempt to load old property file format. + Helpers::LoadOldConfigFile(FileName, result); + } + + return result; +} + +TString LinuxPlatform::GetBundledJVMLibraryFileName(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; +} + +#ifdef DEBUG +bool LinuxPlatform::IsNativeDebuggerPresent() { + // gdb opens file descriptors stdin=3, stdout=4, stderr=5 whereas + // a typical prog uses only stdin=0, stdout=1, stderr=2. + bool result = false; + FILE *fd = fopen("/tmp", "r"); + + if (fileno(fd) > 5) { + result = true; + } + + fclose(fd); + return result; +} + +int LinuxPlatform::GetProcessID() { + int pid = getpid(); + return pid; +} +#endif //DEBUG + +//---------------------------------------------------------------------------- + +#ifndef __UNIX_JPACKAGE_PLATFORM__ +#define __UNIX_JPACKAGE_PLATFORM__ + +/** Provide an abstraction for difference in the platform APIs, + e.g. string manipulation functions, etc. */ +#include +#include +#include +#include + +#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 +#include +#include +#include +#include + + +#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(""), 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("') { + 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); + 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 /* */ +#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 () 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(); + + /* Skip until '>', '/>' or EOF. This should really be an error, */ + /* but we are loose */ +// if(CurTokenType == TOKEN_EMPTY_CLOSE_BRACKET || +// CurTokenType == TOKEN_CLOSE_BRACKET || +// CurTokenType == TOKEN_EOF) { +// println("XML Parsing error: wrong kind of token found"); +// return NULL; +// } + + 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(""), 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(""); + + +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); +} + +namespace { + template + class DllFunction { + const Library& lib; + funcType funcPtr; + std::string theName; + + public: + DllFunction(const Library& library, + const std::string &funcName): lib(library) { + funcPtr = reinterpret_cast(lib.GetProcAddress(funcName)); + if (!funcPtr) { + throw std::runtime_error("Failed to load function \"" + + funcName + "\" from \"" + + library.GetName() + "\" library"); + } + } + + operator funcType() const { + return funcPtr; + } + }; +} // namespace + +extern "C" { +typedef Status (*XInitThreadsFuncPtr)(); +typedef Display* (*XOpenDisplayFuncPtr)(char *display_name); + +typedef Atom (*XInternAtomFuncPtr)( + Display *display, char *atom_name, Bool only_if_exists); + +typedef Window (*XDefaultRootWindowFuncPtr)(Display *display); + +typedef int (*XCloseDisplayFuncPtr)(Display *display); +} + +ProcessReactivator::ProcessReactivator(pid_t pid): _pid(pid) { + const std::string libname = "libX11.so"; + if(!libX11.Load(libname)) { + throw std::runtime_error("Failed to load \"" + libname + "\" library"); + } + + DllFunction XInitThreadsFunc(libX11, "XInitThreads"); + + XInitThreadsFunc(); + + DllFunction XOpenDisplayFunc(libX11, "XOpenDisplay"); + + _display = XOpenDisplayFunc(NULL); + + DllFunction XInternAtomFunc(libX11, "XInternAtom"); + + _atomPid = XInternAtomFunc(_display, (char*)"_NET_WM_PID", True); + + if (_atomPid == None) { + return; + } + + DllFunction XDefaultRootWindowFunc(libX11, + "XDefaultRootWindow"); + + searchWindowHelper(XDefaultRootWindowFunc(_display)); + + reactivateProcess(); + + DllFunction XCloseDisplayFunc(libX11, + "XCloseDisplay"); + + XCloseDisplayFunc(_display); +} + +extern "C" { +typedef int (*XGetWindowPropertyFuncPtr)( + Display *display, Window w, Atom property, long long_offset, + long long_length, Bool d, Atom req_type, Atom *actual_type_return, + int *actual_format_return, unsigned long *nitems_return, + unsigned long *bytes_after_return, unsigned char **prop_return); + +typedef Status (*XQueryTreeFuncPtr)( + Display *display, Window w, Window *root_return, Window *parent_return, + Window **children_return, unsigned int *nchildren_return); + +typedef int (*XFreeFuncPtr)(void *data); +} + +void ProcessReactivator::searchWindowHelper(Window w) { + + DllFunction XGetWindowPropertyFunc(libX11, + "XGetWindowProperty"); + + DllFunction XFreeFunc(libX11, "XFree"); + + Atom type; + int format; + unsigned long num, bytesAfter; + unsigned char* propPid = 0; + if (Success == XGetWindowPropertyFunc(_display, w, _atomPid, 0, 1, + False, XA_CARDINAL, &type, &format, &num, &bytesAfter, &propPid)) { + if (propPid != 0) { + if (_pid == *((pid_t *)propPid)) { + _result.push_back(w); + } + XFreeFunc(propPid); + } + } + + DllFunction XQueryTreeFunc(libX11, "XQueryTree"); + + Window root, parent; + Window* child; + unsigned int numChildren; + if (0 != XQueryTreeFunc(_display, w, &root, + &parent, &child, &numChildren)) { + for (unsigned int i = 0; i < numChildren; i++) { + searchWindowHelper(child[i]); + } + } +} + + +extern "C" { +typedef Status (*XGetWindowAttributesFuncPtr)(Display *display, Window w, + XWindowAttributes *window_attributes_return); + +typedef Status (*XSendEventFuncPtr)(Display *display, Window w, Bool propagate, + long event_mask, XEvent *event_send); + +typedef int (*XRaiseWindowFuncPtr)(Display *display, Window w); +} + +void ProcessReactivator::reactivateProcess() { + + DllFunction XGetWindowAttributesFunc(libX11, + "XGetWindowAttributes"); + + DllFunction XSendEventFunc(libX11, "XSendEvent"); + + DllFunction XRaiseWindowFunc(libX11, "XRaiseWindow"); + + DllFunction XInternAtomFunc(libX11, "XInternAtom"); + + for (std::list::const_iterator it = _result.begin(); + it != _result.end(); it++) { + // try sending an event to activate window, + // after that we can try to raise it. + XEvent xev; + Atom atom = XInternAtomFunc ( + _display, (char*)"_NET_ACTIVE_WINDOW", False); + xev.xclient.type = ClientMessage; + xev.xclient.serial = 0; + xev.xclient.send_event = True; + xev.xclient.display = _display; + xev.xclient.window = *it; + xev.xclient.message_type = atom; + xev.xclient.format = 32; + xev.xclient.data.l[0] = 2; + xev.xclient.data.l[1] = 0; + xev.xclient.data.l[2] = 0; + xev.xclient.data.l[3] = 0; + xev.xclient.data.l[4] = 0; + XWindowAttributes attr; + XGetWindowAttributesFunc(_display, *it, &attr); + XSendEventFunc(_display, attr.root, False, + SubstructureRedirectMask | SubstructureNotifyMask, &xev); + XRaiseWindowFunc(_display, *it); + } +} + + +#endif // LINUX diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/share/native/libapplauncher/LinuxPlatform.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/share/native/libapplauncher/LinuxPlatform.h Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2014, 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. + */ + +#include "Platform.h" + +#ifdef LINUX + +#ifndef LINUXPLATFORM_H +#define LINUXPLATFORM_H + +#include "PosixPlatform.h" +#include "GenericPlatform.h" +#include +#include +#include +#include + + +class LinuxPlatform : virtual public Platform, GenericPlatform, PosixPlatform +{ +private: + pthread_t FMainThread; + +protected: + virtual TString getTmpDirString(); + +public: + LinuxPlatform(void); + virtual ~LinuxPlatform(void); + + 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 GetModuleFileName(); + + virtual TString GetBundledJVMLibraryFileName(TString RuntimePath); + + virtual ISectionalPropertyContainer* GetConfigFile(TString FileName); + + virtual bool IsMainThread(); + virtual TPlatformNumber GetMemorySize(); + +#ifdef DEBUG + virtual bool IsNativeDebuggerPresent(); + virtual int GetProcessID(); +#endif //DEBUG +}; + +class ProcessReactivator { +private: + void searchWindowHelper(Window w); + void reactivateProcess(); + + Library libX11; + + pid_t _pid; + Atom _atomPid; + Display* _display; + std::list _result; +public: + explicit ProcessReactivator(pid_t pid); +}; + +#endif //LINUXPLATFORM_H + +#endif //LINUX diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/share/native/libapplauncher/Lock.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/share/native/libapplauncher/Lock.cpp Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2014, 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. + */ + +#include "Lock.h" + + +Lock::Lock(void) { + Initialize(); +} + +Lock::Lock(bool Value) { + Initialize(); + + if (Value == true) { + Enter(); + } +} + +void Lock::Initialize() { +#ifdef WINDOWS + InitializeCriticalSectionAndSpinCount(&FCriticalSection, 0x00000400); +#endif // WINDOWS +#ifdef MAC + // FMutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER; +#endif // MAC +#ifdef LINUX + // FMutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP; +#endif // LINUX +} + +Lock::~Lock(void) { +#ifdef WINDOWS + DeleteCriticalSection(&FCriticalSection); +#endif // WINDOWS +#ifdef POSIX + pthread_mutex_unlock(&FMutex); +#endif // POSIX +} + +void Lock::Enter() { +#ifdef WINDOWS + EnterCriticalSection(&FCriticalSection); +#endif // WINDOWS +#ifdef POSIX + pthread_mutex_lock(&FMutex); +#endif // POSIX +} + +void Lock::Leave() { +#ifdef WINDOWS + LeaveCriticalSection(&FCriticalSection); +#endif // WINDOWS +#ifdef POSIX + pthread_mutex_unlock(&FMutex); +#endif // POSIX +} + +bool Lock::TryEnter() { + bool result = false; +#ifdef WINDOWS + if (TryEnterCriticalSection (&FCriticalSection) != 0) + result = true; +#endif // WINDOWS +#ifdef POSIX + if (pthread_mutex_lock(&FMutex) == 0) + result = true; +#endif // POSIX + return result; +} diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/share/native/libapplauncher/Lock.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/share/native/libapplauncher/Lock.h Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2014, 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. + */ + +#ifndef LOCK_H +#define LOCK_H + +#include "Platform.h" + +#ifdef POSIX +#include +#endif //POSIX + + +class Lock { +private: +#ifdef WINDOWS + CRITICAL_SECTION FCriticalSection; +#endif //WINDOWS +#ifdef POSIX + pthread_mutex_t FMutex; +#endif //POSIX + + void Initialize(); + +public: + Lock(void); + Lock(bool Value); + ~Lock(void); + + void Enter(); + void Leave(); + bool TryEnter(); +}; + +#endif // LOCK_H diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/share/native/libapplauncher/MacPlatform.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/share/native/libapplauncher/MacPlatform.h Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2014, 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. + */ + +#include "Platform.h" + +#ifdef MAC + +#ifndef MACPLATFORM_H +#define MACPLATFORM_H + +#include "GenericPlatform.h" +#include "PosixPlatform.h" + + +class MacPlatform : virtual public Platform, GenericPlatform, 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 GetBundledJVMLibraryFileName(TString RuntimePath); + virtual TString GetAppName(); + + virtual ISectionalPropertyContainer* GetConfigFile(TString FileName); + virtual TString GetModuleFileName(); + + virtual bool IsMainThread(); + virtual TPlatformNumber GetMemorySize(); + + virtual std::map GetKeys(); + +#ifdef DEBUG + virtual bool IsNativeDebuggerPresent(); + virtual int GetProcessID(); +#endif // DEBUG +}; + + +#endif // MACPLATFORM_H + +#endif // MAC diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/share/native/libapplauncher/Macros.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/share/native/libapplauncher/Macros.cpp Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2014, 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. + */ + +#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.GetJVMLibraryFileName()); + 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::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::value_type(Key, Value)); +} diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/share/native/libapplauncher/Macros.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/share/native/libapplauncher/Macros.h Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2014, 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. + */ + +#ifndef MACROS_H +#define MACROS_H + +#include "Platform.h" + +#include + + +class Macros { +private: + std::map 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 diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/share/native/libapplauncher/Messages.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/share/native/libapplauncher/Messages.cpp Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2014, 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. + */ + +#include "Messages.h" +#include "Platform.h" +#include "Lock.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() { + //Lock lock; + 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; +} diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/share/native/libapplauncher/Messages.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/share/native/libapplauncher/Messages.h Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2014, 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. + */ + +#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 diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/share/native/libapplauncher/OrderedMap.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/share/native/libapplauncher/OrderedMap.h Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,250 @@ +/* + * Copyright (c) 2015, 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. + */ + +#ifndef ORDEREDMAP_H +#define ORDEREDMAP_H + +#ifdef WINDOWS +#pragma warning(disable:4522) +#endif + +#include +#include +#include +#include + +#include + + +template +struct pair +{ + typedef _T1 first_type; + typedef _T2 second_type; + + first_type first; + second_type second; + + pair(first_type Value1, second_type Value2) { + first = Value1; + second = Value2; + } +}; + + +template +class OrderedMap { +public: + typedef TKey key_type; + typedef TValue mapped_type; + typedef pair container_type; + typedef typename std::vector::iterator iterator; + typedef typename std::vector::const_iterator const_iterator; + +private: + typedef std::map map_type; + typedef std::vector 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 &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 GetKeys() { + std::vector 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 &Value) { + Clear(); + Append(Value); + } + + void Append(const OrderedMap &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, 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; + } + + OrderedMap& operator= (const OrderedMap &Value) { + Clear(); + Append(Value); + return *this; + } + + size_t Count() { + return FList.size(); + } +}; + +#endif // ORDEREDMAP_H diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/share/native/libapplauncher/Package.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/share/native/libapplauncher/Package.cpp Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,564 @@ +/* + * Copyright (c) 2014, 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. + */ + +#include "Package.h" +#include "Lock.h" +#include "Helpers.h" +#include "Macros.h" +#include "IniFile.h" + +#include + + +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 keys = platform.GetKeys(); + + // Read from configure.cfg/Info.plist + AutoFreePtr config = + platform.GetConfigFile(platform.GetConfigFileName()); + + config->GetValue(keys[CONFIG_SECTION_APPLICATION], + keys[CONFIG_APP_ID_KEY], FBootFields->FAppID); + 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[JVM_RUNTIME_KEY], FBootFields->FJVMRuntimeDirectory); + + // Read jvmargs. + PromoteAppCDSState(config); + ReadJVMArgs(config); + + // Read args if none were passed in. + if (FBootFields->FArgs.size() == 0) { + OrderedMap 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 "AppCDSJVMOptions" section is present +// -> cdsAuto If "AppCDSJVMOptions" section is present and +// app.appcds.cache=auto +// -> cdsDisabled Default +// +void Package::PromoteAppCDSState(ISectionalPropertyContainer* Config) { + Platform& platform = Platform::GetInstance(); + std::map 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_APPCDSJVMOPTIONS]) == 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::ReadJVMArgs(ISectionalPropertyContainer* Config) { + Platform& platform = Platform::GetInstance(); + std::map 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_JVMOPTIONS], + FBootFields->FJVMArgs); + break; + } + + case cdsGenCache: { + Config->GetSection(keys[ + CONFIG_SECTION_APPCDSGENERATECACHEJVMOPTIONS], + FBootFields->FJVMArgs); + break; + } + + case cdsAuto: + case cdsEnabled: { + if (Config->GetValue(keys[CONFIG_SECTION_APPCDSJVMOPTIONS], + _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(Config); + + if (iniConfig != NULL) { + FBootFields->FAppCDSCacheFileName = + FilePath::FixPathForPlatform( + FBootFields->FAppCDSCacheFileName); + iniConfig->SetValue(keys[ + CONFIG_SECTION_APPCDSJVMOPTIONS], + _T( "-XX:SharedArchiveFile"), + FBootFields->FAppCDSCacheFileName); + } + } + + Config->GetSection(keys[CONFIG_SECTION_APPCDSJVMOPTIONS], + FBootFields->FJVMArgs); + } + + break; + } + } +} + +void Package::SetCommandLineArguments(int argc, TCHAR* argv[]) { + if (argc > 0) { + std::list 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 Package::GetJVMArgs() { + return FBootFields->FJVMArgs; +} + +std::vector GetKeysThatAreNotDuplicates(OrderedMap &Defaults, OrderedMap &Overrides) { + std::vector result; + std::vector 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 CreateOrderedMapFromKeyList(OrderedMap &Map, std::vector &Keys) { + OrderedMap 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 GetKeysThatAreNotOverridesOfDefaultValues( + OrderedMap &Defaults, OrderedMap &Overrides) { + std::vector result; + std::vector 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 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::GetAppID() { + assert(FBootFields != NULL); + return FBootFields->FAppID; +} + +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::GetJVMLibraryFileName() { + assert(FBootFields != NULL); + + if (FBootFields->FJVMLibraryFileName.empty() == true) { + Platform& platform = Platform::GetInstance(); + Macros& macros = Macros::GetInstance(); + TString jvmRuntimePath = macros.ExpandMacros(GetJVMRuntimeDirectory()); + FBootFields->FJVMLibraryFileName = + platform.GetBundledJVMLibraryFileName(jvmRuntimePath); + } + + return FBootFields->FJVMLibraryFileName; +} + +TString Package::GetJVMRuntimeDirectory() { + assert(FBootFields != NULL); + return FBootFields->FJVMRuntimeDirectory; +} + +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; +} diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/share/native/libapplauncher/Package.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/share/native/libapplauncher/Package.h Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2014, 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. + */ + +#ifndef PACKAGE_H +#define PACKAGE_H + + +#include "Platform.h" +#include "PlatformString.h" +#include "FilePath.h" +#include "PropertyFile.h" + +#include +#include + +class PackageBootFields { +public: + enum MemoryState {msManual, msAuto}; + +public: + OrderedMap FJVMArgs; + std::list FArgs; + + TString FPackageRootDirectory; + TString FPackageAppDirectory; + TString FPackageLauncherDirectory; + TString FAppDataDirectory; + TString FAppID; + TString FPackageAppDataDirectory; + TString FClassPath; + TString FModulePath; + TString FMainJar; + TString FMainModule; + TString FMainClassName; + TString FJVMRuntimeDirectory; + TString FJVMLibraryFileName; + 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 ReadJVMArgs(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 GetJVMArgs(); + TString GetMainModule(); + + std::list GetArgs(); + + TString GetPackageRootDirectory(); + TString GetPackageAppDirectory(); + TString GetPackageLauncherDirectory(); + TString GetAppDataDirectory(); + + TString GetAppCDSCacheDirectory(); + TString GetAppCDSCacheFileName(); + + TString GetAppID(); + TString GetPackageAppDataDirectory(); + TString GetClassPath(); + TString GetModulePath(); + TString GetMainClassName(); + TString GetJVMLibraryFileName(); + TString GetJVMRuntimeDirectory(); + TString GetSplashScreenFileName(); + bool HasSplashScreen(); + TString GetCommandName(); + + TPlatformNumber GetMemorySize(); + PackageBootFields::MemoryState GetMemoryState(); + + DebugState Debugging(); +}; + +#endif // PACKAGE_H diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/share/native/libapplauncher/Platform.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/share/native/libapplauncher/Platform.cpp Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,181 @@ +/* + * Copyright (c) 2014, 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. + */ + +#include "Platform.h" +#include "Lock.h" +#include "Messages.h" + +#include "WindowsPlatform.h" +#include "LinuxPlatform.h" +#include "MacPlatform.h" + + +// Environment +StaticReadProperty Environment::NewLine; + + +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; +} + + +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(); + } + + if (FDependenciesLibraries == NULL) { + FDependenciesLibraries = new std::vector(); + } +} + +void Library::LoadDependencies() { + if (FDependentLibraryNames != NULL && FDependenciesLibraries != NULL) { + for (std::vector::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::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 &Dependencies) { + if (Dependencies.size() > 0) { + InitializeDependencies(); + + if (FDependentLibraryNames != NULL) { + for (std::vector::const_iterator iterator = + FDependentLibraryNames->begin(); + iterator != FDependentLibraryNames->end(); iterator++) { + TString fileName = *iterator; + AddDependency(fileName); + } + } + } +} diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/share/native/libapplauncher/Platform.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/share/native/libapplauncher/Platform.h Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,524 @@ +/* + * Copyright (c) 2014, 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. + */ + +#ifndef PLATFORM_H +#define PLATFORM_H + +#include "OrderedMap.h" + +#include +#include +#include +#include +#include +#include +#include + + +#ifdef WIN32 +#ifndef WINDOWS +#define WINDOWS +#endif +#endif //WIN32 + +#ifdef __APPLE__ +#define MAC +#define POSIX +#endif //__APPLE__ + + +#ifdef __linux +#ifndef LINUX +#define LINUX +#endif +#endif //__linux + +#ifdef LINUX +#define POSIX +#endif //LINUX + + + +#ifdef WINDOWS +// Define Windows compatibility requirements XP or later +#define WINVER 0x0600 +#define _WIN32_WINNT 0x0600 + +#include +#include +#include +#include +#include +#include + +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; + +#endif //WINDOWS + + +#ifdef POSIX +#include +#include +#include +#include +#include + +#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* +#endif //POSIX + + +// Config file sections +#define CONFIG_SECTION_APPLICATION _T("CONFIG_SECTION_APPLICATION") +#define CONFIG_SECTION_JVMOPTIONS _T("CONFIG_SECTION_JVMOPTIONS") +#define CONFIG_SECTION_APPCDSJVMOPTIONS _T("CONFIG_SECTION_APPCDSJVMOPTIONS") +#define CONFIG_SECTION_ARGOPTIONS _T("CONFIG_SECTION_ARGOPTIONS") +#define CONFIG_SECTION_APPCDSGENERATECACHEJVMOPTIONS \ + _T("CONFIG_SECTION_APPCDSGENERATECACHEJVMOPTIONS") + +// 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_ID_KEY _T("CONFIG_APP_ID_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 JVM_RUNTIME_KEY _T("JVM_RUNTIME_KEY") +#define JPACKAGE_APP_DATA_DIR _T("CONFIG_APP_IDENTIFIER") + + + +typedef void* Module; +typedef void* Procedure; + + +template +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 +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 +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 +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 +class StaticReadProperty { +public: + StaticReadProperty() { + } + + // The Property class is treated as the internal type which is the getter. + operator ValueType() { + return (*getter)(); + } +}; + +template +class StaticWriteProperty { +public: + StaticWriteProperty() { + } + + // To set the value using the set method. + ValueType operator =(const ValueType& Value) { + (*setter)(Value); + return Value; + } +}; + + +class Process { +protected: + std::list 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 Arguments, bool AWait = false) = 0; + virtual bool Wait() = 0; + virtual TProcessID GetProcessID() = 0; + + virtual std::list GetOutput() { return FOutput; } + virtual void SetInput(TString Value) = 0; + + ReadProperty, &Process::GetOutput> Output; + WriteProperty Input; +}; + + +template +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; + } +}; + + +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 &Data) = 0; +}; + +class Environment { +private: + Environment() { + } + +public: + static TString GetNewLine() { +#ifdef WINDOWS + return _T("\r\n"); +#endif //WINDOWS +#ifdef POSIX + return _T("\n"); +#endif //POSIX + } + + static StaticReadProperty NewLine; +}; + + +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\\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() = 0; + + virtual TString GetBundledJVMLibraryFileName(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; + virtual std::vector GetLibraryImports(const TString FileName) = 0; + virtual std::vector FilterOutRuntimeDependenciesForPlatform( + std::vector Imports) = 0; + + // Caller must free result. + virtual Process* CreateProcess() = 0; + + virtual bool IsMainThread() = 0; + + // Returns megabytes. + virtual TPlatformNumber GetMemorySize() = 0; + + virtual std::map GetKeys() = 0; + + virtual std::list LoadFromFile(TString FileName) = 0; + virtual void SaveToFile(TString FileName, + std::list Contents, bool ownerOnly) = 0; + + virtual TString GetTempDirectory() = 0; + +#ifdef DEBUG + virtual DebugState GetDebugState() = 0; + virtual int GetProcessID() = 0; + virtual bool IsNativeDebuggerPresent() = 0; +#endif //DEBUG +}; + + +class Library { +private: + std::vector *FDependentLibraryNames; + std::vector *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 &Dependencies); +}; + + +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; } +}; + +class FileNotFoundException: public Exception { +public: + explicit FileNotFoundException(const TString Message) : Exception(Message) {} +}; + +#endif // PLATFORM_H diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/share/native/libapplauncher/PlatformString.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/share/native/libapplauncher/PlatformString.cpp Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,400 @@ +/* + * Copyright (c) 2014, 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. + */ + +#include "PlatformString.h" + +#include "JavaTypes.h" +#include "Helpers.h" + +#include +#include +#include +#include +#include +#include + +#include "jni.h" + +#ifdef MAC +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; +} +#endif //MAC + +#ifdef MAC +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; +} +#endif //MAC + + +void PlatformString::initialize() { + FWideTStringToFree = NULL; + FLength = 0; + FData = NULL; +} + +void PlatformString::CopyString(char *Destination, + size_t NumberOfElements, const char *Source) { +#ifdef WINDOWS + strcpy_s(Destination, NumberOfElements, Source); +#endif //WINDOWS +#ifdef POSIX + strncpy(Destination, Source, NumberOfElements); +#endif //POSIX + + if (NumberOfElements > 0) { + Destination[NumberOfElements - 1] = '\0'; + } +} + +void PlatformString::CopyString(wchar_t *Destination, + size_t NumberOfElements, const wchar_t *Source) { +#ifdef WINDOWS + wcscpy_s(Destination, NumberOfElements, Source); +#endif //WINDOWS +#ifdef POSIX + wcsncpy(Destination, Source, NumberOfElements); +#endif //POSIX + + if (NumberOfElements > 0) { + Destination[NumberOfElements - 1] = '\0'; + } +} + +PlatformString::PlatformString(void) { + initialize(); +} + +PlatformString::~PlatformString(void) { + if (FData != NULL) { + delete[] FData; + } + + if (FWideTStringToFree != NULL) { + delete[] FWideTStringToFree; + } +} + +// Owner must free the return value. +MultibyteString PlatformString::WideStringToMultibyteString( + const wchar_t* value) { + MultibyteString result; + size_t count = 0; + + if (value == NULL) { + return result; + } + +#ifdef WINDOWS + 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); +#endif //WINDOWS + +#ifdef POSIX + 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); +#endif //POSIX + } + + return result; +} + +// Owner must free the return value. +WideString PlatformString::MultibyteStringToWideString(const char* value) { + WideString result; + size_t count = 0; + + if (value == NULL) { + return result; + } + +#ifdef WINDOWS + 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); +#endif // WINDOWS +#ifdef POSIX + 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); +#endif //POSIX + } + + return result; +} + +PlatformString::PlatformString(const PlatformString &value) { + initialize(); + FLength = value.FLength; + FData = new char[FLength + 1]; + PlatformString::CopyString(FData, FLength + 1, value.FData); +} + +PlatformString::PlatformString(const char* value) { + initialize(); + FLength = strlen(value); + FData = new char[FLength + 1]; + PlatformString::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]; + PlatformString::CopyString(FData, FLength + 1, s.c_str()); +} + +PlatformString::PlatformString(const wchar_t* value) { + initialize(); + MultibyteString temp = 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]; + PlatformString::CopyString(FData, FLength + 1, lvalue); +} + +PlatformString::PlatformString(const std::wstring &value) { + initialize(); + const wchar_t* lvalue = value.data(); + MultibyteString temp = WideStringToMultibyteString(lvalue); + FLength = temp.length; + FData = temp.data; +} + +PlatformString::PlatformString(JNIEnv *env, jstring value) { + initialize(); + + if (env != NULL) { + const char* lvalue = env->GetStringUTFChars(value, JNI_FALSE); + + if (lvalue == NULL || env->ExceptionCheck() == JNI_TRUE) { + throw JavaException(); + } + + if (lvalue != NULL) { + FLength = env->GetStringUTFLength(value); + + if (env->ExceptionCheck() == JNI_TRUE) { + throw JavaException(); + } + + FData = new char[FLength + 1]; + PlatformString::CopyString(FData, FLength + 1, lvalue); + + env->ReleaseStringUTFChars(value, lvalue); + + if (env->ExceptionCheck() == JNI_TRUE) { + throw JavaException(); + } + } + } +} + +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 = 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; +} + +jstring PlatformString::toJString(JNIEnv *env) { + jstring result = NULL; + + if (env != NULL) { + result = env->NewStringUTF(c_str()); + + if (result == NULL || env->ExceptionCheck() == JNI_TRUE) { + throw JavaException(); + } + } + + 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]; + PlatformString::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]; + PlatformString::CopyString(result, length + 1, Value); + return result; +} diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/share/native/libapplauncher/PlatformString.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/share/native/libapplauncher/PlatformString.h Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,220 @@ +/* + * Copyright (c) 2014, 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. + */ + +#ifndef PLATFORMSTRING_H +#define PLATFORMSTRING_H + + +#include +#include +#include +#include + +#include "jni.h" +#include "Platform.h" + + +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; } +}; + + +template +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]; + } +}; + + +#ifdef MAC +// 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 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 //MAC + +#ifdef LINUX +#define StringToFileSystemString PlatformString +#define FileSystemStringToString PlatformString +#endif //LINUX + + +class PlatformString { +private: + char* FData; // Stored as UTF-8 + size_t FLength; + wchar_t* FWideTStringToFree; + + void initialize(); + + // 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); + +// 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(JNIEnv *env, jstring 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(); + jstring toJString(JNIEnv *env); + 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 diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/share/native/libapplauncher/PlatformThread.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/share/native/libapplauncher/PlatformThread.cpp Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2014, 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. + */ + +#include "PlatformThread.h" + + +PlatformThread::PlatformThread(void) { +} + +PlatformThread::~PlatformThread(void) { + Wait(); + Terminate(); +} + +#ifdef WINDOWS +DWORD WINAPI PlatformThread::Do(LPVOID Data) { + PlatformThread* self = (PlatformThread*)Data; + self->Execute(); + return 0; +} +#endif // WINDOWS +#ifdef POSIX +void* PlatformThread::Do(void *Data) { + PlatformThread* self = (PlatformThread*)Data; + self->Execute(); + pthread_exit(NULL); +} +#endif // POSIX + +void PlatformThread::Run() { +#ifdef WINDOWS + FHandle = CreateThread(NULL, 0, Do, this, 0, &FThreadID); +#endif // WINDOWS +#ifdef POSIX + pthread_create(&FHandle, NULL, Do, this); +#endif // POSIX +} + +void PlatformThread::Terminate() { +#ifdef WINDOWS + CloseHandle(FHandle); +#endif // WINDOWS +#ifdef POSIX + pthread_cancel(FHandle); +#endif // POSIX +} + +void PlatformThread::Wait() { +#ifdef WINDOWS + WaitForSingleObject(FHandle, INFINITE); +#endif // WINDOWS +#ifdef POSIX + pthread_join(FHandle, NULL); +#endif // POSIX +} diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/share/native/libapplauncher/PlatformThread.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/share/native/libapplauncher/PlatformThread.h Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2014, 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. + */ + +#include "Platform.h" + +#ifndef PLATFORMTHREAD_H +#define PLATFORMTHREAD_H + +#ifdef POSIX +#include +#endif // POSIX + + +class PlatformThread { +private: +#ifdef WINDOWS + HANDLE FHandle; + DWORD FThreadID; + static DWORD WINAPI Do(LPVOID lpParam); +#endif // WINDOWS +#ifdef POSIX + pthread_t FHandle; + static void* Do(void *threadid); +#endif // POSIX + +protected: + // Never call directly. Override this method and this is your code + // that runs in a thread. + virtual void Execute() = 0; + +public: + PlatformThread(void); + virtual ~PlatformThread(void); + + void Run(); + void Terminate(); + void Wait(); +}; + +#endif // PLATFORMTHREAD_H diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/share/native/libapplauncher/PosixPlatform.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/share/native/libapplauncher/PosixPlatform.cpp Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,374 @@ +/* + * Copyright (c) 2014, 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. + */ + +#include "PosixPlatform.h" + +#ifdef POSIX + +#include "PlatformString.h" +#include "FilePath.h" +#include "Helpers.h" + +#include +#include +#include +#include +#include +#include +#include +#ifdef LINUX +#include +#endif +#include +#include +#include +#include +#include +#include +#include + + +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)); +} + +std::vector PosixPlatform::GetLibraryImports( + const TString FileName) { + std::vector result; + return result; +} + +std::vector PosixPlatform::FilterOutRuntimeDependenciesForPlatform( + std::vector Imports) { + std::vector result; + return result; +} + +Process* PosixPlatform::CreateProcess() { + return new PosixProcess(); +} + +PosixProcess::PosixProcess() : Process() { + FChildPID = 0; + FRunning = false; + FOutputHandle = 0; + FInputHandle = 0; +} + +PosixProcess::~PosixProcess() { + Terminate(); +} + +void PosixProcess::Cleanup() { + if (FOutputHandle != 0) { + close(FOutputHandle); + FOutputHandle = 0; + } + + if (FInputHandle != 0) { + close(FInputHandle); + FInputHandle = 0; + } + +#ifdef MAC + sigaction(SIGINT, &savintr, (struct sigaction *)0); + sigaction(SIGQUIT, &savequit, (struct sigaction *)0); + sigprocmask(SIG_SETMASK, &saveblock, (sigset_t *)0); +#endif //MAC +} + +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 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; +} + +#define PIPE_READ 0 +#define PIPE_WRITE 1 + +bool PosixProcess::Execute(const TString Application, + const std::vector 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; +#ifdef MAC + 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); +#endif // MAC + 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::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; +} + +bool PosixProcess::Wait() { + bool result = false; + + int status = 0; + pid_t wpid = 0; + +#ifdef LINUX + wpid = wait(&status); +#endif +#ifdef MAC + wpid = wait(&status); +#endif + + 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 PosixProcess::GetOutput() { + ReadOutput(); + return Process::GetOutput(); +} + +#endif // POSIX diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/share/native/libapplauncher/PosixPlatform.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/share/native/libapplauncher/PosixPlatform.h Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2014, 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. + */ + +#include "Platform.h" + +#ifdef POSIX + +#ifndef POSIXPLATFORM_H +#define POSIXPLATFORM_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 std::vector GetLibraryImports(const TString FileName); + virtual std::vector FilterOutRuntimeDependenciesForPlatform( + std::vector Imports); + + virtual Process* CreateProcess(); + virtual TString GetTempDirectory(); +}; + + +class PosixProcess : public Process { +private: + pid_t FChildPID; + sigset_t saveblock; + int FOutputHandle; + int FInputHandle; +#ifdef MAC + struct sigaction savintr, savequit; +#endif //MAC + 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 Arguments, bool AWait = false); + virtual bool Wait(); + virtual TProcessID GetProcessID(); + virtual void SetInput(TString Value); + virtual std::list GetOutput(); +}; + +#endif // POSIXPLATFORM_H +#endif // POSX diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/share/native/libapplauncher/PropertyFile.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/share/native/libapplauncher/PropertyFile.cpp Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,168 @@ +/* + * Copyright (c) 2014, 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. + */ + +#include "PropertyFile.h" + +#include "Helpers.h" +#include "FilePath.h" + +#include + + +PropertyFile::PropertyFile(void) : IPropertyContainer() { + FReadOnly = false; + FModified = false; +} + +PropertyFile::PropertyFile(const TString FileName) : IPropertyContainer() { + FReadOnly = true; + FModified = false; + LoadFromFile(FileName); +} + +PropertyFile::PropertyFile(OrderedMap Value) { + FData.Append(Value); +} + +PropertyFile::PropertyFile(const 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 contents = platform.LoadFromFile(FileName); + + if (contents.empty() == false) { + for (std::list::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 contents; + std::vector 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 PropertyFile::GetData() { + return FData; +} diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/share/native/libapplauncher/PropertyFile.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/share/native/libapplauncher/PropertyFile.h Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2014, 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. + */ + +#ifndef PROPERTYFILE_H +#define PROPERTYFILE_H + +#include "Platform.h" +#include "Helpers.h" + + +class PropertyFile : public IPropertyContainer { +private: + bool FReadOnly; + bool FModified; + OrderedMap FData; + + void SetModified(bool Value); + +public: + PropertyFile(void); + PropertyFile(const TString FileName); + PropertyFile(OrderedMap Value); + PropertyFile(const PropertyFile &Value); + virtual ~PropertyFile(void); + + bool IsModified(); + bool GetReadOnly(); + void SetReadOnly(bool Value); + + //void Assign(std::map 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 GetData(); + + // IPropertyContainer + virtual bool GetValue(const TString Key, TString& Value); + virtual size_t GetCount(); + // virtual std::vector GetKeys(); +}; + +#endif // PROPERTYFILE_H diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/share/native/libapplauncher/WindowsPlatform.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/share/native/libapplauncher/WindowsPlatform.cpp Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,731 @@ +/* + * Copyright (c) 2014, 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. + */ + +#include "Platform.h" + +#ifdef WINDOWS + +#include "JavaVirtualMachine.h" +#include "WindowsPlatform.h" +#include "Package.h" +#include "Helpers.h" +#include "PlatformString.h" +#include "Macros.h" + +#include +#include +#include + +#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 GetKeys() { + std::list result; + DWORD count; + + if (RegQueryInfoKey(FOpenKey, NULL, NULL, NULL, NULL, NULL, NULL, + &count, NULL, NULL, NULL, NULL) == ERROR_SUCCESS) { + + DWORD length = 255; + DynamicBuffer 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 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(), GenericPlatform() { + FMainThread = ::GetCurrentThreadId(); +} + +WindowsPlatform::~WindowsPlatform(void) { +} + +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 filename = GetModuleFileName(); + return FilePath::ExtractFilePath(filename); +} + +TString WindowsPlatform::GetAppDataDirectory() { + TString result; + TCHAR path[MAX_PATH]; + + if (SHGetFolderPath(NULL, CSIDL_APPDATA, NULL, 0, path) == S_OK) { + result = path; + } + + 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::GetBundledJVMLibraryFileName(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; + } + + if (result->LoadFromFile(FileName) == false) { + // New property file format was not found, + // attempt to load old property file format. + Helpers::LoadOldConfigFile(FileName, result); + } + + return result; +} + +TString WindowsPlatform::GetModuleFileName() { + TString result; + DynamicBuffer buffer(MAX_PATH); + if (buffer.GetData() == NULL) { + return result; + } + + ::GetModuleFileName(NULL, buffer.GetData(), + static_cast(buffer.GetSize())); + + while (ERROR_INSUFFICIENT_BUFFER == GetLastError()) { + if (!buffer.Resize(buffer.GetSize() * 2)) { + return result; + } + ::GetModuleFileName(NULL, buffer.GetData(), + static_cast(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 WindowsPlatform::GetLibraryImports( + const TString FileName) { + std::vector result; + WindowsLibrary library(FileName); + result = library.GetImports(); + return result; +} + +std::vector FilterList(std::vector &Items, + std::wregex Pattern) { + std::vector result; + + for (std::vector::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; +} + +std::vector WindowsPlatform::FilterOutRuntimeDependenciesForPlatform( + std::vector Imports) { + std::vector result; + Package& package = Package::GetInstance(); + Macros& macros = Macros::GetInstance(); + TString runtimeDir = macros.ExpandMacros(package.GetJVMRuntimeDirectory()); + std::vector filelist = FilterList(Imports, + std::wregex(_T("MSVCR.*.DLL"), std::regex_constants::icase)); + + for (std::vector::iterator it = filelist.begin(); + it != filelist.end(); ++it) { + TString filename = *it; + TString msvcr100FileName = FilePath::IncludeTrailingSeparator( + runtimeDir) + _T("jre\\bin\\") + filename; + + if (FilePath::FileExists(msvcr100FileName) == true) { + result.push_back(msvcr100FileName); + break; + } + else { + msvcr100FileName = FilePath::IncludeTrailingSeparator(runtimeDir) + + _T("bin\\") + filename; + + if (FilePath::FileExists(msvcr100FileName) == true) { + result.push_back(msvcr100FileName); + break; + } + } + } + + return result; +} + +Process* WindowsPlatform::CreateProcess() { + return new WindowsProcess(); +} + +#ifdef DEBUG +bool WindowsPlatform::IsNativeDebuggerPresent() { + bool result = false; + + if (IsDebuggerPresent() == TRUE) { + result = true; + } + + return result; +} + +int WindowsPlatform::GetProcessID() { + int pid = GetProcessId(GetCurrentProcess()); + return pid; +} +#endif //DEBUG + + +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 WindowsLibrary::GetImports() { + std::vector 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(dwp); // VS2017 - FIXME + } + + return result; +} + +std::vector WindowsLibrary::GetImportsSection(DWORD base, + PIMAGE_NT_HEADERS pNTHeader) { + std::vector 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 WindowsLibrary::DumpPEFile(PIMAGE_DOS_HEADER dosHeader) { + std::vector result; + // all of this is VS2017 - FIXME + DWORD_PTR dwDosHeaders = reinterpret_cast(dosHeader); + DWORD_PTR dwPIHeaders = dwDosHeaders + (DWORD)(dosHeader->e_lfanew); + + PIMAGE_NT_HEADERS pNTHeader = + reinterpret_cast(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 + +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 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::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 WindowsProcess::GetOutput() { + ReadOutput(); + return Process::GetOutput(); +} + +#endif // WINDOWS diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/share/native/libapplauncher/WindowsPlatform.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/share/native/libapplauncher/WindowsPlatform.h Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,190 @@ +/* + * Copyright (c) 2014, 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. + */ + +#include "Platform.h" + +#ifdef WINDOWS + +#ifndef WINDOWSPLATFORM_H +#define WINDOWSPLATFORM_H + +#include "GenericPlatform.h" + +#include + +#pragma warning( push ) +// C4250 - 'class1' : inherits 'class2::member' +#pragma warning( disable : 4250 ) +class WindowsPlatform : virtual public Platform, GenericPlatform { +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 MessageResponse ShowResponseMessage(TString description); + + virtual void SetCurrentDirectory(TString Value); + virtual TString GetPackageRootDirectory(); + virtual TString GetAppDataDirectory(); + virtual TString GetBundledJVMLibraryFileName(TString RuntimePath); + + 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 std::vector GetLibraryImports(const TString FileName); + virtual std::vector FilterOutRuntimeDependenciesForPlatform( + std::vector Imports); + + virtual Process* CreateProcess(); + + virtual bool IsMainThread(); + virtual TPlatformNumber GetMemorySize(); + + virtual TString GetTempDirectory(); + +#ifdef DEBUG + virtual bool IsNativeDebuggerPresent(); + virtual int GetProcessID(); +#endif //DEBUG +}; +#pragma warning( pop ) // C4250 + + +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 GetImportsSection(DWORD base, + PIMAGE_NT_HEADERS pNTHeader); + static std::vector DumpPEFile(PIMAGE_DOS_HEADER dosHeader); + +public: + WindowsLibrary(const TString FileName); + + std::vector 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 Arguments, bool AWait = false); + virtual bool Wait(); + virtual TProcessID GetProcessID(); + virtual void SetInput(TString Value); + virtual std::list GetOutput(); +}; + + + + +#endif // WINDOWSPLATFORM_H + +#endif // WINDOWS diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/share/native/libapplauncher/main.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/share/native/libapplauncher/main.cpp Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,208 @@ +/* + * Copyright (c) 2014, 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. + */ + +#include "Platform.h" +#include "PlatformString.h" +#include "FilePath.h" +#include "PropertyFile.h" +#include "JavaVirtualMachine.h" +#include "Package.h" +#include "PlatformThread.h" +#include "Macros.h" +#include "Messages.h" + + +#ifdef WINDOWS +#include +#endif + + +#include +#include +#include + +/* +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/.cfg for application + launch configuration (package.cfg is property file). + - Load JVM with requested JVM settings (bundled client JVM if availble, + server or installed JVM otherwise). + - Wait for JVM 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 JVM. + See CR 6316197 for more information. +*/ + +extern "C" { + +#ifdef WINDOWS + BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, + LPVOID lpvReserved) { + return true; + } +#endif //WINDOWS + + 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; + } +#ifdef DEBUG + // There is a compiler bug on Mac when overloading + // ShowResponseMessage. + else if (argument == _T("-nativedebug")) { + if (platform.ShowResponseMessage(_T("Test"), + TString(_T("Would you like to debug?\n\nProcessID: ")) + + PlatformString(platform.GetProcessID()).toString()) + == mrOK) { + while (platform.IsNativeDebuggerPresent() == false) { + } + } + } +#endif //DEBUG + } + + // 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 = platform.CreateProcess(); + std::vector 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 FileNotFoundException(message); + } + break; + } + + case cdsUninitialized: { + platform.ShowMessage(_T("Internal Error")); + break; + } + } + + // Run App + result = RunVM(); + } catch (FileNotFoundException &e) { + platform.ShowMessage(e.GetMessage()); + } + + return result; + } + + JNIEXPORT void stop_launcher() { + } +} diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinAppBundler.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinAppBundler.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,313 @@ +/* + * Copyright (c) 2012, 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.internal; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.PrintStream; +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 ICON_ICO = + new StandardBundlerParam<>( + I18N.getString("param.icon-ico.name"), + I18N.getString("param.icon-ico.description"), + "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 params) + throws UnsupportedPlatformException, 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 p) + throws UnsupportedPlatformException, ConfigException { + if (Platform.getPlatform() != Platform.WINDOWS) { + throw new UnsupportedPlatformException(); + } + + 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")); + } + + // validate runtime bit-architectire + testRuntimeBitArchitecture(p); + + return true; + } + + private static void testRuntimeBitArchitecture( + Map params) throws ConfigException { + + if ((BIT_ARCH_64.fetchFrom(params) != + BIT_ARCH_64_RUNTIME.fetchFrom(params))) { + throw new ConfigException( + I18N.getString("error.bit-architecture-mismatch"), + I18N.getString("error.bit-architecture-mismatch.advice")); + } + } + + private static boolean usePredefineAppName(Map p) { + return (PREDEFINED_APP_IMAGE.fetchFrom(p) != null); + } + + private static String appName; + synchronized static String getAppName( + Map 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 = new File( + WIN_APP_IMAGE.fetchFrom(p).toString() + "\\app"); + File [] files = appImageDir.listFiles( + (File dir, String name) -> name.endsWith(".cfg")); + if (files == null || files.length == 0) { + 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 getLauncherName(Map p) { + return getAppName(p) + ".exe"; + } + + public static String getLauncherCfgName(Map p) { + return "app\\" + getAppName(p) +".cfg"; + } + + public boolean bundle(Map p, File outputDirectory) { + return doBundle(p, outputDirectory, false) != null; + } + + File doBundle(Map p, + File outputDirectory, boolean dependentTask) { + if (Arguments.CREATE_JRE_INSTALLER.fetchFrom(p)) { + return doJreBundle(p, outputDirectory, dependentTask); + } else { + return doAppBundle(p, outputDirectory, dependentTask); + } + } + + File doJreBundle(Map p, + File outputDirectory, boolean dependentTask) { + try { + File rootDirectory = createRoot(p, outputDirectory, dependentTask, + APP_NAME.fetchFrom(p), "windowsapp-image-builder"); + AbstractAppImageBuilder appBuilder = new WindowsAppImageBuilder( + APP_NAME.fetchFrom(p), + outputDirectory.toPath()); + File predefined = PREDEFINED_RUNTIME_IMAGE.fetchFrom(p); + if (predefined == null ) { + JLinkBundlerHelper.generateJre(p, appBuilder); + } else { + return predefined; + } + return rootDirectory; + } catch (Exception ex) { + Log.error("Exception: "+ex); + Log.verbose(ex); + return null; + } + } + + File doAppBundle(Map p, + File outputDirectory, boolean dependentTask) { + try { + File rootDirectory = createRoot(p, outputDirectory, dependentTask, + APP_NAME.fetchFrom(p), "windowsapp-image-builder"); + 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 (Exception ex) { + Log.error("Exception: "+ex); + Log.verbose(ex); + return null; + } + } + + private static final String RUNTIME_AUTO_DETECT = ".runtime.autodetect"; + + public static void extractFlagsFromRuntime( + Map params) { + if (params.containsKey(".runtime.autodetect")) return; + + params.put(RUNTIME_AUTO_DETECT, "attempted"); + + String commandline; + File runtimePath = JLinkBundlerHelper.getJDKHome(params).toFile(); + File launcherPath = new File(runtimePath, "bin\\java.exe"); + + ProcessBuilder pb = + new ProcessBuilder(launcherPath.getAbsolutePath(), "-version"); + try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) { + try (PrintStream pout = new PrintStream(baos)) { + IOUtils.exec(pb, Log.isDebug(), true, pout); + } + + commandline = baos.toString(); + } catch (IOException e) { + e.printStackTrace(); + params.put(RUNTIME_AUTO_DETECT, "failed"); + return; + } + + AbstractImageBundler.extractFlagsFromVersion(params, commandline); + params.put(RUNTIME_AUTO_DETECT, "succeeded"); + } + + @Override + public String getName() { + return I18N.getString("app.bundler.name"); + } + + @Override + public String getDescription() { + return I18N.getString("app.bundler.description"); + } + + @Override + public String getID() { + return "windows.app"; + } + + @Override + public String getBundleType() { + return "IMAGE"; + } + + @Override + public Collection> getBundleParameters() { + return getAppBundleParameters(); + } + + public static Collection> getAppBundleParameters() { + return Arrays.asList( + APP_NAME, + APP_RESOURCES, + ARGUMENTS, + CLASSPATH, + ICON_ICO, + JVM_OPTIONS, + MAIN_CLASS, + MAIN_JAR, + PREFERENCES_ID, + VERSION, + VERBOSE + ); + } + + @Override + public File execute( + Map params, File outputParentDir) { + return doBundle(params, outputParentDir, false); + } + + @Override + public boolean supported() { + return (Platform.getPlatform() == Platform.WINDOWS); + } + +} diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinExeBundler.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinExeBundler.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,913 @@ +/* + * Copyright (c) 2017, 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.internal; + +import java.io.*; +import java.nio.charset.Charset; +import java.nio.file.Files; +import java.text.MessageFormat; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import static jdk.jpackage.internal.WindowsBundlerParam.*; + +public class WinExeBundler extends AbstractBundler { + + private static final ResourceBundle I18N = ResourceBundle.getBundle( + "jdk.jpackage.internal.resources.WinResources"); + + public static final BundlerParamInfo APP_BUNDLER = + new WindowsBundlerParam<>( + getString("param.exe-bundler.name"), + getString("param.exe-bundler.description"), + "win.app.bundler", + WinAppBundler.class, + params -> new WinAppBundler(), + null); + + public static final BundlerParamInfo EXE_IMAGE_DIR = + new WindowsBundlerParam<>( + getString("param.image-dir.name"), + getString("param.image-dir.description"), + "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); + + public static final BundlerParamInfo WIN_APP_IMAGE = + new WindowsBundlerParam<>( + getString("param.app-dir.name"), + getString("param.app-dir.description"), + "win.app.image", + File.class, + null, + (s, p) -> null); + + public static final BundlerParamInfo UPGRADE_UUID = + new WindowsBundlerParam<>( + I18N.getString("param.upgrade-uuid.name"), + I18N.getString("param.upgrade-uuid.description"), + Arguments.CLIOptions.WIN_UPGRADE_UUID.getId(), + UUID.class, + params -> UUID.randomUUID(), + (s, p) -> UUID.fromString(s)); + + public static final StandardBundlerParam EXE_SYSTEM_WIDE = + new StandardBundlerParam<>( + getString("param.system-wide.name"), + getString("param.system-wide.description"), + Arguments.CLIOptions.WIN_PER_USER_INSTALLATION.getId(), + Boolean.class, + params -> true, // default to system wide + (s, p) -> (s == null || "null".equalsIgnoreCase(s))? null + : Boolean.valueOf(s) + ); + public static final StandardBundlerParam PRODUCT_VERSION = + new StandardBundlerParam<>( + getString("param.product-version.name"), + getString("param.product-version.description"), + "win.msi.productVersion", + String.class, + VERSION::fetchFrom, + (s, p) -> s + ); + + public static final StandardBundlerParam MENU_HINT = + new WindowsBundlerParam<>( + getString("param.menu-shortcut-hint.name"), + getString("param.menu-shortcut-hint.description"), + Arguments.CLIOptions.WIN_MENU_HINT.getId(), + Boolean.class, + params -> false, + (s, p) -> (s == null || + "null".equalsIgnoreCase(s))? true : Boolean.valueOf(s) + ); + + public static final StandardBundlerParam SHORTCUT_HINT = + new WindowsBundlerParam<>( + getString("param.desktop-shortcut-hint.name"), + getString("param.desktop-shortcut-hint.description"), + Arguments.CLIOptions.WIN_SHORTCUT_HINT.getId(), + Boolean.class, + params -> false, + (s, p) -> (s == null || + "null".equalsIgnoreCase(s))? false : Boolean.valueOf(s) + ); + + private final static String DEFAULT_EXE_PROJECT_TEMPLATE = "template.iss"; + private final static String DEFAULT_JRE_EXE_TEMPLATE = "template.jre.iss"; + private static final String TOOL_INNO_SETUP_COMPILER = "iscc.exe"; + + public static final BundlerParamInfo + TOOL_INNO_SETUP_COMPILER_EXECUTABLE = new WindowsBundlerParam<>( + getString("param.iscc-path.name"), + getString("param.iscc-path.description"), + "win.exe.iscc.exe", + String.class, + params -> { + for (String dirString : (System.getenv("PATH") + + ";C:\\Program Files (x86)\\Inno Setup 5;" + + "C:\\Program Files\\Inno Setup 5").split(";")) { + File f = new File(dirString.replace("\"", ""), + TOOL_INNO_SETUP_COMPILER); + if (f.isFile()) { + return f.toString(); + } + } + return null; + }, + null); + + @Override + public String getName() { + return getString("exe.bundler.name"); + } + + @Override + public String getDescription() { + return getString("exe.bundler.description"); + } + + @Override + public String getID() { + return "exe"; + } + + @Override + public String getBundleType() { + return "INSTALLER"; + } + + @Override + public Collection> getBundleParameters() { + Collection> results = new LinkedHashSet<>(); + results.addAll(WinAppBundler.getAppBundleParameters()); + results.addAll(getExeBundleParameters()); + return results; + } + + public static Collection> getExeBundleParameters() { + return Arrays.asList( + DESCRIPTION, + COPYRIGHT, + LICENSE_FILE, + MENU_GROUP, + MENU_HINT, + SHORTCUT_HINT, + EXE_SYSTEM_WIDE, + TITLE, + VENDOR, + INSTALLDIR_CHOOSER + ); + } + + @Override + public File execute( + Map p, File outputParentDir) { + return bundle(p, outputParentDir); + } + + @Override + public boolean supported() { + return (Platform.getPlatform() == Platform.WINDOWS); + } + + static class VersionExtractor extends PrintStream { + double version = 0f; + + public VersionExtractor() { + super(new ByteArrayOutputStream()); + } + + double getVersion() { + if (version == 0f) { + String content = + new String(((ByteArrayOutputStream) out).toByteArray()); + Pattern pattern = Pattern.compile("Inno Setup (\\d+.?\\d*)"); + Matcher matcher = pattern.matcher(content); + if (matcher.find()) { + String v = matcher.group(1); + version = Double.parseDouble(v); + } + } + return version; + } + } + + private static double findToolVersion(String toolName) { + try { + if (toolName == null || "".equals(toolName)) return 0f; + + ProcessBuilder pb = new ProcessBuilder( + toolName, + "/?"); + VersionExtractor ve = new VersionExtractor(); + IOUtils.exec(pb, Log.isDebug(), true, ve); + // not interested in the output + double version = ve.getVersion(); + Log.verbose(MessageFormat.format( + getString("message.tool-version"), toolName, version)); + return version; + } catch (Exception e) { + if (Log.isDebug()) { + Log.verbose(e); + } + return 0f; + } + } + + @Override + public boolean validate(Map p) + throws UnsupportedPlatformException, ConfigException { + try { + if (p == null) throw new ConfigException( + getString("error.parameters-null"), + 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(p).validate(p); + + // make sure some key values don't have newlines + for (BundlerParamInfo pi : Arrays.asList( + APP_NAME, + COPYRIGHT, + DESCRIPTION, + MENU_GROUP, + TITLE, + VENDOR, + VERSION) + ) { + String v = pi.fetchFrom(p); + if (v.contains("\n") | v.contains("\r")) { + throw new ConfigException("Parmeter '" + pi.getID() + + "' cannot contain a newline.", + " Change the value of '" + pi.getID() + + " so that it does not contain any newlines"); + } + } + + // exe bundlers trim the copyright to 100 characters, + // tell them this will happen + if (COPYRIGHT.fetchFrom(p).length() > 100) { + throw new ConfigException( + getString("error.copyright-is-too-long"), + getString("error.copyright-is-too-long.advice")); + } + + double innoVersion = findToolVersion( + TOOL_INNO_SETUP_COMPILER_EXECUTABLE.fetchFrom(p)); + + //Inno Setup 5+ is required + double minVersion = 5.0f; + + if (innoVersion < minVersion) { + Log.error(MessageFormat.format( + getString("message.tool-wrong-version"), + TOOL_INNO_SETUP_COMPILER, innoVersion, minVersion)); + throw new ConfigException( + getString("error.iscc-not-found"), + getString("error.iscc-not-found.advice")); + } + + /********* validate bundle parameters *************/ + + // only one mime type per association, at least one file extension + List> associations = + FILE_ASSOCIATIONS.fetchFrom(p); + if (associations != null) { + for (int i = 0; i < associations.size(); i++) { + Map assoc = associations.get(i); + List mimes = FA_CONTENT_TYPE.fetchFrom(assoc); + if (mimes.size() > 1) { + throw new ConfigException(MessageFormat.format( + getString("error.too-many-content-" + + "types-for-file-association"), i), + 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); + } + } + } + + private boolean prepareProto(Map p) + throws IOException { + File appImage = StandardBundlerParam.getPredefinedAppImage(p); + File appDir = null; + + // we either have an application image or need to build one + if (appImage != null) { + appDir = new File( + EXE_IMAGE_DIR.fetchFrom(p), APP_NAME.fetchFrom(p)); + // copy everything from appImage dir into appDir/name + IOUtils.copyRecursive(appImage.toPath(), appDir.toPath()); + } else { + appDir = APP_BUNDLER.fetchFrom(p).doBundle(p, + EXE_IMAGE_DIR.fetchFrom(p), true); + } + + if (appDir == null) { + return false; + } + + p.put(WIN_APP_IMAGE.getID(), appDir); + + String licenseFile = LICENSE_FILE.fetchFrom(p); + 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(p), lfile.getName()); + IOUtils.copyFile(lfile, destFile); + ensureByMutationFileIsRTF(destFile); + } + + // copy file association icons + List> fileAssociations = + FILE_ASSOCIATIONS.fetchFrom(p); + + for (Map fa : fileAssociations) { + File icon = FA_ICON.fetchFrom(fa); // TODO FA_ICON_ICO + if (icon == null) { + continue; + } + + File faIconFile = new File(appDir, icon.getName()); + + if (icon.exists()) { + try { + IOUtils.copyFile(icon, faIconFile); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + return true; + } + + public File bundle(Map p, File outdir) { + if (!outdir.isDirectory() && !outdir.mkdirs()) { + throw new RuntimeException(MessageFormat.format( + getString("error.cannot-create-output-dir"), + outdir.getAbsolutePath())); + } + if (!outdir.canWrite()) { + throw new RuntimeException(MessageFormat.format( + getString("error.cannot-write-to-output-dir"), + outdir.getAbsolutePath())); + } + + if (WindowsDefender.isThereAPotentialWindowsDefenderIssue()) { + Log.error(MessageFormat.format( + getString("message.potential.windows.defender.issue"), + WindowsDefender.getUserTempDirectory())); + } + + // validate we have valid tools before continuing + String iscc = TOOL_INNO_SETUP_COMPILER_EXECUTABLE.fetchFrom(p); + if (iscc == null || !new File(iscc).isFile()) { + Log.error(getString("error.iscc-not-found")); + Log.error(MessageFormat.format( + getString("message.iscc-file-string"), iscc)); + return null; + } + + File imageDir = EXE_IMAGE_DIR.fetchFrom(p); + try { + imageDir.mkdirs(); + + boolean menuShortcut = MENU_HINT.fetchFrom(p); + boolean desktopShortcut = SHORTCUT_HINT.fetchFrom(p); + if (!menuShortcut && !desktopShortcut) { + // both can not be false - user will not find the app + Log.verbose(getString("message.one-shortcut-required")); + p.put(MENU_HINT.getID(), true); + } + + if (prepareProto(p) && prepareProjectConfig(p)) { + File configScript = getConfig_Script(p); + if (configScript.exists()) { + Log.verbose(MessageFormat.format( + getString("message.running-wsh-script"), + configScript.getAbsolutePath())); + IOUtils.run("wscript", configScript, VERBOSE.fetchFrom(p)); + } + return buildEXE(p, outdir); + } + return null; + } catch (IOException ex) { + ex.printStackTrace(); + return null; + } + } + + // name of post-image script + private File getConfig_Script(Map p) { + return new File(EXE_IMAGE_DIR.fetchFrom(p), + APP_NAME.fetchFrom(p) + "-post-image.wsf"); + } + + private String getAppIdentifier(Map p) { + String nm = UPGRADE_UUID.fetchFrom(p).toString(); + + // limitation of innosetup + if (nm.length() > 126) { + Log.error(getString("message-truncating-id")); + nm = nm.substring(0, 126); + } + + return nm; + } + + private String getLicenseFile(Map p) { + String licenseFile = LICENSE_FILE.fetchFrom(p); + if (licenseFile != null) { + File lfile = new File(licenseFile); + File destFile = new File(CONFIG_ROOT.fetchFrom(p), lfile.getName()); + String filePath = destFile.getAbsolutePath(); + if (filePath.contains(" ")) { + return "\"" + filePath + "\""; + } else { + return filePath; + } + } + + return null; + } + + void validateValueAndPut(Map data, String key, + BundlerParamInfo param, + Map p) throws IOException { + String value = param.fetchFrom(p); + if (value.contains("\r") || value.contains("\n")) { + throw new IOException("Configuration Parameter " + + param.getID() + " cannot contain multiple lines of text"); + } + data.put(key, innosetupEscape(value)); + } + + private String innosetupEscape(String value) { + if (value == null) { + return ""; + } + + if (value.contains("\"") || !value.trim().equals(value)) { + value = "\"" + value.replace("\"", "\"\"") + "\""; + } + return value; + } + + boolean prepareMainProjectFile(Map p) + throws IOException { + Map data = new HashMap<>(); + data.put("PRODUCT_APP_IDENTIFIER", + innosetupEscape(getAppIdentifier(p))); + + + validateValueAndPut(data, "INSTALLER_NAME", APP_NAME, p); + validateValueAndPut(data, "APPLICATION_VENDOR", VENDOR, p); + validateValueAndPut(data, "APPLICATION_VERSION", VERSION, p); + validateValueAndPut(data, "INSTALLER_FILE_NAME", + INSTALLER_FILE_NAME, p); + + data.put("LAUNCHER_NAME", + innosetupEscape(WinAppBundler.getAppName(p))); + + data.put("APPLICATION_LAUNCHER_FILENAME", + innosetupEscape(WinAppBundler.getLauncherName(p))); + + data.put("APPLICATION_DESKTOP_SHORTCUT", + SHORTCUT_HINT.fetchFrom(p) ? "returnTrue" : "returnFalse"); + data.put("APPLICATION_MENU_SHORTCUT", + MENU_HINT.fetchFrom(p) ? "returnTrue" : "returnFalse"); + validateValueAndPut(data, "APPLICATION_GROUP", MENU_GROUP, p); + validateValueAndPut(data, "APPLICATION_COMMENTS", TITLE, p); + validateValueAndPut(data, "APPLICATION_COPYRIGHT", COPYRIGHT, p); + + data.put("APPLICATION_LICENSE_FILE", + innosetupEscape(getLicenseFile(p))); + data.put("DISABLE_DIR_PAGE", + INSTALLDIR_CHOOSER.fetchFrom(p) ? "No" : "Yes"); + + Boolean isSystemWide = EXE_SYSTEM_WIDE.fetchFrom(p); + + if (isSystemWide) { + data.put("APPLICATION_INSTALL_ROOT", "{pf}"); + data.put("APPLICATION_INSTALL_PRIVILEGE", "admin"); + } else { + data.put("APPLICATION_INSTALL_ROOT", "{localappdata}"); + data.put("APPLICATION_INSTALL_PRIVILEGE", "lowest"); + } + + if (BIT_ARCH_64.fetchFrom(p)) { + data.put("ARCHITECTURE_BIT_MODE", "x64"); + } else { + data.put("ARCHITECTURE_BIT_MODE", ""); + } + validateValueAndPut(data, "RUN_FILENAME", APP_NAME, p); + + validateValueAndPut(data, "APPLICATION_DESCRIPTION", + DESCRIPTION, p); + + data.put("APPLICATION_SERVICE", "returnFalse"); + data.put("APPLICATION_NOT_SERVICE", "returnFalse"); + data.put("APPLICATION_APP_CDS_INSTALL", "returnFalse"); + data.put("START_ON_INSTALL", ""); + data.put("STOP_ON_UNINSTALL", ""); + data.put("RUN_AT_STARTUP", ""); + + String imagePathString = + WIN_APP_IMAGE.fetchFrom(p).toPath().toAbsolutePath().toString(); + data.put("APPLICATION_IMAGE", innosetupEscape(imagePathString)); + Log.verbose("setting APPLICATION_IMAGE to " + + innosetupEscape(imagePathString) + " for InnoSetup"); + + StringBuilder secondaryLaunchersCfg = new StringBuilder(); + for (Map + launcher : SECONDARY_LAUNCHERS.fetchFrom(p)) { + String application_name = APP_NAME.fetchFrom(launcher); + if (MENU_HINT.fetchFrom(launcher)) { + // Name: "{group}\APPLICATION_NAME"; + // Filename: "{app}\APPLICATION_NAME.exe"; + // IconFilename: "{app}\APPLICATION_NAME.ico" + secondaryLaunchersCfg.append("Name: \"{group}\\"); + secondaryLaunchersCfg.append(application_name); + secondaryLaunchersCfg.append("\"; Filename: \"{app}\\"); + secondaryLaunchersCfg.append(application_name); + secondaryLaunchersCfg.append(".exe\"; IconFilename: \"{app}\\"); + secondaryLaunchersCfg.append(application_name); + secondaryLaunchersCfg.append(".ico\"\r\n"); + } + if (SHORTCUT_HINT.fetchFrom(launcher)) { + // Name: "{commondesktop}\APPLICATION_NAME"; + // Filename: "{app}\APPLICATION_NAME.exe"; + // IconFilename: "{app}\APPLICATION_NAME.ico" + secondaryLaunchersCfg.append("Name: \"{commondesktop}\\"); + secondaryLaunchersCfg.append(application_name); + secondaryLaunchersCfg.append("\"; Filename: \"{app}\\"); + secondaryLaunchersCfg.append(application_name); + secondaryLaunchersCfg.append(".exe\"; IconFilename: \"{app}\\"); + secondaryLaunchersCfg.append(application_name); + secondaryLaunchersCfg.append(".ico\"\r\n"); + } + } + data.put("SECONDARY_LAUNCHERS", secondaryLaunchersCfg.toString()); + + StringBuilder registryEntries = new StringBuilder(); + String regName = APP_REGISTRY_NAME.fetchFrom(p); + List> fetchFrom = + FILE_ASSOCIATIONS.fetchFrom(p); + for (int i = 0; i < fetchFrom.size(); i++) { + Map fileAssociation = fetchFrom.get(i); + String description = FA_DESCRIPTION.fetchFrom(fileAssociation); + File icon = FA_ICON.fetchFrom(fileAssociation); //TODO FA_ICON_ICO + + List extensions = FA_EXTENSIONS.fetchFrom(fileAssociation); + String entryName = regName + "File"; + if (i > 0) { + entryName += "." + i; + } + + if (extensions == null) { + Log.verbose(getString( + "message.creating-association-with-null-extension")); + } else { + for (String ext : extensions) { + if (isSystemWide) { + // "Root: HKCR; Subkey: \".myp\"; + // ValueType: string; ValueName: \"\"; + // ValueData: \"MyProgramFile\"; + // Flags: uninsdeletevalue" + registryEntries.append("Root: HKCR; Subkey: \".") + .append(ext) + .append("\"; ValueType: string;" + + " ValueName: \"\"; ValueData: \"") + .append(entryName) + .append("\"; Flags: uninsdeletevalue uninsdeletekeyifempty\r\n"); + } else { + registryEntries.append( + "Root: HKCU; Subkey: \"Software\\Classes\\.") + .append(ext) + .append("\"; ValueType: string;" + + " ValueName: \"\"; ValueData: \"") + .append(entryName) + .append("\"; Flags: uninsdeletevalue uninsdeletekeyifempty\r\n"); + } + } + } + + if (extensions != null && !extensions.isEmpty()) { + String ext = extensions.get(0); + List mimeTypes = + FA_CONTENT_TYPE.fetchFrom(fileAssociation); + for (String mime : mimeTypes) { + if (isSystemWide) { + // "Root: HKCR; + // Subkey: HKCR\\Mime\\Database\\ + // Content Type\\application/chaos; + // ValueType: string; + // ValueName: Extension; + // ValueData: .chaos; + // Flags: uninsdeletevalue" + registryEntries.append("Root: HKCR; Subkey: " + + "\"Mime\\Database\\Content Type\\") + .append(mime) + .append("\"; ValueType: string; ValueName: " + + "\"Extension\"; ValueData: \".") + .append(ext) + .append("\"; Flags: uninsdeletevalue uninsdeletekeyifempty\r\n"); + } else { + registryEntries.append( + "Root: HKCU; Subkey: \"Software\\" + + "Classes\\Mime\\Database\\Content Type\\") + .append(mime) + .append("\"; ValueType: string; " + + "ValueName: \"Extension\"; ValueData: \".") + .append(ext) + .append("\"; Flags: uninsdeletevalue uninsdeletekeyifempty\r\n"); + } + } + } + + if (isSystemWide) { + // "Root: HKCR; + // Subkey: \"MyProgramFile\"; + // ValueType: string; + // ValueName: \"\"; + // ValueData: \"My Program File\"; + // Flags: uninsdeletekey" + registryEntries.append("Root: HKCR; Subkey: \"") + .append(entryName) + .append( + "\"; ValueType: string; ValueName: \"\"; ValueData: \"") + .append(removeQuotes(description)) + .append("\"; Flags: uninsdeletekey\r\n"); + } else { + registryEntries.append( + "Root: HKCU; Subkey: \"Software\\Classes\\") + .append(entryName) + .append( + "\"; ValueType: string; ValueName: \"\"; ValueData: \"") + .append(removeQuotes(description)) + .append("\"; Flags: uninsdeletekey\r\n"); + } + + if (icon != null && icon.exists()) { + if (isSystemWide) { + // "Root: HKCR; + // Subkey: \"MyProgramFile\\DefaultIcon\"; + // ValueType: string; + // ValueName: \"\"; + // ValueData: \"{app}\\MYPROG.EXE,0\"\n" + + registryEntries.append("Root: HKCR; Subkey: \"") + .append(entryName) + .append("\\DefaultIcon\"; ValueType: string; " + + "ValueName: \"\"; ValueData: \"{app}\\") + .append(icon.getName()) + .append("\"\r\n"); + } else { + registryEntries.append( + "Root: HKCU; Subkey: \"Software\\Classes\\") + .append(entryName) + .append("\\DefaultIcon\"; ValueType: string; " + + "ValueName: \"\"; ValueData: \"{app}\\") + .append(icon.getName()) + .append("\"\r\n"); + } + } + + if (isSystemWide) { + // "Root: HKCR; + // Subkey: \"MyProgramFile\\shell\\open\\command\"; + // ValueType: string; + // ValueName: \"\"; + // ValueData: \"\"\"{app}\\MYPROG.EXE\"\" \"\"%1\"\"\"\n" + registryEntries.append("Root: HKCR; Subkey: \"") + .append(entryName) + .append("\\shell\\open\\command\"; ValueType: " + + "string; ValueName: \"\"; ValueData: \"\"\"{app}\\") + .append(APP_NAME.fetchFrom(p)) + .append("\"\" \"\"%1\"\"\"\r\n"); + } else { + registryEntries.append( + "Root: HKCU; Subkey: \"Software\\Classes\\") + .append(entryName) + .append("\\shell\\open\\command\"; ValueType: " + + "string; ValueName: \"\"; ValueData: \"\"\"{app}\\") + .append(APP_NAME.fetchFrom(p)) + .append("\"\" \"\"%1\"\"\"\r\n"); + } + } + if (registryEntries.length() > 0) { + data.put("FILE_ASSOCIATIONS", + "ChangesAssociations=yes\r\n\r\n[Registry]\r\n" + + registryEntries.toString()); + } else { + data.put("FILE_ASSOCIATIONS", ""); + } + + // TODO - alternate template for JRE installer + String iss = Arguments.CREATE_JRE_INSTALLER.fetchFrom(p) ? + DEFAULT_JRE_EXE_TEMPLATE : DEFAULT_EXE_PROJECT_TEMPLATE; + + Writer w = new BufferedWriter(new FileWriter( + getConfig_ExeProjectFile(p))); + + String content = preprocessTextResource( + getConfig_ExeProjectFile(p).getName(), + getString("resource.inno-setup-project-file"), + iss, data, VERBOSE.fetchFrom(p), + RESOURCE_DIR.fetchFrom(p)); + w.write(content); + w.close(); + return true; + } + + private final static String removeQuotes(String s) { + if (s.length() > 2 && s.startsWith("\"") && s.endsWith("\"")) { + // special case for '"XXX"' return 'XXX' not '-XXX-' + // note '"' and '""' are excluded from this special case + s = s.substring(1, s.length() - 1); + } + // if there interior double quotes replace them with '-' + return s.replaceAll("\"", "-"); + } + + private final static String DEFAULT_INNO_SETUP_ICON = + "icon_inno_setup.bmp"; + + private boolean prepareProjectConfig(Map p) + throws IOException { + prepareMainProjectFile(p); + + // prepare installer icon + File iconTarget = getConfig_SmallInnoSetupIcon(p); + fetchResource(iconTarget.getName(), + getString("resource.setup-icon"), + DEFAULT_INNO_SETUP_ICON, + iconTarget, + VERBOSE.fetchFrom(p), + RESOURCE_DIR.fetchFrom(p)); + + fetchResource(getConfig_Script(p).getName(), + getString("resource.post-install-script"), + (String) null, + getConfig_Script(p), + VERBOSE.fetchFrom(p), + RESOURCE_DIR.fetchFrom(p)); + return true; + } + + private File getConfig_SmallInnoSetupIcon( + Map p) { + return new File(EXE_IMAGE_DIR.fetchFrom(p), + APP_NAME.fetchFrom(p) + "-setup-icon.bmp"); + } + + private File getConfig_ExeProjectFile(Map p) { + return new File(EXE_IMAGE_DIR.fetchFrom(p), + APP_NAME.fetchFrom(p) + ".iss"); + } + + + private File buildEXE(Map p, File outdir) + throws IOException { + Log.verbose(MessageFormat.format( + getString("message.outputting-to-location"), + outdir.getAbsolutePath())); + + outdir.mkdirs(); + + // run Inno Setup + ProcessBuilder pb = new ProcessBuilder( + TOOL_INNO_SETUP_COMPILER_EXECUTABLE.fetchFrom(p), + "/q", // turn off inno setup output + "/o"+outdir.getAbsolutePath(), + getConfig_ExeProjectFile(p).getAbsolutePath()); + pb = pb.directory(EXE_IMAGE_DIR.fetchFrom(p)); + IOUtils.exec(pb, VERBOSE.fetchFrom(p)); + + Log.verbose(MessageFormat.format( + getString("message.output-location"), + outdir.getAbsolutePath())); + + // presume the result is the ".exe" 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(".exe") && + f.lastModified() > lastModified) { + result = f; + lastModified = f.lastModified(); + } + } + } + + return result; + } + + 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 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()) { + if (c < 0x10) { + w.write("\\'0"); + w.write(Integer.toHexString(c)); + } else if (c > 0xff) { + w.write("\\ud"); + w.write(Integer.toString(c)); + 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); + } + } + 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); + } + } + + private static String getString(String key) + throws MissingResourceException { + return I18N.getString(key); + } +} diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinMsiBundler.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinMsiBundler.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,1186 @@ +/* + * Copyright (c) 2012, 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.internal; + +import java.io.*; +import java.nio.charset.Charset; +import java.nio.file.Files; +import java.text.MessageFormat; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import static jdk.jpackage.internal.WindowsBundlerParam.*; + +public class WinMsiBundler extends AbstractBundler { + + private static final ResourceBundle I18N = ResourceBundle.getBundle( + "jdk.jpackage.internal.resources.WinResources"); + + public static final BundlerParamInfo APP_BUNDLER = + new WindowsBundlerParam<>( + I18N.getString("param.msi-bundler.name"), + I18N.getString("param.msi-bundler.description"), + "win.app.bundler", + WinAppBundler.class, + params -> new WinAppBundler(), + null); + + public static final BundlerParamInfo CAN_USE_WIX36 = + new WindowsBundlerParam<>( + I18N.getString("param.can-use-wix36.name"), + I18N.getString("param.can-use-wix36.description"), + "win.msi.canUseWix36", + Boolean.class, + params -> false, + (s, p) -> Boolean.valueOf(s)); + + public static final BundlerParamInfo MSI_IMAGE_DIR = + new WindowsBundlerParam<>( + I18N.getString("param.image-dir.name"), + I18N.getString("param.image-dir.description"), + "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 WIN_APP_IMAGE = + new WindowsBundlerParam<>( + I18N.getString("param.app-dir.name"), + I18N.getString("param.app-dir.description"), + "win.app.image", + File.class, + null, + (s, p) -> null); + + public static final StandardBundlerParam MSI_SYSTEM_WIDE = + new StandardBundlerParam<>( + I18N.getString("param.system-wide.name"), + I18N.getString("param.system-wide.description"), + 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 PRODUCT_VERSION = + new StandardBundlerParam<>( + I18N.getString("param.product-version.name"), + I18N.getString("param.product-version.description"), + "win.msi.productVersion", + String.class, + VERSION::fetchFrom, + (s, p) -> s + ); + + public static final BundlerParamInfo UPGRADE_UUID = + new WindowsBundlerParam<>( + I18N.getString("param.upgrade-uuid.name"), + I18N.getString("param.upgrade-uuid.description"), + 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"; + + public static final BundlerParamInfo TOOL_CANDLE_EXECUTABLE = + new WindowsBundlerParam<>( + I18N.getString("param.candle-path.name"), + I18N.getString("param.candle-path.description"), + "win.msi.candle.exe", + String.class, + params -> { + 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; + }, + null); + + public static final BundlerParamInfo TOOL_LIGHT_EXECUTABLE = + new WindowsBundlerParam<>( + I18N.getString("param.light-path.name"), + I18N.getString("param.light-path.description"), + "win.msi.light.exe", + String.class, + params -> { + 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; + }, + null); + + public static final StandardBundlerParam MENU_HINT = + new WindowsBundlerParam<>( + I18N.getString("param.menu-shortcut-hint.name"), + I18N.getString("param.menu-shortcut-hint.description"), + 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 SHORTCUT_HINT = + new WindowsBundlerParam<>( + I18N.getString("param.desktop-shortcut-hint.name"), + I18N.getString("param.desktop-shortcut-hint.description"), + 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 getDescription() { + return I18N.getString("msi.bundler.description"); + } + + @Override + public String getID() { + return "msi"; + } + + @Override + public String getBundleType() { + return "INSTALLER"; + } + + @Override + public Collection> getBundleParameters() { + Collection> results = new LinkedHashSet<>(); + results.addAll(WinAppBundler.getAppBundleParameters()); + results.addAll(getMsiBundleParameters()); + return results; + } + + public static Collection> getMsiBundleParameters() { + return Arrays.asList( + DESCRIPTION, + MENU_GROUP, + MENU_HINT, + PRODUCT_VERSION, + SHORTCUT_HINT, + MSI_SYSTEM_WIDE, + VENDOR, + LICENSE_FILE, + INSTALLDIR_CHOOSER + ); + } + + @Override + public File execute( + Map params, File outputParentDir) { + return bundle(params, outputParentDir); + } + + @Override + public boolean supported() { + return (Platform.getPlatform() == Platform.WINDOWS); + } + + static class VersionExtractor extends PrintStream { + double version = 0f; + + public VersionExtractor() { + super(new ByteArrayOutputStream()); + } + + double getVersion() { + if (version == 0f) { + String content = + new String(((ByteArrayOutputStream) out).toByteArray()); + Pattern pattern = Pattern.compile("version (\\d+.\\d+)"); + Matcher matcher = pattern.matcher(content); + if (matcher.find()) { + String v = matcher.group(1); + version = Double.parseDouble(v); + } + } + return version; + } + } + + private static double findToolVersion(String toolName) { + try { + if (toolName == null || "".equals(toolName)) return 0f; + + ProcessBuilder pb = new ProcessBuilder( + toolName, + "/?"); + VersionExtractor ve = new VersionExtractor(); + // not interested in the output + IOUtils.exec(pb, Log.isDebug(), true, ve); + double 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 0f; + } + } + + @Override + public boolean validate(Map p) + throws UnsupportedPlatformException, ConfigException { + try { + if (p == 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(p).validate(p); + + double candleVersion = + findToolVersion(TOOL_CANDLE_EXECUTABLE.fetchFrom(p)); + double lightVersion = + findToolVersion(TOOL_LIGHT_EXECUTABLE.fetchFrom(p)); + + // WiX 3.0+ is required + double minVersion = 3.0f; + boolean bad = false; + + if (candleVersion < minVersion) { + Log.verbose(MessageFormat.format( + I18N.getString("message.wrong-tool-version"), + TOOL_CANDLE, candleVersion, minVersion)); + bad = true; + } + if (lightVersion < minVersion) { + Log.verbose(MessageFormat.format( + I18N.getString("message.wrong-tool-version"), + TOOL_LIGHT, lightVersion, minVersion)); + bad = true; + } + + if (bad){ + throw new ConfigException( + I18N.getString("error.no-wix-tools"), + I18N.getString("error.no-wix-tools.advice")); + } + + if (lightVersion >= 3.6f) { + Log.verbose(I18N.getString("message.use-wix36-features")); + p.put(CAN_USE_WIX36.getID(), Boolean.TRUE); + } + + /********* validate bundle parameters *************/ + + String version = PRODUCT_VERSION.fetchFrom(p); + 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> associations = + FILE_ASSOCIATIONS.fetchFrom(p); + if (associations != null) { + for (int i = 0; i < associations.size(); i++) { + Map assoc = associations.get(i); + List 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 p) + throws IOException { + File appImage = StandardBundlerParam.getPredefinedAppImage(p); + File appDir = null; + + // we either have an application image or need to build one + if (appImage != null) { + appDir = new File( + MSI_IMAGE_DIR.fetchFrom(p), APP_NAME.fetchFrom(p)); + // copy everything from appImage dir into appDir/name + IOUtils.copyRecursive(appImage.toPath(), appDir.toPath()); + } else { + appDir = APP_BUNDLER.fetchFrom(p).doBundle(p, + MSI_IMAGE_DIR.fetchFrom(p), true); + } + + p.put(WIN_APP_IMAGE.getID(), appDir); + + String licenseFile = LICENSE_FILE.fetchFrom(p); + 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(p), lfile.getName()); + IOUtils.copyFile(lfile, destFile); + ensureByMutationFileIsRTF(destFile); + } + + // copy file association icons + List> fileAssociations = + FILE_ASSOCIATIONS.fetchFrom(p); + for (Map fa : fileAssociations) { + File icon = FA_ICON.fetchFrom(fa); // TODO FA_ICON_ICO + if (icon == null) { + continue; + } + + File faIconFile = new File(appDir, icon.getName()); + + if (icon.exists()) { + try { + IOUtils.copyFile(icon, faIconFile); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + return appDir != null; + } + + public File bundle(Map p, File outdir) { + if (!outdir.isDirectory() && !outdir.mkdirs()) { + throw new RuntimeException(MessageFormat.format( + I18N.getString("error.cannot-create-output-dir"), + outdir.getAbsolutePath())); + } + if (!outdir.canWrite()) { + throw new RuntimeException(MessageFormat.format( + I18N.getString("error.cannot-write-to-output-dir"), + outdir.getAbsolutePath())); + } + + // validate we have valid tools before continuing + String light = TOOL_LIGHT_EXECUTABLE.fetchFrom(p); + String candle = TOOL_CANDLE_EXECUTABLE.fetchFrom(p); + if (light == null || !new File(light).isFile() || + candle == null || !new File(candle).isFile()) { + Log.error(I18N.getString("error.no-wix-tools")); + Log.verbose(MessageFormat.format( + I18N.getString("message.light-file-string"), light)); + Log.verbose(MessageFormat.format( + I18N.getString("message.candle-file-string"), candle)); + return null; + } + + File imageDir = MSI_IMAGE_DIR.fetchFrom(p); + try { + imageDir.mkdirs(); + + boolean menuShortcut = MENU_HINT.fetchFrom(p); + boolean desktopShortcut = SHORTCUT_HINT.fetchFrom(p); + if (!menuShortcut && !desktopShortcut) { + // both can not be false - user will not find the app + Log.verbose(I18N.getString("message.one-shortcut-required")); + p.put(MENU_HINT.getID(), true); + } + + if (prepareProto(p) && prepareWiXConfig(p) + && prepareBasicProjectConfig(p)) { + File configScriptSrc = getConfig_Script(p); + 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, false); + } + return buildMSI(p, outdir); + } + return null; + } catch (IOException ex) { + Log.verbose(ex); + return null; + } + } + + // name of post-image script + private File getConfig_Script(Map params) { + return new File(CONFIG_ROOT.fetchFrom(params), + APP_FS_NAME.fetchFrom(params) + "-post-image.wsf"); + } + + private boolean prepareBasicProjectConfig( + Map 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)); + return true; + } + + private String relativePath(File basedir, File file) { + return file.getAbsolutePath().substring( + basedir.getAbsolutePath().length() + 1); + } + + boolean prepareMainProjectFile( + Map params) throws IOException { + Map 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("PRODUCT_GUID", productGUID.toString()); + data.put("PRODUCT_UPGRADE_GUID", + UPGRADE_UUID.fetchFrom(params).toString()); + + data.put("APPLICATION_NAME", APP_NAME.fetchFrom(params)); + data.put("APPLICATION_DESCRIPTION", DESCRIPTION.fetchFrom(params)); + data.put("APPLICATION_VENDOR", VENDOR.fetchFrom(params)); + data.put("APPLICATION_VERSION", PRODUCT_VERSION.fetchFrom(params)); + + // WinAppBundler will add application folder again => step out + File imageRootDir = WIN_APP_IMAGE.fetchFrom(params); + File launcher = new File(imageRootDir, + WinAppBundler.getLauncherName(params)); + + String launcherPath = relativePath(imageRootDir, launcher); + data.put("APPLICATION_LAUNCHER", launcherPath); + + String iconPath = launcherPath.replace(".exe", ".ico"); + + data.put("APPLICATION_ICON", iconPath); + + data.put("REGISTRY_ROOT", getRegistryRoot(params)); + + boolean canUseWix36Features = CAN_USE_WIX36.fetchFrom(params); + data.put("WIX36_ONLY_START", + canUseWix36Features ? "" : ""); + + if (MSI_SYSTEM_WIDE.fetchFrom(params)) { + data.put("INSTALL_SCOPE", "perMachine"); + } else { + data.put("INSTALL_SCOPE", "perUser"); + } + + if (BIT_ARCH_64.fetchFrom(params)) { + data.put("PLATFORM", "x64"); + data.put("WIN64", "yes"); + } else { + data.put("PLATFORM", "x86"); + data.put("WIN64", "no"); + } + + data.put("UI_BLOCK", getUIBlock(params)); + + List> secondaryLaunchers = + SECONDARY_LAUNCHERS.fetchFrom(params); + + StringBuilder secondaryLauncherIcons = new StringBuilder(); + for (int i = 0; i < secondaryLaunchers.size(); i++) { + Map sl = secondaryLaunchers.get(i); + // + if (SHORTCUT_HINT.fetchFrom(sl) || MENU_HINT.fetchFrom(sl)) { + File secondaryLauncher = new File(imageRootDir, + WinAppBundler.getLauncherName(sl)); + String secondaryLauncherPath = + relativePath(imageRootDir, secondaryLauncher); + String secondaryLauncherIconPath = + secondaryLauncherPath.replace(".exe", ".ico"); + + secondaryLauncherIcons.append(" \r\n"); + } + } + data.put("SECONDARY_LAUNCHER_ICONS", secondaryLauncherIcons.toString()); + + String wxs = Arguments.CREATE_JRE_INSTALLER.fetchFrom(params) ? + MSI_PROJECT_TEMPLATE_SERVER_JRE : MSI_PROJECT_TEMPLATE; + + Writer w = new BufferedWriter( + new FileWriter(getConfig_ProjectFile(params))); + + String content = preprocessTextResource( + getConfig_ProjectFile(params).getName(), + I18N.getString("resource.wix-config-file"), + wxs, data, VERBOSE.fetchFrom(params), + RESOURCE_DIR.fetchFrom(params)); + w.write(content); + w.close(); + return true; + } + private int id; + private int compId; + private final static String LAUNCHER_ID = "LauncherId"; + private final static String LAUNCHER_SVC_ID = "LauncherSvcId"; + + /** + * Overrides the dialog sequence in built-in dialog set "WixUI_InstallDir" + * to exclude license dialog + */ + private static final String TWEAK_FOR_EXCLUDING_LICENSE = + " 1" + + " \n" + + " 1" + + " \n"; + + /** + * Creates UI element using WiX built-in dialog sets + * - WixUI_InstallDir/WixUI_Minimal. + * The dialog sets are the closest to what we want to implement. + * + * WixUI_Minimal for license dialog only + * WixUI_InstallDir for installdir dialog only or for both + * installdir/license dialogs + */ + private String getUIBlock(Map params) { + String uiBlock = " \n"; // UI-less element + + if (INSTALLDIR_CHOOSER.fetchFrom(params)) { + boolean enableTweakForExcludingLicense = + (getLicenseFile(params) == null); + uiBlock = " \n" + + " \n" + + " \n" + + (enableTweakForExcludingLicense ? + TWEAK_FOR_EXCLUDING_LICENSE : "") + +" \n"; + } else if (getLicenseFile(params) != null) { + uiBlock = " \n" + + " \n" + + " \n"; + } + + return uiBlock; + } + + private void walkFileTree(Map params, + File root, PrintStream out, String prefix) { + List dirs = new ArrayList<>(); + List 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 + " "); + out.println(prefix + " "); + out.println(prefix + " "); + + boolean needRegistryKey = !MSI_SYSTEM_WIDE.fetchFrom(params); + File imageRootDir = WIN_APP_IMAGE.fetchFrom(params); + File launcherFile = + new File(imageRootDir, WinAppBundler.getLauncherName(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 + " " : " Action=\"createAndRemoveOnUninstall\">")); + out.println(prefix + + " "); + out.println(prefix + " "); + } + + boolean menuShortcut = MENU_HINT.fetchFrom(params); + boolean desktopShortcut = SHORTCUT_HINT.fetchFrom(params); + + Map 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 + " "); + if (doShortcuts && desktopShortcut) { + out.println(prefix + + " "); + } + if (doShortcuts && menuShortcut) { + out.println(prefix + + " "); + } + + List> secondaryLaunchers = + SECONDARY_LAUNCHERS.fetchFrom(params); + for (int i = 0; i < secondaryLaunchers.size(); i++) { + Map sl = secondaryLaunchers.get(i); + File secondaryLauncherFile = new File(imageRootDir, + WinAppBundler.getLauncherName(sl)); + if (f.equals(secondaryLauncherFile)) { + if (SHORTCUT_HINT.fetchFrom(sl)) { + out.println(prefix + + " "); + } + if (MENU_HINT.fetchFrom(sl)) { + out.println(prefix + + " "); + // Should we allow different menu groups? Not for now. + } + } + } + out.println(prefix + " "); + } + + if (launcherSet) { + List> fileAssociations = + FILE_ASSOCIATIONS.fetchFrom(params); + String regName = APP_REGISTRY_NAME.fetchFrom(params); + Set defaultedMimes = new TreeSet<>(); + int count = 0; + for (Map fa : fileAssociations) { + String description = FA_DESCRIPTION.fetchFrom(fa); + List extensions = FA_EXTENSIONS.fetchFrom(fa); + List mimeTypes = FA_CONTENT_TYPE.fetchFrom(fa); + File icon = FA_ICON.fetchFrom(fa); // TODO FA_ICON_ICO + + 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 + " "); + } else { + for (String ext : extensions) { + String entryName = regName + "File"; + if (count > 0) { + entryName += "." + count; + } + count++; + + out.print(prefix + " "); + + if (extensions == null) { + Log.verbose(I18N.getString( + "message.creating-association-with-null-extension")); + } else { + out.print(prefix + " "); + } else { + out.println(" ContentType='" + mime + "'>"); + if (!defaultedMimes.contains(mime)) { + out.println(prefix + + " "); + defaultedMimes.add(mime); + } + } + out.println(prefix + + " "); + out.println(prefix + " "); + } + out.println(prefix + " "); + } + } + } + } + + out.println(prefix + " "); + + for (File d : dirs) { + out.println(prefix + " "); + walkFileTree(params, d, out, prefix + " "); + out.println(prefix + " "); + } + } + + String getRegistryRoot(Map params) { + if (MSI_SYSTEM_WIDE.fetchFrom(params)) { + return "HKLM"; + } else { + return "HKCU"; + } + } + + boolean prepareContentList(Map params) + throws FileNotFoundException { + File f = new File( + CONFIG_ROOT.fetchFrom(params), MSI_PROJECT_CONTENT_FILE); + PrintStream out = new PrintStream(f); + + // opening + out.println(""); + out.println(""); + + out.println(" "); + if (MSI_SYSTEM_WIDE.fetchFrom(params)) { + // install to programfiles + if (BIT_ARCH_64.fetchFrom(params)) { + out.println(" "); + } else { + out.println(" "); + } + } else { + // install to user folder + out.println( + " "); + } + out.println(" "); + + // dynamic part + id = 0; + compId = 0; // reset counters + walkFileTree(params, WIN_APP_IMAGE.fetchFrom(params), out, " "); + + // closing + out.println(" "); + out.println(" "); + + // for shortcuts + if (SHORTCUT_HINT.fetchFrom(params)) { + out.println(" "); + } + if (MENU_HINT.fetchFrom(params)) { + out.println(" "); + out.println(" "); + out.println(" "); + out.println(" "); + // 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 of them are appealing. + // Leave it for now + out.println( + " "); + out.println(" "); + out.println(" "); + out.println(" "); + } + + out.println(" "); + + out.println(" "); + for (int j = 0; j < compId; j++) { + out.println(" "); + } + // component is defined in the template.wsx + out.println(" "); + out.println(" "); + out.println(""); + + out.close(); + return true; + } + + private File getConfig_ProjectFile(Map params) { + return new File(CONFIG_ROOT.fetchFrom(params), + APP_NAME.fetchFrom(params) + ".wxs"); + } + + private String getLicenseFile(Map p) { + String licenseFile = LICENSE_FILE.fetchFrom(p); + if (licenseFile != null) { + File lfile = new File(licenseFile); + File destFile = new File(CONFIG_ROOT.fetchFrom(p), lfile.getName()); + String filePath = destFile.getAbsolutePath(); + if (filePath.contains(" ")) { + return "\"" + filePath + "\""; + } else { + return filePath; + } + } + + return null; + } + + private boolean prepareWiXConfig( + Map params) throws IOException { + return prepareMainProjectFile(params) && prepareContentList(params); + + } + private final static String MSI_PROJECT_TEMPLATE = "template.wxs"; + private final static String MSI_PROJECT_TEMPLATE_SERVER_JRE = + "template.jre.wxs"; + private final static String MSI_PROJECT_CONTENT_FILE = "bundle.wxi"; + + private File buildMSI(Map params, File outdir) + throws IOException { + File tmpDir = new File(BUILD_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(); + + // run candle + ProcessBuilder pb = new ProcessBuilder( + TOOL_CANDLE_EXECUTABLE.fetchFrom(params), + "-nologo", + getConfig_ProjectFile(params).getAbsolutePath(), + "-ext", "WixUtilExtension", + "-out", candleOut.getAbsolutePath()); + pb = pb.directory(WIN_APP_IMAGE.fetchFrom(params)); + IOUtils.exec(pb, false); + + Log.verbose(MessageFormat.format(I18N.getString( + "message.generating-msi"), msiOut.getAbsolutePath())); + + boolean enableLicenseUI = (getLicenseFile(params) != null); + boolean enableInstalldirUI = INSTALLDIR_CHOOSER.fetchFrom(params); + + List commandLine = new ArrayList<>(); + + commandLine.add(TOOL_LIGHT_EXECUTABLE.fetchFrom(params)); + if (enableLicenseUI) { + commandLine.add("-dWixUILicenseRtf="+getLicenseFile(params)); + } + 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.dll"); + } + commandLine.add("-out"); + commandLine.add(msiOut.getAbsolutePath()); + + // create .msi + pb = new ProcessBuilder(commandLine); + + pb = pb.directory(WIN_APP_IMAGE.fetchFrom(params)); + IOUtils.exec(pb, false); + + 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 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); + } + + } +} diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WindowsAppImageBuilder.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WindowsAppImageBuilder.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,451 @@ +/* + * Copyright (c) 2015, 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.internal; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.FileInputStream; +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.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 jdk.jpackage.internal.Arguments; + +import static jdk.jpackage.internal.StandardBundlerParam.*; + +public class WindowsAppImageBuilder extends AbstractAppImageBuilder { + + 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 Map params; + + public static final BundlerParamInfo REBRAND_EXECUTABLE = + new WindowsBundlerParam<>( + I18N.getString("param.rebrand-executable.name"), + I18N.getString("param.rebrand-executable.description"), + "win.launcher.rebrand", + Boolean.class, + params -> Boolean.TRUE, + (s, p) -> Boolean.valueOf(s)); + + public static final BundlerParamInfo ICON_ICO = + new StandardBundlerParam<>( + I18N.getString("param.icon-ico.name"), + I18N.getString("param.icon-ico.description"), + "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 CONSOLE_HINT = + new WindowsBundlerParam<>( + I18N.getString("param.console-hint.name"), + I18N.getString("param.console-hint.description"), + 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 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"); + 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"); + Files.createDirectories(runtimeDir); + } + + private Path destFile(String dir, String filename) { + return runtimeDir.resolve(dir).resolve(filename); + } + + private void writeEntry(InputStream in, Path dstFile) throws IOException { + Files.createDirectories(dstFile.getParent()); + Files.copy(in, dstFile); + } + + private void writeSymEntry(Path dstFile, Path target) throws IOException { + Files.createDirectories(dstFile.getParent()); + Files.createLink(dstFile, target); + } + + /** + * chmod ugo+x file + */ + private void setExecutable(Path file) { + try { + Set perms = + Files.getPosixFilePermissions(file); + perms.add(PosixFilePermission.OWNER_EXECUTE); + perms.add(PosixFilePermission.GROUP_EXECUTE); + perms.add(PosixFilePermission.OTHERS_EXECUTE); + Files.setPosixFilePermissions(file, perms); + } catch (IOException ioe) { + throw new UncheckedIOException(ioe); + } + } + + private static void createUtf8File(File file, String content) + throws IOException { + try (OutputStream fout = new FileOutputStream(file); + Writer output = new OutputStreamWriter(fout, "UTF-8")) { + output.write(content); + } + } + + public static String getLauncherName(Map p) { + return APP_FS_NAME.fetchFrom(p) + ".exe"; + } + + // Returns launcher resource name for launcher we need to use. + public static String getLauncherResourceName( + Map p) { + if (CONSOLE_HINT.fetchFrom(p)) { + return "jpackageapplauncher.exe"; + } else { + return "jpackageapplauncherw.exe"; + } + } + + public static String getLauncherCfgName(Map p) { + return "app/" + APP_FS_NAME.fetchFrom(p) +".cfg"; + } + + private File getConfig_AppIcon(Map params) { + return new File(getConfigRoot(params), + APP_FS_NAME.fetchFrom(params) + ".ico"); + } + + private File getConfig_ExecutableProperties( + Map params) { + return new File(getConfigRoot(params), + APP_FS_NAME.fetchFrom(params) + ".properties"); + } + + File getConfigRoot(Map 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 originalParams = new HashMap<>(params); + File rootFile = root.toFile(); + if (!rootFile.isDirectory() && !rootFile.mkdirs()) { + throw new RuntimeException(MessageFormat.format(I18N.getString( + "error.cannot-create-output-dir"), rootFile.getAbsolutePath())); + } + if (!rootFile.canWrite()) { + throw new RuntimeException(MessageFormat.format( + I18N.getString("error.cannot-write-to-output-dir"), + rootFile.getAbsolutePath())); + } + // 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, root.resolve(LIBRARY_NAME)); + } + + copyMSVCDLLs(); + + // create the secondary launchers, if any + List> entryPoints = + StandardBundlerParam.SECONDARY_LAUNCHERS.fetchFrom(params); + for (Map entryPoint : entryPoints) { + Map tmp = new HashMap<>(originalParams); + tmp.putAll(entryPoint); + createLauncherForEntryPoint(tmp); + } + } + + @Override + public void prepareJreFiles() throws IOException {} + + private void copyMSVCDLLs() throws IOException { + AtomicReference ioe = new AtomicReference<>(); + try (Stream 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, root.resolve((p.toFile().getName()))); + } catch (IOException e) { + ioe.set(e); + } + }); + } + + IOException e = ioe.get(); + if (e != null) { + throw e; + } + } + + // TODO: do we still need this? + private boolean copyMSVCDLLs(String VS_VER) throws IOException { + final InputStream REDIST_MSVCR_URL = getResourceAsStream( + REDIST_MSVCR.replaceAll("VS_VER", VS_VER)); + final InputStream REDIST_MSVCP_URL = getResourceAsStream( + REDIST_MSVCP.replaceAll("VS_VER", VS_VER)); + + if (REDIST_MSVCR_URL != null && REDIST_MSVCP_URL != null) { + Files.copy( + REDIST_MSVCR_URL, + root.resolve(REDIST_MSVCR.replaceAll("VS_VER", VS_VER))); + Files.copy( + REDIST_MSVCP_URL, + root.resolve(REDIST_MSVCP.replaceAll("VS_VER", VS_VER))); + return true; + } + + return false; + } + + private void validateValueAndPut( + Map data, String key, + BundlerParamInfo param, + Map 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 params) throws IOException { + Map data = new HashMap<>(); + + // mapping Java parameters in strings for version resource + data.put("COMMENTS", ""); + 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("LEGAL_TRADEMARK", ""); + data.put("ORIGINAL_FILENAME", getLauncherName(params)); + data.put("PRIVATE_BUILD", ""); + validateValueAndPut(data, "PRODUCT_NAME", APP_NAME, params); + validateValueAndPut(data, "PRODUCT_VERSION", VERSION, params); + data.put("SPECIAL_BUILD", ""); + + Writer w = new BufferedWriter( + new FileWriter(getConfig_ExecutableProperties(params))); + 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); + w.close(); + } + + private void createLauncherForEntryPoint( + Map p) throws IOException { + + File launcherIcon = ICON_ICO.fetchFrom(p); + File icon = launcherIcon != null ? + launcherIcon : ICON_ICO.fetchFrom(params); + File iconTarget = getConfig_AppIcon(p); + + 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(p, root.resolve( + getLauncherCfgName(p)).toFile(), "$APPDIR\\runtime"); + + prepareExecutableProperties(p); + + // Copy executable root folder + Path executableFile = root.resolve(getLauncherName(p)); + try (InputStream is_launcher = + getResourceAsStream(getLauncherResourceName(p))) { + writeEntry(is_launcher, executableFile); + } + + File launcher = executableFile.toFile(); + launcher.setWritable(true, true); + + // Update branding of EXE file + if (REBRAND_EXECUTABLE.fetchFrom(p)) { + File tool = new File( + System.getProperty("java.home") + "\\bin\\jpackage.exe"); + + // Run tool on launcher file to change the icon and the metadata. + try { + if (WindowsDefender.isThereAPotentialWindowsDefenderIssue()) { + Log.error(MessageFormat.format(I18N.getString( + "message.potential.windows.defender.issue"), + WindowsDefender.getUserTempDirectory())); + } + + launcher.setWritable(true); + + if (iconTarget.exists()) { + ProcessBuilder pb = new ProcessBuilder( + tool.getAbsolutePath(), + "--icon-swap", + iconTarget.getAbsolutePath(), + launcher.getAbsolutePath()); + IOUtils.exec(pb, false); + } + + File executableProperties = getConfig_ExecutableProperties(p); + + if (executableProperties.exists()) { + ProcessBuilder pb = new ProcessBuilder( + tool.getAbsolutePath(), + "--version-swap", + executableProperties.getAbsolutePath(), + launcher.getAbsolutePath()); + IOUtils.exec(pb, false); + } + } + finally { + executableFile.toFile().setReadOnly(); + } + } + + Files.copy(iconTarget.toPath(), + root.resolve(APP_NAME.fetchFrom(p) + ".ico")); + } + + private void copyApplication(Map params) + throws IOException { + List 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); + } + } + } + +} diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WindowsBundlerParam.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WindowsBundlerParam.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2014, 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.internal; + +import java.util.Map; +import java.util.ResourceBundle; +import java.util.function.BiFunction; +import java.util.function.Function; + +class WindowsBundlerParam extends StandardBundlerParam { + + private static final ResourceBundle I18N = ResourceBundle.getBundle( + "jdk.jpackage.internal.resources.WinResources"); + + WindowsBundlerParam(String name, String description, String id, + Class valueType, + Function, T> defaultValueFunction, + BiFunction, T> stringConverter) { + super(name, description, id, valueType, + defaultValueFunction, stringConverter); + } + + static final BundlerParamInfo INSTALLER_FILE_NAME = + new StandardBundlerParam<> ( + I18N.getString("param.installer-name.name"), + I18N.getString("param.installer-name.description"), + "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 APP_REGISTRY_NAME = + new StandardBundlerParam<> ( + I18N.getString("param.registry-name.name"), + I18N.getString("param.registry-name.description"), + 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 MENU_GROUP = + new StandardBundlerParam<>( + I18N.getString("param.menu-group.name"), + I18N.getString("param.menu-group.description"), + Arguments.CLIOptions.WIN_MENU_GROUP.getId(), + String.class, + params -> params.containsKey(VENDOR.getID()) + ? VENDOR.fetchFrom(params) + : params.containsKey(CATEGORY.getID()) + ? CATEGORY.fetchFrom(params) + : I18N.getString("param.menu-group.default"), + (s, p) -> s + ); + + static final StandardBundlerParam BIT_ARCH_64 = + new StandardBundlerParam<>( + I18N.getString("param.64-bit.name"), + I18N.getString("param.64-bit.description"), + "win.64Bit", + Boolean.class, + params -> System.getProperty("os.arch").contains("64"), + (s, p) -> Boolean.valueOf(s) + ); + + static final StandardBundlerParam BIT_ARCH_64_RUNTIME = + new StandardBundlerParam<>( + I18N.getString("param.runtime-64-bit.name"), + I18N.getString("param.runtime-64-bit.description"), + "win.64BitJreRuntime", + Boolean.class, + params -> { + WinAppBundler.extractFlagsFromRuntime(params); + return "64".equals(params.get(".runtime.bit-arch")); + }, + (s, p) -> Boolean.valueOf(s) + ); + + static final BundlerParamInfo INSTALLDIR_CHOOSER = + new StandardBundlerParam<> ( + I18N.getString("param.installdir-chooser.name"), + I18N.getString("param.installdir-chooser.description"), + Arguments.CLIOptions.WIN_DIR_CHOOSER.getId(), + Boolean.class, + params -> Boolean.FALSE, + (s, p) -> Boolean.valueOf(s) + ); +} diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WindowsDefender.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WindowsDefender.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2012, 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.internal; + +import jdk.jpackage.internal.Platform; +import java.util.List; + +final class WindowsDefender { + + private WindowsDefender() {} + + static final boolean isThereAPotentialWindowsDefenderIssue() { + 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() && + !isTempDirectoryInExclusionPath()) { + result = true; + } + } + + return result; + } + + private static boolean isTempDirectoryInExclusionPath() { + boolean result = false; + // If the user temp directory is not found in the exclusion + // list then there may be a problem. + List paths = WindowsRegistry.readExclusionsPaths(); + String tempDirectory = getUserTempDirectory(); + + for (String s : paths) { + if (s.equals(tempDirectory)) { + result = true; + break; + } + } + + return result; + } + + static final String getUserTempDirectory() { + String tempDirectory = System.getProperty("java.io.tmpdir"); + return tempDirectory; + } +} diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WindowsRegistry.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WindowsRegistry.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2012, 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.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; + +import static jdk.jpackage.internal.IOUtils.exec; + +final class WindowsRegistry { + + private WindowsRegistry() {} + + /** + * Reads the registry value for DisableRealtimeMonitoring. + * @return true if DisableRealtimeMonitoring is set to 0x1, + * false otherwise. + */ + static final boolean readDisableRealtimeMonitoring() { + boolean result = false; + final String key = "HKEY_LOCAL_MACHINE\\Software\\Microsoft\\" + + "Windows Defender\\Real-Time Protection"; + final String subkey = "DisableRealtimeMonitoring"; + String value = readRegistry(key, subkey); + + if (!value.isEmpty()) { + // This code could be written better but this works. It validates + // that the result of readRegistry returned what we expect and then + // checks for a 0x0 or 0x1. 0x0 means real time monitoring is + // on, 0x1 means it is off. So this function returns true if + // real-time-monitoring is disabled. + int index = value.indexOf(subkey); + value = value.substring(index + subkey.length()); + String reg = "REG_DWORD"; + index = value.indexOf(reg); + value = value.substring(index + reg.length()); + String hex = "0x"; + index = value.indexOf(hex); + value = value.substring(index + hex.length()); + + if (value.equals("1")) { + result = true; + } + } + + return result; + } + + static final List readExclusionsPaths() { + List result = new ArrayList(); + final String key = "HKEY_LOCAL_MACHINE\\Software\\Microsoft\\" + + "Windows Defender\\Exclusions\\Paths"; + String value = readRegistry(key, ""); + + if (!value.isEmpty()) { + final String reg = "REG_DWORD"; + final String hex = "0x0"; + + int index = value.indexOf(key); + if (index == 0) { + value = value.substring(index + key.length()); + + while (value.length() > 0) { + index = value.indexOf(reg); + String name = value.substring(0, index); + value = value.substring(index + reg.length()); + index = value.indexOf(hex); + value = value.substring(index + hex.length()); + + if (index > 0) { + name = name.trim(); + result.add(name); + } + } + } + } + + return result; + } + + /** + * @param key in the registry + * @param subkey in the registry key + * @return registry value or null if not found + */ + static final String readRegistry(String key, String subkey){ + String result = ""; + + try { + List buildOptions = new ArrayList<>(); + buildOptions.add("reg"); + buildOptions.add("query"); + buildOptions.add("\"" + key + "\""); + + if (!subkey.isEmpty()) { + buildOptions.add("/v"); + buildOptions.add(subkey); + } + + try (ByteArrayOutputStream baos = new ByteArrayOutputStream(); + PrintStream ps = new PrintStream(baos)) { + ProcessBuilder security = new ProcessBuilder(buildOptions); + exec(security, false, false, ps); + BufferedReader bfReader = new BufferedReader( + new InputStreamReader( + new ByteArrayInputStream(baos.toByteArray()))); + String line = null; + + while((line = bfReader.readLine()) != null){ + result += line; + } + } + catch (IOException e) { + } + } + catch (Exception e) { + } + + return result; + } +} diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinLauncher.template --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinLauncher.template Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,38 @@ +# +# Copyright (c) 2017, 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. +# +# + +Comments=COMMENTS +CompanyName=COMPANY_NAME +FileDescription=FILE_DESCRIPTION +FileVersion=FILE_VERSION +InternalName=INTERNAL_NAME +LegalCopyright=LEGAL_COPYRIGHT +LegalTrademarks=LEGAL_TRADEMARK +OriginalFilename=ORIGINAL_FILENAME +PrivateBuild=PRIVATE_BUILD +ProductName=PRODUCT_NAME +ProductVersion=PRODUCT_VERSION +SpecialBuild=SPECIAL_BUILD diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources.properties --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources.properties Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,138 @@ +# +# Copyright (c) 2017, 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. +# +# + +app.bundler.name=Windows Application Image +app.bundler.description=A Directory based image of a windows Application with an optionally co-bundled JRE. Used as a base for the Installer bundlers +exe.bundler.name=EXE Installer +exe.bundler.description=Microsoft Windows EXE Installer, via InnoIDE. +msi.bundler.name=MSI Installer +msi.bundler.description=Microsoft Windows MSI Installer, via WiX. + +param.raw-executable-url.name=Launcher URL +param.raw-executable-url.description=Override the jpackage default launcher with a custom launcher. +param.rebrand-executable.name=Rebrand Launcher +param.rebrand-executable.description=Update the launcher with the application icon and update ownership information. +param.icon-ico.name=.ico Icon +param.icon-ico.description=Icon for the application, in ICO format. +param.console-hint.name=Console Hint +param.console-hint.description=Indicates if the bundler should use console launcher +param.menu-group.name=Menu Group +param.menu-group.description=The Start Menu group this application should be placed in +param.menu-group.default=Unknown +param.64-bit.name=64-bit +param.64-bit.description=Prepare the bundles for 64 bit windows. +param.runtime-64-bit.name=runtime 64-bit +param.runtime-64-bit.description=Embedded JRE runtime is 64-bit, used to detect bit architecture mismatches. +param.installer-name.name=Installer Name +param.installer-name.description=The filename of the generated installer without the file type extension. Default is -. +param.registry-name.name=Registry Name +param.registry-name.description=The name of the application for registry references. Default is the Application Name with only alphanumerics, dots, and dashes (no whitespace). +param.installdir-chooser.name=Install Directory Chooser +param.installdir-chooser.description=Adds a dialog to let the user choose a directory where the product will be installed. +param.system-wide.name=System Wide +param.system-wide.description=Should this application attempt to install itself system wide, or only for each user? Null means use the system default. +param.exe-bundler.name=Exe Installer Bundler +param.exe-bundler.description=Exe Installer Bundler +param.image-dir.name=Image Dir +param.image-dir.description=Image Dir +param.app-dir.name=App Dir +param.app-dir.description=App Dir +param.menu-shortcut-hint.name=Menu Hint +param.menu-shortcut-hint.description=If the bundler can add the application to the system menu, should it? +param.desktop-shortcut-hint.name=Shortcut Hint +param.desktop-shortcut-hint.description=If the bundler can create desktop shortcuts, should it make one? +param.upgrade-uuid.name=Upgrade UUID +param.upgrade-uuid.description=The UUID associated with upgrades for this package. +param.product-version.name=Product Version +param.product-version.description=The version of the application as seen by Windows and MSI, of the form "1.2.3" +param.iscc-path.name=InnoSetup iscc.exe location +param.iscc-path.description=File path to iscc.exe from the InnoSetup tool. +param.msi-bundler.name=MSI App Bundler +param.msi-bundler.description=MSI App Bundler +param.can-use-wix36.name=Can Use Wix +param.can-use-wix36.description=Can Use Wix +param.candle-path.name=WiX candle.exe location +param.candle-path.description=File path to candle.exe from the WiX toolset. +param.light-path.name=WiX light.exe location +param.light-path.description=File path to light.exe from the WiX toolset. + +resource.application-icon=application icon +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.wix-config-file=WiX config file + +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.bit-architecture-mismatch=Bit architecture mismatch between FX SDK and JRE runtime. +error.bit-architecture-mismatch.advice=Make sure to use JRE runtime with correct bit architecture. +error.cannot-find-launcher=Cannot find cfg file in predefined app image directory {0}. +error.cannot-create-output-dir=Output directory {0} cannot be created. +error.cannot-write-to-output-dir=Output directory {0} is not writable. +error.iscc-not-found=Can not find Inno Setup Compiler (iscc.exe). +error.iscc-not-found.advice=Download Inno Setup 5 or later from http\://www.jrsoftware.org and add it to the PATH. +error.copyright-is-too-long=The copyright string is too long for InnoSetup. +error.copyright-is-too-long.advice=Provide a copyright string shorter than 100 characters. +error.too-many-content-types-for-file-association=More than one MIME types was specified for File Association number {0}. +error.too-many-content-types-for-file-association.advice=Specify one and only one MIME type for each file association. +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 + + + +message.result-dir=Result application bundle\: {0} +message.disable-bit-architecture-check=Disabled check for bit architecture mismatch. +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} + diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources_ja.properties --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources_ja.properties Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,138 @@ +# +# Copyright (c) 2017, 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. +# +# + +app.bundler.name=Windows Application Image +app.bundler.description=A Directory based image of a windows Application with an optionally co-bundled JRE. Used as a base for the Installer bundlers +exe.bundler.name=EXE Installer +exe.bundler.description=Microsoft Windows EXE Installer, via InnoIDE. +msi.bundler.name=MSI Installer +msi.bundler.description=Microsoft Windows MSI Installer, via WiX. + +param.raw-executable-url.name=Launcher URL +param.raw-executable-url.description=Override the jpackage default launcher with a custom launcher. +param.rebrand-executable.name=Rebrand Launcher +param.rebrand-executable.description=Update the launcher with the application icon and update ownership information. +param.icon-ico.name=.ico Icon +param.icon-ico.description=Icon for the application, in ICO format. +param.console-hint.name=Console Hint +param.console-hint.description=Indicates if the bundler should use console launcher +param.menu-group.name=Menu Group +param.menu-group.description=The Start Menu group this application should be placed in +param.menu-group.default=Unknown +param.64-bit.name=64-bit +param.64-bit.description=Prepare the bundles for 64 bit windows. +param.runtime-64-bit.name=runtime 64-bit +param.runtime-64-bit.description=Embedded JRE runtime is 64-bit, used to detect bit architecture mismatches. +param.installer-name.name=Installer Name +param.installer-name.description=The filename of the generated installer without the file type extension. Default is -. +param.registry-name.name=Registry Name +param.registry-name.description=The name of the application for registry references. Default is the Application Name with only alphanumerics, dots, and dashes (no whitespace). +param.installdir-chooser.name=Install Directory Chooser +param.installdir-chooser.description=Adds a dialog to let the user choose a directory where the product will be installed. +param.system-wide.name=System Wide +param.system-wide.description=Should this application attempt to install itself system wide, or only for each user? Null means use the system default. +param.exe-bundler.name=Exe Installer Bundler +param.exe-bundler.description=Exe Installer Bundler +param.image-dir.name=Image Dir +param.image-dir.description=Image Dir +param.app-dir.name=App Dir +param.app-dir.description=App Dir +param.menu-shortcut-hint.name=Menu Hint +param.menu-shortcut-hint.description=If the bundler can add the application to the system menu, should it? +param.desktop-shortcut-hint.name=Shortcut Hint +param.desktop-shortcut-hint.description=If the bundler can create desktop shortcuts, should it make one? +param.upgrade-uuid.name=Upgrade UUID +param.upgrade-uuid.description=The UUID associated with upgrades for this package. +param.product-version.name=Product Version +param.product-version.description=The version of the application as seen by Windows and MSI, of the form "1.2.3" +param.iscc-path.name=InnoSetup iscc.exe location +param.iscc-path.description=File path to iscc.exe from the InnoSetup tool. +param.msi-bundler.name=MSI App Bundler +param.msi-bundler.description=MSI App Bundler +param.can-use-wix36.name=Can Use Wix +param.can-use-wix36.description=Can Use Wix +param.candle-path.name=WiX candle.exe location +param.candle-path.description=File path to candle.exe from the WiX toolset. +param.light-path.name=WiX light.exe location +param.light-path.description=File path to light.exe from the WiX toolset. + +resource.application-icon=application icon +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.wix-config-file=WiX config file + +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.bit-architecture-mismatch=Bit architecture mismatch between FX SDK and JRE runtime. +error.bit-architecture-mismatch.advice=Make sure to use JRE runtime with correct bit architecture. +error.cannot-find-launcher=Cannot find cfg file in predefined app image directory {0}. +error.cannot-create-output-dir=Output directory {0} cannot be created. +error.cannot-write-to-output-dir=Output directory {0} is not writable. +error.iscc-not-found=Can not find Inno Setup Compiler (iscc.exe). +error.iscc-not-found.advice=Download Inno Setup 5 or later from http\://www.jrsoftware.org and add it to the PATH. +error.copyright-is-too-long=The copyright string is too long for InnoSetup. +error.copyright-is-too-long.advice=Provide a copyright string shorter than 100 characters. +error.too-many-content-types-for-file-association=More than one MIME types was specified for File Association number {0}. +error.too-many-content-types-for-file-association.advice=Specify one and only one MIME type for each file association. +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 + + + +message.result-dir=Result application bundle\: {0} +message.disable-bit-architecture-check=Disabled check for bit architecture mismatch. +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} + diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources_zh_CN.properties --- /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 Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,138 @@ +# +# Copyright (c) 2017, 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. +# +# + +app.bundler.name=Windows Application Image +app.bundler.description=A Directory based image of a windows Application with an optionally co-bundled JRE. Used as a base for the Installer bundlers +exe.bundler.name=EXE Installer +exe.bundler.description=Microsoft Windows EXE Installer, via InnoIDE. +msi.bundler.name=MSI Installer +msi.bundler.description=Microsoft Windows MSI Installer, via WiX. + +param.raw-executable-url.name=Launcher URL +param.raw-executable-url.description=Override the jpackage default launcher with a custom launcher. +param.rebrand-executable.name=Rebrand Launcher +param.rebrand-executable.description=Update the launcher with the application icon and update ownership information. +param.icon-ico.name=.ico Icon +param.icon-ico.description=Icon for the application, in ICO format. +param.console-hint.name=Console Hint +param.console-hint.description=Indicates if the bundler should use console launcher +param.menu-group.name=Menu Group +param.menu-group.description=The Start Menu group this application should be placed in +param.menu-group.default=Unknown +param.64-bit.name=64-bit +param.64-bit.description=Prepare the bundles for 64 bit windows. +param.runtime-64-bit.name=runtime 64-bit +param.runtime-64-bit.description=Embedded JRE runtime is 64-bit, used to detect bit architecture mismatches. +param.installer-name.name=Installer Name +param.installer-name.description=The filename of the generated installer without the file type extension. Default is -. +param.registry-name.name=Registry Name +param.registry-name.description=The name of the application for registry references. Default is the Application Name with only alphanumerics, dots, and dashes (no whitespace). +param.installdir-chooser.name=Install Directory Chooser +param.installdir-chooser.description=Adds a dialog to let the user choose a directory where the product will be installed. +param.system-wide.name=System Wide +param.system-wide.description=Should this application attempt to install itself system wide, or only for each user? Null means use the system default. +param.exe-bundler.name=Exe Installer Bundler +param.exe-bundler.description=Exe Installer Bundler +param.image-dir.name=Image Dir +param.image-dir.description=Image Dir +param.app-dir.name=App Dir +param.app-dir.description=App Dir +param.menu-shortcut-hint.name=Menu Hint +param.menu-shortcut-hint.description=If the bundler can add the application to the system menu, should it? +param.desktop-shortcut-hint.name=Shortcut Hint +param.desktop-shortcut-hint.description=If the bundler can create desktop shortcuts, should it make one? +param.upgrade-uuid.name=Upgrade UUID +param.upgrade-uuid.description=The UUID associated with upgrades for this package. +param.product-version.name=Product Version +param.product-version.description=The version of the application as seen by Windows and MSI, of the form "1.2.3" +param.iscc-path.name=InnoSetup iscc.exe location +param.iscc-path.description=File path to iscc.exe from the InnoSetup tool. +param.msi-bundler.name=MSI App Bundler +param.msi-bundler.description=MSI App Bundler +param.can-use-wix36.name=Can Use Wix +param.can-use-wix36.description=Can Use Wix +param.candle-path.name=WiX candle.exe location +param.candle-path.description=File path to candle.exe from the WiX toolset. +param.light-path.name=WiX light.exe location +param.light-path.description=File path to light.exe from the WiX toolset. + +resource.application-icon=application icon +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.wix-config-file=WiX config file + +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.bit-architecture-mismatch=Bit architecture mismatch between FX SDK and JRE runtime. +error.bit-architecture-mismatch.advice=Make sure to use JRE runtime with correct bit architecture. +error.cannot-find-launcher=Cannot find cfg file in predefined app image directory {0}. +error.cannot-create-output-dir=Output directory {0} cannot be created. +error.cannot-write-to-output-dir=Output directory {0} is not writable. +error.iscc-not-found=Can not find Inno Setup Compiler (iscc.exe). +error.iscc-not-found.advice=Download Inno Setup 5 or later from http\://www.jrsoftware.org and add it to the PATH. +error.copyright-is-too-long=The copyright string is too long for InnoSetup. +error.copyright-is-too-long.advice=Provide a copyright string shorter than 100 characters. +error.too-many-content-types-for-file-association=More than one MIME types was specified for File Association number {0}. +error.too-many-content-types-for-file-association.advice=Specify one and only one MIME type for each file association. +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 + + + +message.result-dir=Result application bundle\: {0} +message.disable-bit-architecture-check=Disabled check for bit architecture mismatch. +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} + diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/icon_inno_setup.bmp Binary file src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/icon_inno_setup.bmp has changed diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/javalogo_white_48.ico Binary file src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/javalogo_white_48.ico has changed diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/template.iss --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/template.iss Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,72 @@ +;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_COMMENTS +AppCopyright=APPLICATION_COPYRIGHT +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=INSTALLER_NAME\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\LAUNCHER_NAME.exe"; DestDir: "{app}"; Flags: ignoreversion +Source: "INSTALLER_NAME\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs + +[Icons] +Name: "{group}\INSTALLER_NAME"; Filename: "{app}\LAUNCHER_NAME.exe"; IconFilename: "{app}\LAUNCHER_NAME.ico"; Check: APPLICATION_MENU_SHORTCUT() +Name: "{commondesktop}\INSTALLER_NAME"; Filename: "{app}\LAUNCHER_NAME.exe"; IconFilename: "{app}\LAUNCHER_NAME.ico"; Check: APPLICATION_DESKTOP_SHORTCUT() +SECONDARY_LAUNCHERS + +[Run] +Filename: "{app}\RUN_FILENAME.exe"; Parameters: "-Xappcds:generatecache"; Check: APPLICATION_APP_CDS_INSTALL() +Filename: "{app}\RUN_FILENAME.exe"; Description: "{cm:LaunchProgram,INSTALLER_NAME}"; Flags: nowait postinstall skipifsilent; Check: APPLICATION_NOT_SERVICE() +Filename: "{app}\RUN_FILENAME.exe"; Parameters: "-install -svcName ""INSTALLER_NAME"" -svcDesc ""APPLICATION_DESCRIPTION"" -mainExe ""APPLICATION_LAUNCHER_FILENAME"" START_ON_INSTALL RUN_AT_STARTUP"; Check: APPLICATION_SERVICE() + +[UninstallRun] +Filename: "{app}\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; diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/template.jre.iss --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/template.jre.iss Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,55 @@ +;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_COMMENTS +AppCopyright=APPLICATION_COPYRIGHT +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; diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/template.jre.wxs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/template.jre.wxs Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + WIX36_ONLY_START + + WIX36_ONLY_END + + + +UI_BLOCK + + diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/template.wxs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/template.wxs Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + WIX36_ONLY_START + + WIX36_ONLY_END + + + +UI_BLOCK + + +SECONDARY_LAUNCHER_ICONS + + diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/windows/classes/module-info.java.extra --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/windows/classes/module-info.java.extra Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,30 @@ +/* + * Copyright (c) 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. + */ + +provides jdk.jpackage.internal.Bundler with + jdk.jpackage.internal.WinAppBundler, + jdk.jpackage.internal.WinExeBundler, + jdk.jpackage.internal.WinMsiBundler; + diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/windows/native/jpackage/ByteBuffer.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/windows/native/jpackage/ByteBuffer.cpp Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,77 @@ +/* +* Copyright (c) 2015, 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. +*/ + +#include "ByteBuffer.h" + +#include + +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); + } +} diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/windows/native/jpackage/ByteBuffer.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/windows/native/jpackage/ByteBuffer.h Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,56 @@ +/* +* Copyright (c) 2015, 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. +*/ + +#ifndef BYTEBUFFER_H +#define BYTEBUFFER_H + +#include +#include + +using std::wstring; + +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: + std::vector buffer; +}; + +#endif // BYTEBUFFER_H \ No newline at end of file diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/windows/native/jpackage/IconSwap.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/windows/native/jpackage/IconSwap.cpp Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,229 @@ +/* + * Copyright (c) 2012, 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. + */ + +// iconswap.cpp : Defines the entry point for the console application. +// + +//Define Windows compatibility requirements +//XP or later +#define WINVER 0x0501 +#define _WIN32_WINNT 0x0501 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +// 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; + DWORD error = GetLastError(); + + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, NULL, error, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR) &message, 0, NULL); + + wprintf(L"%s\n", (__wchar_t *) message); // VS2017 - FIXME + LocalFree(message); +} + +bool ChangeIcon(_TCHAR* iconFileName, _TCHAR* executableFileName) +{ + bool result = false; + + DWORD dwData = 1; + WORD language = MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT); + + _TCHAR* iconExtension = wcsrchr(iconFileName, '.'); + if (iconExtension == NULL || wcscmp(iconExtension, L".ico") != 0) { + wprintf(L"Unknown icon format - please provide .ICO file.\n"); + return result; + } + + HANDLE icon = CreateFile(iconFileName, GENERIC_READ, 0, NULL, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (icon == INVALID_HANDLE_VALUE) { + PrintError(); + return result; + } + + // 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); + wprintf(L"Unknown error.\n"); + } + + 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); + wprintf(L"Unknown error.\n"); + } + + 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( executableFileName, FALSE ); + + if (update == NULL) { + free(lpid); + free(lpgid); + CloseHandle(icon); + PrintError(); + return result; + } + + 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 result; + } + 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 result; + } + + free(lpgid); + + if (EndUpdateResource(update, FALSE) == FALSE) { + PrintError(); + return result; + } + + result = true; + return result; +} diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/windows/native/jpackage/IconSwap.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/windows/native/jpackage/IconSwap.h Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,33 @@ +/* +* 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. +*/ + +#ifndef ICONSWAP_H +#define ICONSWAP_H + +#include + +bool ChangeIcon(_TCHAR* iconFileName, _TCHAR* executableFileName); + +#endif // ICONSWAP_H \ No newline at end of file diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/windows/native/jpackage/VersionInfoSwap.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/windows/native/jpackage/VersionInfoSwap.cpp Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,298 @@ +/* +* Copyright (c) 2015, 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. +*/ + +#include "VersionInfoSwap.h" + +#include +#include + +#include +#include +#include +#include +#include +#include + + +/* + * Usage: VersionInfoSwap.exe [Property file] [Executable file] + * + * [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. + * + */ + +bool VersionInfoSwap::PatchExecutable() { + bool b = LoadFromPropertyFile(); + if (!b) { + return false; + } + + ByteBuffer buf; + CreateNewResource(&buf); + b = this->UpdateResource(buf.getPtr(), static_cast(buf.getPos())); + if (!b) { + return false; + } + return true; +} + +bool VersionInfoSwap::LoadFromPropertyFile() { + + bool result = false; + std::wifstream stream(m_propFileName.data()); + + const std::locale empty_locale = std::locale::empty(); + const std::locale utf8_locale = + std::locale(empty_locale, new std::codecvt_utf8()); + stream.imbue(utf8_locale); + + if (stream.is_open() == true) { + int lineNumber = 1; + while (stream.eof() == false) { + wstring line; + std::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; + } else { + fwprintf(stderr, TEXT("Unable to find delimiter at line %d\n"), lineNumber); + } + } + lineNumber++; + } + result = true; + } else { + fwprintf(stderr, TEXT("Unable to read property file\n")); + } + + return result; +} + + +/* + * Creates new version resource + * + * MSND docs for VS_VERSION_INFO structure + * https://msdn.microsoft.com/en-us/library/ms647001(v=vs.85).aspx + */ +void 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; + FillFixedFileInfo(&fxi); + 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 + std::vector keys; + for (std::map::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(value.length())); + buf->AppendWORD(1); + buf->AppendString(name); + buf->Align(4); + buf->AppendString(value); + buf->ReplaceWORD(stringStart, + static_cast(buf->getPos() - stringStart)); + buf->Align(4); + } + + buf->ReplaceWORD(stringTableStart, + static_cast(buf->getPos() - stringTableStart)); + buf->ReplaceWORD(stringFileInfoStart, + static_cast(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); + // "040904B0" = LANG_ENGLISH/SUBLANG_ENGLISH_US, Unicode CP + buf->AppendWORD(0x0409); + buf->AppendWORD(0x04B0); + + buf->ReplaceWORD(varFileInfoStart, + static_cast(buf->getPos() - varFileInfoStart)); + buf->ReplaceWORD(versionInfoStart, + static_cast(buf->getPos() - versionInfoStart)); +} + +void 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) { + fwprintf(stderr, TEXT("Unable to parse FileVersion value\n")); + } + + ret = _stscanf_s(productVersion.c_str(), + TEXT("%d.%d.%d.%d"), &pv_1, &pv_2, &pv_3, &pv_4); + if (ret <= 0 || ret > 4) { + fwprintf(stderr, TEXT("Unable to parse ProductVersion value\n")); + } + + 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; + if (m_props.count(TEXT("PrivateBuild"))) { + fxi->dwFileFlags |= VS_FF_PRIVATEBUILD; + } + if (m_props.count(TEXT("SpecialBuild"))) { + fxi->dwFileFlags |= VS_FF_SPECIALBUILD; + } + fxi->dwFileOS = VOS_NT_WINDOWS32; + + wstring exeExt = + m_exeFileName.substr(m_exeFileName.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; +} + +/* + * Adds new resource in the executable + */ +bool VersionInfoSwap::UpdateResource(LPVOID lpResLock, DWORD size) { + + HANDLE hUpdateRes; + BOOL r; + + hUpdateRes = ::BeginUpdateResource(m_exeFileName.c_str(), FALSE); + if (hUpdateRes == NULL) { + fwprintf(stderr, TEXT("Could not open file for writing\n")); + return false; + } + + r = ::UpdateResource(hUpdateRes, + RT_VERSION, + MAKEINTRESOURCE(VS_VERSION_INFO), + MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), + lpResLock, + size); + + if (!r) { + fwprintf(stderr, TEXT("Could not add resource\n")); + return false; + } + + if (!::EndUpdateResource(hUpdateRes, FALSE)) { + fwprintf(stderr, TEXT("Could not write changes to file\n")); + return false; + } + + return true; +} + +VersionInfoSwap::VersionInfoSwap(TCHAR *propFileName, TCHAR *exeFileName) +{ + m_propFileName = propFileName; + m_exeFileName = exeFileName; +} + +VersionInfoSwap::~VersionInfoSwap() +{ +} diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/windows/native/jpackage/VersionInfoSwap.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/windows/native/jpackage/VersionInfoSwap.h Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,51 @@ +/* +* Copyright (c) 2015, 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. +*/ + +#ifndef VERSIONINFOSWAP_H +#define VERSIONINFOSWAP_H + +#include "ByteBuffer.h" +#include + +class VersionInfoSwap { +public: + VersionInfoSwap(TCHAR *propFileName, TCHAR *exeFileName); + ~VersionInfoSwap(); + + bool PatchExecutable(); + +private: + wstring m_propFileName; + wstring m_exeFileName; + + std::map m_props; + + bool LoadFromPropertyFile(); + void CreateNewResource(ByteBuffer *buf); + bool UpdateResource(LPVOID lpResLock, DWORD size); + void FillFixedFileInfo(VS_FIXEDFILEINFO *fxi); +}; + +#endif // VERSIONINFOSWAP_H \ No newline at end of file diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/windows/native/jpackage/jpackage.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/windows/native/jpackage/jpackage.cpp Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,578 @@ +/* + * Copyright (c) 2011, 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. + */ + +#include +#include +#include +#include + +#include "IconSwap.h" +#include "VersionInfoSwap.h" + +#ifdef DEBUG +#include +#include +#endif + +using namespace std; + +#define MAX_KEY_LENGTH 255 +#define MAX_VALUE_NAME 16383 +#define TRAILING_PATHSEPARATOR '\\' + +bool from_string(int &result, string &str) { + const char *p = str.c_str(); + int res = 0; + for (int index = 0;; index++) { + char c = str[index]; + if (c == 0 && index > 0) { + result = res; + return true; + } + if (c < '0' || c > '9') + return false; + res = res * 10 + (c - '0'); + } +} + +void PrintCSBackupAPIErrorMessage(DWORD dwErr) { + + char wszMsgBuff[512]; // Buffer for text. + + DWORD dwChars; // Number of chars returned. + + // Try to get the message from the system errors. + dwChars = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + dwErr, + 0, + wszMsgBuff, + 512, + NULL); + + if (0 == dwChars) { + // The error code did not exist in the system errors. + // Try ntdsbmsg.dll for the error code. + HINSTANCE hInst; + + // Load the library. + hInst = LoadLibraryA("ntdsbmsg.dll"); + if (NULL == hInst) { +#ifdef DEBUG + cerr << "cannot load ntdsbmsg.dll\n"; +#endif + return; + } + + // Try getting message text from ntdsbmsg. + dwChars = FormatMessageA(FORMAT_MESSAGE_FROM_HMODULE | + FORMAT_MESSAGE_IGNORE_INSERTS, + hInst, + dwErr, + 0, + wszMsgBuff, + 512, + NULL); + + // Free the library. + FreeLibrary(hInst); + } + + // Display the error message, or generic text if not found. +#ifdef DEBUG + cerr << "Error value: " << dwErr << " Message: " << ((dwChars > 0) ? wszMsgBuff : "Error message not found.") << endl; +#endif +} + +class JavaVersion { +public: + int v1; + int v2; + int v3; + std::wstring home; + std::wstring path; + + JavaVersion(int pv1, int pv2, int pv3) { + v1 = pv1; + v2 = pv2; + v3 = pv3; + } + + bool operator>(const JavaVersion &other) const { + if (v1 > other.v1) + return true; + if (v1 == other.v1) { + if (v2 > other.v2) + return true; + if (v2 == other.v2) + return v3 > other.v3; + } + return false; + } + + bool operator>=(const JavaVersion &other) const { + if (v1 > other.v1) + return true; + if (v1 == other.v1) { + if (v2 > other.v2) + return true; + if (v2 == other.v2) + return v3 >= other.v3; + } + return false; + } + + bool operator<(const JavaVersion &other) const { + if (v1 < other.v1) + return true; + if (v1 == other.v1) { + if (v2 < other.v2) + return true; + if (v2 == other.v2) + return v3 < other.v3; + } + return false; + } +}; + +class EnvironmentVariable { +private: + std::wstring FValue; + +public: + EnvironmentVariable(std::wstring Name) { + wchar_t* value; + size_t requiredSize; + + _wgetenv_s(&requiredSize, NULL, 0, Name.data()); + + if (requiredSize != 0) { + value = (wchar_t*)malloc(requiredSize * sizeof(wchar_t)); + if (value) + { + // Get the value of the LIB environment variable. + _wgetenv_s(&requiredSize, value, requiredSize, Name.data()); + FValue = value; + } + } + } + + std::wstring get() { + return FValue; + } + + bool exists() { + return !FValue.empty(); + } +}; + +bool checkJavaHome(HKEY key, const char * sKey, const char * jv, + JavaVersion *version) { + char p[MAX_KEY_LENGTH]; + HKEY hKey; + bool result = false; + int res; + + strcpy_s(p, MAX_KEY_LENGTH, sKey); + strcat_s(p, MAX_KEY_LENGTH - strlen(p), "\\"); + strcat_s(p, MAX_KEY_LENGTH - strlen(p), jv); + + if (RegOpenKeyExA(key, + p, + 0, + KEY_READ, + &hKey) == ERROR_SUCCESS + ) { + DWORD ot = REG_SZ; + DWORD size = 255; + wchar_t data[MAX_PATH] = { 0 }; + if ((res = RegQueryValueEx(hKey, L"JavaHome", NULL, &ot, + (BYTE *)data, &size)) == ERROR_SUCCESS) { + version->home = data; + std::wstring ldata = std::wstring(data) + L"\\bin\\java.exe"; + version->path = data; + result = GetFileAttributes(data) != 0xFFFFFFFF; + } + else { + PrintCSBackupAPIErrorMessage(res); + result = false; + } + RegCloseKey(hKey); + } + else { +#ifdef DEBUG + cerr << "Can not open registry key" << endl; +#endif + result = false; + } + + return result; +} + +JavaVersion * parseName(const char * jName) { + string s(jName); + + if (s.length() == 0) { + return NULL; + } + + string n; + string::size_type pos; + + pos = s.find_first_of("."); + if (pos != string::npos) { + n = s.substr(0, pos); + s = s.substr(pos + 1); + } + else { + n = s; + s = ""; + } + + int v1 = 0; + + if (n.length() > 0) { + if (!from_string(v1, n)) + return NULL; + } + + + pos = s.find_first_of("."); + if (pos != string::npos) { + n = s.substr(0, pos); + s = s.substr(pos + 1); + } + else { + n = s; + s = ""; + } + + int v2 = 0; + + if (n.length() > 0) { + if (!from_string(v2, n)) + return NULL; + } + + + size_t nn = s.length(); + for (size_t i = 0; i < s.length(); i++) { + string c = s.substr(i, 1); + int tmp; + if (!from_string(tmp, c)) { + nn = i; + break; + } + } + + n = s.substr(0, nn); + if (nn < s.length()) { + s = s.substr(nn + 1); + } + else s = ""; + + int v3 = 0; + + if (n.length() > 0) { + if (!from_string(v3, n)) + v3 = 0; + } + + int v4 = 0; + + // update version + if (s.length() > 0) { + nn = s.length(); + for (size_t i = 0; i < s.length(); i++) { + string c = s.substr(i, 1); + int tmp; + if (!from_string(tmp, c)) { + nn = i; + break; + } + } + + n = s.substr(0, nn); + + if (n.length() > 0) { + if (!from_string(v4, n)) + v4 = 0; + } + } + + return new JavaVersion(v2, v3, v4); +} + +JavaVersion * GetMaxVersion(HKEY key, const char * sKey) { + HKEY hKey; + JavaVersion * result = NULL; + + if (RegOpenKeyExA(key, + sKey, + 0, + KEY_READ, + &hKey) == ERROR_SUCCESS + ) { + DWORD retCode; + char achClass[MAX_PATH]; // buffer for class name + DWORD cchClassName = MAX_PATH; // size of class string + + + DWORD cchValue = MAX_VALUE_NAME; + DWORD cSubKeys = 0; // number of subkeys + DWORD cbMaxSubKey; // longest subkey size + DWORD cchMaxClass; // longest class string + DWORD cValues; // number of values for key + DWORD cchMaxValue; // longest value name + DWORD cbMaxValueData; // longest value data + DWORD cbSecurityDescriptor; // size of security descriptor + FILETIME ftLastWriteTime; // last write time + + retCode = RegQueryInfoKeyA( + hKey, // key handle + achClass, // buffer for class name + &cchClassName, // size of class string + NULL, // reserved + &cSubKeys, // number of subkeys + &cbMaxSubKey, // longest subkey size + &cchMaxClass, // longest class string + &cValues, // number of values for this key + &cchMaxValue, // longest value name + &cbMaxValueData, // longest value data + &cbSecurityDescriptor, // security descriptor + &ftLastWriteTime); // last write time + + if (cSubKeys) { + for (unsigned int i = 0; i < cSubKeys; i++) { + char achKey[MAX_KEY_LENGTH]; // buffer for subkey name + DWORD cbName = MAX_KEY_LENGTH; + retCode = RegEnumKeyExA(hKey, i, + achKey, + &cbName, + NULL, + NULL, + NULL, + &ftLastWriteTime); + + if (retCode == ERROR_SUCCESS) { +#ifdef DEBUG + cout << achKey << endl; +#endif + JavaVersion * nv = parseName(achKey); + + bool isHome = checkJavaHome(key, sKey, achKey, nv); +#ifdef DEBUG + wcout << nv->home << " " << isHome << endl; +#endif + + if (isHome) + if (result == NULL) { + result = nv; +#ifdef DEBUG + cout << "NEW" << endl; +#endif + } + else { + if (nv != NULL) { + if (*nv > *result) { +#ifdef DEBUG + cout << "REPLACE" << endl; +#endif + delete result; + result = nv; + } + else { +#ifdef DEBUG + cout << "NO" << endl; +#endif + delete nv; + } + } + } + } + } + } + + RegCloseKey(hKey); + } + + return result; +} + +int fileExists(const std::wstring& path) { + WIN32_FIND_DATA ffd; + HANDLE hFind; + + hFind = FindFirstFile(path.data(), &ffd); + if (hFind == INVALID_HANDLE_VALUE) + return FALSE; + + FindClose(hFind); + return (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0; +} + +bool hasEnding(std::wstring const &fullString, std::wstring const &ending) { + if (fullString.length() >= ending.length()) { + return (0 == fullString.compare(fullString.length() - ending.length(), + ending.length(), ending)); + } + else { + return false; + } +} + +std::wstring ExtractFilePath(std::wstring Path) { + std::wstring result; + size_t slash = Path.find_last_of(TRAILING_PATHSEPARATOR); + if (slash != std::wstring::npos) + result = Path.substr(0, slash); + return result; +} + +std::wstring GetCurrentExecutableName() { + TCHAR FileName[MAX_PATH]; + GetModuleFileName(NULL, FileName, MAX_PATH); + return FileName; +} + +int wmain(int argc, wchar_t* argv[]) { + wchar_t buf[MAX_PATH]; + GetModuleFileName(NULL, buf, MAX_PATH); + + std::wstring javacmd; + std::wstring javahome; + + std::wstring exe = GetCurrentExecutableName(); + + if (exe.length() <= 0) { + JavaVersion * jv2 = GetMaxVersion(HKEY_LOCAL_MACHINE, + "SOFTWARE\\JavaSoft\\JDK"); + if (jv2 != NULL) { + javahome = jv2->home; + javacmd = javahome + L"\\bin\\" + L"\\java.exe"; + } + else { + javacmd = L"java.exe"; + } + } else { + javacmd = ExtractFilePath(exe) + L"\\java.exe"; + } + + std::wstring cmd = L"\"" + javacmd + L"\""; + if (javahome.length() > 0) { + SetEnvironmentVariable(L"JAVA_HOME", javahome.c_str()); + } + + std::wstring memory = L"-Xmx512M"; + std::wstring debug = L""; + std::wstring args = L""; + + for (int i = 1; i < argc; i++) { + std::wstring argument = argv[i]; + std::wstring debug_arg = L"-J-Xdebug:"; + std::wstring icon_swap_arg = L"--icon-swap"; + std::wstring version_swap_arg = L"--version-swap"; + + if (argument.find(L"-J-Xmx", 0) == 0) { + memory = argument.substr(2, argument.length() - 2); + } + else if (argument.find(debug_arg, 0) == 0) { + std::wstring address = argument.substr(debug_arg.length(), + argument.length() - debug_arg.length()); + debug = L"-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=" + address; + } + else if (argument.find(icon_swap_arg, 0) == 0) { + if (argc != 4) { + fwprintf(stderr, TEXT("Usage: jpackage.exe --icon-swap [Icon File Name] [Executable File Name]\n")); + return 1; + } + + wprintf(L"Icon File Name: %s\n", argv[i + 1]); + wprintf(L"Executable File Name: %s\n", argv[i + 2]); + + if (ChangeIcon(argv[i + 1], argv[i + 2]) == true) { + return 0; + } + else { + fwprintf(stderr, TEXT("failed\n")); + return 1; + } + } + else if (argument.find(version_swap_arg, 0) == 0) { + if (argc != 4) { + fwprintf(stderr, TEXT("Usage: jpackage.exe --version-swap [Property File Name] [Executable File Name]\n")); + return 1; + } + + fwprintf(stdout, TEXT("Resource File Name: %s\n"), argv[i + 1]); + fwprintf(stdout, TEXT("Executable File Name: %s\n"), argv[i + 2]); + + VersionInfoSwap vs(argv[i + 1], argv[i + 2]); + + if (vs.PatchExecutable()) { + return 0; + } + else { + fwprintf(stderr, TEXT("failed\n")); + return 1; + } + } + else { + args = args + L" \"" + argv[i] + L"\""; + } + } + + + cmd += debug + L" " + memory + + L" -m jdk.jpackage/jdk.jpackage.main.Main" + + L" " + args; + +#ifdef DEBUG + fwprintf (stdout, TEXT("%s\n"), cmd.c_str()); +#endif + + STARTUPINFO start; + PROCESS_INFORMATION pi; + memset(&start, 0, sizeof (start)); + start.cb = sizeof(start); + + if (!CreateProcess(NULL, (wchar_t *) cmd.data(), + NULL, NULL, TRUE, NORMAL_PRIORITY_CLASS, NULL, NULL, &start, &pi)) { +#ifdef DEBUG + fprintf(stderr, "Cannot start java.exe"); +#endif + return EXIT_FAILURE; + } + + WaitForSingleObject(pi.hProcess, INFINITE); + unsigned long exitCode; + GetExitCodeProcess(pi.hProcess, &exitCode); + + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + + return exitCode; +} diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/windows/native/jpackage/jpackage.manifest --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/windows/native/jpackage/jpackage.manifest Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,34 @@ + + + + Java Packaging Tool + + + + + + + + + + + + + true + + + + + + + + + + + diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/windows/native/jpackage/jpackage.rc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/windows/native/jpackage/jpackage.rc Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute 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" + +// Need 2 defines so macro argument to XSTR will get expanded before quoting. +#define XSTR(x) STR(x) +#define STR(x) #x + +LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION JDK_FVER + PRODUCTVERSION JDK_FVER + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + // FILEOS 0x4 is Win32, 0x40004 is Win32 NT only + FILEOS 0x4L + // FILETYPE should be 0x1 for .exe and 0x2 for .dll + FILETYPE 0x1L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "000004b0" + BEGIN + VALUE "CompanyName", XSTR(JDK_COMPANY) "\0" + VALUE "FileDescription", XSTR(JDK_COMPONENT) "\0" + VALUE "FileVersion", XSTR(JDK_VER) "\0" + VALUE "Full Version", XSTR(JDK_VERSION_STRING) "\0" + VALUE "InternalName", XSTR(JDK_INTERNAL_NAME) "\0" + VALUE "LegalCopyright", XSTR(JDK_COPYRIGHT) "\0" + VALUE "OriginalFilename", XSTR(JDK_FNAME) "\0" + VALUE "ProductName", XSTR(JDK_NAME) "\0" + VALUE "ProductVersion", XSTR(JDK_VER) "\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + + +#define MANIFEST_RESOURCE_ID 1 + +// Manifest +// + +MANIFEST_RESOURCE_ID RT_MANIFEST "jpackage.manifest" + diff -r f6ab4cc4c70e -r 3409e81fb3cb src/jdk.jpackage/windows/native/jpackageapplauncher/WinLauncher.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/windows/native/jpackageapplauncher/WinLauncher.cpp Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2012, 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. + */ + +#include +#include +#include +#include +#include + +#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; +} + diff -r f6ab4cc4c70e -r 3409e81fb3cb test/jdk/tools/jpackage/JPackageHelpTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/JPackageHelpTest.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,117 @@ +/* + * Copyright (c) 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 = + "The following options are valid for Windows platforms"; + private static final String OSX_HELP = + "The following options are valid for Mac OS X platforms"; + private static final String LINUX_HELP = + "The following options are valid for Linux platforms"; + + 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(); + } + +} diff -r f6ab4cc4c70e -r 3409e81fb3cb test/jdk/tools/jpackage/JPackageInvalidArgTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/JPackageInvalidArgTest.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,76 @@ +/* + * Copyright (c) 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 ARG = "--no-such-argument"; + private static final String RESULT1 = + "invalid option [--no-such-argument]"; + private static final String RESULT2 = "ERROR: Mode is not specified"; + + private static void validate(String output) throws Exception { + String[] result = output.split("\n"); + 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"); + } + + if (!result[1].trim().equals(RESULT2)) { + System.err.println("Expected: " + RESULT2); + System.err.println("Actual: " + result[1]); + throw new AssertionError("Unexpected line 2"); + } + } + + private static void testInvalidArg() throws Exception { + String output = JPackageHelper.executeCLI(false, ARG); + validate(output); + } + + private static void testInvalidArgToolProvider() throws Exception { + String output = JPackageHelper.executeToolProvider(false, ARG); + validate(output); + } + + public static void main(String[] args) throws Exception { + testInvalidArg(); + testInvalidArgToolProvider(); + } + +} diff -r f6ab4cc4c70e -r 3409e81fb3cb test/jdk/tools/jpackage/JPackageMissingArgumentsTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/JPackageMissingArgumentsTest.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,174 @@ +/* + * Copyright (c) 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 = { + "create-image", + "--input", "input", + "--name", "test", + "--main-jar", "hello.jar", + "--class", "Hello", + "--force", + "--files", "hello.jar"}; + + private static final String [] RESULT_2 = {"--input"}; + private static final String [] CMD_2 = { + "create-image", + "--output", "output", + "--name", "test", + "--main-jar", "hello.jar", + "--class", "Hello", + "--force", + "--files", "hello.jar"}; + + private static final String [] RESULT_3 = {"--input", "--app-image"}; + private static final String [] CMD_3 = { + "create-installer", + "--output", "output", + "--name", "test", + "--main-jar", "hello.jar", + "--class", "Hello", + "--force", + "--files", "hello.jar"}; + + private static final String [] RESULT_4 = {"--class"}; + private static final String [] CMD_4 = { + "create-image", + "--input", "input", + "--output", "output", + "--name", "test", + "--main-jar", "hello.jar", + "--force", + "--files", "hello.jar"}; + + private static final String [] RESULT_5 = {"--main-jar"}; + private static final String [] CMD_5 = { + "create-image", + "--input", "input", + "--output", "output", + "--name", "test", + "--class", "Hello", + "--force", + "--files", "hello.jar"}; + + private static final String [] RESULT_6 = {"--module-path", "--runtime-image"}; + private static final String [] CMD_6 = { + "create-image", + "--output", "output", + "--name", "test", + "--module", "com.hello/com.hello.Hello", + "--force", + "--files", "hello.jar"}; + + private static final String [] RESULT_7 = {"--module-path", "--runtime-image", + "--app-image"}; + private static final String [] CMD_7 = { + "create-installer", + "--output", "output", + "--name", "test", + "--module", "com.hello/com.hello.Hello", + "--force", + "--files", "hello.jar"}; + + private static void validate(String output, String [] expected) + throws Exception { + String[] result = output.split("\n"); + if (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); + + output = JPackageHelper.executeCLI(false, CMD_2); + validate(output, RESULT_2); + + output = JPackageHelper.executeCLI(false, CMD_3); + validate(output, RESULT_3); + + output = JPackageHelper.executeCLI(false, CMD_4); + validate(output, RESULT_4); + + output = JPackageHelper.executeCLI(false, CMD_5); + validate(output, RESULT_5); + + output = JPackageHelper.executeCLI(false, CMD_6); + validate(output, RESULT_6); + + output = JPackageHelper.executeCLI(false, CMD_7); + validate(output, RESULT_7); + } + + private static void testMissingArgToolProvider() throws Exception { + String output = JPackageHelper.executeToolProvider(false, CMD_1); + validate(output, RESULT_1); + + output = JPackageHelper.executeToolProvider(false, CMD_2); + validate(output, RESULT_2); + + output = JPackageHelper.executeToolProvider(false, CMD_3); + validate(output, RESULT_3); + + output = JPackageHelper.executeToolProvider(false, CMD_4); + validate(output, RESULT_4); + + output = JPackageHelper.executeToolProvider(false, CMD_5); + validate(output, RESULT_5); + + output = JPackageHelper.executeToolProvider(false, CMD_6); + validate(output, RESULT_6); + + output = JPackageHelper.executeToolProvider(false, CMD_7); + validate(output, RESULT_7); + } + + public static void main(String[] args) throws Exception { + JPackageHelper.createHelloImageJar(); + testMissingArg(); + testMissingArgToolProvider(); + } + +} diff -r f6ab4cc4c70e -r 3409e81fb3cb test/jdk/tools/jpackage/JPackageNoArgTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/JPackageNoArgTest.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,78 @@ +/* + * Copyright (c) 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 "; + private static final String[] EXPECTED = + {"--help", "list of possible options"}; + + private static void validate(String output) throws Exception { + String[] result = output.split("\n"); + 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(); + } + +} diff -r f6ab4cc4c70e -r 3409e81fb3cb test/jdk/tools/jpackage/JPackageVersionTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/JPackageVersionTest.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,68 @@ +/* + * Copyright (c) 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 = "jpackage version" + + " " + System.getProperty("java.version"); + + private static void validate(String output) throws Exception { + String[] result = output.split("\n"); + 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(); + } + +} diff -r f6ab4cc4c70e -r 3409e81fb3cb test/jdk/tools/jpackage/apps/com.hello/com/hello/Hello.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/apps/com.hello/com/hello/Hello.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,64 @@ +/* + * Copyright (c) 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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()); + } + } + +} diff -r f6ab4cc4c70e -r 3409e81fb3cb test/jdk/tools/jpackage/apps/com.hello/module-info.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/apps/com.hello/module-info.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,26 @@ +/* + * Copyright (c) 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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; +} diff -r f6ab4cc4c70e -r 3409e81fb3cb test/jdk/tools/jpackage/apps/image/Hello.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/apps/image/Hello.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,80 @@ +/* + * Copyright (c) 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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()); + } + } +} diff -r f6ab4cc4c70e -r 3409e81fb3cb test/jdk/tools/jpackage/apps/installer/Hello.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/apps/installer/Hello.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,83 @@ +/* + * Copyright (c) 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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); + if (args.length == 1) { // Called via file association + 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) { + File inputFile = new File(args[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: " + 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()); + } + } +} diff -r f6ab4cc4c70e -r 3409e81fb3cb test/jdk/tools/jpackage/createimage/JPackageCreateImageArgumentsBase.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/createimage/JPackageCreateImageArgumentsBase.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,122 @@ +/* + * Copyright (c) 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 JPackageCreateImageArgumentsBase { + + 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 arguments = new ArrayList<>(); + private static final List 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 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 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 testCreateImage(String[] cmd) throws Exception { + initArguments(false, cmd); + JPackageHelper.executeCLI(true, cmd); + validate(null, arguments); + validate(ARGUMENT_CMD1, argumentsCmd); + } + + public static void testCreateImageToolProvider(String[] cmd) throws Exception { + initArguments(true, cmd); + JPackageHelper.executeToolProvider(true, cmd); + validate(null, arguments); + validate(ARGUMENT_CMD1, argumentsCmd); + } +} diff -r f6ab4cc4c70e -r 3409e81fb3cb test/jdk/tools/jpackage/createimage/JPackageCreateImageArgumentsModuleTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/createimage/JPackageCreateImageArgumentsModuleTest.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,51 @@ +/* + * Copyright (c) 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 JPackageCreateImageArgumentsBase + * @modules jdk.jpackage + * @run main/othervm -Xmx512m JPackageCreateImageArgumentsModuleTest + */ +public class JPackageCreateImageArgumentsModuleTest { + + private static final String[] CMD = { + "create-image", + "--output", "output", + "--name", "test", + "--force", + "--module", "com.hello/com.hello.Hello", + "--module-path", "input", + "--arguments", "TBD"}; + + public static void main(String[] args) throws Exception { + JPackageHelper.createHelloModule(); + JPackageCreateImageArgumentsBase.testCreateImage(CMD); + JPackageCreateImageArgumentsBase.testCreateImageToolProvider(CMD); + } + +} diff -r f6ab4cc4c70e -r 3409e81fb3cb test/jdk/tools/jpackage/createimage/JPackageCreateImageArgumentsTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/createimage/JPackageCreateImageArgumentsTest.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,53 @@ +/* + * Copyright (c) 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 JPackageCreateImageArgumentsBase + * @modules jdk.jpackage + * @run main/othervm -Xmx512m JPackageCreateImageArgumentsTest + */ +public class JPackageCreateImageArgumentsTest { + + private static final String[] CMD = { + "create-image", + "--input", "input", + "--output", "output", + "--name", "test", + "--main-jar", "hello.jar", + "--class", "Hello", + "--files", "hello.jar", + "--force", + "--arguments", "TBD"}; + + public static void main(String[] args) throws Exception { + JPackageHelper.createHelloImageJar(); + JPackageCreateImageArgumentsBase.testCreateImage(CMD); + JPackageCreateImageArgumentsBase.testCreateImageToolProvider(CMD); + } + +} diff -r f6ab4cc4c70e -r 3409e81fb3cb test/jdk/tools/jpackage/createimage/JPackageCreateImageBase.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/createimage/JPackageCreateImageBase.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,73 @@ +/* + * Copyright (c) 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 abstract class JPackageCreateImageBase { + private static final String app = JPackagePath.getApp(); + 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().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); + } + + public static void testCreateImage(String [] cmd) throws Exception { + JPackageHelper.executeCLI(true, cmd); + validate(); + } + + public static void testCreateImageToolProvider(String [] cmd) throws Exception { + JPackageHelper.executeToolProvider(true, cmd); + validate(); + } +} diff -r f6ab4cc4c70e -r 3409e81fb3cb test/jdk/tools/jpackage/createimage/JPackageCreateImageBuildRootTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/createimage/JPackageCreateImageBuildRootTest.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,107 @@ +/* + * Copyright (c) 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 --build-root + * @library ../helpers + * @build JPackageHelper + * @build JPackagePath + * @modules jdk.jpackage + * @run main/othervm -Xmx512m JPackageCreateImageBuildRootTest + */ +public class JPackageCreateImageBuildRootTest { + 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 = { + "create-image", + "--input", "input", + "--output", "output", + "--name", "test", + "--main-jar", "hello.jar", + "--class", "Hello", + "--files", "hello.jar", + "--force" }; + + private static final String [] CMD_BUILD_ROOT = { + "create-image", + "--input", "input", + "--output", "output", + "--name", "test", + "--main-jar", "hello.jar", + "--class", "Hello", + "--files", "hello.jar", + "--force", + "--build-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 testBuildRoot() throws Exception { + init(false); + JPackageHelper.executeCLI(true, CMD); + validate(false); + JPackageHelper.executeCLI(true, CMD_BUILD_ROOT); + validate(true); + } + + private static void testBuildRootToolProvider() throws Exception { + init(true); + JPackageHelper.executeToolProvider(true, CMD); + validate(false); + JPackageHelper.executeToolProvider(true, CMD_BUILD_ROOT); + validate(true); + } + + public static void main(String[] args) throws Exception { + JPackageHelper.createHelloImageJar(); + testBuildRoot(); + testBuildRootToolProvider(); + } + +} diff -r f6ab4cc4c70e -r 3409e81fb3cb test/jdk/tools/jpackage/createimage/JPackageCreateImageBuildRootTest.java.rej --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/createimage/JPackageCreateImageBuildRootTest.java.rej Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,10 @@ +--- JPackageCreateImageBuildRootTest.java ++++ JPackageCreateImageBuildRootTest.java +@@ -25,6 +25,7 @@ + + /* + * @test ++ * @requires (os.family == "windows") + * @summary jpackage create image to test --build-root + * @library ../helpers + * @build JPackageHelper diff -r f6ab4cc4c70e -r 3409e81fb3cb test/jdk/tools/jpackage/createimage/JPackageCreateImageForceTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/createimage/JPackageCreateImageForceTest.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,153 @@ +/* + * Copyright (c) 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 --force + * @library ../helpers + * @build JPackageHelper + * @build JPackagePath + * @modules jdk.jpackage + * @run main/othervm -Xmx512m JPackageCreateImageForceTest + */ +public class JPackageCreateImageForceTest { + 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 = { + "create-image", + "--input", "input", + "--name", "test", + "--main-jar", "hello.jar", + "--class", "Hello", + "--files", "hello.jar", + "--output", "TBD"}; + + private static final String[] CMD_FORCE = { + "create-image", + "--input", "input", + "--name", "test", + "--main-jar", "hello.jar", + "--class", "Hello", + "--force", + "--files", "hello.jar", + "--output", "TBD"}; + + 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(String result) throws Exception { + if (!result.contains("java.io.IOException") && + !result.contains("already exists") && + !result.contains("--force is not specified")) { + System.err.println(result); + throw new AssertionError("Unexpected error message"); + } + } + + private static void validateForce() 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 testForce() throws Exception { + CMD[CMD.length - 1] = "output"; + CMD_FORCE[CMD_FORCE.length - 1] = "output"; + + String appFolder = "test"; + if (JPackageHelper.isOSX()) { + appFolder = "test.app"; + } + + File output = new File("output" + File.separator + appFolder); + if (output.exists()) { + throw new AssertionError( + "Output folder already exist"); + } + output.mkdirs(); + + String result = JPackageHelper.executeCLI(false, CMD); + validate(result); + + JPackageHelper.executeCLI(true, CMD_FORCE); + validateForce(); + } + + private static void testForceToolProvider() throws Exception { + CMD[CMD.length - 1] = "outputToolProvider"; + CMD_FORCE[CMD_FORCE.length - 1] = "outputToolProvider"; + + String appFolder = "test"; + if (JPackageHelper.isOSX()) { + appFolder = "test.app"; + } + + File output = new File("outputToolProvider" + File.separator + appFolder); + if (output.exists()) { + throw new AssertionError( + "Output folder already exist"); + } + output.mkdirs(); + + String result = JPackageHelper.executeToolProvider(false, CMD); + validate(result); + + JPackageHelper.executeToolProvider(true, CMD_FORCE); + validateForce(); + } + + public static void main(String[] args) throws Exception { + JPackageHelper.createHelloImageJar(); + testForce(); + testForceToolProvider(); + } + +} diff -r f6ab4cc4c70e -r 3409e81fb3cb test/jdk/tools/jpackage/createimage/JPackageCreateImageIconTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/createimage/JPackageCreateImageIconTest.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,126 @@ +/* + * Copyright (c) 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 JPackageCreateImageIconTest + */ +public class JPackageCreateImageIconTest { + 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 = { + "create-image", + "--input", "input", + "--name", "test", + "--main-jar", "hello.jar", + "--class", "Hello", + "--force", + "--files", "hello.jar", + "--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.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(); + } + +} diff -r f6ab4cc4c70e -r 3409e81fb3cb test/jdk/tools/jpackage/createimage/JPackageCreateImageInputFilesTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/createimage/JPackageCreateImageInputFilesTest.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,130 @@ +/* + * Copyright (c) 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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; + + /* + * @test + * @summary jpackage create image input/files test + * @library ../helpers + * @build JPackageHelper + * @build JPackagePath + * @modules jdk.jpackage + * @run main/othervm -Xmx512m JPackageCreateImageInputFilesTest + */ +public class JPackageCreateImageInputFilesTest { + private static final String inputFile = + "input" + File.separator + "input.txt"; + private static final String jarFile = + "input" + File.separator + "hello.jar"; + private static final String appInputFilePath; + private static final String appJarFilePath; + + static { + appInputFilePath = JPackagePath.getAppWorkingDir() + File.separator + "input.txt"; + appJarFilePath = JPackagePath.getAppWorkingDir() + File.separator + "hello.jar"; + } + + private static final String [] CMD_1 = { + "create-image", + "--input", "input", + "--output", "output", + "--name", "test", + "--main-jar", "hello.jar", + "--force", + "--class", "Hello"}; + + private static final String [] CMD_2 = { + "create-image", + "--input", "input", + "--output", "output", + "--name", "test", + "--main-jar", "hello.jar", + "--class", "Hello", + "--force", + "--files", "hello.jar"}; + + private static void validate1() throws Exception { + File input = new File(appInputFilePath); + if (!input.exists()) { + throw new AssertionError("Unexpected file does not exist: " + + input.getAbsolutePath()); + } + + File jar = new File(appJarFilePath); + if (!jar.exists()) { + throw new AssertionError("Unexpected file does not exist: " + + jar.getAbsolutePath()); + } + } + + private static void validate2() throws Exception { + File input = new File(appInputFilePath); + if (input.exists()) { + throw new AssertionError("Unexpected file exist: " + + input.getAbsolutePath()); + } + + File jar = new File(appJarFilePath); + if (!jar.exists()) { + throw new AssertionError("Unexpected file does not exist: " + + jar.getAbsolutePath()); + } + } + + private static void testCreateImage() throws Exception { + JPackageHelper.executeCLI(true, CMD_1); + validate1(); + + JPackageHelper.executeCLI(true, CMD_2); + validate2(); + } + + private static void testCreateImageToolProvider() throws Exception { + JPackageHelper.executeToolProvider(true, CMD_1); + validate1(); + + JPackageHelper.executeToolProvider(true, CMD_2); + validate2(); + } + + private static void createInputFile() throws Exception { + try (PrintWriter out = new PrintWriter( + new BufferedWriter(new FileWriter(inputFile)))) { + out.println("jpackgaer resource file"); + } + } + + public static void main(String[] args) throws Exception { + JPackageHelper.createHelloImageJar(); + + createInputFile(); + + testCreateImage(); + testCreateImageToolProvider(); + } + +} diff -r f6ab4cc4c70e -r 3409e81fb3cb test/jdk/tools/jpackage/createimage/JPackageCreateImageJVMArgsBase.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/createimage/JPackageCreateImageJVMArgsBase.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,149 @@ +/* + * Copyright (c) 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 JPackageCreateImageJVMArgsBase { + + 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 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++] = "--jvm-args"; + arguments.clear(); + arguments.add(ARGUMENT1); + cmd[index++] = JPackageHelper.listToArgumentsMap(arguments, + toolProvider); + + cmd[index++] = "--jvm-args"; + arguments.clear(); + arguments.add(ARGUMENT2); + cmd[index++] = JPackageHelper.listToArgumentsMap(arguments, + toolProvider); + + cmd[index++] = "--jvm-args"; + 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 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 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 = output.split("\n"); + validateResult(result, expectedArgs); + } + + public static void testCreateImageJVMArgs(String [] cmd) throws Exception { + initArguments(false, cmd); + JPackageHelper.executeCLI(true, cmd); + validate(arguments); + } + + public static void testCreateImageJVMArgsToolProvider(String [] cmd) throws Exception { + initArguments(true, cmd); + JPackageHelper.executeToolProvider(true, cmd); + validate(arguments); + } + + public static void testCreateImageJVMArgs2(String [] cmd) throws Exception { + initArguments2(false, cmd); + JPackageHelper.executeCLI(true, cmd); + validate(arguments); + } + + public static void testCreateImageJVMArgs2ToolProvider(String [] cmd) throws Exception { + initArguments2(true, cmd); + JPackageHelper.executeToolProvider(true, cmd); + validate(arguments); + } +} diff -r f6ab4cc4c70e -r 3409e81fb3cb test/jdk/tools/jpackage/createimage/JPackageCreateImageJVMArgsModuleTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/createimage/JPackageCreateImageJVMArgsModuleTest.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,66 @@ +/* + * Copyright (c) 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 --jvm-args test + * @library ../helpers + * @build JPackageHelper + * @build JPackagePath + * @build JPackageCreateImageJVMArgsBase + * @modules jdk.jpackage + * @run main/othervm -Xmx512m JPackageCreateImageJVMArgsModuleTest + */ +public class JPackageCreateImageJVMArgsModuleTest { + + private static final String[] CMD = { + "create-image", + "--output", "output", + "--name", "test", + "--force", + "--module", "com.hello/com.hello.Hello", + "--module-path", "input", + "--jvm-args", "TBD"}; + + private static final String[] CMD2 = { + "create-image", + "--output", "output", + "--name", "test", + "--force", + "--module", "com.hello/com.hello.Hello", + "--module-path", "input", + "--jvm-args", "TBD", + "--jvm-args", "TBD", + "--jvm-args", "TBD"}; + + public static void main(String[] args) throws Exception { + JPackageHelper.createHelloModule(); + + JPackageCreateImageJVMArgsBase.testCreateImageJVMArgs(CMD); + JPackageCreateImageJVMArgsBase.testCreateImageJVMArgsToolProvider(CMD); + + JPackageCreateImageJVMArgsBase.testCreateImageJVMArgs2(CMD2); + JPackageCreateImageJVMArgsBase.testCreateImageJVMArgs2ToolProvider(CMD2); + } + +} diff -r f6ab4cc4c70e -r 3409e81fb3cb test/jdk/tools/jpackage/createimage/JPackageCreateImageJVMArgsTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/createimage/JPackageCreateImageJVMArgsTest.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,69 @@ +/* + * Copyright (c) 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 --jvm-args test + * @library ../helpers + * @build JPackageHelper + * @build JPackagePath + * @build JPackageCreateImageJVMArgsBase + * @modules jdk.jpackage + * @run main/othervm -Xmx512m JPackageCreateImageJVMArgsTest + */ +public class JPackageCreateImageJVMArgsTest { + + private static final String[] CMD = { + "create-image", + "--input", "input", + "--output", "output", + "--name", "test", + "--main-jar", "hello.jar", + "--class", "Hello", + "--files", "hello.jar", + "--force", + "--jvm-args", "TBD"}; + + private static final String[] CMD2 = { + "create-image", + "--input", "input", + "--output", "output", + "--name", "test", + "--main-jar", "hello.jar", + "--class", "Hello", + "--files", "hello.jar", + "--force", + "--jvm-args", "TBD", + "--jvm-args", "TBD", + "--jvm-args", "TBD"}; + + public static void main(String[] args) throws Exception { + JPackageHelper.createHelloImageJar(); + JPackageCreateImageJVMArgsBase.testCreateImageJVMArgs(CMD); + JPackageCreateImageJVMArgsBase.testCreateImageJVMArgsToolProvider(CMD); + + JPackageCreateImageJVMArgsBase.testCreateImageJVMArgs2(CMD2); + JPackageCreateImageJVMArgsBase.testCreateImageJVMArgs2ToolProvider(CMD2); + } + +} diff -r f6ab4cc4c70e -r 3409e81fb3cb test/jdk/tools/jpackage/createimage/JPackageCreateImageMainClassAttributeTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/createimage/JPackageCreateImageMainClassAttributeTest.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,98 @@ +/* + * Copyright (c) 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 JPackageCreateImageMainClassAttributeTest + */ +public class JPackageCreateImageMainClassAttributeTest { + 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 = { + "create-image", + "--input", "input", + "--output", "output", + "--name", "test", + "--main-jar", "hello.jar", + "--force", + "--files", "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.executeToolProvider(true, CMD); + validate(); + } + + public static void main(String[] args) throws Exception { + JPackageHelper.createHelloImageJarWithMainClass(); + testMainClassAttribute(); + testMainClassAttributeToolProvider(); + } + +} diff -r f6ab4cc4c70e -r 3409e81fb3cb test/jdk/tools/jpackage/createimage/JPackageCreateImageModuleTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/createimage/JPackageCreateImageModuleTest.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,49 @@ +/* + * Copyright (c) 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 JPackageCreateImageBase + * @modules jdk.jpackage + * @run main/othervm -Xmx512m JPackageCreateImageModuleTest + */ +public class JPackageCreateImageModuleTest { + private static final String [] CMD = { + "create-image", + "--output", "output", + "--name", "test", + "--force", + "--module", "com.hello/com.hello.Hello", + "--module-path", "input"}; + + public static void main(String[] args) throws Exception { + JPackageHelper.createHelloModule(); + JPackageCreateImageBase.testCreateImage(CMD); + JPackageCreateImageBase.testCreateImageToolProvider(CMD); + } + +} diff -r f6ab4cc4c70e -r 3409e81fb3cb test/jdk/tools/jpackage/createimage/JPackageCreateImageNoNameTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/createimage/JPackageCreateImageNoNameTest.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,98 @@ +/* + * Copyright (c) 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 JPackageCreateImageNoNameTest + */ +public class JPackageCreateImageNoNameTest { + private static final String app = JPackagePath.getAppNoName(); + private static final String appOutput = JPackagePath.getAppOutputFile(); + private static final String appWorkingDir = JPackagePath.getAppWorkingDirNoName(); + + private static final String[] CMD = { + "create-image", + "--input", "input", + "--output", "output", + "--main-jar", "hello.jar", + "--class", "Hello", + "--force", + "--files", "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.executeToolProvider(true, CMD); + validate(); + } + + public static void main(String[] args) throws Exception { + JPackageHelper.createHelloImageJar(); + testMainClassAttribute(); + testMainClassAttributeToolProvider(); + } + +} diff -r f6ab4cc4c70e -r 3409e81fb3cb test/jdk/tools/jpackage/createimage/JPackageCreateImageRuntimeBase.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/createimage/JPackageCreateImageRuntimeBase.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,98 @@ +/* + * Copyright (c) 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 JPackageCreateImageRuntimeBase { + 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 = output.split("\n"); + 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 = output.split("\n"); + 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 testCreateImage(String [] cmd) throws Exception { + JPackageHelper.executeCLI(true, cmd); + validate(); + validateRuntime(); + } + + public static void testCreateImageToolProvider(String [] cmd) throws Exception { + JPackageHelper.executeToolProvider(true, cmd); + validate(); + validateRuntime(); + } + +} diff -r f6ab4cc4c70e -r 3409e81fb3cb test/jdk/tools/jpackage/createimage/JPackageCreateImageRuntimeModuleTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/createimage/JPackageCreateImageRuntimeModuleTest.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,52 @@ +/* + * Copyright (c) 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 JPackageCreateImageRuntimeBase + * @modules jdk.jpackage + * @run main/othervm -Xmx512m JPackageCreateImageRuntimeModuleTest + */ +public class JPackageCreateImageRuntimeModuleTest { + private static final String [] CMD = { + "create-image", + "--runtime-image", "runtime", + "--output", "output", + "--name", "test", + "--force", + "--win-console", + "--module", "com.hello/com.hello.Hello", + "--module-path", "input"}; + + public static void main(String[] args) throws Exception { + JPackageHelper.createHelloModule(); + JPackageHelper.createRuntime(); + JPackageCreateImageRuntimeBase.testCreateImage(CMD); + JPackageCreateImageRuntimeBase.testCreateImageToolProvider(CMD); + } + +} diff -r f6ab4cc4c70e -r 3409e81fb3cb test/jdk/tools/jpackage/createimage/JPackageCreateImageRuntimeTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/createimage/JPackageCreateImageRuntimeTest.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,53 @@ +/* + * Copyright (c) 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 JPackageCreateImageRuntimeBase + * @modules jdk.jpackage + * @run main/othervm -Xmx512m JPackageCreateImageRuntimeTest + */ +public class JPackageCreateImageRuntimeTest { + private static final String [] CMD = { + "create-image", + "--runtime-image", "runtime", + "--input", "input", + "--output", "output", + "--name", "test", + "--main-jar", "hello.jar", + "--class", "Hello", + "--force", + "--files", "hello.jar"}; + + public static void main(String[] args) throws Exception { + JPackageHelper.createHelloImageJar(); + JPackageHelper.createRuntime(); + JPackageCreateImageRuntimeBase.testCreateImage(CMD); + JPackageCreateImageRuntimeBase.testCreateImageToolProvider(CMD); + } + +} diff -r f6ab4cc4c70e -r 3409e81fb3cb test/jdk/tools/jpackage/createimage/JPackageCreateImageSecondaryLauncherBase.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/createimage/JPackageCreateImageSecondaryLauncherBase.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,141 @@ +/* + * Copyright (c) 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 JPackageCreateImageSecondaryLauncherBase { + private static final String app = JPackagePath.getApp(); + private static final String app2 = JPackagePath.getAppSL(); + private static final String appOutput = JPackagePath.getAppOutputFile(); + private static final String appWorkingDir = JPackagePath.getAppWorkingDir(); + + // Note: quotes in argument for secondary 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 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 vmArguments = new ArrayList<>(); + + private static void validateResult(List args, List 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"); + + if (result.length != (args.size() + vmArgs.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++; + } + + for (String vmArg : vmArgs) { + if (!result[index].trim().equals(vmArg)) { + throw new AssertionError("Unexpected result[" + index + "]: " + + result[index]); + } + index++; + } + } + + private static void validate() throws Exception { + int retVal = JPackageHelper.execute(null, app); + if (retVal != 0) { + throw new AssertionError("Test application exited with error: " + + retVal); + } + validateResult(new ArrayList<>(), new ArrayList<>()); + + retVal = JPackageHelper.execute(null, app2); + if (retVal != 0) { + throw new AssertionError("Test application exited with error: " + + retVal); + } + validateResult(arguments, vmArguments); + } + + public static void testCreateImage(String [] cmd) throws Exception { + JPackageHelper.executeCLI(true, cmd); + validate(); + } + + public static void testCreateImageToolProvider(String [] cmd) throws Exception { + JPackageHelper.executeToolProvider(true, cmd); + validate(); + } + + 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("name=test2"); + out.println("arguments=" + argumentsMap); + out.println("jvm-args=" + vmArgumentsMap); + } + } + +} diff -r f6ab4cc4c70e -r 3409e81fb3cb test/jdk/tools/jpackage/createimage/JPackageCreateImageSecondaryLauncherModuleTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/createimage/JPackageCreateImageSecondaryLauncherModuleTest.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,51 @@ +/* + * Copyright (c) 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 secondary launcher test + * @library ../helpers + * @build JPackageHelper + * @build JPackagePath + * @build JPackageCreateImageSecondaryLauncherBase + * @modules jdk.jpackage + * @run main/othervm -Xmx512m JPackageCreateImageSecondaryLauncherModuleTest + */ +public class JPackageCreateImageSecondaryLauncherModuleTest { + private static final String [] CMD = { + "create-image", + "--output", "output", + "--name", "test", + "--force", + "--module", "com.hello/com.hello.Hello", + "--module-path", "input", + "--secondary-launcher", "sl.properties"}; + + public static void main(String[] args) throws Exception { + JPackageHelper.createHelloModule(); + JPackageCreateImageSecondaryLauncherBase.createSLProperties(); + JPackageCreateImageSecondaryLauncherBase.testCreateImage(CMD); + JPackageCreateImageSecondaryLauncherBase.testCreateImageToolProvider(CMD); + } + +} diff -r f6ab4cc4c70e -r 3409e81fb3cb test/jdk/tools/jpackage/createimage/JPackageCreateImageSecondaryLauncherTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/createimage/JPackageCreateImageSecondaryLauncherTest.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,53 @@ +/* + * Copyright (c) 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 secondary launcher test + * @library ../helpers + * @build JPackageHelper + * @build JPackagePath + * @build JPackageCreateImageSecondaryLauncherBase + * @modules jdk.jpackage + * @run main/othervm -Xmx512m JPackageCreateImageSecondaryLauncherTest + */ +public class JPackageCreateImageSecondaryLauncherTest { + private static final String [] CMD = { + "create-image", + "--input", "input", + "--output", "output", + "--name", "test", + "--main-jar", "hello.jar", + "--class", "Hello", + "--files", "hello.jar", + "--force", + "--secondary-launcher", "sl.properties"}; + + public static void main(String[] args) throws Exception { + JPackageHelper.createHelloImageJar(); + JPackageCreateImageSecondaryLauncherBase.createSLProperties(); + JPackageCreateImageSecondaryLauncherBase.testCreateImage(CMD); + JPackageCreateImageSecondaryLauncherBase.testCreateImageToolProvider(CMD); + } + +} diff -r f6ab4cc4c70e -r 3409e81fb3cb test/jdk/tools/jpackage/createimage/JPackageCreateImageStripNativeCommandsTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/createimage/JPackageCreateImageStripNativeCommandsTest.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,90 @@ +/* + * Copyright (c) 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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.Paths; +import java.util.List; +import java.util.stream.Collectors; + + /* + * @test + * @summary jpackage create image with --strip-native-commands test + * @library ../helpers + * @build JPackageHelper + * @build JPackagePath + * @modules jdk.jpackage + * @run main/othervm -Xmx512m JPackageCreateImageStripNativeCommandsTest + */ +public class JPackageCreateImageStripNativeCommandsTest { + private static final String runtimeBinPath = JPackagePath.getRuntimeBin(); + + private static final String [] CMD = { + "create-image", + "--input", "input", + "--output", "output", + "--name", "test", + "--main-jar", "hello.jar", + "--class", "Hello", + "--files", "hello.jar", + "--force", + "--strip-native-commands"}; + + private static void validate() throws Exception { + if (JPackageHelper.isWindows()) { + Path binPath = Paths.get(runtimeBinPath).toAbsolutePath(); + List files = Files.walk(binPath).collect(Collectors.toList()); + files.forEach((f) -> { + if (f.toString().endsWith(".exe")) { + throw new AssertionError( + "Found executable file in runtime bin folder: " + + f.toString()); + } + }); + } else { + File binFolder = new File(runtimeBinPath); + if (binFolder.exists()) { + throw new AssertionError("Found bin folder in runtime: " + + binFolder.toString()); + } + } + } + + private static void testCreateImage() throws Exception { + JPackageHelper.executeCLI(true, CMD); + validate(); + } + + private static void testCreateImageToolProvider() throws Exception { + JPackageHelper.executeToolProvider(true, CMD); + validate(); + } + + public static void main(String[] args) throws Exception { + JPackageHelper.createHelloImageJar(); + testCreateImage(); + testCreateImageToolProvider(); + } + +} diff -r f6ab4cc4c70e -r 3409e81fb3cb test/jdk/tools/jpackage/createimage/JPackageCreateImageTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/createimage/JPackageCreateImageTest.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,50 @@ +/* + * Copyright (c) 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 JPackageCreateImageBase + * @modules jdk.jpackage + * @run main/othervm -Xmx512m JPackageCreateImageTest + */ +public class JPackageCreateImageTest { + private static final String [] CMD = { + "create-image", + "--input", "input", + "--output", "output", + "--name", "test", + "--main-jar", "hello.jar", + "--class", "Hello", + "--force", + "--files", "hello.jar"}; + + public static void main(String[] args) throws Exception { + JPackageHelper.createHelloImageJar(); + JPackageCreateImageBase.testCreateImage(CMD); + JPackageCreateImageBase.testCreateImageToolProvider(CMD); + } +} diff -r f6ab4cc4c70e -r 3409e81fb3cb test/jdk/tools/jpackage/createimage/JPackageCreateImageVerboseTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/createimage/JPackageCreateImageVerboseTest.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,90 @@ +/* + * Copyright (c) 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 JPackageCreateImageVerboseTest + */ +public class JPackageCreateImageVerboseTest { + + private static final String[] CMD = { + "create-image", + "--input", "input", + "--output", "output", + "--name", "test", + "--main-jar", "hello.jar", + "--class", "Hello", + "--force", + "--files", "hello.jar"}; + + private static final String[] CMD_VERBOSE = { + "create-image", + "--input", "input", + "--output", "output", + "--name", "test", + "--main-jar", "hello.jar", + "--class", "Hello", + "--files", "hello.jar", + "--force", + "--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 testCreateImage() throws Exception { + String result = JPackageHelper.executeCLI(true, CMD); + String resultVerbose = JPackageHelper.executeCLI(true, CMD_VERBOSE); + validate(result, resultVerbose); + } + + private static void testCreateImageToolProvider() throws Exception { + String result = JPackageHelper.executeToolProvider(true, CMD); + String resultVerbose = + JPackageHelper.executeToolProvider(true, CMD_VERBOSE); + validate(result, resultVerbose); + } + + public static void main(String[] args) throws Exception { + JPackageHelper.createHelloImageJar(); + testCreateImage(); + testCreateImageToolProvider(); + } + +} diff -r f6ab4cc4c70e -r 3409e81fb3cb test/jdk/tools/jpackage/createimage/JPackageCreateImageVersionTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/createimage/JPackageCreateImageVersionTest.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,102 @@ + +import java.io.File; +import java.nio.file.Files; + +/* + * Copyright (c) 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 --app-version test + * @library ../helpers + * @build JPackageHelper + * @build JPackagePath + * @modules jdk.jpackage + * @run main/othervm -Xmx512m JPackageCreateImageVersionTest + */ +public class JPackageCreateImageVersionTest { + 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 = { + "create-image", + "--input", "input", + "--output", "output", + "--name", "test", + "--main-jar", "hello.jar", + "--class", "Hello", + "--force", + "--files", "hello.jar"}; + + private static final String[] CMD_VERSION = { + "create-image", + "--input", "input", + "--output", "output", + "--name", "test", + "--main-jar", "hello.jar", + "--class", "Hello", + "--files", "hello.jar", + "--force", + "--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.executeCLI(true, CMD_VERSION); + validate(VERSION); + } + + private static void testVersionToolProvider() throws Exception { + JPackageHelper.executeToolProvider(true, CMD); + validate(null); + JPackageHelper.executeToolProvider(true, CMD_VERSION); + validate(VERSION); + } + + public static void main(String[] args) throws Exception { + JPackageHelper.createHelloImageJar(); + testVersion(); + testVersionToolProvider(); + } + +} diff -r f6ab4cc4c70e -r 3409e81fb3cb test/jdk/tools/jpackage/createinstaller/linux/base/JPackageCreateInstallerBase.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/createinstaller/linux/base/JPackageCreateInstallerBase.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,92 @@ +/* + * Copyright (c) 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 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[]{ + "create-installer", + EXT, + "--input", "input", + "--output", "output", + "--name", TEST_NAME, + "--main-jar", "hello.jar", + "--class", "Hello", + "--force", + "--files", "hello.jar"}; + } + + 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(); + } + } +} diff -r f6ab4cc4c70e -r 3409e81fb3cb test/jdk/tools/jpackage/createinstaller/linux/base/JPackageCreateInstallerBundleNameBase.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/createinstaller/linux/base/JPackageCreateInstallerBundleNameBase.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,95 @@ +/* + * Copyright (c) 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 JPackageCreateInstallerBundleNameBase { + + 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 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[]{ + "create-installer", + EXT, + "--input", "input", + "--output", "output", + "--name", TEST_NAME, + "--main-jar", "hello.jar", + "--class", "Hello", + "--force", + "--files", "hello.jar", + "--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(); + } + } +} diff -r f6ab4cc4c70e -r 3409e81fb3cb test/jdk/tools/jpackage/createinstaller/linux/base/JPackageCreateInstallerFileAssociationsBase.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/createinstaller/linux/base/JPackageCreateInstallerFileAssociationsBase.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,158 @@ +/* + * Copyright (c) 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 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 { + createFileAssociationsTestFile(); + 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 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 = "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[]{ + "create-installer", + EXT, + "--input", "input", + "--output", "output", + "--name", TEST_NAME, + "--main-jar", "hello.jar", + "--class", "Hello", + "--force", + "--files", "hello.jar", + "--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(); + createFileAssociationsProperties(); + testCreateInstaller(); + } + } +} diff -r f6ab4cc4c70e -r 3409e81fb3cb test/jdk/tools/jpackage/createinstaller/linux/base/JPackageCreateInstallerInstallDirBase.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/createinstaller/linux/base/JPackageCreateInstallerInstallDirBase.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,99 @@ +/* + * Copyright (c) 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 void copyResults() throws Exception { + List 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[]{ + "create-installer", + EXT, + "--input", "input", + "--output", "output", + "--name", TEST_NAME, + "--main-jar", "hello.jar", + "--class", "Hello", + "--force", + "--files", "hello.jar", + "--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(); + } + } +} diff -r f6ab4cc4c70e -r 3409e81fb3cb test/jdk/tools/jpackage/createinstaller/linux/base/JPackageCreateInstallerLicenseBase.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/createinstaller/linux/base/JPackageCreateInstallerLicenseBase.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,94 @@ +/* + * Copyright (c) 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 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 [] { + "create-installer", + EXT, + "--input", "input", + "--output", "output", + "--name", TEST_NAME, + "--main-jar", "hello.jar", + "--class", "Hello", + "--force", + "--files", "hello.jar", + "--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(); + } + } +} diff -r f6ab4cc4c70e -r 3409e81fb3cb test/jdk/tools/jpackage/createinstaller/linux/base/JPackageCreateInstallerLicenseTypeBase.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/createinstaller/linux/base/JPackageCreateInstallerLicenseTypeBase.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,116 @@ +/* + * Copyright (c) 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 JPackageCreateInstallerLicenseTypeBase { + + 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 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[]{ + "create-installer", + EXT, + "--input", "input", + "--output", "output", + "--name", TEST_NAME, + "--main-jar", "hello.jar", + "--class", "Hello", + "--force", + "--files", "hello.jar", + "--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(); + } + } +} diff -r f6ab4cc4c70e -r 3409e81fb3cb test/jdk/tools/jpackage/createinstaller/linux/base/JPackageCreateInstallerMaintainerBase.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/createinstaller/linux/base/JPackageCreateInstallerMaintainerBase.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,112 @@ +/* + * Copyright (c) 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 JPackageCreateInstallerMaintainerBase { + + 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 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[]{ + "create-installer", + EXT, + "--input", "input", + "--output", "output", + "--name", TEST_NAME, + "--main-jar", "hello.jar", + "--class", "Hello", + "--force", + "--files", "hello.jar", + "--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(); + } + } +} diff -r f6ab4cc4c70e -r 3409e81fb3cb test/jdk/tools/jpackage/createinstaller/linux/base/JPackageCreateInstallerPackageDepsBase.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/createinstaller/linux/base/JPackageCreateInstallerPackageDepsBase.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,150 @@ +/* + * Copyright (c) 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 JPackageCreateInstallerPackageDepsBase { + + 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 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[]{ + "create-installer", + EXT, + "--input", "input", + "--output", "output", + "--name", TEST_NAME, + "--main-jar", "hello.jar", + "--class", "Hello", + "--force", + "--files", "hello.jar", + "--linux-package-deps", DEP_NAME.toLowerCase()}; + CMD_DEP = new String[]{ + "create-installer", + EXT, + "--input", "input", + "--output", "output", + "--name", DEP_NAME, + "--main-jar", "hello.jar", + "--class", "Hello", + "--force", + "--files", "hello.jar"}; + } + + 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(); + } + } +} diff -r f6ab4cc4c70e -r 3409e81fb3cb test/jdk/tools/jpackage/createinstaller/linux/deb/JPackageCreateInstallerBundleNameTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/createinstaller/linux/deb/JPackageCreateInstallerBundleNameTest.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,45 @@ +/* + * Copyright (c) 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 JPackageCreateInstallerBundleNameBase + * @requires (os.family == "linux") + * @modules jdk.jpackage + * @ignore + * @run main/othervm -Xmx512m JPackageCreateInstallerBundleNameTest + */ +public class JPackageCreateInstallerBundleNameTest { + private static final String TEST_NAME = "JPackageCreateInstallerBundleNameTest"; + private static final String EXT = "deb"; + + public static void main(String[] args) throws Exception { + JPackageCreateInstallerBundleNameBase.run(TEST_NAME, EXT); + } +} diff -r f6ab4cc4c70e -r 3409e81fb3cb test/jdk/tools/jpackage/createinstaller/linux/deb/JPackageCreateInstallerFileAssociationsTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/createinstaller/linux/deb/JPackageCreateInstallerFileAssociationsTest.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,45 @@ +/* + * Copyright (c) 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 == "linux") + * @modules jdk.jpackage + * @ignore + * @run main/othervm -Xmx512m JPackageCreateInstallerFileAssociationsTest + */ +public class JPackageCreateInstallerFileAssociationsTest { + private static final String TEST_NAME = "JPackageCreateInstallerFileAssociationsTest"; + private static final String EXT = "deb"; + + public static void main(String[] args) throws Exception { + JPackageCreateInstallerFileAssociationsBase.run(TEST_NAME, EXT); + } +} diff -r f6ab4cc4c70e -r 3409e81fb3cb test/jdk/tools/jpackage/createinstaller/linux/deb/JPackageCreateInstallerInstallDirTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/createinstaller/linux/deb/JPackageCreateInstallerInstallDirTest.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,45 @@ +/* + * Copyright (c) 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 JPackageCreateInstallerInstallDirBase + * @requires (os.family == "linux") + * @modules jdk.jpackage + * @ignore + * @run main/othervm -Xmx512m JPackageCreateInstallerInstallDirTest + */ +public class JPackageCreateInstallerInstallDirTest { + private static final String TEST_NAME = "JPackageCreateInstallerInstallDirTest"; + private static final String EXT = "deb"; + + public static void main(String[] args) throws Exception { + JPackageCreateInstallerInstallDirBase.run(TEST_NAME, EXT); + } +} diff -r f6ab4cc4c70e -r 3409e81fb3cb test/jdk/tools/jpackage/createinstaller/linux/deb/JPackageCreateInstallerLicenseTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/createinstaller/linux/deb/JPackageCreateInstallerLicenseTest.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,45 @@ +/* + * Copyright (c) 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 == "linux") + * @modules jdk.jpackage + * @ignore + * @run main/othervm -Xmx512m JPackageCreateInstallerLicenseTest + */ +public class JPackageCreateInstallerLicenseTest { + private static final String TEST_NAME = "JPackageCreateInstallerLicenseTest"; + private static final String EXT = "deb"; + + public static void main(String[] args) throws Exception { + JPackageCreateInstallerLicenseBase.run(TEST_NAME, EXT); + } +} diff -r f6ab4cc4c70e -r 3409e81fb3cb test/jdk/tools/jpackage/createinstaller/linux/deb/JPackageCreateInstallerMaintainerTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/createinstaller/linux/deb/JPackageCreateInstallerMaintainerTest.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,45 @@ +/* + * Copyright (c) 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 JPackageCreateInstallerMaintainerBase + * @requires (os.family == "linux") + * @modules jdk.jpackage + * @ignore + * @run main/othervm -Xmx512m JPackageCreateInstallerMaintainerTest + */ +public class JPackageCreateInstallerMaintainerTest { + private static final String TEST_NAME = "JPackageCreateInstallerMaintainerTest"; + private static final String EXT = "deb"; + + public static void main(String[] args) throws Exception { + JPackageCreateInstallerMaintainerBase.run(TEST_NAME, EXT); + } +} diff -r f6ab4cc4c70e -r 3409e81fb3cb test/jdk/tools/jpackage/createinstaller/linux/deb/JPackageCreateInstallerPackageDepsTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/createinstaller/linux/deb/JPackageCreateInstallerPackageDepsTest.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,45 @@ +/* + * Copyright (c) 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 JPackageCreateInstallerPackageDepsBase + * @requires (os.family == "linux") + * @modules jdk.jpackage + * @ignore + * @run main/othervm/timeout=240 -Xmx512m JPackageCreateInstallerPackageDepsTest + */ +public class JPackageCreateInstallerPackageDepsTest { + private static final String TEST_NAME = "JPackageCreateInstallerPackageDepsTest"; + private static final String EXT = "deb"; + + public static void main(String[] args) throws Exception { + JPackageCreateInstallerPackageDepsBase.run(TEST_NAME, EXT); + } +} diff -r f6ab4cc4c70e -r 3409e81fb3cb test/jdk/tools/jpackage/createinstaller/linux/deb/JPackageCreateInstallerTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/createinstaller/linux/deb/JPackageCreateInstallerTest.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,45 @@ +/* + * Copyright (c) 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 == "linux") + * @modules jdk.jpackage + * @ignore + * @run main/othervm -Xmx512m JPackageCreateInstallerTest + */ +public class JPackageCreateInstallerTest { + private static final String TEST_NAME = "JPackageCreateInstallerTest"; + private static final String EXT = "deb"; + + public static void main(String[] args) throws Exception { + JPackageCreateInstallerBase.run(TEST_NAME, EXT); + } +} diff -r f6ab4cc4c70e -r 3409e81fb3cb test/jdk/tools/jpackage/createinstaller/linux/deb/install.sh --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/createinstaller/linux/deb/install.sh Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,8 @@ +sudo dpkg -i jpackagecreateinstallertest-1.0.deb +sudo dpkg -i jpackagecreateinstallerfileassociationstest-1.0.deb +sudo dpkg -i jpackagecreateinstallerlicensetest-1.0.deb +sudo dpkg -i jpackagecreateinstallerinstalldirtest-1.0.deb +sudo dpkg -i jpackage-test-bundle-name-1.0.deb +sudo dpkg -i jpackagecreateinstallermaintainertest-1.0.deb +sudo dpkg -i jpackagecreateinstallerpackagedepstestdep-1.0.deb +sudo dpkg -i jpackagecreateinstallerpackagedepstest-1.0.deb \ No newline at end of file diff -r f6ab4cc4c70e -r 3409e81fb3cb test/jdk/tools/jpackage/createinstaller/linux/deb/uninstall.sh --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/createinstaller/linux/deb/uninstall.sh Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,8 @@ +sudo dpkg -r jpackagecreateinstallertest +sudo dpkg -r jpackagecreateinstallerfileassociationstest +sudo dpkg -r jpackagecreateinstallerlicensetest +sudo dpkg -r jpackagecreateinstallerinstalldirtest +sudo dpkg -r jpackage-test-bundle-name +sudo dpkg -r jpackagecreateinstallermaintainertest +sudo dpkg -r jpackagecreateinstallerpackagedepstest +sudo dpkg -r jpackagecreateinstallerpackagedepstestdep diff -r f6ab4cc4c70e -r 3409e81fb3cb test/jdk/tools/jpackage/createinstaller/linux/rpm/JPackageCreateInstallerBundleNameTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/createinstaller/linux/rpm/JPackageCreateInstallerBundleNameTest.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,44 @@ +/* + * Copyright (c) 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 JPackageCreateInstallerBundleNameBase + * @requires (os.family == "linux") + * @modules jdk.jpackage + * @run main/othervm -Xmx512m JPackageCreateInstallerBundleNameTest + */ +public class JPackageCreateInstallerBundleNameTest { + private static final String TEST_NAME = "JPackageCreateInstallerBundleNameTest"; + private static final String EXT = "rpm"; + + public static void main(String[] args) throws Exception { + JPackageCreateInstallerBundleNameBase.run(TEST_NAME, EXT); + } +} diff -r f6ab4cc4c70e -r 3409e81fb3cb test/jdk/tools/jpackage/createinstaller/linux/rpm/JPackageCreateInstallerFileAssociationsTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/createinstaller/linux/rpm/JPackageCreateInstallerFileAssociationsTest.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,44 @@ +/* + * Copyright (c) 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 == "linux") + * @modules jdk.jpackage + * @run main/othervm -Xmx512m JPackageCreateInstallerFileAssociationsTest + */ +public class JPackageCreateInstallerFileAssociationsTest { + private static final String TEST_NAME = "JPackageCreateInstallerFileAssociationsTest"; + private static final String EXT = "rpm"; + + public static void main(String[] args) throws Exception { + JPackageCreateInstallerFileAssociationsBase.run(TEST_NAME, EXT); + } +} diff -r f6ab4cc4c70e -r 3409e81fb3cb test/jdk/tools/jpackage/createinstaller/linux/rpm/JPackageCreateInstallerInstallDirTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/createinstaller/linux/rpm/JPackageCreateInstallerInstallDirTest.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,44 @@ +/* + * Copyright (c) 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 JPackageCreateInstallerInstallDirBase + * @requires (os.family == "linux") + * @modules jdk.jpackage + * @run main/othervm -Xmx512m JPackageCreateInstallerInstallDirTest + */ +public class JPackageCreateInstallerInstallDirTest { + private static final String TEST_NAME = "JPackageCreateInstallerInstallDirTest"; + private static final String EXT = "rpm"; + + public static void main(String[] args) throws Exception { + JPackageCreateInstallerInstallDirBase.run(TEST_NAME, EXT); + } +} diff -r f6ab4cc4c70e -r 3409e81fb3cb test/jdk/tools/jpackage/createinstaller/linux/rpm/JPackageCreateInstallerLicenseTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/createinstaller/linux/rpm/JPackageCreateInstallerLicenseTest.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,44 @@ +/* + * Copyright (c) 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 == "linux") + * @modules jdk.jpackage + * @run main/othervm -Xmx512m JPackageCreateInstallerLicenseTest + */ +public class JPackageCreateInstallerLicenseTest { + private static final String TEST_NAME = "JPackageCreateInstallerLicenseTest"; + private static final String EXT = "rpm"; + + public static void main(String[] args) throws Exception { + JPackageCreateInstallerLicenseBase.run(TEST_NAME, EXT); + } +} diff -r f6ab4cc4c70e -r 3409e81fb3cb test/jdk/tools/jpackage/createinstaller/linux/rpm/JPackageCreateInstallerLicenseTypeTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/createinstaller/linux/rpm/JPackageCreateInstallerLicenseTypeTest.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,44 @@ +/* + * Copyright (c) 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 JPackageCreateInstallerLicenseTypeBase + * @requires (os.family == "linux") + * @modules jdk.jpackage + * @run main/othervm -Xmx512m JPackageCreateInstallerLicenseTypeTest + */ +public class JPackageCreateInstallerLicenseTypeTest { + private static final String TEST_NAME = "JPackageCreateInstallerLicenseTypeTest"; + private static final String EXT = "rpm"; + + public static void main(String[] args) throws Exception { + JPackageCreateInstallerLicenseTypeBase.run(TEST_NAME, EXT); + } +} diff -r f6ab4cc4c70e -r 3409e81fb3cb test/jdk/tools/jpackage/createinstaller/linux/rpm/JPackageCreateInstallerPackageDepsTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/createinstaller/linux/rpm/JPackageCreateInstallerPackageDepsTest.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,44 @@ +/* + * Copyright (c) 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 JPackageCreateInstallerPackageDepsBase + * @requires (os.family == "linux") + * @modules jdk.jpackage + * @run main/othervm/timeout=240 -Xmx512m JPackageCreateInstallerPackageDepsTest + */ +public class JPackageCreateInstallerPackageDepsTest { + private static final String TEST_NAME = "JPackageCreateInstallerPackageDepsTest"; + private static final String EXT = "rpm"; + + public static void main(String[] args) throws Exception { + JPackageCreateInstallerPackageDepsBase.run(TEST_NAME, EXT); + } +} diff -r f6ab4cc4c70e -r 3409e81fb3cb test/jdk/tools/jpackage/createinstaller/linux/rpm/JPackageCreateInstallerTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/createinstaller/linux/rpm/JPackageCreateInstallerTest.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,44 @@ +/* + * Copyright (c) 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 == "linux") + * @modules jdk.jpackage + * @run main/othervm -Xmx512m JPackageCreateInstallerTest + */ +public class JPackageCreateInstallerTest { + private static final String TEST_NAME = "JPackageCreateInstallerTest"; + private static final String EXT = "rpm"; + + public static void main(String[] args) throws Exception { + JPackageCreateInstallerBase.run(TEST_NAME, EXT); + } +} diff -r f6ab4cc4c70e -r 3409e81fb3cb test/jdk/tools/jpackage/createinstaller/linux/rpm/install.sh --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/createinstaller/linux/rpm/install.sh Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,8 @@ +sudo rpm --install jpackagecreateinstallerfileassociationstest-1.0-1.x86_64.rpm +sudo rpm --install jpackagecreateinstallerinstalldirtest-1.0-1.x86_64.rpm +sudo rpm --install jpackagecreateinstallerlicensetest-1.0-1.x86_64.rpm +sudo rpm --install jpackagecreateinstallerlicensetypetest-1.0-1.x86_64.rpm +sudo rpm --install jpackagecreateinstallerpackagedepstestdep-1.0-1.x86_64.rpm +sudo rpm --install jpackagecreateinstallerpackagedepstest-1.0-1.x86_64.rpm +sudo rpm --install jpackagecreateinstallertest-1.0-1.x86_64.rpm +sudo rpm --install jpackage-test-bundle-name-1.0-1.x86_64.rpm diff -r f6ab4cc4c70e -r 3409e81fb3cb test/jdk/tools/jpackage/createinstaller/linux/rpm/uninstall.sh --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/createinstaller/linux/rpm/uninstall.sh Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,8 @@ +sudo rpm -e jpackagecreateinstallerfileassociationstest +sudo rpm -e jpackagecreateinstallerinstalldirtest +sudo rpm -e jpackagecreateinstallerlicensetest +sudo rpm -e jpackagecreateinstallerlicensetypetest +sudo rpm -e jpackagecreateinstallerpackagedepstest +sudo rpm -e jpackagecreateinstallerpackagedepstestdep +sudo rpm -e jpackagecreateinstallertest +sudo rpm -e jpackage-test-bundle-name diff -r f6ab4cc4c70e -r 3409e81fb3cb test/jdk/tools/jpackage/createinstaller/macosx/base/JPackageCreateInstallerBase.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/createinstaller/macosx/base/JPackageCreateInstallerBase.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,86 @@ +/* + * Copyright (c) 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 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[]{ + "create-installer", + EXT, + "--input", "input", + "--output", "output", + "--name", TEST_NAME, + "--main-jar", "hello.jar", + "--class", "Hello", + "--force", + "--files", "hello.jar"}; + } + + 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(); + } + } +} diff -r f6ab4cc4c70e -r 3409e81fb3cb test/jdk/tools/jpackage/createinstaller/macosx/base/JPackageCreateInstallerFileAssociationsBase.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/createinstaller/macosx/base/JPackageCreateInstallerFileAssociationsBase.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,152 @@ +/* + * Copyright (c) 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 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(); + } + + 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 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 = "jptest1"; + OUTPUT = "output" + File.separator + TEST_NAME + "-1.0." + EXT; + CMD = new String[]{ + "create-installer", + EXT, + "--input", "input", + "--output", "output", + "--name", TEST_NAME, + "--main-jar", "hello.jar", + "--class", "Hello", + "--force", + "--files", "hello.jar", + "--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(); + createFileAssociationsProperties(); + testCreateInstaller(); + } + } +} diff -r f6ab4cc4c70e -r 3409e81fb3cb test/jdk/tools/jpackage/createinstaller/macosx/base/JPackageCreateInstallerInstallDirBase.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/createinstaller/macosx/base/JPackageCreateInstallerInstallDirBase.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,87 @@ +/* + * Copyright (c) 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 void copyResults() throws Exception { + List 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[]{ + "create-installer", + EXT, + "--input", "input", + "--output", "output", + "--name", TEST_NAME, + "--main-jar", "hello.jar", + "--class", "Hello", + "--force", + "--files", "hello.jar", + "--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(); + } + } +} diff -r f6ab4cc4c70e -r 3409e81fb3cb test/jdk/tools/jpackage/createinstaller/macosx/base/JPackageCreateInstallerLicenseBase.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/createinstaller/macosx/base/JPackageCreateInstallerLicenseBase.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,88 @@ +/* + * Copyright (c) 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 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 [] { + "create-installer", + EXT, + "--input", "input", + "--output", "output", + "--name", TEST_NAME, + "--main-jar", "hello.jar", + "--class", "Hello", + "--force", + "--files", "hello.jar", + "--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(); + } + } +} diff -r f6ab4cc4c70e -r 3409e81fb3cb test/jdk/tools/jpackage/createinstaller/macosx/dmg/JPackageCreateInstallerFileAssociationsTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/createinstaller/macosx/dmg/JPackageCreateInstallerFileAssociationsTest.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,45 @@ +/* + * Copyright (c) 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 == "mac") + * @modules jdk.jpackage + * @ignore + * @run main/othervm -Xmx512m JPackageCreateInstallerFileAssociationsTest + */ +public class JPackageCreateInstallerFileAssociationsTest { + private static final String TEST_NAME = "JPackageCreateInstallerFileAssociationsTest"; + private static final String EXT = "dmg"; + + public static void main(String[] args) throws Exception { + JPackageCreateInstallerFileAssociationsBase.run(TEST_NAME, EXT); + } +} diff -r f6ab4cc4c70e -r 3409e81fb3cb test/jdk/tools/jpackage/createinstaller/macosx/dmg/JPackageCreateInstallerInstallDirTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/createinstaller/macosx/dmg/JPackageCreateInstallerInstallDirTest.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,45 @@ +/* + * Copyright (c) 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 JPackageCreateInstallerInstallDirBase + * @requires (os.family == "mac") + * @modules jdk.jpackage + * @ignore + * @run main/othervm -Xmx512m JPackageCreateInstallerInstallDirTest + */ +public class JPackageCreateInstallerInstallDirTest { + private static final String TEST_NAME = "JPackageCreateInstallerInstallDirTest"; + private static final String EXT = "dmg"; + + public static void main(String[] args) throws Exception { + JPackageCreateInstallerInstallDirBase.run(TEST_NAME, EXT); + } +} diff -r f6ab4cc4c70e -r 3409e81fb3cb test/jdk/tools/jpackage/createinstaller/macosx/dmg/JPackageCreateInstallerLicenseTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/createinstaller/macosx/dmg/JPackageCreateInstallerLicenseTest.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,45 @@ +/* + * Copyright (c) 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 == "mac") + * @modules jdk.jpackage + * @ignore + * @run main/othervm -Xmx512m JPackageCreateInstallerLicenseTest + */ +public class JPackageCreateInstallerLicenseTest { + private static final String TEST_NAME = "JPackageCreateInstallerLicenseTest"; + private static final String EXT = "dmg"; + + public static void main(String[] args) throws Exception { + JPackageCreateInstallerLicenseBase.run(TEST_NAME, EXT); + } +} diff -r f6ab4cc4c70e -r 3409e81fb3cb test/jdk/tools/jpackage/createinstaller/macosx/dmg/JPackageCreateInstallerTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/createinstaller/macosx/dmg/JPackageCreateInstallerTest.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,45 @@ +/* + * Copyright (c) 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 == "mac") + * @modules jdk.jpackage + * @ignore + * @run main/othervm -Xmx512m JPackageCreateInstallerTest + */ +public class JPackageCreateInstallerTest { + private static final String TEST_NAME = "JPackageCreateInstallerTest"; + private static final String EXT = "dmg"; + + public static void main(String[] args) throws Exception { + JPackageCreateInstallerBase.run(TEST_NAME, EXT); + } +} diff -r f6ab4cc4c70e -r 3409e81fb3cb test/jdk/tools/jpackage/createinstaller/macosx/dmg/install.sh --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/createinstaller/macosx/dmg/install.sh Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,21 @@ +echo "Note: This script will install DMG files silently. In order to verify UI, each .dmg needs to launched manually via Finder." + +# JPackageCreateInstallerTest +hdiutil attach JPackageCreateInstallerTest-1.0.dmg +sudo /usr/sbin/installer -pkg /Volumes/JPackageCreateInstallerTest/JPackageCreateInstallerTest-1.0.pkg -target / +hdiutil detach /Volumes/JPackageCreateInstallerTest/ + +# JPackageCreateInstallerLicenseTest +hdiutil attach JPackageCreateInstallerLicenseTest-1.0.dmg +sudo /usr/sbin/installer -pkg /Volumes/JPackageCreateInstallerLicenseTest/JPackageCreateInstallerLicenseTest-1.0.pkg -target / +hdiutil detach /Volumes/JPackageCreateInstallerLicenseTest/ + +# JPackageCreateInstallerFileAssociationsTest +hdiutil attach JPackageCreateInstallerFileAssociationsTest-1.0.dmg +sudo /usr/sbin/installer -pkg /Volumes/JPackageCreateInstallerFileAssociationsTest/JPackageCreateInstallerFileAssociationsTest-1.0.pkg -target / +hdiutil detach /Volumes/JPackageCreateInstallerFileAssociationsTest/ + +# JPackageCreateInstallerInstallDirTest +hdiutil attach JPackageCreateInstallerInstallDirTest-1.0.dmg +sudo /usr/sbin/installer -pkg /Volumes/JPackageCreateInstallerInstallDirTest/JPackageCreateInstallerInstallDirTest-1.0.pkg -target / +hdiutil detach /Volumes/JPackageCreateInstallerInstallDirTest/ diff -r f6ab4cc4c70e -r 3409e81fb3cb test/jdk/tools/jpackage/createinstaller/macosx/dmg/uninstall.sh --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/createinstaller/macosx/dmg/uninstall.sh Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,4 @@ +sudo rm -rf /Applications/JPackageCreateInstallerTest.app +sudo rm -rf /Applications/JPackageCreateInstallerLicenseTest.app +sudo rm -rf /Applications/JPackageCreateInstallerFileAssociationsTest.app +sudo rm -rf /Applications/jpackage/JPackageCreateInstallerInstallDirTest.app diff -r f6ab4cc4c70e -r 3409e81fb3cb test/jdk/tools/jpackage/createinstaller/macosx/pkg/JPackageCreateInstallerFileAssociationsTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/createinstaller/macosx/pkg/JPackageCreateInstallerFileAssociationsTest.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,44 @@ +/* + * Copyright (c) 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 == "mac") + * @modules jdk.jpackage + * @run main/othervm -Xmx512m JPackageCreateInstallerFileAssociationsTest + */ +public class JPackageCreateInstallerFileAssociationsTest { + private static final String TEST_NAME = "JPackageCreateInstallerFileAssociationsTest"; + private static final String EXT = "pkg"; + + public static void main(String[] args) throws Exception { + JPackageCreateInstallerFileAssociationsBase.run(TEST_NAME, EXT); + } +} diff -r f6ab4cc4c70e -r 3409e81fb3cb test/jdk/tools/jpackage/createinstaller/macosx/pkg/JPackageCreateInstallerInstallDirTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/createinstaller/macosx/pkg/JPackageCreateInstallerInstallDirTest.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,44 @@ +/* + * Copyright (c) 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 JPackageCreateInstallerInstallDirBase + * @requires (os.family == "mac") + * @modules jdk.jpackage + * @run main/othervm -Xmx512m JPackageCreateInstallerInstallDirTest + */ +public class JPackageCreateInstallerInstallDirTest { + private static final String TEST_NAME = "JPackageCreateInstallerInstallDirTest"; + private static final String EXT = "pkg"; + + public static void main(String[] args) throws Exception { + JPackageCreateInstallerInstallDirBase.run(TEST_NAME, EXT); + } +} diff -r f6ab4cc4c70e -r 3409e81fb3cb test/jdk/tools/jpackage/createinstaller/macosx/pkg/JPackageCreateInstallerLicenseTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/createinstaller/macosx/pkg/JPackageCreateInstallerLicenseTest.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,44 @@ +/* + * Copyright (c) 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 == "mac") + * @modules jdk.jpackage + * @run main/othervm -Xmx512m JPackageCreateInstallerLicenseTest + */ +public class JPackageCreateInstallerLicenseTest { + private static final String TEST_NAME = "JPackageCreateInstallerLicenseTest"; + private static final String EXT = "pkg"; + + public static void main(String[] args) throws Exception { + JPackageCreateInstallerLicenseBase.run(TEST_NAME, EXT); + } +} diff -r f6ab4cc4c70e -r 3409e81fb3cb test/jdk/tools/jpackage/createinstaller/macosx/pkg/JPackageCreateInstallerTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/createinstaller/macosx/pkg/JPackageCreateInstallerTest.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,44 @@ +/* + * Copyright (c) 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 == "mac") + * @modules jdk.jpackage + * @run main/othervm -Xmx512m JPackageCreateInstallerTest + */ +public class JPackageCreateInstallerTest { + private static final String TEST_NAME = "JPackageCreateInstallerTest"; + private static final String EXT = "pkg"; + + public static void main(String[] args) throws Exception { + JPackageCreateInstallerBase.run(TEST_NAME, EXT); + } +} diff -r f6ab4cc4c70e -r 3409e81fb3cb test/jdk/tools/jpackage/createinstaller/macosx/pkg/install.sh --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/createinstaller/macosx/pkg/install.sh Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,5 @@ +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 JPackageCreateInstallerTest-1.0.pkg -target / +sudo /usr/sbin/installer -pkg JPackageCreateInstallerLicenseTest-1.0.pkg -target / +sudo /usr/sbin/installer -pkg JPackageCreateInstallerFileAssociationsTest-1.0.pkg -target / +sudo /usr/sbin/installer -pkg JPackageCreateInstallerInstallDirTest-1.0.pkg -target / diff -r f6ab4cc4c70e -r 3409e81fb3cb test/jdk/tools/jpackage/createinstaller/macosx/pkg/uninstall.sh --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/createinstaller/macosx/pkg/uninstall.sh Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,4 @@ +sudo rm -rf /Applications/JPackageCreateInstallerTest.app +sudo rm -rf /Applications/JPackageCreateInstallerLicenseTest.app +sudo rm -rf /Applications/JPackageCreateInstallerFileAssociationsTest.app +sudo rm -rf /Applications/jpackage/JPackageCreateInstallerInstallDirTest.app diff -r f6ab4cc4c70e -r 3409e81fb3cb test/jdk/tools/jpackage/createinstaller/windows/base/JPackageCreateInstallerBase.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/createinstaller/windows/base/JPackageCreateInstallerBase.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,94 @@ +/* + * Copyright (c) 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 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[]{ + "create-installer", + EXT, + "--input", "input", + "--output", "output", + "--name", TEST_NAME, + "--main-jar", "hello.jar", + "--class", "Hello", + "--force", + "--files", "hello.jar"}; + } + + 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(); + } + } +} diff -r f6ab4cc4c70e -r 3409e81fb3cb test/jdk/tools/jpackage/createinstaller/windows/base/JPackageCreateInstallerFileAssociationsBase.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/createinstaller/windows/base/JPackageCreateInstallerFileAssociationsBase.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,170 @@ +/* + * Copyright (c) 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 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) { + TEST_NAME = name; + EXT = ext; + TEST_EXT = "jptest1"; + OUTPUT = "output" + File.separator + TEST_NAME + "-1.0." + EXT; + CMD = new String[]{ + "create-installer", + EXT, + "--input", "input", + "--output", "output", + "--name", TEST_NAME, + "--main-jar", "hello.jar", + "--class", "Hello", + "--force", + "--files", "hello.jar", + "--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(); + createFileAssociationsProperties(); + testCreateInstaller(); + } + } +} diff -r f6ab4cc4c70e -r 3409e81fb3cb test/jdk/tools/jpackage/createinstaller/windows/base/JPackageCreateInstallerLicenseBase.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/createinstaller/windows/base/JPackageCreateInstallerLicenseBase.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,95 @@ +/* + * Copyright (c) 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 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 [] { + "create-installer", + EXT, + "--input", "input", + "--output", "output", + "--name", TEST_NAME, + "--main-jar", "hello.jar", + "--class", "Hello", + "--force", + "--files", "hello.jar", + "--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(); + } + } +} diff -r f6ab4cc4c70e -r 3409e81fb3cb test/jdk/tools/jpackage/createinstaller/windows/base/JPackageCreateInstallerWinDirChooserBase.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/createinstaller/windows/base/JPackageCreateInstallerWinDirChooserBase.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,95 @@ +/* + * Copyright (c) 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 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[]{ + "create-installer", + EXT, + "--input", "input", + "--output", "output", + "--name", TEST_NAME, + "--main-jar", "hello.jar", + "--class", "Hello", + "--force", + "--files", "hello.jar", + "--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(); + } + } +} diff -r f6ab4cc4c70e -r 3409e81fb3cb test/jdk/tools/jpackage/createinstaller/windows/base/JPackageCreateInstallerWinMenuBase.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/createinstaller/windows/base/JPackageCreateInstallerWinMenuBase.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,95 @@ +/* + * Copyright (c) 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 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[]{ + "create-installer", + EXT, + "--input", "input", + "--output", "output", + "--name", TEST_NAME, + "--main-jar", "hello.jar", + "--class", "Hello", + "--force", + "--files", "hello.jar", + "--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(); + } + } +} diff -r f6ab4cc4c70e -r 3409e81fb3cb test/jdk/tools/jpackage/createinstaller/windows/base/JPackageCreateInstallerWinMenuGroupBase.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/createinstaller/windows/base/JPackageCreateInstallerWinMenuGroupBase.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,96 @@ +/* + * Copyright (c) 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 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[]{ + "create-installer", + EXT, + "--input", "input", + "--output", "output", + "--name", TEST_NAME, + "--main-jar", "hello.jar", + "--class", "Hello", + "--force", + "--files", "hello.jar", + "--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(); + } + } +} diff -r f6ab4cc4c70e -r 3409e81fb3cb test/jdk/tools/jpackage/createinstaller/windows/base/JPackageCreateInstallerWinPerUserInstallBase.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/createinstaller/windows/base/JPackageCreateInstallerWinPerUserInstallBase.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,97 @@ +/* + * Copyright (c) 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 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[]{ + "create-installer", + EXT, + "--input", "input", + "--output", "output", + "--name", TEST_NAME, + "--main-jar", "hello.jar", + "--class", "Hello", + "--force", + "--files", "hello.jar", + "--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(); + } + } +} diff -r f6ab4cc4c70e -r 3409e81fb3cb test/jdk/tools/jpackage/createinstaller/windows/base/JPackageCreateInstallerWinRegistryNameBase.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/createinstaller/windows/base/JPackageCreateInstallerWinRegistryNameBase.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,173 @@ +/* + * Copyright (c) 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 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[]{ + "create-installer", + EXT, + "--input", "input", + "--output", "output", + "--name", TEST_NAME, + "--main-jar", "hello.jar", + "--class", "Hello", + "--force", + "--files", "hello.jar", + "--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(); + } + } +} diff -r f6ab4cc4c70e -r 3409e81fb3cb test/jdk/tools/jpackage/createinstaller/windows/base/JPackageCreateInstallerWinShortcutBase.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/createinstaller/windows/base/JPackageCreateInstallerWinShortcutBase.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,101 @@ +/* + * Copyright (c) 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 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[]{ + "create-installer", + EXT, + "--input", "input", + "--output", "output", + "--name", TEST_NAME, + "--main-jar", "hello.jar", + "--class", "Hello", + "--force", + "--files", "hello.jar", + "--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(); + } + } +} diff -r f6ab4cc4c70e -r 3409e81fb3cb test/jdk/tools/jpackage/createinstaller/windows/exe/JPackageCreateInstallerFileAssociationsTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/createinstaller/windows/exe/JPackageCreateInstallerFileAssociationsTest.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,45 @@ +/* + * Copyright (c) 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 + * @ignore + * @run main/othervm -Xmx512m JPackageCreateInstallerFileAssociationsTest + */ +public class JPackageCreateInstallerFileAssociationsTest { + private static final String TEST_NAME = "JPackageCreateInstallerFileAssociationsTest"; + private static final String EXT = "exe"; + + public static void main(String[] args) throws Exception { + JPackageCreateInstallerFileAssociationsBase.run(TEST_NAME, EXT); + } +} diff -r f6ab4cc4c70e -r 3409e81fb3cb test/jdk/tools/jpackage/createinstaller/windows/exe/JPackageCreateInstallerLicenseTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/createinstaller/windows/exe/JPackageCreateInstallerLicenseTest.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,45 @@ +/* + * Copyright (c) 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 + * @ignore + * @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 { + JPackageCreateInstallerLicenseBase.run(TEST_NAME, EXT); + } +} diff -r f6ab4cc4c70e -r 3409e81fb3cb test/jdk/tools/jpackage/createinstaller/windows/exe/JPackageCreateInstallerTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/createinstaller/windows/exe/JPackageCreateInstallerTest.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,45 @@ +/* + * Copyright (c) 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 + * @ignore + * @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 { + JPackageCreateInstallerBase.run(TEST_NAME, EXT); + } +} diff -r f6ab4cc4c70e -r 3409e81fb3cb test/jdk/tools/jpackage/createinstaller/windows/exe/JPackageCreateInstallerWinDirChooserTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/createinstaller/windows/exe/JPackageCreateInstallerWinDirChooserTest.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,45 @@ +/* + * Copyright (c) 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 + * @ignore + * @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 { + JPackageCreateInstallerWinDirChooserBase.run(TEST_NAME, EXT); + } +} diff -r f6ab4cc4c70e -r 3409e81fb3cb test/jdk/tools/jpackage/createinstaller/windows/exe/JPackageCreateInstallerWinMenuGroupTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/createinstaller/windows/exe/JPackageCreateInstallerWinMenuGroupTest.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,45 @@ +/* + * Copyright (c) 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 + * @ignore + * @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 { + JPackageCreateInstallerWinMenuGroupBase.run(TEST_NAME, EXT); + } +} diff -r f6ab4cc4c70e -r 3409e81fb3cb test/jdk/tools/jpackage/createinstaller/windows/exe/JPackageCreateInstallerWinMenuTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/createinstaller/windows/exe/JPackageCreateInstallerWinMenuTest.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,45 @@ +/* + * Copyright (c) 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 + * @ignore + * @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 { + JPackageCreateInstallerWinMenuBase.run(TEST_NAME, EXT); + } +} diff -r f6ab4cc4c70e -r 3409e81fb3cb test/jdk/tools/jpackage/createinstaller/windows/exe/JPackageCreateInstallerWinPerUserInstallTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/createinstaller/windows/exe/JPackageCreateInstallerWinPerUserInstallTest.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,45 @@ +/* + * Copyright (c) 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 + * @ignore + * @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 { + JPackageCreateInstallerWinPerUserInstallBase.run(TEST_NAME, EXT); + } +} diff -r f6ab4cc4c70e -r 3409e81fb3cb test/jdk/tools/jpackage/createinstaller/windows/exe/JPackageCreateInstallerWinRegistryNameTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/createinstaller/windows/exe/JPackageCreateInstallerWinRegistryNameTest.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,45 @@ +/* + * Copyright (c) 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 + * @ignore + * @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 { + JPackageCreateInstallerWinRegistryNameBase.run(TEST_NAME, EXT); + } +} diff -r f6ab4cc4c70e -r 3409e81fb3cb test/jdk/tools/jpackage/createinstaller/windows/exe/JPackageCreateInstallerWinShortcutTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/createinstaller/windows/exe/JPackageCreateInstallerWinShortcutTest.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,45 @@ +/* + * Copyright (c) 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 + * @ignore + * @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 { + JPackageCreateInstallerWinShortcutBase.run(TEST_NAME, EXT); + } +} diff -r f6ab4cc4c70e -r 3409e81fb3cb test/jdk/tools/jpackage/createinstaller/windows/exe/install.bat --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/createinstaller/windows/exe/install.bat Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,12 @@ +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 +PAUSE diff -r f6ab4cc4c70e -r 3409e81fb3cb test/jdk/tools/jpackage/createinstaller/windows/exe/uninstall.bat --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/createinstaller/windows/exe/uninstall.bat Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,10 @@ +"%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" +PAUSE \ No newline at end of file diff -r f6ab4cc4c70e -r 3409e81fb3cb test/jdk/tools/jpackage/createinstaller/windows/msi/JPackageCreateInstallerFileAssociationsTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/createinstaller/windows/msi/JPackageCreateInstallerFileAssociationsTest.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,45 @@ +/* + * Copyright (c) 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 + * @ignore + * @run main/othervm -Xmx512m JPackageCreateInstallerFileAssociationsTest + */ +public class JPackageCreateInstallerFileAssociationsTest { + private static final String TEST_NAME = "JPackageCreateInstallerFileAssociationsTest"; + private static final String EXT = "msi"; + + public static void main(String[] args) throws Exception { + JPackageCreateInstallerFileAssociationsBase.run(TEST_NAME, EXT); + } +} diff -r f6ab4cc4c70e -r 3409e81fb3cb test/jdk/tools/jpackage/createinstaller/windows/msi/JPackageCreateInstallerLicenseTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/createinstaller/windows/msi/JPackageCreateInstallerLicenseTest.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,45 @@ +/* + * Copyright (c) 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 + * @ignore + * @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 { + JPackageCreateInstallerLicenseBase.run(TEST_NAME, EXT); + } +} diff -r f6ab4cc4c70e -r 3409e81fb3cb test/jdk/tools/jpackage/createinstaller/windows/msi/JPackageCreateInstallerTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/createinstaller/windows/msi/JPackageCreateInstallerTest.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,45 @@ +/* + * Copyright (c) 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 + * @ignore + * @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 { + JPackageCreateInstallerBase.run(TEST_NAME, EXT); + } +} diff -r f6ab4cc4c70e -r 3409e81fb3cb test/jdk/tools/jpackage/createinstaller/windows/msi/JPackageCreateInstallerWinDirChooserTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/createinstaller/windows/msi/JPackageCreateInstallerWinDirChooserTest.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,45 @@ +/* + * Copyright (c) 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 + * @ignore + * @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 { + JPackageCreateInstallerWinDirChooserBase.run(TEST_NAME, EXT); + } +} diff -r f6ab4cc4c70e -r 3409e81fb3cb test/jdk/tools/jpackage/createinstaller/windows/msi/JPackageCreateInstallerWinMenuGroupTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/createinstaller/windows/msi/JPackageCreateInstallerWinMenuGroupTest.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,45 @@ +/* + * Copyright (c) 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 + * @ignore + * @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 { + JPackageCreateInstallerWinMenuGroupBase.run(TEST_NAME, EXT); + } +} diff -r f6ab4cc4c70e -r 3409e81fb3cb test/jdk/tools/jpackage/createinstaller/windows/msi/JPackageCreateInstallerWinMenuTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/createinstaller/windows/msi/JPackageCreateInstallerWinMenuTest.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,45 @@ +/* + * Copyright (c) 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 + * @ignore + * @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 { + JPackageCreateInstallerWinMenuBase.run(TEST_NAME, EXT); + } +} diff -r f6ab4cc4c70e -r 3409e81fb3cb test/jdk/tools/jpackage/createinstaller/windows/msi/JPackageCreateInstallerWinPerUserInstallTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/createinstaller/windows/msi/JPackageCreateInstallerWinPerUserInstallTest.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,45 @@ +/* + * Copyright (c) 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 + * @ignore + * @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 { + JPackageCreateInstallerWinPerUserInstallBase.run(TEST_NAME, EXT); + } +} diff -r f6ab4cc4c70e -r 3409e81fb3cb test/jdk/tools/jpackage/createinstaller/windows/msi/JPackageCreateInstallerWinRegistryNameTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/createinstaller/windows/msi/JPackageCreateInstallerWinRegistryNameTest.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,45 @@ +/* + * Copyright (c) 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 + * @ignore + * @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 { + JPackageCreateInstallerWinRegistryNameBase.run(TEST_NAME, EXT); + } +} diff -r f6ab4cc4c70e -r 3409e81fb3cb test/jdk/tools/jpackage/createinstaller/windows/msi/JPackageCreateInstallerWinShortcutTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/createinstaller/windows/msi/JPackageCreateInstallerWinShortcutTest.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,45 @@ +/* + * Copyright (c) 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 + * @ignore + * @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 { + JPackageCreateInstallerWinShortcutBase.run(TEST_NAME, EXT); + } +} diff -r f6ab4cc4c70e -r 3409e81fb3cb test/jdk/tools/jpackage/createinstaller/windows/msi/install.bat --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/createinstaller/windows/msi/install.bat Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,12 @@ +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 +PAUSE diff -r f6ab4cc4c70e -r 3409e81fb3cb test/jdk/tools/jpackage/createinstaller/windows/msi/uninstall.bat --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/createinstaller/windows/msi/uninstall.bat Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,10 @@ +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 +PAUSE \ No newline at end of file diff -r f6ab4cc4c70e -r 3409e81fb3cb test/jdk/tools/jpackage/helpers/JPackageHelper.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/helpers/JPackageHelper.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,531 @@ +/* + * Copyright (c) 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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.PrintWriter; +import java.io.StringWriter; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; + +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 + File testSrc = new File(System.getProperty("test.src") + File.separator + ".." + + File.separator + "apps"); + if (testSrc.exists()) { + TEST_SRC_ROOT = System.getProperty("test.src") + File.separator + ".."; + } else { + testSrc = new File(System.getProperty("test.src") + File.separator + + ".." + File.separator + ".." + File.separator + "apps"); + if (testSrc.exists()) { + TEST_SRC_ROOT = System.getProperty("test.src") + File.separator + ".." + + File.separator + ".."; + } else { + testSrc = new File(System.getProperty("test.src") + File.separator + + ".." + File.separator + ".." + File.separator + ".." + + File.separator + "apps"); + if (testSrc.exists()) { + TEST_SRC_ROOT = System.getProperty("test.src") + File.separator + ".." + + File.separator + ".." + File.separator + ".."; + } else { + TEST_SRC_ROOT = System.getProperty("test.src"); + } + } + } + + TEST_SRC = System.getProperty("test.src"); + } + + 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 String executeCLI(boolean retValZero, String... args) throws Exception { + int retVal; + File outfile = new File("output.log"); + try { + String[] command = getCommand(args); + 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(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() throws Exception { + createJar(false, "Hello", "image"); + } + + public static void createHelloImageJarWithMainClass() throws Exception { + createJar(true, "Hello", "image"); + } + + public static void createHelloInstallerJar() throws Exception { + createJar(false, "Hello", "installer"); + } + + public static void createHelloInstallerJarWithMainClass() throws Exception { + createJar(true, "Hello", "installer"); + } + + private static void createJar(boolean mainClassAttribute, String name, + String testType) throws Exception { + int retVal; + + File input = new File("input"); + if (!input.exists()) { + input.mkdir(); + } + + Files.copy(Path.of(TEST_SRC_ROOT + File.separator + "apps" + File.separator + + testType + File.separator + name + ".java"), Path.of(name + ".java")); + + 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 args = new ArrayList<>(); + args.add(JAR.toString()); + args.add("-c"); + args.add("-v"); + args.add("-f"); + args.add("input" + 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"); + } + + private static void createModule(String javaFile) throws Exception { + int retVal; + + File input = new File("input"); + if (!input.exists()) { + input.mkdir(); + } + + File module = new File("module" + File.separator + "com.hello"); + if (!module.exists()) { + module.mkdirs(); + } + + File javacLog = new File("javac.log"); + try { + List args = new ArrayList<>(); + args.add(JAVAC.toString()); + args.add("-d"); + args.add("module" + File.separator + "com.hello"); + args.add(TEST_SRC_ROOT + File.separator + "apps" + File.separator + "com.hello" + + File.separator + "module-info.java"); + args.add(TEST_SRC_ROOT + File.separator + "apps" + File.separator + "com.hello" + + File.separator + "com" + File.separator + "hello" + 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); + } + + File jarLog = new File("jar.log"); + try { + List args = new ArrayList<>(); + args.add(JAR.toString()); + args.add("--create"); + args.add("--file"); + args.add("input" + File.separator + "com.hello.jar"); + args.add("-C"); + args.add("module" + File.separator + "com.hello"); + 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 { + int retVal; + + File jlinkLog = new File("jlink.log"); + try { + List args = new ArrayList<>(); + args.add(JLINK.toString()); + args.add("--output"); + args.add("runtime"); + args.add("--add-modules"); + args.add("java.base"); + 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 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; + } + + 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) != '\\') { + if (!toolProvider && isWindows()) { + sb.appendCodePoint('\\'); + sb.appendCodePoint('\\'); + } + 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 { + if (isWindows()) { + sb.appendCodePoint('\\'); + } + sb.appendCodePoint(code); + } + break; + default: + sb.appendCodePoint(code); + break; + } + } + return sb.toString(); + } + + return in; + } + +} diff -r f6ab4cc4c70e -r 3409e81fb3cb test/jdk/tools/jpackage/helpers/JPackageInstallerHelper.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/helpers/JPackageInstallerHelper.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,243 @@ +/* + * Copyright (c) 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 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); + } + } + } +} diff -r f6ab4cc4c70e -r 3409e81fb3cb test/jdk/tools/jpackage/helpers/JPackagePath.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/helpers/JPackagePath.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,278 @@ +/* + * Copyright (c) 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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; + +/** + * 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() { + if (JPackageHelper.isWindows()) { + return "output" + File.separator + "test" + File.separator + "test.exe"; + } else if (JPackageHelper.isOSX()) { + return "output" + File.separator + "test.app" + File.separator + "Contents" + + File.separator + "MacOS" + File.separator + "test"; + } else if (JPackageHelper.isLinux()) { + return "output" + File.separator + "test" + File.separator + "test"; + } else { + throw new AssertionError("Cannot detect platform"); + } + } + + // Returns path to generate test application icon + public static String getAppIcon() { + if (JPackageHelper.isWindows()) { + return "output" + File.separator + "test" + File.separator + "test.ico"; + } else if (JPackageHelper.isOSX()) { + return "output" + File.separator + "test.app" + File.separator + "Contents" + + File.separator + "Resources" + File.separator + "test.icns"; + } else if (JPackageHelper.isLinux()) { + return "output" + File.separator + "test" + File.separator + + File.separator + "resources"+ File.separator + "test.png"; + } else { + throw new AssertionError("Cannot detect platform"); + } + } + + // Returns path to generate test application without --name argument + public static String getAppNoName() { + if (JPackageHelper.isWindows()) { + return "output" + File.separator + "Hello" + File.separator + "Hello.exe"; + } else if (JPackageHelper.isOSX()) { + return "output" + File.separator + "Hello.app" + File.separator + "Contents" + + File.separator + "MacOS" + File.separator + "Hello"; + } else if (JPackageHelper.isLinux()) { + return "output" + File.separator + "Hello" + File.separator + "Hello"; + } else { + throw new AssertionError("Cannot detect platform"); + } + } + + // Returns path to generate secondary launcher of test application + public static String getAppSL() { + if (JPackageHelper.isWindows()) { + return "output" + File.separator + "test" + File.separator + "test2.exe"; + } else if (JPackageHelper.isOSX()) { + return "output" + File.separator + "test.app" + File.separator + "Contents" + + File.separator + "MacOS" + File.separator + "test2"; + } else if (JPackageHelper.isLinux()) { + return "output" + File.separator + "test" + File.separator + "test2"; + } else { + throw new AssertionError("Cannot detect platform"); + } + } + + // Returns path to app working directory (where test application generates its output) + public static String getAppWorkingDir() { + if (JPackageHelper.isWindows()) { + return "output" + File.separator + "test" + File.separator + "app"; + } else if (JPackageHelper.isOSX()) { + return "output" + File.separator + "test.app" + File.separator + "Contents" + + File.separator + "Java"; + } else if (JPackageHelper.isLinux()) { + return "output" + File.separator + "test" + File.separator + "app"; + } else { + throw new AssertionError("Cannot detect platform"); + } + } + + // Returns path to test application cfg file + public static String getAppCfg() { + if (JPackageHelper.isWindows()) { + return "output" + File.separator + "test" + File.separator + "app" + File.separator + + "test.cfg"; + } else if (JPackageHelper.isOSX()) { + return "output" + File.separator + "test.app" + File.separator + "Contents" + + File.separator + "Java" + File.separator + "test.cfg"; + } else if (JPackageHelper.isLinux()) { + return "output" + File.separator + "test" + File.separator + "app" + File.separator + + "test.cfg"; + } else { + throw new AssertionError("Cannot detect platform"); + } + } + + // Returns path to app working directory without --name (where test application generates its output) + public static String getAppWorkingDirNoName() { + if (JPackageHelper.isWindows()) { + return "output" + File.separator + "Hello" + File.separator + "app"; + } else if (JPackageHelper.isOSX()) { + return "output" + File.separator + "Hello.app" + File.separator + "Contents" + + File.separator + "Java"; + } else if (JPackageHelper.isLinux()) { + return "output" + File.separator + "Hello" + File.separator + "app"; + } else { + throw new AssertionError("Cannot detect platform"); + } + } + + // Returns path including executable to java in image runtime folder + public static String getRuntimeJava() { + if (JPackageHelper.isWindows()) { + return "output" + File.separator + "test" + File.separator + "runtime" + File.separator + + "bin" + File.separator + "java.exe"; + } else if (JPackageHelper.isOSX()) { + return "output" + File.separator + "test.app" + File.separator + + "Contents" + File.separator + "PlugIns" + File.separator + + "Java.runtime" + File.separator + "Contents" + File.separator + + "Home" + File.separator + "bin" + File.separator + "java"; + } else if (JPackageHelper.isLinux()) { + return "output" + File.separator + "test" + File.separator + "runtime" + File.separator + + "bin" + File.separator + "java"; + } 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() { + if (JPackageHelper.isWindows()) { + return "output" + File.separator + "test" + File.separator + "runtime" + + File.separator + "bin"; + } else if (JPackageHelper.isOSX()) { + return "output" + File.separator + "test.app" + File.separator + "Contents" + + File.separator + "PlugIns" + File.separator + "Java.runtime" + + File.separator + "Contents" + File.separator + "Home" + File.separator + + "bin"; + } else if (JPackageHelper.isLinux()) { + return "output" + File.separator + "test" + File.separator + "runtime" + + File.separator + "bin"; + } else { + throw new AssertionError("Cannot detect platform"); + } + } + + public static String getWinProgramFiles() { + return WIN_PROGRAM_FILES; + } + + public static String getWinUserLocal() { + return System.getProperty("user.home") + File.separator + "AppData" + + File.separator + "Local"; + } + + public static String getWinStartMenu() { + return WIN_START_MENU; + } + + public static String getWinPublicDesktop() { + return WIN_PUBLIC_DESKTOP; + } + + public static String getWinUserLocalStartMenu() { + return System.getProperty("user.home") + File.separator + "AppData" + + File.separator + "Roaming" + File.separator + "Microsoft" + + File.separator + "Windows" + File.separator + "Start Menu" + + File.separator + "Programs"; + + } + + public static String getWinInstalledApp(String testName) { + return getWinProgramFiles() + File.separator + testName + File.separator + + testName + ".exe"; + } + + 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 + 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; + } +} diff -r f6ab4cc4c70e -r 3409e81fb3cb test/jdk/tools/jpackage/jdk/jpackage/internal/DeployParamsTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/jdk/jpackage/internal/DeployParamsTest.java Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,124 @@ +/* + * Copyright (c) 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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().equals("Error: Invalid character found in --name argument")) { + 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"); + + 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(); + } + } + +} diff -r f6ab4cc4c70e -r 3409e81fb3cb test/jdk/tools/jpackage/resources/icon.icns Binary file test/jdk/tools/jpackage/resources/icon.icns has changed diff -r f6ab4cc4c70e -r 3409e81fb3cb test/jdk/tools/jpackage/resources/icon.ico Binary file test/jdk/tools/jpackage/resources/icon.ico has changed diff -r f6ab4cc4c70e -r 3409e81fb3cb test/jdk/tools/jpackage/resources/icon.png Binary file test/jdk/tools/jpackage/resources/icon.png has changed diff -r f6ab4cc4c70e -r 3409e81fb3cb test/jdk/tools/jpackage/resources/license.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/resources/license.txt Thu Jan 10 13:13:56 2019 -0500 @@ -0,0 +1,1 @@ +jpackage test lisense file. diff -r f6ab4cc4c70e -r 3409e81fb3cb test/jdk/tools/launcher/HelpFlagsTest.java --- a/test/jdk/tools/launcher/HelpFlagsTest.java Thu Jan 10 09:11:56 2019 -0800 +++ b/test/jdk/tools/launcher/HelpFlagsTest.java Thu Jan 10 13:13:56 2019 -0500 @@ -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, 0, 255), // -h, --help, }; // Returns true if the file is not a tool. diff -r f6ab4cc4c70e -r 3409e81fb3cb test/jdk/tools/launcher/VersionCheck.java --- a/test/jdk/tools/launcher/VersionCheck.java Thu Jan 10 09:11:56 2019 -0800 +++ b/test/jdk/tools/launcher/VersionCheck.java Thu Jan 10 13:13:56 2019 -0500 @@ -62,7 +62,7 @@ "jmc", "jmc.ini", "jweblauncher", - "packager", + "jpackage", "ssvagent", "unpack200", }; @@ -108,7 +108,7 @@ "klist", "ktab", "pack200", - "packager", + "jpackage", "rmic", "rmid", "rmiregistry",