--- old/make/CompileDemos.gmk 2018-10-22 10:21:56.445616000 -0400 +++ new/make/CompileDemos.gmk 2018-10-22 10:21:54.791507200 -0400 @@ -1,5 +1,5 @@ # -# Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved. +# 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 @@ -219,6 +219,11 @@ MAIN_CLASS := transparentruler.Ruler, \ )) +$(eval $(call SetupBuildDemo, JNLPConverter, \ + DEMO_SUBDIR := jpackager, \ + MAIN_CLASS := jnlp.converter.Main, \ +)) + ################################################################################ # Copy html and README files. --- old/make/CompileJavaModules.gmk 2018-10-22 10:22:04.673347200 -0400 +++ new/make/CompileJavaModules.gmk 2018-10-22 10:22:03.111866800 -0400 @@ -384,6 +384,29 @@ ################################################################################ +jdk.packager_ADD_JAVAC_FLAGS += -parameters -XDstringConcat=inline + +jdk.packager_SETUP := GENERATE_JDKBYTECODE_NOWARNINGS +jdk.packager_COPY += .gif .png .txt .spec .script .prerm .preinst .postrm .postinst .list \ + .desktop .copyright .control .plist .template .icns .scpt .entitlements .wxs .iss .ico .bmp + +jdk.packager_CLEAN_FILES += $(wildcard \ + $(TOPDIR)/src/jdk.packager/share/classes/jdk/packager/internal/resources/*.properties \ + $(TOPDIR)/src/jdk.packager/share/classes/jdk/packager/internal/resources/builders/*.properties \ + $(TOPDIR)/src/jdk.packager/linux/classes/jdk/packager/internal/resources/builders/linux/*.properties \ + $(TOPDIR)/src/jdk.packager/macosx/classes/jdk/packager/internal/resources/builders/mac/*.properties \ + $(TOPDIR)/src/jdk.packager/windows/classes/jdk/packager/internal/resources/builders/windows/*.properties \ + $(TOPDIR)/src/jdk.packager/linux/classes/jdk/packager/internal/resources/linux/*.properties \ + $(TOPDIR)/src/jdk.packager/macosx/classes/jdk/packager/internal/resources/mac/*.properties \ + $(TOPDIR)/src/jdk.packager/windows/classes/jdk/packager/internal/resources/windows/*.properties) + +################################################################################ + +jdk.packager.services_SETUP := GENERATE_JDKBYTECODE_NOWARNINGS +jdk.packager.services_COPY += .gif .png .txt + +################################################################################ + jdk.jconsole_COPY += .gif .png jdk.jconsole_CLEAN_FILES += $(wildcard \ --- old/make/common/Modules.gmk 2018-10-22 10:22:12.541167600 -0400 +++ new/make/common/Modules.gmk 2018-10-22 10:22:10.965082800 -0400 @@ -128,6 +128,8 @@ JRE_TOOL_MODULES += \ jdk.jdwp.agent \ jdk.pack \ + jdk.packager \ + jdk.packager.services \ jdk.scripting.nashorn.shell \ # @@ -168,6 +170,8 @@ jdk.naming.rmi \ jdk.net \ jdk.pack \ + jdk.packager \ + jdk.packager.services \ jdk.rmic \ jdk.scripting.nashorn \ jdk.sctp \ @@ -227,6 +231,14 @@ endif ################################################################################ +# Some platforms don't have jpackager + +ifeq ($(OPENJDK_TARGET_OS), solaris) + MODULES_FILTER += jdk.packager + MODULES_FILTER += jdk.packager.services +endif + +################################################################################ # Module list macros # Use append so that the custom extension may add to these variables --- old/src/jdk.jlink/share/classes/module-info.java 2018-10-22 10:22:20.440197600 -0400 +++ new/src/jdk.jlink/share/classes/module-info.java 2018-10-22 10:22:18.879717600 -0400 @@ -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,7 @@ jdk.tools.jlink.internal.plugins.IncludeLocalesPlugin, jdk.tools.jlink.internal.plugins.GenerateJLIClassesPlugin, jdk.tools.jlink.internal.plugins.ReleaseInfoPlugin; + + exports jdk.tools.jlink.internal.packager to + jdk.packager; } --- /dev/null 2018-10-22 10:22:28.000000000 -0400 +++ new/make/launcher/Launcher-jdk.packager.gmk 2018-10-22 10:22:26.699723200 -0400 @@ -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. Oracle designates this +# particular file as subject to the "Classpath" exception as provided +# by Oracle in the LICENSE file that accompanied this code. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please 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) + +JPACKAGEREXE_SRC := $(TOPDIR)/src/jdk.packager/windows/native/jpackager + +$(eval $(call SetupJdkExecutable, BUILD_JPACKAGEREXE, \ + NAME := jpackager, \ + SRC := $(JPACKAGEREXE_SRC), \ + TOOLCHAIN := TOOLCHAIN_LINK_CXX, \ + OPTIMIZATION := LOW, \ + CFLAGS := $(CXXFLAGS_JDKEXE) -nologo -DFULL -EHsc \ + -DWIN32 -D_LITTLE_ENDIAN -DWIN32_LEAN_AND_MEAN \ + -DUNICODE -D_UNICODE, \ + CFLAGS_release := -DPRODUCT, \ + DISABLED_WARNINGS_gcc := unused-result implicit-fallthrough, \ + LDFLAGS := $(LDFLAGS_JDKEXE) $(LDFLAGS_CXX_JDK) -nologo \ + $(call SET_SHARED_LIBRARY_ORIGIN), \ + LIBS := $(LIBCXX) user32.lib shell32.lib advapi32.lib ole32.lib, \ + VERSIONINFO_RESOURCE := $(GLOBAL_VERSION_INFO_RESOURCE), \ +)) + +TARGETS += $(BUILD_JPACKAGEREXE) + +else + + # Chmod to avoid permission issues for exploded JDK +define copy-and-chmod + $(install-file) + $(CHMOD) +x $@ +endef + +#copy script for Unix +$(eval $(call SetupCopyFiles, COPY_JPACKAGERSCRIPT, \ + SRC := $(TOPDIR)/src/jdk.packager/unix/scripts/, \ + DEST := $(SUPPORT_OUTPUTDIR)/modules_cmds/$(MODULE), \ + FILES := jpackager, \ + MACRO := copy-and-chmod, \ +)) + +TARGETS += $(COPY_JPACKAGERSCRIPT) + +endif + +################################################################################ --- /dev/null 2018-10-22 10:22:34.000000000 -0400 +++ new/make/lib/Lib-jdk.packager.gmk 2018-10-22 10:22:32.583733200 -0400 @@ -0,0 +1,154 @@ +# +# 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 + +################################################################################ + +LIBPACKAGER_SRC_ROOT := $(TOPDIR)/src/jdk.packager +LIBPACKAGER_SHARED_SRC := $(LIBPACKAGER_SRC_ROOT)/share/native/library/common +LIBPACKAGER_PLATFORM_SRC := $(LIBPACKAGER_SRC_ROOT)/$(OPENJDK_TARGET_OS)/native/library + +BUILD_LIBPACKAGER_SRC := + +#FIXME: make separate directories for each platform +ifeq ($(OPENJDK_TARGET_OS), macosx) + BUILD_LIBPACKAGER_SRC := $(LIBPACKAGER_PLATFORM_SRC) +endif +BUILD_LIBPACKAGER_SRC += $(LIBPACKAGER_SHARED_SRC) + + +# Output shared library and debug symbols files in the same directory as .obj files. +$(eval $(call SetupJdkLibrary, BUILD_LIBPACKAGER, \ + NAME := packager, \ + SRC := $(BUILD_LIBPACKAGER_SRC), \ + OUTPUT_DIR := $(SUPPORT_OUTPUTDIR)/native/$(MODULE)/libpackager, \ + TOOLCHAIN := TOOLCHAIN_LINK_CXX, \ + OPTIMIZATION := LOW, \ + CFLAGS := $(CXXFLAGS_JDKLIB) -I$(LIBPACKAGER_SHARED_SRC), \ + CFLAGS_windows := -nologo -EHsc -D_WINDOWS -DUNICODE -D_UNICODE -DWIN32 -D_LITTLE_ENDIAN -DWIN32_LEAN_AND_MEAN, \ + 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, \ + CFLAGS_linux := -Wextra -Wformat -Wformat-security -c -fPIC, \ + LDFLAGS_macosx := -dynamiclib -stdlib=libc++, \ + LIBS_macosx := -ldl -framework Cocoa, \ + VERSIONINFO_RESOURCE := $(GLOBAL_VERSION_INFO_RESOURCE), \ +)) + +$(BUILD_LIBPACKAGER): $(call FindLib, java.base, java) + +TARGETS += $(BUILD_LIBPACKAGER) + +################################################################################ + +PACKAGERAPPLAUNCHEREXE_SRC := $(TOPDIR)/src/jdk.packager/$(OPENJDK_TARGET_OS)/native/launcher + + +# Output executable and debug symbols files in the same directory as .obj files. +$(eval $(call SetupJdkExecutable, BUILD_PACKAGERAPPLAUNCHEREXE, \ + NAME := papplauncher, \ + SRC := $(PACKAGERAPPLAUNCHEREXE_SRC), \ + OUTPUT_DIR := $(SUPPORT_OUTPUTDIR)/native/$(MODULE)/papplauncher, \ + TOOLCHAIN := TOOLCHAIN_LINK_CXX, \ + OPTIMIZATION := LOW, \ + CFLAGS := $(CXXFLAGS_JDKEXE) -DFULL, \ + CFLAGS_release := -DPRODUCT, \ + CFLAGS_linux := -fPIC, \ + CFLAGS_solaris := -KPIC, \ + CFLAGS_macosx := -fPIC, \ + CFLAGS_windows := -nologo -EHsc -D_WINDOWS -DUNICODE -D_UNICODE -DWIN32 -D_LITTLE_ENDIAN -DWIN32_LEAN_AND_MEAN, \ + DISABLED_WARNINGS_gcc := unused-result implicit-fallthrough, \ + LDFLAGS := $(LDFLAGS_JDKEXE) $(LDFLAGS_CXX_JDK) \ + $(call SET_SHARED_LIBRARY_ORIGIN), \ + LDFLAGS_macosx := -stdlib=libstdc++, \ + 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_PACKAGERAPPLAUNCHEREXE) + +# Build console version of launcher +ifeq ($(OPENJDK_TARGET_OS), windows) + +PACKAGERAPPLAUNCHERCEXE_SRC := $(TOPDIR)/src/jdk.packager/$(OPENJDK_TARGET_OS)/native/launcher + +# Output executable and debug symbols files in the same directory as .obj files. +$(eval $(call SetupJdkExecutable, BUILD_PACKAGERAPPLAUNCHERCEXE, \ + NAME := papplauncherc, \ + SRC := $(PACKAGERAPPLAUNCHERCEXE_SRC), \ + OUTPUT_DIR := $(SUPPORT_OUTPUTDIR)/native/$(MODULE)/papplauncherc, \ + TOOLCHAIN := TOOLCHAIN_LINK_CXX, \ + OPTIMIZATION := LOW, \ + CFLAGS := $(CXXFLAGS_JDKEXE) -DFULL, \ + CFLAGS_release := -DPRODUCT, \ + CFLAGS_windows := -nologo -EHsc -DLAUNCHERC -D_WINDOWS -DUNICODE -D_UNICODE -DWIN32 -D_LITTLE_ENDIAN -DWIN32_LEAN_AND_MEAN, \ + LDFLAGS := $(LDFLAGS_JDKEXE) $(LDFLAGS_CXX_JDK) \ + $(call SET_SHARED_LIBRARY_ORIGIN), \ + LIBS := $(LIBCXX), \ + LIBS_windows := user32.lib shell32.lib advapi32.lib, \ + VERSIONINFO_RESOURCE := $(GLOBAL_VERSION_INFO_RESOURCE), \ +)) + +TARGETS += $(BUILD_PACKAGERAPPLAUNCHERCEXE) +endif + +################################################################################ + + +# Copy debug symbols to module lib output directory so that JDK build system put them in jdk/bin directory. +# Copy binaries to module classes output directory so that JDK build system put them in module resources. + +ifeq ($(OPENJDK_TARGET_OS), macosx) + RESOURCE_SUBDIR := mac +else + RESOURCE_SUBDIR := $(OPENJDK_TARGET_OS) +endif + +SetupCopyTargetFiles = \ + $(eval $(call SetupCopyFiles, COPY_DEBUG_SYMBOLS_$1, \ + SRC := $(dir $(firstword $($1))), \ + DEST := $(INSTALL_LIBRARIES_HERE), \ + FILES := $(notdir $(filter %.diz %.pdb %.map, $($1))) \ + )) \ + $(eval $(call SetupCopyFiles, COPY_BINARIES_$1, \ + SRC := $(dir $(firstword $($1))), \ + DEST := $(JDK_OUTPUTDIR)/modules/$(MODULE)/jdk/packager/internal/resources/$(RESOURCE_SUBDIR), \ + FILES := $(notdir $(firstword $($1))) \ + )) \ + $(eval TARGETS += $(COPY_DEBUG_SYMBOLS_$1) $(COPY_BINARIES_$1)) + +$(call SetupCopyTargetFiles,BUILD_LIBPACKAGER) +$(call SetupCopyTargetFiles,BUILD_PACKAGERAPPLAUNCHEREXE) + +ifeq ($(OPENJDK_TARGET_OS), windows) +$(call SetupCopyTargetFiles,BUILD_PACKAGERAPPLAUNCHERCEXE) +endif --- /dev/null 2018-10-22 10:22:41.000000000 -0400 +++ new/src/demo/share/jpackager/JNLPConverter/README.txt 2018-10-22 10:22:38.858863600 -0400 @@ -0,0 +1,23 @@ +About JNLPConverter +=================== + +JNLPConverter is a standalone tool which uses jpackager to create bundles from +Java Web Start(TM) Applications and helps to migrate from JNLP to jpackager. +JNLPConverter will locate and use the jpackager 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. --- /dev/null 2018-10-22 10:22:47.000000000 -0400 +++ new/src/demo/share/jpackager/JNLPConverter/src/jnlp/converter/HTTPHelper.java 2018-10-22 10:22:44.993550800 -0400 @@ -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."); + } + } +} --- /dev/null 2018-10-22 10:22:53.000000000 -0400 +++ new/src/demo/share/jpackager/JNLPConverter/src/jnlp/converter/HTTPHelperException.java 2018-10-22 10:22:51.084424800 -0400 @@ -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; + } +} --- /dev/null 2018-10-22 10:23:00.000000000 -0400 +++ new/src/demo/share/jpackager/JNLPConverter/src/jnlp/converter/JNLPConverter.java 2018-10-22 10:22:57.595628000 -0400 @@ -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 jpackagerPath = 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(); + runJPackager(); + } 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 jpackager option --runtime-image (using --jpackager-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 --jpackager-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 --jpackager-options"); + } + } + } + + private void displayLaunchArgs() { + if (Log.isVerbose()) { + System.out.println(); + System.out.println("jpackager 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 jpackager 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 --jpackager-options + List jpackagerOptions = options.getJPackagerOptions(); + jpackagerOptions.forEach((option) -> { + launchArgs.add(option); + }); + + displayLaunchArgs(); + } + + private String getCommandFileName() { + Platform platform = Platform.getPlatform(); + switch (platform) { + case WINDOWS: + return "run_jpackager.bat"; + case LINUX: + return "run_jpackager.sh"; + case MAC: + return "run_jpackager.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(getJPackagerPath()); + 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 runJPackager() { + List command = new ArrayList<>(); + command.add(getJPackagerPath()); + 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("jpackager 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. + // jpackager 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 + // jpackager will need: + // "-Djnlp.Prop1=\"Some \\"Value\\" 1\" -Djnlp.Prop2=\"Some Value 2\"" + // but since we using ProcessBuilder to run jpackager 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 getJPackagerPath() { + if (jpackagerPath == null) { + jpackagerPath = System.getProperty("java.home"); + jpackagerPath += File.separator; + jpackagerPath += "bin"; + jpackagerPath += File.separator; + + Platform platform = Platform.getPlatform(); + switch (platform) { + case WINDOWS: + jpackagerPath += "jpackager.exe"; + break; + case LINUX: + jpackagerPath += "jpackager"; + break; + case MAC: + jpackagerPath += "jpackager"; + break; + default: + Log.error("Cannot determine platform type."); + break; + } + + Log.verbose("jpackager: " + jpackagerPath); + } + + return jpackagerPath; + } + + public static String getIconFormat(String icon) { + // GIF, JPEG, ICO, or PNG + if (icon.toLowerCase().endsWith(".gif")) { + return "GIF"; + } else if (icon.toLowerCase().endsWith(".jpg")) { + return "JPEG"; + } else if (icon.toLowerCase().endsWith(".ico")) { + return "ICO"; + } else if (icon.toLowerCase().endsWith(".png")) { + return "PNG"; + } + + return "UNKNOWN"; + } + + public static boolean isIconSupported(String icon) { + Platform platform = Platform.getPlatform(); + switch (platform) { + case WINDOWS: + if (icon.endsWith(".ico")) { + return true; + } else { + Log.warning("Icon file format (" + getIconFormat(icon) + ") is not supported on Windows for file " + icon + "."); + return false; + } + case LINUX: + if (icon.endsWith(".png")) { + return true; + } else { + Log.warning("Icon file format (" + getIconFormat(icon) + ") is not supported on Linux for file " + icon + "."); + return false; + } + case MAC: + Log.warning("Icon file format (" + getIconFormat(icon) + ") is not supported on OS X for file " + icon + "."); + return false; + } + + return false; + } +} --- /dev/null 2018-10-22 10:23:08.000000000 -0400 +++ new/src/demo/share/jpackager/JNLPConverter/src/jnlp/converter/Log.java 2018-10-22 10:23:04.941888800 -0400 @@ -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); + } +} --- /dev/null 2018-10-22 10:23:14.000000000 -0400 +++ new/src/demo/share/jpackager/JNLPConverter/src/jnlp/converter/Main.java 2018-10-22 10:23:12.820516400 -0400 @@ -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 jpackagerPath = JNLPConverter.getJPackagerPath(); + File file = new File(jpackagerPath); + if (!file.exists()) { + Log.error("Cannot find " + jpackagerPath + ". Make sure you running JNLPConverter with supported JDK version."); + } + } + + public static void main(String[] args) { + Options options = Options.parseArgs(args); // Only valid options will be returned + + Log.setVerbose(options.verbose()); + + validateJDK(); + + if (options.help()) { + showHelp(); + } else if (options.version()) { + showVersion(); + } else { + createBundle(options); + } + + System.exit(0); + } +} --- /dev/null 2018-10-22 10:23:21.000000000 -0400 +++ new/src/demo/share/jpackager/JNLPConverter/src/jnlp/converter/Options.java 2018-10-22 10:23:19.656419200 -0400 @@ -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 jpackagerOptions = 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_JPACKAGER_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 getJPackagerOptions() { + return jpackagerOptions; + } + + 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 < jpackagerOptions.size(); i++) { + System.out.println("jpackagerOptions[" + i + "]: " + jpackagerOptions.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); + } + } + + jpackagerOptions.forEach((option) -> { + if (isBlockedOption(option)) { + Log.error(option + " is not allowed via --jpackager-options, since it will conflict with " + + "same option generated by JNLPConverter."); + } + }); + } + + public boolean isOptionPresent(String option) { + for (String jpackagerOption : jpackagerOptions) { + if (jpackagerOption.equalsIgnoreCase(option)) { + return true; + } + } + + return false; + } + + private boolean isBlockedOption(String option) { + for (String blockedOption : BLOCKED_JPACKAGER_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 jpackager"); + System.out.println(" in directory provided."); + System.out.println(" --jpackager-options "); + System.out.println(" Specify additional jpackager options or overwrite provided by JNLPConverter."); + System.out.println(" All jpackager 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("--jpackager-options")) { + for (i = (i + 1); i < args.length; i++) { + if (!options.isRuntimeImageSet) { + if (args[i].equals(RUNTIME_IMAGE_OPTION)) { + options.isRuntimeImageSet = true; + } + } + options.jpackagerOptions.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); + } +} --- /dev/null 2018-10-22 10:23:29.000000000 -0400 +++ new/src/demo/share/jpackager/JNLPConverter/src/jnlp/converter/Platform.java 2018-10-22 10:23:27.354989200 -0400 @@ -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; + } +} --- /dev/null 2018-10-22 10:23:37.000000000 -0400 +++ new/src/demo/share/jpackager/JNLPConverter/src/jnlp/converter/parser/GeneralUtil.java 2018-10-22 10:23:34.744062800 -0400 @@ -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; + } +} --- /dev/null 2018-10-22 10:23:44.000000000 -0400 +++ new/src/demo/share/jpackager/JNLPConverter/src/jnlp/converter/parser/JNLPDesc.java 2018-10-22 10:23:41.619178400 -0400 @@ -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; + } + } +} --- /dev/null 2018-10-22 10:23:50.000000000 -0400 +++ new/src/demo/share/jpackager/JNLPConverter/src/jnlp/converter/parser/ResourceType.java 2018-10-22 10:23:48.030151600 -0400 @@ -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; +} --- /dev/null 2018-10-22 10:23:58.000000000 -0400 +++ new/src/demo/share/jpackager/JNLPConverter/src/jnlp/converter/parser/ResourceVisitor.java 2018-10-22 10:23:55.952599600 -0400 @@ -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) { + } +} --- /dev/null 2018-10-22 10:24:05.000000000 -0400 +++ new/src/demo/share/jpackager/JNLPConverter/src/jnlp/converter/parser/ResourcesDesc.java 2018-10-22 10:24:03.104600000 -0400 @@ -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); + } + } +} --- /dev/null 2018-10-22 10:24:11.000000000 -0400 +++ new/src/demo/share/jpackager/JNLPConverter/src/jnlp/converter/parser/VersionID.java 2018-10-22 10:24:09.567988400 -0400 @@ -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(); + } +} --- /dev/null 2018-10-22 10:24:18.000000000 -0400 +++ new/src/demo/share/jpackager/JNLPConverter/src/jnlp/converter/parser/VersionString.java 2018-10-22 10:24:15.968957600 -0400 @@ -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; + } +} --- /dev/null 2018-10-22 10:24:25.000000000 -0400 +++ new/src/demo/share/jpackager/JNLPConverter/src/jnlp/converter/parser/XMLFormat.java 2018-10-22 10:24:23.101351600 -0400 @@ -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); + } +} --- /dev/null 2018-10-22 10:24:32.000000000 -0400 +++ new/src/demo/share/jpackager/JNLPConverter/src/jnlp/converter/parser/XMLUtils.java 2018-10-22 10:24:29.875835600 -0400 @@ -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(); + } + } +} --- /dev/null 2018-10-22 10:24:38.000000000 -0400 +++ new/src/demo/share/jpackager/JNLPConverter/src/jnlp/converter/parser/exception/BadFieldException.java 2018-10-22 10:24:36.713739200 -0400 @@ -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() + "]"; + } +} --- /dev/null 2018-10-22 10:24:45.000000000 -0400 +++ new/src/demo/share/jpackager/JNLPConverter/src/jnlp/converter/parser/exception/JNLParseException.java 2018-10-22 10:24:43.239546800 -0400 @@ -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(); + } +} --- /dev/null 2018-10-22 10:24:51.000000000 -0400 +++ new/src/demo/share/jpackager/JNLPConverter/src/jnlp/converter/parser/exception/MissingFieldException.java 2018-10-22 10:24:49.362841500 -0400 @@ -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() + "]"; + } +} --- /dev/null 2018-10-22 10:24:58.000000000 -0400 +++ new/src/demo/share/jpackager/JNLPConverter/src/jnlp/converter/parser/xml/XMLAttribute.java 2018-10-22 10:24:55.888649100 -0400 @@ -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; + } +} --- /dev/null 2018-10-22 10:25:04.000000000 -0400 +++ new/src/demo/share/jpackager/JNLPConverter/src/jnlp/converter/parser/xml/XMLEncoding.java 2018-10-22 10:25:02.837787100 -0400 @@ -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"; +} --- /dev/null 2018-10-22 10:25:11.000000000 -0400 +++ new/src/demo/share/jpackager/JNLPConverter/src/jnlp/converter/parser/xml/XMLNode.java 2018-10-22 10:25:09.127521900 -0400 @@ -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(); + } +} --- /dev/null 2018-10-22 10:25:17.000000000 -0400 +++ new/src/demo/share/jpackager/JNLPConverter/src/jnlp/converter/parser/xml/XMLParser.java 2018-10-22 10:25:15.623120300 -0400 @@ -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 + } + } +} --- /dev/null 2018-10-22 10:25:24.000000000 -0400 +++ new/src/demo/share/nbproject/jpackager/JNLPConverter/build.xml 2018-10-22 10:25:22.055299100 -0400 @@ -0,0 +1,80 @@ + + + + + + + + + + + Builds, tests, and runs the project JNLPConverter. + + + + + + + + + + --- /dev/null 2018-10-22 10:25:34.000000000 -0400 +++ new/src/demo/share/nbproject/jpackager/JNLPConverter/manifest.mf 2018-10-22 10:25:31.850914700 -0400 @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +X-COMMENT: Main-Class will be added automatically by build + --- /dev/null 2018-10-22 10:25:41.000000000 -0400 +++ new/src/demo/share/nbproject/jpackager/JNLPConverter/nbproject/build-impl.xml 2018-10-22 10:25:38.841865100 -0400 @@ -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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + --- /dev/null 2018-10-22 10:25:47.000000000 -0400 +++ new/src/demo/share/nbproject/jpackager/JNLPConverter/nbproject/genfiles.properties 2018-10-22 10:25:45.621351100 -0400 @@ -0,0 +1,8 @@ +build.xml.data.CRC32=9017e41e +build.xml.script.CRC32=5cf818d6 +build.xml.stylesheet.CRC32=8064a381@1.80.1.48 +# This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml. +# Do not edit this file. You may delete it but then the IDE will never regenerate such files for you. +nbproject/build-impl.xml.data.CRC32=9017e41e +nbproject/build-impl.xml.script.CRC32=d0290d6d +nbproject/build-impl.xml.stylesheet.CRC32=830a3534@1.80.1.48 --- /dev/null 2018-10-22 10:26:01.000000000 -0400 +++ new/src/demo/share/nbproject/jpackager/JNLPConverter/nbproject/project.properties 2018-10-22 10:25:57.628050700 -0400 @@ -0,0 +1,96 @@ +annotation.processing.enabled=true +annotation.processing.enabled.in.editor=false +annotation.processing.processors.list= +annotation.processing.run.all.processors=true +annotation.processing.source.output=${build.generated.sources.dir}/ap-source-output +application.title=JNLPConverter +application.vendor= +auxiliary.org-netbeans-spi-editor-hints-projects.perProjectHintSettingsFile=nbproject/cfg_hints.xml +build.classes.dir=${build.dir}/classes +build.classes.excludes=**/*.java,**/*.form +# This directory is removed when the project is cleaned: +build.dir=build +build.generated.dir=${build.dir}/generated +build.generated.sources.dir=${build.dir}/generated-sources +# Only compile against the classpath explicitly listed here: +build.sysclasspath=ignore +build.test.classes.dir=${build.dir}/test/classes +build.test.results.dir=${build.dir}/test/results +# Uncomment to specify the preferred debugger connection transport: +#debug.transport=dt_socket +debug.classpath=\ + ${run.classpath} +debug.test.classpath=\ + ${run.test.classpath} +# Files in build.classes.dir which should be excluded from distribution jar +dist.archive.excludes= +# This directory is removed when the project is cleaned: +dist.dir=dist +dist.jar=${dist.dir}/JNLPConverter.jar +dist.javadoc.dir=${dist.dir}/javadoc +endorsed.classpath= +excludes= +file.reference.jnlpconverter-src=../../../jpackager/jnlpconverter/src +includes=** +jar.archive.disabled=${jnlp.enabled} +jar.compress=false +jar.index=${jnlp.enabled} +javac.classpath= +# Space-separated list of extra javac options +javac.compilerargs= +javac.deprecation=false +javac.external.vm=true +javac.processorpath=\ + ${javac.classpath} +javac.source=1.8 +javac.target=1.8 +javac.test.classpath=\ + ${javac.classpath}:\ + ${build.classes.dir} +javac.test.processorpath=\ + ${javac.test.classpath} +javadoc.additionalparam= +javadoc.author=false +javadoc.encoding=${source.encoding} +javadoc.noindex=false +javadoc.nonavbar=false +javadoc.notree=false +javadoc.private=false +javadoc.splitindex=true +javadoc.use=true +javadoc.version=false +javadoc.windowtitle= +jnlp.codebase.type=no.codebase +jnlp.descriptor=application +jnlp.enabled=false +jnlp.mixed.code=default +jnlp.offline-allowed=false +jnlp.signed=false +jnlp.signing= +jnlp.signing.alias= +jnlp.signing.keystore= +main.class=jnlp.converter.Main +# Optional override of default Application-Library-Allowable-Codebase attribute identifying the locations where your signed RIA is expected to be found. +manifest.custom.application.library.allowable.codebase= +# Optional override of default Caller-Allowable-Codebase attribute identifying the domains from which JavaScript code can make calls to your RIA without security prompts. +manifest.custom.caller.allowable.codebase= +# Optional override of default Codebase manifest attribute, use to prevent RIAs from being repurposed +manifest.custom.codebase= +# Optional override of default Permissions manifest attribute (supported values: sandbox, all-permissions) +manifest.custom.permissions= +manifest.file=manifest.mf +meta.inf.dir=${src.dir}/META-INF +mkdist.disabled=false +platform.active=default_platform +run.classpath=\ + ${javac.classpath}:\ + ${build.classes.dir} +# Space-separated list of JVM arguments used when running the project. +# You may also define separate properties like run-sys-prop.name=value instead of -Dname=value. +# To set system properties for unit tests define test-sys-prop.name=value: +run.jvmargs= +run.test.classpath=\ + ${javac.test.classpath}:\ + ${build.test.classes.dir} +source.encoding=UTF-8 +src.src.dir=${file.reference.jnlpconverter-src} --- /dev/null 2018-10-22 10:26:11.000000000 -0400 +++ new/src/demo/share/nbproject/jpackager/JNLPConverter/nbproject/project.xml 2018-10-22 10:26:07.629001500 -0400 @@ -0,0 +1,13 @@ + + + org.netbeans.modules.java.j2seproject + + + JNLPConverter + + + + + + + --- /dev/null 2018-10-22 10:26:20.000000000 -0400 +++ new/src/jdk.packager.services/share/classes/jdk/packager/services/package.html 2018-10-22 10:26:18.270034500 -0400 @@ -0,0 +1,41 @@ + + + + + + + + jdk.packager.services + + +

Defines the services used by the jpackager tool. This package is +not typically available in the Java Runtime, you must explicitly include +the 'jdk.packager.services' module from the jmod directory of +the JDK as part of your application bundle.

+ + --- /dev/null 2018-10-22 10:26:28.000000000 -0400 +++ new/src/jdk.packager.services/share/classes/jdk/packager/services/singleton/SingleInstanceImpl.java 2018-10-22 10:26:26.012929500 -0400 @@ -0,0 +1,455 @@ +/* + * 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.packager.services.singleton; + +import java.awt.Desktop; +import java.awt.desktop.OpenFilesHandler; +import java.awt.desktop.OpenFilesEvent; +import java.net.ServerSocket; +import java.net.InetAddress; +import java.net.Socket; +import java.io.File; +import java.io.PrintStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.List; +import java.security.PrivilegedAction; +import java.security.AccessController; +import java.security.SecureRandom; + + +class SingleInstanceImpl { + + static final String SI_FILEDIR = getTmpDir() + File.separator + + "si" + File.separator; + static final String SI_MAGICWORD = "jpackager.singleinstance.init"; + static final String SI_ACK = "jpackager.singleinstance.ack"; + static final String SI_STOP = "jpackager.singleinstance.stop"; + static final String SI_EOF = "jpackager.singleinstance.EOF"; + + private final ArrayList siListeners = + new ArrayList<>(); + private SingleInstanceServer siServer; + + private static final SecureRandom random = new SecureRandom(); + private static volatile boolean serverStarted = false; + private static int randomNumber; + + private final Object lock = new Object(); + + static String getSingleInstanceFilePrefix(final String stringId) { + String filePrefix = stringId.replace('/','_'); + filePrefix = filePrefix.replace(':','_'); + return filePrefix; + } + + static String getTmpDir() { + String os = System.getProperty("os.name").toLowerCase(); + if (os.contains("win")) { + return System.getProperty("user.home") + + "\\AppData\\LocalLow\\Sun\\Java\\Packager\\tmp"; + } else if (os.contains("mac") || os.contains("os x")) { + return System.getProperty("user.home") + + "/Library/Application Support/Oracle/Java/Packager/tmp"; + } else if (os.contains("nix") || os.contains("nux") + || os.contains("aix")) { + return System.getProperty("user.home") + "/.java/packager/tmp"; + } + + return System.getProperty("java.io.tmpdir"); + } + + void addSingleInstanceListener(SingleInstanceListener sil, String id) { + + if (sil == null || id == null) { + return; + } + + // start a new server thread for this unique id + // first time + synchronized (lock) { + if (!serverStarted) { + SingleInstanceService.trace("unique id: " + id); + try { + siServer = new SingleInstanceServer(id); + siServer.start(); + } catch (Exception e) { + SingleInstanceService.trace( + "addSingleInstanceListener failed"); + SingleInstanceService.trace(e); + return; // didn't start + } + serverStarted = true; + } + } + + synchronized (siListeners) { + // add the sil to the arrayList + if (!siListeners.contains(sil)) { + siListeners.add(sil); + } + } + } + + class SingleInstanceServer { + + private final SingleInstanceServerRunnable runnable; + private final Thread thread; + + SingleInstanceServer(SingleInstanceServerRunnable runnable) + throws IOException { + thread = new Thread(null, runnable, "JavaPackagerSIThread", + 0, false); + thread.setDaemon(true); + this.runnable = runnable; + } + + SingleInstanceServer(String stringId) throws IOException { + this(new SingleInstanceServerRunnable(stringId)); + } + + int getPort() { + return runnable.getPort(); + } + + void start() { + thread.start(); + } + } + + private class SingleInstanceServerRunnable implements Runnable { + + ServerSocket ss; + int port; + String stringId; + String[] arguments; + + int getPort() { + return port; + } + + SingleInstanceServerRunnable(String id) throws IOException { + stringId = id; + + // open a free ServerSocket + ss = null; + + // we should bind the server to the local InetAddress 127.0.0.1 + // port number is automatically allocated for current SI + ss = new ServerSocket(0, 0, InetAddress.getByName("127.0.0.1")); + + // get the port number + port = ss.getLocalPort(); + SingleInstanceService.trace("server port at: " + port); + + // create the single instance file with canonical home and port num + createSingleInstanceFile(stringId, port); + } + + private String getSingleInstanceFilename(final String id, + final int port) { + String name = SI_FILEDIR + getSingleInstanceFilePrefix(id) + + "_" + port; + SingleInstanceService.trace("getSingleInstanceFilename: " + name); + return name; + } + + private void removeSingleInstanceFile(final String id, final int port) { + new File(getSingleInstanceFilename(id, port)).delete(); + SingleInstanceService.trace("removed SingleInstanceFile: " + + getSingleInstanceFilename(id, port)); + } + + private void createSingleInstanceFile(final String id, final int port) { + String filename = getSingleInstanceFilename(id, port); + final File siFile = new File(filename); + final File siDir = new File(SI_FILEDIR); + AccessController.doPrivileged(new PrivilegedAction() { + @Override + public Void run() { + siDir.mkdirs(); + String[] fList = siDir.list(); + if (fList != null) { + String prefix = getSingleInstanceFilePrefix(id); + for (String file : fList) { + // if file with the same prefix exist, remove it + if (file.startsWith(prefix)) { + SingleInstanceService.trace( + "file should be removed: " + + SI_FILEDIR + file); + new File(SI_FILEDIR + file).delete(); + } + } + } + + PrintStream out = null; + try { + siFile.createNewFile(); + siFile.deleteOnExit(); + // write random number to single instance file + out = new PrintStream(new FileOutputStream(siFile)); + randomNumber = random.nextInt(); + out.print(randomNumber); + } catch (IOException ioe) { + SingleInstanceService.trace(ioe); + } finally { + if (out != null) { + out.close(); + } + } + return null; + } + }); + } + + @Override + public void run() { + // start sil to handle all the incoming request + // from the server port of the current url + AccessController.doPrivileged(new PrivilegedAction() { + @Override + public Void run() { + List recvArgs = new ArrayList<>(); + while (true) { + recvArgs.clear(); + InputStream is = null; + BufferedReader in = null; + InputStreamReader isr = null; + Socket s = null; + String line = null; + boolean sendAck = false; + int port = -1; + String charset = null; + try { + SingleInstanceService.trace("waiting connection"); + s = ss.accept(); + is = s.getInputStream(); + // read first byte for encoding type + int encoding = is.read(); + if (encoding == + SingleInstanceService.ENCODING_PLATFORM) { + charset = Charset.defaultCharset().name(); + } else if (encoding == + SingleInstanceService.ENCODING_UNICODE) { + charset = + SingleInstanceService.ENCODING_UNICODE_NAME; + } else { + SingleInstanceService.trace( + "SingleInstanceImpl - unknown encoding"); + return null; + } + isr = new InputStreamReader(is, charset); + in = new BufferedReader(isr); + // first read the random number + line = in.readLine(); + if (line.equals(String.valueOf(randomNumber)) == + false) { + // random number does not match + // should not happen + // shutdown server socket + removeSingleInstanceFile(stringId, port); + ss.close(); + serverStarted = false; + SingleInstanceService.trace("Unexpected Error, " + + "SingleInstanceService disabled"); + return null; + } else { + line = in.readLine(); + // no need to continue reading if MAGICWORD + // did not come first + SingleInstanceService.trace("recv: " + line); + if (line.equals(SI_MAGICWORD)) { + SingleInstanceService.trace( + "got magic word."); + while (true) { + // Get input string + try { + line = in.readLine(); + if (line != null + && line.equals(SI_EOF)) { + // end of file reached + break; + } else { + recvArgs.add(line); + } + } catch (IOException ioe) { + SingleInstanceService.trace(ioe); + } + } + arguments = recvArgs.toArray( + new String[recvArgs.size()]); + sendAck = true; + } else if (line.equals(SI_STOP)) { + // remove the SingleInstance file + removeSingleInstanceFile(stringId, port); + break; + } + } + } catch (IOException ioe) { + SingleInstanceService.trace(ioe); + } finally { + try { + if (sendAck) { + // let the action listener handle the rest + for (String arg : arguments) { + SingleInstanceService.trace( + "Starting new instance with " + + "arguments: arg:" + arg); + } + + performNewActivation(arguments); + + // now the event is handled, we can send + // out the ACK + SingleInstanceService.trace( + "sending out ACK"); + if (s != null) { + try (OutputStream os = + s.getOutputStream(); + PrintStream ps = new PrintStream(os, + true, charset)) { + // send OK (ACK) + ps.println(SI_ACK); + ps.flush(); + } + } + } + + if (in != null) { + in.close(); + } + + if (isr != null) { + isr.close(); + } + + if (is != null) { + is.close(); + } + + if (s != null) { + s.close(); + } + } catch (IOException ioe) { + SingleInstanceService.trace(ioe); + } + } + } + return null; + } + }); + } + } + + private void performNewActivation(final String[] args) { + // enumerate the sil list and call + // each sil with arguments + @SuppressWarnings("unchecked") + ArrayList silal = + (ArrayList)siListeners.clone(); + silal.forEach(sil -> sil.newActivation(args)); + } + + void setOpenFileHandler() { + String os = System.getProperty("os.name").toLowerCase(); + if (!os.contains("mac") && !os.contains("os x")) { + return; + } + + Desktop.getDesktop().setOpenFileHandler(new OpenFilesHandler() { + @Override + public void openFiles(OpenFilesEvent e) { + List arguments = new ArrayList<>(); + e.getFiles().forEach(file -> arguments.add(file.toString())); + performNewActivation(arguments.toArray( + new String[arguments.size()])); + } + }); + } + + void removeSingleInstanceListener(SingleInstanceListener sil) { + if (sil == null) { + return; + } + + synchronized (siListeners) { + + if (!siListeners.remove(sil)) { + return; + } + + if (siListeners.isEmpty()) { + AccessController.doPrivileged(new PrivilegedAction() { + @Override + public Void run() { + // stop server + Socket socket = null; + PrintStream out = null; + OutputStream os = null; + try { + socket = new Socket("127.0.0.1", + siServer.getPort()); + os = socket.getOutputStream(); + byte[] encoding = new byte[1]; + encoding[0] = + SingleInstanceService.ENCODING_PLATFORM; + os.write(encoding); + String charset = Charset.defaultCharset().name(); + out = new PrintStream(os, true, charset); + out.println(randomNumber); + out.println(SingleInstanceImpl.SI_STOP); + out.flush(); + serverStarted = false; + } catch (IOException ioe) { + SingleInstanceService.trace(ioe); + } finally { + try { + if (out != null) { + out.close(); + } + if (os != null) { + os.close(); + } + if (socket != null) { + socket.close(); + } + } catch (IOException ioe) { + SingleInstanceService.trace(ioe); + } + } + return null; + } + }); + } + } + } +} --- /dev/null 2018-10-22 10:26:37.000000000 -0400 +++ new/src/jdk.packager.services/share/classes/jdk/packager/services/singleton/SingleInstanceListener.java 2018-10-22 10:26:34.798743300 -0400 @@ -0,0 +1,45 @@ +/* + * 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.packager.services.singleton; + +/** + * The {@code SingleInstanceListener} interface is used for implementing + * Single Instance functionality for Java Packager. + * + * @since 12 + */ +public interface SingleInstanceListener { + + /** + * This method should be implemented by the application to + * handle the single instance behaviour - how should the application + * handle the arguments when another instance of the application is + * invoked with params. + * + * @param params parameters for the application main + */ + public void newActivation(String... params); +} --- /dev/null 2018-10-22 10:26:43.000000000 -0400 +++ new/src/jdk.packager.services/share/classes/jdk/packager/services/singleton/SingleInstanceNewActivation.java 2018-10-22 10:26:41.544123300 -0400 @@ -0,0 +1,49 @@ +/* + * 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.packager.services.singleton; + +import java.util.Arrays; + +// This class is used for notifying Single Instance for JPackager. + +public class SingleInstanceNewActivation { + + public static void main(String[] args) { + + if (args.length < 2) { + // no user args specified + return; + } + + // the first arg is process id of the single instance + String appId = SingleInstanceService.APP_ID_PREFIX + args[0]; + + if (SingleInstanceService.isServerRunning(appId)) { + String[] newArgs = Arrays.copyOfRange(args, 1, args.length); + SingleInstanceService.connectToServer(newArgs); + } + } +} --- /dev/null 2018-10-22 10:26:50.000000000 -0400 +++ new/src/jdk.packager.services/share/classes/jdk/packager/services/singleton/SingleInstanceService.java 2018-10-22 10:26:48.036873900 -0400 @@ -0,0 +1,274 @@ +/* + * 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.packager.services.singleton; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.PrintStream; +import java.net.Socket; +import java.nio.charset.Charset; + +/** + * The {@code SingleInstanceService} class provides public methods for using + * Single Instance functionality for Java Packager. To use these methods, + * the option named "-singleton" must be specified on jpackager command line. + * + * @since 12 + */ +public class SingleInstanceService { + + static private boolean DEBUG = false; + static private PrintStream DEBUG_STREAM = null; + + static private int currPort; + static private String stringId = null; + static private String randomNumberString = null; + + static private SingleInstanceImpl instance = null; + + static final int ENCODING_PLATFORM = 1; + static final int ENCODING_UNICODE = 2; + + static final String ENCODING_PLATFORM_NAME = "UTF-8"; + static final String ENCODING_UNICODE_NAME = "UTF-16LE"; + + static final String APP_ID_PREFIX = "jpackager.si."; + + private SingleInstanceService() {} + + static void enableDebug(boolean enable, PrintStream stream) { + DEBUG = enable; + DEBUG_STREAM = stream; + } + + static void trace(String message) { + if (DEBUG && DEBUG_STREAM != null) { + DEBUG_STREAM.println(message); + } + } + + static void trace(Throwable t) { + if (DEBUG && DEBUG_STREAM != null) { + t.printStackTrace(DEBUG_STREAM); + } + } + + /** + * Registers {@code SingleInstanceListener} for current process. + * If the {@code SingleInstanceListener} object is already registered, or + * {@code slistener} is {@code null}, then the registration is skipped. + * + * @param slistener the listener to handle the single instance behaviour. + */ + public static void registerSingleInstance( + SingleInstanceListener slistener) { + registerSingleInstance(slistener, false); + } + + /** + * Registers {@code SingleInstanceListener} for current process. + * If the {@code SingleInstanceListener} object is already registered, or + * {@code slistener} is {@code null}, then the registration is skipped. + * + * @param slistener the listener to handle the single instance behaviour. + * @param setFileHandler if {@code true}, the listener is notified when the + * application is asked to open a list of files. If OS is not MacOS, + * the parameter is ignored. + */ + public static void registerSingleInstance(SingleInstanceListener slistener, + boolean setFileHandler) { + String appId = APP_ID_PREFIX + ProcessHandle.current().pid(); + registerSingleInstanceForId(slistener, appId, setFileHandler); + } + + static void registerSingleInstanceForId(SingleInstanceListener slistener, + String stringId, boolean setFileHandler) { + // register SingleInstanceListener for given Id + instance = new SingleInstanceImpl(); + instance.addSingleInstanceListener(slistener, stringId); + if (setFileHandler) { + instance.setOpenFileHandler(); + } + } + + /** + * Unregisters {@code SingleInstanceListener} for current process. + * If the {@code SingleInstanceListener} object is not registered, or + * {@code slistener} is {@code null}, then the unregistration is skipped. + * + * @param slistener the listener for unregistering. + */ + public static void unregisterSingleInstance( + SingleInstanceListener slistener) { + instance.removeSingleInstanceListener(slistener); + } + + /** + * Returns true if single instance server is running for the id + */ + static boolean isServerRunning(String id) { + trace("isServerRunning ? : "+ id); + File siDir = new File(SingleInstanceImpl.SI_FILEDIR); + String[] fList = siDir.list(); + if (fList != null) { + String prefix = SingleInstanceImpl.getSingleInstanceFilePrefix(id); + for (String file : fList) { + trace("isServerRunning: " + file); + trace("\t String id: " + id); + trace("\t SingleInstanceFilePrefix: " + prefix); + // if file with the same prefix already exist, server is running + if (file.startsWith(prefix)) { + try { + currPort = Integer.parseInt( + file.substring(file.lastIndexOf('_') + 1)); + trace("isServerRunning: " + file + + ": port: " + currPort); + } catch (NumberFormatException nfe) { + trace("isServerRunning: " + file + + ": port parsing failed"); + trace(nfe); + return false; + } + + trace("Server running at port: " + currPort); + File siFile = new File(SingleInstanceImpl.SI_FILEDIR, file); + + // get random number from single instance file + try (BufferedReader br = new BufferedReader( + new FileReader(siFile))) { + randomNumberString = br.readLine(); + trace("isServerRunning: " + file + ": magic: " + + randomNumberString); + } catch (IOException ioe ) { + trace("isServerRunning: " + file + + ": reading magic failed"); + trace(ioe); + } + trace("isServerRunning: " + file + ": setting id - OK"); + stringId = id; + return true; + } else { + trace("isServerRunning: " + file + ": prefix NOK"); + } + } + } else { + trace("isServerRunning: empty file list"); + } + trace("isServerRunning: false"); + return false; + } + + /** + * Returns true if we connect successfully to the server for the stringId + */ + static boolean connectToServer(String[] args) { + trace("Connect to: " + stringId + " " + currPort); + + if (randomNumberString == null) { + // should not happen + trace("MAGIC number is null, bail out."); + return false; + } + + // Now we open the tcpSocket and the stream + Socket socket = null; + OutputStream os = null; + PrintStream out = null; + InputStreamReader isr = null; + BufferedReader br = null; + try { + socket = new Socket("127.0.0.1", currPort); + os = socket.getOutputStream(); + byte[] encoding = new byte[1]; + encoding[0] = ENCODING_PLATFORM; + os.write(encoding); + String encodingName = Charset.defaultCharset().name(); + + out = new PrintStream(os, true, encodingName); + isr = new InputStreamReader(socket.getInputStream(), encodingName); + br = new BufferedReader(isr); + + // send random number + out.println(randomNumberString); + // send MAGICWORD + out.println(SingleInstanceImpl.SI_MAGICWORD); + + for (String arg : args) { + out.println(arg); + } + + // indicate end of file transmission + out.println(SingleInstanceImpl.SI_EOF); + out.flush(); + + // wait for ACK (OK) response + trace("Waiting for ack"); + final int tries = 5; + + // try to listen for ACK + for (int i=0; i < tries; i++) { + String str = br.readLine(); + if (str != null && str.equals(SingleInstanceImpl.SI_ACK)) { + trace("Got ACK"); + return true; + } + } + } catch (java.net.SocketException se) { + // no server is running - continue launch + trace("No server is running - continue launch."); + trace(se); + } catch (Exception ioe) { + trace(ioe); + } + finally { + try { + if (br != null) { + br.close(); + } + if (isr != null) { + isr.close(); + } + if (out != null) { + out.close(); + } + if (os != null) { + os.close(); + } + if (socket != null) { + socket.close(); + } + } catch (IOException ioe) { + trace(ioe); + } + } + trace("No ACK from server, bail out."); + return false; + } +} --- /dev/null 2018-10-22 10:27:13.000000000 -0400 +++ new/src/jdk.packager.services/share/classes/module-info.java 2018-10-22 10:26:59.632011900 -0400 @@ -0,0 +1,37 @@ +/* + * 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 services used by the jpackager tool. + * + * @moduleGraph + * @since 12 + */ +module jdk.packager.services { + exports jdk.packager.services.singleton; + + requires java.desktop; + +} --- /dev/null 2018-10-22 10:27:23.000000000 -0400 +++ new/src/jdk.packager/linux/classes/jdk/packager/internal/builders/linux/LinuxAppImageBuilder.java 2018-10-22 10:27:21.150697500 -0400 @@ -0,0 +1,262 @@ +/* + * 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.packager.internal.builders.linux; + +import jdk.packager.internal.BundlerParamInfo; +import jdk.packager.internal.IOUtils; +import jdk.packager.internal.Log; +import jdk.packager.internal.RelativeFileSet; +import jdk.packager.internal.StandardBundlerParam; +import jdk.packager.internal.resources.linux.LinuxResources; +import jdk.packager.internal.builders.AbstractAppImageBuilder; + +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.packager.internal.StandardBundlerParam.*; + +public class LinuxAppImageBuilder extends AbstractAppImageBuilder { + + private static final ResourceBundle I18N = ResourceBundle.getBundle( + "jdk.packager.internal.resources.builders.linux.LinuxAppImageBuilder"); + + protected static final String LINUX_BUNDLER_PREFIX = + BUNDLER_PREFIX + "linux" + File.separator; + private static final String EXECUTABLE_NAME = "JavaAppLauncher"; + private static final String LIBRARY_NAME = "libpackager.so"; + + private final Path root; + private final Path appDir; + 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.info(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.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.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 InputStream getResourceAsStream(String name) { + return LinuxResources.class.getResourceAsStream(name); + } + + @Override + public void prepareApplicationFiles() throws IOException { + Map originalParams = new HashMap<>(params); + + try { + // 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(); + + } catch (IOException ex) { + Log.info("Exception: " + ex); + Log.debug(ex); + } + } + + @Override + public void prepareServerJreFiles() 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("papplauncher")) { + 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); + } + } + } + +} --- /dev/null 2018-10-22 10:27:30.000000000 -0400 +++ new/src/jdk.packager/linux/classes/jdk/packager/internal/linux/LinuxAppBundler.java 2018-10-22 10:27:27.922878900 -0400 @@ -0,0 +1,284 @@ +/* + * 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.packager.internal.linux; + +import jdk.packager.internal.AbstractImageBundler; +import jdk.packager.internal.BundlerParamInfo; +import jdk.packager.internal.ConfigException; +import jdk.packager.internal.IOUtils; +import jdk.packager.internal.Log; +import jdk.packager.internal.Platform; +import jdk.packager.internal.RelativeFileSet; +import jdk.packager.internal.StandardBundlerParam; +import jdk.packager.internal.Arguments; +import jdk.packager.internal.UnsupportedPlatformException; +import jdk.packager.internal.bundlers.BundleParams; +import jdk.packager.internal.builders.linux.LinuxAppImageBuilder; +import jdk.packager.internal.resources.linux.LinuxResources; +import jdk.packager.internal.JLinkBundlerHelper; +import jdk.packager.internal.builders.AbstractAppImageBuilder; + +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.packager.internal.StandardBundlerParam.*; + +public class LinuxAppBundler extends AbstractImageBundler { + + private static final ResourceBundle I18N = ResourceBundle.getBundle( + "jdk.packager.internal.resources.linux.LinuxAppBundler"); + + protected static final String LINUX_BUNDLER_PREFIX = + BUNDLER_PREFIX + "linux" + File.separator; + private static final String EXECUTABLE_NAME = "JavaAppLauncher"; + + 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.info(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); + } + } + } + + //used by chained bundlers to reuse validation logic + 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); + AbstractAppImageBuilder appBuilder = new LinuxAppImageBuilder( + APP_NAME.fetchFrom(p), outputDirectory.toPath()); + File predefined = PREDEFINED_RUNTIME_IMAGE.fetchFrom(p); + if (predefined == null ) { + JLinkBundlerHelper.generateServerJre(p, appBuilder); + } else { + return predefined; + } + return rootDirectory; + } catch (Exception ex) { + Log.info("Exception: "+ex); + Log.debug(ex); + return null; + } + } + + private File doAppBundle(Map p, + File outputDirectory, boolean dependentTask) { + try { + File rootDirectory = createRoot(p, outputDirectory, dependentTask); + 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.info("Exception: "+ex); + Log.debug(ex); + return null; + } + } + + private File createRoot(Map p, + File outputDirectory, boolean dependentTask) 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())); + } + + // Create directory structure + File rootDirectory = getRootDir(outputDirectory, p); + IOUtils.deleteRecursive(rootDirectory); + rootDirectory.mkdirs(); + + if (!dependentTask) { + Log.info(MessageFormat.format(I18N.getString( + "message.creating-bundle-location"), + rootDirectory.getAbsolutePath())); + } + + if (!p.containsKey(JLinkBundlerHelper.JLINK_BUILDER.getID())) { + p.put(JLinkBundlerHelper.JLINK_BUILDER.getID(), + "linuxapp-image-builder"); + } + + return rootDirectory; + } + + @Override + public String getName() { + return I18N.getString("bundler.name"); + } + + @Override + public String getDescription() { + return I18N.getString("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, + JVM_PROPERTIES, + 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); + } +} --- /dev/null 2018-10-22 10:27:36.000000000 -0400 +++ new/src/jdk.packager/linux/classes/jdk/packager/internal/linux/LinuxDebBundler.java 2018-10-22 10:27:34.178003100 -0400 @@ -0,0 +1,979 @@ +/* + * 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.packager.internal.linux; + +import jdk.packager.internal.*; +import jdk.packager.internal.Arguments; +import jdk.packager.internal.bundlers.BundleParams; +import jdk.packager.internal.resources.linux.LinuxResources; + +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.packager.internal.StandardBundlerParam.*; +import static jdk.packager.internal.linux.LinuxAppBundler.ICON_PNG; +import static jdk.packager.internal.linux.LinuxAppBundler.LINUX_INSTALL_DIR; +import static + jdk.packager.internal.linux.LinuxAppBundler.LINUX_PACKAGE_DEPENDENCIES; + +public class LinuxDebBundler extends AbstractBundler { + + private static final ResourceBundle I18N = + ResourceBundle.getBundle( + "jdk.packager.internal.resources.linux.LinuxDebBundler"); + + public static final BundlerParamInfo APP_BUNDLER = + new StandardBundlerParam<>( + I18N.getString("param.app-bundler.name"), + I18N.getString("param.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 CONFIG_ROOT = + new StandardBundlerParam<>( + I18N.getString("param.config-root.name"), + I18N.getString("param.config-root.description"), + "configRoot", + File.class, + params -> new File(BUILD_ROOT.fetchFrom(params), "linux"), + (s, p) -> new File(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 { + List licenseFiles = LICENSE_FILE.fetchFrom(params); + + //need to copy license file to the root of linux-app.image + if (licenseFiles.size() > 0) { + String licFileStr = licenseFiles.get(0); + + for (RelativeFileSet rfs : + APP_RESOURCES_LIST.fetchFrom(params)) { + if (rfs.contains(licFileStr)) { + return new String(Files.readAllBytes(( + new File(rfs.getBaseDirectory(), + licFileStr)).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 = "jpackager"; + } + 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 LinuxDebBundler() { + super(); + baseResourceLoader = LinuxResources.class; + } + + 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).doValidate(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")); + } + + + // validate license file, if used, exists in the proper place + if (p.containsKey(LICENSE_FILE.getID())) { + List appResourcesList = + APP_RESOURCES_LIST.fetchFrom(p); + for (String license : LICENSE_FILE.fetchFrom(p)) { + boolean found = false; + for (RelativeFileSet appResources : appResourcesList) { + found = found || appResources.contains(license); + } + if (!found) { + throw new ConfigException( + I18N.getString("error.license-missing"), + MessageFormat.format(I18N.getString( + "error.license-missing.advice"), + license)); + } + } + } else { + Log.info(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; + } finally { + try { + if (imageDir != null && + PREDEFINED_APP_IMAGE.fetchFrom(p) == null && + (PREDEFINED_RUNTIME_IMAGE.fetchFrom(p) == null || + !Arguments.CREATE_JRE_INSTALLER.fetchFrom(p)) && + !Log.isDebug()) { + IOUtils.deleteRecursive(imageDir); + } else if (imageDir != null) { + Log.info(MessageFormat.format(I18N.getString( + "message.debug-working-directory"), + imageDir.getAbsolutePath())); + } + } catch (IOException ex) { + //noinspection ReturnInsideFinallyBlock + Log.debug(ex.getMessage()); + 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(LinuxAppBundler.LINUX_BUNDLER_PREFIX + + iconTarget.getName(), + I18N.getString("resource.menu-icon"), + DEFAULT_ICON, + iconTarget, + VERBOSE.fetchFrom(params), + DROP_IN_RESOURCES_ROOT.fetchFrom(params)); + } else { + fetchResource(LinuxAppBundler.LINUX_BUNDLER_PREFIX + + iconTarget.getName(), + I18N.getString("resource.menu-icon"), + icon, + iconTarget, + VERBOSE.fetchFrom(params), + DROP_IN_RESOURCES_ROOT.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( + LinuxAppBundler.LINUX_BUNDLER_PREFIX + + getConfig_DesktopShortcutFile(rootDir, + secondaryLauncher).getName(), + I18N.getString("resource.menu-shortcut-descriptor"), + DEFAULT_DESKTOP_FILE_TEMPLATE, + secondaryLauncherData, + VERBOSE.fetchFrom(params), + DROP_IN_RESOURCES_ROOT.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(LinuxAppBundler.LINUX_BUNDLER_PREFIX + + iconTarget.getName(), + I18N.getString("resource.menu-icon"), + DEFAULT_ICON, + iconTarget, + VERBOSE.fetchFrom(params), + DROP_IN_RESOURCES_ROOT.fetchFrom(params)); + } else { + fetchResource(LinuxAppBundler.LINUX_BUNDLER_PREFIX + + iconTarget.getName(), + I18N.getString("resource.menu-icon"), + icon, + iconTarget, + VERBOSE.fetchFrom(params), + DROP_IN_RESOURCES_ROOT.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.info(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( + LinuxAppBundler.LINUX_BUNDLER_PREFIX + + getConfig_DesktopShortcutFile( + rootDir, params).getName(), + I18N.getString("resource.menu-shortcut-descriptor"), + DEFAULT_DESKTOP_FILE_TEMPLATE, + data, + VERBOSE.fetchFrom(params), + DROP_IN_RESOURCES_ROOT.fetchFrom(params)); + w.write(content); + w.close(); + } + // prepare control file + Writer w = new BufferedWriter(new FileWriter( + getConfig_ControlFile(params))); + String content = preprocessTextResource( + LinuxAppBundler.LINUX_BUNDLER_PREFIX + + getConfig_ControlFile(params).getName(), + I18N.getString("resource.deb-control-file"), + DEFAULT_CONTROL_TEMPLATE, + data, + VERBOSE.fetchFrom(params), + DROP_IN_RESOURCES_ROOT.fetchFrom(params)); + w.write(content); + w.close(); + + w = new BufferedWriter(new FileWriter( + getConfig_PreinstallFile(params))); + content = preprocessTextResource( + LinuxAppBundler.LINUX_BUNDLER_PREFIX + + getConfig_PreinstallFile(params).getName(), + I18N.getString("resource.deb-preinstall-script"), + DEFAULT_PREINSTALL_TEMPLATE, + data, + VERBOSE.fetchFrom(params), + DROP_IN_RESOURCES_ROOT.fetchFrom(params)); + w.write(content); + w.close(); + setPermissions(getConfig_PreinstallFile(params), "rwxr-xr-x"); + + w = new BufferedWriter(new FileWriter(getConfig_PrermFile(params))); + content = preprocessTextResource( + LinuxAppBundler.LINUX_BUNDLER_PREFIX + + getConfig_PrermFile(params).getName(), + I18N.getString("resource.deb-prerm-script"), + DEFAULT_PRERM_TEMPLATE, + data, + VERBOSE.fetchFrom(params), + DROP_IN_RESOURCES_ROOT.fetchFrom(params)); + w.write(content); + w.close(); + setPermissions(getConfig_PrermFile(params), "rwxr-xr-x"); + + w = new BufferedWriter(new FileWriter( + getConfig_PostinstallFile(params))); + content = preprocessTextResource( + LinuxAppBundler.LINUX_BUNDLER_PREFIX + + getConfig_PostinstallFile(params).getName(), + I18N.getString("resource.deb-postinstall-script"), + DEFAULT_POSTINSTALL_TEMPLATE, + data, + VERBOSE.fetchFrom(params), + DROP_IN_RESOURCES_ROOT.fetchFrom(params)); + w.write(content); + w.close(); + setPermissions(getConfig_PostinstallFile(params), "rwxr-xr-x"); + + w = new BufferedWriter(new FileWriter(getConfig_PostrmFile(params))); + content = preprocessTextResource( + LinuxAppBundler.LINUX_BUNDLER_PREFIX + + getConfig_PostrmFile(params).getName(), + I18N.getString("resource.deb-postrm-script"), + DEFAULT_POSTRM_TEMPLATE, + data, + VERBOSE.fetchFrom(params), + DROP_IN_RESOURCES_ROOT.fetchFrom(params)); + w.write(content); + w.close(); + setPermissions(getConfig_PostrmFile(params), "rwxr-xr-x"); + + w = new BufferedWriter(new FileWriter(getConfig_CopyrightFile(params))); + content = preprocessTextResource( + LinuxAppBundler.LINUX_BUNDLER_PREFIX + + getConfig_CopyrightFile(params).getName(), + I18N.getString("resource.deb-copyright-file"), + DEFAULT_COPYRIGHT_TEMPLATE, + data, + VERBOSE.fetchFrom(params), + DROP_IN_RESOURCES_ROOT.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.info(MessageFormat.format(I18N.getString( + "message.output-to-location"), outFile.getAbsolutePath())); + + return outFile; + } + + @Override + public String getName() { + return I18N.getString("bundler.name"); + } + + @Override + public String getDescription() { + return I18N.getString("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; + } + } +} --- /dev/null 2018-10-22 10:27:42.000000000 -0400 +++ new/src/jdk.packager/linux/classes/jdk/packager/internal/linux/LinuxRpmBundler.java 2018-10-22 10:27:40.545537900 -0400 @@ -0,0 +1,807 @@ +/* + * 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.packager.internal.linux; + +import jdk.packager.internal.*; +import jdk.packager.internal.Arguments; +import jdk.packager.internal.resources.linux.LinuxResources; + +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.packager.internal.StandardBundlerParam.*; +import static jdk.packager.internal.linux.LinuxAppBundler.LINUX_INSTALL_DIR; +import static + jdk.packager.internal.linux.LinuxAppBundler.LINUX_PACKAGE_DEPENDENCIES; + +public class LinuxRpmBundler extends AbstractBundler { + + private static final ResourceBundle I18N = ResourceBundle.getBundle( + "jdk.packager.internal.resources.linux.LinuxRpmBundler"); + + public static final BundlerParamInfo APP_BUNDLER = + new StandardBundlerParam<>( + I18N.getString("param.app-bundler.name"), + I18N.getString("param.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)); + + public static final BundlerParamInfo CONFIG_ROOT = + new StandardBundlerParam<>( + I18N.getString("param.config-root.name"), + I18N.getString("param.config-root.description"), + "configRoot", + File.class, + params -> new File(BUILD_ROOT.fetchFrom(params), "linux"), + (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 = "jpackager"; + } + 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 LinuxRpmBundler() { + super(); + baseResourceLoader = LinuxResources.class; + } + + 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 = new Double(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).doValidate(p); + + // validate license file, if used, exists in the proper place + if (p.containsKey(LICENSE_FILE.getID())) { + List appResourcesList = + APP_RESOURCES_LIST.fetchFrom(p); + for (String license : LICENSE_FILE.fetchFrom(p)) { + boolean found = false; + for (RelativeFileSet appResources : appResourcesList) { + found = found || appResources.contains(license); + } + if (!found) { + throw new ConfigException( + I18N.getString("error.license-missing"), + MessageFormat.format( + I18N.getString("error.license-missing.advice"), + license)); + } + } + } + + // 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; + } finally { + try { + if (imageDir != null && + PREDEFINED_APP_IMAGE.fetchFrom(p) == null && + (PREDEFINED_RUNTIME_IMAGE.fetchFrom(p) == null || + !Arguments.CREATE_JRE_INSTALLER.fetchFrom(p)) && + !Log.isDebug()) { + IOUtils.deleteRecursive(imageDir); + } else if (imageDir != null) { + Log.info(MessageFormat.format(I18N.getString( + "message.debug-working-directory"), + imageDir.getAbsolutePath())); + } + } catch (IOException ex) { + // noinspection ReturnInsideFinallyBlock + Log.debug(ex.getMessage()); + 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 getLicenseFileString(Map params) { + StringBuilder sb = new StringBuilder(); + for (String f: LICENSE_FILE.fetchFrom(params)) { + if (sb.length() != 0) { + sb.append("\n"); + } + sb.append("%doc "); + sb.append(LINUX_INSTALL_DIR.fetchFrom(params)); + sb.append("/"); + sb.append(APP_FS_NAME.fetchFrom(params)); + sb.append("/app/"); + sb.append(f); + } + 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(LinuxAppBundler.LINUX_BUNDLER_PREFIX + + iconTarget.getName(), + I18N.getString("resource.menu-icon"), + DEFAULT_ICON, + iconTarget, + VERBOSE.fetchFrom(params), + DROP_IN_RESOURCES_ROOT.fetchFrom(params)); + } else { + fetchResource(LinuxAppBundler.LINUX_BUNDLER_PREFIX + + iconTarget.getName(), + I18N.getString("resource.menu-icon"), + icon, + iconTarget, + VERBOSE.fetchFrom(params), + DROP_IN_RESOURCES_ROOT.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( + LinuxAppBundler.LINUX_BUNDLER_PREFIX + + getConfig_DesktopShortcutFile(rootDir, + secondaryLauncher).getName(), + I18N.getString("resource.menu-shortcut-descriptor"), + DEFAULT_DESKTOP_FILE_TEMPLATE, secondaryLauncherData, + VERBOSE.fetchFrom(params), + DROP_IN_RESOURCES_ROOT.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(LinuxAppBundler.LINUX_BUNDLER_PREFIX + + iconTarget.getName(), + I18N.getString("resource.menu-icon"), + DEFAULT_ICON, + iconTarget, + VERBOSE.fetchFrom(params), + DROP_IN_RESOURCES_ROOT.fetchFrom(params)); + } else { + fetchResource(LinuxAppBundler.LINUX_BUNDLER_PREFIX + + iconTarget.getName(), + I18N.getString("resource.menu-icon"), + icon, + iconTarget, + VERBOSE.fetchFrom(params), + DROP_IN_RESOURCES_ROOT.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.info(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( + LinuxAppBundler.LINUX_BUNDLER_PREFIX + + getConfig_DesktopShortcutFile(rootDir, + params).getName(), + I18N.getString("resource.menu-shortcut-descriptor"), + DEFAULT_DESKTOP_FILE_TEMPLATE, data, + VERBOSE.fetchFrom(params), + DROP_IN_RESOURCES_ROOT.fetchFrom(params)); + w.write(content); + w.close(); + } + + // prepare spec file + Writer w = new BufferedWriter( + new FileWriter(getConfig_SpecFile(params))); + String content = preprocessTextResource( + LinuxAppBundler.LINUX_BUNDLER_PREFIX + + getConfig_SpecFile(params).getName(), + I18N.getString("resource.rpm-spec-file"), + DEFAULT_SPEC_TEMPLATE, data, + VERBOSE.fetchFrom(params), + DROP_IN_RESOURCES_ROOT.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_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); + + if (!Log.isDebug()) { + IOUtils.deleteRecursive(broot); + } + + Log.info(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("bundler.name"); + } + + @Override + public String getDescription() { + return I18N.getString("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; + } + } +} --- /dev/null 2018-10-22 10:27:49.000000000 -0400 +++ new/src/jdk.packager/linux/classes/jdk/packager/internal/resources/builders/linux/LinuxAppImageBuilder.properties 2018-10-22 10:27:47.146500900 -0400 @@ -0,0 +1,29 @@ +# +# 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.icon-png.name=.png Icon +param.icon-png.description=Icon for the application, in PNG format. + +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. --- /dev/null 2018-10-22 10:27:55.000000000 -0400 +++ new/src/jdk.packager/linux/classes/jdk/packager/internal/resources/builders/linux/LinuxAppImageBuilder_ja.properties 2018-10-22 10:27:53.288010300 -0400 @@ -0,0 +1,30 @@ +# +# 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.icon-png.name=.png\u30A2\u30A4\u30B3\u30F3 +param.icon-png.description=PNG\u5F62\u5F0F\u3067\u306E\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u306E\u30A2\u30A4\u30B3\u30F3\u3002 + +message.icon-not-png=\u6307\u5B9A\u3057\u305F\u30A2\u30A4\u30B3\u30F3"{0}"\u306FPNG\u30D5\u30A1\u30A4\u30EB\u3067\u306F\u306A\u304F\u3001\u4F7F\u7528\u3055\u308C\u307E\u305B\u3093\u3002\u30C7\u30D5\u30A9\u30EB\u30C8\u30FB\u30A2\u30A4\u30B3\u30F3\u304C\u305D\u306E\u4F4D\u7F6E\u306B\u4F7F\u7528\u3055\u308C\u307E\u3059\u3002 --- /dev/null 2018-10-22 10:28:01.000000000 -0400 +++ new/src/jdk.packager/linux/classes/jdk/packager/internal/resources/builders/linux/LinuxAppImageBuilder_zh_CN.properties 2018-10-22 10:27:59.509529100 -0400 @@ -0,0 +1,30 @@ +# +# 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.icon-png.name=.png \u56FE\u6807 +param.icon-png.description=\u5E94\u7528\u7A0B\u5E8F\u7684\u56FE\u6807, \u91C7\u7528 PNG \u683C\u5F0F\u3002 + +message.icon-not-png=\u6307\u5B9A\u7684\u56FE\u6807 "{0}" \u4E0D\u662F PNG \u6587\u4EF6, \u4E0D\u4F1A\u4F7F\u7528\u3002\u5C06\u4F7F\u7528\u9ED8\u8BA4\u56FE\u6807\u4EE3\u66FF\u3002 --- /dev/null 2018-10-22 10:28:08.000000000 -0400 +++ new/src/jdk.packager/linux/classes/jdk/packager/internal/resources/linux/LinuxAppBundler.properties 2018-10-22 10:28:05.857862700 -0400 @@ -0,0 +1,47 @@ +# +# 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. +# +# + +bundler.name=Linux Application Image +bundler.description=A Directory based image of a linux Application with an optionally co-bundled JRE. Used as a base for the Installer bundlers. + +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. + +error.parameters-null=Parameters map is null. +error.parameters-null.advice=Pass in a non-null parameters map. +error.no-linux-resources=Java Packager does not support Linux. +error.no-linux-resources.advice=Please use the Java Packager that ships with Oracle JDK for Linux. +error.cannot-create-output-dir=Output directory {0} cannot be created. +error.cannot-write-to-output-dir=Output directory {0} is not writable. + +message.creating-bundle-location=Creating app bundle\: {0} +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. --- /dev/null 2018-10-22 10:28:14.000000000 -0400 +++ new/src/jdk.packager/linux/classes/jdk/packager/internal/resources/linux/LinuxAppBundler_ja.properties 2018-10-22 10:28:12.396418500 -0400 @@ -0,0 +1,47 @@ +# +# 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. +# +# + +bundler.name=Linux\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u30FB\u30A4\u30E1\u30FC\u30B8 +bundler.description=\u30AA\u30D7\u30B7\u30E7\u30F3\u3067JRE\u304C\u30D0\u30F3\u30C9\u30EB\u3055\u308C\u3066\u3044\u308BLinux\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u306E\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u30FB\u30D9\u30FC\u30B9\u306E\u30A4\u30E1\u30FC\u30B8\u3002\u30A4\u30F3\u30B9\u30C8\u30FC\u30E9\u30FB\u30D0\u30F3\u30C9\u30E9\u306E\u30D9\u30FC\u30B9\u3068\u3057\u3066\u4F7F\u7528\u3055\u308C\u307E\u3059\u3002 + +param.icon-png.name=.png\u30A2\u30A4\u30B3\u30F3 +param.icon-png.description=PNG\u5F62\u5F0F\u3067\u306E\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u306E\u30A2\u30A4\u30B3\u30F3\u3002 + +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. + +error.parameters-null=\u30D1\u30E9\u30E1\u30FC\u30BF\u30FB\u30DE\u30C3\u30D7\u304Cnull\u3067\u3059\u3002 +error.parameters-null.advice=\u975Enull\u30D1\u30E9\u30E1\u30FC\u30BF\u30FB\u30DE\u30C3\u30D7\u3067\u6E21\u3057\u3066\u304F\u3060\u3055\u3044\u3002 +error.no-linux-resources=Java\u30D1\u30C3\u30B1\u30FC\u30B8\u30E3\u306FLinux\u3092\u30B5\u30DD\u30FC\u30C8\u3057\u3066\u3044\u307E\u305B\u3093\u3002 +error.no-linux-resources.advice=Oracle JDK for Linux\u306B\u4ED8\u5C5E\u3057\u3066\u3044\u308BJava\u30D1\u30C3\u30B1\u30FC\u30B8\u30E3\u3092\u4F7F\u7528\u3057\u3066\u304F\u3060\u3055\u3044\u3002 +error.cannot-create-output-dir=\u51FA\u529B\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA{0}\u3092\u4F5C\u6210\u3067\u304D\u307E\u305B\u3093\u3002 +error.cannot-write-to-output-dir=\u51FA\u529B\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA{0}\u306F\u66F8\u8FBC\u307F\u4E0D\u53EF\u3067\u3059\u3002 + +message.creating-bundle-location=\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u30FB\u30D0\u30F3\u30C9\u30EB\u3092\u4F5C\u6210\u3057\u3066\u3044\u307E\u3059: {0} +message.icon-not-png=\u6307\u5B9A\u3057\u305F\u30A2\u30A4\u30B3\u30F3"{0}"\u306FPNG\u30D5\u30A1\u30A4\u30EB\u3067\u306F\u306A\u304F\u3001\u4F7F\u7528\u3055\u308C\u307E\u305B\u3093\u3002\u30C7\u30D5\u30A9\u30EB\u30C8\u30FB\u30A2\u30A4\u30B3\u30F3\u304C\u305D\u306E\u4F4D\u7F6E\u306B\u4F7F\u7528\u3055\u308C\u307E\u3059\u3002 --- /dev/null 2018-10-22 10:28:21.000000000 -0400 +++ new/src/jdk.packager/linux/classes/jdk/packager/internal/resources/linux/LinuxAppBundler_zh_CN.properties 2018-10-22 10:28:19.107192900 -0400 @@ -0,0 +1,47 @@ +# +# 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. +# +# + +bundler.name=Linux \u5E94\u7528\u7A0B\u5E8F\u6620\u50CF +bundler.description=\u4E00\u4E2A\u57FA\u4E8E\u76EE\u5F55\u7684 linux \u5E94\u7528\u7A0B\u5E8F\u6620\u50CF, \u53EF\u4EE5\u9009\u62E9\u6027\u5730\u5E26\u6709\u5171\u540C\u6253\u5305\u7684 JRE\u3002\u7528\u4F5C\u5B89\u88C5\u7A0B\u5E8F\u6253\u5305\u7A0B\u5E8F\u7684\u57FA\u7840\u3002 + +param.icon-png.name=.png \u56FE\u6807 +param.icon-png.description=\u5E94\u7528\u7A0B\u5E8F\u7684\u56FE\u6807, \u91C7\u7528 PNG \u683C\u5F0F\u3002 + +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. + +error.parameters-null=\u53C2\u6570\u6620\u5C04\u4E3A\u7A7A\u503C\u3002 +error.parameters-null.advice=\u8BF7\u4F20\u5165\u975E\u7A7A\u53C2\u6570\u6620\u5C04\u3002 +error.no-linux-resources=Java \u6253\u5305\u7A0B\u5E8F\u4E0D\u652F\u6301 Linux\u3002 +error.no-linux-resources.advice=\u8BF7\u4F7F\u7528 Oracle JDK for Linux \u4E2D\u9644\u5E26\u7684 Java \u6253\u5305\u7A0B\u5E8F\u3002 +error.cannot-create-output-dir=\u65E0\u6CD5\u521B\u5EFA\u8F93\u51FA\u76EE\u5F55 {0}\u3002 +error.cannot-write-to-output-dir=\u8F93\u51FA\u76EE\u5F55 {0} \u4E0D\u53EF\u5199\u3002 + +message.creating-bundle-location=\u6B63\u5728\u521B\u5EFA\u5E94\u7528\u7A0B\u5E8F\u5305: {0} +message.icon-not-png=\u6307\u5B9A\u7684\u56FE\u6807 "{0}" \u4E0D\u662F PNG \u6587\u4EF6, \u4E0D\u4F1A\u4F7F\u7528\u3002\u5C06\u4F7F\u7528\u9ED8\u8BA4\u56FE\u6807\u4EE3\u66FF\u3002 --- /dev/null 2018-10-22 10:28:27.000000000 -0400 +++ new/src/jdk.packager/linux/classes/jdk/packager/internal/resources/linux/LinuxDebBundler.properties 2018-10-22 10:28:25.302107700 -0400 @@ -0,0 +1,108 @@ +# +# 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. +# +# + +bundler.name=DEB Installer +bundler.description=Linux Debian Bundle. + +param.app-bundler.name=DEB Bundler Name +param.app-bundler.description=DEB Bundler Name + +param.bundle-name.name=DEB Bundle Name +param.bundle-name.description=DEB Bundle 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.config-root.name=Config Root Dir +param.config-root.description=Config Root 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-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. + +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 + +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.license-missing=Specified license file is missing. +error.license-missing.advice=Make sure that "{0}" references a file in the app resources, and that it is relative to the basedir "{1}". + +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. + +message.test-for-tool=Test for [{0}]. Result\: {1} +message.debug-working-directory=Kept working directory for debug\: {0} +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.creating-association-with-null-extension=Creating association with null extension. --- /dev/null 2018-10-22 10:28:33.000000000 -0400 +++ new/src/jdk.packager/linux/classes/jdk/packager/internal/resources/linux/LinuxDebBundler_ja.properties 2018-10-22 10:28:31.685244300 -0400 @@ -0,0 +1,108 @@ +# +# 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. +# +# + +bundler.name=DEB\u30A4\u30F3\u30B9\u30C8\u30FC\u30E9 +bundler.description=Linux Debian\u30D0\u30F3\u30C9\u30EB\u3002 + +param.app-bundler.name=DEB Bundler Name +param.app-bundler.description=DEB Bundler Name + +param.bundle-name.name=DEB Bundle Name +param.bundle-name.description=DEB Bundle 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.config-root.name=Config Root Dir +param.config-root.description=Config Root 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-text.name=License Content +param.license-text.description=License Content + +param.xdg-prefix.name=XDG\u30D5\u30A1\u30A4\u30EB\u306E\u63A5\u982D\u8F9E(mime\u3001\u30C7\u30B9\u30AF\u30C8\u30C3\u30D7) +param.xdg-prefix.description=XDG MimeInfo\u304A\u3088\u3073\u30C7\u30B9\u30AF\u30C8\u30C3\u30D7\u30FB\u30D5\u30A1\u30A4\u30EB\u306E\u63A5\u982D\u8F9E\u3002\u30C7\u30D5\u30A9\u30EB\u30C8\u306F-\u3067\u3042\u308A\u3001\u30B9\u30DA\u30FC\u30B9\u306F\u524A\u9664\u3055\u308C\u307E\u3059\u3002 + +resource.deb-control-file=DEB\u5236\u5FA1\u30D5\u30A1\u30A4\u30EB +resource.deb-preinstall-script=DEB\u30A4\u30F3\u30B9\u30C8\u30FC\u30EB\u524D\u30B9\u30AF\u30EA\u30D7\u30C8 +resource.deb-prerm-script=DEB prerm\u30B9\u30AF\u30EA\u30D7\u30C8 +resource.deb-postinstall-script=DEB\u30A4\u30F3\u30B9\u30C8\u30FC\u30EB\u5F8C\u30B9\u30AF\u30EA\u30D7\u30C8 +resource.deb-postrm-script=DEB postrm\u30B9\u30AF\u30EA\u30D7\u30C8 +resource.deb-copyright-file=DEB\u30B3\u30D4\u30FC\u30E9\u30A4\u30C8\u30FB\u30D5\u30A1\u30A4\u30EB +resource.deb-init-script=DEB\u521D\u671F\u5316\u30B9\u30AF\u30EA\u30D7\u30C8 +resource.menu-shortcut-descriptor=\u30E1\u30CB\u30E5\u30FC\u30FB\u30B7\u30E7\u30FC\u30C8\u30AB\u30C3\u30C8\u30FB\u30C7\u30A3\u30B9\u30AF\u30EA\u30D7\u30BF +resource.menu-icon=\u30E1\u30CB\u30E5\u30FC\u30FB\u30A2\u30A4\u30B3\u30F3 + +error.parameters-null=\u30D1\u30E9\u30E1\u30FC\u30BF\u30FB\u30DE\u30C3\u30D7\u304Cnull\u3067\u3059\u3002 +error.parameters-null.advice=\u975Enull\u30D1\u30E9\u30E1\u30FC\u30BF\u30FB\u30DE\u30C3\u30D7\u3067\u6E21\u3057\u3066\u304F\u3060\u3055\u3044\u3002 + +error.tool-not-found={0}\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093\u3002 +error.tool-not-found.advice=\u5FC5\u8981\u306A\u30D1\u30C3\u30B1\u30FC\u30B8\u3092\u30A4\u30F3\u30B9\u30C8\u30FC\u30EB\u3057\u3066\u304F\u3060\u3055\u3044\u3002 + +error.license-missing=\u6307\u5B9A\u3057\u305F\u30E9\u30A4\u30BB\u30F3\u30B9\u30FB\u30D5\u30A1\u30A4\u30EB\u304C\u3042\u308A\u307E\u305B\u3093\u3002 +error.license-missing.advice="{0}"\u304C\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u30FB\u30EA\u30BD\u30FC\u30B9\u5185\u306E\u30D5\u30A1\u30A4\u30EB\u3092\u53C2\u7167\u3057\u3001\u30D9\u30FC\u30B9\u30FB\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA"{1}"\u306B\u5BFE\u3057\u3066\u76F8\u5BFE\u7684\u3067\u3042\u308B\u3053\u3068\u3092\u78BA\u8A8D\u3057\u3066\u304F\u3060\u3055\u3044\u3002 + +error.launcher-name-too-long=\u30D0\u30F3\u30C9\u30EB\u540D"{0}"\u304C\u3001\u30C7\u30FC\u30E2\u30F3\u306B\u5BFE\u3057\u3066\u9577\u3059\u304E\u307E\u3059\u3002 +error.launcher-name-too-long.advice=\u30D0\u30F3\u30C9\u30E9\u5F15\u6570"{0}"\u309216\u6587\u5B57\u672A\u6E80\u306E\u30D0\u30F3\u30C9\u30EB\u540D\u306B\u8A2D\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044\u3002 + +error.cannot-create-output-dir=\u51FA\u529B\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA{0}\u3092\u4F5C\u6210\u3067\u304D\u307E\u305B\u3093\u3002 +error.cannot-write-to-output-dir=\u51FA\u529B\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA{0}\u306F\u66F8\u8FBC\u307F\u4E0D\u53EF\u3067\u3059\u3002 + +error.no-content-types-for-file-association=\u30D5\u30A1\u30A4\u30EB\u30FB\u30A2\u30BD\u30B7\u30A8\u30FC\u30B7\u30E7\u30F3\u756A\u53F7{0}\u306BMIME\u30BF\u30A4\u30D7\u304C\u6307\u5B9A\u3055\u308C\u307E\u305B\u3093\u3067\u3057\u305F\u3002 +error.no-content-types-for-file-association.advice=Linux\u30D0\u30F3\u30C9\u30EB\u306E\u5834\u5408\u3001\u5404\u30D5\u30A1\u30A4\u30EB\u30FB\u30A2\u30BD\u30B7\u30A8\u30FC\u30B7\u30E7\u30F3\u306BMIME\u30BF\u30A4\u30D7\u30921\u3064\u3060\u3051\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044\u3002 + +error.too-many-content-types-for-file-association=\u30D5\u30A1\u30A4\u30EB\u30FB\u30A2\u30BD\u30B7\u30A8\u30FC\u30B7\u30E7\u30F3\u756A\u53F7{0}\u306B\u8907\u6570\u306EMIME\u30BF\u30A4\u30D7\u304C\u6307\u5B9A\u3055\u308C\u307E\u3057\u305F\u3002 +error.too-many-content-types-for-file-association.advice=Linux\u30D0\u30F3\u30C9\u30EB\u306E\u5834\u5408\u3001\u5404\u30D5\u30A1\u30A4\u30EB\u30FB\u30A2\u30BD\u30B7\u30A8\u30FC\u30B7\u30E7\u30F3\u306BMIME\u30BF\u30A4\u30D7\u30921\u3064\u3060\u3051\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044\u3002 + +error.no-support-for-peruser-daemons=\u30D0\u30F3\u30C9\u30E9\u306F\u3001\u30E6\u30FC\u30B6\u30FC\u5358\u4F4D\u306E\u30C7\u30FC\u30E2\u30F3\u3092\u30B5\u30DD\u30FC\u30C8\u3057\u3066\u3044\u307E\u305B\u3093\u3002 +error.no-support-for-peruser-daemons.advice=\u30B7\u30B9\u30C6\u30E0\u5168\u4F53\u306E\u30D2\u30F3\u30C8\u304Ctrue\u306B\u8A2D\u5B9A\u3055\u308C\u3066\u3044\u308B\u3053\u3068\u3092\u78BA\u8A8D\u3057\u3066\u304F\u3060\u3055\u3044\u3002 + +error.invalid-value-for-package-name=\u30D1\u30C3\u30B1\u30FC\u30B8\u540D\u306B\u5BFE\u3057\u3066\u5024"{0}"\u306F\u7121\u52B9\u3067\u3059\u3002 +error.invalid-value-for-package-name.advice="linux.bundleName"\u30D1\u30E9\u30E1\u30FC\u30BF\u3092\u6709\u52B9\u306ADebian\u30D1\u30C3\u30B1\u30FC\u30B8\u540D\u306B\u8A2D\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044\u3002\u30D1\u30C3\u30B1\u30FC\u30B8\u540D\u306B\u306F\u3001\u5C0F\u6587\u5B57(a-z)\u3001\u6570\u5B57(0-9)\u3001\u30D7\u30E9\u30B9(+)\u3068\u30DE\u30A4\u30CA\u30B9(-)\u306E\u8A18\u53F7\u304A\u3088\u3073\u30D4\u30EA\u30AA\u30C9(.)\u306E\u307F\u3092\u542B\u3081\u308B\u3088\u3046\u306B\u3057\u3066\u304F\u3060\u3055\u3044\u3002\u9577\u3055\u306F2\u6587\u5B57\u4EE5\u4E0A\u3068\u3057\u3001\u82F1\u6570\u5B57\u3067\u59CB\u3081\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059\u3002 + +message.test-for-tool=[{0}]\u306E\u30C6\u30B9\u30C8\u3002\u7D50\u679C: {1} +message.debug-working-directory=\u30C7\u30D0\u30C3\u30B0\u306E\u4F5C\u696D\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u304C\u4FDD\u6301\u3055\u308C\u307E\u3057\u305F: {0} +message.outputting-to-location=\u30A4\u30F3\u30B9\u30C8\u30FC\u30E9\u306EDEB\u3092\u6B21\u306B\u751F\u6210\u3057\u3066\u3044\u307E\u3059: {0} +message.output-to-location=\u30D1\u30C3\u30B1\u30FC\u30B8(.deb)\u306F\u6B21\u306B\u4FDD\u5B58\u3055\u308C\u307E\u3057\u305F: {0} +message.debs-like-licenses=Debian\u30D1\u30C3\u30B1\u30FC\u30B8\u3067\u306F\u30E9\u30A4\u30BB\u30F3\u30B9\u3092\u6307\u5B9A\u3059\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059\u3002\u30E9\u30A4\u30BB\u30F3\u30B9\u304C\u306A\u3044\u5834\u5408\u3001\u4E00\u90E8\u306ELinux\u30C7\u30A3\u30B9\u30C8\u30EA\u30D3\u30E5\u30FC\u30B7\u30E7\u30F3\u3067\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u306E\u54C1\u8CEA\u306B\u554F\u984C\u304C\u767A\u751F\u3059\u308B\u5834\u5408\u304C\u3042\u308A\u307E\u3059\u3002 +message.creating-association-with-null-extension=null\u62E1\u5F35\u5B50\u3068\u306E\u95A2\u9023\u4ED8\u3051\u3092\u4F5C\u6210\u3057\u3066\u3044\u307E\u3059\u3002 --- /dev/null 2018-10-22 10:28:40.000000000 -0400 +++ new/src/jdk.packager/linux/classes/jdk/packager/internal/resources/linux/LinuxDebBundler_zh_CN.properties 2018-10-22 10:28:38.429222700 -0400 @@ -0,0 +1,108 @@ +# +# 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. +# +# + +bundler.name=DEB \u5B89\u88C5\u7A0B\u5E8F +bundler.description=Linux Debian \u5305\u3002 + +param.app-bundler.name=DEB Bundler Name +param.app-bundler.description=DEB Bundler Name + +param.bundle-name.name=DEB Bundle Name +param.bundle-name.description=DEB Bundle 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.config-root.name=Config Root Dir +param.config-root.description=Config Root 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-text.name=License Content +param.license-text.description=License Content + +param.xdg-prefix.name=XDG \u6587\u4EF6 (mime, desktop) \u7684\u524D\u7F00 +param.xdg-prefix.description=XDG MimeInfo \u548C Desktop \u6587\u4EF6\u7684\u524D\u7F00\u3002\u9ED8\u8BA4\u4E3A -, \u4E0D\u542B\u7A7A\u683C\u3002 + +resource.deb-control-file=DEB \u63A7\u5236\u6587\u4EF6 +resource.deb-preinstall-script=DEB \u5B89\u88C5\u524D\u811A\u672C +resource.deb-prerm-script=DEB \u5220\u9664\u524D\u811A\u672C +resource.deb-postinstall-script=DEB \u5B89\u88C5\u540E\u811A\u672C +resource.deb-postrm-script=DEB \u5220\u9664\u540E\u811A\u672C +resource.deb-copyright-file=DEB \u7248\u6743\u6587\u4EF6 +resource.deb-init-script=DEB \u521D\u59CB\u5316\u811A\u672C +resource.menu-shortcut-descriptor=\u83DC\u5355\u5FEB\u6377\u65B9\u5F0F\u63CF\u8FF0\u7B26 +resource.menu-icon=\u83DC\u5355\u56FE\u6807 + +error.parameters-null=\u53C2\u6570\u6620\u5C04\u4E3A\u7A7A\u503C\u3002 +error.parameters-null.advice=\u8BF7\u4F20\u5165\u975E\u7A7A\u53C2\u6570\u6620\u5C04\u3002 + +error.tool-not-found=\u627E\u4E0D\u5230{0}\u3002 +error.tool-not-found.advice=\u8BF7\u5B89\u88C5\u6240\u9700\u7684\u7A0B\u5E8F\u5305\u3002 + +error.license-missing=\u7F3A\u5C11\u6307\u5B9A\u7684\u8BB8\u53EF\u8BC1\u6587\u4EF6\u3002 +error.license-missing.advice=\u8BF7\u786E\u4FDD "{0}" \u5F15\u7528\u5E94\u7528\u7A0B\u5E8F\u8D44\u6E90\u4E2D\u7684\u6587\u4EF6, \u5E76\u4E14\u4F7F\u7528\u57FA\u76EE\u5F55 "{1}" \u7684\u76F8\u5BF9\u76EE\u5F55\u3002 + +error.launcher-name-too-long=\u5B88\u62A4\u7A0B\u5E8F\u7684\u5305\u540D "{0}" \u592A\u957F\u3002 +error.launcher-name-too-long.advice=\u5C06\u6253\u5305\u7A0B\u5E8F\u53C2\u6570 "{0}" \u8BBE\u7F6E\u4E3A\u5C11\u4E8E 16 \u4E2A\u5B57\u7B26\u7684\u5305\u540D\u3002 + +error.cannot-create-output-dir=\u65E0\u6CD5\u521B\u5EFA\u8F93\u51FA\u76EE\u5F55 {0}\u3002 +error.cannot-write-to-output-dir=\u8F93\u51FA\u76EE\u5F55 {0} \u4E0D\u53EF\u5199\u3002 + +error.no-content-types-for-file-association=\u6CA1\u6709\u4E3A\u6587\u4EF6\u5173\u8054\u53F7{0}\u6307\u5B9A MIME \u7C7B\u578B\u3002 +error.no-content-types-for-file-association.advice=\u5BF9\u4E8E Linux \u6253\u5305, \u8BF7\u4E3A\u6BCF\u4E2A\u6587\u4EF6\u5173\u8054\u6307\u5B9A\u4E00\u4E2A\u4E14\u4EC5\u6307\u5B9A\u4E00\u4E2A MIME \u7C7B\u578B\u3002 + +error.too-many-content-types-for-file-association=\u4E3A\u6587\u4EF6\u5173\u8054\u53F7{0}\u6307\u5B9A\u4E86\u591A\u4E2A MIME \u7C7B\u578B\u3002 +error.too-many-content-types-for-file-association.advice=\u5BF9\u4E8E Linux \u6253\u5305, \u8BF7\u4E3A\u6BCF\u4E2A\u6587\u4EF6\u5173\u8054\u6307\u5B9A\u4E00\u4E2A\u4E14\u4EC5\u6307\u5B9A\u4E00\u4E2A MIME \u7C7B\u578B\u3002 + +error.no-support-for-peruser-daemons=\u6253\u5305\u7A0B\u5E8F\u4E0D\u652F\u6301\u6BCF\u7528\u6237\u5B88\u62A4\u7A0B\u5E8F\u3002 +error.no-support-for-peruser-daemons.advice=\u786E\u4FDD\u7CFB\u7EDF\u8303\u56F4\u63D0\u793A\u8BBE\u7F6E\u4E3A\u201C\u771F\u201D\u3002 + +error.invalid-value-for-package-name=\u7A0B\u5E8F\u5305\u540D\u79F0\u7684\u503C "{0}" \u65E0\u6548\u3002 +error.invalid-value-for-package-name.advice=\u5C06 "linux.bundleName" \u53C2\u6570\u8BBE\u7F6E\u4E3A\u6709\u6548\u7684 Debian \u7A0B\u5E8F\u5305\u540D\u79F0\u3002\u8BF7\u6CE8\u610F, \u7A0B\u5E8F\u5305\u540D\u79F0\u53EA\u80FD\u5305\u542B\u5C0F\u5199\u5B57\u6BCD (a-z), \u6570\u5B57 (0-9), \u52A0\u53F7 (+) \u548C\u51CF\u53F7 (-) \u4EE5\u53CA\u53E5\u70B9 (.)\u3002\u540D\u79F0\u957F\u5EA6\u5FC5\u987B\u81F3\u5C11\u4E3A\u4E24\u4E2A\u5B57\u7B26\u5E76\u4E14\u5FC5\u987B\u4EE5\u5B57\u6BCD\u6570\u5B57\u5B57\u7B26\u5F00\u5934\u3002 + +message.test-for-tool=[{0}] \u7684\u6D4B\u8BD5\u3002\u7ED3\u679C: {1} +message.debug-working-directory=\u7528\u4E8E\u8C03\u8BD5\u7684\u5DF2\u4FDD\u7559\u5DE5\u4F5C\u76EE\u5F55: {0} +message.outputting-to-location=\u6B63\u5728\u4E3A\u5B89\u88C5\u7A0B\u5E8F\u751F\u6210 DEB, \u4F4D\u7F6E: {0} +message.output-to-location=\u7A0B\u5E8F\u5305 (.deb) \u5DF2\u4FDD\u5B58\u5230: {0} +message.debs-like-licenses=Debian \u7A0B\u5E8F\u5305\u5E94\u6307\u5B9A\u8BB8\u53EF\u8BC1\u3002\u7F3A\u5C11\u8BB8\u53EF\u8BC1\u5C06\u5BFC\u81F4\u67D0\u4E9B Linux \u5206\u53D1\u6295\u8BC9\u5E94\u7528\u7A0B\u5E8F\u8D28\u91CF\u3002 +message.creating-association-with-null-extension=\u6B63\u5728\u4F7F\u7528\u7A7A\u6269\u5C55\u540D\u521B\u5EFA\u5173\u8054\u3002 --- /dev/null 2018-10-22 10:28:48.000000000 -0400 +++ new/src/jdk.packager/linux/classes/jdk/packager/internal/resources/linux/LinuxResources.java 2018-10-22 10:28:45.975900900 -0400 @@ -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.packager.internal.resources.linux; + +public class LinuxResources { + +} --- /dev/null 2018-10-22 10:28:55.000000000 -0400 +++ new/src/jdk.packager/linux/classes/jdk/packager/internal/resources/linux/LinuxRpmBundler.properties 2018-10-22 10:28:53.042917500 -0400 @@ -0,0 +1,83 @@ +# +# 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. +# +# + +bundler.name=RPM Bundle +bundler.description=Redhat Package Manager (RPM) bundler. + +param.app-bundler.name=RPM Bundler +param.app-bundler.description=RPM Bundler + +param.image-dir.name=Image Dir +param.image-dir.description=Image Dir + +param.bundle-name.name=RPM Bundle +param.bundle-name.description=RPM Bundle + +param.config-root.name=Config Root Dir +param.config-root.description=Config Root Dir + +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.license-type.name=License Type +param.license-type.description=License Type for RPM package. +param.license-type.default=Unknown + +resource.rpm-spec-file=RPM spec file +resource.rpm-init-script=RPM init script +resource.menu-shortcut-descriptor=Menu shortcut descriptor +resource.menu-icon=menu icon + +error.parameters-null=Parameters map is null. +error.parameters-null.advice=Pass in a non-null parameters map. + +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. + +error.license-missing=Specified license file is missing. +error.license-missing.advice=Make sure that "{0}" references a file in the app resources, and that it is relative file reference. + +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 RPM package name. Note that the packages must be named using only the following ASCII characters: abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._+ + +message.test-for-tool=Test for [{0}]. Result\: {1} +message.one-shortcut-required=At least one type of shortcut is required. Enabling menu shortcut. +message.debug-working-directory=Kept working directory for debug\: {0} +message.outputting-bundle-location=Generating RPM for installer to\: {0} +message.output-bundle-location=Package (.rpm) saved to\: {0} +message.creating-association-with-null-extension=Creating association with null extension. --- /dev/null 2018-10-22 10:29:01.000000000 -0400 +++ new/src/jdk.packager/linux/classes/jdk/packager/internal/resources/linux/LinuxRpmBundler_ja.properties 2018-10-22 10:28:59.498463300 -0400 @@ -0,0 +1,79 @@ +# +# 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. +# +# + +bundler.name=RPM\u30D0\u30F3\u30C9\u30EB +bundler.description=RedHat Package Manager (RPM)\u306E\u30D0\u30F3\u30C9\u30E9\u3002 + +param.app-bundler.name=RPM Bundler +param.app-bundler.description=RPM Bundler + +param.image-dir.name=Image Dir +param.image-dir.description=Image Dir + +param.bundle-name.name=RPM Bundle +param.bundle-name.description=RPM Bundle + +param.config-root.name=Config Root Dir +param.config-root.description=Config Root Dir + +param.xdg-prefix.name=XDG\u30D5\u30A1\u30A4\u30EB\u306E\u63A5\u982D\u8F9E(mime\u3001\u30C7\u30B9\u30AF\u30C8\u30C3\u30D7) +param.xdg-prefix.description=XDG MimeInfo\u304A\u3088\u3073\u30C7\u30B9\u30AF\u30C8\u30C3\u30D7\u30FB\u30D5\u30A1\u30A4\u30EB\u306E\u63A5\u982D\u8F9E\u3002\u30C7\u30D5\u30A9\u30EB\u30C8\u306F-\u3067\u3042\u308A\u3001\u30B9\u30DA\u30FC\u30B9\u306F\u524A\u9664\u3055\u308C\u307E\u3059\u3002 + +resource.rpm-spec-file=RPM\u4ED5\u69D8\u30D5\u30A1\u30A4\u30EB +resource.rpm-init-script=RPM\u521D\u671F\u5316\u30B9\u30AF\u30EA\u30D7\u30C8 +resource.menu-shortcut-descriptor=\u30E1\u30CB\u30E5\u30FC\u30FB\u30B7\u30E7\u30FC\u30C8\u30AB\u30C3\u30C8\u30FB\u30C7\u30A3\u30B9\u30AF\u30EA\u30D7\u30BF +resource.menu-icon=\u30E1\u30CB\u30E5\u30FC\u30FB\u30A2\u30A4\u30B3\u30F3 + +error.parameters-null=\u30D1\u30E9\u30E1\u30FC\u30BF\u30FB\u30DE\u30C3\u30D7\u304Cnull\u3067\u3059\u3002 +error.parameters-null.advice=\u975Enull\u30D1\u30E9\u30E1\u30FC\u30BF\u30FB\u30DE\u30C3\u30D7\u3067\u6E21\u3057\u3066\u304F\u3060\u3055\u3044\u3002 + +error.cannot-find-rpmbuild=rpmbuild {0}\u307E\u305F\u306F\u305D\u308C\u4EE5\u964D\u306E\u3082\u306E\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093\u3002 +error.cannot-find-rpmbuild.advice=\ \u30A4\u30F3\u30B9\u30C8\u30FC\u30EB\u30FB\u30D1\u30C3\u30B1\u30FC\u30B8\u306F\u30D0\u30FC\u30B8\u30E7\u30F3{0}\u4EE5\u964D\u306ERPM\u3092\u30D3\u30EB\u30C9\u3059\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059\u3002 + +error.license-missing=\u6307\u5B9A\u3057\u305F\u30E9\u30A4\u30BB\u30F3\u30B9\u30FB\u30D5\u30A1\u30A4\u30EB\u304C\u3042\u308A\u307E\u305B\u3093\u3002 +error.license-missing.advice="{0}"\u304C\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u30FB\u30EA\u30BD\u30FC\u30B9\u5185\u306E\u30D5\u30A1\u30A4\u30EB\u3092\u53C2\u7167\u3057\u3001\u76F8\u5BFE\u30D5\u30A1\u30A4\u30EB\u53C2\u7167\u3067\u3042\u308B\u3053\u3068\u3092\u78BA\u8A8D\u3057\u3066\u304F\u3060\u3055\u3044\u3002 + +error.cannot-create-output-dir=\u51FA\u529B\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA{0}\u3092\u4F5C\u6210\u3067\u304D\u307E\u305B\u3093\u3002 +error.cannot-write-to-output-dir=\u51FA\u529B\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA{0}\u306F\u66F8\u8FBC\u307F\u4E0D\u53EF\u3067\u3059\u3002 + +error.no-content-types-for-file-association=\u30D5\u30A1\u30A4\u30EB\u30FB\u30A2\u30BD\u30B7\u30A8\u30FC\u30B7\u30E7\u30F3\u756A\u53F7{0}\u306BMIME\u30BF\u30A4\u30D7\u304C\u6307\u5B9A\u3055\u308C\u307E\u305B\u3093\u3067\u3057\u305F\u3002 +error.no-content-types-for-file-association.advice=Linux\u30D0\u30F3\u30C9\u30EB\u306E\u5834\u5408\u3001\u5404\u30D5\u30A1\u30A4\u30EB\u30FB\u30A2\u30BD\u30B7\u30A8\u30FC\u30B7\u30E7\u30F3\u306BMIME\u30BF\u30A4\u30D7\u30921\u3064\u3060\u3051\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044\u3002 + +error.too-many-content-types-for-file-association=\u30D5\u30A1\u30A4\u30EB\u30FB\u30A2\u30BD\u30B7\u30A8\u30FC\u30B7\u30E7\u30F3\u756A\u53F7{0}\u306B\u8907\u6570\u306EMIME\u30BF\u30A4\u30D7\u304C\u6307\u5B9A\u3055\u308C\u307E\u3057\u305F\u3002 +error.too-many-content-types-for-file-association.advice=Linux\u30D0\u30F3\u30C9\u30EB\u306E\u5834\u5408\u3001\u5404\u30D5\u30A1\u30A4\u30EB\u30FB\u30A2\u30BD\u30B7\u30A8\u30FC\u30B7\u30E7\u30F3\u306BMIME\u30BF\u30A4\u30D7\u30921\u3064\u3060\u3051\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044\u3002 + +error.no-support-for-peruser-daemons=\u30D0\u30F3\u30C9\u30E9\u306F\u3001\u30E6\u30FC\u30B6\u30FC\u5358\u4F4D\u306E\u30C7\u30FC\u30E2\u30F3\u3092\u30B5\u30DD\u30FC\u30C8\u3057\u3066\u3044\u307E\u305B\u3093\u3002 +error.no-support-for-peruser-daemons.advice=\u30B7\u30B9\u30C6\u30E0\u5168\u4F53\u306E\u30D2\u30F3\u30C8\u304Ctrue\u306B\u8A2D\u5B9A\u3055\u308C\u3066\u3044\u308B\u3053\u3068\u3092\u78BA\u8A8D\u3057\u3066\u304F\u3060\u3055\u3044\u3002 + +error.invalid-value-for-package-name=\u30D1\u30C3\u30B1\u30FC\u30B8\u540D\u306B\u5BFE\u3057\u3066\u5024"{0}"\u306F\u7121\u52B9\u3067\u3059\u3002 +error.invalid-value-for-package-name.advice="linux.bundleName"\u30D1\u30E9\u30E1\u30FC\u30BF\u3092\u6709\u52B9\u306ARPM\u30D1\u30C3\u30B1\u30FC\u30B8\u540D\u306B\u8A2D\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044\u3002\u30D1\u30C3\u30B1\u30FC\u30B8\u540D\u306F\u3001\u6B21\u306EASCII\u6587\u5B57\u306E\u307F\u3092\u4F7F\u7528\u3057\u3066\u6307\u5B9A\u3059\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059: abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._+ + +message.test-for-tool=[{0}]\u306E\u30C6\u30B9\u30C8\u3002\u7D50\u679C: {1} +message.one-shortcut-required=\u5C11\u306A\u304F\u3068\u30821\u3064\u306E\u30B7\u30E7\u30FC\u30C8\u30AB\u30C3\u30C8\u30FB\u30BF\u30A4\u30D7\u304C\u5FC5\u8981\u3067\u3059\u3002\u30E1\u30CB\u30E5\u30FC\u30FB\u30B7\u30E7\u30FC\u30C8\u30AB\u30C3\u30C8\u3092\u6709\u52B9\u5316\u3057\u3066\u3044\u307E\u3059\u3002 +message.debug-working-directory=\u30C7\u30D0\u30C3\u30B0\u306E\u4F5C\u696D\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u304C\u4FDD\u6301\u3055\u308C\u307E\u3057\u305F: {0} +message.outputting-bundle-location=\u30A4\u30F3\u30B9\u30C8\u30FC\u30E9\u306ERPM\u3092\u6B21\u306B\u751F\u6210\u3057\u3066\u3044\u307E\u3059: {0} +message.output-bundle-location=\u30D1\u30C3\u30B1\u30FC\u30B8(.rpm)\u306F\u6B21\u306B\u4FDD\u5B58\u3055\u308C\u307E\u3057\u305F: {0} +message.creating-association-with-null-extension=null\u62E1\u5F35\u5B50\u3068\u306E\u95A2\u9023\u4ED8\u3051\u3092\u4F5C\u6210\u3057\u3066\u3044\u307E\u3059\u3002 --- /dev/null 2018-10-22 10:29:08.000000000 -0400 +++ new/src/jdk.packager/linux/classes/jdk/packager/internal/resources/linux/LinuxRpmBundler_zh_CN.properties 2018-10-22 10:29:06.592682700 -0400 @@ -0,0 +1,79 @@ +# +# 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. +# +# + +bundler.name=RPM \u5305 +bundler.description=Redhat Package Manager (RPM) \u6253\u5305\u7A0B\u5E8F\u3002 + +param.app-bundler.name=RPM Bundler +param.app-bundler.description=RPM Bundler + +param.image-dir.name=Image Dir +param.image-dir.description=Image Dir + +param.bundle-name.name=RPM Bundle +param.bundle-name.description=RPM Bundle + +param.config-root.name=Config Root Dir +param.config-root.description=Config Root Dir + +param.xdg-prefix.name=XDG \u6587\u4EF6 (mime, desktop) \u7684\u524D\u7F00 +param.xdg-prefix.description=XDG MimeInfo \u548C Desktop \u6587\u4EF6\u7684\u524D\u7F00\u3002\u9ED8\u8BA4\u4E3A -, \u4E0D\u542B\u7A7A\u683C\u3002 + +resource.rpm-spec-file=RPM \u89C4\u8303\u6587\u4EF6 +resource.rpm-init-script=RPM \u521D\u59CB\u5316\u811A\u672C +resource.menu-shortcut-descriptor=\u83DC\u5355\u5FEB\u6377\u65B9\u5F0F\u63CF\u8FF0\u7B26 +resource.menu-icon=\u83DC\u5355\u56FE\u6807 + +error.parameters-null=\u53C2\u6570\u6620\u5C04\u4E3A\u7A7A\u503C\u3002 +error.parameters-null.advice=\u8BF7\u4F20\u5165\u975E\u7A7A\u53C2\u6570\u6620\u5C04\u3002 + +error.cannot-find-rpmbuild=\u627E\u4E0D\u5230 rpmbuild {0} \u6216\u66F4\u65B0\u7248\u672C\u3002 +error.cannot-find-rpmbuild.advice=\ \u8BF7\u5B89\u88C5\u6784\u5EFA RPM \u7248\u672C {0} \u6216\u66F4\u65B0\u7248\u672C\u6240\u9700\u7684\u7A0B\u5E8F\u5305\u3002 + +error.license-missing=\u7F3A\u5C11\u6307\u5B9A\u7684\u8BB8\u53EF\u8BC1\u6587\u4EF6\u3002 +error.license-missing.advice=\u786E\u4FDD "{0}" \u5F15\u7528\u5E94\u7528\u7A0B\u5E8F\u8D44\u6E90\u4E2D\u7684\u6587\u4EF6, \u5E76\u4E14\u662F\u76F8\u5BF9\u6587\u4EF6\u5F15\u7528\u3002 + +error.cannot-create-output-dir=\u65E0\u6CD5\u521B\u5EFA\u8F93\u51FA\u76EE\u5F55 {0}\u3002 +error.cannot-write-to-output-dir=\u8F93\u51FA\u76EE\u5F55 {0} \u4E0D\u53EF\u5199\u3002 + +error.no-content-types-for-file-association=\u6CA1\u6709\u4E3A\u6587\u4EF6\u5173\u8054\u53F7{0}\u6307\u5B9A MIME \u7C7B\u578B\u3002 +error.no-content-types-for-file-association.advice=\u5BF9\u4E8E Linux \u6253\u5305, \u8BF7\u4E3A\u6BCF\u4E2A\u6587\u4EF6\u5173\u8054\u6307\u5B9A\u4E00\u4E2A\u4E14\u4EC5\u6307\u5B9A\u4E00\u4E2A MIME \u7C7B\u578B\u3002 + +error.too-many-content-types-for-file-association=\u4E3A\u6587\u4EF6\u5173\u8054\u53F7{0}\u6307\u5B9A\u4E86\u591A\u4E2A MIME \u7C7B\u578B\u3002 +error.too-many-content-types-for-file-association.advice=\u5BF9\u4E8E Linux \u6253\u5305, \u8BF7\u4E3A\u6BCF\u4E2A\u6587\u4EF6\u5173\u8054\u6307\u5B9A\u4E00\u4E2A\u4E14\u4EC5\u6307\u5B9A\u4E00\u4E2A MIME \u7C7B\u578B\u3002 + +error.no-support-for-peruser-daemons=\u6253\u5305\u7A0B\u5E8F\u4E0D\u652F\u6301\u6BCF\u7528\u6237\u5B88\u62A4\u7A0B\u5E8F\u3002 +error.no-support-for-peruser-daemons.advice=\u786E\u4FDD\u7CFB\u7EDF\u8303\u56F4\u63D0\u793A\u8BBE\u7F6E\u4E3A\u201C\u771F\u201D\u3002 + +error.invalid-value-for-package-name=\u7A0B\u5E8F\u5305\u540D\u79F0\u7684\u503C "{0}" \u65E0\u6548\u3002 +error.invalid-value-for-package-name.advice=\u5C06 "linux.bundleName" \u53C2\u6570\u8BBE\u7F6E\u4E3A\u6709\u6548\u7684 RPM \u7A0B\u5E8F\u5305\u540D\u79F0\u3002\u8BF7\u6CE8\u610F, \u7A0B\u5E8F\u5305\u540D\u79F0\u53EA\u80FD\u4F7F\u7528\u4EE5\u4E0B ASCII \u5B57\u7B26: abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._+ + +message.test-for-tool=[{0}] \u7684\u6D4B\u8BD5\u3002\u7ED3\u679C: {1} +message.one-shortcut-required=\u81F3\u5C11\u9700\u8981\u4E00\u79CD\u7C7B\u578B\u7684\u5FEB\u6377\u65B9\u5F0F\u3002\u6B63\u5728\u542F\u7528\u83DC\u5355\u5FEB\u6377\u65B9\u5F0F\u3002 +message.debug-working-directory=\u7528\u4E8E\u8C03\u8BD5\u7684\u5DF2\u4FDD\u7559\u5DE5\u4F5C\u76EE\u5F55: {0} +message.outputting-bundle-location=\u6B63\u5728\u4E3A\u5B89\u88C5\u7A0B\u5E8F\u751F\u6210 RPM, \u4F4D\u7F6E: {0} +message.output-bundle-location=\u7A0B\u5E8F\u5305 (.rpm) \u5DF2\u4FDD\u5B58\u5230: {0} +message.creating-association-with-null-extension=\u6B63\u5728\u4F7F\u7528\u7A7A\u6269\u5C55\u540D\u521B\u5EFA\u5173\u8054\u3002 Binary files /dev/null and new/src/jdk.packager/linux/classes/jdk/packager/internal/resources/linux/javalogo_white_16.png differ Binary files /dev/null and new/src/jdk.packager/linux/classes/jdk/packager/internal/resources/linux/javalogo_white_32.png differ Binary files /dev/null and new/src/jdk.packager/linux/classes/jdk/packager/internal/resources/linux/javalogo_white_48.png differ --- /dev/null 2018-10-22 10:29:38.000000000 -0400 +++ new/src/jdk.packager/linux/classes/jdk/packager/internal/resources/linux/template.control 2018-10-22 10:29:36.012294900 -0400 @@ -0,0 +1,10 @@ +Package: APPLICATION_PACKAGE +Version: APPLICATION_VERSION +Section: unknown +Maintainer: APPLICATION_MAINTAINER +Priority: optional +Architecture: APPLICATION_ARCH +Provides: APPLICATION_PACKAGE +Description: APPLICATION_SUMMARY +Installed-Size: APPLICATION_INSTALLED_SIZE +PACKAGE_DEPENDENCIES --- /dev/null 2018-10-22 10:29:46.000000000 -0400 +++ new/src/jdk.packager/linux/classes/jdk/packager/internal/resources/linux/template.copyright 2018-10-22 10:29:44.180643300 -0400 @@ -0,0 +1,8 @@ + +Copyright: + + APPLICATION_COPYRIGHT + +License: + + APPLICATION_LICENSE_TEXT --- /dev/null 2018-10-22 10:29:55.000000000 -0400 +++ new/src/jdk.packager/linux/classes/jdk/packager/internal/resources/linux/template.desktop 2018-10-22 10:29:52.667627700 -0400 @@ -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 --- /dev/null 2018-10-22 10:30:02.000000000 -0400 +++ new/src/jdk.packager/linux/classes/jdk/packager/internal/resources/linux/template.postinst 2018-10-22 10:29:59.429412300 -0400 @@ -0,0 +1,61 @@ +#!/bin/sh +# postinst script for APPLICATION_NAME +# +# see: dh_installdeb(1) + +set -e + +# summary of how this script can be called: +# * `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 --- /dev/null 2018-10-22 10:30:10.000000000 -0400 +++ new/src/jdk.packager/linux/classes/jdk/packager/internal/resources/linux/template.postrm 2018-10-22 10:30:08.157826500 -0400 @@ -0,0 +1,44 @@ +#!/bin/sh +# postrm script for APPLICATION_NAME +# +# see: dh_installdeb(1) + +set -e + +# summary of how this script can be called: +# * `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 --- /dev/null 2018-10-22 10:30:18.000000000 -0400 +++ new/src/jdk.packager/linux/classes/jdk/packager/internal/resources/linux/template.preinst 2018-10-22 10:30:15.492284700 -0400 @@ -0,0 +1,35 @@ +#!/bin/sh +# preinst script for APPLICATION_NAME +# +# see: dh_installdeb(1) + +set -e + +# summary of how this script can be called: +# * `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 --- /dev/null 2018-10-22 10:30:26.000000000 -0400 +++ new/src/jdk.packager/linux/classes/jdk/packager/internal/resources/linux/template.prerm 2018-10-22 10:30:23.208378300 -0400 @@ -0,0 +1,45 @@ +#!/bin/sh +# prerm script for APPLICATION_NAME +# +# see: dh_installdeb(1) + +set -e + +# summary of how this script can be called: +# * `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 + --- /dev/null 2018-10-22 10:30:33.000000000 -0400 +++ new/src/jdk.packager/linux/classes/jdk/packager/internal/resources/linux/template.spec 2018-10-22 10:30:30.637039500 -0400 @@ -0,0 +1,60 @@ +Summary: APPLICATION_SUMMARY +Name: APPLICATION_PACKAGE +Version: APPLICATION_VERSION +Release: 1 +License: APPLICATION_LICENSE_TYPE +Vendor: APPLICATION_VENDOR +Prefix: INSTALLATION_DIRECTORY +Provides: APPLICATION_PACKAGE +Autoprov: 0 +Autoreq: 0 +PACKAGE_DEPENDENCIES + +#avoid ARCH subfolder +%define _rpmfilename %%{NAME}-%%{VERSION}-%%{RELEASE}.%%{ARCH}.rpm + +#comment line below to enable effective jar compression +#it could easily get your package size from 40 to 15Mb but +#build time will substantially increase and it may require unpack200/system java to install +%define __jar_repack %{nil} + +%description +APPLICATION_DESCRIPTION + +%prep + +%build + +%install +rm -rf %{buildroot} +mkdir -p %{buildroot}INSTALLATION_DIRECTORY +cp -r %{_sourcedir}/APPLICATION_FS_NAME %{buildroot}INSTALLATION_DIRECTORY + +%files +APPLICATION_LICENSE_FILE +INSTALLATION_DIRECTORY/APPLICATION_FS_NAME + +%post +if [ "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 --- /dev/null 2018-10-22 10:30:40.000000000 -0400 +++ new/src/jdk.packager/linux/classes/module-info.java.extra 2018-10-22 10:30:37.687460700 -0400 @@ -0,0 +1,32 @@ +/* + * 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.packager.internal.Bundler with + jdk.packager.internal.linux.LinuxAppBundler, + jdk.packager.internal.linux.LinuxDebBundler, + jdk.packager.internal.linux.LinuxRpmBundler; + + --- /dev/null 2018-10-22 10:30:47.000000000 -0400 +++ new/src/jdk.packager/linux/native/launcher/launcher.cpp 2018-10-22 10:30:44.841290700 -0400 @@ -0,0 +1,88 @@ +/* + * 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 += "/libpackager.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 libpackager.so"); + } + + dlclose(library); + } + + + return result; +} --- /dev/null 2018-10-22 10:30:55.000000000 -0400 +++ new/src/jdk.packager/macosx/classes/jdk/packager/internal/builders/mac/MacAppImageBuilder.java 2018-10-22 10:30:52.845428700 -0400 @@ -0,0 +1,984 @@ +/* + * 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.packager.internal.builders.mac; + +import jdk.packager.internal.BundlerParamInfo; +import jdk.packager.internal.IOUtils; +import jdk.packager.internal.Log; +import jdk.packager.internal.Platform; +import jdk.packager.internal.RelativeFileSet; +import jdk.packager.internal.StandardBundlerParam; +import jdk.packager.internal.Arguments; +import jdk.packager.internal.resources.mac.MacResources; +import jdk.packager.internal.builders.AbstractAppImageBuilder; + +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.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.packager.internal.StandardBundlerParam.*; +import static jdk.packager.internal.mac.MacBaseInstallerBundler.*; +import static jdk.packager.internal.mac.MacAppBundler.*; + +public class MacAppImageBuilder extends AbstractAppImageBuilder { + + private static final ResourceBundle I18N = + ResourceBundle.getBundle( + "jdk.packager.internal.resources.builders.mac.MacAppImageBuilder"); + + private static final String EXECUTABLE_NAME = "JavaAppLauncher"; + private static final String LIBRARY_NAME = "libpackager.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 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 CONFIG_ROOT = + new StandardBundlerParam<>( + I18N.getString("param.config-root.name"), + I18N.getString("param.config-root.description"), + "configRoot", + File.class, + params -> { + File configRoot = + new File(BUILD_ROOT.fetchFrom(params), "macosx"); + configRoot.mkdirs(); + return configRoot; + }, + (s, p) -> new File(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.info(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.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.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 InputStream getResourceAsStream(String name) { + return MacResources.class.getResourceAsStream(name); + } + + @Override + public void prepareApplicationFiles() throws IOException { + File f; + + // Generate PkgInfo + File pkgInfoFile = new File(contentsDir.toFile(), "PkgInfo"); + pkgInfoFile.createNewFile(); + writePkgInfo(pkgInfoFile); + + Path executable = macOSDir.resolve(getLauncherName(params)); + + try (InputStream is_launcher = getResourceAsStream("papplauncher"); + 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 launcher config + writeCfgFile(params, + new File(root.toFile(), getLauncherCfgName(params)), + "$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( + "package/macosx/" + APP_NAME.fetchFrom(params) + ".icns", + "icon", + DEFAULT_ICNS_ICON.fetchFrom(params), + icon, + VERBOSE.fetchFrom(params), + DROP_IN_RESOURCES_ROOT.fetchFrom(params)); + Files.copy(in, + resourcesDir.resolve(APP_NAME.fetchFrom(params) + ".icns")); + + // copy file association icons + for (Map fa : FILE_ASSOCIATIONS.fetchFrom(params)) { + 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 prepareServerJreFiles() 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.info(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( + "package/macosx/Runtime-Info.plist", + I18N.getString("resource.runtime-info-plist"), + TEMPLATE_RUNTIME_INFO_PLIST, + data, + VERBOSE.fetchFrom(params), + DROP_IN_RESOURCES_ROOT.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"; + } + + Map jvmProps = JVM_PROPERTIES.fetchFrom(params); + for (Map.Entry entry : jvmProps.entrySet()) { + sb.append(newline) + .append(" -D") + .append(entry.getKey()) + .append("=") + .append(entry.getValue()) + .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.info(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( + //MAC_BUNDLER_PREFIX + getConfig_InfoPlist(params).getName(), + "package/macosx/Info.plist", + I18N.getString("resource.app-info-plist"), + TEMPLATE_INFO_PLIST_LITE, + data, VERBOSE.fetchFrom(params), + DROP_IN_RESOURCES_ROOT.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); + } + +} --- /dev/null 2018-10-22 10:31:02.000000000 -0400 +++ new/src/jdk.packager/macosx/classes/jdk/packager/internal/mac/MacAppBundler.java 2018-10-22 10:30:59.800232100 -0400 @@ -0,0 +1,516 @@ +/* + * 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.packager.internal.mac; + +import jdk.packager.internal.AbstractImageBundler; +import jdk.packager.internal.BundlerParamInfo; +import jdk.packager.internal.ConfigException; +import jdk.packager.internal.EnumeratedBundlerParam; +import jdk.packager.internal.IOUtils; +import jdk.packager.internal.Log; +import jdk.packager.internal.Platform; +import jdk.packager.internal.StandardBundlerParam; +import jdk.packager.internal.Arguments; +import jdk.packager.internal.UnsupportedPlatformException; +import jdk.packager.internal.builders.mac.MacAppImageBuilder; +import jdk.packager.internal.resources.mac.MacResources; +import jdk.packager.internal.JLinkBundlerHelper; + +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.packager.internal.StandardBundlerParam.*; +import static jdk.packager.internal.mac.MacBaseInstallerBundler.*; +import jdk.packager.internal.builders.AbstractAppImageBuilder; + +public class MacAppBundler extends AbstractImageBundler { + + private static final ResourceBundle I18N = + ResourceBundle.getBundle( + "jdk.packager.internal.resources.mac.MacAppBundler"); + + public final static String MAC_BUNDLER_PREFIX = + BUNDLER_PREFIX + "macosx" + File.separator; + + 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 CONFIG_ROOT = + new StandardBundlerParam<>( + I18N.getString("param.config-root.name"), + I18N.getString("param.config-root.description"), + "configRoot", + File.class, + params -> { + File configRoot = + new File(BUILD_ROOT.fetchFrom(params), "macosx"); + configRoot.mkdirs(); + return configRoot; + }, + (s, p) -> new File(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.info(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.info(MessageFormat.format( + I18N.getString("message.icon-not-icns"), f)); + return null; + } + return f; + }, + (s, p) -> new File(s)); + + public MacAppBundler() { + super(); + baseResourceLoader = MacResources.class; + } + + 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); + } + } + } + + // to be used by chained bundlers, e.g. by EXE bundler to avoid + // skipping validation if p.type does not include "image" + public 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; + } + + private File getConfig_InfoPlist(Map params) { + return new File(CONFIG_ROOT.fetchFrom(params), "Info.plist"); + } + + private File getConfig_Icon(Map params) { + return new File(CONFIG_ROOT.fetchFrom(params), + APP_NAME.fetchFrom(params) + ".icns"); + } + + 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)); + AbstractAppImageBuilder appBuilder = new MacAppImageBuilder(p, + APP_NAME.fetchFrom(p), outputDirectory.toPath()); + File predefined = PREDEFINED_RUNTIME_IMAGE.fetchFrom(p); + if (predefined == null ) { + JLinkBundlerHelper.generateServerJre(p, appBuilder); + } else { + return predefined; + } + return rootDirectory; + } catch (IOException ex) { + Log.info(ex.toString()); + Log.verbose(ex); + return null; + } catch (Exception ex) { + Log.info("Exception: "+ex); + Log.debug(ex); + return null; + } + } + + File doAppBundle(Map p, File outputDirectory, + boolean dependentTask) { + try { + File rootDirectory = createRoot(p, outputDirectory, dependentTask, + APP_NAME.fetchFrom(p) + ".app"); + 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 (IOException ex) { + Log.info(ex.toString()); + Log.verbose(ex); + return null; + } catch (Exception ex) { + Log.info("Exception: "+ex); + Log.debug(ex); + return null; + } + } + + private File createRoot(Map p, + File outputDirectory, boolean dependentTask, String name) + 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())); + } + + // Create directory structure + File rootDirectory = new File(outputDirectory, name); + IOUtils.deleteRecursive(rootDirectory); + rootDirectory.mkdirs(); + + if (!dependentTask) { + Log.info(MessageFormat.format(I18N.getString( + "message.creating-app-bundle"), + rootDirectory.getAbsolutePath())); + } + + if (!p.containsKey(JLinkBundlerHelper.JLINK_BUILDER.getID())) { + p.put(JLinkBundlerHelper.JLINK_BUILDER.getID(), + "macapp-image-builder"); + } + return rootDirectory; + } + + public void cleanupConfigFiles(Map params) { + if (CONFIG_ROOT.fetchFrom(params) != null) { + getConfig_Icon(params).delete(); + getConfig_InfoPlist(params).delete(); + } + } + + ///////////////////////////////////////////////////////////////////////// + // Implement Bundler + ///////////////////////////////////////////////////////////////////////// + + @Override + public String getName() { + return I18N.getString("bundler.name"); + } + + @Override + public String getDescription() { + return I18N.getString("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; + } + +} --- /dev/null 2018-10-22 10:31:10.000000000 -0400 +++ new/src/jdk.packager/macosx/classes/jdk/packager/internal/mac/MacAppStoreBundler.java 2018-10-22 10:31:08.495206400 -0400 @@ -0,0 +1,425 @@ +/* + * 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.packager.internal.mac; + +import jdk.packager.internal.BundlerParamInfo; +import jdk.packager.internal.StandardBundlerParam; +import jdk.packager.internal.Arguments; +import jdk.packager.internal.Log; +import jdk.packager.internal.ConfigException; +import jdk.packager.internal.IOUtils; +import jdk.packager.internal.Platform; +import jdk.packager.internal.UnsupportedPlatformException; +import jdk.packager.internal.builders.mac.MacAppImageBuilder; +import jdk.packager.internal.resources.mac.MacResources; + +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.packager.internal.StandardBundlerParam.*; +import static jdk.packager.internal.mac.MacAppBundler.*; + +public class MacAppStoreBundler extends MacBaseInstallerBundler { + + private static final ResourceBundle I18N = + ResourceBundle.getBundle( + "jdk.packager.internal.resources.mac.MacAppStoreBundler"); + + 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.info(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.info(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); + + public MacAppStoreBundler() { + super(); + baseResourceLoader = MacResources.class; + } + + //@Override + public File bundle(Map p, File outdir) { + Log.info(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.info("App Store Ready Bundle failed : " + ex.getMessage()); + ex.printStackTrace(); + Log.debug(ex); + return null; + } finally { + try { + if (appImageDir != null && + PREDEFINED_APP_IMAGE.fetchFrom(p) == null && + (PREDEFINED_RUNTIME_IMAGE.fetchFrom(p) == null || + !Arguments.CREATE_JRE_INSTALLER.fetchFrom(p)) && + !Log.isDebug()) { + IOUtils.deleteRecursive(appImageDir); + } else if (appImageDir != null) { + Log.info(MessageFormat.format(I18N.getString( + "mesasge.intermediate-bundle-location"), + appImageDir.getAbsolutePath())); + } + + //cleanup + cleanupConfigFiles(p); + } catch (IOException ex) { + //noinspection ReturnInsideFinallyBlock + Log.debug(ex.getMessage()); + return null; + } + } + } + + protected void cleanupConfigFiles(Map params) { + if (getConfig_Entitlements(params) != null) { + getConfig_Entitlements(params).delete(); + } + if (getConfig_Inherit_Entitlements(params) != null) { + getConfig_Inherit_Entitlements(params).delete(); + } + if (PREDEFINED_APP_IMAGE.fetchFrom(params) == null) { + APP_BUNDLER.fetchFrom(params).cleanupConfigFiles(params); + } + } + + 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), + DROP_IN_RESOURCES_ROOT.fetchFrom(params)); + } else { + fetchResource(getEntitlementsFileName(params), + I18N.getString("resource.mac-app-store-entitlements"), + entitlements, + getConfig_Entitlements(params), + VERBOSE.fetchFrom(params), + DROP_IN_RESOURCES_ROOT.fetchFrom(params)); + } + fetchResource(getInheritEntitlementsFileName(params), + I18N.getString("resource.mac-app-store-inherit-entitlements"), + DEFAULT_INHERIT_ENTITLEMENTS, + getConfig_Inherit_Entitlements(params), + VERBOSE.fetchFrom(params), + DROP_IN_RESOURCES_ROOT.fetchFrom(params)); + } + + private String getEntitlementsFileName(Map params) { + return MAC_BUNDLER_PREFIX+ APP_NAME.fetchFrom(params) + ".entitlements"; + } + + private String getInheritEntitlementsFileName( + Map params) { + return MAC_BUNDLER_PREFIX + APP_NAME.fetchFrom(params) + + "_Inherit.entitlements"; + } + + + /////////////////////////////////////////////////////////////////////// + // Implement Bundler + /////////////////////////////////////////////////////////////////////// + + @Override + public String getName() { + return I18N.getString("bundler.name"); + } + + @Override + public String getDescription() { + return I18N.getString("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; + } +} --- /dev/null 2018-10-22 10:31:17.000000000 -0400 +++ new/src/jdk.packager/macosx/classes/jdk/packager/internal/mac/MacBaseInstallerBundler.java 2018-10-22 10:31:15.312756000 -0400 @@ -0,0 +1,247 @@ +/* + * 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.packager.internal.mac; + +import jdk.packager.internal.AbstractBundler; +import jdk.packager.internal.BundlerParamInfo; +import jdk.packager.internal.StandardBundlerParam; +import jdk.packager.internal.Arguments; +import jdk.packager.internal.Log; +import jdk.packager.internal.ConfigException; +import jdk.packager.internal.IOUtils; +import jdk.packager.internal.Platform; +import jdk.packager.internal.UnsupportedPlatformException; + +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.packager.internal.StandardBundlerParam.*; + +public abstract class MacBaseInstallerBundler extends AbstractBundler { + + private static final ResourceBundle I18N = + ResourceBundle.getBundle( + "jdk.packager.internal.resources.mac.MacBaseInstallerBundler"); + + // 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 CONFIG_ROOT = + new StandardBundlerParam<>( + I18N.getString("param.config-root.name"), + I18N.getString("param.config-root.description"), + "configRoot", + File.class, + params -> { + File imagesRoot = + new File(BUILD_ROOT.fetchFrom(params), "macosx"); + imagesRoot.mkdirs(); + return imagesRoot; + }, + (s, p) -> null); + + 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).doValidate(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.info("Did not find a key matching '" + key + "'"); + return null; + } + String matchedKey = m.group(1); + if (m.find()) { + Log.info("Found more than one key matching '" + key + "'"); + return null; + } + Log.debug("Using key '" + matchedKey + "'"); + return matchedKey; + } catch (IOException ioe) { + Log.verbose(ioe); + return null; + } + } +} --- /dev/null 2018-10-22 10:31:24.000000000 -0400 +++ new/src/jdk.packager/macosx/classes/jdk/packager/internal/mac/MacCertificate.java 2018-10-22 10:31:22.124533000 -0400 @@ -0,0 +1,166 @@ +/* + * 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.packager.internal.mac; + +import jdk.packager.internal.IOUtils; +import jdk.packager.internal.Log; + +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 public class MacCertificate { + private final String certificate; + private final boolean verbose; + + public MacCertificate(String certificate) { + this.certificate = certificate; + this.verbose = false; + } + + public MacCertificate(String certificate, boolean verbose) { + this.certificate = certificate; + this.verbose = verbose; + } + + public 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; + } +} --- /dev/null 2018-10-22 10:31:31.000000000 -0400 +++ new/src/jdk.packager/macosx/classes/jdk/packager/internal/mac/MacDmgBundler.java 2018-10-22 10:31:28.997583900 -0400 @@ -0,0 +1,576 @@ +/* + * 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.packager.internal.mac; + +import jdk.packager.internal.*; +import jdk.packager.internal.IOUtils; +import jdk.packager.internal.resources.mac.MacResources; +import jdk.packager.internal.Arguments; + +import java.io.*; +import java.nio.file.Files; +import java.text.MessageFormat; +import java.util.*; + +import static jdk.packager.internal.StandardBundlerParam.*; + +public class MacDmgBundler extends MacBaseInstallerBundler { + + private static final ResourceBundle I18N = + ResourceBundle.getBundle( + "jdk.packager.internal.resources.mac.MacDmgBundler"); + + 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 MacDmgBundler() { + super(); + baseResourceLoader = MacResources.class; + } + + public File bundle(Map params, File outdir) { + Log.info(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.info(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; + } finally { + try { + if (appImageDir != null && + PREDEFINED_APP_IMAGE.fetchFrom(params) == null && + (PREDEFINED_RUNTIME_IMAGE.fetchFrom(params) == null || + !Arguments.CREATE_JRE_INSTALLER.fetchFrom(params)) && + !Log.isDebug()) { + IOUtils.deleteRecursive(appImageDir); + } else if (appImageDir != null) { + Log.info(MessageFormat.format(I18N.getString( + "message.intermediate-image-location"), + appImageDir.getAbsolutePath())); + } + + //cleanup + cleanupConfigFiles(params); + } catch (IOException ex) { + Log.debug(ex); + //noinspection ReturnInsideFinallyBlock + return null; + } + } + } + + //remove + protected void cleanupConfigFiles(Map params) { + if (getConfig_VolumeBackground(params) != null) { + getConfig_VolumeBackground(params).delete(); + } + if (getConfig_VolumeIcon(params) != null) { + getConfig_VolumeIcon(params).delete(); + } + if (getConfig_VolumeScript(params) != null) { + getConfig_VolumeScript(params).delete(); + } + if (getConfig_Script(params) != null) { + getConfig_Script(params).delete(); + } + if (getConfig_LicenseFile(params) != null) { + getConfig_LicenseFile(params).delete(); + } + APP_BUNDLER.fetchFrom(params).cleanupConfigFiles(params); + } + + 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( + MacAppBundler.MAC_BUNDLER_PREFIX + dmgSetup.getName(), + I18N.getString("resource.dmg-setup-script"), + DEFAULT_DMG_SETUP_SCRIPT, data, VERBOSE.fetchFrom(p), + DROP_IN_RESOURCES_ROOT.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 { + File licFile = null; + + List licFiles = LICENSE_FILE.fetchFrom(params); + if (licFiles.isEmpty()) { + return; + } + String licFileStr = licFiles.get(0); + + for (RelativeFileSet rfs : APP_RESOURCES_LIST.fetchFrom(params)) { + if (rfs.contains(licFileStr)) { + licFile = new File(rfs.getBaseDirectory(), licFileStr); + break; + } + } + + if (licFile == null) { + // this is NPE protection, + // validate should have already caught it's absence + Log.error("Licence file is null"); + return; + } + + 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( + MacAppBundler.MAC_BUNDLER_PREFIX + + getConfig_LicenseFile(params).getName(), + I18N.getString("resource.license-setup"), + DEFAULT_LICENSE_PLIST, data, VERBOSE.fetchFrom(params), + DROP_IN_RESOURCES_ROOT.fetchFrom(params))); + w.close(); + + } catch (IOException ex) { + Log.verbose(ex); + } + } + + private boolean prepareConfigFiles(Map params) + throws IOException { + File bgTarget = getConfig_VolumeBackground(params); + fetchResource(MacAppBundler.MAC_BUNDLER_PREFIX + bgTarget.getName(), + I18N.getString("resource.dmg-background"), + DEFAULT_BACKGROUND_IMAGE, + bgTarget, + VERBOSE.fetchFrom(params), + DROP_IN_RESOURCES_ROOT.fetchFrom(params)); + + File iconTarget = getConfig_VolumeIcon(params); + if (MacAppBundler.ICON_ICNS.fetchFrom(params) == null || + !MacAppBundler.ICON_ICNS.fetchFrom(params).exists()) { + fetchResource( + MacAppBundler.MAC_BUNDLER_PREFIX + iconTarget.getName(), + I18N.getString("resource.volume-icon"), + TEMPLATE_BUNDLE_ICON, + iconTarget, + VERBOSE.fetchFrom(params), + DROP_IN_RESOURCES_ROOT.fetchFrom(params)); + } else { + fetchResource( + MacAppBundler.MAC_BUNDLER_PREFIX + iconTarget.getName(), + I18N.getString("resource.volume-icon"), + MacAppBundler.ICON_ICNS.fetchFrom(params), + iconTarget, + VERBOSE.fetchFrom(params), + DROP_IN_RESOURCES_ROOT.fetchFrom(params)); + } + + + fetchResource(MacAppBundler.MAC_BUNDLER_PREFIX + + getConfig_Script(params).getName(), + I18N.getString("resource.post-install-script"), + (String) null, + getConfig_Script(params), + VERBOSE.fetchFrom(params), + DROP_IN_RESOURCES_ROOT.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.info(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.info(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("bundler.name"); + } + + @Override + public String getDescription() { + return I18N.getString("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); + + // validate license file, if used, exists in the proper place + if (params.containsKey(LICENSE_FILE.getID())) { + List appResourcesList = + APP_RESOURCES_LIST.fetchFrom(params); + for (String license : LICENSE_FILE.fetchFrom(params)) { + boolean found = false; + for (RelativeFileSet appResources : appResourcesList) { + found = found || appResources.contains(license); + } + if (!found) { + throw new ConfigException( + I18N.getString("error.license-missing"), + MessageFormat.format(I18N.getString( + "error.license-missing.advice"), license)); + } + } + } + + 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; + } +} --- /dev/null 2018-10-22 10:31:37.000000000 -0400 +++ new/src/jdk.packager/macosx/classes/jdk/packager/internal/mac/MacPkgBundler.java 2018-10-22 10:31:35.191101500 -0400 @@ -0,0 +1,603 @@ +/* + * 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.packager.internal.mac; + +import jdk.packager.internal.BundlerParamInfo; +import jdk.packager.internal.StandardBundlerParam; +import jdk.packager.internal.Log; +import jdk.packager.internal.ConfigException; +import jdk.packager.internal.IOUtils; +import jdk.packager.internal.Platform; +import jdk.packager.internal.RelativeFileSet; +import jdk.packager.internal.UnsupportedPlatformException; +import jdk.packager.internal.resources.mac.MacResources; +import jdk.packager.internal.builders.mac.MacAppImageBuilder; +import jdk.packager.internal.Arguments; + +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.packager.internal.StandardBundlerParam.*; +import static + jdk.packager.internal.mac.MacBaseInstallerBundler.SIGNING_KEYCHAIN; +import static + jdk.packager.internal.mac.MacBaseInstallerBundler.SIGNING_KEY_USER; + +public class MacPkgBundler extends MacBaseInstallerBundler { + + private static final ResourceBundle I18N = ResourceBundle.getBundle( + "jdk.packager.internal.resources.mac.MacPkgBundler"); + + public final static String MAC_BUNDLER_PREFIX = + BUNDLER_PREFIX + "macosx" + File.separator; + + 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.info(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 MacPkgBundler() { + super(); + baseResourceLoader = MacResources.class; + } + + public File bundle(Map params, File outdir) { + Log.info(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.info(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; + } finally { + try { + if (appImageDir != null && + PREDEFINED_APP_IMAGE.fetchFrom(params) == null && + (PREDEFINED_RUNTIME_IMAGE.fetchFrom(params) == null || + !Arguments.CREATE_JRE_INSTALLER.fetchFrom(params)) && + !Log.isDebug()) { + IOUtils.deleteRecursive(appImageDir); + } else if (appImageDir != null) { + Log.info(MessageFormat.format(I18N.getString( + "message.intermediate-image-location"), + appImageDir.getAbsolutePath())); + } + + // cleanup + cleanupConfigFiles(params); + } catch (IOException ex) { + Log.debug(ex); + // noinspection ReturnInsideFinallyBlock + 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 void cleanupPackagesFiles(Map params) { + if (getPackages_AppPackage(params) != null) { + getPackages_AppPackage(params).delete(); + } + if (getPackages_DaemonPackage(params) != null) { + getPackages_DaemonPackage(params).delete(); + } + } + + 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 void cleanupConfigFiles(Map params) { + if (getConfig_DistributionXMLFile(params) != null) { + getConfig_DistributionXMLFile(params).delete(); + } + if (getConfig_BackgroundImage(params) != null) { + getConfig_BackgroundImage(params).delete(); + } + } + + 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(MAC_BUNDLER_PREFIX + + getScripts_PreinstallFile(params).getName(), + I18N.getString("resource.pkg-preinstall-script"), + TEMPLATE_PREINSTALL_SCRIPT, + data, + VERBOSE.fetchFrom(params), + DROP_IN_RESOURCES_ROOT.fetchFrom(params)); + w.write(content); + w.close(); + getScripts_PreinstallFile(params).setExecutable(true, false); + + w = new BufferedWriter( + new FileWriter(getScripts_PostinstallFile(params))); + content = preprocessTextResource(MAC_BUNDLER_PREFIX + + getScripts_PostinstallFile(params).getName(), + I18N.getString("resource.pkg-postinstall-script"), + TEMPLATE_POSTINSTALL_SCRIPT, + data, + VERBOSE.fetchFrom(params), + DROP_IN_RESOURCES_ROOT.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(""); + + if (!LICENSE_FILE.fetchFrom(params).isEmpty()) { + File licFile = null; + + List licFiles = LICENSE_FILE.fetchFrom(params); + if (licFiles.isEmpty()) { + return; + } + String licFileStr = licFiles.get(0); + + for (RelativeFileSet rfs : APP_RESOURCES_LIST.fetchFrom(params)) { + if (rfs.contains(licFileStr)) { + licFile = new File(rfs.getBaseDirectory(), licFileStr); + break; + } + } + + // this is NPE protection, validate should have caught it's absence + // so we don't complain or throw an error + if (licFile != null) { + out.println(""); + } + } + + /* + * Note that the content of the distribution file + * below is generated by productbuild --synthesize + */ + + String appId = getAppIdentifier(params); + String daemonId = getDaemonIdentifier(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(MacAppBundler.MAC_BUNDLER_PREFIX + imageTarget.getName(), + I18N.getString("resource.pkg-background-image"), + DEFAULT_BACKGROUND_IMAGE, + imageTarget, + VERBOSE.fetchFrom(params), + DROP_IN_RESOURCES_ROOT.fetchFrom(params)); + + prepareDistributionXMLFile(params); + + fetchResource(MacAppBundler.MAC_BUNDLER_PREFIX + + getConfig_Script(params).getName(), + I18N.getString("resource.post-install-script"), + (String) null, + getConfig_Script(params), + VERBOSE.fetchFrom(params), + DROP_IN_RESOURCES_ROOT.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.info(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; + } finally { + cleanupPackagesFiles(params); + cleanupConfigFiles(params); + } + } + + ////////////////////////////////////////////////////////////////////////// + // Implement Bundler + ////////////////////////////////////////////////////////////////////////// + + @Override + public String getName() { + return I18N.getString("bundler.name"); + } + + @Override + public String getDescription() { + return I18N.getString("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); + + // validate license file, if used, exists in the proper place + if (params.containsKey(LICENSE_FILE.getID())) { + List appResourcesList = + APP_RESOURCES_LIST.fetchFrom(params); + for (String license : LICENSE_FILE.fetchFrom(params)) { + boolean found = false; + for (RelativeFileSet appResources : appResourcesList) { + found = found || appResources.contains(license); + } + if (!found) { + throw new ConfigException( + I18N.getString("error.license-missing"), + MessageFormat.format( + I18N.getString("error.license-missing.advice"), + license)); + } + } + } + + // 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; + } + +} --- /dev/null 2018-10-22 10:31:45.000000000 -0400 +++ new/src/jdk.packager/macosx/classes/jdk/packager/internal/resources/builders/mac/MacAppImageBuilder.properties 2018-10-22 10:31:42.866695100 -0400 @@ -0,0 +1,74 @@ +# +# 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. +# +# + +bundler.name=Mac Application Image +bundler.description=A Directory based image of a mac Application with an optionally co-bundled JRE. Used as a base for the Installer bundlers + +param.icon-icns.name=.icns Icon +param.icon-icns.description= Icon for the application, in ICNS format. + +param.config-root.name=Configuration Root Name +param.config-root.description=The root directory of COnfiguration files. + +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.default-icon-icns=Default Icon +param.default-icon-icns.description=The Default Icon for when a user does not specify an icns file. + +param.sign-bundle.name=Sign Bundle +param.sign-bundle.description=Request that the bundle be signed. + +resource.app-info-plist=Application Info.plist +resource.runtime-info-plist=Java Runtime Info.plist + +message.bundle-name-too-long-warning={0} is set to ''{1}'', which is longer than 16 characters. For a better Mac experience consider shortening it. +message.null-classpath=Null app resources? +message.preparing-info-plist=Preparing Info.plist\: {0} +message.icon-not-icns= The specified icon "{0}" is not an ICNS file and will not be used. The default icon will be used in it's place. +message.version-string-too-many-components=Version sting may have between 1 and 3 numbers: 1, 1.2, 1.2.3. +message.version-string-first-number-not-zero=The first number in a CFBundleVersion cannot be zero or negative. +message.version-string-no-negative-numbers=Negative numbers are not allowed in version strings. +message.version-string-numbers-only=Version strings can consist of only numbers and up to two dots. +message.creating-association-with-null-extension=Creating association with null extension. + +message.ignoring.symlink=Warning: codesign is skipping the symlink {0} +message.keychain.error=Error: unable to get keychain list. --- /dev/null 2018-10-22 10:31:51.000000000 -0400 +++ new/src/jdk.packager/macosx/classes/jdk/packager/internal/resources/builders/mac/MacAppImageBuilder_ja.properties 2018-10-22 10:31:49.862854200 -0400 @@ -0,0 +1,71 @@ +# +# 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. +# +# + +bundler.name=Mac\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u30FB\u30A4\u30E1\u30FC\u30B8 +bundler.description=\u30AA\u30D7\u30B7\u30E7\u30F3\u3067JRE\u304C\u30D0\u30F3\u30C9\u30EB\u3055\u308C\u3066\u3044\u308BMac\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u306E\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u30FB\u30D9\u30FC\u30B9\u306E\u30A4\u30E1\u30FC\u30B8\u3002\u30A4\u30F3\u30B9\u30C8\u30FC\u30E9\u30FB\u30D0\u30F3\u30C9\u30EB\u306E\u30D9\u30FC\u30B9\u3068\u3057\u3066\u4F7F\u7528\u3055\u308C\u307E\u3059\u3002 + +param.icon-icns.name=.icns\u30A2\u30A4\u30B3\u30F3 +param.icon-icns.description= ICNS\u5F62\u5F0F\u3067\u306E\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u306E\u30A2\u30A4\u30B3\u30F3\u3002 + +param.config-root.name=Configuration Root Name +param.config-root.description=The root directory of COnfiguration files. + +param.configure-launcher-in-plist=Info.plist\u3067\u306E\u30E9\u30F3\u30C1\u30E3\u306E\u69CB\u6210 +param.configure-launcher-in-plist.description=Info.plist\u3067\u306E\u30E9\u30F3\u30C1\u30E3\u306E\u69CB\u6210\u306B\u306F\u30EC\u30AC\u30B7\u30FC\u30FB\u30E1\u30BD\u30C3\u30C9\u3092\u4F7F\u7528\u3059\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059\u3002 + +param.category-name=\u30AB\u30C6\u30B4\u30EA +param.category-name.description=Mac App Store\u306E\u30AB\u30C6\u30B4\u30EA\u3002\u30AD\u30FC\u306F\u30E6\u30FC\u30B6\u30FC\u306B\u8868\u793A\u3055\u308C\u308B\u6587\u5B57\u5217\u3067\u3001\u5024\u306F\u30AB\u30C6\u30B4\u30EA\u306EID\u3067\u3059\u3002 + +param.cfbundle-name.name=CFBundleName +param.cfbundle-name.description=\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u304C\u30E1\u30CB\u30E5\u30FC\u30FB\u30D0\u30FC\u306B\u8868\u793A\u3055\u308C\u308B\u969B\u306E\u540D\u524D\u3002\u3053\u308C\u306F\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u540D\u3068\u306F\u7570\u306A\u308B\u5834\u5408\u304C\u3042\u308A\u307E\u3059\u3002\u3053\u306E\u540D\u524D\u306F16\u6587\u5B57\u672A\u6E80\u3068\u3057\u3001\u30E1\u30CB\u30E5\u30FC\u30FB\u30D0\u30FC\u3068\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u306E\u60C5\u5831\u30A6\u30A3\u30F3\u30C9\u30A6\u3067\u306E\u8868\u793A\u306B\u9069\u3057\u305F\u3082\u306E\u306B\u3059\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059\u3002 + +param.cfbundle-identifier.name=CFBundleIdentifier +param.cfbundle-identifier.description=MacOSX(\u304A\u3088\u3073Mac App Store)\u306E\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u3092\u4E00\u610F\u306B\u8B58\u5225\u3059\u308B\u8B58\u5225\u5B50\u3002\u82F1\u6570\u5B57(A-Z\u3001a-z\u30010-9)\u3001\u30CF\u30A4\u30D5\u30F3(-)\u304A\u3088\u3073\u30D4\u30EA\u30AA\u30C9(.)\u306E\u307F\u3092\u4F7F\u7528\u3067\u304D\u307E\u3059\u3002 + +param.cfbundle-version.name=CFBundleVersion +param.cfbundle-version.description=\u30B3\u30F3\u30D4\u30E5\u30FC\u30BF\u3067\u8AAD\u53D6\u308A\u53EF\u80FD\u306ACFBundle\u306E\u30D0\u30FC\u30B8\u30E7\u30F3\u3002\u6570\u5B57\u304A\u3088\u3073\u30BC\u30ED\u304B\u30892\u3064\u307E\u3067\u306E\u30C9\u30C3\u30C8\u306E\u307F\u3092\u542B\u3081\u308B\u3053\u3068\u304C\u3067\u304D\u307E\u3059("1.8.1"\u3001"100"\u306A\u3069)\u3002 + +param.bundle-id-signing-prefix.name=\u30D0\u30F3\u30C9\u30EB\u7F72\u540D\u63A5\u982D\u8F9E +param.bundle-id-signing-prefix.description=\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u30FB\u30D0\u30F3\u30C9\u30EB\u306B\u7F72\u540D\u3059\u308B\u969B\u3001\u7F72\u540D\u3059\u308B\u5FC5\u8981\u306E\u3042\u308B\u3001\u65E2\u5B58\u306ECFBundleIdentifier\u3092\u6301\u305F\u306A\u3044\u3059\u3079\u3066\u306E\u30B3\u30F3\u30DD\u30FC\u30CD\u30F3\u30C8\u306B\u3053\u306E\u5024\u304C\u63A5\u982D\u8F9E\u3068\u3057\u3066\u4ED8\u3051\u3089\u308C\u307E\u3059\u3002 + +param.default-icon-icns=\u30C7\u30D5\u30A9\u30EB\u30C8\u30FB\u30A2\u30A4\u30B3\u30F3 +param.default-icon-icns.description=\u30E6\u30FC\u30B6\u30FC\u304C\u30A2\u30A4\u30B3\u30F3\u30FB\u30D5\u30A1\u30A4\u30EB\u3092\u6307\u5B9A\u3057\u306A\u3044\u5834\u5408\u306E\u30C7\u30D5\u30A9\u30EB\u30C8\u30FB\u30A2\u30A4\u30B3\u30F3\u3002 + +resource.app-info-plist=\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u306EInfo.plist +resource.runtime-info-plist=Java\u30E9\u30F3\u30BF\u30A4\u30E0\u306EInfo.plist + +message.bundle-name-too-long-warning={0}\u304C16\u6587\u5B57\u3092\u8D85\u3048\u308B''{1}''\u306B\u8A2D\u5B9A\u3055\u308C\u3066\u3044\u307E\u3059\u3002Mac\u3067\u306E\u64CD\u4F5C\u6027\u3092\u3088\u308A\u826F\u304F\u3059\u308B\u305F\u3081\u306B\u77ED\u304F\u3059\u308B\u3053\u3068\u3092\u691C\u8A0E\u3057\u3066\u304F\u3060\u3055\u3044\u3002 +message.null-classpath=Null\u306E\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u30FB\u30EA\u30BD\u30FC\u30B9\u3067\u3059\u304B\u3002 +message.preparing-info-plist=Info.plist\u3092\u6E96\u5099\u3057\u3066\u3044\u307E\u3059: {0} +message.icon-not-icns= \u6307\u5B9A\u3057\u305F\u30A2\u30A4\u30B3\u30F3"{0}"\u306FICNS\u30D5\u30A1\u30A4\u30EB\u3067\u306F\u306A\u304F\u3001\u4F7F\u7528\u3055\u308C\u307E\u305B\u3093\u3002\u30C7\u30D5\u30A9\u30EB\u30C8\u30FB\u30A2\u30A4\u30B3\u30F3\u304C\u305D\u306E\u4F4D\u7F6E\u306B\u4F7F\u7528\u3055\u308C\u307E\u3059\u3002 +message.version-string-too-many-components=\u30D0\u30FC\u30B8\u30E7\u30F3\u6587\u5B57\u5217\u306B\u306F\u30011\u30011.2\u30011.2.3\u306A\u30691\u304B\u30893\u306E\u6570\u5B57\u3092\u4F7F\u7528\u3067\u304D\u307E\u3059\u3002 +message.version-string-first-number-not-zero=CFBundleVersion\u306E\u6700\u521D\u306E\u6570\u5B57\u306F\u3001\u30BC\u30ED\u307E\u305F\u306F\u8CA0\u306E\u5024\u306B\u3067\u304D\u307E\u305B\u3093\u3002 +message.version-string-no-negative-numbers=\u30D0\u30FC\u30B8\u30E7\u30F3\u6587\u5B57\u5217\u306B\u8CA0\u306E\u6570\u306F\u8A31\u53EF\u3055\u308C\u307E\u305B\u3093\u3002 +message.version-string-numbers-only=\u30D0\u30FC\u30B8\u30E7\u30F3\u6587\u5B57\u5217\u306F\u3001\u6570\u5B57\u30682\u3064\u307E\u3067\u306E\u30C9\u30C3\u30C8\u3067\u306E\u307F\u69CB\u6210\u3067\u304D\u307E\u3059\u3002 +message.creating-association-with-null-extension=null\u62E1\u5F35\u5B50\u3068\u306E\u95A2\u9023\u4ED8\u3051\u3092\u4F5C\u6210\u3057\u3066\u3044\u307E\u3059\u3002 + +message.ignoring.symlink=\u8B66\u544A: codesign\u304Csymlink {0}\u3092\u30B9\u30AD\u30C3\u30D7\u3057\u3066\u3044\u307E\u3059 +message.keychain.error=Error: unable to get keychain list. --- /dev/null 2018-10-22 10:31:58.000000000 -0400 +++ new/src/jdk.packager/macosx/classes/jdk/packager/internal/resources/builders/mac/MacAppImageBuilder_zh_CN.properties 2018-10-22 10:31:55.993968600 -0400 @@ -0,0 +1,72 @@ +# +# 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. +# +# + +bundler.name=Mac \u5E94\u7528\u7A0B\u5E8F\u6620\u50CF +bundler.description=\u4E00\u4E2A\u57FA\u4E8E\u76EE\u5F55\u7684 mac \u5E94\u7528\u7A0B\u5E8F\u6620\u50CF, \u53EF\u4EE5\u9009\u62E9\u6027\u5730\u5E26\u6709\u5171\u540C\u6253\u5305\u7684 JRE\u3002\u7528\u4F5C\u5B89\u88C5\u7A0B\u5E8F\u6253\u5305\u7A0B\u5E8F\u7684\u57FA\u7840 + +param.icon-icns.name=.icns \u56FE\u6807 +param.icon-icns.description= \u5E94\u7528\u7A0B\u5E8F\u7684\u56FE\u6807, \u91C7\u7528 ICNS \u683C\u5F0F\u3002 + +param.config-root.name=Configuration Root Name +param.config-root.description=The root directory of Configuration files. + +param.configure-launcher-in-plist=\u5728 Info.plist \u4E2D\u914D\u7F6E\u542F\u52A8\u7A0B\u5E8F +param.configure-launcher-in-plist.description=\u662F\u5426\u5E94\u4F7F\u7528\u5728 Info.plist \u4E2D\u914D\u7F6E\u542F\u52A8\u7A0B\u5E8F\u7684\u65E7\u65B9\u6CD5\u3002 + +param.category-name=\u7C7B\u522B +param.category-name.description=Mac App Store \u7C7B\u522B\u3002\u8BF7\u6CE8\u610F, \u952E\u662F\u5411\u7528\u6237\u663E\u793A\u7684\u5B57\u7B26\u4E32, \u503C\u662F\u7C7B\u522B\u7684 ID\u3002 + +param.cfbundle-name.name=CFBundleName +param.cfbundle-name.description=\u5E94\u7528\u7A0B\u5E8F\u5728\u83DC\u5355\u680F\u4E2D\u7684\u663E\u793A\u540D\u79F0\u3002\u53EF\u4EE5\u4E0E\u5E94\u7528\u7A0B\u5E8F\u540D\u79F0\u4E0D\u540C\u3002\u6B64\u540D\u79F0\u7684\u957F\u5EA6\u5E94\u5C11\u4E8E 16 \u4E2A\u5B57\u7B26, \u5E76\u4E14\u5E94\u9002\u5408\u5728\u83DC\u5355\u680F\u548C\u5E94\u7528\u7A0B\u5E8F\u7684\u201C\u4FE1\u606F\u201D\u7A97\u53E3\u4E2D\u663E\u793A\u3002 + +param.cfbundle-identifier.name=CFBundleIdentifier +param.cfbundle-identifier.description=\u552F\u4E00\u6807\u8BC6 MacOSX \u5E94\u7528\u7A0B\u5E8F (\u5728 Mac App Store \u4E0A) \u7684\u6807\u8BC6\u7B26\u3002\u53EA\u80FD\u4F7F\u7528\u5B57\u6BCD\u6570\u5B57 (A-Z,a-z,0-9), \u8FDE\u5B57\u7B26 (-) \u548C\u53E5\u70B9 (.) \u5B57\u7B26\u3002 + +param.cfbundle-version.name=CFBundleVersion +param.cfbundle-version.description=CFBundle \u7684\u8BA1\u7B97\u673A\u53EF\u8BFB\u7248\u672C\u3002\u53EA\u80FD\u5305\u542B\u6570\u5B57\u4EE5\u53CA\u96F6\u5230\u4E24\u4E2A\u70B9, \u4F8B\u5982 "1.8.1" \u6216 "100"\u3002 + +param.bundle-id-signing-prefix.name=\u5305\u7B7E\u540D\u524D\u7F00 +param.bundle-id-signing-prefix.description=\u5BF9\u5E94\u7528\u7A0B\u5E8F\u5305\u8FDB\u884C\u7B7E\u540D\u65F6, \u6B64\u503C\u5C06\u4F5C\u4E3A\u524D\u7F00\u6DFB\u52A0\u5230\u9700\u8981\u7B7E\u540D\u4F46\u4E0D\u5177\u6709\u73B0\u6709 CFBundleIdentifier \u7684\u6240\u6709\u7EC4\u4EF6\u4E2D\u3002 + +param.default-icon-icns=\u9ED8\u8BA4\u56FE\u6807 +param.default-icon-icns.description=\u7528\u6237\u672A\u6307\u5B9A icns \u6587\u4EF6\u65F6\u4F7F\u7528\u7684\u9ED8\u8BA4\u56FE\u6807\u3002 + +resource.app-info-plist=\u5E94\u7528\u7A0B\u5E8F Info.plist +resource.runtime-info-plist=Java \u8FD0\u884C\u65F6 Info.plist + +message.bundle-name-too-long-warning={0}\u5DF2\u8BBE\u7F6E\u4E3A ''{1}'', \u5176\u957F\u5EA6\u8D85\u8FC7\u4E86 16 \u4E2A\u5B57\u7B26\u3002\u4E3A\u4E86\u83B7\u5F97\u66F4\u597D\u7684 Mac \u4F53\u9A8C, \u8BF7\u8003\u8651\u5C06\u5176\u7F29\u77ED\u3002 +message.null-classpath=\u662F\u5426\u4E3A\u7A7A\u5E94\u7528\u7A0B\u5E8F\u8D44\u6E90? +message.preparing-info-plist=\u6B63\u5728\u51C6\u5907 Info.plist: {0} +message.icon-not-icns= \u6307\u5B9A\u7684\u56FE\u6807 "{0}" \u4E0D\u662F ICNS \u6587\u4EF6, \u4E0D\u4F1A\u4F7F\u7528\u3002\u5C06\u4F7F\u7528\u9ED8\u8BA4\u56FE\u6807\u4EE3\u66FF\u3002 +message.version-string-too-many-components=\u7248\u672C\u5B57\u7B26\u4E32\u53EF\u4EE5\u5305\u542B 1 \u5230 3 \u4E2A\u6570\u5B57: 1, 1.2, 1.2.3\u3002 +message.version-string-first-number-not-zero=CFBundleVersion \u4E2D\u7684\u7B2C\u4E00\u4E2A\u6570\u5B57\u4E0D\u80FD\u4E3A\u96F6\u6216\u8D1F\u6570\u3002 +message.version-string-no-negative-numbers=\u7248\u672C\u5B57\u7B26\u4E32\u4E2D\u4E0D\u5141\u8BB8\u4F7F\u7528\u8D1F\u6570\u3002 +message.version-string-numbers-only=\u7248\u672C\u5B57\u7B26\u4E32\u53EA\u80FD\u5305\u542B\u6570\u5B57\u548C\u6700\u591A\u4E24\u4E2A\u70B9\u3002 +message.creating-association-with-null-extension=\u6B63\u5728\u4F7F\u7528\u7A7A\u6269\u5C55\u540D\u521B\u5EFA\u5173\u8054\u3002 + +message.ignoring.symlink=\u8B66\u544A: codesign \u6B63\u5728\u8DF3\u8FC7\u7B26\u53F7\u94FE\u63A5 {0} +message.keychain.error=Error: unable to get keychain list. + --- /dev/null 2018-10-22 10:32:06.000000000 -0400 +++ new/src/jdk.packager/macosx/classes/jdk/packager/internal/resources/mac/DMGsetup.scpt 2018-10-22 10:32:04.093784100 -0400 @@ -0,0 +1,15 @@ +tell application "Finder" + tell disk "DEPLOY_ACTUAL_VOLUME_NAME" + open + set current view of container window to icon view + set toolbar visible of container window to false + set statusbar visible of container window to false + + set the bounds of container window to {400, 100, 917, 370} + + set theViewOptions to the icon view options of container window + set arrangement of theViewOptions to not arranged + set icon size of theViewOptions to 128 + end tell +end tell + Binary files /dev/null and new/src/jdk.packager/macosx/classes/jdk/packager/internal/resources/mac/GenericApp.icns differ Binary files /dev/null and new/src/jdk.packager/macosx/classes/jdk/packager/internal/resources/mac/GenericAppHiDPI.icns differ --- /dev/null 2018-10-22 10:32:25.000000000 -0400 +++ new/src/jdk.packager/macosx/classes/jdk/packager/internal/resources/mac/Info-lite.plist.template 2018-10-22 10:32:23.672788100 -0400 @@ -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 + + --- /dev/null 2018-10-22 10:32:33.000000000 -0400 +++ new/src/jdk.packager/macosx/classes/jdk/packager/internal/resources/mac/Info.plist.template 2018-10-22 10:32:31.317180100 -0400 @@ -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 + + --- /dev/null 2018-10-22 10:32:40.000000000 -0400 +++ new/src/jdk.packager/macosx/classes/jdk/packager/internal/resources/mac/MacAppBundler.properties 2018-10-22 10:32:37.828714300 -0400 @@ -0,0 +1,85 @@ +# +# 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. +# +# + +bundler.name=Mac Application Image +bundler.description=A Directory based image of a mac Application with an optionally co-bundled JRE. Used as a base for the Installer bundlers + +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.config-root.name=Config Root Dir +param.config-root.description=Configuration directory. + +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 packager 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. + +error.cannot-create-output-dir=Output directory {0} cannot be created. +error.cannot-write-to-output-dir=Output directory {0} is not writable. +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. +resource.bundle-config-file=Bundle config file + +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.creating-app-bundle=Creating app bundle\: {0} +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. --- /dev/null 2018-10-22 10:32:47.000000000 -0400 +++ new/src/jdk.packager/macosx/classes/jdk/packager/internal/resources/mac/MacAppBundler_ja.properties 2018-10-22 10:32:45.254695100 -0400 @@ -0,0 +1,59 @@ +bundler.name=Mac\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u30FB\u30A4\u30E1\u30FC\u30B8 +bundler.description=\u30AA\u30D7\u30B7\u30E7\u30F3\u3067JRE\u304C\u30D0\u30F3\u30C9\u30EB\u3055\u308C\u3066\u3044\u308BMac\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u306E\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u30FB\u30D9\u30FC\u30B9\u306E\u30A4\u30E1\u30FC\u30B8\u3002\u30A4\u30F3\u30B9\u30C8\u30FC\u30E9\u30FB\u30D0\u30F3\u30C9\u30EB\u306E\u30D9\u30FC\u30B9\u3068\u3057\u3066\u4F7F\u7528\u3055\u308C\u307E\u3059\u3002 + +param.signing-key-developer-id-app.name=Apple Developer ID\u306E\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u7F72\u540D\u30AD\u30FC +param.signing-key-developer-id-app.description=Apple Developer ID\u306E\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u7F72\u540D\u30AD\u30FC\u306E\u30D5\u30EB\u30FB\u30CD\u30FC\u30E0\u3002 + +param.icon-icns.name=.icns\u30A2\u30A4\u30B3\u30F3 +param.icon-icns.description=ICNS\u5F62\u5F0F\u3067\u306E\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u306E\u30A2\u30A4\u30B3\u30F3\u3002 + +param.config-root.name=Config Root Dir +param.config-root.description=Configuration directory. + +param.configure-launcher-in-plist=Info.plist\u3067\u306E\u30E9\u30F3\u30C1\u30E3\u306E\u69CB\u6210 +param.configure-launcher-in-plist.description=Info.plist\u3067\u306E\u30E9\u30F3\u30C1\u30E3\u306E\u69CB\u6210\u306B\u306F\u30EC\u30AC\u30B7\u30FC\u30FB\u30E1\u30BD\u30C3\u30C9\u3092\u4F7F\u7528\u3059\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059\u3002 + +param.category-name=\u30AB\u30C6\u30B4\u30EA +param.category-name.description=Mac App Store\u306E\u30AB\u30C6\u30B4\u30EA\u3002\u30AD\u30FC\u306F\u30E6\u30FC\u30B6\u30FC\u306B\u8868\u793A\u3055\u308C\u308B\u6587\u5B57\u5217\u3067\u3001\u5024\u306F\u30AB\u30C6\u30B4\u30EA\u306EID\u3067\u3059\u3002 + +param.cfbundle-name.name=CFBundleName +param.cfbundle-name.description=\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u304C\u30E1\u30CB\u30E5\u30FC\u30FB\u30D0\u30FC\u306B\u8868\u793A\u3055\u308C\u308B\u969B\u306E\u540D\u524D\u3002\u3053\u308C\u306F\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u540D\u3068\u306F\u7570\u306A\u308B\u5834\u5408\u304C\u3042\u308A\u307E\u3059\u3002\u3053\u306E\u540D\u524D\u306F16\u6587\u5B57\u672A\u6E80\u3068\u3057\u3001\u30E1\u30CB\u30E5\u30FC\u30FB\u30D0\u30FC\u3068\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u306E\u60C5\u5831\u30A6\u30A3\u30F3\u30C9\u30A6\u3067\u306E\u8868\u793A\u306B\u9069\u3057\u305F\u3082\u306E\u306B\u3059\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059\u3002 + +param.cfbundle-identifier.name=CFBundleIdentifier +param.cfbundle-identifier.description=MacOSX(\u304A\u3088\u3073Mac App Store)\u306E\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u3092\u4E00\u610F\u306B\u8B58\u5225\u3059\u308B\u8B58\u5225\u5B50\u3002\u82F1\u6570\u5B57(A-Z\u3001a-z\u30010-9)\u3001\u30CF\u30A4\u30D5\u30F3(-)\u304A\u3088\u3073\u30D4\u30EA\u30AA\u30C9(.)\u306E\u307F\u3092\u4F7F\u7528\u3067\u304D\u307E\u3059\u3002 + +param.cfbundle-version.name=CFBundleVersion +param.cfbundle-version.description=\u30B3\u30F3\u30D4\u30E5\u30FC\u30BF\u3067\u8AAD\u53D6\u308A\u53EF\u80FD\u306ACFBundle\u306E\u30D0\u30FC\u30B8\u30E7\u30F3\u3002\u6570\u5B57\u304A\u3088\u3073\u30BC\u30ED\u304B\u30892\u3064\u307E\u3067\u306E\u30C9\u30C3\u30C8\u306E\u307F\u3092\u542B\u3081\u308B\u3053\u3068\u304C\u3067\u304D\u307E\u3059("1.8.1"\u3001"100"\u306A\u3069)\u3002 + +param.bundle-id-signing-prefix.name=\u30D0\u30F3\u30C9\u30EB\u7F72\u540D\u63A5\u982D\u8F9E +param.bundle-id-signing-prefix.description=\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u30FB\u30D0\u30F3\u30C9\u30EB\u306B\u7F72\u540D\u3059\u308B\u969B\u3001\u7F72\u540D\u3059\u308B\u5FC5\u8981\u306E\u3042\u308B\u3001\u65E2\u5B58\u306ECFBundleIdentifier\u3092\u6301\u305F\u306A\u3044\u3059\u3079\u3066\u306E\u30B3\u30F3\u30DD\u30FC\u30CD\u30F3\u30C8\u306B\u3053\u306E\u5024\u304C\u63A5\u982D\u8F9E\u3068\u3057\u3066\u4ED8\u3051\u3089\u308C\u307E\u3059\u3002 + +param.raw-executable-url.name=\u30E9\u30F3\u30C1\u30E3URL +param.raw-executable-url.description=\u30D1\u30C3\u30B1\u30FC\u30B8\u30E3\u306E\u30C7\u30D5\u30A9\u30EB\u30C8\u30FB\u30E9\u30F3\u30C1\u30E3\u3092\u30AB\u30B9\u30BF\u30E0\u30FB\u30E9\u30F3\u30C1\u30E3\u3067\u30AA\u30FC\u30D0\u30FC\u30E9\u30A4\u30C9\u3057\u307E\u3059\u3002 + +param.default-icon-icns=\u30C7\u30D5\u30A9\u30EB\u30C8\u30FB\u30A2\u30A4\u30B3\u30F3 +param.default-icon-icns.description=\u30E6\u30FC\u30B6\u30FC\u304C\u30A2\u30A4\u30B3\u30F3\u30FB\u30D5\u30A1\u30A4\u30EB\u3092\u6307\u5B9A\u3057\u306A\u3044\u5834\u5408\u306E\u30C7\u30D5\u30A9\u30EB\u30C8\u30FB\u30A2\u30A4\u30B3\u30F3\u3002 + +error.cannot-create-output-dir=\u51FA\u529B\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA{0}\u3092\u4F5C\u6210\u3067\u304D\u307E\u305B\u3093\u3002 +error.cannot-write-to-output-dir=\u51FA\u529B\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA{0}\u306F\u66F8\u8FBC\u307F\u4E0D\u53EF\u3067\u3059\u3002 +error.invalid-cfbundle-version=CFBundleVersion - ''{0}''\u304C\u7121\u52B9\u3067\u3059 +error.invalid-cfbundle-version.advice=\u4E92\u63DB\u6027\u306E\u3042\u308B'appVersion'\u3092\u8A2D\u5B9A\u3059\u308B\u304B\u3001'mac.CFBundleVersion'\u3092\u8A2D\u5B9A\u3057\u307E\u3059\u3002\u6709\u52B9\u306A\u30D0\u30FC\u30B8\u30E7\u30F3\u306F\u3001\u30C9\u30C3\u30C8\u3067\u533A\u5207\u3089\u308C\u305F1\u304B\u30893\u3064\u306E\u6574\u6570\u3067\u3059\u3002 +error.explicit-sign-no-cert=\u7F72\u540D\u304C\u660E\u793A\u7684\u306B\u8981\u6C42\u3055\u308C\u307E\u3057\u305F\u304C\u3001\u7F72\u540D\u8A3C\u660E\u66F8\u304C\u6307\u5B9A\u3055\u308C\u3066\u3044\u307E\u305B\u3093\u3002 +error.explicit-sign-no-cert.advice=\u6709\u52B9\u306A\u8A3C\u660E\u66F8\u3092'mac.signing-key-developer-id-app'\u3067\u6307\u5B9A\u3059\u308B\u304B\u3001'signBundle'\u3092\u8A2D\u5B9A\u89E3\u9664\u3059\u308B\u304B\u3001\u307E\u305F\u306F'signBundle'\u3092false\u306B\u8A2D\u5B9A\u3057\u307E\u3059\u3002 +error.non-existent-runtime=\u30E9\u30F3\u30BF\u30A4\u30E0/JRE\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u306B\u30D5\u30A1\u30A4\u30EB\u304C\u5B58\u5728\u3057\u307E\u305B\u3093\u3002 +error.non-existent-runtime.advice=\u30E9\u30F3\u30BF\u30A4\u30E0\u30FB\u30D1\u30E9\u30E1\u30FC\u30BF\u306B\u3001JRE\u3092\u542B\u3080\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u3092\u6307\u5B9A\u3057\u307E\u3059\u3002 +error.cannot-detect-runtime-in-directory=\u6307\u5B9A\u3057\u305F\u30E9\u30F3\u30BF\u30A4\u30E0\u30FB\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u306B\u3069\u306EJRE/JDK\u304C\u5B58\u5728\u3059\u308B\u306E\u304B\u3092\u7279\u5B9A\u3067\u304D\u307E\u305B\u3093\u3002 +error.cannot-detect-runtime-in-directory.advice=\u30E9\u30A4\u30BF\u30A4\u30E0\u30FB\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u306B\u3001JDK/JRE\u30EB\u30FC\u30C8\u3001\u305D\u306E\u30EB\u30FC\u30C8\u306E\u30B3\u30F3\u30C6\u30F3\u30C4/\u30DB\u30FC\u30E0\u30FB\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u3001\u307E\u305F\u306FJDK\u306E\u30B3\u30F3\u30C6\u30F3\u30C4/\u30DB\u30FC\u30E0/jre\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u306E\u3044\u305A\u308C\u304B\u3092\u6307\u5B9A\u3057\u307E\u3059\u3002 +resource.bundle-config-file=\u30D0\u30F3\u30C9\u30EB\u69CB\u6210\u30D5\u30A1\u30A4\u30EB + +message.bundle-name-too-long-warning={0}\u304C16\u6587\u5B57\u3092\u8D85\u3048\u308B''{1}''\u306B\u8A2D\u5B9A\u3055\u308C\u3066\u3044\u307E\u3059\u3002Mac\u3067\u306E\u64CD\u4F5C\u6027\u3092\u3088\u308A\u826F\u304F\u3059\u308B\u305F\u3081\u306B\u77ED\u304F\u3059\u308B\u3053\u3068\u3092\u691C\u8A0E\u3057\u3066\u304F\u3060\u3055\u3044\u3002 +message.no-mac-jre-support=\u73FE\u5728\u3001Mac\u3067\u306FJDK\u3092\u30D1\u30C3\u30B1\u30FC\u30B8\u5316\u3059\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059 +message.creating-app-bundle=\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u30FB\u30D0\u30F3\u30C9\u30EB\u3092\u4F5C\u6210\u3057\u3066\u3044\u307E\u3059: {0} +message.null-classpath=Null\u306E\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u30FB\u30EA\u30BD\u30FC\u30B9\u3067\u3059\u304B\u3002 +message.preparing-info-plist=Info.plist\u3092\u6E96\u5099\u3057\u3066\u3044\u307E\u3059: {0} +message.icon-not-icns= \u6307\u5B9A\u3057\u305F\u30A2\u30A4\u30B3\u30F3"{0}"\u306FICNS\u30D5\u30A1\u30A4\u30EB\u3067\u306F\u306A\u304F\u3001\u4F7F\u7528\u3055\u308C\u307E\u305B\u3093\u3002\u30C7\u30D5\u30A9\u30EB\u30C8\u30FB\u30A2\u30A4\u30B3\u30F3\u304C\u305D\u306E\u4F4D\u7F6E\u306B\u4F7F\u7528\u3055\u308C\u307E\u3059\u3002 +message.version-string-too-many-components=\u30D0\u30FC\u30B8\u30E7\u30F3\u6587\u5B57\u5217\u306B\u306F\u30011\u30011.2\u30011.2.3\u306A\u30691\u304B\u30893\u306E\u6570\u5B57\u3092\u4F7F\u7528\u3067\u304D\u307E\u3059\u3002 +message.version-string-first-number-not-zero=CFBundleVersion\u306E\u6700\u521D\u306E\u6570\u5B57\u306F\u3001\u30BC\u30ED\u307E\u305F\u306F\u8CA0\u306E\u5024\u306B\u3067\u304D\u307E\u305B\u3093\u3002 +message.version-string-no-negative-numbers=\u30D0\u30FC\u30B8\u30E7\u30F3\u6587\u5B57\u5217\u306B\u8CA0\u306E\u6570\u306F\u8A31\u53EF\u3055\u308C\u307E\u305B\u3093\u3002 +message.version-string-numbers-only=\u30D0\u30FC\u30B8\u30E7\u30F3\u6587\u5B57\u5217\u306F\u3001\u6570\u5B57\u30682\u3064\u307E\u3067\u306E\u30C9\u30C3\u30C8\u3067\u306E\u307F\u69CB\u6210\u3067\u304D\u307E\u3059\u3002 +message.creating-association-with-null-extension=null\u62E1\u5F35\u5B50\u3068\u306E\u95A2\u9023\u4ED8\u3051\u3092\u4F5C\u6210\u3057\u3066\u3044\u307E\u3059\u3002 --- /dev/null 2018-10-22 10:32:53.000000000 -0400 +++ new/src/jdk.packager/macosx/classes/jdk/packager/internal/resources/mac/MacAppBundler_zh_CN.properties 2018-10-22 10:32:51.301205600 -0400 @@ -0,0 +1,85 @@ +# +# 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. +# +# + +bundler.name=Mac \u5E94\u7528\u7A0B\u5E8F\u6620\u50CF +bundler.description=\u4E00\u4E2A\u57FA\u4E8E\u76EE\u5F55\u7684 mac \u5E94\u7528\u7A0B\u5E8F\u6620\u50CF, \u53EF\u4EE5\u9009\u62E9\u6027\u5730\u5E26\u6709\u5171\u540C\u6253\u5305\u7684 JRE\u3002\u7528\u4F5C\u5B89\u88C5\u7A0B\u5E8F\u6253\u5305\u7A0B\u5E8F\u7684\u57FA\u7840 + +param.signing-key-developer-id-app.name=Apple \u5F00\u53D1\u8005 ID \u5E94\u7528\u7A0B\u5E8F\u7B7E\u540D\u5BC6\u94A5 +param.signing-key-developer-id-app.description=Apple \u5F00\u53D1\u8005 ID \u5E94\u7528\u7A0B\u5E8F\u7B7E\u540D\u5BC6\u94A5\u7684\u5168\u540D\u3002 + +param.icon-icns.name=.icns \u56FE\u6807 +param.icon-icns.description=\u5E94\u7528\u7A0B\u5E8F\u7684\u56FE\u6807, \u91C7\u7528 ICNS \u683C\u5F0F\u3002 + +param.config-root.name=Config Root Dir +param.config-root.description=Configuration directory. + +param.configure-launcher-in-plist=\u5728 Info.plist \u4E2D\u914D\u7F6E\u542F\u52A8\u7A0B\u5E8F +param.configure-launcher-in-plist.description=\u662F\u5426\u5E94\u4F7F\u7528\u5728 Info.plist \u4E2D\u914D\u7F6E\u542F\u52A8\u7A0B\u5E8F\u7684\u65E7\u65B9\u6CD5\u3002 + +param.category-name=\u7C7B\u522B +param.category-name.description=Mac App Store \u7C7B\u522B\u3002\u8BF7\u6CE8\u610F, \u952E\u662F\u5411\u7528\u6237\u663E\u793A\u7684\u5B57\u7B26\u4E32, \u503C\u662F\u7C7B\u522B\u7684 ID\u3002 + +param.cfbundle-name.name=CFBundleName +param.cfbundle-name.description=\u5E94\u7528\u7A0B\u5E8F\u5728\u83DC\u5355\u680F\u4E2D\u7684\u663E\u793A\u540D\u79F0\u3002\u53EF\u4EE5\u4E0E\u5E94\u7528\u7A0B\u5E8F\u540D\u79F0\u4E0D\u540C\u3002\u6B64\u540D\u79F0\u7684\u957F\u5EA6\u5E94\u5C11\u4E8E 16 \u4E2A\u5B57\u7B26, \u5E76\u4E14\u5E94\u9002\u5408\u5728\u83DC\u5355\u680F\u548C\u5E94\u7528\u7A0B\u5E8F\u7684\u201C\u4FE1\u606F\u201D\u7A97\u53E3\u4E2D\u663E\u793A\u3002 + +param.cfbundle-identifier.name=CFBundleIdentifier +param.cfbundle-identifier.description=\u552F\u4E00\u6807\u8BC6 MacOSX \u5E94\u7528\u7A0B\u5E8F (\u5728 Mac App Store \u4E0A) \u7684\u6807\u8BC6\u7B26\u3002\u53EA\u80FD\u4F7F\u7528\u5B57\u6BCD\u6570\u5B57 (A-Z,a-z,0-9), \u8FDE\u5B57\u7B26 (-) \u548C\u53E5\u70B9 (.) \u5B57\u7B26\u3002 + +param.cfbundle-version.name=CFBundleVersion +param.cfbundle-version.description=CFBundle \u7684\u8BA1\u7B97\u673A\u53EF\u8BFB\u7248\u672C\u3002\u53EA\u80FD\u5305\u542B\u6570\u5B57\u4EE5\u53CA\u96F6\u5230\u4E24\u4E2A\u70B9, \u4F8B\u5982 "1.8.1" \u6216 "100"\u3002 + +param.bundle-id-signing-prefix.name=\u5305\u7B7E\u540D\u524D\u7F00 +param.bundle-id-signing-prefix.description=\u5BF9\u5E94\u7528\u7A0B\u5E8F\u5305\u8FDB\u884C\u7B7E\u540D\u65F6, \u6B64\u503C\u5C06\u4F5C\u4E3A\u524D\u7F00\u6DFB\u52A0\u5230\u9700\u8981\u7B7E\u540D\u4F46\u4E0D\u5177\u6709\u73B0\u6709 CFBundleIdentifier \u7684\u6240\u6709\u7EC4\u4EF6\u4E2D\u3002 + +param.raw-executable-url.name=\u542F\u52A8\u7A0B\u5E8F URL +param.raw-executable-url.description=\u4F7F\u7528\u5B9A\u5236\u542F\u52A8\u7A0B\u5E8F\u8986\u76D6\u6253\u5305\u7A0B\u5E8F\u9ED8\u8BA4\u542F\u52A8\u7A0B\u5E8F\u3002 + +param.default-icon-icns=\u9ED8\u8BA4\u56FE\u6807 +param.default-icon-icns.description=\u7528\u6237\u672A\u6307\u5B9A icns \u6587\u4EF6\u65F6\u4F7F\u7528\u7684\u9ED8\u8BA4\u56FE\u6807\u3002 + +error.cannot-create-output-dir=\u65E0\u6CD5\u521B\u5EFA\u8F93\u51FA\u76EE\u5F55 {0}\u3002 +error.cannot-write-to-output-dir=\u8F93\u51FA\u76EE\u5F55 {0} \u4E0D\u53EF\u5199\u3002 +error.invalid-cfbundle-version=\u65E0\u6548\u7684 CFBundleVersion - ''{0}'' +error.invalid-cfbundle-version.advice=\u8BBE\u7F6E\u517C\u5BB9\u7684 'appVersion' \u6216\u8005\u8BBE\u7F6E 'mac.CFBundleVersion'\u3002\u6709\u6548\u7248\u672C\u5305\u542B\u4E00\u5230\u4E09\u4E2A\u7528\u70B9\u5206\u9694\u7684\u6574\u6570\u3002 +error.explicit-sign-no-cert=\u5DF2\u660E\u786E\u8BF7\u6C42\u7B7E\u540D, \u4F46\u672A\u6307\u5B9A\u7B7E\u540D\u8BC1\u4E66\u3002 +error.explicit-sign-no-cert.advice=\u5728 'mac.signing-key-developer-id-app' \u4E2D\u6307\u5B9A\u6709\u6548\u7684\u8BC1\u4E66, \u6216\u8005\u53D6\u6D88\u8BBE\u7F6E 'signBundle', \u6216\u8005\u5C06 'signBundle' \u8BBE\u7F6E\u4E3A\u201C\u5047\u201D\u3002 +error.non-existent-runtime=\u8FD0\u884C\u65F6/JRE \u76EE\u5F55\u7684\u6587\u4EF6\u4E0D\u5B58\u5728\u3002 +error.non-existent-runtime.advice=\u5C06\u8FD0\u884C\u65F6\u53C2\u6570\u6307\u5411\u5305\u542B JRE \u7684\u76EE\u5F55\u3002 +error.cannot-detect-runtime-in-directory=\u65E0\u6CD5\u786E\u5B9A\u6307\u5B9A\u7684\u8FD0\u884C\u65F6\u76EE\u5F55\u4E2D\u5B58\u5728\u54EA\u4E2A\u7248\u672C\u7684 JRE/JDK\u3002 +error.cannot-detect-runtime-in-directory.advice=\u5C06\u8FD0\u884C\u65F6\u76EE\u5F55\u6307\u5411\u4EE5\u4E0B\u76EE\u5F55\u4E4B\u4E00: JDK/JRE \u6839\u76EE\u5F55, \u8BE5\u6839\u76EE\u5F55\u7684 Contents/Home \u76EE\u5F55\u6216 JDK \u7684 Contents/Home/jre \u76EE\u5F55\u3002 +resource.bundle-config-file=\u5305\u914D\u7F6E\u6587\u4EF6 + +message.bundle-name-too-long-warning={0}\u5DF2\u8BBE\u7F6E\u4E3A ''{1}'', \u5176\u957F\u5EA6\u8D85\u8FC7\u4E86 16 \u4E2A\u5B57\u7B26\u3002\u4E3A\u4E86\u83B7\u5F97\u66F4\u597D\u7684 Mac \u4F53\u9A8C, \u8BF7\u8003\u8651\u5C06\u5176\u7F29\u77ED\u3002 +message.no-mac-jre-support=Mac \u5F53\u524D\u9700\u8981 JDK \u4EE5\u4FBF\u6253\u5305 +message.creating-app-bundle=\u6B63\u5728\u521B\u5EFA\u5E94\u7528\u7A0B\u5E8F\u5305: {0} +message.null-classpath=\u662F\u5426\u4E3A\u7A7A\u5E94\u7528\u7A0B\u5E8F\u8D44\u6E90? +message.preparing-info-plist=\u6B63\u5728\u51C6\u5907 Info.plist: {0} +message.icon-not-icns= \u6307\u5B9A\u7684\u56FE\u6807 "{0}" \u4E0D\u662F ICNS \u6587\u4EF6, \u4E0D\u4F1A\u4F7F\u7528\u3002\u5C06\u4F7F\u7528\u9ED8\u8BA4\u56FE\u6807\u4EE3\u66FF\u3002 +message.version-string-too-many-components=\u7248\u672C\u5B57\u7B26\u4E32\u53EF\u4EE5\u5305\u542B 1 \u5230 3 \u4E2A\u6570\u5B57: 1, 1.2, 1.2.3\u3002 +message.version-string-first-number-not-zero=CFBundleVersion \u4E2D\u7684\u7B2C\u4E00\u4E2A\u6570\u5B57\u4E0D\u80FD\u4E3A\u96F6\u6216\u8D1F\u6570\u3002 +message.version-string-no-negative-numbers=\u7248\u672C\u5B57\u7B26\u4E32\u4E2D\u4E0D\u5141\u8BB8\u4F7F\u7528\u8D1F\u6570\u3002 +message.version-string-numbers-only=\u7248\u672C\u5B57\u7B26\u4E32\u53EA\u80FD\u5305\u542B\u6570\u5B57\u548C\u6700\u591A\u4E24\u4E2A\u70B9\u3002 +message.creating-association-with-null-extension=\u6B63\u5728\u4F7F\u7528\u7A7A\u6269\u5C55\u540D\u521B\u5EFA\u5173\u8054\u3002 --- /dev/null 2018-10-22 10:33:00.000000000 -0400 +++ new/src/jdk.packager/macosx/classes/jdk/packager/internal/resources/mac/MacAppStore.entitlements 2018-10-22 10:32:57.756337200 -0400 @@ -0,0 +1,8 @@ + + + + + com.apple.security.app-sandbox + + + --- /dev/null 2018-10-22 10:33:11.000000000 -0400 +++ new/src/jdk.packager/macosx/classes/jdk/packager/internal/resources/mac/MacAppStoreBundler.properties 2018-10-22 10:33:09.153922100 -0400 @@ -0,0 +1,61 @@ +# +# 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. +# +# + +bundler.name=Mac App Store Ready Bundler +bundler.description=Creates a binary bundle ready for deployment into the Mac App Store." + +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 name for this package. .pkg. + +resource.mac-app-store-entitlements=Mac App Store Entitlements +resource.mac-app-store-inherit-entitlements=Mac App Store Inherit Entitlements + +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}. + + +message.building-bundle=Building Mac App Store Bundle for {0} +mesasge.intermediate-bundle-location=Intermediate application bundle image\: {0} --- /dev/null 2018-10-22 10:33:18.000000000 -0400 +++ new/src/jdk.packager/macosx/classes/jdk/packager/internal/resources/mac/MacAppStoreBundler_ja.properties 2018-10-22 10:33:16.408294100 -0400 @@ -0,0 +1,61 @@ +# +# 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. +# +# + +bundler.name=Mac App Store\u306E\u6E96\u5099\u5B8C\u4E86\u30D0\u30F3\u30C9\u30E9 +bundler.description=Mac App Store\u3078\u306E\u30C7\u30D7\u30ED\u30A4\u30E1\u30F3\u30C8\u6E96\u5099\u5B8C\u4E86\u306E\u30D0\u30A4\u30CA\u30EA\u30FB\u30D0\u30F3\u30C9\u30EB\u3092\u4F5C\u6210\u3057\u307E\u3059\u3002" + +param.signing-key-app.name=\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u7F72\u540D\u30AD\u30FC +param.signing-key-app.description=\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u3092\u7F72\u540D\u3059\u308B\u7F72\u540D\u30AD\u30FC\u306E\u30D5\u30EB\u30FB\u30CD\u30FC\u30E0\u3002 + +param.signing-key-pkg.name=\u30A4\u30F3\u30B9\u30C8\u30FC\u30E9\u7F72\u540D\u30AD\u30FC +param.signing-key-pkg.description=PKG\u30A4\u30F3\u30B9\u30C8\u30FC\u30E9\u3092\u7F72\u540D\u3059\u308B\u7F72\u540D\u30AD\u30FC\u306E\u30D5\u30EB\u30FB\u30CD\u30FC\u30E0\u3002 + +param.mac-app-store-entitlements.name=\u6A29\u9650\u30D5\u30A1\u30A4\u30EB +param.mac-app-store-entitlements.description=\u30AB\u30B9\u30BF\u30E0Mac App Store\u6A29\u9650\u30D5\u30A1\u30A4\u30EB\u306E\u30D5\u30A1\u30A4\u30EB\u5834\u6240 + +param.installer-suffix.name=\u30A4\u30F3\u30B9\u30C8\u30FC\u30E9\u63A5\u5C3E\u8F9E +param.installer-suffix.description=\u3053\u306E\u30D1\u30C3\u30B1\u30FC\u30B8\u306E\u30A4\u30F3\u30B9\u30C8\u30FC\u30E9\u540D\u63A5\u5C3E\u8F9E\u3002.pkg\u3002 + +resource.mac-app-store-entitlements=Mac App Store\u6A29\u9650 +resource.mac-app-store-inherit-entitlements=Mac App Store\u7D99\u627F\u6A29\u9650 + +error.parameters-null=\u30D1\u30E9\u30E1\u30FC\u30BF\u30FB\u30DE\u30C3\u30D7\u304Cnull\u3067\u3059\u3002 +error.parameters-null.advice=\u975Enull\u30D1\u30E9\u30E1\u30FC\u30BF\u30FB\u30DE\u30C3\u30D7\u3067\u6E21\u3057\u3066\u304F\u3060\u3055\u3044\u3002 +error.cannot-create-output-dir=\u51FA\u529B\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA{0}\u3092\u4F5C\u6210\u3067\u304D\u307E\u305B\u3093\u3002 +error.cannot-write-to-output-dir=\u51FA\u529B\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA{0}\u306F\u66F8\u8FBC\u307F\u4E0D\u53EF\u3067\u3059\u3002 +error.no-system-runtime=\u30B7\u30B9\u30C6\u30E0JRE\u3092\u4F7F\u7528\u3059\u308B\u3088\u3046\u306B\u69CB\u6210\u3055\u308C\u305F\u30D0\u30F3\u30C9\u30EB +error.no-system-runtime.advice='runtime'\u3092null\u306B\u8A2D\u5B9A\u3057\u306A\u3044\u3067\u304F\u3060\u3055\u3044\u3002\u8A2D\u5B9A\u3057\u306A\u3044\u304B\u3001\u6709\u52B9\u306A\u5024\u306B\u8A2D\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044\u3002 +error.must-sign-app-store=Mac App Store\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u306F\u7F72\u540D\u3055\u308C\u3066\u3044\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059\u304C\u3001\u7F72\u540D\u306F\u30D0\u30F3\u30C9\u30E9\u69CB\u6210\u306B\u3088\u3063\u3066\u7121\u52B9\u5316\u3055\u308C\u3066\u3044\u307E\u3059\u3002 +error.must-sign-app-store.advice='signBundle'\u3092\u8A2D\u5B9A\u89E3\u9664\u3059\u308B\u304B\u3001'signBundle'\u3092true\u306B\u8A2D\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044\u3002 +error.no-app-signing-key=Mac App Store\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u306E\u7F72\u540D\u30AD\u30FC\u304C\u3042\u308A\u307E\u305B\u3093 +error.no-app-signing-key.advice=XCode\u3092\u4F7F\u7528\u3057\u3066\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u306E\u7F72\u540D\u30AD\u30FC\u3092Mac\u30AD\u30FC\u30C1\u30A7\u30FC\u30F3\u306B\u30A4\u30F3\u30B9\u30C8\u30FC\u30EB\u3057\u307E\u3059\u3002 +error.no-pkg-signing-key=Mac App Store\u30A4\u30F3\u30B9\u30C8\u30FC\u30E9\u306E\u7F72\u540D\u30AD\u30FC\u304C\u3042\u308A\u307E\u305B\u3093 +error.no-pkg-signing-key.advice=XCode\u3092\u4F7F\u7528\u3057\u3066\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u306E\u7F72\u540D\u30AD\u30FC\u3092Mac\u30AD\u30FC\u30C1\u30A7\u30FC\u30F3\u306B\u30A4\u30F3\u30B9\u30C8\u30FC\u30EB\u3057\u307E\u3059\u3002 +error.certificate.expired=\u30A8\u30E9\u30FC: \u8A3C\u660E\u66F8\u306F{0}\u306B\u671F\u9650\u304C\u5207\u308C\u307E\u3057\u305F\u3002 + + +message.building-bundle={0}\u306EMac App Store\u30D0\u30F3\u30C9\u30EB\u306E\u4F5C\u6210 +mesasge.intermediate-bundle-location=\u4E2D\u9593\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u30FB\u30D0\u30F3\u30C9\u30EB\u30FB\u30A4\u30E1\u30FC\u30B8: {0} --- /dev/null 2018-10-22 10:33:25.000000000 -0400 +++ new/src/jdk.packager/macosx/classes/jdk/packager/internal/resources/mac/MacAppStoreBundler_zh_CN.properties 2018-10-22 10:33:23.319448500 -0400 @@ -0,0 +1,61 @@ +# +# 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. +# +# + +bundler.name=\u652F\u6301 Mac App Store \u7684\u6253\u5305\u7A0B\u5E8F +bundler.description=\u521B\u5EFA\u53EF\u90E8\u7F72\u5230 Mac App Store \u4E2D\u7684\u4E8C\u8FDB\u5236\u6587\u4EF6\u5305\u3002" + +param.signing-key-app.name=\u5E94\u7528\u7A0B\u5E8F\u7B7E\u540D\u5BC6\u94A5 +param.signing-key-app.description=\u7528\u4E8E\u5BF9\u5E94\u7528\u7A0B\u5E8F\u7B7E\u540D\u7684\u7B7E\u540D\u5BC6\u94A5\u7684\u5B8C\u6574\u540D\u79F0\u3002 + +param.signing-key-pkg.name=\u5B89\u88C5\u7A0B\u5E8F\u7B7E\u540D\u5BC6\u94A5 +param.signing-key-pkg.description=\u7528\u4E8E\u5BF9 PKG \u5B89\u88C5\u7A0B\u5E8F\u7B7E\u540D\u7684\u7B7E\u540D\u5BC6\u94A5\u7684\u5B8C\u6574\u540D\u79F0\u3002 + +param.mac-app-store-entitlements.name=\u6743\u5229\u6587\u4EF6 +param.mac-app-store-entitlements.description=\u5B9A\u5236 Mac App Store \u6743\u5229\u6587\u4EF6\u7684\u6587\u4EF6\u4F4D\u7F6E + +param.installer-suffix.name=\u5B89\u88C5\u7A0B\u5E8F\u540E\u7F00 +param.installer-suffix.description=\u6B64\u7A0B\u5E8F\u5305\u7684\u5B89\u88C5\u6587\u4EF6\u540D\u79F0\u7684\u540E\u7F00\u3002<\u540D\u79F0><\u540E\u7F00>.pkg\u3002 + +resource.mac-app-store-entitlements=Mac App Store \u6743\u5229 +resource.mac-app-store-inherit-entitlements=Mac App Store \u7EE7\u627F\u6743\u5229 + +error.parameters-null=\u53C2\u6570\u6620\u5C04\u4E3A\u7A7A\u503C\u3002 +error.parameters-null.advice=\u8BF7\u4F20\u5165\u975E\u7A7A\u53C2\u6570\u6620\u5C04\u3002 +error.cannot-create-output-dir=\u65E0\u6CD5\u521B\u5EFA\u8F93\u51FA\u76EE\u5F55 {0}\u3002 +error.cannot-write-to-output-dir=\u8F93\u51FA\u76EE\u5F55 {0} \u4E0D\u53EF\u5199\u3002 +error.no-system-runtime=\u914D\u7F6E\u4E3A\u4F7F\u7528\u7CFB\u7EDF JRE \u7684\u5305 +error.no-system-runtime.advice=\u4E0D\u8981\u5C06 'runtime' \u8BBE\u7F6E\u4E3A\u7A7A\u503C, \u8981\u4E48\u4E0D\u8BBE\u7F6E\u8BE5\u503C, \u8981\u4E48\u5C06\u5176\u8BBE\u7F6E\u4E3A\u6709\u6548\u503C\u3002 +error.must-sign-app-store=Mac App Store \u5E94\u7528\u7A0B\u5E8F\u5FC5\u987B\u7B7E\u540D, \u800C\u6253\u5305\u7A0B\u5E8F\u914D\u7F6E\u5DF2\u7981\u7528\u7B7E\u540D\u3002 +error.must-sign-app-store.advice=\u53D6\u6D88\u8BBE\u7F6E 'signBundle' \u6216\u8005\u5C06 'signBundle' \u8BBE\u7F6E\u4E3A true\u3002 +error.no-app-signing-key=\u65E0 Mac App Store \u5E94\u7528\u7A0B\u5E8F\u7B7E\u540D\u5BC6\u94A5 +error.no-app-signing-key.advice=\u4F7F\u7528 XCode \u5C06\u5E94\u7528\u7A0B\u5E8F\u7B7E\u540D\u5BC6\u94A5\u5B89\u88C5\u5230 Mac \u5BC6\u94A5\u94FE\u4E2D\u3002 +error.no-pkg-signing-key=\u65E0 Mac App Store \u5B89\u88C5\u7A0B\u5E8F\u7B7E\u540D\u5BC6\u94A5 +error.no-pkg-signing-key.advice=\u4F7F\u7528 XCode \u5C06\u5E94\u7528\u7A0B\u5E8F\u7B7E\u540D\u5BC6\u94A5\u5B89\u88C5\u5230 Mac \u5BC6\u94A5\u94FE\u4E2D\u3002 +error.certificate.expired=\u9519\u8BEF: \u8BC1\u4E66\u5DF2\u5931\u6548 {0}\u3002 + + +message.building-bundle=\u6B63\u5728\u4E3A {0} \u6784\u5EFA Mac App Store \u5305 +mesasge.intermediate-bundle-location=\u4E34\u65F6\u5E94\u7528\u7A0B\u5E8F\u5305\u6620\u50CF: {0} --- /dev/null 2018-10-22 10:33:32.000000000 -0400 +++ new/src/jdk.packager/macosx/classes/jdk/packager/internal/resources/mac/MacAppStore_Inherit.entitlements 2018-10-22 10:33:29.857183800 -0400 @@ -0,0 +1,10 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.inherit + + + --- /dev/null 2018-10-22 10:33:38.000000000 -0400 +++ new/src/jdk.packager/macosx/classes/jdk/packager/internal/resources/mac/MacBaseInstallerBundler.properties 2018-10-22 10:33:36.612330200 -0400 @@ -0,0 +1,58 @@ +# +# 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. +# +# + +bundler.name=Mac App Store +bundler.description=Creates a binary bundle ready for deployment into the Mac App Store. + +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 the packager 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.config-root.name=Config Root Dir +param.config-root.description=Configuration directory. + +error.parameters-null=Parameters map is null. +error.parameters-null.advice=Pass in a non-null parameters map. + +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. --- /dev/null 2018-10-22 10:33:46.000000000 -0400 +++ new/src/jdk.packager/macosx/classes/jdk/packager/internal/resources/mac/MacBaseInstallerBundler_ja.properties 2018-10-22 10:33:43.866702200 -0400 @@ -0,0 +1,57 @@ +# +# 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. +# +# + +bundler.name=Mac App Store +bundler.description=Mac App Store\u3078\u306e\u30c7\u30d7\u30ed\u30a4\u30e1\u30f3\u30c8\u6e96\u5099\u5b8c\u4e86\u306e\u30d0\u30a4\u30ca\u30ea\u30fb\u30d0\u30f3\u30c9\u30eb\u3092\u4f5c\u6210\u3057\u307e\u3059\u3002 + +param.app-bundler.name=Mac\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u30fb\u30d0\u30f3\u30c9\u30e9 +param.app-bundle.description=Mac\u7528\u306e.app\u30d0\u30f3\u30c9\u30eb\u3092\u4f5c\u6210\u3057\u307e\u3059 + +param.app-image-build-root.name=Build Root Dir +param.app-image-build-root.description=\u3053\u308c\u306f\u30a4\u30e1\u30fc\u30b8\u30fb\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u306e\u30eb\u30fc\u30c8\u3067\u3042\u308b\u30d1\u30c3\u30b1\u30fc\u30b8\u30e3\u306b\u3088\u3063\u3066\u4f5c\u6210\u3055\u308c\u308b\u4e00\u6642\u7684\u306a\u5834\u6240\u3067\u3059 + +param.signing-keychain.name=\u7f72\u540d\u30ad\u30fc\u30c1\u30a7\u30fc\u30f3 +param.signing-keychain.description=\u4f7f\u7528\u3059\u308b\u30ad\u30fc\u30c1\u30a7\u30fc\u30f3\u306e\u5834\u6240\u3002\u6307\u5b9a\u3055\u308c\u3066\u3044\u306a\u3044\u5834\u5408\u306f\u3001\u6a19\u6e96\u306e\u30ad\u30fc\u30c1\u30a7\u30fc\u30f3\u304c\u4f7f\u7528\u3055\u308c\u307e\u3059\u3002 + +param.signing-key-name.name=\u7f72\u540d\u30ad\u30fc\u306e\u30e6\u30fc\u30b6\u30fc\u540d +param.signing-key-name.description=\u4e00\u822c\u7684\u306a"Mac Developer ID\u306e\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3: "\u7f72\u540d\u30ad\u30fc\u306e\u30e6\u30fc\u30b6\u30fc\u540d\u90e8\u5206\u3002 + +param.installer-name.name=\u30a4\u30f3\u30b9\u30c8\u30fc\u30e9\u540d +param.installer-name.description=\u30d5\u30a1\u30a4\u30eb\u30fb\u30bf\u30a4\u30d7\u62e1\u5f35\u5b50\u306a\u3057\u306e\u751f\u6210\u3055\u308c\u305f\u30a4\u30f3\u30b9\u30c8\uff0d\u30e9\u306e\u30d5\u30a1\u30a4\u30eb\u540d\u3002\u30c7\u30d5\u30a9\u30eb\u30c8\u306f-\u3067\u3059\u3002 + +param.config-root.name=Config Root Dir +param.config-root.description=Configuration directory. + +error.parameters-null=\u30d1\u30e9\u30e1\u30fc\u30bf\u30fb\u30de\u30c3\u30d7\u304cnull\u3067\u3059\u3002 +error.parameters-null.advice=\u975enull\u30d1\u30e9\u30e1\u30fc\u30bf\u30fb\u30de\u30c3\u30d7\u3067\u6e21\u3057\u3066\u304f\u3060\u3055\u3044\u3002 + +message.app-image-dir-does-not-exist=\u6307\u5b9a\u3055\u308c\u305f\u30a4\u30e1\u30fc\u30b8\u30fb\u30c7\u30a3\u30ec\u30af\u30c8\u30ea {0}: {1}\u306f\u5b58\u5728\u3057\u307e\u305b\u3093 +message.app-image-dir-does-not-exist.advice={0}\u306e\u5024\u304c\u5b58\u5728\u3059\u308b\u3053\u3068\u3092\u78ba\u8a8d\u3057\u3066\u304f\u3060\u3055\u3044 +message.could-not-retrieve-name=gecos\u540d\u3092\u53d6\u5f97\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f +message.app-image-requires-app-name=\u5916\u90e8\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u30fb\u30a4\u30e1\u30fc\u30b8\u3092\u4f7f\u7528\u3059\u308b\u5834\u5408\u3001\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u540d\u3092\u6307\u5b9a\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002 +message.app-image-requires-app-name.advice=-name CLI\u30d5\u30e9\u30b0\u3001fx:application/@name ANT\u5c5e\u6027\u307e\u305f\u306f'appName'\u30d0\u30f3\u30c9\u30e9\u5f15\u6570\u3067\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u540d\u3092\u8a2d\u5b9a\u3057\u307e\u3059\u3002 +message.app-image-requires-identifier=\u5916\u90e8\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u30fb\u30a4\u30e1\u30fc\u30b8\u3092\u4f7f\u7528\u3059\u308b\u5834\u5408\u3001\u8b58\u5225\u5b50\u3092\u6307\u5b9a\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002 +message.app-image-requires-identifier.advice=-appId CLI\u30d5\u30e9\u30b0\u3001fx:application/@id ANT\u5c5e\u6027\u307e\u305f\u306f'identifier'\u30d0\u30f3\u30c9\u30e9\u5f15\u6570\u3067\u8b58\u5225\u5b50\u3092\u8a2d\u5b9a\u3057\u307e\u3059\u3002 --- /dev/null 2018-10-22 10:33:53.000000000 -0400 +++ new/src/jdk.packager/macosx/classes/jdk/packager/internal/resources/mac/MacBaseInstallerBundler_zh_CN.properties 2018-10-22 10:33:50.916305400 -0400 @@ -0,0 +1,57 @@ +# +# 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. +# +# + +bundler.name=Mac App Store +bundler.description=\u521b\u5efa\u53ef\u90e8\u7f72\u5230 Mac App Store \u4e2d\u7684\u4e8c\u8fdb\u5236\u6587\u4ef6\u5305\u3002 + +param.app-bundler.name=Mac \u5e94\u7528\u7a0b\u5e8f\u6253\u5305\u7a0b\u5e8f +param.app-bundle.description=\u521b\u5efa\u7528\u4e8e Mac \u7684 .app \u5305 + +param.app-image-build-root.name=Build Root Dir +param.app-image-build-root.description=\u8fd9\u662f\u7531\u6253\u5305\u7a0b\u5e8f\u6784\u5efa\u7684\u4e34\u65f6\u4f4d\u7f6e, \u662f\u6620\u50cf\u5e94\u7528\u7a0b\u5e8f\u7684\u6839\u76ee\u5f55 + +param.signing-keychain.name=\u7b7e\u540d\u5bc6\u94a5\u94fe +param.signing-keychain.description=\u8981\u4f7f\u7528\u7684\u5bc6\u94a5\u94fe\u7684\u4f4d\u7f6e\u3002\u5982\u679c\u672a\u6307\u5b9a, \u5219\u5c06\u4f7f\u7528\u6807\u51c6\u5bc6\u94a5\u94fe\u3002 + +param.signing-key-name.name=\u7b7e\u540d\u5bc6\u94a5\u7528\u6237\u540d +param.signing-key-name.description=\u5178\u578b "Mac \u5f00\u53d1\u8005 ID \u5e94\u7528\u7a0b\u5e8f: <\u7528\u6237\u540d>" \u7b7e\u540d\u5bc6\u94a5\u7684\u7528\u6237\u540d\u90e8\u5206\u3002 + +param.installer-name.name=\u5b89\u88c5\u7a0b\u5e8f\u540d\u79f0 +param.installer-name.description=\u6240\u751f\u6210\u5b89\u88c5\u7a0b\u5e8f\u4e0d\u5e26\u6587\u4ef6\u7c7b\u578b\u6269\u5c55\u540d\u7684\u6587\u4ef6\u540d\u3002\u9ed8\u8ba4\u503c\u4e3a <\u5e94\u7528\u7a0b\u5e8f\u540d\u79f0>-<\u7248\u672c>\u3002 + +param.config-root.name=Config Root Dir +param.config-root.description=Configuration directory. + +error.parameters-null=\u53c2\u6570\u6620\u5c04\u4e3a\u7a7a\u503c\u3002 +error.parameters-null.advice=\u8bf7\u4f20\u5165\u975e\u7a7a\u53c2\u6570\u6620\u5c04\u3002 + +message.app-image-dir-does-not-exist=\u6307\u5b9a\u7684\u6620\u50cf\u76ee\u5f55 {0}: {1} \u4e0d\u5b58\u5728 +message.app-image-dir-does-not-exist.advice=\u786e\u8ba4 {0} \u7684\u503c\u662f\u5426\u5b58\u5728 +message.could-not-retrieve-name=\u65e0\u6cd5\u68c0\u7d22 gecos \u540d\u79f0 +message.app-image-requires-app-name=\u4f7f\u7528\u5916\u90e8\u5e94\u7528\u7a0b\u5e8f\u6620\u50cf\u65f6, \u5fc5\u987b\u6307\u5b9a\u5e94\u7528\u7a0b\u5e8f\u540d\u79f0\u3002 +message.app-image-requires-app-name.advice=\u901a\u8fc7 -name CLI \u6807\u8bb0, fx:application/@name ANT \u5c5e\u6027\u6216\u901a\u8fc7 'appName' \u6253\u5305\u7a0b\u5e8f\u53c2\u6570\u8bbe\u7f6e\u5e94\u7528\u7a0b\u5e8f\u540d\u79f0\u3002 +message.app-image-requires-identifier=\u4f7f\u7528\u5916\u90e8\u5e94\u7528\u7a0b\u5e8f\u6620\u50cf\u65f6, \u5fc5\u987b\u6307\u5b9a\u6807\u8bc6\u7b26\u3002 +message.app-image-requires-identifier.advice=\u901a\u8fc7 -appId CLI \u6807\u8bb0, fx:application/@id ANT \u5c5e\u6027\u6216\u901a\u8fc7 'identifier' \u6253\u5305\u7a0b\u5e8f\u53c2\u6570\u8bbe\u7f6e\u6807\u8bc6\u7b26\u3002 --- /dev/null 2018-10-22 10:34:00.000000000 -0400 +++ new/src/jdk.packager/macosx/classes/jdk/packager/internal/resources/mac/MacDmgBundler.properties 2018-10-22 10:33:58.390088700 -0400 @@ -0,0 +1,61 @@ +# +# 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. +# +# + +bundler.name=DMG Installer +bundler.description=Mac DMG Installer Bundle + +param.simple-dmg.name=Simple DMG Generation +param.simple-dmg.description=Generate a DMG without AppleScript customizations. Recommended for continuous automated builds. + +param.installer-suffix.name=Installer Suffix +param.installer-suffix.description=The suffix for the installer name for this package. .dmg. + +error.parameters-null=Parameters map is null. +error.parameters-null.advice=Pass in a non-null parameters map. + +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. + +error.license-missing=Specified license file is missing. +error.license-missing.advice=Make sure that "{0}" references a file in the app resources, and that it is relative file reference. + +error.cannot-create-output-dir=Output directory {0} cannot be created. +error.cannot-write-to-output-dir=Output directory {0} is not writable. + +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 + +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} + --- /dev/null 2018-10-22 10:34:07.000000000 -0400 +++ new/src/jdk.packager/macosx/classes/jdk/packager/internal/resources/mac/MacDmgBundler_ja.properties 2018-10-22 10:34:04.974626400 -0400 @@ -0,0 +1,61 @@ +# +# 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. +# +# + +bundler.name=DMG\u30A4\u30F3\u30B9\u30C8\u30FC\u30E9 +bundler.description=Mac DMG\u30A4\u30F3\u30B9\u30C8\u30FC\u30E9\u30FB\u30D0\u30F3\u30C9\u30EB + +param.simple-dmg.name=DMG\u306E\u7C21\u6613\u751F\u6210 +param.simple-dmg.description=AppleScript\u306E\u30AB\u30B9\u30BF\u30DE\u30A4\u30BA\u306A\u3057\u3067DMG\u3092\u751F\u6210\u3057\u307E\u3059\u3002\u9023\u7D9A\u3057\u305F\u81EA\u52D5\u4F5C\u6210\u306E\u5834\u5408\u306B\u304A\u85A6\u3081\u3057\u307E\u3059\u3002 + +param.installer-suffix.name=\u30A4\u30F3\u30B9\u30C8\u30FC\u30E9\u63A5\u5C3E\u8F9E +param.installer-suffix.description=\u3053\u306E\u30D1\u30C3\u30B1\u30FC\u30B8\u306E\u30A4\u30F3\u30B9\u30C8\u30FC\u30E9\u540D\u306E\u63A5\u5C3E\u8F9E\u3002.dmg\u3002 + +error.parameters-null=\u30D1\u30E9\u30E1\u30FC\u30BF\u30FB\u30DE\u30C3\u30D7\u304Cnull\u3067\u3059\u3002 +error.parameters-null.advice=\u975Enull\u30D1\u30E9\u30E1\u30FC\u30BF\u30FB\u30DE\u30C3\u30D7\u3067\u6E21\u3057\u3066\u304F\u3060\u3055\u3044\u3002 + +error.dmg-does-not-do-daemons=DMG\u30D0\u30F3\u30C9\u30E9\u306F\u30B5\u30FC\u30D3\u30B9\u3092\u30B5\u30DD\u30FC\u30C8\u3057\u3066\u3044\u307E\u305B\u3093\u3002 +error.dmg-does-not-do-daemons.advice=\u30B5\u30FC\u30D3\u30B9\u306E\u30D2\u30F3\u30C8\u304Cfalse\u306B\u8A2D\u5B9A\u3055\u308C\u3066\u3044\u308B\u3053\u3068\u3092\u78BA\u8A8D\u3057\u3066\u304F\u3060\u3055\u3044\u3002 + +error.license-missing=\u6307\u5B9A\u3057\u305F\u30E9\u30A4\u30BB\u30F3\u30B9\u30FB\u30D5\u30A1\u30A4\u30EB\u304C\u3042\u308A\u307E\u305B\u3093\u3002 +error.license-missing.advice="{0}"\u304C\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u30FB\u30EA\u30BD\u30FC\u30B9\u5185\u306E\u30D5\u30A1\u30A4\u30EB\u3092\u53C2\u7167\u3057\u3001\u76F8\u5BFE\u30D5\u30A1\u30A4\u30EB\u53C2\u7167\u3067\u3042\u308B\u3053\u3068\u3092\u78BA\u8A8D\u3057\u3066\u304F\u3060\u3055\u3044\u3002 + +error.cannot-create-output-dir=\u51FA\u529B\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA{0}\u3092\u4F5C\u6210\u3067\u304D\u307E\u305B\u3093\u3002 +error.cannot-write-to-output-dir=\u51FA\u529B\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA{0}\u306F\u66F8\u8FBC\u307F\u4E0D\u53EF\u3067\u3059\u3002 + +resource.dmg-setup-script=DMG\u8A2D\u5B9A\u30B9\u30AF\u30EA\u30D7\u30C8 +resource.license-setup=\u30E9\u30A4\u30BB\u30F3\u30B9\u306E\u8A2D\u5B9A +resource.dmg-background=dmg\u80CC\u666F +resource.volume-icon=\u30DC\u30EA\u30E5\u30FC\u30E0\u30FB\u30A2\u30A4\u30B3\u30F3 +resource.post-install-script=\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u30FB\u30A4\u30E1\u30FC\u30B8\u3092\u79FB\u5165\u3057\u305F\u5F8C\u306B\u5B9F\u884C\u3059\u308B\u30B9\u30AF\u30EA\u30D7\u30C8 + +message.building-dmg={0}\u306EDMG\u30D1\u30C3\u30B1\u30FC\u30B8\u3092\u4F5C\u6210\u3057\u3066\u3044\u307E\u3059 +message.running-script=\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u30FB\u30A4\u30E1\u30FC\u30B8[{0}]\u3067\u30B7\u30A7\u30EB\u30FB\u30B9\u30AF\u30EA\u30D7\u30C8\u3092\u5B9F\u884C\u3057\u3066\u3044\u307E\u3059 +message.intermediate-image-location=[\u30C7\u30D0\u30C3\u30B0]\u4E2D\u9593\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u30FB\u30D0\u30F3\u30C9\u30EB\u30FB\u30A4\u30E1\u30FC\u30B8: {0} +message.preparing-dmg-setup=dmg\u306E\u8A2D\u5B9A\u3092\u6E96\u5099\u3057\u3066\u3044\u307E\u3059: {0} +message.creating-dmg-file=DMG\u30D5\u30A1\u30A4\u30EB\u3092\u4F5C\u6210\u3057\u3066\u3044\u307E\u3059: {0} +message.dmg-cannot-be-overwritten=Dmg\u30D5\u30A1\u30A4\u30EB\u306F\u5B58\u5728\u3057({0}\u3001\u524A\u9664\u3067\u304D\u307E\u305B\u3093\u3002 +message.output-to-location={0}\u306E\u7D50\u679C\u306EDMG\u30A4\u30F3\u30B9\u30C8\u30FC\u30E9: {1} + --- /dev/null 2018-10-22 10:34:13.000000000 -0400 +++ new/src/jdk.packager/macosx/classes/jdk/packager/internal/resources/mac/MacDmgBundler_zh_CN.properties 2018-10-22 10:34:11.589365600 -0400 @@ -0,0 +1,61 @@ +# +# 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. +# +# + +bundler.name=DMG \u5B89\u88C5\u7A0B\u5E8F +bundler.description=Mac DMG \u5B89\u88C5\u7A0B\u5E8F\u5305 + +param.simple-dmg.name=\u7B80\u5355 DMG \u751F\u6210 +param.simple-dmg.description=\u751F\u6210\u4E0D\u5E26 AppleScript \u5B9A\u5236\u7684 DMG\u3002\u5EFA\u8BAE\u7528\u4E8E\u8FDE\u7EED\u81EA\u52A8\u6784\u5EFA\u3002 + +param.installer-suffix.name=\u5B89\u88C5\u7A0B\u5E8F\u540E\u7F00 +param.installer-suffix.description=\u6B64\u7A0B\u5E8F\u5305\u7684\u5B89\u88C5\u6587\u4EF6\u540D\u79F0\u7684\u540E\u7F00\u3002<\u540D\u79F0><\u540E\u7F00>.dmg\u3002 + +error.parameters-null=\u53C2\u6570\u6620\u5C04\u4E3A\u7A7A\u503C\u3002 +error.parameters-null.advice=\u8BF7\u4F20\u5165\u975E\u7A7A\u53C2\u6570\u6620\u5C04\u3002 + +error.dmg-does-not-do-daemons=DMG \u6253\u5305\u7A0B\u5E8F\u4E0D\u652F\u6301\u670D\u52A1\u3002 +error.dmg-does-not-do-daemons.advice=\u786E\u4FDD\u670D\u52A1\u63D0\u793A\u8BBE\u7F6E\u4E3A false\u3002 + +error.license-missing=\u7F3A\u5C11\u6307\u5B9A\u7684\u8BB8\u53EF\u8BC1\u6587\u4EF6\u3002 +error.license-missing.advice=\u786E\u4FDD "{0}" \u5F15\u7528\u5E94\u7528\u7A0B\u5E8F\u8D44\u6E90\u4E2D\u7684\u6587\u4EF6, \u5E76\u4E14\u662F\u76F8\u5BF9\u6587\u4EF6\u5F15\u7528\u3002 + +error.cannot-create-output-dir=\u65E0\u6CD5\u521B\u5EFA\u8F93\u51FA\u76EE\u5F55 {0}\u3002 +error.cannot-write-to-output-dir=\u8F93\u51FA\u76EE\u5F55 {0} \u4E0D\u53EF\u5199\u3002 + +resource.dmg-setup-script=DMG \u8BBE\u7F6E\u811A\u672C +resource.license-setup=\u8BB8\u53EF\u8BC1\u8BBE\u7F6E +resource.dmg-background=dmg \u80CC\u666F +resource.volume-icon=\u5377\u56FE\u6807 +resource.post-install-script=\u8981\u5728\u586B\u5145\u5E94\u7528\u7A0B\u5E8F\u6620\u50CF\u4E4B\u540E\u8FD0\u884C\u7684\u811A\u672C + +message.building-dmg=\u6B63\u5728\u4E3A {0} \u6784\u5EFA DMG \u7A0B\u5E8F\u5305 +message.running-script=\u6B63\u5728\u5E94\u7528\u7A0B\u5E8F\u6620\u50CF [{0}] \u4E0A\u8FD0\u884C shell \u811A\u672C +message.intermediate-image-location=[\u8C03\u8BD5] \u4E34\u65F6\u5E94\u7528\u7A0B\u5E8F\u5305\u6620\u50CF: {0} +message.preparing-dmg-setup=\u6B63\u5728\u51C6\u5907 dmg \u8BBE\u7F6E: {0} +message.creating-dmg-file=\u6B63\u5728\u521B\u5EFA DMG \u6587\u4EF6: {0} +message.dmg-cannot-be-overwritten=Dmg \u6587\u4EF6\u5DF2\u5B58\u5728 ({0}) \u4E14\u65E0\u6CD5\u5220\u9664\u3002 +message.output-to-location=\u4E3A {0} \u751F\u6210\u7684 DMG \u5B89\u88C5\u7A0B\u5E8F: {1} + --- /dev/null 2018-10-22 10:34:24.000000000 -0400 +++ new/src/jdk.packager/macosx/classes/jdk/packager/internal/resources/mac/MacPkgBundler.properties 2018-10-22 10:34:21.324264800 -0400 @@ -0,0 +1,64 @@ +# +# 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. +# +# + +bundler.name=PKG Installer +bundler.description=Mac PKG Installer Bundle. + +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.installer-suffix.name=Installer Suffix +param.installer-suffix.description=The suffix for the installer name for this package. .pkg. + +param.scripts-dir.name= +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. + +resource.pkg-preinstall-script=PKG preinstall script +resource.pkg-postinstall-script=PKG postinstall script +resource.pkg-background-image=pkg background image +resource.post-install-script=script to run after application image is populated + +error.parameters-null=Parameters map is null. +error.parameters-null.advice=Pass in a non-null parameters map. +error.license-missing=Specified license file is missing. +error.license-missing.advice=Make sure that "{0}" references a file in the app resources, and that it is relative to the basedir "{1}". +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-installer' or unset 'signBundle' or set 'signBundle' to false. +error.cannot-create-output-dir=Output directory {0} cannot be created. +error.cannot-write-to-output-dir=Output directory {0} is not writable. + +message.building-pkg=Building PKG package for {0} +message.running-script=Running shell script on application image [{0}] +message.preparing-scripts=Preparing package scripts +message.preparing-distribution-dist=Preparing distribution.dist\: {0} +message.intermediate-image-location=[DEBUG] Intermediate application bundle image\: {0} +message.signing.pkg=Warning: For signing PKG, you might need to set "Always Trust" for your certificate using "Keychain Access" tool. --- /dev/null 2018-10-22 10:34:31.000000000 -0400 +++ new/src/jdk.packager/macosx/classes/jdk/packager/internal/resources/mac/MacPkgBundler_ja.properties 2018-10-22 10:34:28.907253700 -0400 @@ -0,0 +1,60 @@ +# +# 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. +# +# + +bundler.name=PKG\u30A4\u30F3\u30B9\u30C8\u30FC\u30E9 +bundler.description=Mac PKG\u30A4\u30F3\u30B9\u30C8\u30FC\u30E9\u30FB\u30D0\u30F3\u30C9\u30EB\u3002 + +param.signing-key-developer-id-installer.name=Apple Developer ID\u306E\u30A4\u30F3\u30B9\u30C8\u30FC\u30E9\u7F72\u540D\u30AD\u30FC +param.signing-key-developer-id-installer.description=Apple Developer ID\u306E\u30A4\u30F3\u30B9\u30C8\u30FC\u30E9\u7F72\u540D\u30AD\u30FC\u306E\u30D5\u30EB\u30FB\u30CD\u30FC\u30E0\u3002 + +param.packages-root.name=PKG Root Dir +param.packages-root.description=\u3053\u308C\u306F\u30B3\u30F3\u30DD\u30FC\u30CD\u30F3\u30C8\u30FB\u30D1\u30C3\u30B1\u30FC\u30B8(\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u304A\u3088\u3073\u30C7\u30FC\u30E2\u30F3)\u306E\u4E00\u6642\u7684\u306A\u5834\u6240\u3067\u3059\u3002\u30D1\u30C3\u30B1\u30FC\u30B8\u306F\u6700\u7D42\u88FD\u54C1\u30D1\u30C3\u30B1\u30FC\u30B8\u306B\u7D44\u307F\u8FBC\u307E\u308C\u307E\u3059\u3002 + +param.installer-suffix.name=\u30A4\u30F3\u30B9\u30C8\u30FC\u30E9\u63A5\u5C3E\u8F9E +param.installer-suffix.description=\u3053\u306E\u30D1\u30C3\u30B1\u30FC\u30B8\u306E\u30A4\u30F3\u30B9\u30C8\u30FC\u30E9\u540D\u63A5\u5C3E\u8F9E\u3002.pkg\u3002 + +param.scripts-dir.name= +param.scripts-dir.description=\u3053\u308C\u306F\u30D1\u30C3\u30B1\u30FC\u30B8\u30FB\u30B9\u30AF\u30EA\u30D7\u30C8\u306E\u4E00\u6642\u7684\u306A\u5834\u6240\u3067\u3059 + +resource.pkg-preinstall-script=PKG\u30A4\u30F3\u30B9\u30C8\u30FC\u30EB\u524D\u30B9\u30AF\u30EA\u30D7\u30C8 +resource.pkg-postinstall-script=PKG\u30A4\u30F3\u30B9\u30C8\u30FC\u30EB\u5F8C\u30B9\u30AF\u30EA\u30D7\u30C8 +resource.pkg-background-image=pkg\u80CC\u666F\u30A4\u30E1\u30FC\u30B8 +resource.post-install-script=\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u30FB\u30A4\u30E1\u30FC\u30B8\u3092\u79FB\u5165\u3057\u305F\u5F8C\u306B\u5B9F\u884C\u3059\u308B\u30B9\u30AF\u30EA\u30D7\u30C8 + +error.parameters-null=\u30D1\u30E9\u30E1\u30FC\u30BF\u30FB\u30DE\u30C3\u30D7\u304Cnull\u3067\u3059\u3002 +error.parameters-null.advice=\u975Enull\u30D1\u30E9\u30E1\u30FC\u30BF\u30FB\u30DE\u30C3\u30D7\u3067\u6E21\u3057\u3066\u304F\u3060\u3055\u3044\u3002 +error.license-missing=\u6307\u5B9A\u3057\u305F\u30E9\u30A4\u30BB\u30F3\u30B9\u30FB\u30D5\u30A1\u30A4\u30EB\u304C\u3042\u308A\u307E\u305B\u3093\u3002 +error.license-missing.advice="{0}"\u304C\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u30FB\u30EA\u30BD\u30FC\u30B9\u5185\u306E\u30D5\u30A1\u30A4\u30EB\u3092\u53C2\u7167\u3057\u3001\u30D9\u30FC\u30B9\u30FB\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA"{1}"\u306B\u5BFE\u3057\u3066\u76F8\u5BFE\u7684\u3067\u3042\u308B\u3053\u3068\u3092\u78BA\u8A8D\u3057\u3066\u304F\u3060\u3055\u3044\u3002 +error.explicit-sign-no-cert=\u7F72\u540D\u304C\u660E\u793A\u7684\u306B\u8981\u6C42\u3055\u308C\u307E\u3057\u305F\u304C\u3001\u7F72\u540D\u8A3C\u660E\u66F8\u304C\u6307\u5B9A\u3055\u308C\u3066\u3044\u307E\u305B\u3093\u3002 +error.explicit-sign-no-cert.advice=\u6709\u52B9\u306A\u8A3C\u660E\u66F8\u3092'mac.signing-key-developer-id-installer'\u3067\u6307\u5B9A\u3059\u308B\u304B\u3001'signBundle'\u3092\u8A2D\u5B9A\u89E3\u9664\u3059\u308B\u304B\u3001\u307E\u305F\u306F'signBundle'\u3092false\u306B\u8A2D\u5B9A\u3057\u307E\u3059\u3002 +error.cannot-create-output-dir=\u51FA\u529B\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA{0}\u3092\u4F5C\u6210\u3067\u304D\u307E\u305B\u3093\u3002 +error.cannot-write-to-output-dir=\u51FA\u529B\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA{0}\u306F\u66F8\u8FBC\u307F\u4E0D\u53EF\u3067\u3059\u3002 + +message.building-pkg={0}\u306EPKG\u30D1\u30C3\u30B1\u30FC\u30B8\u3092\u4F5C\u6210\u3057\u3066\u3044\u307E\u3059 +message.running-script=\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u30FB\u30A4\u30E1\u30FC\u30B8[{0}]\u3067\u30B7\u30A7\u30EB\u30FB\u30B9\u30AF\u30EA\u30D7\u30C8\u3092\u5B9F\u884C\u3057\u3066\u3044\u307E\u3059 +message.preparing-scripts=\u30D1\u30C3\u30B1\u30FC\u30B8\u30FB\u30B9\u30AF\u30EA\u30D7\u30C8\u3092\u6E96\u5099\u3057\u3066\u3044\u307E\u3059 +message.preparing-distribution-dist=distribution.dist\u3092\u6E96\u5099\u3057\u3066\u3044\u307E\u3059: {0} +message.intermediate-image-location=[\u30C7\u30D0\u30C3\u30B0]\u4E2D\u9593\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u30FB\u30D0\u30F3\u30C9\u30EB\u30FB\u30A4\u30E1\u30FC\u30B8: {0} --- /dev/null 2018-10-22 10:34:38.000000000 -0400 +++ new/src/jdk.packager/macosx/classes/jdk/packager/internal/resources/mac/MacPkgBundler_zh_CN.properties 2018-10-22 10:34:36.208428100 -0400 @@ -0,0 +1,60 @@ +# +# 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. +# +# + +bundler.name=PKG \u5B89\u88C5\u7A0B\u5E8F +bundler.description=Mac PKG \u5B89\u88C5\u7A0B\u5E8F\u5305\u3002 + +param.signing-key-developer-id-installer.name=Apple \u5F00\u53D1\u8005 ID \u5B89\u88C5\u7A0B\u5E8F\u7B7E\u540D\u5BC6\u94A5 +param.signing-key-developer-id-installer.description=Apple \u5F00\u53D1\u8005 ID \u5B89\u88C5\u7A0B\u5E8F\u7B7E\u540D\u5BC6\u94A5\u7684\u5168\u540D\u3002 + +param.packages-root.name=PKG Root Dir +param.packages-root.description=\u8FD9\u662F\u7EC4\u4EF6\u7A0B\u5E8F\u5305 (\u5E94\u7528\u7A0B\u5E8F\u548C\u5B88\u62A4\u7A0B\u5E8F) \u7684\u4E34\u65F6\u4F4D\u7F6E\u3002\u7A0B\u5E8F\u5305\u96C6\u6210\u5230\u6700\u7EC8\u4EA7\u54C1\u7A0B\u5E8F\u5305\u4E2D\u3002 + +param.installer-suffix.name=\u5B89\u88C5\u7A0B\u5E8F\u540E\u7F00 +param.installer-suffix.description=\u6B64\u7A0B\u5E8F\u5305\u7684\u5B89\u88C5\u6587\u4EF6\u540D\u79F0\u7684\u540E\u7F00\u3002<\u540D\u79F0><\u540E\u7F00>.pkg\u3002 + +param.scripts-dir.name= +param.scripts-dir.description=\u8FD9\u662F\u7A0B\u5E8F\u5305\u811A\u672C\u7684\u4E34\u65F6\u4F4D\u7F6E + +resource.pkg-preinstall-script=PKG \u5B89\u88C5\u524D\u811A\u672C +resource.pkg-postinstall-script=PKG \u5B89\u88C5\u540E\u811A\u672C +resource.pkg-background-image=pkg \u80CC\u666F\u56FE\u50CF +resource.post-install-script=\u8981\u5728\u586B\u5145\u5E94\u7528\u7A0B\u5E8F\u6620\u50CF\u4E4B\u540E\u8FD0\u884C\u7684\u811A\u672C + +error.parameters-null=\u53C2\u6570\u6620\u5C04\u4E3A\u7A7A\u503C\u3002 +error.parameters-null.advice=\u8BF7\u4F20\u5165\u975E\u7A7A\u53C2\u6570\u6620\u5C04\u3002 +error.license-missing=\u7F3A\u5C11\u6307\u5B9A\u7684\u8BB8\u53EF\u8BC1\u6587\u4EF6\u3002 +error.license-missing.advice=\u8BF7\u786E\u4FDD "{0}" \u5F15\u7528\u5E94\u7528\u7A0B\u5E8F\u8D44\u6E90\u4E2D\u7684\u6587\u4EF6, \u5E76\u4E14\u4F7F\u7528\u57FA\u76EE\u5F55 "{1}" \u7684\u76F8\u5BF9\u76EE\u5F55\u3002 +error.explicit-sign-no-cert=\u5DF2\u660E\u786E\u8BF7\u6C42\u7B7E\u540D, \u4F46\u672A\u6307\u5B9A\u7B7E\u540D\u8BC1\u4E66\u3002 +error.explicit-sign-no-cert.advice=\u5728 'mac.signing-key-developer-id-installer' \u4E2D\u6307\u5B9A\u6709\u6548\u7684\u8BC1\u4E66, \u6216\u8005\u53D6\u6D88\u8BBE\u7F6E 'signBundle', \u6216\u8005\u5C06 'signBundle' \u8BBE\u7F6E\u4E3A false\u3002 +error.cannot-create-output-dir=\u65E0\u6CD5\u521B\u5EFA\u8F93\u51FA\u76EE\u5F55 {0}\u3002 +error.cannot-write-to-output-dir=\u8F93\u51FA\u76EE\u5F55 {0} \u4E0D\u53EF\u5199\u3002 + +message.building-pkg=\u6B63\u5728\u4E3A {0} \u6784\u5EFA PKG \u7A0B\u5E8F\u5305 +message.running-script=\u6B63\u5728\u5E94\u7528\u7A0B\u5E8F\u6620\u50CF [{0}] \u4E0A\u8FD0\u884C shell \u811A\u672C +message.preparing-scripts=\u6B63\u5728\u51C6\u5907\u7A0B\u5E8F\u5305\u811A\u672C +message.preparing-distribution-dist=\u6B63\u5728\u51C6\u5907 distribution.dist: {0} +message.intermediate-image-location=[\u8C03\u8BD5] \u4E34\u65F6\u5E94\u7528\u7A0B\u5E8F\u5305\u6620\u50CF: {0} --- /dev/null 2018-10-22 10:34:45.000000000 -0400 +++ new/src/jdk.packager/macosx/classes/jdk/packager/internal/resources/mac/MacResources.java 2018-10-22 10:34:42.854368900 -0400 @@ -0,0 +1,31 @@ +/* + * 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.packager.internal.resources.mac; + +// no-op, use as anchor for resource loading +public class MacResources { + +} --- /dev/null 2018-10-22 10:34:51.000000000 -0400 +++ new/src/jdk.packager/macosx/classes/jdk/packager/internal/resources/mac/Runtime-Info.plist.template 2018-10-22 10:34:49.456709900 -0400 @@ -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 + + Binary files /dev/null and new/src/jdk.packager/macosx/classes/jdk/packager/internal/resources/mac/background_dmg.png differ Binary files /dev/null and new/src/jdk.packager/macosx/classes/jdk/packager/internal/resources/mac/background_pkg.png differ --- /dev/null 2018-10-22 10:35:11.000000000 -0400 +++ new/src/jdk.packager/macosx/classes/jdk/packager/internal/resources/mac/launchd.plist.template 2018-10-22 10:35:09.789755100 -0400 @@ -0,0 +1,14 @@ + + + + + Label + DEPLOY_DAEMON_IDENTIFIER + ProgramArguments + + DEPLOY_DAEMON_LAUNCHER_PATH + + RunAtLoad + KeepAlive + + --- /dev/null 2018-10-22 10:35:20.000000000 -0400 +++ new/src/jdk.packager/macosx/classes/jdk/packager/internal/resources/mac/lic_template.plist 2018-10-22 10:35:18.215187200 -0400 @@ -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 + + + + --- /dev/null 2018-10-22 10:35:31.000000000 -0400 +++ new/src/jdk.packager/macosx/classes/jdk/packager/internal/resources/mac/postinstall.template 2018-10-22 10:35:27.779477700 -0400 @@ -0,0 +1,6 @@ +#!/usr/bin/env sh + +set -e +launchctl load "/Library/LaunchDaemons/DEPLOY_LAUNCHD_PLIST_FILE" + +exit 0 --- /dev/null 2018-10-22 10:35:39.000000000 -0400 +++ new/src/jdk.packager/macosx/classes/jdk/packager/internal/resources/mac/preinstall.template 2018-10-22 10:35:37.327167300 -0400 @@ -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 --- /dev/null 2018-10-22 10:35:46.000000000 -0400 +++ new/src/jdk.packager/macosx/classes/module-info.java.extra 2018-10-22 10:35:44.503535300 -0400 @@ -0,0 +1,32 @@ +/* + * 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.packager.internal.Bundler with + jdk.packager.internal.mac.MacAppBundler, + jdk.packager.internal.mac.MacAppStoreBundler, + jdk.packager.internal.mac.MacDmgBundler, + jdk.packager.internal.mac.MacPkgBundler; + --- /dev/null 2018-10-22 10:35:56.000000000 -0400 +++ new/src/jdk.packager/macosx/native/launcher/main.m 2018-10-22 10:35:52.932967800 -0400 @@ -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/libpackager.dylib"]; + + void* library = dlopen([libraryName UTF8String], RTLD_LAZY); + + if (library == NULL) { + NSLog(@"%@ not found.\n", libraryName); + } + + if (library != NULL) { + start_launcher start = + (start_launcher)dlsym(library, "start_launcher"); + stop_launcher stop = + (stop_launcher)dlsym(library, "stop_launcher"); + + if (start != NULL && stop != NULL) { + if (start(argc, argv) == true) { + result = 0; + stop(); + } + } else if (start == NULL) { + NSLog(@"start_launcher not found in %@.\n", libraryName); + } else { + NSLog(@"stop_launcher not found in %@.\n", libraryName); + } + dlclose(library); + } + } @catch (NSException *exception) { + NSLog(@"%@: %@", exception, [exception callStackSymbols]); + result = 1; + } + +#if !__has_feature(objc_arc) + [pool drain]; +#endif + + return result; +} --- /dev/null 2018-10-22 10:36:04.000000000 -0400 +++ new/src/jdk.packager/macosx/native/library/MacPlatform.mm 2018-10-22 10:36:02.012633400 -0400 @@ -0,0 +1,422 @@ +/* + * 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_PACKAGER_TMP_DIR \ + "/Library/Application Support/Oracle/Java/Packager/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_PACKAGER_TMP_DIR); +} + +void MacPlatform::reactivateAnotherInstance() { + if (singleInstanceProcessId == 0) { + printf("Unable to reactivate another instance, PID is undefined"); + return; + } + NSRunningApplication* app = + [NSRunningApplication runningApplicationWithProcessIdentifier: + singleInstanceProcessId]; + if (app != nil) { + [app activateWithOptions: NSApplicationActivateIgnoringOtherApps]; + } else { + printf("Unable to reactivate another instance PID: %d", + singleInstanceProcessId); + } +} + +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 packager +// uses unless a packager 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(); + + // Packager 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(PACKAGER_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 --- /dev/null 2018-10-22 10:36:15.000000000 -0400 +++ new/src/jdk.packager/share/classes/jdk/packager/internal/AbstractBundler.java 2018-10-22 10:36:11.498754300 -0400 @@ -0,0 +1,193 @@ +/* + * 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.packager.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; + +/** + * 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.packager.internal.resources.AbstractBundler"); + + 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); + + // do not use file separator - + // we use it for classpath lookup and there / are not platform specific + public final static String BUNDLER_PREFIX = "package/"; + + protected Class baseResourceLoader = null; + + 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.info(MessageFormat.format(I18N.getString( + "message.using-default-resource"), + 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.info(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 = baseResourceLoader.getClassLoader().getResourceAsStream( + publicName); + } + custom = (is != null); + } + if (is == null && defaultName != null) { + is = baseResourceLoader.getResourceAsStream(defaultName); + } + if (verbose && is != null) { + String msg = null; + if (custom) { + msg = MessageFormat.format(I18N.getString( + "message.using-custom-resource-from-classpath"), + category == null ? + "" : "[" + category + "] ", publicName); + } else { + msg = MessageFormat.format(I18N.getString( + "message.using-default-resource-from-classpath"), + category == null ? + "" : "[" + category + "] ", publicName); + } + Log.info(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()); + } + } +} --- /dev/null 2018-10-22 10:36:24.000000000 -0400 +++ new/src/jdk.packager/share/classes/jdk/packager/internal/AbstractImageBundler.java 2018-10-22 10:36:22.419034300 -0400 @@ -0,0 +1,120 @@ +/* + * 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.packager.internal; + +import java.text.MessageFormat; +import java.util.Map; +import java.util.ResourceBundle; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import static jdk.packager.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.packager.internal.resources.AbstractImageBundler"); + + 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", ""); + } + } + } +} --- /dev/null 2018-10-22 10:36:33.000000000 -0400 +++ new/src/jdk.packager/share/classes/jdk/packager/internal/ArgAction.java 2018-10-22 10:36:31.404864700 -0400 @@ -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.packager.internal; + +@FunctionalInterface +public interface ArgAction { + public void execute(); +} --- /dev/null 2018-10-22 10:36:41.000000000 -0400 +++ new/src/jdk.packager/share/classes/jdk/packager/internal/Arguments.java 2018-10-22 10:36:38.893056700 -0400 @@ -0,0 +1,873 @@ +/* + * 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.packager.internal; + +import jdk.packager.internal.bundlers.BundlerType; +import jdk.packager.internal.bundlers.BundleParams; + +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 jpackager 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.packager.internal.resources.Arguments"); + + 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 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); + } + + 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()); + }), + + SINGLETON ("singleton", OptionCategories.PROPERTY, () -> { + setOptionValue("singleton", true); + }), + + NAME ("name", "n", OptionCategories.PROPERTY), + + IDENTIFIER ("identifier", OptionCategories.PROPERTY), + + VERBOSE ("verbose", OptionCategories.PROPERTY, () -> { + setOptionValue("verbose", true); + Log.setVerbose(true); + }), + + 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 ("version", "v", 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), + + 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_MSI_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; + } + + public 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; + } + + public String getIdWithPrefix() { + String prefix = isMode() ? "create-" : "--"; + return prefix + this.id; + } + + public String getShortIdWithPrefix() { + return this.shortId == null ? null : "-" + this.shortId; + } + + public void execute() { + if (action != null) { + action.execute(); + } else { + defaultAction(); + } + } + + public boolean isMode() { + return category == OptionCategories.MODE; + } + + public 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(); + } + } + + public enum OptionCategories { + MODE, + MODULAR, + PROPERTY, + PLATFORM_MAC, + PLATFORM_WIN, + PLATFORM_LINUX; + } + + private void initArgumentList(String[] args) { + argList = new ArrayList<>(Arrays.asList(args)); + pos = 0; + + deployParams = new DeployParams(); + bundleType = BundlerType.NONE; + + allOptions = new ArrayList<>(); + + secondaryLaunchers = new ArrayList<>(); + } + + public boolean processArguments() throws Exception { + try { + + // init context of arguments + CLIOptions.setContext(this); + + // parse cmd line + String arg; + CLIOptions option; + for (; CLIOptions.hasNextArg(); CLIOptions.nextArg()) { + arg = CLIOptions.getArg(); + // check if it's a CLI option + if ((option = toCLIOption(arg)) != null) { + allOptions.add(option); + option.execute(); + } else { + Log.info("Illegal argument ["+arg+"]"); + } + } + + if (allOptions.isEmpty() || !allOptions.get(0).isMode()) { + // first argument should always be a mode. + Log.info("ERROR: Mode is not specified"); + return false; + } + + if (!hasAppImage && !hasMainJar && !hasMainModule && + !hasMainClass && !jreInstaller) { + Log.info("ERROR: Main jar, main class, main module, " + + "or app-image must be specified."); + } else if (!hasMainModule && !hasMainClass) { + // try to get main-class from manifest + String mainClass = getMainClassFromManifest(); + if (mainClass != null) { + CLIOptions.setOptionValue( + CLIOptions.APPCLASS.getId(), mainClass); + } + } + + // display warning for arguments that are not supported + // for current configuration. + + validateArguments(); + + addResources(deployParams, input, files); + + deployParams.setBundleType(bundleType); + + List> 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"); + } + + generateBundle(bp.getBundleParamsAsMap()); + } catch (Exception e) { + if (Log.isVerbose()) { + throw e; + } else { + System.err.println(e.getMessage()); + if (e.getCause() != null && e.getCause() != e) { + System.err.println(e.getCause().getMessage()); + } + System.exit(-1); + } + } + return true; + } + + 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.packager.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 void generateBundle(Map params) + throws PackagerException { + for (jdk.packager.internal.Bundler bundler : getPlatformBundlers()) { + Map localParams = new HashMap<>(params); + try { + if (bundler.validate(localParams)) { + File result = + bundler.execute(localParams, deployParams.outdir); + bundler.cleanup(localParams); + if (result == null) { + throw new PackagerException("MSG_BundlerFailed", + bundler.getID(), bundler.getName()); + } + } + } catch (UnsupportedPlatformException e) { + Log.debug(MessageFormat.format( + I18N.getString("MSG_BundlerPlatformException"), + bundler.getName())); + } catch (ConfigException e) { + Log.debug(e); + if (e.getAdvice() != null) { + Log.info(MessageFormat.format( + I18N.getString("MSG_BundlerConfigException"), + bundler.getName(), e.getMessage(), e.getAdvice())); + } else { + Log.info(MessageFormat.format(I18N.getString( + "MSG_BundlerConfigExceptionNoAdvice"), + bundler.getName(), e.getMessage())); + } + } catch (RuntimeException re) { + Log.info(MessageFormat.format( + I18N.getString("MSG_BundlerRuntimeException"), + bundler.getName(), re.toString())); + Log.debug(re); + } + } + } + + private void addResources(DeployParams deployParams, + String inputdir, List inputfiles) { + + if (inputdir == null || inputdir.isEmpty()) { + return; + } + + File baseDir = new File(inputdir); + + if (!baseDir.isDirectory()) { + Log.info( + "Unable to add resources: \"-srcdir\" 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.info("Unable to add resources: " + e.getMessage()); + } + } + fileNames.forEach(file -> deployParams.addResource(baseDir, file)); + setClasspath(fileNames); + } + + private void setClasspath(List inputfiles) { + String classpath = ""; + for (String file : inputfiles) { + if (file.endsWith(".jar")) { + classpath += file; + classpath += File.pathSeparator; + } + } + deployParams.addBundleArgument( + StandardBundlerParam.CLASSPATH.getID(), classpath); + } + + public static boolean isCLIOption(String arg) { + return toCLIOption(arg) != null; + } + + public 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); + in.close(); + } catch (IOException e) { + Log.info("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; + } + + public static boolean isJreInstaller() { + return jreInstaller; + } +} --- /dev/null 2018-10-22 10:36:49.000000000 -0400 +++ new/src/jdk.packager/share/classes/jdk/packager/internal/BasicBundlers.java 2018-10-22 10:36:47.167269300 -0400 @@ -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.packager.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); + } +} --- /dev/null 2018-10-22 10:36:57.000000000 -0400 +++ new/src/jdk.packager/share/classes/jdk/packager/internal/Bundler.java 2018-10-22 10:36:55.319684800 -0400 @@ -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.packager.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. + */ + 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(); +} --- /dev/null 2018-10-22 10:37:07.000000000 -0400 +++ new/src/jdk.packager/share/classes/jdk/packager/internal/BundlerParamInfo.java 2018-10-22 10:37:04.929531200 -0400 @@ -0,0 +1,158 @@ +/* + * 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.packager.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 . + */ +public 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; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public String getID() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public Class getValueType() { + return valueType; + } + + public void setValueType(Class valueType) { + this.valueType = valueType; + } + + public Function, T> getDefaultValueFunction() { + return defaultValueFunction; + } + + public void setDefaultValueFunction( + Function, T> defaultValueFunction) { + this.defaultValueFunction = defaultValueFunction; + } + + public BiFunction,T> + getStringConverter() { + return stringConverter; + } + + public void setStringConverter(BiFunction, T> stringConverter) { + this.stringConverter = stringConverter; + } + + @SuppressWarnings("unchecked") + public final T fetchFrom(Map params) { + return fetchFrom(params, true); + } + + @SuppressWarnings("unchecked") + public 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; + } +} --- /dev/null 2018-10-22 10:37:15.000000000 -0400 +++ new/src/jdk.packager/share/classes/jdk/packager/internal/Bundlers.java 2018-10-22 10:37:12.511325600 -0400 @@ -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.packager.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); +} --- /dev/null 2018-10-22 10:37:26.000000000 -0400 +++ new/src/jdk.packager/share/classes/jdk/packager/internal/CLIHelp.java 2018-10-22 10:37:23.806015200 -0400 @@ -0,0 +1,70 @@ +/* + * 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.packager.internal; + +import java.util.ResourceBundle; + +/** + * CLIHelp + * + * Generate and show the command line interface help message(s). + */ +public class CLIHelp { + + private static final ResourceBundle I18N = ResourceBundle.getBundle( + "jdk.packager.internal.resources.CLIHelp"); + + // generates --help for jpackager's CLI + public static void showHelp(boolean all) { + + Platform platform = Platform.getPlatform(); + Log.info(I18N.getString("MSG_Help_common")); + + switch (platform) { + case MAC: + Log.info(I18N.getString("MSG_Help_mac")); + if (all) { + 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 (all) { + 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 (all) { + Log.info(I18N.getString("MSG_Help_linux")); + Log.info(I18N.getString("MSG_Help_mac")); + } + break; + } + } +} --- /dev/null 2018-10-22 10:37:34.000000000 -0400 +++ new/src/jdk.packager/share/classes/jdk/packager/internal/ConfigException.java 2018-10-22 10:37:32.058626800 -0400 @@ -0,0 +1,44 @@ +/* + * 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.packager.internal; + +public class ConfigException extends Exception { + final String advice; + + public ConfigException(String msg, String advice) { + super(msg); + this.advice = advice; + } + + public ConfigException(Exception cause) { + super(cause); + this.advice = null; + } + + public String getAdvice() { + return advice; + } +} --- /dev/null 2018-10-22 10:37:42.000000000 -0400 +++ new/src/jdk.packager/share/classes/jdk/packager/internal/DeployParams.java 2018-10-22 10:37:39.422015600 -0400 @@ -0,0 +1,569 @@ +/* + * 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.packager.internal; + +import jdk.packager.internal.bundlers.BundlerType; +import jdk.packager.internal.bundlers.BundleParams; + +import java.io.File; +import java.nio.file.Files; +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; + Boolean singleton; + + 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<>(); + + // list of jvm properties (can also be passed as VM args + // but keeping them separate make it a bit more convinient + Map properties = new LinkedHashMap<>(); + + // raw arguments to the bundler + Map bundlerArguments = new LinkedHashMap<>(); + + public void setId(String id) { + this.id = id; + } + + public void setCategory(String category) { + this.category = category; + } + + public void setLicenseType(String licenseType) { + this.licenseType = licenseType; + } + + public void setCopyright(String copyright) { + this.copyright = copyright; + } + + public void setVersion(String version) { + this.version = version; + } + + public void setSystemWide(Boolean systemWide) { + this.systemWide = systemWide; + } + + public void setServiceHint(Boolean serviceHint) { + this.serviceHint = serviceHint; + } + + public void setInstalldirChooser(Boolean installdirChooser) { + this.installdirChooser = installdirChooser; + } + + public void setSingleton(Boolean singleton) { + this.singleton = singleton; + } + + public void setSignBundle(Boolean signBundle) { + this.signBundle = signBundle; + } + + public void addJvmArg(String v) { + jvmargs.add(v); + } + + public void addJvmProperty(String n, String v) { + properties.put(n, v); + } + + public void setArguments(List args) { + this.arguments = args; + } + + public List getArguments() { + return this.arguments; + } + + public void addArgument(String arg) { + this.arguments.add(arg); + } + + public void addAddModule(String value) { + if (addModules == null) { + addModules = value; + } + else { + addModules += "," + value; + } + } + + public void addLimitModule(String value) { + if (limitModules == null) { + limitModules = value; + } + else { + limitModules += "," + value; + } + } + + public String getModulePath() { + return this.modulePath; + } + + public void setModulePath(String value) { + this.modulePath = value; + } + + public void setModule(String value) { + this.module = value; + } + + public void setDebug(String value) { + this.debugPort = value; + } + + public void setStripNativeCommands(boolean value) { + this.stripNativeCommands = value; + } + + public void setDescription(String description) { + this.description = description; + } + + public void setAppId(String id) { + appId = id; + } + + public void setParams(List params) { + this.params = params; + } + + public void setTitle(String title) { + this.title = title; + } + + public void setVendor(String vendor) { + this.vendor = vendor; + } + + public void setEmail(String email) { + this.email = email; + } + + public void setApplicationClass(String applicationClass) { + this.applicationClass = applicationClass; + } + + public void setJreInstaller(boolean value) { + jreInstaller = value; + } + + public 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 jpackager) + // 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)))); + } + + 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); + } + + public 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, + // then check if it is acceptable extended ASCII or unicode character. + if (a < ' ' || a > '~' || a == '%') { + // Reject '%', whitespaces and ISO Control. + // Accept anything else including special characters 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); + + // 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); + } + } + } + + public 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 + + public void setBundleType(BundlerType type) { + bundleType = type; + } + + public BundlerType getBundleType() { + return bundleType; + } + + public void setTargetFormat(String t) { + targetFormat = t; + } + + public 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_PROPERTIES.getID(), + 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); + } + } + + public 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.setServiceHint(serviceHint); + bundleParams.setInstalldirChooser(installdirChooser); + bundleParams.setSingleton(singleton); + bundleParams.setCopyright(copyright); + bundleParams.setApplicationCategory(category); + bundleParams.setDescription(description); + bundleParams.setTitle(title); + + bundleParams.setJvmProperties(properties); + 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; + } + + public Map getBundlerArguments() { + return this.bundlerArguments; + } + + public void putUnlessNull(String param, Object value) { + if (value != null) { + bundlerArguments.put(param, value); + } + } + + public void putUnlessNullOrEmpty(String param, Map value) { + if (value != null && !value.isEmpty()) { + bundlerArguments.put(param, value); + } + } + + public void putUnlessNullOrEmpty(String param, Collection value) { + if (value != null && !value.isEmpty()) { + bundlerArguments.put(param, value); + } + } + + @Override + public String toString() { + return "DeployParams{" + "outdir=" + outdir + '}'; + } + +} --- /dev/null 2018-10-22 10:37:50.000000000 -0400 +++ new/src/jdk.packager/share/classes/jdk/packager/internal/EnumeratedBundlerParam.java 2018-10-22 10:37:47.366619600 -0400 @@ -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.packager.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);
+ * }
+ * + */ +public 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; + + public 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; + } + + public boolean isInPossibleValues(T value) { + return elements.values().contains(value); + } + + // Having the displayable values as the keys seems a bit wacky + public Set getDisplayableKeys() { + return Collections.unmodifiableSet(elements.keySet()); + } + + // mapping from a "displayable" key to an "identifier" value. + public T getValueForDisplayableKey(String displayableKey) { + return elements.get(displayableKey); + } + + public boolean isStrict() { + return strict; + } + + public boolean isLoose() { + return !isStrict(); + } + + public 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); + } + +} --- /dev/null 2018-10-22 10:37:59.000000000 -0400 +++ new/src/jdk.packager/share/classes/jdk/packager/internal/IOUtils.java 2018-10-22 10:37:56.322249300 -0400 @@ -0,0 +1,289 @@ +/* + * 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.packager.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 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 if (verbose) { + Log.info(lineRead); + } else { + Log.debug(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.packager.internal.Log.verbose(ioe); + } + }); + t.setDaemon(true); + t.start(); + + logErrorStream(p); + + int ret = p.waitFor(); + + result.clear(); + result.addAll(list); + + return ret; + } +} --- /dev/null 2018-10-22 10:38:07.000000000 -0400 +++ new/src/jdk.packager/share/classes/jdk/packager/internal/InvalidBundlerParamException.java 2018-10-22 10:38:05.107274700 -0400 @@ -0,0 +1,32 @@ +/* + * 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.packager.internal; + +public class InvalidBundlerParamException extends RuntimeException { + public InvalidBundlerParamException(String message) { + super(message); + } +} --- /dev/null 2018-10-22 10:38:16.000000000 -0400 +++ new/src/jdk.packager/share/classes/jdk/packager/internal/JLinkBundlerHelper.java 2018-10-22 10:38:13.250683500 -0400 @@ -0,0 +1,502 @@ +/* + * 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.packager.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 jdk.packager.internal.builders.AbstractAppImageBuilder; +import jdk.tools.jlink.internal.packager.AppRuntimeImageBuilder; + +public final class JLinkBundlerHelper { + + private static final ResourceBundle I18N = ResourceBundle.getBundle( + "jdk.packager.internal.resources.JLinkBundlerHelper"); + private static final String JRE_MODULES_FILENAME = + "jdk/packager/internal/resources/jre.list"; + private static final String SERVER_JRE_MODULES_FILENAME = + "jdk/packager/internal/resources/jre.module.list"; + + private JLinkBundlerHelper() {} + + @SuppressWarnings("unchecked") + public 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") + public static final BundlerParamInfo DEBUG = + new StandardBundlerParam<>( + "", + "", + "-J-Xdebug", + Integer.class, + p -> null, + (s, p) -> { + return Integer.valueOf(s); + }); + + public static String listOfPathToString(List value) { + String result = ""; + + for (Path path : value) { + if (result.length() > 0) { + result += File.pathSeparator; + } + + result += path.toString(); + } + + return result; + } + + public static String setOfStringToString(Set value) { + String result = ""; + + for (String element : value) { + if (result.length() > 0) { + result += ","; + } + + result += element; + } + + return result; + } + + public 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; + } + + public 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; + } + + public 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; + } + + public 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 = getRedistributableModules(modulePath, + StandardBundlerParam.ADD_MODULES.fetchFrom(params), + limitModules, JRE_MODULES_FILENAME); + + if (javaBasePath != null && javaBasePath.toFile().exists()) { + result = RedistributableModules.getModuleVersion( + javaBasePath.toFile(), modulePath, addModules, limitModules); + } + + return result; + } + + public 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 getRedistributableModules(List modulePath, + Set addModules, Set limitModules, String filename) { + ModuleHelper moduleHelper = new ModuleHelper( + modulePath, addModules, limitModules, filename); + return removeInvalidModules(modulePath, moduleHelper.modules()); + } + + public 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 (mainJar == null && + 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-redistributable 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())); + } + } + + Set redistModules = getRedistributableModules( + modulePath, addModules, limitModules, JRE_MODULES_FILENAME); + addModules.addAll(redistModules); + + if (imageBuilder.getPlatformSpecificModulesFile() != null) { + Set platformModules = + RedistributableModules.getRedistributableModules( + modulePath, imageBuilder.getPlatformSpecificModulesFile()); + addModules.addAll(platformModules); + } + + Log.info(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(); + } + + public static void generateServerJre(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 = getRedistributableModules(modulePath, + addModules, limitModules, SERVER_JRE_MODULES_FILENAME); + addModules.addAll(redistModules); + + if (imageBuilder.getPlatformSpecificModulesFile() != null) { + Set platformModules = + RedistributableModules.getRedistributableModules( + modulePath, imageBuilder.getPlatformSpecificModulesFile()); + addModules.addAll(platformModules); + } + + Log.info(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.prepareServerJreFiles(); + } + + // Returns the path to the JDK modules in the user defined module path. + public 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.info(MessageFormat.format( + I18N.getString("warning.module.does.not.exist"), name)); + } + } + + return result; + } + + private static class ModuleHelper { + // The token for "all modules on the module path". + private static final String ALL_MODULE_PATH = "ALL-MODULE-PATH"; + + // The token for "all redistributable runtime modules". + public static final String ALL_RUNTIME = "ALL-RUNTIME"; + + private final Set modules = new HashSet<>(); + private enum Macros {None, AllModulePath, AllRuntime} + + public ModuleHelper(List paths, Set roots, + Set limitMods, String filename) { + 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 m = + RedistributableModules.getRedistributableModules( + paths, filename); + + if (m != null) { + this.modules.addAll(m); + } + + break; + } + } + + public Set modules() { + return modules; + } + + 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; + } + } +} --- /dev/null 2018-10-22 10:38:23.000000000 -0400 +++ new/src/jdk.packager/share/classes/jdk/packager/internal/JPackagerToolProvider.java 2018-10-22 10:38:21.254688800 -0400 @@ -0,0 +1,52 @@ +/* + * 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.packager.internal; + +import java.io.PrintWriter; +import java.util.spi.ToolProvider; + +/** + * JPackagerToolProvider + * + * This is the ToolProvider implementation exported + * to java.util.spi.ToolProvider and ultimately javax.tools.ToolProvider + */ +public class JPackagerToolProvider implements ToolProvider { + + public String name() { + return "jpackager"; + } + + public synchronized int run( + PrintWriter out, PrintWriter err, String... args) { + try { + jdk.packager.main.Main.run(out, err, args); + } catch (Exception ignored) { + return 1; + } + return 0; + } +} --- /dev/null 2018-10-22 10:38:31.000000000 -0400 +++ new/src/jdk.packager/share/classes/jdk/packager/internal/Log.java 2018-10-22 10:38:28.961286400 -0400 @@ -0,0 +1,213 @@ +/* + * 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.packager.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 infof(String format, Object... args) { + if (out != null) { + out.printf(format, args); + } else { + System.out.printf(format, args); + } + } + + 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("JPACKAGER_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 infof(String format, Object... args) { + if (delegate != null) { + delegate.infof(format, args); + } + } + + 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(RuntimeException re) { + debug((Throwable) re); + } + + public static void debug(Throwable t) { + try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) { + try (PrintStream ps = new PrintStream(baos)) { + t.printStackTrace(ps); + } + debug(baos.toString()); + } catch (IOException e) { + e.printStackTrace(); + } + } + + public static boolean isDebug() { + return debug; + } + + public static void setDebug(boolean debug) { + Log.debug = debug; + } +} --- /dev/null 2018-10-22 10:38:39.000000000 -0400 +++ new/src/jdk.packager/share/classes/jdk/packager/internal/ModFile.java 2018-10-22 10:38:36.605482400 -0400 @@ -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.packager.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; + +public final class ModFile { + private final String filename; + private final ModType moduleType; + + public enum JarType {All, UnnamedJar, ModularJar} + public enum ModType { + Unknown, UnnamedJar, ModularJar, Jmod, ExplodedModule} + + public ModFile(File aFile) { + super(); + filename = aFile.getPath(); + moduleType = getModType(aFile); + } + + public String getModName() { + File file = new File(getFileName()); + // do not try to remove extension for directories + return moduleType == ModType.ExplodedModule ? + file.getName() : getFileWithoutExtension(file.getName()); + } + + public String getFileName() { + return filename; + } + + public 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("[.][^.]+$", ""); + } +} --- /dev/null 2018-10-22 10:38:48.000000000 -0400 +++ new/src/jdk.packager/share/classes/jdk/packager/internal/ModuleManager.java 2018-10-22 10:38:45.747316800 -0400 @@ -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.packager.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; + +public final class ModuleManager { + private final List folders = new ArrayList(); + + public enum SearchType {UnnamedJar, ModularJar, Jmod, ExplodedModule} + + public 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); + } + + public ModuleManager(List Paths) { + super(); + initialize(Paths); + } + + private void initialize(List Paths) { + for (Path path : Paths) { + folders.add(path.toString().replaceAll("^\"|\"$", "")); + } + } + + public List getModules() { + return getModules(EnumSet.of(SearchType.UnnamedJar, + SearchType.ModularJar, SearchType.Jmod, + SearchType.ExplodedModule)); + } + + public 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; + } +} --- /dev/null 2018-10-22 10:38:56.000000000 -0400 +++ new/src/jdk.packager/share/classes/jdk/packager/internal/PackagerException.java 2018-10-22 10:38:53.160507100 -0400 @@ -0,0 +1,58 @@ +/* + * 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.packager.internal; + +import java.text.MessageFormat; +import java.util.ResourceBundle; + +public class PackagerException extends Exception { + private static final ResourceBundle bundle = + ResourceBundle.getBundle("jdk.packager.internal.resources.Bundle"); + + public PackagerException(Throwable cause) { + super(cause); + } + + public PackagerException(String key, Throwable cause) { + super(bundle.getString(key), cause); + } + + public PackagerException(String key) { + super(bundle.getString(key)); + } + + public PackagerException(String key, String ... arguments) { + super(MessageFormat.format( + bundle.getString(key), (Object[]) arguments)); + } + + public PackagerException( + Throwable cause, String key, String ... arguments) { + super(MessageFormat.format(bundle.getString(key), + (Object[]) arguments), cause); + } + +} --- /dev/null 2018-10-22 10:39:05.000000000 -0400 +++ new/src/jdk.packager/share/classes/jdk/packager/internal/Param.java 2018-10-22 10:39:03.831180700 -0400 @@ -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.packager.internal; + +public class Param { + String name; + String value; + + public void setName(String name) { + this.name = name; + } + + public void setValue(String value) { + this.value = value; + } + +} --- /dev/null 2018-10-22 10:39:13.000000000 -0400 +++ new/src/jdk.packager/share/classes/jdk/packager/internal/Platform.java 2018-10-22 10:39:11.085366700 -0400 @@ -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.packager.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 + * } + * } + * + */ +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.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() {} + + public static Platform getPlatform() { + return platform; + } + + public static int getMajorVersion() { + return majorVersion; + } + + public static int getMinorVersion() { + return minorVersion; + } +} --- /dev/null 2018-10-22 10:39:20.000000000 -0400 +++ new/src/jdk.packager/share/classes/jdk/packager/internal/RedistributableModules.java 2018-10-22 10:39:18.308351900 -0400 @@ -0,0 +1,149 @@ +/* + * 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.packager.internal; + +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.UnsupportedEncodingException; +import java.util.Optional; +import java.lang.module.ModuleDescriptor; +import java.lang.module.ModuleFinder; +import java.lang.module.ModuleReference; +import java.lang.module.ModuleReader; +import java.nio.file.Path; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.NoSuchElementException; +import java.util.Set; + +import jdk.tools.jlink.internal.packager.AppRuntimeImageBuilder; + +public final class RedistributableModules { + private static final String JDK_PACKAGER_MODULE = "jdk.packager"; + + private RedistributableModules() {} + + public static String stripComments(String line) { + String result = line.trim(); + int i = result.indexOf(";"); + + if (i >= 0) { + result = result.substring(0, i); + result = result.trim(); + } + + return result; + } + + public static Set getRedistributableModules(List modulePath, + String filename) { + Set result = null; + + Set addModules = new HashSet<>(); + Set limitModules = new HashSet<>(); + ModuleFinder finder = AppRuntimeImageBuilder.moduleFinder( + modulePath, addModules, limitModules); + Optional mref = finder.find(JDK_PACKAGER_MODULE); + + if (mref.isPresent()) { + ModuleReader reader = null; + + try { + reader = mref.get().open(); + } catch (NoSuchElementException | IOException ex) { + } + + if (reader != null) { + Optional stream = null; + + try { + stream = reader.open(filename); + } catch (IOException ex) { + } + + if (stream != null) { + if (stream.isPresent()) { + BufferedReader br = null; + + try { + br = new BufferedReader(new InputStreamReader( + stream.get(), "UTF-8")); + } catch (UnsupportedEncodingException ex) { + } + + if (br != null) { + result = new LinkedHashSet(); + String line; + + try { + while ((line = br.readLine()) != null) { + String module = stripComments(line); + + if (!module.isEmpty()) { + result.add(module); + } + } + } catch (IOException ex) { + } + } + } + } + } + } + + return result; + } + + public 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; + } +} --- /dev/null 2018-10-22 10:39:33.000000000 -0400 +++ new/src/jdk.packager/share/classes/jdk/packager/internal/RelativeFileSet.java 2018-10-22 10:39:31.585292400 -0400 @@ -0,0 +1,152 @@ +/* + * 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.packager.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. + */ +public class RelativeFileSet { + + private String mode; + private String os; + private String arch; + + private File basedir; + private Set files = new LinkedHashSet<>(); + + public RelativeFileSet(RelativeFileSet copy) { + mode = copy.mode; + os = copy.os; + arch = copy.arch; + basedir = copy.basedir; + files = new LinkedHashSet<>(copy.files); + } + + public 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 jpackager case + this.files.add(absolute.substring(baseAbsolute.length()+1)); + } + } + } + + public 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; + } + + public RelativeFileSet(File base, Set files) { + this(base, (Collection) files); + } + + public 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; + } + + public boolean contains(String requiredFile) { + if (files.contains(requiredFile)) { + return true; + } else { + Log.debug("RelativeFileSet does not contain [" +requiredFile+ "]"); + return false; + } + } + + public File getBaseDirectory() { + return basedir; + } + + public Set getIncludedFiles() { + return files; + } + + public void dump() { + Log.verbose("\n=========\nBasedir: " + basedir + "\n"); + for (String fname : files) { + Log.verbose(" " + fname); + } + Log.verbose("\n========"); + } + + public String getMode() { + return mode; + } + + public void setMode(String mode) { + this.mode = mode; + } + + public String getOs() { + return os; + } + + public void setOs(String os) { + this.os = os; + } + + public String getArch() { + return arch; + } + + public void setArch(String arch) { + this.arch = arch; + } + + @Override + public String toString() { + return "RelativeFileSet{basedir:" + basedir + ", files:" + files + "}"; + } + +} --- /dev/null 2018-10-22 10:39:41.000000000 -0400 +++ new/src/jdk.packager/share/classes/jdk/packager/internal/SecondaryLauncherArguments.java 2018-10-22 10:39:39.073484400 -0400 @@ -0,0 +1,167 @@ +/* + * 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.packager.internal; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.io.File; +import jdk.packager.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 + * + */ +public class SecondaryLauncherArguments { + + private final String filename; + private Map allArgs; + private Map bundleParams; + + public 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)); + + putUnlessNull(bundleParams, Arguments.CLIOptions.SINGLETON.getId(), + getOptionValue(CLIOptions.SINGLETON)); + + 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; + } + + public 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); + } + } +} --- /dev/null 2018-10-22 10:39:49.000000000 -0400 +++ new/src/jdk.packager/share/classes/jdk/packager/internal/StandardBundlerParam.java 2018-10-22 10:39:47.014088000 -0400 @@ -0,0 +1,898 @@ +/* + * 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.packager.internal; + +import jdk.packager.internal.bundlers.BundleParams; +import jdk.packager.internal.builders.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. + */ +public class StandardBundlerParam extends BundlerParamInfo { + + private static final ResourceBundle I18N = ResourceBundle.getBundle( + "jdk.packager.internal.resources.StandardBundlerParam"); + private static final String JAVABASEJMOD = "java.base.jmod"; + + public 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; + } + + public 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") + public 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 + ); + + @SuppressWarnings("unchecked") + public 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; + } + } + ); + + public static final StandardBundlerParam> SOURCE_FILES = + new StandardBundlerParam<>( + I18N.getString("param.source-files.name"), + I18N.getString("param.source-files.description"), + Arguments.CLIOptions.FILES.getId(), + (Class>) (Object) List.class, + p -> null, + (s, p) -> null + ); + + // note that each bundler is likely to replace this one with + // their own converter + public 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 + public 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, " ") + ); + + public 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 + ); + + public 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 + + public 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 + ); + + public 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) + ); + + public 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 + ); + + public 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 + ); + + public 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 + ); + + public 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") + public 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") + public 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")) + ); + + @SuppressWarnings("unchecked") + public static final + StandardBundlerParam> JVM_PROPERTIES = + new StandardBundlerParam<>( + I18N.getString("param.jvm-system-properties.name"), + I18N.getString("param.jvm-system-properties.description"), + "jvmProperties", + (Class>) (Object) Map.class, + params -> Collections.emptyMap(), + (s, params) -> { + Map map = new HashMap<>(); + try { + Properties p = new Properties(); + p.load(new StringReader(s)); + for (Map.Entry entry : p.entrySet()) { + map.put((String)entry.getKey(), + (String)entry.getValue()); + } + } catch (IOException e) { + e.printStackTrace(); + } + return map; + } + ); + + public 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 + public 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(), + (Class>)(Object)List.class, + params -> Collections.emptyList(), + (s, p) -> Arrays.asList(s.split(",")) + ); + + public 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.packager").toFile(); + } catch (IOException ioe) { + return null; + } + }, + (s, p) -> new File(s) + ); + + public 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 + ); + + public 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 + ); + + public 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) + ); + + public static final StandardBundlerParam DROP_IN_RESOURCES_ROOT = + new StandardBundlerParam<>( + I18N.getString("param.drop-in-resources-root.name"), + I18N.getString("param.drop-in-resources-root.description"), + "dropinResourcesRoot", + File.class, + params -> new File("."), + (s, p) -> new File(s) + ); + + public 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 + ); + + public 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)); + + public 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") + public 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") + public 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") + public 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") + public 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)+")) + ); + + public 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 + ); + + public 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") + public 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)) { + jdk.packager.internal.Log.info( + String.format(I18N.getString( + "warning.no.jdk.modules.found"))); + } + + return modulePath; + }); + + @SuppressWarnings("unchecked") + public 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") + public 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") + public 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(","))) + ); + + @SuppressWarnings("unchecked") + public 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) + ); + + public static final BundlerParamInfo SINGLETON = + new StandardBundlerParam<> ( + I18N.getString("param.singleton.name"), + I18N.getString("param.singleton.description"), + Arguments.CLIOptions.SINGLETON.getId(), + Boolean.class, + params -> Boolean.FALSE, + (s, p) -> Boolean.valueOf(s) + ); + + public 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; + } + + public 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())); + } + IOUtils.copyRecursive(image.toPath(), appBuilder.getRoot()); + appBuilder.prepareApplicationFiles(); + } + + public 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(); + } + } + } + + public 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 if (hasMainJarClassPath) { + throw new ConfigException( + I18N.getString("error.no-main-class-with-classpath"), + I18N.getString( + "error.no-main-class-with-classpath.advice")); + } 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 moduleName, Map params) { + for (RelativeFileSet rfs : APP_RESOURCES_LIST.fetchFrom(params)) { + File appResourcesRoot = rfs.getBaseDirectory(); + File mainJarFile = new File(appResourcesRoot, moduleName); + + if (mainJarFile.exists()) { + return new RelativeFileSet(appResourcesRoot, + new LinkedHashSet<>(Collections.singletonList( + mainJarFile))); + } + else { + List modulePath = MODULE_PATH.fetchFrom(params); + Path modularJarPath = JLinkBundlerHelper.findPathOfModule( + modulePath, moduleName); + + 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"), + moduleName), I18N.getString( + "error.main-jar-does-not-exist.advice"))); + } + + public 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; + } +} --- /dev/null 2018-10-22 10:39:55.000000000 -0400 +++ new/src/jdk.packager/share/classes/jdk/packager/internal/UnsupportedPlatformException.java 2018-10-22 10:39:53.550655600 -0400 @@ -0,0 +1,29 @@ +/* + * 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.packager.internal; + +public class UnsupportedPlatformException extends Exception { +} --- /dev/null 2018-10-22 10:40:02.000000000 -0400 +++ new/src/jdk.packager/share/classes/jdk/packager/internal/ValidOptions.java 2018-10-22 10:40:00.196426000 -0400 @@ -0,0 +1,220 @@ +/* + * 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.packager.internal; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import jdk.packager.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. + */ +public 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.SINGLETON); + add(CLIOptions.CREATE_IMAGE, CLIOptions.NAME); + add(CLIOptions.CREATE_IMAGE, CLIOptions.IDENTIFIER); + add(CLIOptions.CREATE_IMAGE, CLIOptions.VERBOSE); + 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); + + 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_MSI_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_MSI_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; + } + + public static void add(CLIOptions mode, CLIOptions arg) { + if (mode.equals(arg)) { + return; + } + options.computeIfAbsent(mode, + k -> new HashSet<>()).add(arg); + } + + public 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; + } +} --- /dev/null 2018-10-22 10:40:10.000000000 -0400 +++ new/src/jdk.packager/share/classes/jdk/packager/internal/builders/AbstractAppImageBuilder.java 2018-10-22 10:40:07.310208400 -0400 @@ -0,0 +1,278 @@ +/* + * 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.packager.internal.builders; + +import jdk.packager.internal.IOUtils; +import jdk.packager.internal.Log; +import jdk.packager.internal.StandardBundlerParam; +import jdk.packager.internal.JLinkBundlerHelper; +import jdk.packager.internal.ModFile; + +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 static jdk.packager.internal.StandardBundlerParam.*; +import static jdk.packager.internal.StandardBundlerParam.ARGUMENTS; + +public abstract class AbstractAppImageBuilder { + + private static final ResourceBundle I18N = + ResourceBundle.getBundle( + "jdk.packager.internal.resources.builders.AbstractAppImageBuilder"); + + //do not use file separator - + // we use it for classpath lookup and there / are not platform specific + public final static String BUNDLER_PREFIX = "package/"; + + 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 abstract InputStream getResourceAsStream(String name); + public abstract void prepareApplicationFiles() throws IOException; + public abstract void prepareServerJreFiles() throws IOException; + + 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-from-classpath"), + 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-from-classpath"), + category == null ? "" : "[" + category + "] ", publicName); + } else { + msg = MessageFormat.format(I18N.getString( + "message.using-default-resource"), + category == null ? "" : "[" + category + "] ", publicName); + } + if (msg != null) { + Log.info(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("[ :;]"))); + out.println("app.application.instance=" + + (SINGLETON.fetchFrom(params) ? "single" : "multiple")); + + // 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("packager.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); + } + Map jvmProps = JVM_PROPERTIES.fetchFrom(params); + for (Map.Entry property : jvmProps.entrySet()) { + out.println("-D" + property.getKey() + "=" + property.getValue()); + } + + 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(); + } + + public String getPlatformSpecificModulesFile() { + return null; + } + +} --- /dev/null 2018-10-22 10:40:17.000000000 -0400 +++ new/src/jdk.packager/share/classes/jdk/packager/internal/bundlers/BundleParams.java 2018-10-22 10:40:14.892002800 -0400 @@ -0,0 +1,490 @@ +/* + * 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.packager.internal.bundlers; + +import jdk.packager.internal.*; +import jdk.packager.internal.bundlers.BundlerType; +import jdk.packager.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.packager.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"; + + // List - File(s) with license. Format is OS/bundler specific + public static final String PARAM_LICENSE_FILE = "licenseFile"; + + // boolean - service/daemon install. null means "default" + public static final String PARAM_SERVICE_HINT = "serviceHint"; + + + // 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"; + + // boolean - Prevents from launching multiple instances of application. + public static final String PARAM_SINGLETON = "singleton"; + + /** + * 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 setJvmProperties(Map jvmProperties) { + putUnlessNullOrEmpty(JVM_PROPERTIES.getID(), jvmProperties); + } + + 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); + } + + //path is relative to the application root + public void addLicenseFile(String path) { + List licenseFiles = fetchParam(LICENSE_FILE); + if (licenseFiles == null || licenseFiles.isEmpty()) { + licenseFiles = new ArrayList<>(); + params.put(PARAM_LICENSE_FILE, licenseFiles); + } + licenseFiles.add(path); + } + + public void setServiceHint(Boolean b) { + putUnlessNull(PARAM_SERVICE_HINT, b); + } + + public void setInstalldirChooser(Boolean b) { + putUnlessNull(PARAM_INSTALLDIR_CHOOSER, b); + } + + public void setSingleton(Boolean b) { + putUnlessNull(PARAM_SINGLETON, 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 getLicenseFile() { + return fetchParam(LICENSE_FILE); + } + + public List getJvmargs() { + return JVM_OPTIONS.fetchFrom(params); + } + + public List getArguments() { + return ARGUMENTS.fetchFrom(params); + } + + // Validation approach: + // - javac and + // + // - /jmods dir + // or + // - JRE marker (rt.jar) + // - FX marker (jfxrt.jar) + // - JDK marker (tools.jar) + private static boolean checkJDKRoot(File jdkRoot) { + String exe = (Platform.getPlatform() == Platform.WINDOWS) ? + ".exe" : ""; + File javac = new File(jdkRoot, "bin/javac" + exe); + if (!javac.exists()) { + Log.verbose("javac is not found at " + javac.getAbsolutePath()); + return false; + } + + File jmods = new File(jdkRoot, "jmods"); + if (!jmods.exists()) { + Log.verbose("jmods is not found in " + jdkRoot.getAbsolutePath()); + return false; + } + return true; + } + + public jdk.packager.internal.RelativeFileSet getAppResource() { + return fetchParam(APP_RESOURCES); + } + + public void setAppResource(jdk.packager.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; + private String mainJarClassPath = null; + private boolean useFXPackaging = true; + + // For regular executable Jars we need to take care of classpath + // For JavaFX executable jars we do not need to pay attention to + // ClassPath entry in manifest + public String getAppClassPath() { + if (mainJar == null) { + // this will find out answer + getMainApplicationJar(); + } + if (useFXPackaging || mainJarClassPath == null) { + return ""; + } + return mainJarClassPath; + } + + // 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.packager.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; + mainJarClassPath = attrs.getValue( + Attributes.Name.CLASS_PATH); + 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); + } + } + +} --- /dev/null 2018-10-22 10:40:24.000000000 -0400 +++ new/src/jdk.packager/share/classes/jdk/packager/internal/bundlers/BundlerType.java 2018-10-22 10:40:22.099387600 -0400 @@ -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.packager.internal.bundlers; + +public enum BundlerType { + NONE, + IMAGE, // Generates app image only + INSTALLER // Generates installers +} --- /dev/null 2018-10-22 10:40:32.000000000 -0400 +++ new/src/jdk.packager/share/classes/jdk/packager/internal/resources/AbstractBundler.properties 2018-10-22 10:40:29.415975200 -0400 @@ -0,0 +1,33 @@ +# +# 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 + +message.using-default-resource=Using default package resource {0} (add {1} to the class path to customize) +message.using-custom-resource-from-file=Using custom package resource {0} (loaded from file {1}) +message.using-custom-resource-from-classpath=Using custom package resource {0} (loaded from {1}) +message.using-default-resource-from-classpath=Using default package resource {0} (add {1} to the class path to customize) --- /dev/null 2018-10-22 10:40:39.000000000 -0400 +++ new/src/jdk.packager/share/classes/jdk/packager/internal/resources/AbstractBundler_ja.properties 2018-10-22 10:40:37.543783600 -0400 @@ -0,0 +1,33 @@ +# +# 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 + +message.using-default-resource=\u30C7\u30D5\u30A9\u30EB\u30C8\u306E\u30D1\u30C3\u30B1\u30FC\u30B8\u30FB\u30EA\u30BD\u30FC\u30B9{0}\u306E\u4F7F\u7528({1}\u3092\u30AF\u30E9\u30B9\u30FB\u30D1\u30B9\u306B\u8FFD\u52A0\u3057\u3066\u30AB\u30B9\u30BF\u30DE\u30A4\u30BA) +message.using-custom-resource-from-file=\u30AB\u30B9\u30BF\u30E0\u30FB\u30D1\u30C3\u30B1\u30FC\u30B8\u30FB\u30EA\u30BD\u30FC\u30B9{0}\u306E\u4F7F\u7528(\u30D5\u30A1\u30A4\u30EB{1}\u304B\u3089\u30ED\u30FC\u30C9\u6E08) +message.using-custom-resource-from-classpath=\u30AB\u30B9\u30BF\u30E0\u30FB\u30D1\u30C3\u30B1\u30FC\u30B8\u30FB\u30EA\u30BD\u30FC\u30B9{0}\u306E\u4F7F\u7528({1}\u304B\u3089\u30ED\u30FC\u30C9\u6E08) +message.using-default-resource-from-classpath=\u30C7\u30D5\u30A9\u30EB\u30C8\u306E\u30D1\u30C3\u30B1\u30FC\u30B8\u30FB\u30EA\u30BD\u30FC\u30B9{0}\u306E\u4F7F\u7528({1}\u3092\u30AF\u30E9\u30B9\u30FB\u30D1\u30B9\u306B\u8FFD\u52A0\u3057\u3066\u30AB\u30B9\u30BF\u30DE\u30A4\u30BA) --- /dev/null 2018-10-22 10:40:46.000000000 -0400 +++ new/src/jdk.packager/share/classes/jdk/packager/internal/resources/AbstractBundler_zh_CN.properties 2018-10-22 10:40:43.914747400 -0400 @@ -0,0 +1,33 @@ +# +# 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 + +message.using-default-resource=\u4F7F\u7528\u9ED8\u8BA4\u7A0B\u5E8F\u5305\u8D44\u6E90 {0} (\u5C06 {1} \u6DFB\u52A0\u5230\u7C7B\u8DEF\u5F84\u4EE5\u5B9A\u5236) +message.using-custom-resource-from-file=\u4F7F\u7528\u5B9A\u5236\u7A0B\u5E8F\u5305\u8D44\u6E90 {0} (\u4ECE\u6587\u4EF6 {1} \u52A0\u8F7D) +message.using-custom-resource-from-classpath=\u4F7F\u7528\u5B9A\u5236\u7A0B\u5E8F\u5305\u8D44\u6E90 {0} (\u4ECE {1} \u52A0\u8F7D) +message.using-default-resource-from-classpath=\u4F7F\u7528\u9ED8\u8BA4\u7A0B\u5E8F\u5305\u8D44\u6E90 {0} (\u5C06 {1} \u6DFB\u52A0\u5230\u7C7B\u8DEF\u5F84\u4EE5\u5B9A\u5236) --- /dev/null 2018-10-22 10:40:52.000000000 -0400 +++ new/src/jdk.packager/share/classes/jdk/packager/internal/resources/AbstractImageBundler.properties 2018-10-22 10:40:50.591718600 -0400 @@ -0,0 +1,32 @@ +# +# 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. +# +# + +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. + --- /dev/null 2018-10-22 10:40:59.000000000 -0400 +++ new/src/jdk.packager/share/classes/jdk/packager/internal/resources/AbstractImageBundler_ja.properties 2018-10-22 10:40:57.659699900 -0400 @@ -0,0 +1,29 @@ +# +# 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. +# +# + +error.no-main-module=\u30e1\u30a4\u30f3\u30fb\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u30fb\u30e2\u30b8\u30e5\u30fc\u30eb\u304c\u3042\u308a\u307e\u305b\u3093\u3002 +error.no-main-module.advice=fx:module\u30bf\u30b9\u30af\u3092\u4f7f\u7528\u3057\u3066\u3001\u30e2\u30b8\u30e5\u30e9\u30fb\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u3092\u4f5c\u6210\u3057\u3066\u304f\u3060\u3055\u3044\u3002 + --- /dev/null 2018-10-22 10:41:07.000000000 -0400 +++ new/src/jdk.packager/share/classes/jdk/packager/internal/resources/AbstractImageBundler_zh_CN.properties 2018-10-22 10:41:04.746281900 -0400 @@ -0,0 +1,30 @@ +# +# 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. +# +# + +error.no-main-module=\u7f3a\u5c11\u4e3b\u5e94\u7528\u7a0b\u5e8f\u6a21\u5757\u3002 +error.no-main-module.advice=\u786e\u4fdd\u4f7f\u7528 fx:module \u4efb\u52a1\u521b\u5efa\u6a21\u5757\u5316\u5e94\u7528\u7a0b\u5e8f\u3002 + + --- /dev/null 2018-10-22 10:41:15.000000000 -0400 +++ new/src/jdk.packager/share/classes/jdk/packager/internal/resources/Arguments.properties 2018-10-22 10:41:12.187627000 -0400 @@ -0,0 +1,41 @@ +# +# 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.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. + +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} --- /dev/null 2018-10-22 10:41:21.000000000 -0400 +++ new/src/jdk.packager/share/classes/jdk/packager/internal/resources/Arguments_ja.properties 2018-10-22 10:41:19.495568100 -0400 @@ -0,0 +1,41 @@ +# +# 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.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. + +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} --- /dev/null 2018-10-22 10:41:28.000000000 -0400 +++ new/src/jdk.packager/share/classes/jdk/packager/internal/resources/Arguments_zh_CN.properties 2018-10-22 10:41:25.661687000 -0400 @@ -0,0 +1,41 @@ +# +# 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.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. + +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} --- /dev/null 2018-10-22 10:41:35.000000000 -0400 +++ new/src/jdk.packager/share/classes/jdk/packager/internal/resources/Bundle.properties 2018-10-22 10:41:33.134230700 -0400 @@ -0,0 +1,43 @@ +# +# 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_Version=jpackager version + +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 + +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} --- /dev/null 2018-10-22 10:41:42.000000000 -0400 +++ new/src/jdk.packager/share/classes/jdk/packager/internal/resources/Bundle_ja.properties 2018-10-22 10:41:40.450771400 -0400 @@ -0,0 +1,43 @@ +# +# 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_Version=Java\u30D1\u30C3\u30B1\u30FC\u30B8\u30E3\u30FB\u30D0\u30FC\u30B8\u30E7\u30F3 + +ERR_MissingArgument=\u30A8\u30E9\u30FC: \u5F15\u6570\u304C\u3042\u308A\u307E\u305B\u3093: {0} +ERR_MissingAppResources=\u30A8\u30E9\u30FC: \u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3jar\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093\u3067\u3057\u305F +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 + + +MSG_BundlerFailed=\u30A8\u30E9\u30FC: \u30D0\u30F3\u30C9\u30E9"{1}" ({0})\u304C\u30D0\u30F3\u30C9\u30EB\u306E\u751F\u6210\u306B\u5931\u6557\u3057\u307E\u3057\u305F\u3002 +MSG_BundlerPlatformException=\u30D0\u30F3\u30C9\u30E9{0}\u306F\u3053\u306E\u30D7\u30E9\u30C3\u30C8\u30D5\u30A9\u30FC\u30E0\u3067\u306E\u30D0\u30F3\u30C9\u30EB\u3092\u30B5\u30DD\u30FC\u30C8\u3057\u3066\u3044\u306A\u3044\u305F\u3081\u3001\u30D0\u30F3\u30C9\u30E9\u304C\u30B9\u30AD\u30C3\u30D7\u3055\u308C\u307E\u3057\u305F\u3002 +MSG_BundlerConfigException=\u69CB\u6210\u306E\u554F\u984C\u306E\u305F\u3081\u3001\u30D0\u30F3\u30C9\u30E9{0}\u304C\u30B9\u30AD\u30C3\u30D7\u3055\u308C\u307E\u3057\u305F: {1} \n\u6B21\u306E\u4FEE\u6B63\u3092\u884C\u3063\u3066\u304F\u3060\u3055\u3044: {2} +MSG_BundlerConfigExceptionNoAdvice=\u69CB\u6210\u306E\u554F\u984C\u306E\u305F\u3081\u3001\u30D0\u30F3\u30C9\u30E9{0}\u304C\u30B9\u30AD\u30C3\u30D7\u3055\u308C\u307E\u3057\u305F: {1} +MSG_BundlerRuntimeException={1}\u306E\u305F\u3081\u3001\u30D0\u30F3\u30C9\u30E9{0}\u304C\u5931\u6557\u3057\u307E\u3057\u305F --- /dev/null 2018-10-22 10:41:50.000000000 -0400 +++ new/src/jdk.packager/share/classes/jdk/packager/internal/resources/Bundle_zh_CN.properties 2018-10-22 10:41:47.942915800 -0400 @@ -0,0 +1,42 @@ +# +# 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_Version=Java \u6253\u5305\u7A0B\u5E8F\u7248\u672C + +ERR_MissingArgument=\u9519\u8BEF: \u7F3A\u5C11\u53C2\u6570: {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 + +MSG_BundlerFailed=\u9519\u8BEF: \u6253\u5305\u7A0B\u5E8F "{1}" ({0}) \u65E0\u6CD5\u751F\u6210\u5305\u3002 +MSG_BundlerPlatformException=\u7531\u4E8E\u6253\u5305\u7A0B\u5E8F{0}\u4E0D\u652F\u6301\u5728\u6B64\u5E73\u53F0\u4E0A\u6253\u5305, \u56E0\u6B64\u5C06\u5176\u8DF3\u8FC7\u3002 +MSG_BundlerConfigException=\u7531\u4E8E\u914D\u7F6E\u95EE\u9898, \u8DF3\u8FC7\u4E86\u6253\u5305\u7A0B\u5E8F{0}: {1} \n\u4FEE\u590D\u5EFA\u8BAE: {2} +MSG_BundlerConfigExceptionNoAdvice=\u7531\u4E8E\u914D\u7F6E\u95EE\u9898, \u8DF3\u8FC7\u4E86\u6253\u5305\u7A0B\u5E8F{0}: {1} +MSG_BundlerRuntimeException=\u7531\u4E8E{1}, \u6253\u5305\u7A0B\u5E8F{0}\u5931\u8D25 --- /dev/null 2018-10-22 10:41:56.000000000 -0400 +++ new/src/jdk.packager/share/classes/jdk/packager/internal/resources/CLIHelp.properties 2018-10-22 10:41:54.073833700 -0400 @@ -0,0 +1,190 @@ +# +# 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: jpackager \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 packages\n\ +\ for current platform will be generated.\n\ +\ create-jre-installer \n\ +\ Generates a platform-specific installer for Server 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 packages\n\ +\ for current platform will be generated.\n\ +\ \n\ +Sample usages:\n\ +--------------\n\ +jpackager create-image --input inputdir --output outputdir --name AppName --class package.ClassName\n\ +or\n\ +jpackager create-image -i inputdir -o outputdir -n AppName -c package.ClassName\n\ +Generates an application image.\n\ +\n\ +jpackager create-installer -i inputdir -o outputdir -n "App Name" -c package.ClassName\n\ +Generates an application installer.\n\ +\n\ +jpackager create-jre-installer -n -o outputdir\n\ +Generates a Server JRE installer.\n\ +\n\ +The following options are valid for all platforms:\n\ +\ --help -h \n\ +\ Shows the usage text, followed by a list and description of each valid option for the current platform and the given mode.\n\ +\ If no mode is given, shows the usage text, followed by a list and description of each valid option for the current platform.\n\ +\ When this option is used, all other options are ignored.\n\ +\ --output -o \n\ +\ Name of the directory where generated output file is placed.\n\ +\ --input -i \n\ +\ Name of the base directory that contains the files to package.\n\ +\ --files -f \n\ +\ List of files in the base directory. If omitted, all files from "input"\n\ +\ directory (which is a mandatory argument in this case) will be packaged.\n\ +\ --name -n \n\ +\ Name of the application.\n\ +\ --main-jar -j
\n\ +\ The main JAR of the application. This JAR should have the main-class,\n\ +\ and is relative to the assembled application directory.\n\ +\ --class -c \n\ +\ Qualified name of the application class to execute.\n\ +\ --version -v \n\ +\ Version of the application.\n\ +\ --arguments -a
\n\ +\ Command line arguments to pass to the main class if no arguments\n\ +\ are specified by the launcher.\n\ +\ --icon \n\ +\ Icon of the application bundle.\n\ +\ --singleton\n\ +\ Prevents multiple instances of the application from launching\n\ +\ (see SingleInstanceService API for more details).\n\ +\ --identifier \n\ +\ Machine readable identifier of the application. The format\n\ +\ must be a DNS name in reverse order, such as com.example.myapplication.\n\ +\ The identifier is used for composing Single Instance unique id.\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\ +\ Properties file that contains list of key=value parameters that\n\ +\ describe a file association. "extension", "mime-type", "icon",\n\ +\ "description" can be used as keys for the association.\n\ +\ --secondary-launcher \n\ +\ Properties file that contains a collection of options for a secondary launcher.\n\ +\ --build-root \n\ +\ Directory in which to use and place temporary files.\n\ +\ --runtime-image \n\ +\ Location of the predefined runtime image that is used to build\n\ +\ an application image and installable package.\n\ +\ --app-image \n\ +\ Location of the predefined application image that is used to build\n\ +\ an installable package.\n\ +\ --install-dir \n\ +\ Installation directory of the application. Ignored on Windows, use\n\ +\ --win-dir-chooser to provide an ability to choose an installation directory.\n\ +\ --license-file \n\ +\ The license file, relative to the base 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\ +\n\ +Modular options:\n\ +\ --module -m \n\ +\ Main module of the application. This module must have the main-class,\n\ +\ and be on the module path.\n\ +\ --module-path -p \n\ +\ When packaging the Java Runtime, this is the path JLink looks in for modules.\n\ +\ --add-modules \n\ +\ List of modules to add to JImage creation, including possible services.\n\ +\ --limit-modules \n\ +\ 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. This can be\n\ +\ different from the application name. This name must be less than 16\n\ +\ characters long and be suitable for displaying in the menu bar and\n\ +\ the application Info window. 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). May only use alphanumeric (A-Z,a-z,0-9),\n\ +\ hyphen (-), and period (.) characters.\n\ +\ --mac-app-store-category \n\ +\ Mac App Store Categories. 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\ +\ File location of a custom mac app store entitlements file.\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 an existing bundle identifier.\n\ +\ --mac-signing-key-user-name \n\ +\ User name portion of the typical "Mac Developer ID Application: " signing key.\n\ +\ --mac-signing-keychain \n\ +\ Location of the keychain to use. 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. Default is\n\ +\ the Application Name with only 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 specified for\n\ +\ application which requires console interactions.\n\ --- /dev/null 2018-10-22 10:42:03.000000000 -0400 +++ new/src/jdk.packager/share/classes/jdk/packager/internal/resources/CLIHelpi_ja.properties 2018-10-22 10:42:01.268572300 -0400 @@ -0,0 +1,190 @@ +# +# 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: jpackager \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 packages\n\ +\ for current platform will be generated.\n\ +\ create-jre-installer \n\ +\ Generates a platform-specific installer for Server 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 packages\n\ +\ for current platform will be generated.\n\ +\ \n\ +Sample usages:\n\ +--------------\n\ +jpackager create-image --input inputdir --output outputdir --name AppName --class package.ClassName\n\ +or\n\ +jpackager create-image -i inputdir -o outputdir -n AppName -c package.ClassName\n\ +Generates an application image.\n\ +\n\ +jpackager create-installer -i inputdir -o outputdir -n "App Name" -c package.ClassName\n\ +Generates an application installer.\n\ +\n\ +jpackager create-jre-installer -n -o outputdir\n\ +Generates a Server JRE installer.\n\ +\n\ +The following options are valid for all platforms:\n\ +\ --help -h \n\ +\ Shows the usage text, followed by a list and description of each valid option for the current platform and the given mode.\n\ +\ If no mode is given, shows the usage text, followed by a list and description of each valid option for the current platform.\n\ +\ When this option is used, all other options are ignored.\n\ +\ --output -o \n\ +\ Name of the directory where generated output file is placed.\n\ +\ --input -i \n\ +\ Name of the base directory that contains the files to package.\n\ +\ --files -f \n\ +\ List of files in the base directory. If omitted, all files from "input"\n\ +\ directory (which is a mandatory argument in this case) will be packaged.\n\ +\ --name -n \n\ +\ Name of the application.\n\ +\ --main-jar -j
\n\ +\ The main JAR of the application. This JAR should have the main-class,\n\ +\ and is relative to the assembled application directory.\n\ +\ --class -c \n\ +\ Qualified name of the application class to execute.\n\ +\ --version -v \n\ +\ Version of the application.\n\ +\ --arguments -a
\n\ +\ Command line arguments to pass to the main class if no arguments\n\ +\ are specified by the launcher.\n\ +\ --icon \n\ +\ Icon of the application bundle.\n\ +\ --singleton\n\ +\ Prevents multiple instances of the application from launching\n\ +\ (see SingleInstanceService API for more details).\n\ +\ --identifier \n\ +\ Machine readable identifier of the application. The format\n\ +\ must be a DNS name in reverse order, such as com.example.myapplication.\n\ +\ The identifier is used for composing Single Instance unique id.\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\ +\ Properties file that contains list of key=value parameters that\n\ +\ describe a file association. "extension", "mime-type", "icon",\n\ +\ "description" can be used as keys for the association.\n\ +\ --secondary-launcher \n\ +\ Properties file that contains a collection of options for a secondary launcher.\n\ +\ --build-root \n\ +\ Directory in which to use and place temporary files.\n\ +\ --runtime-image \n\ +\ Location of the predefined runtime image that is used to build\n\ +\ an application image and installable package.\n\ +\ --app-image \n\ +\ Location of the predefined application image that is used to build\n\ +\ an installable package.\n\ +\ --install-dir \n\ +\ Installation directory of the application. Ignored on Windows, use\n\ +\ --win-dir-chooser to provide an ability to choose an installation directory.\n\ +\ --license-file \n\ +\ The license file, relative to the base 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\ +\n\ +Modular options:\n\ +\ --module -m \n\ +\ Main module of the application. This module must have the main-class,\n\ +\ and be on the module path.\n\ +\ --module-path -p \n\ +\ When packaging the Java Runtime, this is the path JLink looks in for modules.\n\ +\ --add-modules \n\ +\ List of modules to add to JImage creation, including possible services.\n\ +\ --limit-modules \n\ +\ 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. This can be\n\ +\ different from the application name. This name must be less than 16\n\ +\ characters long and be suitable for displaying in the menu bar and\n\ +\ the application Info window. 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). May only use alphanumeric (A-Z,a-z,0-9),\n\ +\ hyphen (-), and period (.) characters.\n\ +\ --mac-app-store-category \n\ +\ Mac App Store Categories. 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\ +\ File location of a custom mac app store entitlements file.\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 an existing bundle identifier.\n\ +\ --mac-signing-key-user-name \n\ +\ User name portion of the typical "Mac Developer ID Application: " signing key.\n\ +\ --mac-signing-keychain \n\ +\ Location of the keychain to use. 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. Default is\n\ +\ the Application Name with only 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 specified for\n\ +\ application which requires console interactions.\n\ --- /dev/null 2018-10-22 10:42:12.000000000 -0400 +++ new/src/jdk.packager/share/classes/jdk/packager/internal/resources/CLIHelpi_zh_CN.properties 2018-10-22 10:42:09.187925400 -0400 @@ -0,0 +1,190 @@ +# +# 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: jpackager \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 packages\n\ +\ for current platform will be generated.\n\ +\ create-jre-installer \n\ +\ Generates a platform-specific installer for Server 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 packages\n\ +\ for current platform will be generated.\n\ +\ \n\ +Sample usages:\n\ +--------------\n\ +jpackager create-image --input inputdir --output outputdir --name AppName --class package.ClassName\n\ +or\n\ +jpackager create-image -i inputdir -o outputdir -n AppName -c package.ClassName\n\ +Generates an application image.\n\ +\n\ +jpackager create-installer -i inputdir -o outputdir -n "App Name" -c package.ClassName\n\ +Generates an application installer.\n\ +\n\ +jpackager create-jre-installer -n -o outputdir\n\ +Generates a Server JRE installer.\n\ +\n\ +The following options are valid for all platforms:\n\ +\ --help -h \n\ +\ Shows the usage text, followed by a list and description of each valid option for the current platform and the given mode.\n\ +\ If no mode is given, shows the usage text, followed by a list and description of each valid option for the current platform.\n\ +\ When this option is used, all other options are ignored.\n\ +\ --output -o \n\ +\ Name of the directory where generated output file is placed.\n\ +\ --input -i \n\ +\ Name of the base directory that contains the files to package.\n\ +\ --files -f \n\ +\ List of files in the base directory. If omitted, all files from "input"\n\ +\ directory (which is a mandatory argument in this case) will be packaged.\n\ +\ --name -n \n\ +\ Name of the application.\n\ +\ --main-jar -j
\n\ +\ The main JAR of the application. This JAR should have the main-class,\n\ +\ and is relative to the assembled application directory.\n\ +\ --class -c \n\ +\ Qualified name of the application class to execute.\n\ +\ --version -v \n\ +\ Version of the application.\n\ +\ --arguments -a
\n\ +\ Command line arguments to pass to the main class if no arguments\n\ +\ are specified by the launcher.\n\ +\ --icon \n\ +\ Icon of the application bundle.\n\ +\ --singleton\n\ +\ Prevents multiple instances of the application from launching\n\ +\ (see SingleInstanceService API for more details).\n\ +\ --identifier \n\ +\ Machine readable identifier of the application. The format\n\ +\ must be a DNS name in reverse order, such as com.example.myapplication.\n\ +\ The identifier is used for composing Single Instance unique id.\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\ +\ Properties file that contains list of key=value parameters that\n\ +\ describe a file association. "extension", "mime-type", "icon",\n\ +\ "description" can be used as keys for the association.\n\ +\ --secondary-launcher \n\ +\ Properties file that contains a collection of options for a secondary launcher.\n\ +\ --build-root \n\ +\ Directory in which to use and place temporary files.\n\ +\ --runtime-image \n\ +\ Location of the predefined runtime image that is used to build\n\ +\ an application image and installable package.\n\ +\ --app-image \n\ +\ Location of the predefined application image that is used to build\n\ +\ an installable package.\n\ +\ --install-dir \n\ +\ Installation directory of the application. Ignored on Windows, use\n\ +\ --win-dir-chooser to provide an ability to choose an installation directory.\n\ +\ --license-file \n\ +\ The license file, relative to the base 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\ +\n\ +Modular options:\n\ +\ --module -m \n\ +\ Main module of the application. This module must have the main-class,\n\ +\ and be on the module path.\n\ +\ --module-path -p \n\ +\ When packaging the Java Runtime, this is the path JLink looks in for modules.\n\ +\ --add-modules \n\ +\ List of modules to add to JImage creation, including possible services.\n\ +\ --limit-modules \n\ +\ 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. This can be\n\ +\ different from the application name. This name must be less than 16\n\ +\ characters long and be suitable for displaying in the menu bar and\n\ +\ the application Info window. 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). May only use alphanumeric (A-Z,a-z,0-9),\n\ +\ hyphen (-), and period (.) characters.\n\ +\ --mac-app-store-category \n\ +\ Mac App Store Categories. 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\ +\ File location of a custom mac app store entitlements file.\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 an existing bundle identifier.\n\ +\ --mac-signing-key-user-name \n\ +\ User name portion of the typical "Mac Developer ID Application: " signing key.\n\ +\ --mac-signing-keychain \n\ +\ Location of the keychain to use. 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. Default is\n\ +\ the Application Name with only 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 specified for\n\ +\ application which requires console interactions.\n\ --- /dev/null 2018-10-22 10:42:20.000000000 -0400 +++ new/src/jdk.packager/share/classes/jdk/packager/internal/resources/JLinkBundlerHelper.properties 2018-10-22 10:42:18.209899300 -0400 @@ -0,0 +1,36 @@ +# +# 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.jlink-builder.name=JLink Builder +param.jlink-builder.description=Name of the JLink Builder to build the applicaiton image with. + +error.srcfiles.contain.modules=Error: Modules are not allowed in srcfiles: {0}. + +warning.module.does.not.exist=Module {0} does not exist. + +message.detected.modules="Automatically adding detected modules: {0}." +message.modules="Adding modules: {0} to runtime image." + --- /dev/null 2018-10-22 10:42:28.000000000 -0400 +++ new/src/jdk.packager/share/classes/jdk/packager/internal/resources/JLinkBundlerHelper_ja.properties 2018-10-22 10:42:26.028649900 -0400 @@ -0,0 +1,36 @@ +# +# 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.jlink-builder.name=JLink\u30D3\u30EB\u30C0\u30FC +param.jlink-builder.description=\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u30FB\u30A4\u30E1\u30FC\u30B8\u3092\u4F5C\u6210\u3059\u308BJLink\u30D3\u30EB\u30C0\u30FC\u306E\u540D\u524D + +error.srcfiles.contain.modules=\u30A8\u30E9\u30FC: \u30E2\u30B8\u30E5\u30FC\u30EB\u306Fsrcfiles\u3067\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093: {0}\u3002 + +warning.module.does.not.exist=\u30E2\u30B8\u30E5\u30FC\u30EB{0}\u306F\u5B58\u5728\u3057\u307E\u305B\u3093\u3002 + +message.detected.modules="\u691C\u51FA\u3055\u308C\u305F\u30E2\u30B8\u30E5\u30FC\u30EB\u3092\u81EA\u52D5\u7684\u306B\u8FFD\u52A0\u3057\u3066\u3044\u307E\u3059: {0}." +message.modules="\u30E2\u30B8\u30E5\u30FC\u30EB: {0}\u3092\u30E9\u30F3\u30BF\u30A4\u30E0\u30FB\u30A4\u30E1\u30FC\u30B8\u306B\u8FFD\u52A0\u3057\u3066\u3044\u307E\u3059\u3002" + --- /dev/null 2018-10-22 10:42:36.000000000 -0400 +++ new/src/jdk.packager/share/classes/jdk/packager/internal/resources/JLinkBundlerHelper_zh_CN.properties 2018-10-22 10:42:34.142608600 -0400 @@ -0,0 +1,35 @@ +# +# 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.jlink-builder.name=JLink Builder +param.jlink-builder.description=\u7528\u4E8E\u6784\u5EFA\u5E94\u7528\u7A0B\u5E8F\u6620\u50CF\u7684 JLink Builder \u7684\u540D\u79F0\u3002 + +error.srcfiles.contain.modules=\u9519\u8BEF: srcfile \u4E2D\u4E0D\u5141\u8BB8\u6A21\u5757: {0}\u3002 + +warning.module.does.not.exist=\u6A21\u5757 {0} \u4E0D\u5B58\u5728\u3002 + +message.detected.modules="\u6B63\u5728\u81EA\u52A8\u6DFB\u52A0\u68C0\u6D4B\u5230\u7684\u6A21\u5757: {0}\u3002" +message.modules="\u6B63\u5728\u5C06\u6A21\u5757 {0} \u6DFB\u52A0\u5230\u8FD0\u884C\u65F6\u6620\u50CF\u3002" --- /dev/null 2018-10-22 10:42:43.000000000 -0400 +++ new/src/jdk.packager/share/classes/jdk/packager/internal/resources/StandardBundlerParam.properties 2018-10-22 10:42:41.883357700 -0400 @@ -0,0 +1,173 @@ +# +# 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.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.source-files.name=Files +param.source-files.description=Set of files from input directory to be bundled. + +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.drop-in-resources-root.name=Drop-In Resources Root +param.drop-in-resources-root.description=The directory to look for bundler specific drop in resources. If not set the classpath will be searched. + +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.singleton.name=Singleton +param.singleton.description=Prevents from launching multiple instances of application. + +param.install-dir.name=Installation Directory +param.install-dir.description=Installation directory of the application. + +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 + +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 applicationClass or ensure that the jar {0} specifies one in the manifest. +error.no-main-class-with-classpath=An application class was not specified nor was one found in the supplied classpath +error.no-main-class-with-classpath.advice=Please specify a applicationClass or ensure that the classpath has a jar containing 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 applicationClass 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.no.jdk.modules.found=Warning: No JDK Modules found. --- /dev/null 2018-10-22 10:42:51.000000000 -0400 +++ new/src/jdk.packager/share/classes/jdk/packager/internal/resources/StandardBundlerParam_ja.properties 2018-10-22 10:42:49.261700800 -0400 @@ -0,0 +1,168 @@ +# +# 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.app-name.name=\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u540D +param.app-name.description=\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u306E\u540D\u524D\u3002 + +param.app-fs-name.name=\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u30FB\u30D5\u30A1\u30A4\u30EB\u30FB\u30B7\u30B9\u30C6\u30E0\u540D +param.app-fs-name.description=\u30D5\u30A1\u30A4\u30EB\u30FB\u30B7\u30B9\u30C6\u30E0\u306E\u4F7F\u7528\u306B\u9069\u3057\u305F\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u306E\u540D\u524D\u3002\u4E00\u822C\u7684\u306B\u3001\u5358\u306B\u6587\u5B57\u3001\u6570\u5024\u3001\u30C9\u30C3\u30C8\u304A\u3088\u3073\u30C0\u30C3\u30B7\u30E5\u3067\u3059\u3002 + +param.app-resource.description=\u30EA\u30BD\u30FC\u30B9\u30FB\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u306B\u914D\u7F6E\u3059\u308B\u3059\u3079\u3066\u306E\u30D5\u30A1\u30A4\u30EB\u3002\u30A2\u30BB\u30C3\u30C8\u3068\u3057\u3066\u5FC5\u8981\u306A\u3059\u3079\u3066\u306Ejar\u3092\u542B\u3081\u3066\u3044\u307E\u3059\u3002 +param.app-resources.name=\u30EA\u30BD\u30FC\u30B9 + +param.app-resource-list.description=\u30EA\u30BD\u30FC\u30B9\u30FB\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u306B\u914D\u7F6E\u3059\u308B\u3059\u3079\u3066\u306E\u30D5\u30A1\u30A4\u30EB\u3092\u542B\u3080RelativeFileSet\u30AA\u30D6\u30B8\u30A7\u30AF\u30C8\u306E\u30EA\u30B9\u30C8\u3002\u30A2\u30BB\u30C3\u30C8\u3068\u3057\u3066\u5FC5\u8981\u306A\u3059\u3079\u3066\u306Ejar\u3092\u542B\u3081\u3066\u3044\u307E\u3059\u3002 +param.app-resources-list.name=\u30EA\u30BD\u30FC\u30B9\u30FB\u30EA\u30B9\u30C8 + +param.build-root.name=\u4F5C\u6210\u30EB\u30FC\u30C8 +param.build-root.description=\u4E00\u6642\u30D5\u30A1\u30A4\u30EB\u3092\u4F7F\u7528\u304A\u3088\u3073\u914D\u7F6E\u3059\u308B\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u3002 + +param.category.name=\u30AB\u30C6\u30B4\u30EA +param.category.description=\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u306E\u30AB\u30C6\u30B4\u30EA\u307E\u305F\u306F\u30B0\u30EB\u30FC\u30D7\u3002\u4E00\u822C\u7684\u306B\u3001\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u56FA\u6709\u306E\u30AB\u30C6\u30B4\u30EA\u3082\u6307\u5B9A\u3057\u307E\u3059\u3002 +param.category.default=\u4E0D\u660E + +param.copyright.name=\u30B3\u30D4\u30FC\u30E9\u30A4\u30C8 +param.copyright.description=\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u306E\u30B3\u30D4\u30FC\u30E9\u30A4\u30C8\u3002 +param.copyright.default=Copyright (C) {0,date,YYYY} + +param.description.name=\u8AAC\u660E +param.description.description=\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u306E\u8A73\u7D30\u306A\u8AAC\u660E +param.description.default=\u306A\u3057 + +param.desktop-shortcut-hint.name=\u30B7\u30E7\u30FC\u30C8\u30AB\u30C3\u30C8\u306E\u30D2\u30F3\u30C8 +param.desktop-shortcut-hint.description=\u30D0\u30F3\u30C9\u30E9\u304C\u30C7\u30B9\u30AF\u30C8\u30C3\u30D7\u30FB\u30B7\u30E7\u30FC\u30C8\u30AB\u30C3\u30C8\u3092\u4F5C\u6210\u3067\u304D\u308B\u5834\u5408\u3001\u4F5C\u6210\u3057\u307E\u3059\u304B\u3002 + +param.icon-file.name=\u30A2\u30A4\u30B3\u30F3 +param.icon-file.description=\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u30FB\u30D0\u30F3\u30C9\u30EB\u306E\u30E1\u30A4\u30F3\u30FB\u30A2\u30A4\u30B3\u30F3\u3002 + +param.identifier.name=\u8B58\u5225\u5B50 +param.identifier.description=\u3053\u306E\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u306E\u30DE\u30B7\u30F3\u3067\u8AAD\u53D6\u308A\u53EF\u80FD\u306A\u8B58\u5225\u5B50\u306F\u4F55\u3067\u3059\u304B\u3002\u5F62\u5F0F\u306F\u3001com.example.myapplication\u306A\u3069\u3001DNS\u540D\u306E\u9006\u9806\u306B\u3059\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059\u3002 + +param.arguments.name=\u30B3\u30DE\u30F3\u30C9\u30E9\u30A4\u30F3\u5F15\u6570 +param.arguments.description=\u5F15\u6570\u304C\u30E9\u30F3\u30C1\u30E3\u306B\u3088\u3063\u3066\u6307\u5B9A\u3055\u308C\u3066\u3044\u306A\u3044\u5834\u5408\u306B\u3001\u30E1\u30A4\u30F3\u30FB\u30AF\u30E9\u30B9\u306B\u6E21\u3055\u308C\u308B\u30B3\u30DE\u30F3\u30C9\u30E9\u30A4\u30F3\u5F15\u6570\u3002 + +param.jvm-options.name=JVM\u30AA\u30D7\u30B7\u30E7\u30F3 +param.jvm-options.description=\u6E21\u3055\u308C\u308BJVM\u30D5\u30E9\u30B0\u304A\u3088\u3073\u30AA\u30D7\u30B7\u30E7\u30F3\u3002 + +param.jvm-system-properties.name=JVM\u30B7\u30B9\u30C6\u30E0\u30FB\u30D7\u30ED\u30D1\u30C6\u30A3 +param.jvm-system-properties.description=(\u5404\u7A2E-Dname=value\u306E)JVM\u30B7\u30B9\u30C6\u30E0\u30FB\u30D7\u30ED\u30D1\u30C6\u30A3\u3002 + +param.license-file.name=\u30E9\u30A4\u30BB\u30F3\u30B9 +param.license-file.description=\u30E9\u30A4\u30BB\u30F3\u30B9\u30FB\u30D5\u30A1\u30A4\u30EB\u3001\u30A2\u30BB\u30F3\u30D6\u30EB\u3055\u308C\u305F\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u30FB\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u306B\u5BFE\u3057\u3066\u76F8\u5BFE\u7684\u3067\u3059\u3002 + +param.license-type.name= +param.license-type.description= +param.license-type.default=\u4E0D\u660E + +param.main-class.name=\u30E1\u30A4\u30F3\u30FB\u30AF\u30E9\u30B9 +param.main-class.description=\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u306E\u30E1\u30A4\u30F3\u30FB\u30AF\u30E9\u30B9\u3002javafx.application.Application\u30A4\u30F3\u30B9\u30BF\u30F3\u30B9\u307E\u305F\u306Fmain\u30E1\u30BD\u30C3\u30C9\u3092\u542B\u3080Java\u30AF\u30E9\u30B9\u3002 + +param.main-module.name=\u30E1\u30A4\u30F3\u30FB\u30E2\u30B8\u30E5\u30FC\u30EB +param.main-module.description=\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u306E\u30E1\u30A4\u30F3\u30FB\u30E2\u30B8\u30E5\u30FC\u30EB\u3002\u3053\u308C\u306F\u30E1\u30A4\u30F3\u30FB\u30AF\u30E9\u30B9\u3092\u542B\u3080\u30E2\u30B8\u30E5\u30FC\u30EB\u3067\u3059\u3002 + +param.classpath.name=\u30E1\u30A4\u30F3Jar\u306E\u30AF\u30E9\u30B9\u30D1\u30B9 +param.classpath.description=\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u306E\u30E1\u30A4\u30F3jar\u304B\u3089\u306E\u30AF\u30E9\u30B9\u30D1\u30B9\u3067\u3001\u30A2\u30BB\u30F3\u30D6\u30EB\u3055\u308C\u305F\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u30FB\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u306B\u5BFE\u3057\u3066\u76F8\u5BFE\u7684\u3067\u3059\u3002 + +param.main-jar.name=\u30E1\u30A4\u30F3Jar +param.main-jar.description=\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u306E\u30E1\u30A4\u30F3jar\u3002\u3053\u306Ejar\u306Fmain-class\u3092\u6301\u3064\u5FC5\u8981\u304C\u3042\u308A\u3001\u30A2\u30BB\u30F3\u30D6\u30EB\u3055\u308C\u305F\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u30FB\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u306B\u5BFE\u3057\u3066\u76F8\u5BFE\u7684\u3067\u3059\u3002 + +param.preferences-id.name=\u30D7\u30EA\u30D5\u30A1\u30EC\u30F3\u30B9ID +param.preferences-id.description=\u30E6\u30FC\u30B6\u30FC\u306EJVM\u30AA\u30D7\u30B7\u30E7\u30F3\u3092\u691C\u7D22\u3059\u308B\u30D7\u30EA\u30D5\u30A1\u30EC\u30F3\u30B9\u30FB\u30CE\u30FC\u30C9\u3002\u5F62\u5F0F\u306F\u3001"com/example/myapplication"\u306A\u3069\u3001\u30E1\u30A4\u30F3\u30FB\u30D1\u30C3\u30B1\u30FC\u30B8\u540D\u306E\u30B9\u30E9\u30C3\u30B7\u30E5\u533A\u5207\u308A\u30D0\u30FC\u30B8\u30E7\u30F3\u3067\u3059\u3002 + +param.sign-bundle.name=\u7F72\u540D\u30D0\u30F3\u30C9\u30EB +param.sign-bundle.description=\u30D0\u30F3\u30C9\u30E9\u304C\u7F72\u540D\u3092\u30B5\u30DD\u30FC\u30C8\u3057\u3066\u3044\u308B\u5834\u5408\u3001\u30D0\u30F3\u30C9\u30EB\u304C\u7F72\u540D\u3055\u308C\u3066\u3044\u308B\u3053\u3068\u3092\u8981\u6C42\u3057\u307E\u3059\u3002\u30C7\u30D5\u30A9\u30EB\u30C8\u5024\u306F\u30D0\u30F3\u30C9\u30E9\u306B\u3088\u3063\u3066\u7570\u306A\u308A\u307E\u3059\u3002\u7F72\u540D\u3092\u30B5\u30DD\u30FC\u30C8\u3057\u3066\u3044\u306A\u3044\u30D0\u30F3\u30C9\u30E9\u3067\u306F\u3001\u3053\u306E\u8A2D\u5B9A\u306F\u8B66\u544A\u306A\u3057\u3067\u7121\u8996\u3055\u308C\u307E\u3059\u3002 + +param.menu-shortcut-hint.name=\u30E1\u30CB\u30E5\u30FC\u306E\u30D2\u30F3\u30C8 +param.menu-shortcut-hint.description=\u30D0\u30F3\u30C9\u30E9\u304C\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u3092\u30B7\u30B9\u30C6\u30E0\u30FB\u30E1\u30CB\u30E5\u30FC\u306B\u8FFD\u52A0\u3067\u304D\u308B\u5834\u5408\u3001\u5B9F\u884C\u3057\u307E\u3059\u304B\u3002 + +param.name.name=\u540D\u524D +param.name.description=\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u306E\u540D\u524D\u3002 + +param.system-wide.name=\u30B7\u30B9\u30C6\u30E0\u5168\u4F53 +param.system-wide.description=\u3053\u306E\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u306F\u3001\u305D\u308C\u81EA\u4F53\u3092\u30B7\u30B9\u30C6\u30E0\u5168\u4F53\u306B\u30A4\u30F3\u30B9\u30C8\u30FC\u30EB\u3057\u307E\u3059\u304B\u3001\u307E\u305F\u306F\u5404\u30E6\u30FC\u30B6\u30FC\u306B\u5BFE\u3057\u3066\u306E\u307F\u30A4\u30F3\u30B9\u30C8\u30FC\u30EB\u3057\u307E\u3059\u304B\u3002Null\u306F\u30B7\u30B9\u30C6\u30E0\u30FB\u30C7\u30D5\u30A9\u30EB\u30C8\u3092\u4F7F\u7528\u3059\u308B\u3053\u3068\u3092\u610F\u5473\u3057\u307E\u3059\u3002 + +param.title.name=\u30BF\u30A4\u30C8\u30EB +param.title.description=\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u306E\u30BF\u30A4\u30C8\u30EB\u3002 + +param.vendor.name=\u30D9\u30F3\u30C0\u30FC +param.vendor.description=\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u306E\u30D9\u30F3\u30C0\u30FC\u3002 +param.vendor.default=\u4E0D\u660E + +param.version.name=\u30D0\u30FC\u30B8\u30E7\u30F3 +param.version.description=\u3053\u306E\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u306E\u30D0\u30FC\u30B8\u30E7\u30F3\u3002 +param.version.default=1.0 + +param.verbose.name=\u8A73\u7D30 +param.verbose.description=\u8A73\u7D30\u60C5\u5831\u3092\u51FA\u529B\u3059\u308B\u305F\u3081\u306E\u30D5\u30E9\u30B0\u3067\u3001\u30D0\u30F3\u30C9\u30E9\u306E\u69CB\u6210\u30D5\u30A1\u30A4\u30EB\u3092\u4FDD\u5B58\u3057\u307E\u3059\u3002 + +param.drop-in-resources-root.name=\u30EA\u30BD\u30FC\u30B9\u5185\u306E\u30C9\u30ED\u30C3\u30D7\u30FB\u30EB\u30FC\u30C8 +param.drop-in-resources-root.description=\u30EA\u30BD\u30FC\u30B9\u5185\u306E\u30D0\u30F3\u30C9\u30E9\u56FA\u6709\u306E\u30C9\u30ED\u30C3\u30D7\u3092\u63A2\u3059\u305F\u3081\u306E\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u3002\u8A2D\u5B9A\u3055\u308C\u3066\u3044\u306A\u3044\u5834\u5408\u306F\u30AF\u30E9\u30B9\u30D1\u30B9\u3092\u691C\u7D22\u3057\u307E\u3059\u3002 + +param.secondary-launchers.name=\u30BB\u30AB\u30F3\u30C0\u30EA\u30FB\u30E9\u30F3\u30C1\u30E3 +param.secondary-launchers.description=\u30BB\u30AB\u30F3\u30C0\u30EA\u30FB\u30E9\u30F3\u30C1\u30E3\u306E\u30D0\u30F3\u30C9\u30EB\u30FB\u30D1\u30E9\u30E1\u30FC\u30BF\u60C5\u5831\u306E\u30B3\u30EC\u30AF\u30B7\u30E7\u30F3 + +param.file-associations.name=\u30D5\u30A1\u30A4\u30EB\u30FB\u30A2\u30BD\u30B7\u30A8\u30FC\u30B7\u30E7\u30F3 +param.file-associations.description=\u5404\u30DE\u30C3\u30D7\u304C\u30D5\u30A1\u30A4\u30EB\u30FB\u30A2\u30BD\u30B7\u30A8\u30FC\u30B7\u30E7\u30F3\u3092\u793A\u3059\u30DE\u30C3\u30D7\u306E\u30EA\u30B9\u30C8\u3002\u5404\u30DE\u30C3\u30D7\u3067\u4E00\u9023\u306E"fileAssociation."\u30D0\u30F3\u30C9\u30EB\u5F15\u6570\u3092\u4F7F\u7528\u3057\u307E\u3059\u3002 + +param.fa-extension.name=\u30D5\u30A1\u30A4\u30EB\u30FB\u30A2\u30BD\u30B7\u30A8\u30FC\u30B7\u30E7\u30F3\u306E\u62E1\u5F35\u5B50 +param.fa-extension.description=\u95A2\u9023\u4ED8\u3051\u308B\u30D5\u30A1\u30A4\u30EB\u62E1\u5F35\u5B50\u3001\u30C9\u30C3\u30C8\u306A\u3057\u306E\u62E1\u5F35\u5B50\u3002 + +param.fa-content-type.name=\u30D5\u30A1\u30A4\u30EB\u30FB\u30A2\u30BD\u30B7\u30A8\u30FC\u30B7\u30E7\u30F3\u30FB\u30B3\u30F3\u30C6\u30F3\u30C4\u30FB\u30BF\u30A4\u30D7 +param.fa-content-type.description=\u95A2\u9023\u4ED8\u3051\u308B\u30B3\u30F3\u30C6\u30F3\u30C4\u30FB\u30BF\u30A4\u30D7\u3002application/x-vnd.my-awesome-app\u306A\u3069\u3067\u3059\u3002 + +param.fa-icon.name=\u30D5\u30A1\u30A4\u30EB\u30FB\u30A2\u30BD\u30B7\u30A8\u30FC\u30B7\u30E7\u30F3\u30FB\u30A2\u30A4\u30B3\u30F3 +param.fa-icon.description=\u95A2\u9023\u4ED8\u3051\u3089\u308C\u305F\u30D5\u30A1\u30A4\u30EB\u306B\u4F7F\u7528\u3059\u308B\u30A2\u30A4\u30B3\u30F3\u3002\u30C7\u30D5\u30A9\u30EB\u30C8\u306F\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u30FB\u30A2\u30A4\u30B3\u30F3\u3067\u3059\u3002 + +param.fa-description.name=\u30D5\u30A1\u30A4\u30EB\u30FB\u30A2\u30BD\u30B7\u30A8\u30FC\u30B7\u30E7\u30F3\u306E\u8AAC\u660E +param.fa-description.description=\u95A2\u9023\u4ED8\u3051\u3089\u308C\u305F\u30D5\u30A1\u30A4\u30EB\u306B\u4F7F\u7528\u3059\u308B\u8AAC\u660E\u3002\u30C7\u30D5\u30A9\u30EB\u30C8\u306F"\u30D5\u30A1\u30A4\u30EB"\u3067\u3059\u3002 + +param.source-dir.name=\u30BD\u30FC\u30B9\u30FB\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA +param.source-dir.description=\u30D0\u30F3\u30C9\u30EB\u3059\u308B\u30D5\u30A1\u30A4\u30EB\u3092\u542B\u3080\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u3078\u306E\u30D1\u30B9\u3002 + +param.module-path.name=\u30E2\u30B8\u30E5\u30FC\u30EB\u30FB\u30D1\u30B9 +param.module-path.description=Java\u30E9\u30F3\u30BF\u30A4\u30E0\u3092\u30D1\u30C3\u30B1\u30FC\u30B8\u30F3\u30B0\u3059\u308B\u969B\u306B\u3001JLink\u304C\u30E2\u30B8\u30E5\u30FC\u30EB\u3092\u63A2\u3059\u30D1\u30B9\u3067\u3059\u3002 + +param.add-modules.name=\u8FFD\u52A0\u30E2\u30B8\u30E5\u30FC\u30EB +param.add-modules.description=\u4F7F\u7528\u53EF\u80FD\u306A\u30B5\u30FC\u30D3\u30B9\u3092\u542B\u3080\u3001JImage\u306E\u4F5C\u6210\u306B\u8FFD\u52A0\u3059\u308B\u30E2\u30B8\u30E5\u30FC\u30EB\u306E\u30EA\u30B9\u30C8\u3002 + +param.limit-modules.name=\u5236\u9650\u30E2\u30B8\u30E5\u30FC\u30EB +param.limit-modules.description=JImage\u306E\u4F5C\u6210\u3092\u5236\u9650\u3059\u308B\u30E2\u30B8\u30E5\u30FC\u30EB\u3002 + +param.strip-executables.name=\u30CD\u30A4\u30C6\u30A3\u30D6\u306E\u5B9F\u884C\u53EF\u80FD\u30D5\u30A1\u30A4\u30EB\u306E\u524A\u9664 +param.strip-executables.description=JImage\u306E\u4F5C\u6210\u304B\u3089\u30CD\u30A4\u30C6\u30A3\u30D6\u306E\u5B9F\u884C\u53EF\u80FD\u30D5\u30A1\u30A4\u30EB\u3092\u524A\u9664\u3057\u307E\u3059\u3002 + +param.main.module.name=\u30E1\u30A4\u30F3\u30FB\u30E2\u30B8\u30E5\u30FC\u30EB +param.main.module.description=\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u306E\u30E1\u30A4\u30F3\u30FB\u30E2\u30B8\u30E5\u30FC\u30EB\u3002\u3053\u306E\u30E2\u30B8\u30E5\u30FC\u30EB\u306Fmain-class\u3092\u6301\u3064\u5FC5\u8981\u304C\u3042\u308A\u3001\u30E2\u30B8\u30E5\u30FC\u30EB\u30FB\u30D1\u30B9\u4E0A\u306B\u3042\u308A\u307E\u3059\u3002 + +error.no-main-class-with-main-jar=\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u30FB\u30AF\u30E9\u30B9\u304C\u6307\u5B9A\u3055\u308C\u3066\u3044\u306A\u304B\u3063\u305F\u304B\u3001jar {0}\u306B\u898B\u3064\u304B\u308A\u307E\u305B\u3093\u3067\u3057\u305F +error.no-main-class-with-main-jar.advice=applicationClass\u3092\u6307\u5B9A\u3059\u308B\u304B\u3001jar {0}\u304C\u30DE\u30CB\u30D5\u30A7\u30B9\u30C8\u306EapplicationClass\u3092\u6307\u5B9A\u3057\u3066\u3044\u308B\u3053\u3068\u3092\u78BA\u8A8D\u3057\u3066\u304F\u3060\u3055\u3044\u3002 +error.no-main-class-with-classpath=\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u30FB\u30AF\u30E9\u30B9\u304C\u6307\u5B9A\u3055\u308C\u3066\u3044\u306A\u304B\u3063\u305F\u304B\u3001\u6307\u5B9A\u3055\u308C\u305F\u30AF\u30E9\u30B9\u30D1\u30B9\u306B\u898B\u3064\u304B\u308A\u307E\u305B\u3093\u3067\u3057\u305F +error.no-main-class-with-classpath.advice=applicationClass\u3092\u6307\u5B9A\u3059\u308B\u304B\u3001\u30AF\u30E9\u30B9\u30D1\u30B9\u306B\u30DE\u30CB\u30D5\u30A7\u30B9\u30C8\u306EapplicationClass\u3092\u542B\u3080jar\u304C\u3042\u308B\u3053\u3068\u3092\u78BA\u8A8D\u3057\u3066\u304F\u3060\u3055\u3044\u3002 +error.no-main-class=\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u30FB\u30AF\u30E9\u30B9\u304C\u6307\u5B9A\u3055\u308C\u3066\u3044\u306A\u304B\u3063\u305F\u304B\u3001\u6307\u5B9A\u3055\u308C\u305F\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u30FB\u30EA\u30BD\u30FC\u30B9\u306B\u898B\u3064\u304B\u308A\u307E\u305B\u3093\u3067\u3057\u305F +error.no-main-class.advice=applicationClass\u3092\u6307\u5B9A\u3059\u308B\u304B\u3001appResources\u306B\u30DE\u30CB\u30D5\u30A7\u30B9\u30C8\u306EapplicationClass\u3092\u542B\u3080jar\u304C\u3042\u308B\u3053\u3068\u3092\u78BA\u8A8D\u3057\u3066\u304F\u3060\u3055\u3044\u3002 +error.main-jar-does-not-exist=\u69CB\u6210\u3055\u308C\u305F\u30E1\u30A4\u30F3jar\u306F{0}\u306B\u5B58\u5728\u3057\u307E\u305B\u3093 +error.main-jar-does-not-exist.advice=\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u30FB\u30EA\u30BD\u30FC\u30B9\u306B\u5BFE\u3057\u3066\u76F8\u5BFE\u7684\u306B(\u7D76\u5BFE\u30D1\u30B9\u3067\u306F\u306A\u3044)\u30E1\u30A4\u30F3jar\u3092\u6307\u5B9A\u3059\u308B\u5FC5\u8981\u304C\u3042\u308A\u3001\u305D\u306E\u30EA\u30BD\u30FC\u30B9\u5185\u306B\u5B58\u5728\u3059\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059\u3002 + +warning.no.jdk.modules.found=\u8B66\u544A: JDK\u30E2\u30B8\u30E5\u30FC\u30EB\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093\u3002 --- /dev/null 2018-10-22 10:42:58.000000000 -0400 +++ new/src/jdk.packager/share/classes/jdk/packager/internal/resources/StandardBundlerParam_zh_CN.properties 2018-10-22 10:42:56.500240000 -0400 @@ -0,0 +1,168 @@ +# +# 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.app-name.name=\u5E94\u7528\u7A0B\u5E8F\u540D\u79F0 +param.app-name.description=\u5E94\u7528\u7A0B\u5E8F\u7684\u540D\u79F0\u3002 + +param.app-fs-name.name=\u5E94\u7528\u7A0B\u5E8F\u6587\u4EF6\u7CFB\u7EDF\u540D\u79F0 +param.app-fs-name.description=\u9002\u5408\u6587\u4EF6\u7CFB\u7EDF\u4F7F\u7528\u7684\u5E94\u7528\u7A0B\u5E8F\u7684\u540D\u79F0\u3002\u901A\u5E38\u6B64\u540D\u79F0\u4EC5\u5305\u542B\u5B57\u6BCD, \u6570\u5B57, \u70B9\u548C\u77ED\u5212\u7EBF\u3002 + +param.app-resource.description=\u8981\u653E\u5728\u8D44\u6E90\u76EE\u5F55\u4E2D\u7684\u6240\u6709\u6587\u4EF6\u3002\u5C06\u6240\u6709\u5FC5\u9700\u7684 jar \u4F5C\u4E3A\u8D44\u4EA7\u5305\u62EC\u3002 +param.app-resources.name=\u8D44\u6E90 + +param.app-resource-list.description=RelativeFileSet \u5BF9\u8C61\u7684\u5217\u8868, \u5176\u4E2D\u5305\u542B\u8981\u653E\u5728\u8D44\u6E90\u76EE\u5F55\u4E2D\u7684\u6240\u6709\u6587\u4EF6\u3002\u5C06\u6240\u6709\u5FC5\u9700\u7684 jar \u4F5C\u4E3A\u8D44\u4EA7\u5305\u62EC\u3002 +param.app-resources-list.name=\u8D44\u6E90\u5217\u8868 + +param.build-root.name=\u6784\u5EFA\u6839\u76EE\u5F55 +param.build-root.description=\u5728\u5176\u4E2D\u4F7F\u7528\u548C\u653E\u7F6E\u4E34\u65F6\u6587\u4EF6\u7684\u76EE\u5F55\u3002 + +param.category.name=\u7C7B\u522B +param.category.description=\u5E94\u7528\u7A0B\u5E8F\u7684\u7C7B\u522B\u6216\u7EC4\u3002\u901A\u5E38\u800C\u8A00, \u8FD8\u9700\u8981\u6307\u5B9A\u5E94\u7528\u7A0B\u5E8F\u7279\u5B9A\u7C7B\u522B\u3002 +param.category.default=\u672A\u77E5 + +param.copyright.name=\u7248\u6743 +param.copyright.description=\u5E94\u7528\u7A0B\u5E8F\u7684\u7248\u6743\u3002 +param.copyright.default=\u7248\u6743\u6240\u6709 (C) {0,date,YYYY} + +param.description.name=\u8BF4\u660E +param.description.description=\u5E94\u7528\u7A0B\u5E8F\u7684\u66F4\u8BE6\u7EC6\u7684\u8BF4\u660E +param.description.default=\u65E0 + +param.desktop-shortcut-hint.name=\u5FEB\u6377\u65B9\u5F0F\u63D0\u793A +param.desktop-shortcut-hint.description=\u5982\u679C\u6253\u5305\u7A0B\u5E8F\u53EF\u4EE5\u521B\u5EFA\u684C\u9762\u5FEB\u6377\u65B9\u5F0F, \u662F\u5426\u5E94\u521B\u5EFA? + +param.icon-file.name=\u56FE\u6807 +param.icon-file.description=\u5E94\u7528\u7A0B\u5E8F\u5305\u7684\u4E3B\u56FE\u6807\u3002 + +param.identifier.name=\u6807\u8BC6\u7B26 +param.identifier.description=\u4EC0\u4E48\u662F\u6B64\u5E94\u7528\u7A0B\u5E8F\u7684\u8BA1\u7B97\u673A\u53EF\u8BFB\u6807\u8BC6\u7B26? \u683C\u5F0F\u5E94\u4E3A\u53CD\u5411\u987A\u5E8F\u7684 DNS \u540D\u79F0, \u4F8B\u5982 com.example.myapplication + +param.arguments.name=\u547D\u4EE4\u884C\u53C2\u6570 +param.arguments.description=\u8981\u4F20\u9012\u5230\u4E3B\u7C7B\u7684\u547D\u4EE4\u884C\u53C2\u6570 (\u542F\u52A8\u7A0B\u5E8F\u672A\u6307\u5B9A\u4EFB\u4F55\u53C2\u6570\u65F6)\u3002 + +param.jvm-options.name=JVM \u9009\u9879 +param.jvm-options.description=\u8981\u4F20\u5165\u7684 JVM \u6807\u8BB0\u548C\u9009\u9879\u3002 + +param.jvm-system-properties.name=JVM \u7CFB\u7EDF\u5C5E\u6027 +param.jvm-system-properties.description=JVM \u7CFB\u7EDF\u5C5E\u6027 (-Dname=value \u53D8\u4F53)\u3002 + +param.license-file.name=\u8BB8\u53EF\u8BC1 +param.license-file.description=\u8BB8\u53EF\u8BC1\u6587\u4EF6, \u76F8\u5BF9\u4E8E\u7EC4\u5408\u7684\u5E94\u7528\u7A0B\u5E8F\u76EE\u5F55\u3002 + +param.license-type.name= +param.license-type.description= +param.license-type.default=\u672A\u77E5 + +param.main-class.name=\u4E3B\u7C7B +param.main-class.description=\u5E94\u7528\u7A0B\u5E8F\u7684\u4E3B\u7C7B\u3002\u53EF\u4EE5\u4E3A javafx.application.Application \u5B9E\u4F8B\u6216\u5E26\u6709 main \u65B9\u6CD5\u7684 java \u7C7B\u3002 + +param.main-module.name=\u4E3B\u6A21\u5757 +param.main-module.description=\u5E94\u7528\u7A0B\u5E8F\u7684\u4E3B\u6A21\u5757\u3002\u8FD9\u662F\u5305\u542B\u4E3B\u7C7B\u7684\u6A21\u5757\u3002 + +param.classpath.name=\u4E3B Jar \u7C7B\u8DEF\u5F84 +param.classpath.description=\u5E94\u7528\u7A0B\u5E8F\u7684\u4E3B jar \u7684\u7C7B\u8DEF\u5F84, \u76F8\u5BF9\u4E8E\u7EC4\u5408\u7684\u5E94\u7528\u7A0B\u5E8F\u76EE\u5F55\u3002 + +param.main-jar.name=\u4E3B Jar +param.main-jar.description=\u5E94\u7528\u7A0B\u5E8F\u7684\u4E3B jar\u3002\u6B64 jar \u5E94\u5177\u6709\u4E3B\u7C7B, \u5E76\u4E14\u76F8\u5BF9\u4E8E\u7EC4\u5408\u7684\u5E94\u7528\u7A0B\u5E8F\u76EE\u5F55\u3002 + +param.preferences-id.name=\u9996\u9009 ID +param.preferences-id.description=\u5728\u5176\u4E2D\u641C\u7D22\u7528\u6237 JVM \u9009\u9879\u7684\u9996\u9009\u8282\u70B9\u3002\u5176\u683C\u5F0F\u5E94\u4E3A\u4E3B\u7A0B\u5E8F\u5305\u540D\u79F0\u4EE5\u659C\u6760\u5206\u9694\u7684\u7248\u672C, \u4F8B\u5982 "com/example/myapplication"\u3002 + +param.sign-bundle.name=\u5BF9\u5305\u7B7E\u540D +param.sign-bundle.description=\u5982\u679C\u6253\u5305\u7A0B\u5E8F\u652F\u6301\u7B7E\u540D, \u5219\u8BF7\u6C42\u5BF9\u5305\u7B7E\u540D\u3002\u9ED8\u8BA4\u503C\u56E0\u6253\u5305\u7A0B\u5E8F\u800C\u5F02\u3002\u4E0D\u652F\u6301\u7B7E\u540D\u7684\u6253\u5305\u7A0B\u5E8F\u5C06\u65E0\u63D0\u793A\u5FFD\u7565\u6B64\u8BBE\u7F6E\u3002 + +param.menu-shortcut-hint.name=\u83DC\u5355\u63D0\u793A +param.menu-shortcut-hint.description=\u5982\u679C\u6253\u5305\u7A0B\u5E8F\u53EF\u4EE5\u5C06\u5E94\u7528\u7A0B\u5E8F\u6DFB\u52A0\u5230\u7CFB\u7EDF\u83DC\u5355, \u662F\u5426\u5E94\u6DFB\u52A0? + +param.name.name=\u540D\u79F0 +param.name.description=\u5E94\u7528\u7A0B\u5E8F\u7684\u540D\u79F0\u3002 + +param.system-wide.name=\u7CFB\u7EDF\u8303\u56F4 +param.system-wide.description=\u6B64\u5E94\u7528\u7A0B\u5E8F\u662F\u5E94\u5C1D\u8BD5\u5728\u7CFB\u7EDF\u8303\u56F4\u5185\u5B89\u88C5, \u8FD8\u662F\u4EC5\u4E3A\u6BCF\u4E2A\u7528\u6237\u5B89\u88C5? \u7A7A\u503C\u8868\u793A\u4F7F\u7528\u7CFB\u7EDF\u9ED8\u8BA4\u503C\u3002 + +param.title.name=\u6807\u9898 +param.title.description=\u5E94\u7528\u7A0B\u5E8F\u7684\u6807\u9898\u3002 + +param.vendor.name=\u4F9B\u5E94\u5546 +param.vendor.description=\u5E94\u7528\u7A0B\u5E8F\u7684\u4F9B\u5E94\u5546\u3002 +param.vendor.default=\u672A\u77E5 + +param.version.name=\u7248\u672C +param.version.description=\u6B64\u5E94\u7528\u7A0B\u5E8F\u7684\u7248\u672C\u3002 +param.version.default=1.0 + +param.verbose.name=\u8BE6\u7EC6 +param.verbose.description=\u6307\u793A\u8F93\u51FA\u8BE6\u7EC6\u4FE1\u606F\u5E76\u4FDD\u5B58\u6253\u5305\u7A0B\u5E8F\u914D\u7F6E\u6587\u4EF6\u7684\u6807\u8BB0\u3002 + +param.drop-in-resources-root.name=\u76F4\u63A5\u8BBF\u95EE\u8D44\u6E90\u6839\u76EE\u5F55 +param.drop-in-resources-root.description=\u5728\u5176\u4E2D\u67E5\u627E\u6253\u5305\u7A0B\u5E8F\u7279\u5B9A\u76F4\u63A5\u8BBF\u95EE\u8D44\u6E90\u7684\u76EE\u5F55\u3002\u5982\u679C\u672A\u8BBE\u7F6E, \u5219\u641C\u7D22\u7C7B\u8DEF\u5F84\u3002 + +param.secondary-launchers.name=\u8F85\u52A9\u542F\u52A8\u7A0B\u5E8F +param.secondary-launchers.description=\u8F85\u52A9\u542F\u52A8\u7A0B\u5E8F\u7684\u6253\u5305\u53C2\u6570\u4FE1\u606F\u7684\u96C6\u5408 + +param.file-associations.name=\u6587\u4EF6\u5173\u8054 +param.file-associations.description=\u6620\u5C04\u7684\u5217\u8868, \u5176\u4E2D\u6BCF\u4E2A\u6620\u5C04\u63CF\u8FF0\u4E00\u4E2A\u6587\u4EF6\u5173\u8054\u3002\u5728\u6BCF\u4E2A\u6620\u5C04\u4E2D\u4F7F\u7528\u6253\u5305\u53C2\u6570\u7684 "fileAssociation." \u7CFB\u5217\u3002 + +param.fa-extension.name=\u6587\u4EF6\u5173\u8054\u6269\u5C55\u540D +param.fa-extension.description=\u8981\u5173\u8054\u7684\u6587\u4EF6\u6269\u5C55\u540D (\u4EC5\u9650\u6269\u5C55\u540D, \u4E0D\u542B\u70B9)\u3002 + +param.fa-content-type.name=\u6587\u4EF6\u5173\u8054\u5185\u5BB9\u7C7B\u578B +param.fa-content-type.description=\u8981\u5173\u8054\u7684\u5185\u5BB9\u7C7B\u578B\u3002\u4F8B\u5982 application/x-vnd.my-awesome-app\u3002 + +param.fa-icon.name=\u6587\u4EF6\u5173\u8054\u56FE\u6807 +param.fa-icon.description=\u7528\u4E8E\u6240\u5173\u8054\u6587\u4EF6\u7684\u56FE\u6807\u3002\u9ED8\u8BA4\u503C\u4E3A\u5E94\u7528\u7A0B\u5E8F\u56FE\u6807\u3002 + +param.fa-description.name=\u6587\u4EF6\u5173\u8054\u8BF4\u660E +param.fa-description.description=\u7528\u4E8E\u6240\u5173\u8054\u6587\u4EF6\u7684\u8BF4\u660E\u3002\u9ED8\u8BA4\u503C\u4E3A " \u6587\u4EF6"\u3002 + +param.source-dir.name=\u6E90\u76EE\u5F55 +param.source-dir.description=\u5305\u542B\u8981\u6253\u5305\u6587\u4EF6\u7684\u76EE\u5F55\u8DEF\u5F84\u3002 + +param.module-path.name=\u6A21\u5757\u8DEF\u5F84 +param.module-path.description=\u5728\u6253\u5305 Java \u8FD0\u884C\u65F6\u671F\u95F4, \u8FD9\u662F JLink \u5C06\u5728\u5176\u4E2D\u67E5\u627E\u6A21\u5757\u7684\u8DEF\u5F84\u3002 + +param.add-modules.name=\u6DFB\u52A0\u6A21\u5757 +param.add-modules.description=\u8981\u6DFB\u52A0\u5230 JImage \u521B\u5EFA\u64CD\u4F5C\u7684\u6A21\u5757\u5217\u8868, \u5305\u62EC\u53EF\u80FD\u7684\u670D\u52A1\u3002 + +param.limit-modules.name=\u9650\u5236\u6A21\u5757 +param.limit-modules.description=\u5C06 JImage \u521B\u5EFA\u64CD\u4F5C\u9650\u5236\u4E3A\u7684\u6A21\u5757\u3002 + +param.strip-executables.name=\u5220\u9664\u672C\u673A\u53EF\u6267\u884C\u6587\u4EF6 +param.strip-executables.description=\u4ECE JImage \u521B\u5EFA\u64CD\u4F5C\u4E2D\u5220\u9664\u672C\u673A\u53EF\u6267\u884C\u6587\u4EF6\u3002 + +param.main.module.name=\u4E3B\u6A21\u5757 +param.main.module.description=\u5E94\u7528\u7A0B\u5E8F\u7684\u4E3B\u6A21\u5757\u3002\u6B64\u6A21\u5757\u5E94\u5177\u6709\u4E3B\u7C7B, \u5E76\u4E14\u4F4D\u4E8E\u6A21\u5757\u8DEF\u5F84\u4E0A\u3002 + +error.no-main-class-with-main-jar=\u672A\u6307\u5B9A\u5E94\u7528\u7A0B\u5E8F\u7C7B, \u5728 jar {0} \u4E2D\u4E5F\u672A\u627E\u5230\u5E94\u7528\u7A0B\u5E8F\u7C7B +error.no-main-class-with-main-jar.advice=\u8BF7\u6307\u5B9A applicationClass \u6216\u786E\u4FDD jar {0} \u5728\u6E05\u5355\u4E2D\u6307\u5B9A\u4E00\u4E2A\u3002 +error.no-main-class-with-classpath=\u672A\u6307\u5B9A\u5E94\u7528\u7A0B\u5E8F\u7C7B, \u5728\u63D0\u4F9B\u7684\u7C7B\u8DEF\u5F84\u4E2D\u4E5F\u672A\u627E\u5230\u5E94\u7528\u7A0B\u5E8F\u7C7B +error.no-main-class-with-classpath.advice=\u8BF7\u6307\u5B9A applicationClass \u6216\u786E\u4FDD\u7C7B\u8DEF\u5F84\u4E2D\u6709\u4E00\u4E2A jar \u5728\u6E05\u5355\u4E2D\u5305\u542B\u5B83\u3002 +error.no-main-class=\u672A\u6307\u5B9A\u5E94\u7528\u7A0B\u5E8F\u7C7B, \u5728\u63D0\u4F9B\u7684\u5E94\u7528\u7A0B\u5E8F\u8D44\u6E90\u4E2D\u4E5F\u672A\u627E\u5230\u5E94\u7528\u7A0B\u5E8F\u7C7B +error.no-main-class.advice=\u8BF7\u6307\u5B9A applicationClass \u6216\u786E\u4FDD appResources \u4E2D\u6709\u4E00\u4E2A jar \u5728\u6E05\u5355\u4E2D\u5305\u542B\u5B83\u3002 +error.main-jar-does-not-exist=\u914D\u7F6E\u7684\u4E3B jar \u4E0D\u5B58\u5728 {0} +error.main-jar-does-not-exist.advice=\u4E3B jar \u5FC5\u987B\u76F8\u5BF9\u4E8E\u5E94\u7528\u7A0B\u5E8F\u8D44\u6E90\u6307\u5B9A (\u4E0D\u662F\u7EDD\u5BF9\u8DEF\u5F84), \u5E76\u4E14\u5FC5\u987B\u5B58\u5728\u4E8E\u8FD9\u4E9B\u8D44\u6E90\u4E2D\u3002 + +warning.no.jdk.modules.found=\u8B66\u544A: \u672A\u627E\u5230 JDK \u6A21\u5757\u3002 --- /dev/null 2018-10-22 10:43:05.000000000 -0400 +++ new/src/jdk.packager/share/classes/jdk/packager/internal/resources/builders/AbstractAppImageBuilder.properties 2018-10-22 10:43:02.865162400 -0400 @@ -0,0 +1,30 @@ +# +# 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. +# +# + +message.using-default-resource=Using default package resource {0} (add {1} to the class path to customize) +message.using-custom-resource-from-file=Using custom package resource {0} (loaded from file {1}) +message.using-custom-resource-from-classpath=Using custom package resource {0} (loaded from {1}) +message.using-default-resource-from-classpath=Using default package resource {0} (add {1} to the class path to customize) --- /dev/null 2018-10-22 10:43:13.000000000 -0400 +++ new/src/jdk.packager/share/classes/jdk/packager/internal/resources/builders/AbstractAppImageBuilder_ja.properties 2018-10-22 10:43:10.652712400 -0400 @@ -0,0 +1,30 @@ +# +# 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. +# +# + +message.using-default-resource=\u30C7\u30D5\u30A9\u30EB\u30C8\u306E\u30D1\u30C3\u30B1\u30FC\u30B8\u30FB\u30EA\u30BD\u30FC\u30B9{0}\u306E\u4F7F\u7528({1}\u3092\u30AF\u30E9\u30B9\u30FB\u30D1\u30B9\u306B\u8FFD\u52A0\u3057\u3066\u30AB\u30B9\u30BF\u30DE\u30A4\u30BA) +message.using-custom-resource-from-file=\u30AB\u30B9\u30BF\u30E0\u30FB\u30D1\u30C3\u30B1\u30FC\u30B8\u30FB\u30EA\u30BD\u30FC\u30B9{0}\u306E\u4F7F\u7528(\u30D5\u30A1\u30A4\u30EB{1}\u304B\u3089\u30ED\u30FC\u30C9\u6E08) +message.using-custom-resource-from-classpath=\u30AB\u30B9\u30BF\u30E0\u30FB\u30D1\u30C3\u30B1\u30FC\u30B8\u30FB\u30EA\u30BD\u30FC\u30B9{0}\u306E\u4F7F\u7528({1}\u304B\u3089\u30ED\u30FC\u30C9\u6E08) +message.using-default-resource-from-classpath=\u30C7\u30D5\u30A9\u30EB\u30C8\u306E\u30D1\u30C3\u30B1\u30FC\u30B8\u30FB\u30EA\u30BD\u30FC\u30B9{0}\u306E\u4F7F\u7528({1}\u3092\u30AF\u30E9\u30B9\u30FB\u30D1\u30B9\u306B\u8FFD\u52A0\u3057\u3066\u30AB\u30B9\u30BF\u30DE\u30A4\u30BA) --- /dev/null 2018-10-22 10:43:20.000000000 -0400 +++ new/src/jdk.packager/share/classes/jdk/packager/internal/resources/builders/AbstractAppImageBuilder_zh_CN.properties 2018-10-22 10:43:17.466444400 -0400 @@ -0,0 +1,30 @@ +# +# 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. +# +# + +message.using-default-resource=\u4F7F\u7528\u9ED8\u8BA4\u7A0B\u5E8F\u5305\u8D44\u6E90 {0} (\u5C06 {1} \u6DFB\u52A0\u5230\u7C7B\u8DEF\u5F84\u4EE5\u5B9A\u5236) +message.using-custom-resource-from-file=\u4F7F\u7528\u5B9A\u5236\u7A0B\u5E8F\u5305\u8D44\u6E90 {0} (\u4ECE\u6587\u4EF6 {1} \u52A0\u8F7D) +message.using-custom-resource-from-classpath=\u4F7F\u7528\u5B9A\u5236\u7A0B\u5E8F\u5305\u8D44\u6E90 {0} (\u4ECE {1} \u52A0\u8F7D) +message.using-default-resource-from-classpath=\u4F7F\u7528\u9ED8\u8BA4\u7A0B\u5E8F\u5305\u8D44\u6E90 {0} (\u5C06 {1} \u6DFB\u52A0\u5230\u7C7B\u8DEF\u5F84\u4EE5\u5B9A\u5236) --- /dev/null 2018-10-22 10:43:27.000000000 -0400 +++ new/src/jdk.packager/share/classes/jdk/packager/internal/resources/jre.list 2018-10-22 10:43:25.188592900 -0400 @@ -0,0 +1,49 @@ +; This file contains a list of all modules in a modular JDK that match a JRE. + +java.base +java.compiler +java.datatransfer +java.xml +java.prefs +java.desktop +java.instrument +java.logging +java.management +java.security.sasl +java.naming +java.rmi +java.management.rmi +java.net.http +java.scripting +java.security.jgss +java.transaction.xa +java.sql +java.sql.rowset +java.xml.crypto +java.smartcardio +jdk.accessibility +jdk.charsets +jdk.crypto.ec +jdk.crypto.cryptoki +jdk.dynalink +jdk.httpserver +jdk.management +jdk.unsupported +jdk.jdeps +jdk.jdwp.agent +jdk.jlink +jdk.jsobject +jdk.localedata +jdk.management.agent +jdk.naming.dns +jdk.naming.rmi +jdk.net +jdk.packager +jdk.packager.services +jdk.scripting.nashorn +jdk.scripting.nashorn.shell +jdk.sctp +jdk.security.auth +jdk.security.jgss +jdk.xml.dom +jdk.zipfs \ No newline at end of file --- /dev/null 2018-10-22 10:43:35.000000000 -0400 +++ new/src/jdk.packager/share/classes/jdk/packager/internal/resources/jre.module.list 2018-10-22 10:43:32.792939700 -0400 @@ -0,0 +1,56 @@ +; This file contains a list of all modules in a modular JDK that match a JRE. + +java.base +java.compiler +java.datatransfer +java.xml +java.prefs +java.desktop +java.instrument +java.logging +java.management +java.security.sasl +java.naming +java.rmi +java.management.rmi +java.net.http +java.scripting +java.security.jgss +java.transaction.xa +java.sql +java.sql.rowset +java.xml.crypto +java.se +java.smartcardio +jdk.internal.jvmstat +jdk.attach +jdk.charsets +jdk.compiler +jdk.crypto.ec +jdk.crypto.cryptoki +jdk.dynalink +jdk.httpserver +jdk.internal.le +jdk.internal.opt +jdk.internal.vm.ci +jdk.management +jdk.unsupported +jdk.internal.vm.compiler +jdk.internal.vm.compiler.management +jdk.jartool +jdk.jcmd +jdk.jdwp.agent +jdk.jdi +jdk.jsobject +jdk.jstatd +jdk.localedata +jdk.management.agent +jdk.naming.dns +jdk.naming.rmi +jdk.net +jdk.scripting.nashorn +jdk.sctp +jdk.security.auth +jdk.security.jgss +jdk.xml.dom +jdk.zipfs --- /dev/null 2018-10-22 10:43:42.000000000 -0400 +++ new/src/jdk.packager/share/classes/jdk/packager/main/Main.java 2018-10-22 10:43:39.987678300 -0400 @@ -0,0 +1,113 @@ +/* + * 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.packager.main; + +import jdk.packager.internal.Arguments; +import jdk.packager.internal.Log; +import jdk.packager.internal.CLIHelp; +import java.io.PrintWriter; +import java.util.ResourceBundle; + +public class Main { + + private static final ResourceBundle bundle = + ResourceBundle.getBundle("jdk.packager.internal.resources.Bundle"); + + private static final String version = bundle.getString("MSG_Version") + + " " + System.getProperty("java.version") + "\n"; + + /** + * main(String... args) + * This is the entry point for the jpackager 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)){ + CLIHelp.showHelp(false); + } else if (args.length == 1 && args[0].equals("--version")) { + Log.info(version); + } else { + try { + Arguments arguments = new Arguments(args); + arguments.processArguments(); + } 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; + } + +} --- /dev/null 2018-10-22 10:43:48.000000000 -0400 +++ new/src/jdk.packager/share/classes/module-info.java 2018-10-22 10:43:46.027994700 -0400 @@ -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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 packager tool, jpackager. + * + *

jpackager is a tool for generating self-contained application bundles. + * + * @moduleGraph + * @since 12 + */ + +module jdk.packager { + requires jdk.jlink; + + requires java.xml; + requires java.logging; + requires java.desktop; + + uses jdk.packager.internal.Bundler; + uses jdk.packager.internal.Bundlers; + + provides jdk.packager.internal.Bundlers with + jdk.packager.internal.BasicBundlers; + + provides java.util.spi.ToolProvider + with jdk.packager.internal.JPackagerToolProvider; +} --- /dev/null 2018-10-22 10:43:54.000000000 -0400 +++ new/src/jdk.packager/share/native/library/common/FilePath.cpp 2018-10-22 10:43:52.470918600 -0400 @@ -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(); + } + } +} --- /dev/null 2018-10-22 10:44:01.000000000 -0400 +++ new/src/jdk.packager/share/native/library/common/FilePath.h 2018-10-22 10:43:59.268050200 -0400 @@ -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 --- /dev/null 2018-10-22 10:44:08.000000000 -0400 +++ new/src/jdk.packager/share/native/library/common/GenericPlatform.cpp 2018-10-22 10:44:05.918778500 -0400 @@ -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(PACKAGER_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 --- /dev/null 2018-10-22 10:44:16.000000000 -0400 +++ new/src/jdk.packager/share/native/library/common/GenericPlatform.h 2018-10-22 10:44:14.046534800 -0400 @@ -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 --- /dev/null 2018-10-22 10:44:22.000000000 -0400 +++ new/src/jdk.packager/share/native/library/common/Helpers.cpp 2018-10-22 10:44:20.832665300 -0400 @@ -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 result = false; + Name = _T(""); + Value = _T(""); + unsigned int index = 0; + + for (; index < option.length(); index++) { + TCHAR c = option[index]; + + switch (c) { + case '=': { + index++; + result = 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 (result) { + Value = option.substr(index, index - option.length()); + } + + return result; +} + + +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[PACKAGER_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; +} --- /dev/null 2018-10-22 10:44:30.000000000 -0400 +++ new/src/jdk.packager/share/native/library/common/Helpers.h 2018-10-22 10:44:27.962002400 -0400 @@ -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 --- /dev/null 2018-10-22 10:44:37.000000000 -0400 +++ new/src/jdk.packager/share/native/library/common/IniFile.cpp 2018-10-22 10:44:34.763733200 -0400 @@ -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(); +} --- /dev/null 2018-10-22 10:44:43.000000000 -0400 +++ new/src/jdk.packager/share/native/library/common/IniFile.h 2018-10-22 10:44:41.311260000 -0400 @@ -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 --- /dev/null 2018-10-22 10:44:49.000000000 -0400 +++ new/src/jdk.packager/share/native/library/common/JavaTypes.cpp 2018-10-22 10:44:47.679182700 -0400 @@ -0,0 +1,322 @@ +/* + * 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); +} + +//---------------------------------------------------------------------------- + +JavaStaticMethod::JavaStaticMethod(JNIEnv *Env, jclass Class, jmethodID Method) { + FEnv = Env; + FClass = Class; + FMethod = Method; +} + +void JavaStaticMethod::CallVoidMethod(int Count, ...) { + va_list args; + va_start(args, Count); + FEnv->CallStaticVoidMethodV(FClass, FMethod, args); + va_end(args); + + if (FEnv->ExceptionCheck() == JNI_TRUE) { + Messages& messages = Messages::GetInstance(); + throw JavaException(FEnv, messages.GetMessage(ERROR_INVOKING_METHOD)); + } +} + +JavaStaticMethod::operator jmethodID () { + return FMethod; +} + +//---------------------------------------------------------------------------- + +JavaMethod::JavaMethod(JNIEnv *Env, jobject Obj, jmethodID Method) { + FEnv = Env; + FObj = Obj; + FMethod = Method; +} + +void JavaMethod::CallVoidMethod(int Count, ...) { + va_list args; + va_start(args, Count); + FEnv->CallVoidMethodV(FObj, FMethod, args); + va_end(args); + + if (FEnv->ExceptionCheck() == JNI_TRUE) { + Messages& messages = Messages::GetInstance(); + throw JavaException(FEnv, messages.GetMessage(ERROR_INVOKING_METHOD)); + } +} + +JavaMethod::operator jmethodID () { + return FMethod; +} + +//---------------------------------------------------------------------------- + +JavaClass::JavaClass(JNIEnv *Env, TString Name) { + FEnv = Env; + FClassName = Name; + FClass = FEnv->FindClass(PlatformString(FClassName)); + + if (FClass == NULL || FEnv->ExceptionCheck() == JNI_TRUE) { + Messages& messages = Messages::GetInstance(); + TString message = messages.GetMessage(CLASS_NOT_FOUND); + message = PlatformString::Format(message, FClassName.data()); + throw JavaException(FEnv, message); + } +} + +JavaClass::~JavaClass() { + FEnv->DeleteLocalRef(FClass); +} + +JavaStaticMethod JavaClass::GetStaticMethod(TString Name, TString Signature) { + jmethodID method = FEnv->GetStaticMethodID(FClass, PlatformString(Name), + PlatformString(Signature)); + + if (method == NULL || FEnv->ExceptionCheck() == JNI_TRUE) { + Messages& messages = Messages::GetInstance(); + TString message = messages.GetMessage(METHOD_NOT_FOUND); + message = PlatformString::Format(message, Name.data(), + FClassName.data()); + throw JavaException(FEnv, message); + } + + return JavaStaticMethod(FEnv, FClass, method); +} + +JavaClass::operator jclass () { + return FClass; +} + +//---------------------------------------------------------------------------- + +void JavaStringArray::Initialize(size_t Size) { + JavaClass jstringClass(FEnv, _T("java/lang/String")); + + if (FEnv->ExceptionCheck() == JNI_TRUE) { + Messages& messages = Messages::GetInstance(); + TString message = messages.GetMessage(CLASS_NOT_FOUND); + message = PlatformString::Format(message, _T("String")); + throw JavaException(FEnv, message.data()); + } + + jstring str = PlatformString("").toJString(FEnv); + FData = (jobjectArray)FEnv->NewObjectArray((jsize)Size, jstringClass, str); + + if (FEnv->ExceptionCheck() == JNI_TRUE) { + throw JavaException(FEnv, _T("Error")); + } +} + +JavaStringArray::JavaStringArray(JNIEnv *Env, size_t Size) { + FEnv = Env; + Initialize(Size); +} + +JavaStringArray::JavaStringArray(JNIEnv *Env, jobjectArray Data) { + FEnv = Env; + FData = Data; +} + +JavaStringArray::JavaStringArray(JNIEnv *Env, std::list Items) { + FEnv = Env; + Initialize(Items.size()); + unsigned int index = 0; + + for (std::list::const_iterator iterator = Items.begin(); + iterator != Items.end(); iterator++) { + TString item = *iterator; + SetValue(index, PlatformString(item).toJString(FEnv)); + index++; + } +} + +jobjectArray JavaStringArray::GetData() { + return FData; +} + +void JavaStringArray::SetValue(jsize Index, jstring Item) { + FEnv->SetObjectArrayElement(FData, Index, Item); + + if (FEnv->ExceptionCheck() == JNI_TRUE) { + throw JavaException(FEnv, _T("Error")); + } +} + +jstring JavaStringArray::GetValue(jsize Index) { + jstring result = (jstring)FEnv->GetObjectArrayElement(FData, Index); + + if (FEnv->ExceptionCheck() == JNI_TRUE) { + throw JavaException(FEnv, _T("Error")); + } + + return result; +} + +unsigned int JavaStringArray::Count() { + unsigned int result = FEnv->GetArrayLength(FData); + + if (FEnv->ExceptionCheck() == JNI_TRUE) { + throw JavaException(FEnv, _T("Error")); + } + + return result; +} --- /dev/null 2018-10-22 10:44:55.000000000 -0400 +++ new/src/jdk.packager/share/native/library/common/JavaTypes.h 2018-10-22 10:44:53.811100700 -0400 @@ -0,0 +1,153 @@ +/* + * 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 JavaClass; +class JavaStaticMethod; +class JavaMethod; +class JavaStringArray; + + +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(); +}; + + +class JavaStaticMethod { +// Prohibit Heap-Based Classes. +private: + static void *operator new(size_t size); + static void operator delete(void *ptr); + +private: + JNIEnv *FEnv; + jmethodID FMethod; + jclass FClass; +public: + JavaStaticMethod(JNIEnv *Env, jclass Class, jmethodID Method); + + void CallVoidMethod(int Count, ...); + operator jmethodID (); +}; + + +class JavaMethod { +// Prohibit Heap-Based Classes. +private: + static void *operator new(size_t size); + static void operator delete(void *ptr); + + JavaMethod(JavaMethod const&); // Don't Implement. + void operator=(JavaMethod const&); // Don't implement + +private: + JNIEnv *FEnv; + jmethodID FMethod; + jobject FObj; +public: + JavaMethod(JNIEnv *Env, jobject Obj, jmethodID Method); + + void CallVoidMethod(int Count, ...); + operator jmethodID (); +}; + + +class JavaClass { +// Prohibit Heap-Based Classes. +private: + static void *operator new(size_t size); + static void operator delete(void *ptr); + + JavaClass(JavaClass const&); // Don't Implement. + void operator=(JavaClass const&); // Don't implement + +private: + JNIEnv *FEnv; + jclass FClass; + TString FClassName; + +public: + JavaClass(JNIEnv *Env, TString Name); + ~JavaClass(); + + JavaStaticMethod GetStaticMethod(TString Name, TString Signature); + operator jclass (); +}; + + +class JavaStringArray { +// Prohibit Heap-Based Classes. +private: + static void *operator new(size_t size); + static void operator delete(void *ptr); + + JavaStringArray(JavaStringArray const&); // Don't Implement. + void operator=(JavaStringArray const&); // Don't implement + +private: + JNIEnv *FEnv; + jobjectArray FData; + + void Initialize(size_t Size); + +public: + JavaStringArray(JNIEnv *Env, size_t Size); + JavaStringArray(JNIEnv *Env, jobjectArray Data); + JavaStringArray(JNIEnv *Env, std::list Array); + + jobjectArray GetData(); + void SetValue(jsize Index, jstring Item); + jstring GetValue(jsize Index); + unsigned int Count(); +}; + +#endif // JAVATYPES_H --- /dev/null 2018-10-22 10:45:02.000000000 -0400 +++ new/src/jdk.packager/share/native/library/common/JavaVirtualMachine.cpp 2018-10-22 10:45:00.069821300 -0400 @@ -0,0 +1,391 @@ +/* + * 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(JvmLaunchType type) { + bool result = false; + JavaVirtualMachine javavm; + + switch (type){ + case USER_APP_LAUNCH: + result = javavm.StartJVM(); + break; + case SINGLE_INSTANCE_NOTIFICATION_LAUNCH: + result = javavm.NotifySingleInstance(); + break; + default: + break; + } + + 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, false); +} + +bool JavaVirtualMachine::NotifySingleInstance() { + Package& package = Package::GetInstance(); + + std::list vmargs; + vmargs.push_back(package.GetCommandName()); + + JavaOptions options; + options.AppendValue(_T("-Djava.library.path"), + package.GetPackageAppDirectory() + FilePath::PathSeparator() + + package.GetPackageLauncherDirectory()); + options.AppendValue(_T("-Djava.launcher.path"), + package.GetPackageLauncherDirectory()); + // launch SingleInstanceNewActivation.main() to pass arguments to + // another instance + options.AppendValue(_T("-m")); + options.AppendValue( + _T("jdk.packager.services/jdk.packager.services.singleton.SingleInstanceNewActivation")); + + configureLibrary(); + + return launchVM(options, vmargs, true); +} + +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, bool addSiProcessId) { + 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 + + if (addSiProcessId) { + // add single instance process ID as a first argument + TProcessID pid = platform.GetSingleInstanceProcessId(); + std::ostringstream s; + s << pid; + std::string procIdStr(s.str()); + vmargs.push_back(TString(procIdStr.begin(), procIdStr.end())); + } + + 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; +} --- /dev/null 2018-10-22 10:45:08.000000000 -0400 +++ new/src/jdk.packager/share/native/library/common/JavaVirtualMachine.h 2018-10-22 10:45:06.403543100 -0400 @@ -0,0 +1,104 @@ +/* + * 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" + + +enum JvmLaunchType { + USER_APP_LAUNCH, + SINGLE_INSTANCE_NOTIFICATION_LAUNCH, + JVM_LAUNCH_TYPES_NUM +}; + +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, + bool addSiProcessId); +public: + JavaVirtualMachine(); + ~JavaVirtualMachine(void); + + bool StartJVM(); + bool NotifySingleInstance(); +}; + +bool RunVM(JvmLaunchType type); + +#endif // JAVAVIRTUALMACHINE_H --- /dev/null 2018-10-22 10:45:15.000000000 -0400 +++ new/src/jdk.packager/share/native/library/common/LinuxPlatform.cpp 2018-10-22 10:45:12.911868500 -0400 @@ -0,0 +1,1190 @@ +/* + * 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_PACKAGER_TMP_DIR "/.java/packager/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_PACKAGER_TMP_DIR); +} + +void LinuxPlatform::reactivateAnotherInstance() { + if (singleInstanceProcessId == 0) { + printf("Unable to reactivate another instance, PID is undefined"); + return; + } + + const ProcessReactivator reactivator(singleInstanceProcessId); +} + +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_PACKAGER_PLATFORM__ +#define __UNIX_PACKAGER_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 PACKAGER_MULTIBYTE_SNPRINTF snprintf + +#define PACKAGER_SNPRINTF(buffer, sizeOfBuffer, count, format, ...) \ + snprintf((buffer), (count), (format), __VA_ARGS__) + +#define PACKAGER_PRINTF(format, ...) \ + printf((format), ##__VA_ARGS__) + +#define PACKAGER_FPRINTF(dest, format, ...) \ + fprintf((dest), (format), __VA_ARGS__) + +#define PACKAGER_SSCANF(buf, format, ...) \ + sscanf((buf), (format), __VA_ARGS__) + +#define PACKAGER_STRDUP(strSource) \ + strdup((strSource)) + +//return "error code" (like on Windows) +static int PACKAGER_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 PACKAGER_STRICMP(x, y) \ + strcasecmp((x), (y)) + +#define PACKAGER_STRNICMP(x, y, cnt) \ + strncasecmp((x), (y), (cnt)) + +#define PACKAGER_STRNCMP(x, y, cnt) \ + strncmp((x), (y), (cnt)) + +#define PACKAGER_STRLEN(x) \ + strlen((x)) + +#define PACKAGER_STRSTR(x, y) \ + strstr((x), (y)) + +#define PACKAGER_STRCHR(x, y) \ + strchr((x), (y)) + +#define PACKAGER_STRRCHR(x, y) \ + strrchr((x), (y)) + +#define PACKAGER_STRPBRK(x, y) \ + strpbrk((x), (y)) + +#define PACKAGER_GETENV(x) \ + getenv((x)) + +#define PACKAGER_PUTENV(x) \ + putenv((x)) + +#define PACKAGER_STRCMP(x, y) \ + strcmp((x), (y)) + +#define PACKAGER_STRCPY(x, y) \ + strcpy((x), (y)) + +#define PACKAGER_STRCAT(x, y) \ + strcat((x), (y)) + +#define PACKAGER_ATOI(x) \ + atoi((x)) + +#define PACKAGER_FOPEN(x, y) \ + fopen((x), (y)) + +#define PACKAGER_FGETS(x, y, z) \ + fgets((x), (y), (z)) + +#define PACKAGER_REMOVE(x) \ + remove((x)) + +#define PACKAGER_SPAWNV(mode, cmd, args) \ + spawnv((mode), (cmd), (args)) + +#define PACKAGER_ISDIGIT(ch) isdigit(ch) + +// for non-unicode, just return the input string for +// the following 2 conversions +#define PACKAGER_NEW_MULTIBYTE(message) message + +#define PACKAGER_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 PACKAGER_RELEASE_MULTIBYTE(tmpMBCS) + +#define PACKAGER_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 PACKAGER_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 (PACKAGER_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 (PACKAGER_STRNCMP(p, _T("') { + NEXT_CHAR(p); + return p; + } + NEXT_CHAR(p); + } + } + } + return p; +} + +static TCHAR* SkipXMLProlog(TCHAR *p) { + if (p != NULL) { + if (PACKAGER_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 (PACKAGER_STRNCMP(p, _T("&"), 5) == 0) { + *q++ = '&'; + SKIP_CHARS(p, 5); + } else if (PACKAGER_STRNCMP(p, _T("<"), 4) == 0) { + *q = '<'; + SKIP_CHARS(p, 4); + } else if (PACKAGER_STRNCMP(p, _T(">"), 4) == 0) { + *q = '>'; + SKIP_CHARS(p, 4); + } else if (PACKAGER_STRNCMP(p, _T("'"), 6) == 0) { + *q = '\''; + SKIP_CHARS(p, 6); + } else if (PACKAGER_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; + PACKAGER_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, PACKAGER_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, PACKAGER_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 = PACKAGER_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, PACKAGER_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 && PACKAGER_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 (PACKAGER_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) { + PACKAGER_PRINTF(_T("\n")); + indent(indt); + PACKAGER_PRINTF(_T("<%s"), node->_name); + PrintXMLAttributes(node->_attributes); + if (node->_sub == NULL) { + PACKAGER_PRINTF(_T("/>\n")); + } else { + PACKAGER_PRINTF(_T(">")); + PrintXMLDocument(node->_sub, indt + 1); + indent(indt); + PACKAGER_PRINTF(_T(""), node->_name); + } + } else { + PACKAGER_PRINTF(_T("%s"), node->_name); + } + PrintXMLDocument(node->_next, indt); +} + +static void PrintXMLAttributes(XMLAttribute* attr) { + if (attr == NULL) return; + + PACKAGER_PRINTF(_T(" %s=\"%s\""), attr->_name, attr->_value); + PrintXMLAttributes(attr->_next); +} + +static void indent(int indt) { + int i; + for(i = 0; i < indt; i++) { + PACKAGER_PRINTF(_T(" ")); + } +} + +const TCHAR *CDStart = _T(""); + + +static TCHAR* SkipPCData(TCHAR *p) { + TCHAR *end = PACKAGER_STRSTR(p, CDEnd); + if (end != NULL) { + return end+sizeof(CDEnd); + } + return (++p); +} + +static int IsPCData(TCHAR *p) { + const int size = sizeof(CDStart); + return (PACKAGER_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 --- /dev/null 2018-10-22 10:45:22.000000000 -0400 +++ new/src/jdk.packager/share/native/library/common/LinuxPlatform.h 2018-10-22 10:45:20.059806200 -0400 @@ -0,0 +1,98 @@ +/* + * 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 void reactivateAnotherInstance(); + 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 --- /dev/null 2018-10-22 10:45:30.000000000 -0400 +++ new/src/jdk.packager/share/native/library/common/Lock.cpp 2018-10-22 10:45:28.161362400 -0400 @@ -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; +} --- /dev/null 2018-10-22 10:45:36.000000000 -0400 +++ new/src/jdk.packager/share/native/library/common/Lock.h 2018-10-22 10:45:34.245479400 -0400 @@ -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 --- /dev/null 2018-10-22 10:45:43.000000000 -0400 +++ new/src/jdk.packager/share/native/library/common/MacPlatform.h 2018-10-22 10:45:41.224814100 -0400 @@ -0,0 +1,81 @@ +/* + * 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 void reactivateAnotherInstance(); + 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 --- /dev/null 2018-10-22 10:45:50.000000000 -0400 +++ new/src/jdk.packager/share/native/library/common/Macros.cpp 2018-10-22 10:45:47.842341600 -0400 @@ -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)); +} --- /dev/null 2018-10-22 10:45:57.000000000 -0400 +++ new/src/jdk.packager/share/native/library/common/Macros.h 2018-10-22 10:45:55.439687700 -0400 @@ -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 --- /dev/null 2018-10-22 10:46:04.000000000 -0400 +++ new/src/jdk.packager/share/native/library/common/Messages.cpp 2018-10-22 10:46:02.085415500 -0400 @@ -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; +} --- /dev/null 2018-10-22 10:46:12.000000000 -0400 +++ new/src/jdk.packager/share/native/library/common/Messages.h 2018-10-22 10:46:09.763733300 -0400 @@ -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 --- /dev/null 2018-10-22 10:46:20.000000000 -0400 +++ new/src/jdk.packager/share/native/library/common/OrderedMap.h 2018-10-22 10:46:18.162641500 -0400 @@ -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 --- /dev/null 2018-10-22 10:46:27.000000000 -0400 +++ new/src/jdk.packager/share/native/library/common/Package.cpp 2018-10-22 10:46:24.784127000 -0400 @@ -0,0 +1,595 @@ +/* + * 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; +} + +bool Package::CheckForSingleInstance() { + Platform& platform = Platform::GetInstance(); +#ifdef MAC + if (platform.IsMainThread()) { + return false; + } +#endif + if (FInitialized == true) { + // everything must be initialised at this point + return false; + } + TString appName; + TString appVersion; + AutoFreePtr config = + platform.GetConfigFile(platform.GetConfigFileName()); + std::map keys = platform.GetKeys(); + config->GetValue(keys[CONFIG_SECTION_APPLICATION], + keys[APP_NAME_KEY], appName); + config->GetValue(keys[CONFIG_SECTION_APPLICATION], + keys[CONFIG_VERSION], appVersion); + TString singleInstance; + config->GetValue(keys[CONFIG_SECTION_APPLICATION], + keys[CONFIG_APPLICATION_INSTANCE], singleInstance); + if (singleInstance == _T("single")) { + TString uniqueID = appName + FBootFields->FAppID + appVersion; + // if another instance is running, later we can try to reactivate it + return platform.CheckForSingleInstance(uniqueID); + } + return false; +} + +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[PACKAGER_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; +} --- /dev/null 2018-10-22 10:46:34.000000000 -0400 +++ new/src/jdk.packager/share/native/library/common/Package.h 2018-10-22 10:46:31.807822800 -0400 @@ -0,0 +1,129 @@ +/* + * 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(); + bool CheckForSingleInstance(); + + 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 --- /dev/null 2018-10-22 10:46:41.000000000 -0400 +++ new/src/jdk.packager/share/native/library/common/Platform.cpp 2018-10-22 10:46:39.174117500 -0400 @@ -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); + } + } + } +} --- /dev/null 2018-10-22 10:46:49.000000000 -0400 +++ new/src/jdk.packager/share/native/library/common/Platform.h 2018-10-22 10:46:46.446811000 -0400 @@ -0,0 +1,529 @@ +/* + * 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 PACKAGER_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: + TProcessID singleInstanceProcessId; + + Platform(void): FAppCDSState(cdsUninitialized), singleInstanceProcessId(0) { + } + +public: + AppCDSState GetAppCDSState() { return FAppCDSState; } + void SetAppCDSState(AppCDSState Value) { FAppCDSState = Value; } + TProcessID GetSingleInstanceProcessId() { return singleInstanceProcessId; } + + 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; + virtual bool CheckForSingleInstance(TString Name) = 0; + virtual void reactivateAnotherInstance() = 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 --- /dev/null 2018-10-22 10:46:56.000000000 -0400 +++ new/src/jdk.packager/share/native/library/common/PlatformString.cpp 2018-10-22 10:46:53.966107400 -0400 @@ -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; +} --- /dev/null 2018-10-22 10:47:03.000000000 -0400 +++ new/src/jdk.packager/share/native/library/common/PlatformString.h 2018-10-22 10:47:00.939396800 -0400 @@ -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 --- /dev/null 2018-10-22 10:47:10.000000000 -0400 +++ new/src/jdk.packager/share/native/library/common/PlatformThread.cpp 2018-10-22 10:47:07.222678400 -0400 @@ -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 +} --- /dev/null 2018-10-22 10:47:16.000000000 -0400 +++ new/src/jdk.packager/share/native/library/common/PlatformThread.h 2018-10-22 10:47:14.688574900 -0400 @@ -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 --- /dev/null 2018-10-22 10:47:23.000000000 -0400 +++ new/src/jdk.packager/share/native/library/common/PosixPlatform.cpp 2018-10-22 10:47:20.850653900 -0400 @@ -0,0 +1,414 @@ +/* + * 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) { + if (!SingleInstanceFile.empty()) { + unlink(SingleInstanceFile.c_str()); + } +} + +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; +} + +// returns true if another instance is already running. +// if false, we need to continue regular launch. +bool PosixPlatform::CheckForSingleInstance(TString appName) { + TString tmpDir = GetTempDirectory(); + if (tmpDir.empty()) { + printf("Unable to check for single instance.\n"); + return false; + } + + TString lockFile = tmpDir + "/" + fixName(appName); + SingleInstanceFile = lockFile; + int pid_file = open(lockFile.c_str(), O_CREAT | O_RDWR, 0666); + int rc = flock(pid_file, LOCK_EX | LOCK_NB); + + if (rc) { + if (EWOULDBLOCK == errno) { + // another instance is running + pid_t pid = 0; + read(pid_file, (void*)&pid, sizeof(pid_t)); + printf("Another instance is running PID: %d\n", pid); + if (pid != 0) { + singleInstanceProcessId = pid; + SingleInstanceFile.clear(); + return true; + } + } else { + printf("Unable to check for single instance.\n"); + } + } else { + // It is the first instance. + pid_t pid = getpid(); + write(pid_file, (void*)&pid, sizeof(pid_t)); + } + + return false; +} + +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]; + + 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] == EOF) { + buffer[count] = '\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 --- /dev/null 2018-10-22 10:47:30.000000000 -0400 +++ new/src/jdk.packager/share/native/library/common/PosixPlatform.h 2018-10-22 10:47:28.404151000 -0400 @@ -0,0 +1,94 @@ +/* + * 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 SingleInstanceFile; + + 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 bool CheckForSingleInstance(TString Name); + 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 --- /dev/null 2018-10-22 10:47:37.000000000 -0400 +++ new/src/jdk.packager/share/native/library/common/PropertyFile.cpp 2018-10-22 10:47:35.190238000 -0400 @@ -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; +} --- /dev/null 2018-10-22 10:47:43.000000000 -0400 +++ new/src/jdk.packager/share/native/library/common/PropertyFile.h 2018-10-22 10:47:41.383517400 -0400 @@ -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 --- /dev/null 2018-10-22 10:47:49.000000000 -0400 +++ new/src/jdk.packager/share/native/library/common/WindowsPlatform.cpp 2018-10-22 10:47:47.873200600 -0400 @@ -0,0 +1,839 @@ +/* + * 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_PACKAGER_TMP_DIR \ + L"\\AppData\\LocalLow\\Sun\\Java\\Packager\\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_PACKAGER_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; +} + +void WindowsPlatform::reactivateAnotherInstance() { + if (singleInstanceProcessId == 0) { + printf("Unable to reactivate another instance, PID is undefined"); + return; + } + EnumWindows(&enumWindows, (LPARAM)singleInstanceProcessId); +} + +// returns true if another instance is already running. +// if false, we need to continue regular launch. +bool WindowsPlatform::CheckForSingleInstance(TString name) { + if (SingleInstance::getInstance(name)->IsAnotherInstanceRunning()) { + // read PID + DWORD pid = SingleInstance::getInstance(name)->readPid(); + if (pid != 0) { + singleInstanceProcessId = pid; + return true; + } + } else { + // it is the first intance + // write pid and continue regular launch + SingleInstance::getInstance(name)->writePid(GetCurrentProcessId()); + } + return false; +} + +SingleInstance::SingleInstance(TString& name_): BUF_SIZE(256), _name(name_), + _hMapFile(NULL), _pBuf(NULL) { + _mutex = CreateMutex(NULL, TRUE, name_.data()); + _lastError = GetLastError(); + _sharedMemoryName = _T("Local\\jpackager-") + _name; +} + +SingleInstance::~SingleInstance() { + if (_pBuf != NULL) { + UnmapViewOfFile(_pBuf); + _pBuf = NULL; + } + + if (_hMapFile != NULL) { + CloseHandle(_hMapFile); + _hMapFile = NULL; + } + + if (_mutex != NULL) { + CloseHandle(_mutex); + _mutex = NULL; + } +} + +bool SingleInstance::writePid(DWORD pid) { + _hMapFile = CreateFileMapping( + INVALID_HANDLE_VALUE, + NULL, + PAGE_READWRITE, + 0, + BUF_SIZE, + _sharedMemoryName.data()); + + if (_hMapFile == NULL) { + return false; + } + + _pBuf = (LPTSTR) MapViewOfFile(_hMapFile, + FILE_MAP_ALL_ACCESS, + 0, + 0, + BUF_SIZE); + + if (_pBuf == NULL) { + CloseHandle(_hMapFile); + _hMapFile = NULL; + return false; + } + + CopyMemory((PVOID)_pBuf, &pid, sizeof(DWORD)); + + return true; +} + +DWORD SingleInstance::readPid() { + _hMapFile = OpenFileMapping( + FILE_MAP_ALL_ACCESS, + FALSE, + _sharedMemoryName.data()); + + if (_hMapFile == NULL) { + return 0; + } + + _pBuf = (LPTSTR) MapViewOfFile(_hMapFile, + FILE_MAP_ALL_ACCESS, + 0, + 0, + BUF_SIZE); + + if (_pBuf == NULL) { + CloseHandle(_hMapFile); + _hMapFile = NULL; + return 0; + } + + DWORD pid = 0; + CopyMemory(&pid, (PVOID)_pBuf, sizeof(DWORD)); + + return pid; +} + +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 --- /dev/null 2018-10-22 10:47:56.000000000 -0400 +++ new/src/jdk.packager/share/native/library/common/WindowsPlatform.h 2018-10-22 10:47:54.035279600 -0400 @@ -0,0 +1,230 @@ +/* + * 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 + + +// the class is used to create and detect single instance of user application +class SingleInstance { +private: + const int BUF_SIZE; + + DWORD _lastError; + HANDLE _mutex; + TString _name; + TString _sharedMemoryName; + HANDLE _hMapFile; + LPCTSTR _pBuf; + + SingleInstance(): BUF_SIZE(0) {} + + SingleInstance(TString& name_); + +public: + static SingleInstance* getInstance(TString& name) { + static SingleInstance* result = NULL; + + if (result == NULL) { + result = new SingleInstance(name); + } + + return result; + } + + ~SingleInstance(); + + bool IsAnotherInstanceRunning() { + return (ERROR_ALREADY_EXISTS == _lastError); + } + + bool writePid(DWORD pid); + DWORD readPid(); +}; + +#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 void reactivateAnotherInstance(); + virtual bool IsMainThread(); + virtual bool CheckForSingleInstance(TString Name); + 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 --- /dev/null 2018-10-22 10:48:03.000000000 -0400 +++ new/src/jdk.packager/share/native/library/common/main.cpp 2018-10-22 10:48:00.899367600 -0400 @@ -0,0 +1,218 @@ +/* + * 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 launcher program for application packaging on Windows, Mac, + and Linux. + +Basic approach: + - Launcher executable loads packager.dll/libpackager.dylib/libpackager.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()); + + if (package.CheckForSingleInstance()) { + // reactivate the first instance if the process Id is valid + platform.reactivateAnotherInstance(); + if (package.GetArgs().size() > 0 && + platform.GetSingleInstanceProcessId() != 0) { + // if user specified args, pass them to the first instance + return RunVM(SINGLE_INSTANCE_NOTIFICATION_LAUNCH); + } + return true; + } + + 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(USER_APP_LAUNCH); + } catch (FileNotFoundException &e) { + platform.ShowMessage(e.GetMessage()); + } + + return result; + } + + JNIEXPORT void stop_launcher() { + } +} --- /dev/null 2018-10-22 10:48:09.000000000 -0400 +++ new/src/jdk.packager/unix/scripts/jpackager 2018-10-22 10:48:07.747855400 -0400 @@ -0,0 +1,103 @@ +#!/bin/bash +# +# Java Packager tool execution script for Linux and MacOS. +# + +# Default values. +DEBUG="" +MEMORY="-Xmx512M" +JAVA_ARGS="" +DEBUG_ARG="-J-Xdebug:"; + +# Argument parsing. +args= +narg=1 +for i in "$@"; do + if [[ "$i" == "-J-Xmx"* ]]; then + ARGUMENT=${i:2} + MEMORY=${ARGUMENT} + elif [[ "$i" == ${DEBUG_ARG}* ]]; then + ADDRESS=${i:${#DEBUG_ARG}} + DEBUG="-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=${ADDRESS}" + elif [[ "$i" == "-J-"* ]]; then + ARGUMENT=${i:2} + JAVA_ARGS="${JAVA_ARGS} ${ARGUMENT}" + else + args="$args \"$""{"$narg"}\"" + fi + narg=`expr $narg + 1` +done + +# resolve symlinks +PRG=$0 +while [ -h "$PRG" ]; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '^.*-> \(.*\)$' 2>/dev/null` + if expr "$link" : '^/' 2> /dev/null >/dev/null; then + PRG="$link" + else + PRG="`dirname "$PRG"`/$link" + fi +done + +# detect Darwin and Cygwin environments +darwin=false; +cygwin=false; +case "`uname`" in + Darwin*) darwin=true; + if [ -z "$JAVA_HOME" ] ; then + BIN_DIR=`dirname $PRG` + JAVA_HOME=`cd $BIN_DIR > /dev/null; pwd`/.. + fi + ;; + CYGWIN*) cygwin=true;; +esac + +JAVA_PACKAGER_PATH=$(cd $(dirname $0) ; pwd -P) +JAVA_CMD="${JAVA_PACKAGER_PATH}/java" + +if [[ ! -f ${JAVA_CMD} ]]; then + if [ -n "$JAVA_HOME" -a -x "$JAVA_HOME/bin/javac" ] ; then + JAVA_CMD="$JAVA_HOME/bin/java" + export JAVA_HOME + else + JAVAC_CMD=`which javac 2> /dev/null ` + while [ -h "$JAVAC_CMD" ]; do + ls=`ls -ld "$JAVAC_CMD"` + link=`expr "$ls" : '^.*-> \(.*\)$' 2>/dev/null` + if expr "$link" : '^/' 2> /dev/null >/dev/null; then + JAVAC_CMD="$link" + else + JAVAC_CMD="`dirname "$JAVAC_CMD"`/$link" + fi + done + + BIN_DIR=`dirname "$JAVAC_CMD"` + JAVA_HOME=`dirname "$BIN_DIR"` + if [ "m$JAVA_HOME" != "m." ]; then + JAVA_CMD="$JAVA_HOME/bin/java" + export JAVA_HOME + else + unset JAVA_HOME + JAVA_CMD=`which java 2> /dev/null ` + if [ -z "$JAVA_CMD" ]; then + JAVA_CMD="java" + fi + fi + fi +fi + +if [ ! -x "$JAVA_CMD" ] ; then + echo 'Error: JAVA_HOME is not defined, cannot find "java" command.' + exit 1 +fi + +if $cygwin ; then + JAVA_CMD=`cygpath --unix "$JAVA_CMD"` +fi + +# This is similar to "$@" except we had to strip out some arguments +# that we don't want to be passed to the Java Packager. +eval exec "$JAVA_CMD" ${DEBUG} ${MEMORY} ${JAVA_ARGS} \ + -m jdk.packager/jdk.packager.main.Main $args + --- /dev/null 2018-10-22 10:48:17.000000000 -0400 +++ new/src/jdk.packager/windows/classes/jdk/packager/internal/builders/windows/WindowsAppImageBuilder.java 2018-10-22 10:48:14.956147900 -0400 @@ -0,0 +1,497 @@ +/* + * 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.packager.internal.builders.windows; + +import jdk.packager.internal.BundlerParamInfo; +import jdk.packager.internal.Log; +import jdk.packager.internal.RelativeFileSet; +import jdk.packager.internal.IOUtils; +import jdk.packager.internal.StandardBundlerParam; +import jdk.packager.internal.resources.windows.WinResources; +import jdk.packager.internal.windows.WindowsBundlerParam; +import jdk.packager.internal.builders.AbstractAppImageBuilder; +import jdk.packager.internal.windows.WindowsDefender; + +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.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.packager.internal.Arguments; + +import static jdk.packager.internal.StandardBundlerParam.*; + +public class WindowsAppImageBuilder extends AbstractAppImageBuilder { + + private static final ResourceBundle I18N = + ResourceBundle.getBundle( + "jdk.packager.internal.resources.builders.windows.WindowsAppImageBuilder"); + + private static final String MODULES_FILENAME = + "jdk/packager/internal/resources/windows/windows.jre.list"; + + protected static final String WINDOWS_BUNDLER_PREFIX = + BUNDLER_PREFIX + "windows" + File.separator; + + private final static String EXECUTABLE_NAME = "WinLauncher.exe"; + private final static String LIBRARY_NAME = "packager.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.properties"; + + private final Path root; + private final Path appDir; + private final Path runtimeDir; + private final Path mdir; + + private final Map params; + + public static final BundlerParamInfo CONFIG_ROOT = + new WindowsBundlerParam<>( + I18N.getString("param.config-root.name"), + I18N.getString("param.config-root.description"), + "configRoot", + File.class, + params -> { + File imagesRoot = + new File(BUILD_ROOT.fetchFrom(params), "windows"); + imagesRoot.mkdirs(); + return imagesRoot; + }, + (s, p) -> null); + + 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.info(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.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.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); + } + } + + // This method 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) + ".exe"; + } + + // Returns launcher resource name for launcher we need to use. + public static String getLauncherResourceName(Map p) { + if (CONSOLE_HINT.fetchFrom(p)) { + return "papplauncherc.exe"; + } + + return "papplauncher.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); + } + + protected void cleanupConfigFiles(Map params) { + getConfig_AppIcon(params).delete(); + getConfig_ExecutableProperties(params).delete(); + } + + @Override + public InputStream getResourceAsStream(String name) { + return WinResources.class.getResourceAsStream(name); + } + + @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())); + } + try { + // create the .exe launchers + createLauncherForEntryPoint(params); + + // copy the jars + copyApplication(params); + + // copy in the needed libraries + try (InputStream is_lib = getResourceAsStream("packager.dll")) { + 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); + } + + } catch (IOException ex) { + Log.info("Exception: "+ex); + Log.debug(ex); + } finally { + cleanupConfigFiles(params); + } + } + + @Override + public void prepareServerJreFiles() 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 = + WinResources.class.getResourceAsStream( + REDIST_MSVCR.replaceAll("VS_VER", VS_VER)); + final InputStream REDIST_MSVCP_URL = + WinResources.class.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.info("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(WINDOWS_BUNDLER_PREFIX + + getConfig_ExecutableProperties(params).getName(), + I18N.getString("resource.executable-properties-template"), + EXECUTABLE_PROPERTIES_TEMPLATE, data, + VERBOSE.fetchFrom(params), + DROP_IN_RESOURCES_ROOT.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( + "package/windows/" + APP_NAME.fetchFrom(params) + ".ico", + "icon", + TEMPLATE_APP_ICON, + icon, + VERBOSE.fetchFrom(params), + DROP_IN_RESOURCES_ROOT.fetchFrom(params)); + Files.copy(in, iconTarget.toPath()); + + 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\\jpackager.exe"); + + // Run tool on launcher file to change the icon and the metadata. + try { + if (WindowsDefender.isThereAPotentialWindowsDefenderIssue()) { + Log.info(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); + } + } + } + + @Override + public String getPlatformSpecificModulesFile() { + return MODULES_FILENAME; + } + +} --- /dev/null 2018-10-22 10:48:26.000000000 -0400 +++ new/src/jdk.packager/windows/classes/jdk/packager/internal/resources/builders/windows/WindowsAppImageBuilder.properties 2018-10-22 10:48:22.170441000 -0400 @@ -0,0 +1,44 @@ +# +# 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.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.config-root.name=Config Root Dir +param.config-root.description=Config Root Dir + +error.cannot-create-output-dir=Output directory {0} cannot be created. +error.cannot-write-to-output-dir=Output directory {0} is not writable. + +message.potential.windows.defender.issue=Warning: Windows Defender may prevent the Java Packager from functioning. If there is an issue, it can be addressed by either disabling realtime monitoring, or adding an exclusion for the directory "{0}". + +resource.executable-properties-template=Template for creating executable properties file. --- /dev/null 2018-10-22 10:48:36.000000000 -0400 +++ new/src/jdk.packager/windows/classes/jdk/packager/internal/resources/builders/windows/WindowsAppImageBuilder_ja.properties 2018-10-22 10:48:32.809777400 -0400 @@ -0,0 +1,44 @@ +# +# 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.rebrand-executable.name = \u30E9\u30F3\u30C1\u30E3\u306E\u30EA\u30D6\u30E9\u30F3\u30C9 +param.rebrand-executable.description = \u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u30FB\u30A2\u30A4\u30B3\u30F3\u3092\u542B\u3080\u30E9\u30F3\u30C1\u30E3\u304A\u3088\u3073\u6240\u6709\u8005\u60C5\u5831\u3092\u66F4\u65B0\u3057\u307E\u3059\u3002 + +param.icon-ico.name=.ico\u30A2\u30A4\u30B3\u30F3 +param.icon-ico.description=ICO\u5F62\u5F0F\u3067\u306E\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u306E\u30A2\u30A4\u30B3\u30F3\u3002 + +param.console-hint.name=Console Hint +param.console-hint.description=Indicates if the bundler should use console launcher + +param.config-root.name=Config Root Dir +param.config-root.description=Config Root Dir + +error.cannot-create-output-dir=\u51FA\u529B\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA{0}\u3092\u4F5C\u6210\u3067\u304D\u307E\u305B\u3093\u3002 +error.cannot-write-to-output-dir=\u51FA\u529B\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA{0}\u306F\u66F8\u8FBC\u307F\u4E0D\u53EF\u3067\u3059\u3002 + +message.potential.windows.defender.issue=\u8B66\u544A: Windows Defender\u304C\u539F\u56E0\u3067Java\u30D1\u30C3\u30B1\u30FC\u30B8\u30E3\u304C\u6A5F\u80FD\u3057\u306A\u3044\u3053\u3068\u304C\u3042\u308A\u307E\u3059\u3002\u554F\u984C\u304C\u767A\u751F\u3057\u305F\u5834\u5408\u306F\u3001\u30EA\u30A2\u30EB\u30BF\u30A4\u30E0\u30FB\u30E2\u30CB\u30BF\u30EA\u30F3\u30B0\u3092\u7121\u52B9\u306B\u3059\u308B\u304B\u3001\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA"{0}"\u306E\u9664\u5916\u3092\u8FFD\u52A0\u3059\u308B\u3053\u3068\u306B\u3088\u308A\u3001\u554F\u984C\u306B\u5BFE\u51E6\u3067\u304D\u307E\u3059\u3002 + +resource.executable-properties-template=\u5B9F\u884C\u53EF\u80FD\u306A\u30D7\u30ED\u30D1\u30C6\u30A3\u30FB\u30D5\u30A1\u30A4\u30EB\u4F5C\u6210\u7528\u306E\u30C6\u30F3\u30D7\u30EC\u30FC\u30C8\u3002 --- /dev/null 2018-10-22 10:48:44.000000000 -0400 +++ new/src/jdk.packager/windows/classes/jdk/packager/internal/resources/builders/windows/WindowsAppImageBuilder_zh_CN.properties 2018-10-22 10:48:42.091896400 -0400 @@ -0,0 +1,44 @@ +# +# 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.rebrand-executable.name = \u66F4\u6539\u542F\u52A8\u7A0B\u5E8F\u54C1\u724C +param.rebrand-executable.description = \u4F7F\u7528\u5E94\u7528\u7A0B\u5E8F\u56FE\u6807\u66F4\u65B0\u542F\u52A8\u7A0B\u5E8F\u5E76\u66F4\u65B0\u6240\u6709\u6743\u4FE1\u606F\u3002 + +param.icon-ico.name=.ico \u56FE\u6807 +param.icon-ico.description=\u5E94\u7528\u7A0B\u5E8F\u7684\u56FE\u6807, \u91C7\u7528 ICO \u683C\u5F0F\u3002 + +param.console-hint.name=Console Hint +param.console-hint.description=Indicates if the bundler should use console launcher + +param.config-root.name=Config Root Dir +param.config-root.description=Config Root Dir + +error.cannot-create-output-dir=\u65E0\u6CD5\u521B\u5EFA\u8F93\u51FA\u76EE\u5F55 {0}\u3002 +error.cannot-write-to-output-dir=\u8F93\u51FA\u76EE\u5F55 {0} \u4E0D\u53EF\u5199\u3002 + +message.potential.windows.defender.issue=\u8B66\u544A: Windows Defender \u53EF\u80FD\u4F1A\u963B\u6B62 Java \u6253\u5305\u7A0B\u5E8F\u6B63\u5E38\u5DE5\u4F5C\u3002\u5982\u679C\u5B58\u5728\u95EE\u9898, \u53EF\u4EE5\u901A\u8FC7\u7981\u7528\u5B9E\u65F6\u76D1\u89C6\u6216\u8005\u4E3A\u76EE\u5F55 "{0}" \u6DFB\u52A0\u6392\u9664\u9879\u8FDB\u884C\u89E3\u51B3\u3002 + +resource.executable-properties-template=\u7528\u4E8E\u521B\u5EFA\u53EF\u6267\u884C\u5C5E\u6027\u6587\u4EF6\u7684\u6A21\u677F\u3002 --- /dev/null 2018-10-22 10:48:51.000000000 -0400 +++ new/src/jdk.packager/windows/classes/jdk/packager/internal/resources/windows/WinAppBundler.properties 2018-10-22 10:48:48.675180800 -0400 @@ -0,0 +1,57 @@ +# +# 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. +# +# + +bundler.name=Windows Application Image +bundler.description=A Directory based image of a windows Application with an optionally co-bundled JRE. Used as a base for the Installer bundlers + +param.config-root.name=Config Root Dir +param.config-root.description=Config Root Dir + +param.raw-executable-url.name=Launcher URL +param.raw-executable-url.description=Override the packager 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. + +resource.application-icon=application icon + +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-create-output-dir=Output directory {0} cannot be created. +error.cannot-write-to-output-dir=Output directory {0} is not writable. +error.cannot-find-launcher=Cannot find cfg file in predefined app image directory {0}. + +message.creating-app-bundle=Creating app bundle\: {0} in {1} +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. --- /dev/null 2018-10-22 10:48:57.000000000 -0400 +++ new/src/jdk.packager/windows/classes/jdk/packager/internal/resources/windows/WinAppBundler_ja.properties 2018-10-22 10:48:55.102463200 -0400 @@ -0,0 +1,57 @@ +# +# 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. +# +# + +bundler.name=Windows\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u30FB\u30A4\u30E1\u30FC\u30B8 +bundler.description=\u30AA\u30D7\u30B7\u30E7\u30F3\u3067JRE\u304C\u30D0\u30F3\u30C9\u30EB\u3055\u308C\u3066\u3044\u308BWindows\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u306E\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u30FB\u30D9\u30FC\u30B9\u306E\u30A4\u30E1\u30FC\u30B8\u3002\u30A4\u30F3\u30B9\u30C8\u30FC\u30E9\u30FB\u30D0\u30F3\u30C9\u30E9\u306E\u30D9\u30FC\u30B9\u3068\u3057\u3066\u4F7F\u7528\u3055\u308C\u307E\u3059 + +param.config-root.name=Config Root Dir +param.config-root.description=Config Root Dir + +param.raw-executable-url.name=\u30E9\u30F3\u30C1\u30E3URL +param.raw-executable-url.description=\u30D1\u30C3\u30B1\u30FC\u30B8\u30E3\u306E\u30C7\u30D5\u30A9\u30EB\u30C8\u30FB\u30E9\u30F3\u30C1\u30E3\u3092\u30AB\u30B9\u30BF\u30E0\u30FB\u30E9\u30F3\u30C1\u30E3\u3067\u30AA\u30FC\u30D0\u30FC\u30E9\u30A4\u30C9\u3057\u307E\u3059\u3002 + +param.rebrand-executable.name=\u30E9\u30F3\u30C1\u30E3\u306E\u30EA\u30D6\u30E9\u30F3\u30C9 +param.rebrand-executable.description=\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u30FB\u30A2\u30A4\u30B3\u30F3\u3092\u542B\u3080\u30E9\u30F3\u30C1\u30E3\u304A\u3088\u3073\u6240\u6709\u8005\u60C5\u5831\u3092\u66F4\u65B0\u3057\u307E\u3059\u3002 + +param.icon-ico.name=.ico\u30A2\u30A4\u30B3\u30F3 +param.icon-ico.description=ICO\u5F62\u5F0F\u3067\u306E\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u306E\u30A2\u30A4\u30B3\u30F3\u3002 + +resource.application-icon=\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u30FB\u30A2\u30A4\u30B3\u30F3 + +error.parameters-null=\u30D1\u30E9\u30E1\u30FC\u30BF\u30FB\u30DE\u30C3\u30D7\u304Cnull\u3067\u3059\u3002 +error.parameters-null.advice=\u975Enull\u30D1\u30E9\u30E1\u30FC\u30BF\u30FB\u30DE\u30C3\u30D7\u3067\u6E21\u3057\u3066\u304F\u3060\u3055\u3044\u3002 +error.no-windows-resources=\u3053\u306EJDK\u306E\u30B3\u30D4\u30FC\u3067\u306F\u3001Windows\u306F\u30B5\u30DD\u30FC\u30C8\u3055\u308C\u3066\u3044\u307E\u305B\u3093\u3002 +error.no-windows-resources.advice=Oracle JDK for Windows\u3092\u4F7F\u7528\u3057\u3066\u304F\u3060\u3055\u3044\u3002 +error.bit-architecture-mismatch=FX SDK\u3068JRE\u30E9\u30F3\u30BF\u30A4\u30E0\u9593\u306E\u30D3\u30C3\u30C8\u30FB\u30A2\u30FC\u30AD\u30C6\u30AF\u30C1\u30E3\u304C\u4E00\u81F4\u3057\u307E\u305B\u3093\u3002 +error.bit-architecture-mismatch.advice=\u6B63\u3057\u3044\u30D3\u30C3\u30C8\u30FB\u30A2\u30FC\u30AD\u30C6\u30AF\u30C1\u30E3\u3092\u6301\u3064JRE\u30E9\u30F3\u30BF\u30A4\u30E0\u3092\u4F7F\u7528\u3057\u3066\u304F\u3060\u3055\u3044\u3002 +error.cannot-create-output-dir=\u51FA\u529B\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA{0}\u3092\u4F5C\u6210\u3067\u304D\u307E\u305B\u3093\u3002 +error.cannot-write-to-output-dir=\u51FA\u529B\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA{0}\u306F\u66F8\u8FBC\u307F\u4E0D\u53EF\u3067\u3059\u3002 +error.cannot-find-launcher=Cannot find cfg file in predefined app image directory {0}. + +message.creating-app-bundle=\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u30FB\u30D0\u30F3\u30C9\u30EB\u3092\u4F5C\u6210\u3057\u3066\u3044\u307E\u3059: {1}\u5185\u306E{0} +message.result-dir=\u7D50\u679C\u306E\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u30FB\u30D0\u30F3\u30C9\u30EB: {0} +message.disable-bit-architecture-check=\u30D3\u30C3\u30C8\u30FB\u30A2\u30FC\u30AD\u30C6\u30AF\u30C1\u30E3\u306E\u4E0D\u4E00\u81F4\u30C1\u30A7\u30C3\u30AF\u304C\u7121\u52B9\u306B\u306A\u3063\u3066\u3044\u307E\u3059\u3002 +message.icon-not-ico=\u6307\u5B9A\u3057\u305F\u30A2\u30A4\u30B3\u30F3"{0}"\u306FICO\u30D5\u30A1\u30A4\u30EB\u3067\u306F\u306A\u304F\u3001\u4F7F\u7528\u3055\u308C\u307E\u305B\u3093\u3002\u30C7\u30D5\u30A9\u30EB\u30C8\u30FB\u30A2\u30A4\u30B3\u30F3\u304C\u305D\u306E\u4F4D\u7F6E\u306B\u4F7F\u7528\u3055\u308C\u307E\u3059\u3002 --- /dev/null 2018-10-22 10:49:03.000000000 -0400 +++ new/src/jdk.packager/windows/classes/jdk/packager/internal/resources/windows/WinAppBundler_zh_CN.properties 2018-10-22 10:49:01.716948000 -0400 @@ -0,0 +1,57 @@ +# +# 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. +# +# + +bundler.name=Windows \u5E94\u7528\u7A0B\u5E8F\u6620\u50CF +bundler.description=\u4E00\u4E2A\u57FA\u4E8E\u76EE\u5F55\u7684 Windows \u5E94\u7528\u7A0B\u5E8F\u6620\u50CF, \u53EF\u4EE5\u9009\u62E9\u6027\u5730\u5E26\u6709\u5171\u540C\u6253\u5305\u7684 JRE\u3002\u7528\u4F5C\u5B89\u88C5\u7A0B\u5E8F\u6253\u5305\u7A0B\u5E8F\u7684\u57FA\u7840 + +param.config-root.name=Config Root Dir +param.config-root.description=Config Root Dir + +param.raw-executable-url.name=\u542F\u52A8\u7A0B\u5E8F URL +param.raw-executable-url.description=\u4F7F\u7528\u5B9A\u5236\u542F\u52A8\u7A0B\u5E8F\u8986\u76D6\u6253\u5305\u7A0B\u5E8F\u9ED8\u8BA4\u542F\u52A8\u7A0B\u5E8F\u3002 + +param.rebrand-executable.name=\u66F4\u6539\u542F\u52A8\u7A0B\u5E8F\u54C1\u724C +param.rebrand-executable.description=\u4F7F\u7528\u5E94\u7528\u7A0B\u5E8F\u56FE\u6807\u66F4\u65B0\u542F\u52A8\u7A0B\u5E8F\u5E76\u66F4\u65B0\u6240\u6709\u6743\u4FE1\u606F\u3002 + +param.icon-ico.name=.ico \u56FE\u6807 +param.icon-ico.description=\u5E94\u7528\u7A0B\u5E8F\u7684\u56FE\u6807, \u91C7\u7528 ICO \u683C\u5F0F\u3002 + +resource.application-icon=\u5E94\u7528\u7A0B\u5E8F\u56FE\u6807 + +error.parameters-null=\u53C2\u6570\u6620\u5C04\u4E3A\u7A7A\u503C\u3002 +error.parameters-null.advice=\u8BF7\u4F20\u5165\u975E\u7A7A\u53C2\u6570\u6620\u5C04\u3002 +error.no-windows-resources=\u6B64 JDK \u7684\u526F\u672C\u4E0D\u652F\u6301 Windows\u3002 +error.no-windows-resources.advice=\u8BF7\u4F7F\u7528 Oracle JDK for Windows\u3002 +error.bit-architecture-mismatch=FX SDK \u4E0E JRE \u8FD0\u884C\u65F6\u4E4B\u95F4\u7684\u4F4D\u4F53\u7CFB\u7ED3\u6784\u4E0D\u5339\u914D\u3002 +error.bit-architecture-mismatch.advice=\u8BF7\u786E\u4FDD\u4F7F\u7528\u5E26\u6709\u6B63\u786E\u4F4D\u4F53\u7CFB\u7ED3\u6784\u7684 JRE \u8FD0\u884C\u65F6\u3002 +error.cannot-create-output-dir=\u65E0\u6CD5\u521B\u5EFA\u8F93\u51FA\u76EE\u5F55 {0}\u3002 +error.cannot-write-to-output-dir=\u8F93\u51FA\u76EE\u5F55 {0} \u4E0D\u53EF\u5199\u3002 +error.cannot-find-launcher=Cannot find cfg file in predefined app image directory {0}. + +message.creating-app-bundle=\u6B63\u5728 {1} \u4E2D\u521B\u5EFA\u5E94\u7528\u7A0B\u5E8F\u5305 {0} +message.result-dir=\u751F\u6210\u7684\u5E94\u7528\u7A0B\u5E8F\u5305: {0} +message.disable-bit-architecture-check=\u5DF2\u7981\u7528\u4F4D\u4F53\u7CFB\u7ED3\u6784\u4E0D\u5339\u914D\u68C0\u67E5\u3002 +message.icon-not-ico=\u6307\u5B9A\u7684\u56FE\u6807 "{0}" \u4E0D\u662F ICO \u6587\u4EF6, \u4E0D\u4F1A\u4F7F\u7528\u3002\u5C06\u4F7F\u7528\u9ED8\u8BA4\u56FE\u6807\u4EE3\u66FF\u3002 --- /dev/null 2018-10-22 10:49:10.000000000 -0400 +++ new/src/jdk.packager/windows/classes/jdk/packager/internal/resources/windows/WinExeBundler.properties 2018-10-22 10:49:07.847826600 -0400 @@ -0,0 +1,103 @@ +# +# 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. +# +# + +bundler.name=EXE Installer +bundler.description=Microsoft Windows EXE Installer, via InnoIDE. + +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.app-bundler.name=Exe Installer Bundler +param.app-bundler.description=Exe Installer Bundler + +param.config-root.name=Config Root +param.config-root.description=Config Root + +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. + +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 + +error.parameters-null=Parameters map is null. +error.parameters-null.advice=Pass in a non-null parameters map. + +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.license-missing=Specified license file is missing. +error.license-missing.advice=Make sure that "{0}" references a file in the app resources, and that it is relative file reference. + +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.cannot-create-output-dir=Output directory {0} cannot be created. +error.cannot-write-to-output-dir=Output directory {0} is not writable. + +message.tool-wrong-version=Detected [{0}] version {1} but version {2} is required. +message.debug-working-directory=Kept working directory for debug\: {0} +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.potential.windows.defender.issue=Warning: Windows Defender may prevent the Java Packager 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-version=Detected [{0}] version [{1}] +message.running-wsh-script=Running WSH script on application image [{0}] +message.wrong-tool-version=Detected [{0}] version {1} but version {2} is required. +message.use-wix36-features=WiX 3.6 detected. Enabling advanced cleanup action. +message.version-string-too-many-components=Version sting may have up to 3 components - major.minor.build . +message.debug-working-directory=Kept working directory for debug\: {0} +message.one-shortcut-required=At least one type of shortcut is required. Enabling menu shortcut. +message.creating-association-with-null-extension=Creating association with null extension. +message.truncating.id=Truncating Application ID to 126 chars for Inno Setup. --- /dev/null 2018-10-22 10:49:16.000000000 -0400 +++ new/src/jdk.packager/windows/classes/jdk/packager/internal/resources/windows/WinExeBundler_ja.properties 2018-10-22 10:49:14.743115000 -0400 @@ -0,0 +1,90 @@ +# +# 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. +# +# + +bundler.name=EXE Installer +bundler.description=Microsoft Windows EXE Installer, via InnoIDE. + +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.app-bundler.name=Exe Installer Bundler +param.app-bundler.description=Exe Installer Bundler + +param.config-root.name=Config Root +param.config-root.description=Config Root + +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. + +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 + +error.parameters-null=Parameters map is null. +error.parameters-null.advice=Pass in a non-null parameters map. + +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.license-missing=Specified license file is missing. +error.license-missing.advice=Make sure that "{0}" references a file in the app resources, and that it is relative file reference. + +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.cannot-create-output-dir=Output directory {0} cannot be created. +error.cannot-write-to-output-dir=Output directory {0} is not writable. + +message.tool-version=Detected [{0}] version [{1}] +message.running-wsh-script=Running WSH script on application image [{0}] +message.wrong-tool-version=Detected [{0}] version {1} but version {2} is required. +message.use-wix36-features=WiX 3.6 detected. Enabling advanced cleanup action. +message.version-string-too-many-components=Version sting may have up to 3 components - major.minor.build . +message.debug-working-directory=Kept working directory for debug\: {0} +message.one-shortcut-required=At least one type of shortcut is required. Enabling menu shortcut. +message.creating-association-with-null-extension=Creating association with null extension. +message.truncating.id=Truncating Application ID to 126 chars for Inno Setup. --- /dev/null 2018-10-22 10:49:23.000000000 -0400 +++ new/src/jdk.packager/windows/classes/jdk/packager/internal/resources/windows/WinExeBundler_zh_CN.properties 2018-10-22 10:49:21.341999600 -0400 @@ -0,0 +1,91 @@ +# +# 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. +# +# + +bundler.name=EXE Installer +bundler.description=Microsoft Windows EXE Installer, via InnoIDE. + +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.app-bundler.name=Exe Installer Bundler +param.app-bundler.description=Exe Installer Bundler + +param.config-root.name=Config Root +param.config-root.description=Config Root + +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. + +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 + +error.parameters-null=Parameters map is null. +error.parameters-null.advice=Pass in a non-null parameters map. + +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.license-missing=Specified license file is missing. +error.license-missing.advice=Make sure that "{0}" references a file in the app resources, and that it is relative file reference. + +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.cannot-create-output-dir=Output directory {0} cannot be created. +error.cannot-write-to-output-dir=Output directory {0} is not writable. + +message.tool-wrong-version=Detected [{0}] version {1} but version {2} is required. +message.debug-working-directory=Kept working directory for debug\: {0} +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.potential.windows.defender.issue=Warning: Windows Defender may prevent the Java Packager from functioning. If there is an issue, it can be addressed by either disabling realtime monitoring, or adding an exclusion for the directory "{0}". --- /dev/null 2018-10-22 10:49:30.000000000 -0400 +++ new/src/jdk.packager/windows/classes/jdk/packager/internal/resources/windows/WinLauncher.properties 2018-10-22 10:49:28.196488000 -0400 @@ -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 --- /dev/null 2018-10-22 10:49:37.000000000 -0400 +++ new/src/jdk.packager/windows/classes/jdk/packager/internal/resources/windows/WinMsiBundler.properties 2018-10-22 10:49:35.481781400 -0400 @@ -0,0 +1,100 @@ +# +# 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. +# +# + +bundler.name=MSI Installer +bundler.description=Microsoft Windows MSI Installer, via WiX. + +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.app-bundler.name=MSI App Bundler +param.app-bundler.description=MSI App Bundler + +param.can-use-wix36.name=Can Use Wix +param.can-use-wix36.description=Can Use Wix + +param.config-root.name=Config Root Dir +param.config-root.description=Config Root Dir + +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.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.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-wix-tools=Can not find WiX tools (light.exe, candle.exe). +error.no-wix-tools.advice=Download WiX 3.0 or later from http\://wix.sf.net and add it to the PATH. +error.version-string-wrong-format=Version string is not compatible with MSI rules [{0}]. +error.version-string-wrong-format.advice=Set the bundler argument "{0}" according to these rules: http\://msdn.microsoft.com/en-us/library/aa370859%28v\=VS.85%29.aspx +error.version-string-major-out-of-range=Major version must be in the range [0, 255] +error.version-string-build-out-of-range=Build part of version must be in the range [0, 65535] +error.version-string-minor-out-of-range=Minor version must be in the range [0, 255] +error.version-string-part-not-number=Failed to convert version component to int. +error.cannot-walk-directory=Can not walk [{0}] - it is not a valid directory +error.cannot-create-output-dir=Output directory {0} cannot be created. +error.cannot-write-to-output-dir=Output directory {0} is not writable. +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.license-missing=can not find license file {0}. +error.license-missing.advice=include license file {0} in the --files argument. + + +message.tool-version=Detected [{0}] version [{1}] +message.running-wsh-script=Running WSH script on application image [{0}] +message.wrong-tool-version=Detected [{0}] version {1} but version {2} is required. +message.use-wix36-features=WiX 3.6 detected. Enabling advanced cleanup action. +message.version-string-too-many-components=Version sting may have up to 3 components - major.minor.build . +message.debug-working-directory=Kept working directory for debug\: {0} +message.generated-product-guid=Generated product GUID\: {0} +message.preparing-msi-config=Preparing MSI config\: {0} +message.generating-msi=Generating MSI\: {0} +message.one-shortcut-required=At least one type of shortcut is required. Enabling menu shortcut. +message.light-file-string=WiX light tool set to {0} +message.candle-file-string=WiX candle tool set to {0} +message.creating-association-with-null-extension=Creating association with null extension. --- /dev/null 2018-10-22 10:49:44.000000000 -0400 +++ new/src/jdk.packager/windows/classes/jdk/packager/internal/resources/windows/WinMsiBundler_ja.properties 2018-10-22 10:49:42.548672000 -0400 @@ -0,0 +1,91 @@ +# +# 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. +# +# + +bundler.name=MSI\u30A4\u30F3\u30B9\u30C8\u30FC\u30E9 +bundler.description=WiX\u3092\u4F7F\u7528\u3057\u305FMicrosoft Windows MSI\u30A4\u30F3\u30B9\u30C8\u30FC\u30E9\u3002 + +param.system-wide.name=\u30B7\u30B9\u30C6\u30E0\u5168\u4F53 +param.system-wide.description=\u3053\u306E\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u306F\u3001\u305D\u308C\u81EA\u4F53\u3092\u30B7\u30B9\u30C6\u30E0\u5168\u4F53\u306B\u30A4\u30F3\u30B9\u30C8\u30FC\u30EB\u3057\u307E\u3059\u304B\u3001\u307E\u305F\u306F\u5404\u30E6\u30FC\u30B6\u30FC\u306B\u5BFE\u3057\u3066\u306E\u307F\u30A4\u30F3\u30B9\u30C8\u30FC\u30EB\u3057\u307E\u3059\u304B\u3002Null\u306F\u30B7\u30B9\u30C6\u30E0\u30FB\u30C7\u30D5\u30A9\u30EB\u30C8\u3092\u4F7F\u7528\u3059\u308B\u3053\u3068\u3092\u610F\u5473\u3057\u307E\u3059\u3002 + +param.app-bundler.name=MSI App Bundler +param.app-bundler.description=MSI App Bundler + +param.can-use-wix36.name=Can Use Wix +param.can-use-wix36.description=Can Use Wix + +param.config-root.name=Config Root Dir +param.config-root.description=Config Root Dir + +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.upgrade-uuid.name=\u30A2\u30C3\u30D7\u30B0\u30EC\u30FC\u30C9UUID +param.upgrade-uuid.description=\u3053\u306E\u30D1\u30C3\u30B1\u30FC\u30B8\u306E\u30A2\u30C3\u30D7\u30B0\u30EC\u30FC\u30C9\u306B\u95A2\u9023\u4ED8\u3051\u3089\u308C\u3066\u3044\u308BUUID\u3002 + +param.product-version.name=\u88FD\u54C1\u30D0\u30FC\u30B8\u30E7\u30F3 +param.product-version.description=Windows\u304A\u3088\u3073MSI\u306B\u8868\u793A\u3055\u308C\u308B\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u306E\u30D0\u30FC\u30B8\u30E7\u30F3(\u5F62\u5F0F\u306F"1.2.3") + +param.candle-path.name=WiX candle.exe\u306E\u5834\u6240 +param.candle-path.description=WiX\u30C4\u30FC\u30EB\u30BB\u30C3\u30C8\u304B\u3089\u306Ecandle.exe\u3078\u306E\u30D5\u30A1\u30A4\u30EB\u30FB\u30D1\u30B9\u3002 + +param.light-path.name=WiX light.exe\u306E\u5834\u6240 +param.light-path.description=WiX\u30C4\u30FC\u30EB\u30BB\u30C3\u30C8\u304B\u3089\u306Elight.exe\u3078\u306E\u30D5\u30A1\u30A4\u30EB\u30FB\u30D1\u30B9\u3002 + +resource.post-install-script=\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u30FB\u30A4\u30E1\u30FC\u30B8\u3092\u79FB\u5165\u3057\u305F\u5F8C\u306B\u5B9F\u884C\u3059\u308B\u30B9\u30AF\u30EA\u30D7\u30C8 +resource.wix-config-file=WiX\u69CB\u6210\u30D5\u30A1\u30A4\u30EB + +error.parameters-null=\u30D1\u30E9\u30E1\u30FC\u30BF\u30FB\u30DE\u30C3\u30D7\u304Cnull\u3067\u3059\u3002 +error.parameters-null.advice=\u975Enull\u30D1\u30E9\u30E1\u30FC\u30BF\u30FB\u30DE\u30C3\u30D7\u3067\u6E21\u3057\u3066\u304F\u3060\u3055\u3044\u3002 +error.no-wix-tools=WiX\u30C4\u30FC\u30EB(light.exe\u3001candle.exe)\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093\u3002 +error.no-wix-tools.advice=WiX 3.0\u4EE5\u964D\u3092http://wix.sf.net\u304B\u3089\u30C0\u30A6\u30F3\u30ED\u30FC\u30C9\u3057\u3001PATH\u306B\u8FFD\u52A0\u3057\u307E\u3059\u3002 +error.version-string-wrong-format=\u30D0\u30FC\u30B8\u30E7\u30F3\u6587\u5B57\u5217\u306FMSI\u898F\u5247[{0}]\u3068\u4E92\u63DB\u6027\u304C\u3042\u308A\u307E\u305B\u3093\u3002 +error.version-string-wrong-format.advice=\u30D0\u30F3\u30C9\u30E9\u5F15\u6570"{0}"\u3092\u6B21\u306E\u898F\u5247\u306B\u5F93\u3063\u3066\u8A2D\u5B9A\u3057\u307E\u3059: http://msdn.microsoft.com/en-us/library/aa370859%28v=VS.85%29.aspx +error.version-string-major-out-of-range=\u30E1\u30B8\u30E3\u30FC\u30FB\u30D0\u30FC\u30B8\u30E7\u30F3\u306F\u7BC4\u56F2[0, 255]\u5185\u3067\u3042\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059 +error.version-string-build-out-of-range=\u30D0\u30FC\u30B8\u30E7\u30F3\u306E\u30D3\u30EB\u30C9\u90E8\u5206\u306F\u7BC4\u56F2[0, 65535]\u5185\u3067\u3042\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059 +error.version-string-minor-out-of-range=\u30DE\u30A4\u30CA\u30FC\u30FB\u30D0\u30FC\u30B8\u30E7\u30F3\u306F\u7BC4\u56F2[0, 255]\u5185\u3067\u3042\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059 +error.version-string-part-not-number=\u30D0\u30FC\u30B8\u30E7\u30F3\u30FB\u30B3\u30F3\u30DD\u30FC\u30CD\u30F3\u30C8\u306Eint\u3078\u306E\u5909\u63DB\u306B\u5931\u6557\u3057\u307E\u3057\u305F\u3002 +error.cannot-walk-directory=[{0}]\u3067\u79FB\u52D5\u3067\u304D\u307E\u305B\u3093 - \u6709\u52B9\u306A\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u3067\u306F\u3042\u308A\u307E\u305B\u3093 +error.cannot-create-output-dir=\u51FA\u529B\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA{0}\u3092\u4F5C\u6210\u3067\u304D\u307E\u305B\u3093\u3002 +error.cannot-write-to-output-dir=\u51FA\u529B\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA{0}\u306F\u66F8\u8FBC\u307F\u4E0D\u53EF\u3067\u3059\u3002 +error.too-many-content-types-for-file-association=\u30D5\u30A1\u30A4\u30EB\u30FB\u30A2\u30BD\u30B7\u30A8\u30FC\u30B7\u30E7\u30F3\u756A\u53F7{0}\u306B\u8907\u6570\u306EMIME\u30BF\u30A4\u30D7\u304C\u6307\u5B9A\u3055\u308C\u307E\u3057\u305F\u3002 +error.too-many-content-types-for-file-association.advice=Linux\u30D0\u30F3\u30C9\u30EB\u306E\u5834\u5408\u3001\u5404\u30D5\u30A1\u30A4\u30EB\u30FB\u30A2\u30BD\u30B7\u30A8\u30FC\u30B7\u30E7\u30F3\u306BMIME\u30BF\u30A4\u30D7\u30921\u3064\u3060\u3051\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044\u3002 + +message.tool-version=[{0}]\u30D0\u30FC\u30B8\u30E7\u30F3[{1}]\u304C\u691C\u51FA\u3055\u308C\u307E\u3057\u305F +message.running-wsh-script=\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u30FB\u30A4\u30E1\u30FC\u30B8[{0}]\u3067WSH\u30B9\u30AF\u30EA\u30D7\u30C8\u3092\u5B9F\u884C\u3057\u3066\u3044\u307E\u3059 +message.wrong-tool-version=[{0}]\u30D0\u30FC\u30B8\u30E7\u30F3{1}\u304C\u691C\u51FA\u3055\u308C\u307E\u3057\u305F\u304C\u3001\u30D0\u30FC\u30B8\u30E7\u30F3{2}\u304C\u5FC5\u8981\u3067\u3059\u3002 +message.use-wix36-features=WiX 3.6\u304C\u691C\u51FA\u3055\u308C\u307E\u3057\u305F\u3002\u62E1\u5F35\u30AF\u30EA\u30FC\u30F3\u30A2\u30C3\u30D7\u30FB\u30A2\u30AF\u30B7\u30E7\u30F3\u3092\u6709\u52B9\u5316\u3057\u3066\u3044\u307E\u3059\u3002 +message.version-string-too-many-components=\u30D0\u30FC\u30B8\u30E7\u30F3\u6587\u5B57\u5217\u306B\u306F\u3001\u30B3\u30F3\u30DD\u30FC\u30CD\u30F3\u30C8\u30923\u3064(\u30E1\u30B8\u30E3\u30FC.\u30DE\u30A4\u30CA\u30FC.\u30D3\u30EB\u30C9)\u307E\u3067\u542B\u3081\u308B\u3053\u3068\u304C\u3067\u304D\u307E\u3059\u3002 +message.debug-working-directory=\u30C7\u30D0\u30C3\u30B0\u306E\u4F5C\u696D\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u304C\u4FDD\u6301\u3055\u308C\u307E\u3057\u305F: {0} +message.generated-product-guid=\u88FD\u54C1GUID\u3092\u751F\u6210\u3057\u307E\u3057\u305F: {0} +message.preparing-msi-config=MSI\u69CB\u6210\u3092\u6E96\u5099\u3057\u3066\u3044\u307E\u3059: {0} +message.generating-msi=MSI\u3092\u751F\u6210\u3057\u3066\u3044\u307E\u3059: {0} +message.one-shortcut-required=\u5C11\u306A\u304F\u3068\u30821\u3064\u306E\u30B7\u30E7\u30FC\u30C8\u30AB\u30C3\u30C8\u30FB\u30BF\u30A4\u30D7\u304C\u5FC5\u8981\u3067\u3059\u3002\u30E1\u30CB\u30E5\u30FC\u30FB\u30B7\u30E7\u30FC\u30C8\u30AB\u30C3\u30C8\u3092\u6709\u52B9\u5316\u3057\u3066\u3044\u307E\u3059\u3002 +message.light-file-string=WiX light\u30C4\u30FC\u30EB\u304C{0}\u306B\u8A2D\u5B9A\u3055\u308C\u307E\u3057\u305F +message.candle-file-string=WiX candle\u30C4\u30FC\u30EB\u304C{0}\u306B\u8A2D\u5B9A\u3055\u308C\u307E\u3057\u305F +message.creating-association-with-null-extension=null\u62E1\u5F35\u5B50\u3068\u306E\u95A2\u9023\u4ED8\u3051\u3092\u4F5C\u6210\u3057\u3066\u3044\u307E\u3059\u3002 --- /dev/null 2018-10-22 10:49:50.000000000 -0400 +++ new/src/jdk.packager/windows/classes/jdk/packager/internal/resources/windows/WinMsiBundler_zh_CN.properties 2018-10-22 10:49:48.804352200 -0400 @@ -0,0 +1,91 @@ +# +# 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. +# +# + +bundler.name=MSI \u5B89\u88C5\u7A0B\u5E8F +bundler.description=Microsoft Windows MSI \u5B89\u88C5\u7A0B\u5E8F, \u901A\u8FC7 WiX\u3002 + +param.system-wide.name=\u7CFB\u7EDF\u8303\u56F4 +param.system-wide.description=\u6B64\u5E94\u7528\u7A0B\u5E8F\u662F\u5E94\u5C1D\u8BD5\u5728\u7CFB\u7EDF\u8303\u56F4\u5185\u5B89\u88C5, \u8FD8\u662F\u4EC5\u4E3A\u6BCF\u4E2A\u7528\u6237\u5B89\u88C5? \u7A7A\u503C\u8868\u793A\u4F7F\u7528\u7CFB\u7EDF\u9ED8\u8BA4\u503C\u3002 + +param.app-bundler.name=MSI App Bundler +param.app-bundler.description=MSI App Bundler + +param.can-use-wix36.name=Can Use Wix +param.can-use-wix36.description=Can Use Wix + +param.config-root.name=Config Root Dir +param.config-root.description=Config Root Dir + +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.upgrade-uuid.name=\u5347\u7EA7 UUID +param.upgrade-uuid.description=\u4E0E\u6B64\u7A0B\u5E8F\u5305\u7684\u5347\u7EA7\u5173\u8054\u7684 UUID\u3002 + +param.product-version.name=\u4EA7\u54C1\u7248\u672C +param.product-version.description=\u5411 Windows \u548C MSI \u663E\u793A\u7684\u5E94\u7528\u7A0B\u5E8F\u7684\u7248\u672C, \u5F62\u5F0F\u4E3A "1.2.3" + +param.candle-path.name=WiX candle.exe \u4F4D\u7F6E +param.candle-path.description=WiX \u5DE5\u5177\u96C6\u4E2D candle.exe \u7684\u6587\u4EF6\u8DEF\u5F84\u3002 + +param.light-path.name=WiX light.exe \u4F4D\u7F6E +param.light-path.description=WiX \u5DE5\u5177\u96C6\u4E2D light.exe \u7684\u6587\u4EF6\u8DEF\u5F84\u3002 + +resource.post-install-script=\u8981\u5728\u586B\u5145\u5E94\u7528\u7A0B\u5E8F\u6620\u50CF\u4E4B\u540E\u8FD0\u884C\u7684\u811A\u672C +resource.wix-config-file=WiX \u914D\u7F6E\u6587\u4EF6 + +error.parameters-null=\u53C2\u6570\u6620\u5C04\u4E3A\u7A7A\u503C\u3002 +error.parameters-null.advice=\u8BF7\u4F20\u5165\u975E\u7A7A\u53C2\u6570\u6620\u5C04\u3002 +error.no-wix-tools=\u627E\u4E0D\u5230 WiX \u5DE5\u5177 (light.exe, candle.exe)\u3002 +error.no-wix-tools.advice=\u4ECE http://wix.sf.net \u4E0B\u8F7D WiX 3.0 \u6216\u66F4\u9AD8\u7248\u672C, \u7136\u540E\u5C06\u5176\u6DFB\u52A0\u5230 PATH\u3002 +error.version-string-wrong-format=\u7248\u672C\u5B57\u7B26\u4E32\u4E0D\u7B26\u5408 MSI \u89C4\u5219 [{0}]\u3002 +error.version-string-wrong-format.advice=\u6839\u636E\u4EE5\u4E0B\u89C4\u5219\u8BBE\u7F6E\u6253\u5305\u7A0B\u5E8F\u53C2\u6570 "{0}": http://msdn.microsoft.com/en-us/library/aa370859%28v=VS.85%29.aspx +error.version-string-major-out-of-range=\u4E3B\u7248\u672C\u5FC5\u987B\u4F4D\u4E8E [0, 255] \u8303\u56F4\u4E2D +error.version-string-build-out-of-range=\u7248\u672C\u7684\u5DE5\u4F5C\u7248\u672C\u90E8\u5206\u5FC5\u987B\u4F4D\u4E8E [0, 65535] \u8303\u56F4\u4E2D +error.version-string-minor-out-of-range=\u6B21\u7248\u672C\u5FC5\u987B\u4F4D\u4E8E [0, 255] \u8303\u56F4\u4E2D +error.version-string-part-not-number=\u65E0\u6CD5\u5C06\u7248\u672C\u7EC4\u6210\u90E8\u5206\u8F6C\u6362\u4E3A\u6574\u6570\u3002 +error.cannot-walk-directory=\u65E0\u6CD5\u904D\u5386 [{0}] - \u5B83\u4E0D\u662F\u6709\u6548\u7684\u76EE\u5F55 +error.cannot-create-output-dir=\u65E0\u6CD5\u521B\u5EFA\u8F93\u51FA\u76EE\u5F55 {0}\u3002 +error.cannot-write-to-output-dir=\u8F93\u51FA\u76EE\u5F55 {0} \u4E0D\u53EF\u5199\u3002 +error.too-many-content-types-for-file-association=\u4E3A\u6587\u4EF6\u5173\u8054\u53F7{0}\u6307\u5B9A\u4E86\u591A\u4E2A MIME \u7C7B\u578B\u3002 +error.too-many-content-types-for-file-association.advice=\u5BF9\u4E8E Linux \u6253\u5305, \u8BF7\u4E3A\u6BCF\u4E2A\u6587\u4EF6\u5173\u8054\u6307\u5B9A\u4E00\u4E2A\u4E14\u4EC5\u6307\u5B9A\u4E00\u4E2A MIME \u7C7B\u578B\u3002 + +message.tool-version=\u68C0\u6D4B\u5230 [{0}] \u7248\u672C [{1}] +message.running-wsh-script=\u5728\u5E94\u7528\u7A0B\u5E8F\u6620\u50CF [{0}] \u4E0A\u8FD0\u884C WSH \u811A\u672C +message.wrong-tool-version=\u68C0\u6D4B\u5230 [{0}] \u7248\u672C {1}, \u4F46\u9700\u8981\u7248\u672C {2}\u3002 +message.use-wix36-features=\u68C0\u6D4B\u5230 WiX 3.6\u3002\u6B63\u5728\u542F\u7528\u9AD8\u7EA7\u6E05\u9664\u64CD\u4F5C\u3002 +message.version-string-too-many-components=\u7248\u672C\u5B57\u7B26\u4E32\u6700\u591A\u53EF\u4EE5\u5177\u6709 3 \u4E2A\u7EC4\u6210\u90E8\u5206 - major.minor.build\u3002 +message.debug-working-directory=\u7528\u4E8E\u8C03\u8BD5\u7684\u5DF2\u4FDD\u7559\u5DE5\u4F5C\u76EE\u5F55: {0} +message.generated-product-guid=\u5DF2\u751F\u6210\u4EA7\u54C1 GUID: {0} +message.preparing-msi-config=\u6B63\u5728\u51C6\u5907 MSI \u914D\u7F6E: {0} +message.generating-msi=\u6B63\u5728\u751F\u6210 MSI: {0} +message.one-shortcut-required=\u81F3\u5C11\u9700\u8981\u4E00\u79CD\u7C7B\u578B\u7684\u5FEB\u6377\u65B9\u5F0F\u3002\u6B63\u5728\u542F\u7528\u83DC\u5355\u5FEB\u6377\u65B9\u5F0F\u3002 +message.light-file-string=WiX light \u5DE5\u5177\u8BBE\u7F6E\u4E3A{0} +message.candle-file-string=WiX candle \u5DE5\u5177\u8BBE\u7F6E\u4E3A{0} +message.creating-association-with-null-extension=\u6B63\u5728\u4F7F\u7528\u7A7A\u6269\u5C55\u540D\u521B\u5EFA\u5173\u8054\u3002 --- /dev/null 2018-10-22 10:49:57.000000000 -0400 +++ new/src/jdk.packager/windows/classes/jdk/packager/internal/resources/windows/WinResources.java 2018-10-22 10:49:55.060032400 -0400 @@ -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.packager.internal.resources.windows; + +public class WinResources { + +} --- /dev/null 2018-10-22 10:50:21.000000000 -0400 +++ new/src/jdk.packager/windows/classes/jdk/packager/internal/resources/windows/WindowsBundlerParam.properties 2018-10-22 10:50:14.685084000 -0400 @@ -0,0 +1,44 @@ +# +# 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.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. --- /dev/null 2018-10-22 10:50:28.000000000 -0400 +++ new/src/jdk.packager/windows/classes/jdk/packager/internal/resources/windows/WindowsBundlerParam_ja.properties 2018-10-22 10:50:26.120030600 -0400 @@ -0,0 +1,44 @@ +# +# 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.menu-group.name=\u30E1\u30CB\u30E5\u30FC\u30FB\u30B0\u30EB\u30FC\u30D7 +param.menu-group.description=\u3053\u306E\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u3092\u914D\u7F6E\u3059\u308B\u5FC5\u8981\u304C\u3042\u308B\u8D77\u52D5\u30E1\u30CB\u30E5\u30FC\u30FB\u30B0\u30EB\u30FC\u30D7 +param.menu-group.default=\u4E0D\u660E + +param.64-bit.name=64\u30D3\u30C3\u30C8 +param.64-bit.description=64\u30D3\u30C3\u30C8\u306EWindows\u7528\u306E\u30D0\u30F3\u30C9\u30EB\u3092\u6E96\u5099\u3057\u307E\u3059\u3002 + +param.runtime-64-bit.name=\u30E9\u30F3\u30BF\u30A4\u30E064\u30D3\u30C3\u30C8 +param.runtime-64-bit.description=\u57CB\u8FBC\u307FJRE\u30E9\u30F3\u30BF\u30A4\u30E0\u306F64\u30D3\u30C3\u30C8\u3067\u3001\u30D3\u30C3\u30C8\u30FB\u30A2\u30FC\u30AD\u30C6\u30AF\u30C1\u30E3\u306E\u4E0D\u4E00\u81F4\u306E\u691C\u51FA\u306B\u4F7F\u7528\u3055\u308C\u307E\u3059\u3002 + +param.installer-name.name=\u30A4\u30F3\u30B9\u30C8\u30FC\u30E9\u540D +param.installer-name.description=\u30D5\u30A1\u30A4\u30EB\u30FB\u30BF\u30A4\u30D7\u62E1\u5F35\u5B50\u306A\u3057\u306E\u751F\u6210\u3055\u308C\u305F\u30A4\u30F3\u30B9\u30C8\uFF0D\u30E9\u306E\u30D5\u30A1\u30A4\u30EB\u540D\u3002\u30C7\u30D5\u30A9\u30EB\u30C8\u306F-\u3067\u3059\u3002 + +param.registry-name.name=\u30EC\u30B8\u30B9\u30C8\u30EA\u540D +param.registry-name.description=\u30EC\u30B8\u30B9\u30C8\u30EA\u53C2\u7167\u7528\u306E\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u306E\u540D\u524D\u3002\u30C7\u30D5\u30A9\u30EB\u30C8\u306F\u3001\u82F1\u6570\u5B57\u3001\u30C9\u30C3\u30C8\u304A\u3088\u3073\u30C0\u30C3\u30B7\u30E5(\u7A7A\u767D\u306A\u3057)\u306E\u307F\u3092\u4F7F\u7528\u3057\u305F\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u540D\u3067\u3059\u3002 + +param.installdir-chooser.name=\u30A4\u30F3\u30B9\u30C8\u30FC\u30EB\u30FB\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u9078\u629E +param.installdir-chooser.description=\u30E6\u30FC\u30B6\u30FC\u304C\u88FD\u54C1\u3092\u30A4\u30F3\u30B9\u30C8\u30FC\u30EB\u3059\u308B\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u3092\u9078\u629E\u3059\u308B\u305F\u3081\u306E\u30C0\u30A4\u30A2\u30ED\u30B0\u3092\u8FFD\u52A0\u3057\u307E\u3059\u3002 --- /dev/null 2018-10-22 10:50:35.000000000 -0400 +++ new/src/jdk.packager/windows/classes/jdk/packager/internal/resources/windows/WindowsBundlerParam_zh_CN.properties 2018-10-22 10:50:32.999718800 -0400 @@ -0,0 +1,44 @@ +# +# 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.menu-group.name=\u83DC\u5355\u7EC4 +param.menu-group.description=\u5E94\u5C06\u6B64\u5E94\u7528\u7A0B\u5E8F\u653E\u7F6E\u5230\u7684\u5F00\u59CB\u83DC\u5355\u7EC4 +param.menu-group.default=\u672A\u77E5 + +param.64-bit.name=64 \u4F4D +param.64-bit.description=\u51C6\u5907\u9002\u7528\u4E8E 64 \u4F4D Windows \u7684\u5305\u3002 + +param.runtime-64-bit.name=\u8FD0\u884C\u65F6 64 \u4F4D +param.runtime-64-bit.description=\u5D4C\u5165\u5F0F JRE \u8FD0\u884C\u65F6\u4E3A 64 \u4F4D, \u7528\u4E8E\u68C0\u6D4B\u4F4D\u4F53\u7CFB\u7ED3\u6784\u4E0D\u5339\u914D\u60C5\u51B5\u3002 + +param.installer-name.name=\u5B89\u88C5\u7A0B\u5E8F\u540D\u79F0 +param.installer-name.description=\u6240\u751F\u6210\u5B89\u88C5\u7A0B\u5E8F\u4E0D\u5E26\u6587\u4EF6\u7C7B\u578B\u6269\u5C55\u540D\u7684\u6587\u4EF6\u540D\u3002\u9ED8\u8BA4\u503C\u4E3A <\u5E94\u7528\u7A0B\u5E8F\u540D\u79F0>-<\u7248\u672C>\u3002 + +param.registry-name.name=\u6CE8\u518C\u8868\u540D\u79F0 +param.registry-name.description=\u7528\u4E8E\u6CE8\u518C\u8868\u5F15\u7528\u7684\u5E94\u7528\u7A0B\u5E8F\u7684\u540D\u79F0\u3002\u9ED8\u8BA4\u503C\u4E3A\u53EA\u5305\u542B\u5B57\u6BCD\u6570\u5B57, \u70B9\u548C\u77ED\u5212\u7EBF (\u65E0\u7A7A\u683C) \u7684\u5E94\u7528\u7A0B\u5E8F\u540D\u79F0\u3002 + +param.installdir-chooser.name=\u5B89\u88C5\u76EE\u5F55\u9009\u62E9\u5668 +param.installdir-chooser.description=\u6DFB\u52A0\u5BF9\u8BDD\u6846\u4EE5\u5141\u8BB8\u7528\u6237\u9009\u62E9\u5C06\u5728\u5176\u4E2D\u5B89\u88C5\u4EA7\u54C1\u7684\u76EE\u5F55\u3002 Binary files /dev/null and new/src/jdk.packager/windows/classes/jdk/packager/internal/resources/windows/icon_inno_setup.bmp differ Binary files /dev/null and new/src/jdk.packager/windows/classes/jdk/packager/internal/resources/windows/javalogo_white_16.ico differ Binary files /dev/null and new/src/jdk.packager/windows/classes/jdk/packager/internal/resources/windows/javalogo_white_32.ico differ Binary files /dev/null and new/src/jdk.packager/windows/classes/jdk/packager/internal/resources/windows/javalogo_white_48.ico differ --- /dev/null 2018-10-22 10:51:07.000000000 -0400 +++ new/src/jdk.packager/windows/classes/jdk/packager/internal/resources/windows/template.iss 2018-10-22 10:51:05.178332300 -0400 @@ -0,0 +1,72 @@ +;This file will be executed next to the application bundle image +;I.e. current directory will contain folder APPLICATION_NAME with application files +[Setup] +AppId={{PRODUCT_APP_IDENTIFIER}} +AppName=APPLICATION_NAME +AppVersion=APPLICATION_VERSION +AppVerName=APPLICATION_NAME APPLICATION_VERSION +AppPublisher=APPLICATION_VENDOR +AppComments=APPLICATION_COMMENTS +AppCopyright=APPLICATION_COPYRIGHT +DefaultDirName=APPLICATION_INSTALL_ROOT\APPLICATION_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=APPLICATION_NAME\APPLICATION_NAME.ico +UninstallDisplayIcon={app}\APPLICATION_NAME.ico +UninstallDisplayName=APPLICATION_NAME +WizardImageStretch=No +WizardSmallImageFile=APPLICATION_NAME-setup-icon.bmp +ArchitecturesInstallIn64BitMode=ARCHITECTURE_BIT_MODE +FILE_ASSOCIATIONS + +[Languages] +Name: "english"; MessagesFile: "compiler:Default.isl" + +[Files] +Source: "APPLICATION_NAME\APPLICATION_NAME.exe"; DestDir: "{app}"; Flags: ignoreversion +Source: "APPLICATION_NAME\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs + +[Icons] +Name: "{group}\APPLICATION_NAME"; Filename: "{app}\APPLICATION_NAME.exe"; IconFilename: "{app}\APPLICATION_NAME.ico"; Check: APPLICATION_MENU_SHORTCUT() +Name: "{commondesktop}\APPLICATION_NAME"; Filename: "{app}\APPLICATION_NAME.exe"; IconFilename: "{app}\APPLICATION_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,APPLICATION_NAME}"; Flags: nowait postinstall skipifsilent; Check: APPLICATION_NOT_SERVICE() +Filename: "{app}\RUN_FILENAME.exe"; Parameters: "-install -svcName ""APPLICATION_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 APPLICATION_NAME STOP_ON_UNINSTALL"; Check: APPLICATION_SERVICE() + +[Code] +function returnTrue(): Boolean; +begin + Result := True; +end; + +function returnFalse(): Boolean; +begin + Result := False; +end; + +function InitializeSetup(): Boolean; +begin +// Possible future improvements: +// if version less or same => just launch app +// if upgrade => check if same app is running and wait for it to exit + Result := True; +end; --- /dev/null 2018-10-22 10:51:13.000000000 -0400 +++ new/src/jdk.packager/windows/classes/jdk/packager/internal/resources/windows/template.jre.iss 2018-10-22 10:51:11.715816200 -0400 @@ -0,0 +1,55 @@ +;This file will be executed next to the application bundle image +;I.e. current directory will contain folder APPLICATION_NAME with application files +[Setup] +AppId={{PRODUCT_APP_IDENTIFIER}} +AppName=APPLICATION_NAME +AppVersion=APPLICATION_VERSION +AppVerName=APPLICATION_NAME APPLICATION_VERSION +AppPublisher=APPLICATION_VENDOR +AppComments=APPLICATION_COMMENTS +AppCopyright=APPLICATION_COPYRIGHT +DefaultDirName=APPLICATION_INSTALL_ROOT\APPLICATION_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=APPLICATION_NAME +WizardImageStretch=No +WizardSmallImageFile=APPLICATION_NAME-setup-icon.bmp +ArchitecturesInstallIn64BitMode=ARCHITECTURE_BIT_MODE +FILE_ASSOCIATIONS + +[Languages] +Name: "english"; MessagesFile: "compiler:Default.isl" + +[Files] +Source: "APPLICATION_NAME\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs + +[Code] +function returnTrue(): Boolean; +begin + Result := True; +end; + +function returnFalse(): Boolean; +begin + Result := False; +end; + +function InitializeSetup(): Boolean; +begin + Result := True; +end; --- /dev/null 2018-10-22 10:51:21.000000000 -0400 +++ new/src/jdk.packager/windows/classes/jdk/packager/internal/resources/windows/template.jre.wxs 2018-10-22 10:51:18.801307300 -0400 @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + WIX36_ONLY_START + + WIX36_ONLY_END + + + +UI_BLOCK + + --- /dev/null 2018-10-22 10:51:28.000000000 -0400 +++ new/src/jdk.packager/windows/classes/jdk/packager/internal/resources/windows/template.wxs 2018-10-22 10:51:26.089601000 -0400 @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + WIX36_ONLY_START + + WIX36_ONLY_END + + + +UI_BLOCK + + +SECONDARY_LAUNCHER_ICONS + + --- /dev/null 2018-10-22 10:51:34.000000000 -0400 +++ new/src/jdk.packager/windows/classes/jdk/packager/internal/resources/windows/windows.jre.list 2018-10-22 10:51:32.251680000 -0400 @@ -0,0 +1,3 @@ +; This file contains Windows-specific modules. + +jdk.crypto.mscapi --- /dev/null 2018-10-22 10:51:42.000000000 -0400 +++ new/src/jdk.packager/windows/classes/jdk/packager/internal/windows/WinAppBundler.java 2018-10-22 10:51:40.160981400 -0400 @@ -0,0 +1,383 @@ +/* + * 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.packager.internal.windows; + +import jdk.packager.internal.AbstractImageBundler; +import jdk.packager.internal.BundlerParamInfo; +import jdk.packager.internal.ConfigException; +import jdk.packager.internal.IOUtils; +import jdk.packager.internal.Log; +import jdk.packager.internal.Platform; +import jdk.packager.internal.StandardBundlerParam; +import jdk.packager.internal.UnsupportedPlatformException; +import jdk.packager.internal.builders.windows.WindowsAppImageBuilder; +import jdk.packager.internal.resources.windows.WinResources; +import jdk.packager.internal.JLinkBundlerHelper; +import jdk.packager.internal.Arguments; +import jdk.packager.internal.builders.AbstractAppImageBuilder; + +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.packager.internal.windows.WindowsBundlerParam.*; +import static jdk.packager.internal.windows.WinMsiBundler.WIN_APP_IMAGE; + +public class WinAppBundler extends AbstractImageBundler { + + private static final ResourceBundle I18N = + ResourceBundle.getBundle( + "jdk.packager.internal.resources.windows.WinAppBundler"); + + 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.info(MessageFormat.format( + I18N.getString("message.icon-not-ico"), f)); + return null; + } + return f; + }, + (s, p) -> new File(s)); + + public WinAppBundler() { + super(); + baseResourceLoader = WinResources.class; + } + + public final static String WIN_BUNDLER_PREFIX = + BUNDLER_PREFIX + "windows/"; + + @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" + 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 jpackager.exe exists. + File tool = new File( + System.getProperty("java.home") + "\\bin\\jpackager.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 ("true".equalsIgnoreCase(System.getProperty( + "fxpackager.disableBitArchitectureMismatchCheck"))) { + Log.debug(I18N.getString("message.disable-bit-architecture-check")); + return; + } + + 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")); + } + } + + // it is static for the sake of sharing with "Exe" bundles + // that may skip calls to validate/bundle in this class! + private static File getRootDir(File outDir, Map p) { + return new File(outDir, APP_NAME.fetchFrom(p)); + } + + private static boolean usePredefineAppName(Map p) { + return (PREDEFINED_APP_IMAGE.fetchFrom(p) != null); + } + + private static String appName; + private 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 != 1) { + throw new RuntimeException(MessageFormat.format( + I18N.getString("error.cannot-find-cfg"), + appImageDir)); + } else { + appName = files[0].getName(); + int index = appName.indexOf("."); + if (index != -1) { + appName = appName.substring(0, index); + } + } + + 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; + } + + private File createRoot(Map p, + File outputDirectory, boolean dependentTask) 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.info(MessageFormat.format( + I18N.getString("message.creating-app-bundle"), + APP_NAME.fetchFrom(p), outputDirectory.getAbsolutePath())); + } + + // Create directory structure + File rootDirectory = getRootDir(outputDirectory, p); + IOUtils.deleteRecursive(rootDirectory); + rootDirectory.mkdirs(); + + if (!p.containsKey(JLinkBundlerHelper.JLINK_BUILDER.getID())) { + p.put(JLinkBundlerHelper.JLINK_BUILDER.getID(), + "windowsapp-image-builder"); + } + + return rootDirectory; + } + + 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); + AbstractAppImageBuilder appBuilder = new WindowsAppImageBuilder( + APP_NAME.fetchFrom(p), + outputDirectory.toPath()); + File predefined = PREDEFINED_RUNTIME_IMAGE.fetchFrom(p); + if (predefined == null ) { + JLinkBundlerHelper.generateServerJre(p, appBuilder); + } else { + return predefined; + } + return rootDirectory; + } catch (IOException ex) { + Log.info("Exception: "+ex); + Log.debug(ex); + return null; + } catch (Exception ex) { + Log.info("Exception: "+ex); + Log.debug(ex); + return null; + } + } + + File doAppBundle(Map p, + File outputDirectory, boolean dependentTask) { + try { + File rootDirectory = + createRoot(p, outputDirectory, dependentTask); + 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.info(MessageFormat.format( + I18N.getString("message.result-dir"), + outputDirectory.getAbsolutePath())); + } + return rootDirectory; + } catch (IOException ex) { + Log.info(ex.toString()); + Log.verbose(ex); + return null; + } catch (Exception ex) { + Log.info("Exception: "+ex); + Log.debug(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("bundler.name"); + } + + @Override + public String getDescription() { + return I18N.getString("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, + JVM_PROPERTIES, + 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); + } + +} --- /dev/null 2018-10-22 10:51:49.000000000 -0400 +++ new/src/jdk.packager/windows/classes/jdk/packager/internal/windows/WinExeBundler.java 2018-10-22 10:51:47.727078400 -0400 @@ -0,0 +1,955 @@ +/* + * 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.packager.internal.windows; + +import jdk.packager.internal.*; +import jdk.packager.internal.ConfigException; +import jdk.packager.internal.Arguments; +import jdk.packager.internal.UnsupportedPlatformException; +import jdk.packager.internal.resources.windows.WinResources; + +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.packager.internal.windows.WindowsBundlerParam.*; + +public class WinExeBundler extends AbstractBundler { + + private static final ResourceBundle I18N = ResourceBundle.getBundle( + "jdk.packager.internal.resources.windows.WinExeBundler"); + + public static final BundlerParamInfo APP_BUNDLER = + new WindowsBundlerParam<>( + getString("param.app-bundler.name"), + getString("param.app-bundler.description"), + "win.app.bundler", + WinAppBundler.class, + params -> new WinAppBundler(), + null); + + public static final BundlerParamInfo CONFIG_ROOT = + new WindowsBundlerParam<>( + getString("param.config-root.name"), + getString("param.config-root.description"), + "configRoot", + File.class, + params -> { + File imagesRoot = + new File(BUILD_ROOT.fetchFrom(params), "windows"); + imagesRoot.mkdirs(); + return imagesRoot; + }, + (s, p) -> 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 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); + + public WinExeBundler() { + super(); + baseResourceLoader = WinResources.class; + } + + @Override + public String getName() { + return getString("bundler.name"); + } + + @Override + public String getDescription() { + return getString("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 = new Double(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.info(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")); + } + } + } + + // validate license file, if used, exists in the proper place + if (p.containsKey(LICENSE_FILE.getID())) { + List appResourcesList = + APP_RESOURCES_LIST.fetchFrom(p); + for (String license : LICENSE_FILE.fetchFrom(p)) { + boolean found = false; + for (RelativeFileSet appResources : appResourcesList) { + found = found || appResources.contains(license); + } + if (!found) { + throw new ConfigException( + MessageFormat.format(getString( + "error.license-missing"), license), + MessageFormat.format(getString( + "error.license-missing.advice"), license)); + } + } + } + + 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); + + List licenseFiles = LICENSE_FILE.fetchFrom(p); + if (licenseFiles != null) { + // need to copy license file to the root of win.app.image + outerLoop: + for (RelativeFileSet rfs : APP_RESOURCES_LIST.fetchFrom(p)) { + for (String s : licenseFiles) { + if (rfs.contains(s)) { + File lfile = new File(rfs.getBaseDirectory(), s); + File destFile = + new File(appDir.getParentFile(), lfile.getName()); + IOUtils.copyFile(lfile, destFile); + ensureByMutationFileIsRTF(destFile); + break outerLoop; + } + } + } + } + + // 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.info(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.info(getString("error.iscc-not-found")); + Log.info(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.info(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; + } finally { + try { + if (imageDir != null && + PREDEFINED_APP_IMAGE.fetchFrom(p) == null && + (PREDEFINED_RUNTIME_IMAGE.fetchFrom(p) == null || + !Arguments.CREATE_JRE_INSTALLER.fetchFrom(p)) && + !Log.isDebug()) { + IOUtils.deleteRecursive(imageDir); + } else if (imageDir != null) { + Log.info(MessageFormat.format( + I18N.getString("message.debug-working-directory"), + imageDir.getAbsolutePath())); + } + } catch (IOException ex) { + // noinspection ReturnInsideFinallyBlock + Log.debug(ex.getMessage()); + 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 = IDENTIFIER.fetchFrom(p); + + if (nm == null) { + nm = APP_NAME.fetchFrom(p); + } + + // limitation of innosetup + if (nm.length() > 126) { + Log.info(getString("message-truncating-id")); + nm = nm.substring(0, 126); + } + + return nm; + } + + + private String getLicenseFile(Map p) { + List licenseFiles = LICENSE_FILE.fetchFrom(p); + if (licenseFiles == null || licenseFiles.isEmpty()) { + return ""; + } else { + return licenseFiles.get(0); + } + } + + 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.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, "APPLICATION_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("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", ""); + + 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.info(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\r\n"); + } else { + registryEntries.append( + "Root: HKCU; Subkey: \"Software\\Classes\\.") + .append(ext) + .append("\"; ValueType: string;" + + " ValueName: \"\"; ValueData: \"") + .append(entryName) + .append("\"; Flags: uninsdeletevalue\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\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\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(description) + .append("\"; Flags: uninsdeletekey\r\n"); + } else { + registryEntries.append( + "Root: HKCU; Subkey: \"Software\\Classes\\") + .append(entryName) + .append( + "\"; ValueType: string; ValueName: \"\"; ValueData: \"") + .append(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( + WinAppBundler.WIN_BUNDLER_PREFIX + + getConfig_ExeProjectFile(p).getName(), + getString("resource.inno-setup-project-file"), + iss, data, VERBOSE.fetchFrom(p), + DROP_IN_RESOURCES_ROOT.fetchFrom(p)); + w.write(content); + w.close(); + return true; + } + + 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(WinAppBundler.WIN_BUNDLER_PREFIX + iconTarget.getName(), + getString("resource.setup-icon"), + DEFAULT_INNO_SETUP_ICON, + iconTarget, + VERBOSE.fetchFrom(p), + DROP_IN_RESOURCES_ROOT.fetchFrom(p)); + + fetchResource(WinAppBundler.WIN_BUNDLER_PREFIX + + getConfig_Script(p).getName(), + getString("resource.post-install-script"), + (String) null, + getConfig_Script(p), + VERBOSE.fetchFrom(p), + DROP_IN_RESOURCES_ROOT.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.info(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); + } +} --- /dev/null 2018-10-22 10:51:56.000000000 -0400 +++ new/src/jdk.packager/windows/classes/jdk/packager/internal/windows/WinMsiBundler.java 2018-10-22 10:51:54.094960300 -0400 @@ -0,0 +1,1265 @@ +/* + * 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.packager.internal.windows; + +import jdk.packager.internal.*; +import jdk.packager.internal.ConfigException; +import jdk.packager.internal.Arguments; +import jdk.packager.internal.UnsupportedPlatformException; +import jdk.packager.internal.resources.windows.WinResources; + +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.packager.internal.windows.WindowsBundlerParam.*; + +public class WinMsiBundler extends AbstractBundler { + + private static final ResourceBundle I18N = + ResourceBundle.getBundle( + "jdk.packager.internal.resources.windows.WinMsiBundler"); + + public static final BundlerParamInfo APP_BUNDLER = + new WindowsBundlerParam<>( + I18N.getString("param.app-bundler.name"), + I18N.getString("param.app-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 CONFIG_ROOT = + new WindowsBundlerParam<>( + I18N.getString("param.config-root.name"), + I18N.getString("param.config-root.description"), + "configRoot", + File.class, + params -> { + File imagesRoot = + new File(BUILD_ROOT.fetchFrom(params), "windows"); + imagesRoot.mkdirs(); + return imagesRoot; + }, + (s, p) -> null); + + 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_MSI_UPGRADE_UUID.getId(), + UUID.class, + params -> UUID.randomUUID(), // TODO check to see + // if identifier is a valid UUID during default + (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) + ); + + public WinMsiBundler() { + super(); + baseResourceLoader = WinResources.class; + } + + + @Override + public String getName() { + return I18N.getString("bundler.name"); + } + + @Override + public String getDescription() { + return I18N.getString("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 = new Double(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).doValidate(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")); + } + } + } + + // validate license file, if used, exists in the proper place + if (p.containsKey(LICENSE_FILE.getID())) { + List appResourcesList = + APP_RESOURCES_LIST.fetchFrom(p); + for (String license : LICENSE_FILE.fetchFrom(p)) { + boolean found = false; + for (RelativeFileSet appResources : appResourcesList) { + found = found || appResources.contains(license); + } + if (!found) { + throw new ConfigException( + MessageFormat.format(I18N.getString( + "error.license-missing"), license), + MessageFormat.format(I18N.getString( + "error.license-missing.advice"), license)); + } + } + } + + 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); + + List licenseFiles = LICENSE_FILE.fetchFrom(p); + if (licenseFiles != null) { + // need to copy license file to the root of win.app.image + outerLoop: + for (RelativeFileSet rfs : APP_RESOURCES_LIST.fetchFrom(p)) { + for (String s : licenseFiles) { + if (rfs.contains(s)) { + File lfile = new File(rfs.getBaseDirectory(), s); + File destFile = new File(appDir, lfile.getName()); + IOUtils.copyFile(lfile, destFile); + ensureByMutationFileIsRTF(destFile); + break outerLoop; + } + } + } + } + + // 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.info(I18N.getString("error.no-wix-tools")); + Log.info(MessageFormat.format( + I18N.getString("message.light-file-string"), light)); + Log.info(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.info(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; + } finally { + try { + if (imageDir != null && + PREDEFINED_APP_IMAGE.fetchFrom(p) == null && + (PREDEFINED_RUNTIME_IMAGE.fetchFrom(p) == null || + !Arguments.CREATE_JRE_INSTALLER.fetchFrom(p)) && + !Log.isDebug()) { + IOUtils.deleteRecursive(imageDir); + } else if (imageDir != null) { + Log.info(MessageFormat.format( + I18N.getString("message.debug-working-directory"), + imageDir.getAbsolutePath())); + } + + cleanupConfigFiles(p); + } catch (IOException ex) { + // noinspection ReturnInsideFinallyBlock + Log.debug(ex.getMessage()); + return null; + } + } + } + + protected void cleanupConfigFiles(Map params) { + if (getConfig_ProjectFile(params) != null) { + getConfig_ProjectFile(params).delete(); + } + if (getConfig_Script(params) != null) { + getConfig_Script(params).delete(); + } + } + + // 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(WinAppBundler.WIN_BUNDLER_PREFIX + + getConfig_Script(params).getName(), + I18N.getString("resource.post-install-script"), + (String) null, + getConfig_Script(params), + VERBOSE.fetchFrom(params), + DROP_IN_RESOURCES_ROOT.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( + WinAppBundler.WIN_BUNDLER_PREFIX + + getConfig_ProjectFile(params).getName(), + I18N.getString("resource.wix-config-file"), + wxs, data, VERBOSE.fetchFrom(params), + DROP_IN_RESOURCES_ROOT.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.info(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.info(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 params) { + List licenseFiles = LICENSE_FILE.fetchFrom(params); + if (licenseFiles == null || licenseFiles.isEmpty()) { + return null; + } else { + return licenseFiles.get(0); + } + } + + 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); + } + + } +} --- /dev/null 2018-10-22 10:52:03.000000000 -0400 +++ new/src/jdk.packager/windows/classes/jdk/packager/internal/windows/WindowsBundlerParam.java 2018-10-22 10:52:01.310253500 -0400 @@ -0,0 +1,132 @@ +/* + * 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.packager.internal.windows; + +import jdk.packager.internal.BundlerParamInfo; +import jdk.packager.internal.StandardBundlerParam; +import jdk.packager.internal.Arguments; +import jdk.packager.internal.RelativeFileSet; +import jdk.packager.internal.bundlers.BundleParams; + +import java.util.Map; +import java.util.ResourceBundle; +import java.util.function.BiFunction; +import java.util.function.Function; + +public class WindowsBundlerParam extends StandardBundlerParam { + + private static final ResourceBundle I18N = ResourceBundle.getBundle( + "jdk.packager.internal.resources.windows.WindowsBundlerParam"); + + public WindowsBundlerParam(String name, String description, String id, + Class valueType, + Function, T> defaultValueFunction, + BiFunction, T> stringConverter) { + super(name, description, id, valueType, + defaultValueFunction, stringConverter); + } + + public 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); + + public 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); + + public 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 + ); + + public 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) + ); + + public 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) + ); + + public 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) + ); +} --- /dev/null 2018-10-22 10:52:10.000000000 -0400 +++ new/src/jdk.packager/windows/classes/jdk/packager/internal/windows/WindowsDefender.java 2018-10-22 10:52:07.909138100 -0400 @@ -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.packager.internal.windows; + +import jdk.packager.internal.Platform; +import java.util.List; + +public final class WindowsDefender { + + private WindowsDefender() {} + + public 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; + } + + public static final String getUserTempDirectory() { + String tempDirectory = System.getProperty("java.io.tmpdir"); + return tempDirectory; + } +} --- /dev/null 2018-10-22 10:52:16.000000000 -0400 +++ new/src/jdk.packager/windows/classes/jdk/packager/internal/windows/WindowsRegistry.java 2018-10-22 10:52:14.133617900 -0400 @@ -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.packager.internal.windows; + +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.packager.internal.IOUtils.exec; + +public final class WindowsRegistry { + + private WindowsRegistry() {} + + /** + * Reads the registry value for DisableRealtimeMonitoring. + * @return true if DisableRealtimeMonitoring is set to 0x1, + * false otherwise. + */ + public 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; + } + + public 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 + */ + public 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; + } +} --- /dev/null 2018-10-22 10:52:23.000000000 -0400 +++ new/src/jdk.packager/windows/classes/module-info.java.extra 2018-10-22 10:52:21.031906600 -0400 @@ -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.packager.internal.Bundler with + jdk.packager.internal.windows.WinAppBundler, + jdk.packager.internal.windows.WinExeBundler, + jdk.packager.internal.windows.WinMsiBundler; + --- /dev/null 2018-10-22 10:52:30.000000000 -0400 +++ new/src/jdk.packager/windows/native/jpackager/ByteBuffer.cpp 2018-10-22 10:52:28.145597800 -0400 @@ -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); + } +} --- /dev/null 2018-10-22 10:52:37.000000000 -0400 +++ new/src/jdk.packager/windows/native/jpackager/ByteBuffer.h 2018-10-22 10:52:35.384090600 -0400 @@ -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 --- /dev/null 2018-10-22 10:52:44.000000000 -0400 +++ new/src/jdk.packager/windows/native/jpackager/IconSwap.cpp 2018-10-22 10:52:42.388580400 -0400 @@ -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; +} --- /dev/null 2018-10-22 10:52:51.000000000 -0400 +++ new/src/jdk.packager/windows/native/jpackager/IconSwap.h 2018-10-22 10:52:49.456471100 -0400 @@ -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 --- /dev/null 2018-10-22 10:52:58.000000000 -0400 +++ new/src/jdk.packager/windows/native/jpackager/VersionInfoSwap.cpp 2018-10-22 10:52:56.697964200 -0400 @@ -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() +{ +} --- /dev/null 2018-10-22 10:53:05.000000000 -0400 +++ new/src/jdk.packager/windows/native/jpackager/VersionInfoSwap.h 2018-10-22 10:53:03.110646500 -0400 @@ -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 --- /dev/null 2018-10-22 10:53:14.000000000 -0400 +++ new/src/jdk.packager/windows/native/jpackager/jpackager.cpp 2018-10-22 10:53:10.598742500 -0400 @@ -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: jpackager.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: jpackager.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.packager/jdk.packager.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; +} --- /dev/null 2018-10-22 10:53:20.000000000 -0400 +++ new/src/jdk.packager/windows/native/jpackager/jpackager.manifest 2018-10-22 10:53:18.617245300 -0400 @@ -0,0 +1,34 @@ + + + + Java Packager + + + + + + + + + + + + + true + + + + + + + + + + + --- /dev/null 2018-10-22 10:53:29.000000000 -0400 +++ new/src/jdk.packager/windows/native/jpackager/jpackager.rc 2018-10-22 10:53:27.166154900 -0400 @@ -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 "jpackager.manifest" + --- /dev/null 2018-10-22 10:53:39.000000000 -0400 +++ new/src/jdk.packager/windows/native/launcher/WinLauncher.cpp 2018-10-22 10:53:37.041081500 -0400 @@ -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 PACKAGER_LIBRARY TEXT("packager.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(PACKAGER_LIBRARY); + + if (library == NULL) { + std::wstring title = GetTitle(); + std::wstring description = std::wstring(PACKAGER_LIBRARY) + + std::wstring(TEXT(" not found.")); + MessageBox(NULL, description.data(), + title.data(), MB_ICONERROR | MB_OK); + } + else { + start_launcher start = + (start_launcher)GetProcAddress(library, "start_launcher"); + stop_launcher stop = + (stop_launcher)GetProcAddress(library, "stop_launcher"); + + if (start != NULL && stop != NULL) { + if (start(argc, argv) == true) { + result = 0; + stop(); + } + } + + ::FreeLibrary(library); + } + + if (argv != NULL) { + LocalFree(argv); + } + + return result; +} + --- /dev/null 2018-10-22 10:53:48.000000000 -0400 +++ new/test/jdk/tools/jpackager/jdk/packager/internal/DeployParamsTest.java 2018-10-22 10:53:45.979996100 -0400 @@ -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.packager.internal.Arguments; +import jdk.packager.internal.DeployParams; +import jdk.packager.internal.PackagerException; +import java.io.File; + +/* + * @test + * @bug 8211285 + * @summary DeployParamsTest + * @modules jdk.packager + * @modules jdk.packager/jdk.packager.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.setOutdir(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(); + } + } + +}