提交 a08d1d42 编写于 作者: A alanb

8003255: (profiles) Update JAR file specification to support profiles

Reviewed-by: dholmes, mchung, ksrini
上级 242e007c
......@@ -57,6 +57,12 @@ import sun.security.util.SecurityConstants;
* <p>
* The classes that are loaded are by default granted permission only to
* access the URLs specified when the URLClassLoader was created.
* <p>
* Where a JAR file contains the {@link Name#PROFILE Profile} attribute
* then its value is the name of the Java SE profile that the library
* minimally requires. If this runtime does not support the profile then
* it causes {@link java.util.jar.UnsupportedProfileException} to be
* thrown at some unspecified time.
*
* @author David Connelly
* @since 1.2
......
......@@ -564,6 +564,15 @@ public class Attributes implements Map<Object,Object>, Cloneable {
*/
public static final Name MAIN_CLASS = new Name("Main-Class");
/**
* {@code Name} object for {@code Profile} manifest attribute used by
* applications or libraries packaged as JAR files to indicate the
* minimum profile required to execute the application.
* @since 1.8
* @see UnsupportedProfileException
*/
public static final Name PROFILE = new Name("Profile");
/**
* <code>Name</code> object for <code>Sealed</code> manifest attribute
* used for sealing.
......
/*
* Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please 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 java.util.jar;
/**
* Thrown to indicate an attempt to access a JAR file with a {@link
* Attributes.Name#PROFILE Profile} attribute that names a profile that
* is not supported by this runtime.
*
* @since 1.8
*/
public class UnsupportedProfileException extends RuntimeException {
private static final long serialVersionUID = -1834773870678792406L;
/**
* Constructs an {@code UnsupportedProfileException} with no detail
* message.
*/
public UnsupportedProfileException() {
}
/**
* Constructs an {@code UnsupportedProfileException} with the
* specified detail message.
*
* @param message the detail message
*/
public UnsupportedProfileException(String message) {
super(message);
}
}
......@@ -65,10 +65,14 @@ import java.util.TreeSet;
import java.util.jar.Attributes;
import java.util.jar.JarFile;
import java.util.jar.Manifest;
import sun.misc.Version;
import sun.misc.URLClassPath;
public enum LauncherHelper {
INSTANCE;
private static final String MAIN_CLASS = "Main-Class";
private static final String PROFILE = "Profile";
private static StringBuilder outBuf = new StringBuilder();
private static final String INDENT = " ";
......@@ -418,6 +422,28 @@ public enum LauncherHelper {
new Attributes.Name(FXHelper.JAVAFX_APPLICATION_MARKER))) {
return FXHelper.class.getName();
}
/*
* If this is not a full JRE then the Profile attribute must be
* present with the Main-Class attribute so as to indicate the minimum
* profile required. Note that we need to suppress checking of the Profile
* attribute after we detect an error. This is because the abort may
* need to lookup resources and this may involve opening additional JAR
* files that would result in errors that suppress the main error.
*/
String profile = mainAttrs.getValue(PROFILE);
if (profile == null) {
if (!Version.isFullJre()) {
URLClassPath.suppressProfileCheckForLauncher();
abort(null, "java.launcher.jar.error4", jarname);
}
} else {
if (!Version.supportsProfile(profile)) {
URLClassPath.suppressProfileCheckForLauncher();
abort(null, "java.launcher.jar.error5", profile, jarname);
}
}
return mainValue.trim();
} catch (IOException ioe) {
abort(ioe, "java.launcher.jar.error1", jarname);
......
......@@ -139,6 +139,8 @@ java.launcher.jar.error1=\
Error: An unexpected error occurred while trying to open file {0}
java.launcher.jar.error2=manifest not found in {0}
java.launcher.jar.error3=no main manifest attribute, in {0}
java.launcher.jar.error4=no Profile manifest attribute in {0}
java.launcher.jar.error5=Profile {0} required by {1} not supported by this runtime
java.launcher.init.error=initialization error
java.launcher.javafx.error1=\
Error: The JavaFX launchApplication method has the wrong signature, it\n\
......
......@@ -35,6 +35,7 @@ import java.util.jar.JarEntry;
import java.util.jar.Manifest;
import java.util.jar.Attributes;
import java.util.jar.Attributes.Name;
import java.util.jar.UnsupportedProfileException;
import java.net.JarURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
......@@ -64,6 +65,12 @@ public class URLClassPath {
final static String JAVA_VERSION;
private static final boolean DEBUG;
/**
* Used by launcher to indicate that checking of the JAR file "Profile"
* attribute has been suppressed.
*/
private static boolean profileCheckSuppressedByLauncher;
static {
JAVA_VERSION = java.security.AccessController.doPrivileged(
new sun.security.action.GetPropertyAction("java.version"));
......@@ -581,6 +588,15 @@ public class URLClassPath {
}
}
/**
* Used by the launcher to suppress further checking of the JAR file Profile
* attribute (necessary when the launcher is aborting as the abort involves
* a resource lookup that may involve opening additional JAR files)
*/
public static void suppressProfileCheckForLauncher() {
profileCheckSuppressedByLauncher = true;
}
/*
* Inner class used to represent a Loader of resources from a JAR URL.
*/
......@@ -788,6 +804,28 @@ public class URLClassPath {
return false;
}
/**
* If the Profile attribute is present then this method checks that the runtime
* supports that profile.
*
* ## Add a fast path like Class-Path to avoid reading the manifest when the attribute
* is not present.
*/
void checkProfileAttribute() throws IOException {
Manifest man = jar.getManifest();
if (man != null) {
Attributes attr = man.getMainAttributes();
if (attr != null) {
String value = attr.getValue(Name.PROFILE);
if (value != null && !Version.supportsProfile(value)) {
String prefix = Version.profileName().length() > 0 ?
"This runtime implements " + Version.profileName() + ", " : "";
throw new UnsupportedProfileException(prefix + csu + " requires " + value);
}
}
}
}
/*
* Returns the URL for a resource with the specified name
*/
......@@ -957,6 +995,12 @@ public class URLClassPath {
ensureOpen();
parseExtensionsDependencies();
// check Profile attribute if present
if (!profileCheckSuppressedByLauncher) {
checkProfileAttribute();
}
if (SharedSecrets.javaUtilJarAccess().jarFileHasClassPathAttribute(jar)) { // Only get manifest when necessary
Manifest man = jar.getManifest();
if (man != null) {
......
......@@ -47,7 +47,7 @@ public
class Main {
String program;
PrintStream out, err;
String fname, mname, ename;
String fname, mname, ename, pname;
String zname = "";
String[] files;
String rootjar = null;
......@@ -78,6 +78,9 @@ class Main {
static final String MANIFEST_DIR = "META-INF/";
static final String VERSION = "1.0";
// valid values for Profile attribute
private static final String[] PROFILES = { "compact1", "compact2", "compact3" };
private static ResourceBundle rsrc;
/**
......@@ -184,6 +187,14 @@ class Main {
if (ename != null) {
addMainClass(manifest, ename);
}
if (pname != null) {
if (!addProfileName(manifest, pname)) {
if (in != null) {
in.close();
}
return false;
}
}
}
OutputStream out;
if (fname != null) {
......@@ -230,7 +241,7 @@ class Main {
if (manifest != null) {
manifest.close();
}
if (fname != null) {
if (ok && fname != null) {
// on Win32, we need this delete
inputFile.delete();
if (!tmpFile.renameTo(inputFile)) {
......@@ -361,6 +372,9 @@ class Main {
case 'e':
ename = args[count++];
break;
case 'p':
pname = args[count++];
break;
default:
error(formatMsg("error.illegal.option",
String.valueOf(flags.charAt(i))));
......@@ -410,7 +424,7 @@ class Main {
usageError();
return false;
} else if (uflag) {
if ((mname != null) || (ename != null)) {
if ((mname != null) || (ename != null) || (pname != null)) {
/* just want to update the manifest */
return true;
} else {
......@@ -544,7 +558,7 @@ class Main {
|| (Mflag && isManifestEntry)) {
continue;
} else if (isManifestEntry && ((newManifest != null) ||
(ename != null))) {
(ename != null) || (pname != null))) {
foundManifest = true;
if (newManifest != null) {
// Don't read from the newManifest InputStream, as we
......@@ -563,7 +577,9 @@ class Main {
if (newManifest != null) {
old.read(newManifest);
}
updateManifest(old, zos);
if (!updateManifest(old, zos)) {
return false;
}
} else {
if (!entryMap.containsKey(name)) { // copy the old stuff
// do our own compression
......@@ -596,10 +612,14 @@ class Main {
Manifest m = new Manifest(newManifest);
updateOk = !isAmbiguousMainClass(m);
if (updateOk) {
updateManifest(m, zos);
if (!updateManifest(m, zos)) {
updateOk = false;
}
}
} else if (ename != null || pname != null) {
if (!updateManifest(new Manifest(), zos)) {
updateOk = false;
}
} else if (ename != null) {
updateManifest(new Manifest(), zos);
}
}
zis.close();
......@@ -623,7 +643,7 @@ class Main {
zos.closeEntry();
}
private void updateManifest(Manifest m, ZipOutputStream zos)
private boolean updateManifest(Manifest m, ZipOutputStream zos)
throws IOException
{
addVersion(m);
......@@ -631,6 +651,11 @@ class Main {
if (ename != null) {
addMainClass(m, ename);
}
if (pname != null) {
if (!addProfileName(m, pname)) {
return false;
}
}
ZipEntry e = new ZipEntry(MANIFEST_NAME);
e.setTime(System.currentTimeMillis());
if (flag0) {
......@@ -641,6 +666,7 @@ class Main {
if (vflag) {
output(getMsg("out.update.manifest"));
}
return true;
}
......@@ -687,6 +713,28 @@ class Main {
global.put(Attributes.Name.MAIN_CLASS, mainApp);
}
private boolean addProfileName(Manifest m, String profile) {
// check profile name
boolean found = false;
int i = 0;
while (i < PROFILES.length) {
if (profile.equals(PROFILES[i])) {
found = true;
break;
}
i++;
}
if (!found) {
error(formatMsg("error.bad.pvalue", profile));
return false;
}
// overrides any existing Profile attribute
Attributes global = m.getMainAttributes();
global.put(Attributes.Name.PROFILE, profile);
return true;
}
private boolean isAmbiguousMainClass(Manifest m) {
if (ename != null) {
Attributes global = m.getMainAttributes();
......
......@@ -36,6 +36,8 @@ error.bad.uflag=\
error.bad.eflag=\
'e' flag and manifest with the 'Main-Class' attribute cannot be specified \n\
together!
error.bad.pvalue=\
bad value for 'Profile' attribute: {0}
error.nosuch.fileordir=\
{0} : no such file or directory
error.write.file=\
......@@ -77,6 +79,7 @@ Options:\n\
\ \ -m include manifest information from specified manifest file\n\
\ \ -e specify application entry point for stand-alone application \n\
\ \ bundled into an executable jar file\n\
\ \ -p specify profile name\n\
\ \ -0 store only; use no ZIP compression\n\
\ \ -M do not create a manifest file for the entries\n\
\ \ -i generate index information for the specified jar files\n\
......
/*
* Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.net.*;
import java.io.File;
import java.util.jar.*;
/**
* Attempts to load classes or resources from a JAR file. The load should succeed
* if the runtime supports the profile indicated by the Profile attribute, fail
* with UnsupportedProfileException otherwise.
*/
public class Basic {
static int indexOf(String profile) {
if (profile == null || "compact1".equals(profile)) return 1;
if ("compact2".equals(profile)) return 2;
if ("compact3".equals(profile)) return 3;
if ("".equals(profile)) return 4;
return Integer.MAX_VALUE; // unknown profile name
}
public static void main(String[] args) throws Exception {
if (args.length < 2)
throw new RuntimeException("Usage: java <jarfile> <classname>");
String jar = args[0];
String cn = args[1];
File lib = new File(jar);
URL url = lib.toURI().toURL();
URL urls[] = { url };
// ## replace this if there is a standard way to determine the profile
String thisProfile = sun.misc.Version.profileName();
String jarProfile = null;
try (JarFile jf = new JarFile(lib)) {
Manifest manifest = jf.getManifest();
if (manifest != null) {
Attributes mainAttrs = manifest.getMainAttributes();
if (mainAttrs != null) {
jarProfile = mainAttrs.getValue(Attributes.Name.PROFILE);
}
}
}
boolean shouldFail = indexOf(thisProfile) < indexOf(jarProfile);
try (URLClassLoader cl = new URLClassLoader(urls)) {
System.out.format("Loading %s from %s ...%n", cn, jar);
Class<?> c = Class.forName(cn, true, cl);
System.out.println(c);
if (shouldFail)
throw new RuntimeException("UnsupportedProfileException expected");
} catch (UnsupportedProfileException x) {
if (!shouldFail)
throw x;
System.out.println("UnsupportedProfileException thrown as expected");
}
try (URLClassLoader cl = new URLClassLoader(urls)) {
System.out.format("Loading resource from %s ...%n", jar);
URL r = cl.findResource("META-INF/MANIFEST.MF");
System.out.println(r);
if (shouldFail)
throw new RuntimeException("UnsupportedProfileException expected");
} catch (UnsupportedProfileException x) {
if (!shouldFail)
throw x;
System.out.println("UnsupportedProfileException thrown as expected");
}
}
}
/*
* Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please 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 lib;
public class Lib {
private Lib() { }
public static void doSomething() { }
}
#
# Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
#
# This code is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 2 only, as
# published by the Free Software Foundation.
#
# This code is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
# version 2 for more details (a copy is included in the LICENSE file that
# accompanied this code).
#
# You should have received a copy of the GNU General Public License version
# 2 along with this work; if not, write to the Free Software Foundation,
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
#
# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
# or visit www.oracle.com if you need additional information or have any
# questions.
#
# @test
# @bug 8003255
# @compile -XDignore.symbol.file Basic.java Lib.java
# @summary Test that UnsupportedProfileException thrown when attempting to
# load classes or resources from a JAR file with the Profile attribute
# @run shell basic.sh
if [ -z "$TESTJAVA" ]; then
if [ $# -lt 1 ]; then exit 1; fi
TESTJAVA=$1; shift
COMPILEJAVA=$TESTJAVA
TESTSRC=`pwd`
TESTCLASSES=`pwd`
fi
echo "Creating GoodLib.jar ..."
echo "Profile: compact3" > good.mf
$COMPILEJAVA/bin/jar cvfm GoodLib.jar good.mf -C $TESTCLASSES lib
echo "Create BadLib.jar ..."
echo "Profile: badname" > bad.mf
$COMPILEJAVA/bin/jar cvfm BadLib.jar bad.mf -C $TESTCLASSES lib
# remove classes so that they aren't on the classpath
rm -rf $TESTCLASSES/lib
echo "Test with GoodLib.jar ..."
$TESTJAVA/bin/java -cp $TESTCLASSES Basic GoodLib.jar lib.Lib
echo "Test with BadLib.jar ..."
$TESTJAVA/bin/java -cp $TESTCLASSES Basic BadLib.jar lib.Lib
/*
* Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/* @test
* @bug 8003255
* @compile -XDignore.symbol.file AddAndUpdateProfile.java
* @run main AddAndUpdateProfile
* @summary Basic test of jar tool "p" option to add or update the Profile
* attribute in the main manifest of a JAR file
*/
import java.util.jar.*;
import static java.util.jar.Attributes.Name.*;
import java.nio.file.*;
import java.io.IOException;
import sun.tools.jar.Main;
public class AddAndUpdateProfile {
static boolean doJar(String... args) {
System.out.print("jar");
for (String arg: args)
System.out.print(" " + arg);
System.out.println("");
Main jartool = new Main(System.out, System.err, "jar");
return jartool.run(args);
}
static void jar(String... args) {
if (!doJar(args))
throw new RuntimeException("jar command failed");
}
static void jarExpectingFail(String... args) {
if (doJar(args))
throw new RuntimeException("jar command not expected to succeed");
}
static void checkMainAttribute(String jarfile, Attributes.Name name,
String expectedValue)
throws IOException
{
try (JarFile jf = new JarFile(jarfile)) {
Manifest mf = jf.getManifest();
if (mf == null && expectedValue != null)
throw new RuntimeException("Manifest not found");
if (mf != null) {
String actual = mf.getMainAttributes().getValue(name);
if (actual != null) {
if (!actual.equals(expectedValue))
throw new RuntimeException("Profile attribute has unexpected value");
} else {
if (expectedValue != null)
throw new RuntimeException("Profile attribute should not be present");
}
}
}
}
public static void main(String[] args) throws Exception {
Path entry = Files.createFile(Paths.get("xfoo"));
String jarfile = "xFoo.jar";
try {
// create JAR file with Profile attribute
jar("cfp", jarfile, "compact1", entry.toString());
checkMainAttribute(jarfile, PROFILE, "compact1");
// attempt to create JAR file with Profile attribute and bad value
jarExpectingFail("cfp", jarfile, "garbage", entry.toString());
jarExpectingFail("cfp", jarfile, "Compact1", entry.toString());
jarExpectingFail("cfp", jarfile, "COMPACT1", entry.toString());
// update value of Profile attribute
jar("ufp", jarfile, "compact2");
checkMainAttribute(jarfile, PROFILE, "compact2");
// attempt to update value of Profile attribute to bad value
// (update should not change the JAR file)
jarExpectingFail("ufp", jarfile, "garbage");
checkMainAttribute(jarfile, PROFILE, "compact2");
jarExpectingFail("ufp", jarfile, "COMPACT1");
checkMainAttribute(jarfile, PROFILE, "compact2");
// create JAR file with both a Main-Class and Profile attribute
jar("cfep", jarfile, "Foo", "compact1", entry.toString());
checkMainAttribute(jarfile, MAIN_CLASS, "Foo");
checkMainAttribute(jarfile, PROFILE, "compact1");
// update value of Profile attribute
jar("ufp", jarfile, "compact2");
checkMainAttribute(jarfile, PROFILE, "compact2");
// create JAR file without Profile attribute
jar("cf", jarfile, entry.toString());
checkMainAttribute(jarfile, PROFILE, null);
// update value of Profile attribute
jar("ufp", jarfile, "compact3");
checkMainAttribute(jarfile, PROFILE, "compact3");
} finally {
Files.deleteIfExists(Paths.get(jarfile));
Files.delete(entry);
}
}
}
/*
* Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/**
* @test
* @bug 8003255
* @compile -XDignore.symbol.file Basic.java Main.java Logging.java
* @run main Basic
* @summary Test the launcher checks the Profile attribute of executable JAR
* files. Also checks that libraries that specify the Profile attribute
* are not loaded if the runtime does not support the required profile.
*/
import java.io.*;
import java.util.jar.*;
import static java.util.jar.JarFile.MANIFEST_NAME;
import java.util.zip.*;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
public class Basic {
static final String MANIFEST_DIR = "META-INF/";
static final String JAVA_HOME = System.getProperty("java.home");
static final String OS_NAME = System.getProperty("os.name");
static final String OS_ARCH = System.getProperty("os.arch");
static final String JAVA_CMD =
OS_NAME.startsWith("Windows") ? "java.exe" : "java";
static final boolean NEED_D64 =
OS_NAME.equals("SunOS") &&
(OS_ARCH.equals("sparcv9") || OS_ARCH.equals("amd64"));
/**
* Creates a JAR file with the given attributes and the given entries.
* Class files are assumed to be in ${test.classes}. Note that this this
* method cannot use the "jar" tool as it may not be present in the image.
*/
static void createJarFile(String jarfile,
String mainAttributes,
String... entries)
throws IOException
{
// create Manifest
Manifest manifest = new Manifest();
Attributes jarAttrs = manifest.getMainAttributes();
jarAttrs.put(Attributes.Name.MANIFEST_VERSION, "1.0");
if (mainAttributes.length() > 0) {
for (String attr: mainAttributes.split(",")) {
String[] s = attr.split("=");
jarAttrs.put(new Attributes.Name(s[0]), s[1]);
}
}
try (OutputStream out = Files.newOutputStream(Paths.get(jarfile));
ZipOutputStream zos = new JarOutputStream(out))
{
// add manifest directory and manifest file
ZipEntry e = new JarEntry(MANIFEST_DIR);
e.setTime(System.currentTimeMillis());
e.setSize(0);
e.setCrc(0);
zos.putNextEntry(e);
e = new ZipEntry(MANIFEST_NAME);
e.setTime(System.currentTimeMillis());
zos.putNextEntry(e);
manifest.write(zos);
zos.closeEntry();
// entries in JAR file
for (String entry: entries) {
e = new JarEntry(entry);
Path path;
if (entry.endsWith(".class")) {
path = Paths.get(System.getProperty("test.classes"), entry);
} else {
path = Paths.get(entry);
}
BasicFileAttributes attrs =
Files.readAttributes(path, BasicFileAttributes.class);
e.setTime(attrs.lastModifiedTime().toMillis());
if (attrs.size() == 0) {
e.setMethod(ZipEntry.STORED);
e.setSize(0);
e.setCrc(0);
}
zos.putNextEntry(e);
if (attrs.isRegularFile())
Files.copy(path, zos);
zos.closeEntry();
}
}
}
/**
* Execute the given executable JAR file with the given arguments. This
* method blocks until the launched VM terminates. Any output or error
* message from the launched VM are printed to System.out. Returns the
* exit value.
*/
static int exec(String jf, String... args) throws IOException {
StringBuilder sb = new StringBuilder();
sb.append(Paths.get(JAVA_HOME, "bin", JAVA_CMD).toString());
if (NEED_D64)
sb.append(" -d64");
sb.append(" -jar ");
sb.append(Paths.get(jf).toAbsolutePath());
for (String arg: args) {
sb.append(' ');
sb.append(arg);
}
String[] cmd = sb.toString().split(" ");
ProcessBuilder pb = new ProcessBuilder(cmd);
pb.redirectErrorStream(true);
Process p = pb.start();
BufferedReader reader =
new BufferedReader(new InputStreamReader(p.getInputStream()));
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
try {
return p.waitFor();
} catch (InterruptedException e) {
throw new RuntimeException("Should not happen");
}
}
static void checkRun(String jf, String... args) throws IOException {
if (exec(jf) != 0)
throw new RuntimeException(jf + " failed!!!");
}
static void checkRunFail(String jf, String... args) throws IOException {
if (exec(jf) == 0)
throw new RuntimeException(jf + " did not fail!!!");
System.out.println("Failed as expected");
}
public static void main(String[] args) throws IOException {
// ## replace this if there is a standard way to determine the profile
String profile = sun.misc.Version.profileName();
int thisProfile = 4;
if ("compact1".equals(profile)) thisProfile = 1;
if ("compact2".equals(profile)) thisProfile = 2;
if ("compact3".equals(profile)) thisProfile = 3;
// "library" JAR file used by the test
createJarFile("Logging.jar", "", "Logging.class");
// Executable JAR file without the Profile attribute
if (thisProfile <= 3) {
createJarFile("Main.jar",
"Main-Class=Main,Class-Path=Logging.jar",
"Main.class");
checkRunFail("Main.jar");
}
// Executable JAR file with Profile attribute, Library JAR file without
for (int p=1; p<=3; p++) {
String attrs = "Main-Class=Main,Class-Path=Logging.jar" +
",Profile=compact" + p;
createJarFile("Main.jar", attrs, "Main.class");
if (p <= thisProfile) {
checkRun("Main.jar");
} else {
checkRunFail("Main.jar");
}
}
// Executable JAR file with Profile attribute that has invalid profile
// name, including incorrect case.
createJarFile("Main.jar",
"Main-Class=Main,Class-Path=Logging.jar,Profile=BadName",
"Main.class");
checkRunFail("Main.jar");
createJarFile("Main.jar",
"Main-Class=Main,Class-Path=Logging.jar,Profile=Compact1",
"Main.class");
checkRunFail("Main.jar");
// Executable JAR file and Librrary JAR file with Profile attribute
createJarFile("Main.jar",
"Main-Class=Main,Class-Path=Logging.jar,Profile=compact1",
"Main.class");
for (int p=1; p<=3; p++) {
String attrs = "Profile=compact" + p;
createJarFile("Logging.jar", attrs, "Logging.class");
if (p <= thisProfile) {
checkRun("Main.jar");
} else {
checkRunFail("Main.jar");
}
}
// Executable JAR file and Library JAR with Profile attribute, value
// of Profile not recognized
createJarFile("Logging.jar", "Profile=BadName", "Logging.class");
createJarFile("Main.jar",
"Main-Class=Main,Class-Path=Logging.jar,Profile=compact1",
"Main.class");
checkRunFail("Main.jar");
System.out.println("TEST PASSED.");
}
}
/*
* Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
public class Logging {
private Logging() { }
public static void log(String msg) {
System.out.println(msg);
}
}
/*
* Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
public class Main {
private Main() { }
public static void main(String[] args) {
Logging.log("main running");
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册