Hi, Essentially the same patch as last time [1] but this time with a bug number [2].The patch does not include the updated UnicodeTest.jar which I created with ../jtreg-5.0-b01/bin/jtreg -va -nr -jdk:build/linux-x86_64-server- release/images/jdk/ -Xshare:off test/jdk/tools/launcher/UnicodeTest.javacp JTwork/scratch/UnicodeTest.jar test/jdk/tools/launcher/ Regards,Philipp [1] https://mail.openjdk.java.net/pipermail/core-libs-dev/2020-January/064190.html[2 ] https://bugs.openjdk.java.net/browse/JDK-8243454
On Sat, 2020-01-04 at 11:00 +0100, Philipp Kunz wrote: > Hi, > When I tried to improve Unicode support in JAR manifests in [1], > independent of what will happen with that, I found that there are not > only Manifest and Attributes classes parsing manifests but also some > c code which parses "SplashScreen-Image" attribute and also used to > parse some other attributes such as "Main-Class" and others. > There already are tests for splash screen images but those existing > ones work with the "-splash" command line option and not with the > "SplashScreen-Image" manifest attribute. I found "SplashScreen-Image" > manifest attribute not yet covered with a test and extended the > existing UnicodeTest accordingly, see attached patch which confirmed > that the "SplashScreen-Image" manifest attribute already fully > supports Unicode. > Support for "JRE-Version" manifest attribute and "-jre-restrict- > search" and "-jre-no-restrict-search" command line attributes has > already been removed earlier already and the relevant lines of code > determining the main class from the manifest when launching have > already been moved to or near LauncherHelper::getMainClassFromJar in > earlier versions, apparently leaving them with no use any longer in > java.c, java.h, manifest_info.h, and parse_manifest.c, I figure. > Hence, I propose to remove those parts as in the attached patch. > This leaves manifest_info.h and parse_manifest.c with "SplashScreen- > Image" as the only attribute parsed there. Certainly it would be a > different change but anyway it might be worth a consideration to move > the code opening the splash screen image to LauncherHelper or a > similar appropriate place in Java which would allow to remove quite a > number of some lines of c code, provided it could be acceptable to > show the splash screen image slightly later. > There is no existing related bug and I didn't find a new one. It > would be nice to have "SplashScreen-Image" manifest attribute covered > with a test and there is some potential for cleaning up unused code > which certainly is not urgent at all and I would not know how > desirable this really is. Also I'm not sure whether it's better or > not to add SPLASHSCREEN_IMAGE to Attributes.Name.KNOWN_NAMES. > Any opinion about to how to proceed with this, if at all or would > someone sponsor this patch? > Regards,Philipp > > [1] > https://mail.openjdk.java.net/pipermail/core-libs-dev/2019-December/064149.html >
diff -r 6c5509217407 src/java.base/share/classes/java/util/jar/Attributes.java --- a/src/java.base/share/classes/java/util/jar/Attributes.java Sat May 16 09:43:44 2020 +0200 +++ b/src/java.base/share/classes/java/util/jar/Attributes.java Sun May 17 11:48:10 2020 +0200 @@ -577,6 +577,18 @@ public static final Name MAIN_CLASS; /** + * {@link Name} object for {@code SplashScreen-Image} manifest attribute + * used for pointing to an image to be shown early during the launch + * of applications packaged in JAR files. + * The {@code SplashScreen-Image} manifest attribute is used in + * conjunction with the {@code -jar} command-line option of the + * {@code java} application launcher. + * + * @see java.awt.SplashScreen + */ + public static final Name SPLASHSCREEN_IMAGE; + + /** * {@code Name} object for {@code Sealed} manifest attribute * used for sealing. * @see <a href="{@docRoot}/../specs/jar/jar.html#package-sealing"> @@ -680,6 +692,7 @@ CONTENT_TYPE = new Name("Content-Type"); CLASS_PATH = new Name("Class-Path"); MAIN_CLASS = new Name("Main-Class"); + SPLASHSCREEN_IMAGE = new Name("SplashScreen-Image"); SEALED = new Name("Sealed"); EXTENSION_LIST = new Name("Extension-List"); EXTENSION_NAME = new Name("Extension-Name"); @@ -700,6 +713,7 @@ addName(names, CONTENT_TYPE); addName(names, CLASS_PATH); addName(names, MAIN_CLASS); + addName(names, SPLASHSCREEN_IMAGE); addName(names, SEALED); addName(names, EXTENSION_LIST); addName(names, EXTENSION_NAME); @@ -738,6 +752,7 @@ CONTENT_TYPE = KNOWN_NAMES.get("Content-Type"); CLASS_PATH = KNOWN_NAMES.get("Class-Path"); MAIN_CLASS = KNOWN_NAMES.get("Main-Class"); + SPLASHSCREEN_IMAGE = KNOWN_NAMES.get("SplashScreen-Image"); SEALED = KNOWN_NAMES.get("Sealed"); EXTENSION_LIST = KNOWN_NAMES.get("Extension-List"); EXTENSION_NAME = KNOWN_NAMES.get("Extension-Name"); diff -r 6c5509217407 src/java.base/share/native/libjli/java.c --- a/src/java.base/share/native/libjli/java.c Sat May 16 09:43:44 2020 +0200 +++ b/src/java.base/share/native/libjli/java.c Sun May 17 11:48:10 2020 +0200 @@ -107,7 +107,7 @@ static void SetJavaLauncherProp(); static void SetClassPath(const char *s); static void SetMainModule(const char *s); -static void SelectVersion(int argc, char **argv, char **main_class); +static void SelectVersion(int argc, char **argv); static void SetJvmEnvironment(int argc, char **argv); static jboolean ParseArguments(int *pargc, char ***pargv, int *pmode, char **pwhat, @@ -238,7 +238,6 @@ { int mode = LM_UNKNOWN; char *what = NULL; - char *main_class = NULL; int ret; InvocationFunctions ifn; jlong start = 0, end = 0; @@ -277,7 +276,7 @@ * the pre 1.9 JRE [ 1.6 thru 1.8 ], it is as if 1.9+ has been * invoked from the command line. */ - SelectVersion(argc, argv, &main_class); + SelectVersion(argc, argv); CreateExecutionEnvironment(&argc, &argv, jrepath, sizeof(jrepath), @@ -1031,65 +1030,35 @@ } /* - * The SelectVersion() routine ensures that an appropriate version of - * the JRE is running. The specification for the appropriate version - * is obtained from either the manifest of a jar file (preferred) or - * from command line options. - * The routine also parses splash screen command line options and + * The SelectVersion() parses the splash screen command line options and * passes on their values in private environment variables. */ static void -SelectVersion(int argc, char **argv, char **main_class) +SelectVersion(int argc, char **argv) { char *arg; char *operand; - char *version = NULL; - char *jre = NULL; int jarflag = 0; int headlessflag = 0; - int restrict_search = -1; /* -1 implies not known */ manifest_info info; - char env_entry[MAXNAMELEN + 24] = ENV_ENTRY "="; char *splash_file_name = NULL; char *splash_jar_name = NULL; - char *env_in; int res; jboolean has_arg; /* - * If the version has already been selected, set *main_class - * with the value passed through the environment (if any) and - * simply return. - */ - - /* - * This environmental variable can be set by mJRE capable JREs - * [ 1.5 thru 1.8 ]. All other aspects of mJRE processing have been - * stripped by those JREs. This environmental variable allows 1.9+ - * JREs to be started by these mJRE capable JREs. - * Note that mJRE directives in the jar manifest file would have been - * ignored for a JRE started by another JRE... - * .. skipped for JRE 1.5 and beyond. - * .. not even checked for pre 1.5. + * Scan through the command line arguments. */ - if ((env_in = getenv(ENV_ENTRY)) != NULL) { - if (*env_in != '\0') - *main_class = JLI_StringDup(env_in); - return; - } - - /* - * Scan through the arguments for options relevant to multiple JRE - * support. Multiple JRE support existed in JRE versions 1.5 thru 1.8. - * - * This capability is no longer available with JRE versions 1.9 and later. - * These command line options are reported as errors. - */ - argc--; argv++; while ((arg = *argv) != 0 && *arg == '-') { has_arg = IsOptionWithArgument(argc, argv); + + /* + * Multiple JRE support existed in JRE versions 1.5 thru 1.8. + * This capability is no longer available with JRE versions 1.9 and later. + * Options relevant to multiple JRE support are reported as errors. + */ if (JLI_StrCCmp(arg, "-version:") == 0) { JLI_ReportErrorMessage(SPC_ERROR1); } else if (JLI_StrCmp(arg, "-jre-restrict-search") == 0) { @@ -1157,11 +1126,6 @@ splash_file_name = info.splashscreen_image_file_name; splash_jar_name = operand; } - } else { - info.manifest_version = NULL; - info.main_class = NULL; - info.jre_version = NULL; - info.jre_restrict_search = 0; } /* @@ -1180,19 +1144,7 @@ putenv(splash_jar_entry); } - - /* - * "Valid" returns (other than unrecoverable errors) follow. Set - * main_class as a side-effect of this routine. - */ - if (info.main_class != NULL) - *main_class = JLI_StringDup(info.main_class); - - if (info.jre_version == NULL) { - JLI_FreeManifest(); - return; - } - + JLI_FreeManifest(); } /* @@ -2292,13 +2244,11 @@ * Done with all command line processing and potential re-execs so * clean up the environment. */ - (void)UnsetEnv(ENV_ENTRY); (void)UnsetEnv(SPLASH_FILE_ENV_ENTRY); (void)UnsetEnv(SPLASH_JAR_ENV_ENTRY); JLI_MemFree(splash_jar_entry); JLI_MemFree(splash_file_entry); - } static const char* GetFullVersion() diff -r 6c5509217407 src/java.base/share/native/libjli/java.h --- a/src/java.base/share/native/libjli/java.h Sat May 16 09:43:44 2020 +0200 +++ b/src/java.base/share/native/libjli/java.h Sun May 17 11:48:10 2020 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -50,25 +50,6 @@ #define CURRENT_DATA_MODEL (CHAR_BIT * sizeof(void*)) -/* - * The following environment variable is used to influence the behavior - * of the jre exec'd through the SelectVersion routine. The command line - * options which specify the version are not passed to the exec'd version, - * because that jre may be an older version which wouldn't recognize them. - * This environment variable is known to this (and later) version and serves - * to suppress the version selection code. This is not only for efficiency, - * but also for correctness, since any command line options have been - * removed which would cause any value found in the manifest to be used. - * This would be incorrect because the command line options are defined - * to take precedence. - * - * The value associated with this environment variable is the MainClass - * name from within the executable jar file (if any). This is strictly a - * performance enhancement to avoid re-reading the jar file manifest. - * - */ -#define ENV_ENTRY "_JAVA_VERSION_SET" - #define SPLASH_FILE_ENV_ENTRY "_JAVA_SPLASH_FILE" #define SPLASH_JAR_ENV_ENTRY "_JAVA_SPLASH_JAR" #define JDK_JAVA_OPTIONS "JDK_JAVA_OPTIONS" diff -r 6c5509217407 src/java.base/share/native/libjli/manifest_info.h --- a/src/java.base/share/native/libjli/manifest_info.h Sat May 16 09:43:44 2020 +0200 +++ b/src/java.base/share/native/libjli/manifest_info.h Sun May 17 11:48:10 2020 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -167,10 +167,6 @@ * Java launcher). */ typedef struct manifest_info { /* Interesting fields from the Manifest */ - char *manifest_version; /* Manifest-Version string */ - char *main_class; /* Main-Class entry */ - char *jre_version; /* Appropriate J2SE release spec */ - char jre_restrict_search; /* Restricted JRE search */ char *splashscreen_image_file_name; /* splashscreen image file */ } manifest_info; diff -r 6c5509217407 src/java.base/share/native/libjli/parse_manifest.c --- a/src/java.base/share/native/libjli/parse_manifest.c Sat May 16 09:43:44 2020 +0200 +++ b/src/java.base/share/native/libjli/parse_manifest.c Sun May 17 11:48:10 2020 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -595,10 +595,6 @@ )) == -1) { return (-1); } - info->manifest_version = NULL; - info->main_class = NULL; - info->jre_version = NULL; - info->jre_restrict_search = 0; info->splashscreen_image_file_name = NULL; if ((rc = find_file(fd, &entry, manifest_name)) != 0) { close(fd); @@ -611,17 +607,7 @@ } lp = manifest; while ((rc = parse_nv_pair(&lp, &name, &value)) > 0) { - if (JLI_StrCaseCmp(name, "Manifest-Version") == 0) { - info->manifest_version = value; - } else if (JLI_StrCaseCmp(name, "Main-Class") == 0) { - info->main_class = value; - } else if (JLI_StrCaseCmp(name, "JRE-Version") == 0) { - /* - * Manifest specification overridden by command line option - * so we will silently override there with no specification. - */ - info->jre_version = 0; - } else if (JLI_StrCaseCmp(name, "Splashscreen-Image") == 0) { + if (JLI_StrCaseCmp(name, "SplashScreen-Image") == 0) { info->splashscreen_image_file_name = value; } } diff -r 6c5509217407 test/jdk/java/awt/SplashScreen/FullscreenAfterSplash/FullScreenAfterSplash.java --- a/test/jdk/java/awt/SplashScreen/FullscreenAfterSplash/FullScreenAfterSplash.java Sat May 16 09:43:44 2020 +0200 +++ b/test/jdk/java/awt/SplashScreen/FullscreenAfterSplash/FullScreenAfterSplash.java Sun May 17 11:48:10 2020 +0200 @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013, 2017, Oracle and/or its affiliates. All rights reserved. +* Copyright (c) 2013, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,10 +25,6 @@ import java.awt.Window; import java.awt.Robot; import java.awt.event.InputEvent; -import java.lang.InterruptedException; -import java.lang.System; -import java.lang.Thread; -import java.lang.reflect.Method; import java.lang.reflect.Proxy; import javax.swing.JFrame; import javax.swing.SwingUtilities; @@ -45,7 +41,7 @@ * @modules java.desktop/sun.awt * java.desktop/com.apple.eawt * @build GenerateTestImage - * @run main GenerateTestImage + * @run main GenerateTestImage test.png * @author Petr Pchelko area=awt.event * @run main/othervm -splash:test.png FullScreenAfterSplash */ diff -r 6c5509217407 test/jdk/java/awt/SplashScreen/GenerateTestImage.java --- a/test/jdk/java/awt/SplashScreen/GenerateTestImage.java Sat May 16 09:43:44 2020 +0200 +++ b/test/jdk/java/awt/SplashScreen/GenerateTestImage.java Sun May 17 11:48:10 2020 +0200 @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013, 2017, Oracle and/or its affiliates. All rights reserved. +* Copyright (c) 2013, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -35,16 +35,17 @@ private static final int IMAGE_SIZE = 20; public static void main(String[] args) throws Exception { - File file = new File("test.png"); + File file = new File(args[0]); if (file.exists()) { return; } + BufferedImage image = new BufferedImage(IMAGE_SIZE, IMAGE_SIZE, BufferedImage.TYPE_INT_ARGB); Graphics2D graphics2D = image.createGraphics(); graphics2D.setColor(Color.red); graphics2D.fillOval(0, 0, IMAGE_SIZE, IMAGE_SIZE); - graphics2D.dispose();; + graphics2D.dispose(); - ImageIO.write(image, "png", file); + ImageIO.write(image, "png", file); } } diff -r 6c5509217407 test/jdk/tools/launcher/MainClassAttributeTest.java --- a/test/jdk/tools/launcher/MainClassAttributeTest.java Sat May 16 09:43:44 2020 +0200 +++ b/test/jdk/tools/launcher/MainClassAttributeTest.java Sun May 17 11:48:10 2020 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,29 +21,24 @@ * questions. */ +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + /** * @test * @bug 7067922 * @author sogoel - * @summary Test negative scenarios for main class attribute * @modules jdk.compiler * jdk.zipfs - * @build MainClassAttributeTest * @run main MainClassAttributeTest - */ - -import java.io.File; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -/* - * This tests negative scenarios for Main class entry in a jar file. + * @summary + * This tests negative scenarios for Main-Class entry in a jar manifest. * An error should be thrown for each of the test cases when such a * jar is executed. + * Positive scenarios can be found in {@link UnicodeTest}. */ - public class MainClassAttributeTest extends TestHelper { /* diff -r 6c5509217407 test/jdk/tools/launcher/UnicodeTest.java --- a/test/jdk/tools/launcher/UnicodeTest.java Sat May 16 09:43:44 2020 +0200 +++ b/test/jdk/tools/launcher/UnicodeTest.java Sun May 17 11:48:10 2020 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,14 +23,14 @@ /* * @test - * @bug 5030265 + * @bug 5030265 8243454 * @modules jdk.compiler * jdk.zipfs - * @compile -XDignore.symbol.file UnicodeTest.java + * @library ../../java/awt/SplashScreen * @run main/othervm UnicodeTest - * @summary Verify that the J2RE can handle all legal Unicode characters - * in class names unless limited by the file system encoding - * or the encoding used for command line arguments. + * @summary Verify that the JVM can handle Unicode characters + * in class names and splash images unless limited by the file + * system encoding or the encoding used for command line arguments. * @author Norbert Lindenberg, ksrini */ @@ -51,12 +51,25 @@ import java.io.OutputStreamWriter; import java.nio.charset.Charset; import java.util.Locale; +import java.util.jar.Attributes.Name; +import java.util.jar.Attributes; +import java.util.jar.Manifest; public class UnicodeTest extends TestHelper { + + static final String PNG_FILE_EXT = ".png"; static final File UnicodeTestSrc = new File("UnicodeTest-src"); static final File UnicodeTestClasses = new File("UnicodeTest-classes"); static final String UnicodeTestJarName = "UnicodeTest" + JAR_FILE_EXT; static final File UnicodeTestJar = new File(UnicodeTestJarName); + /** + * For running this test in environments with no Unicode platform encoding + * support where UnicodeTest.jar cannot be built, a jar file + * "UnicodeTest.jar" in the same directory as this here "UnicodeTest.java" + * test class is checked in to the source code. + * It is created by runnnig the test alone (with jtreg) and then copying the + * resulting "UnicodeTest.jar" from "JTwork/scratch" to here. + */ static final File SolarisUnicodeTestJar = new File(TEST_SOURCES_DIR, UnicodeTestJarName); @@ -80,15 +93,16 @@ createJar("-cvfm", UnicodeTestJar.getAbsolutePath(), new File(UnicodeTestSrc, "MANIFEST.MF").getAbsolutePath(), "-C", UnicodeTestClasses.getAbsolutePath(), "."); - if (!UnicodeTestJar.exists()) { throw new Error("failed to create " + UnicodeTestJar.getAbsolutePath()); } + generateSplashImage(classname + PNG_FILE_EXT); System.out.println("running test app using class file"); TestResult tr = doExec(javaCmd, - "-cp", UnicodeTestClasses.getAbsolutePath(), classname); - if (!tr.isOK()) { + "-splash:" + classname + PNG_FILE_EXT, + "-cp", UnicodeTestClasses.getAbsolutePath(), classname); + if (!tr.isOK() || !tr.contains("success")) { System.out.println(tr); throw new RuntimeException("test fails"); } @@ -132,7 +146,7 @@ static void runTest(File testJar) { TestResult tr = doExec(javaCmd, "-jar", testJar.getAbsolutePath()); - if (!tr.isOK()) { + if (!tr.isOK() || !tr.contains("success")) { System.out.println(tr); throw new RuntimeException("test fails"); } @@ -159,6 +173,9 @@ generateSource(commandLineClassName, manifestClassName); generateSource(manifestClassName, commandLineClassName); + generateSplashImage(new File( + UnicodeTestClasses, manifestClassName + PNG_FILE_EXT + ).getAbsolutePath()); generateManifest(manifestClassName); return commandLineClassName; } @@ -180,12 +197,14 @@ private static final String turkish = "T\u00fcrk\u00e7e"; private static final String spanish = "espa\u00f1ol"; private static final String thai = "\u0e44\u0e17\u0e22"; + private static final String precombined = "\u00e4\u00e9"; + private static final String combining = "a\u0308e\u0301"; private static final String unicode = arabic + s_chinese + t_chinese + russian + hindi + greek + hebrew + japanese + korean - + lithuanian + czech + turkish + spanish + thai; + + lithuanian + czech + turkish + spanish + thai + + precombined + combining; private static String commandLineClassNameSuffix() { - // Mapping from main platform encodings to language names // for Unix and Windows, respectively. Use empty suffix // for Windows encodings where OEM encoding differs. @@ -238,17 +257,27 @@ } private static boolean hasUnicodeFileSystem() { - return (isWindows) ? true : defaultEncoding.equalsIgnoreCase("UTF-8"); + return isWindows || defaultEncoding.equalsIgnoreCase("UTF-8"); } private static void generateSource(String thisClass, String otherClass) throws Exception { File file = new File(UnicodeTestSrc, thisClass + JAVA_FILE_EXT); OutputStreamWriter out = new OutputStreamWriter(new FileOutputStream(file), "UTF-8"); + out.write("import java.awt.SplashScreen;\n"); out.write("public class " + thisClass + " {\n"); out.write(" public static void main(String[] args) {\n"); out.write(" if (!" + otherClass + "." + otherClass.toLowerCase() + "().equals(\"" + otherClass + "\")) {\n"); out.write(" throw new RuntimeException();\n"); out.write(" }\n"); + out.write(" SplashScreen splash = " + + "SplashScreen.getSplashScreen();\n"); + out.write(" if (splash == null) {\n"); + out.write(" throw new AssertionError(\"no splash\");\n"); + out.write(" }\n"); + out.write(" System.out.println(\"splash screen image url = " + + "\" + splash.getImageURL());\n"); + out.write(" splash.close();\n"); + out.write(" System.out.println(\"success\");\n"); out.write(" }\n"); out.write(" public static String " + thisClass.toLowerCase() + "() {\n"); out.write(" return \"" + thisClass + "\";\n"); @@ -257,27 +286,20 @@ out.close(); } + private static void generateSplashImage(String splashImg) throws Exception { + GenerateTestImage.main(new String[] { splashImg }); + } + private static void generateManifest(String mainClass) throws Exception { File file = new File(UnicodeTestSrc, "MANIFEST.MF"); - FileOutputStream out = new FileOutputStream(file); - out.write("Manifest-Version: 1.0\n".getBytes("UTF-8")); - // Header lines are limited to 72 bytes. - // The manifest spec doesn't say we have to break at character boundaries, - // so we rudely break at byte boundaries. - byte[] headerBytes = ("Main-Class: " + mainClass + "\n").getBytes("UTF-8"); - if (headerBytes.length <= 72) { - out.write(headerBytes); - } else { - out.write(headerBytes, 0, 72); - int start = 72; - while (headerBytes.length > start) { - out.write((byte) '\n'); - out.write((byte) ' '); - int count = Math.min(71, headerBytes.length - start); - out.write(headerBytes, start, count); - start += count; - } + try (FileOutputStream out = new FileOutputStream(file)) { + Manifest mf = new Manifest(); + Attributes mainAtts = mf.getMainAttributes(); + mainAtts.put(Name.MANIFEST_VERSION, "1.0"); + mainAtts.put(Name.MAIN_CLASS, mainClass); + mainAtts.put(Name.SPLASHSCREEN_IMAGE, mainClass + PNG_FILE_EXT); + mf.write(out); } - out.close(); } + }