This commit is contained in:
Jesper Wilhelmsson 2020-06-12 05:12:32 +02:00
commit 494f9667b4
7 changed files with 373 additions and 158 deletions

View file

@ -635,9 +635,7 @@ dd5198db2e5b1ebcafe065d987c03ba9fcb50fc3 jdk-15+17
7223c6d610343fd8323af9d07d501e01fa1a7696 jdk-15+22 7223c6d610343fd8323af9d07d501e01fa1a7696 jdk-15+22
f143729ca00ec14a98ea5c7f73acba88da97746e jdk-15+23 f143729ca00ec14a98ea5c7f73acba88da97746e jdk-15+23
497fd9f9129c4928fd5a876dd55e0daf6298b511 jdk-15+24 497fd9f9129c4928fd5a876dd55e0daf6298b511 jdk-15+24
58833044988772ca06c97ab2f142474a8627af80 jdk-15+25
58833044988772ca06c97ab2f142474a8627af80 jdk-15+25
90b266a84c06f1b3dc0ed8767856793e8c1c357e jdk-15+25 90b266a84c06f1b3dc0ed8767856793e8c1c357e jdk-15+25
0a32396f7a690015d22ca3328ac441a358295d90 jdk-15+26 0a32396f7a690015d22ca3328ac441a358295d90 jdk-15+26
506abc554caeb275928c02bf3a16e95d1978749f jdk-15+27
93813843680bbe1b7efbca56c03fd137f20a2c31 jdk-16+0 93813843680bbe1b7efbca56c03fd137f20a2c31 jdk-16+0
93813843680bbe1b7efbca56c03fd137f20a2c31 jdk-15+27

View file

@ -858,10 +858,18 @@ public abstract class Provider extends Properties {
// serviceMap changed since last call to getServices() // serviceMap changed since last call to getServices()
private volatile transient boolean servicesChanged; private volatile transient boolean servicesChanged;
// Map<String,String> used to keep track of legacy registration
private transient Map<String,String> legacyStrings;
// Map<ServiceKey,Service> // Map<ServiceKey,Service>
// used for services added via putService(), initialized on demand // used for services added via putService(), initialized on demand
private transient Map<ServiceKey,Service> serviceMap; private transient Map<ServiceKey,Service> serviceMap;
// For backward compatibility, the registration ordering of
// SecureRandom (RNG) algorithms needs to be preserved for
// "new SecureRandom()" calls when this provider is used
private transient Set<Service> prngServices;
// Map<ServiceKey,Service> // Map<ServiceKey,Service>
// used for services added via legacy methods, init on demand // used for services added via legacy methods, init on demand
private transient Map<ServiceKey,Service> legacyMap; private transient Map<ServiceKey,Service> legacyMap;
@ -913,12 +921,18 @@ public abstract class Provider extends Properties {
putAll(copy); putAll(copy);
} }
private static boolean isProviderInfo(Object key) { // check whether to update 'legacyString' with the specified key
private boolean checkLegacy(Object key) {
String keyString = (String)key; String keyString = (String)key;
if (keyString.startsWith("Provider.")) { if (keyString.startsWith("Provider.")) {
return true; return false;
} }
return false;
legacyChanged = true;
if (legacyStrings == null) {
legacyStrings = new LinkedHashMap<>();
}
return true;
} }
/** /**
@ -934,20 +948,20 @@ public abstract class Provider extends Properties {
private Object implRemove(Object key) { private Object implRemove(Object key) {
if (key instanceof String) { if (key instanceof String) {
if (isProviderInfo(key)) { if (!checkLegacy(key)) {
return null; return null;
} }
legacyChanged = true; legacyStrings.remove((String)key);
} }
return super.remove(key); return super.remove(key);
} }
private boolean implRemove(Object key, Object value) { private boolean implRemove(Object key, Object value) {
if (key instanceof String && value instanceof String) { if (key instanceof String && value instanceof String) {
if (isProviderInfo(key)) { if (!checkLegacy(key)) {
return false; return false;
} }
legacyChanged = true; legacyStrings.remove((String)key, (String)value);
} }
return super.remove(key, value); return super.remove(key, value);
} }
@ -955,20 +969,21 @@ public abstract class Provider extends Properties {
private boolean implReplace(Object key, Object oldValue, Object newValue) { private boolean implReplace(Object key, Object oldValue, Object newValue) {
if ((key instanceof String) && (oldValue instanceof String) && if ((key instanceof String) && (oldValue instanceof String) &&
(newValue instanceof String)) { (newValue instanceof String)) {
if (isProviderInfo(key)) { if (!checkLegacy(key)) {
return false; return false;
} }
legacyChanged = true; legacyStrings.replace((String)key, (String)oldValue,
(String)newValue);
} }
return super.replace(key, oldValue, newValue); return super.replace(key, oldValue, newValue);
} }
private Object implReplace(Object key, Object value) { private Object implReplace(Object key, Object value) {
if ((key instanceof String) && (value instanceof String)) { if ((key instanceof String) && (value instanceof String)) {
if (isProviderInfo(key)) { if (!checkLegacy(key)) {
return null; return null;
} }
legacyChanged = true; legacyStrings.replace((String)key, (String)value);
} }
return super.replace(key, value); return super.replace(key, value);
} }
@ -977,17 +992,26 @@ public abstract class Provider extends Properties {
private void implReplaceAll(BiFunction<? super Object, ? super Object, private void implReplaceAll(BiFunction<? super Object, ? super Object,
? extends Object> function) { ? extends Object> function) {
legacyChanged = true; legacyChanged = true;
if (legacyStrings == null) {
legacyStrings = new LinkedHashMap<>();
} else {
legacyStrings.replaceAll((BiFunction<? super String, ? super String,
? extends String>) function);
}
super.replaceAll(function); super.replaceAll(function);
} }
@SuppressWarnings("unchecked") // Function must actually operate over strings @SuppressWarnings("unchecked") // Function must actually operate over strings
private Object implMerge(Object key, Object value, BiFunction<? super Object, private Object implMerge(Object key, Object value,
? super Object, ? extends Object> remappingFunction) { BiFunction<? super Object, ? super Object, ? extends Object>
remappingFunction) {
if ((key instanceof String) && (value instanceof String)) { if ((key instanceof String) && (value instanceof String)) {
if (isProviderInfo(key)) { if (!checkLegacy(key)) {
return null; return null;
} }
legacyChanged = true; legacyStrings.merge((String)key, (String)value,
(BiFunction<? super String, ? super String,
? extends String>) remappingFunction);
} }
return super.merge(key, value, remappingFunction); return super.merge(key, value, remappingFunction);
} }
@ -996,10 +1020,12 @@ public abstract class Provider extends Properties {
private Object implCompute(Object key, BiFunction<? super Object, private Object implCompute(Object key, BiFunction<? super Object,
? super Object, ? extends Object> remappingFunction) { ? super Object, ? extends Object> remappingFunction) {
if (key instanceof String) { if (key instanceof String) {
if (isProviderInfo(key)) { if (!checkLegacy(key)) {
return null; return null;
} }
legacyChanged = true; legacyStrings.compute((String) key,
(BiFunction<? super String,? super String,
? extends String>) remappingFunction);
} }
return super.compute(key, remappingFunction); return super.compute(key, remappingFunction);
} }
@ -1008,10 +1034,12 @@ public abstract class Provider extends Properties {
private Object implComputeIfAbsent(Object key, Function<? super Object, private Object implComputeIfAbsent(Object key, Function<? super Object,
? extends Object> mappingFunction) { ? extends Object> mappingFunction) {
if (key instanceof String) { if (key instanceof String) {
if (isProviderInfo(key)) { if (!checkLegacy(key)) {
return null; return null;
} }
legacyChanged = true; legacyStrings.computeIfAbsent((String) key,
(Function<? super String, ? extends String>)
mappingFunction);
} }
return super.computeIfAbsent(key, mappingFunction); return super.computeIfAbsent(key, mappingFunction);
} }
@ -1020,35 +1048,40 @@ public abstract class Provider extends Properties {
private Object implComputeIfPresent(Object key, BiFunction<? super Object, private Object implComputeIfPresent(Object key, BiFunction<? super Object,
? super Object, ? extends Object> remappingFunction) { ? super Object, ? extends Object> remappingFunction) {
if (key instanceof String) { if (key instanceof String) {
if (isProviderInfo(key)) { if (!checkLegacy(key)) {
return null; return null;
} }
legacyChanged = true; legacyStrings.computeIfPresent((String) key,
(BiFunction<? super String, ? super String,
? extends String>) remappingFunction);
} }
return super.computeIfPresent(key, remappingFunction); return super.computeIfPresent(key, remappingFunction);
} }
private Object implPut(Object key, Object value) { private Object implPut(Object key, Object value) {
if ((key instanceof String) && (value instanceof String)) { if ((key instanceof String) && (value instanceof String)) {
if (isProviderInfo(key)) { if (!checkLegacy(key)) {
return null; return null;
} }
legacyChanged = true; legacyStrings.put((String)key, (String)value);
} }
return super.put(key, value); return super.put(key, value);
} }
private Object implPutIfAbsent(Object key, Object value) { private Object implPutIfAbsent(Object key, Object value) {
if ((key instanceof String) && (value instanceof String)) { if ((key instanceof String) && (value instanceof String)) {
if (isProviderInfo(key)) { if (!checkLegacy(key)) {
return null; return null;
} }
legacyChanged = true; legacyStrings.putIfAbsent((String)key, (String)value);
} }
return super.putIfAbsent(key, value); return super.putIfAbsent(key, value);
} }
private void implClear() { private void implClear() {
if (legacyStrings != null) {
legacyStrings.clear();
}
if (legacyMap != null) { if (legacyMap != null) {
legacyMap.clear(); legacyMap.clear();
} }
@ -1056,6 +1089,7 @@ public abstract class Provider extends Properties {
legacyChanged = false; legacyChanged = false;
servicesChanged = false; servicesChanged = false;
serviceSet = null; serviceSet = null;
prngServices = null;
super.clear(); super.clear();
putId(); putId();
} }
@ -1095,7 +1129,7 @@ public abstract class Provider extends Properties {
* service objects. * service objects.
*/ */
private void ensureLegacyParsed() { private void ensureLegacyParsed() {
if (legacyChanged == false) { if (legacyChanged == false || (legacyStrings == null)) {
return; return;
} }
serviceSet = null; serviceSet = null;
@ -1104,7 +1138,7 @@ public abstract class Provider extends Properties {
} else { } else {
legacyMap.clear(); legacyMap.clear();
} }
for (Map.Entry<?,?> entry : super.entrySet()) { for (Map.Entry<String,String> entry : legacyStrings.entrySet()) {
parseLegacyPut(entry.getKey(), entry.getValue()); parseLegacyPut(entry.getKey(), entry.getValue());
} }
removeInvalidServices(legacyMap); removeInvalidServices(legacyMap);
@ -1125,12 +1159,12 @@ public abstract class Provider extends Properties {
} }
} }
private String[] getTypeAndAlgorithm(String key) { private static String[] getTypeAndAlgorithm(String key) {
int i = key.indexOf('.'); int i = key.indexOf('.');
if (i < 1) { if (i < 1) {
if (debug != null) { if (debug != null) {
debug.println("Ignoring invalid entry in provider " debug.println("Ignoring invalid entry in provider: "
+ name + ":" + key); + key);
} }
return null; return null;
} }
@ -1143,15 +1177,7 @@ public abstract class Provider extends Properties {
private static final String ALIAS_PREFIX_LOWER = "alg.alias."; private static final String ALIAS_PREFIX_LOWER = "alg.alias.";
private static final int ALIAS_LENGTH = ALIAS_PREFIX.length(); private static final int ALIAS_LENGTH = ALIAS_PREFIX.length();
private void parseLegacyPut(Object k, Object v) { private void parseLegacyPut(String name, String value) {
if (!(k instanceof String) || !(v instanceof String)) {
return;
}
String name = (String) k;
String value = (String) v;
if (isProviderInfo(name)) {
return;
}
if (name.toLowerCase(ENGLISH).startsWith(ALIAS_PREFIX_LOWER)) { if (name.toLowerCase(ENGLISH).startsWith(ALIAS_PREFIX_LOWER)) {
// e.g. put("Alg.Alias.MessageDigest.SHA", "SHA-1"); // e.g. put("Alg.Alias.MessageDigest.SHA", "SHA-1");
// aliasKey ~ MessageDigest.SHA // aliasKey ~ MessageDigest.SHA
@ -1193,6 +1219,10 @@ public abstract class Provider extends Properties {
legacyMap.put(key, s); legacyMap.put(key, s);
} }
s.className = className; s.className = className;
if (type.equals("SecureRandom")) {
updateSecureRandomEntries(true, s);
}
} else { // attribute } else { // attribute
// e.g. put("MessageDigest.SHA-1 ImplementedIn", "Software"); // e.g. put("MessageDigest.SHA-1 ImplementedIn", "Software");
String attributeValue = value; String attributeValue = value;
@ -1352,9 +1382,46 @@ public abstract class Provider extends Properties {
servicesChanged = true; servicesChanged = true;
synchronized (this) { synchronized (this) {
putPropertyStrings(s); putPropertyStrings(s);
if (type.equals("SecureRandom")) {
updateSecureRandomEntries(true, s);
}
} }
} }
private void updateSecureRandomEntries(boolean doAdd, Service s) {
Objects.requireNonNull(s);
if (doAdd) {
if (prngServices == null) {
prngServices = new LinkedHashSet<Service>();
}
prngServices.add(s);
} else {
prngServices.remove(s);
}
if (debug != null) {
debug.println((doAdd? "Add":"Remove") + " SecureRandom algo " +
s.getAlgorithm());
}
}
// used by new SecureRandom() to find out the default SecureRandom
// service for this provider
synchronized Service getDefaultSecureRandomService() {
checkInitialized();
if (legacyChanged) {
prngServices = null;
ensureLegacyParsed();
}
if (prngServices != null && !prngServices.isEmpty()) {
return prngServices.iterator().next();
}
return null;
}
/** /**
* Put the string properties for this Service in this Provider's * Put the string properties for this Service in this Provider's
* Hashtable. * Hashtable.
@ -1448,6 +1515,9 @@ public abstract class Provider extends Properties {
} }
synchronized (this) { synchronized (this) {
removePropertyStrings(s); removePropertyStrings(s);
if (type.equals("SecureRandom")) {
updateSecureRandomEntries(false, s);
}
} }
} }

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 1996, 2019, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -259,35 +259,51 @@ public class SecureRandom extends java.util.Random {
} }
private void getDefaultPRNG(boolean setSeed, byte[] seed) { private void getDefaultPRNG(boolean setSeed, byte[] seed) {
String prng = getPrngAlgorithm(); Service prngService = null;
if (prng == null) { String prngAlgorithm = null;
// bummer, get the SUN implementation for (Provider p : Providers.getProviderList().providers()) {
prng = "SHA1PRNG"; // SUN provider uses the SunEntries.DEF_SECURE_RANDOM_ALGO
// as the default SecureRandom algorithm; for other providers,
// Provider.getDefaultSecureRandom() will use the 1st
// registered SecureRandom algorithm
if (p.getName().equals("SUN")) {
prngAlgorithm = SunEntries.DEF_SECURE_RANDOM_ALGO;
prngService = p.getService("SecureRandom", prngAlgorithm);
break;
} else {
prngService = p.getDefaultSecureRandomService();
if (prngService != null) {
prngAlgorithm = prngService.getAlgorithm();
break;
}
}
}
// per javadoc, if none of the Providers support a RNG algorithm,
// then an implementation-specific default is returned.
if (prngService == null) {
prngAlgorithm = "SHA1PRNG";
this.secureRandomSpi = new sun.security.provider.SecureRandom(); this.secureRandomSpi = new sun.security.provider.SecureRandom();
this.provider = Providers.getSunProvider(); this.provider = Providers.getSunProvider();
if (setSeed) {
this.secureRandomSpi.engineSetSeed(seed);
}
} else { } else {
try { try {
SecureRandom random = SecureRandom.getInstance(prng); this.secureRandomSpi = (SecureRandomSpi)
this.secureRandomSpi = random.getSecureRandomSpi(); prngService.newInstance(null);
this.provider = random.getProvider(); this.provider = prngService.getProvider();
if (setSeed) {
this.secureRandomSpi.engineSetSeed(seed);
}
} catch (NoSuchAlgorithmException nsae) { } catch (NoSuchAlgorithmException nsae) {
// never happens, because we made sure the algorithm exists // should not happen
throw new RuntimeException(nsae); throw new RuntimeException(nsae);
} }
} }
if (setSeed) {
this.secureRandomSpi.engineSetSeed(seed);
}
// JDK 1.1 based implementations subclass SecureRandom instead of // JDK 1.1 based implementations subclass SecureRandom instead of
// SecureRandomSpi. They will also go through this code path because // SecureRandomSpi. They will also go through this code path because
// they must call a SecureRandom constructor as it is their superclass. // they must call a SecureRandom constructor as it is their superclass.
// If we are dealing with such an implementation, do not set the // If we are dealing with such an implementation, do not set the
// algorithm value as it would be inaccurate. // algorithm value as it would be inaccurate.
if (getClass() == SecureRandom.class) { if (getClass() == SecureRandom.class) {
this.algorithm = prng; this.algorithm = prngAlgorithm;
} }
} }
@ -620,13 +636,6 @@ public class SecureRandom extends java.util.Random {
instance.provider, algorithm); instance.provider, algorithm);
} }
/**
* Returns the {@code SecureRandomSpi} of this {@code SecureRandom} object.
*/
SecureRandomSpi getSecureRandomSpi() {
return secureRandomSpi;
}
/** /**
* Returns the provider of this {@code SecureRandom} object. * Returns the provider of this {@code SecureRandom} object.
* *
@ -868,30 +877,6 @@ public class SecureRandom extends java.util.Random {
return retVal; return retVal;
} }
/**
* Gets a default PRNG algorithm by looking through all registered
* providers. Returns the first PRNG algorithm of the first provider that
* has registered a {@code SecureRandom} implementation, or null if none of
* the registered providers supplies a {@code SecureRandom} implementation.
*/
private static String getPrngAlgorithm() {
for (Provider p : Providers.getProviderList().providers()) {
// For SUN provider, we use SunEntries.DEFF_SECURE_RANDOM_ALGO
// as the default SecureRandom algorithm; for other providers,
// we continue to iterate through to the 1st SecureRandom
// service
if (p.getName().equals("SUN")) {
return SunEntries.DEF_SECURE_RANDOM_ALGO;
}
for (Service s : p.getServices()) {
if (s.getType().equals("SecureRandom")) {
return s.getAlgorithm();
}
}
}
return null;
}
/* /*
* Lazily initialize since Pattern.compile() is heavy. * Lazily initialize since Pattern.compile() is heavy.
* Effective Java (2nd Edition), Item 71. * Effective Java (2nd Edition), Item 71.

View file

@ -484,6 +484,16 @@ class StandardBundlerParam<T> extends BundlerParamInfo<T> {
PREDEFINED_RUNTIME_IMAGE.getID())); PREDEFINED_RUNTIME_IMAGE.getID()));
} }
if (Platform.isMac()) {
// On Mac topImage can be runtime root or runtime home.
Path runtimeHome = topImage.toPath().resolve("Contents/Home");
if (Files.isDirectory(runtimeHome)) {
// topImage references runtime root, adjust it to pick data from
// runtime home
topImage = runtimeHome.toFile();
}
}
// copy whole runtime, need to skip jmods and src.zip // copy whole runtime, need to skip jmods and src.zip
final List<String> excludes = Arrays.asList("jmods", "src.zip"); final List<String> excludes = Arrays.asList("jmods", "src.zip");
IOUtils.copyRecursive(topImage.toPath(), IOUtils.copyRecursive(topImage.toPath(),

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -22,33 +22,96 @@
*/ */
import static java.lang.System.out; import static java.lang.System.out;
import java.security.Provider;
import java.security.Security;
import java.security.SecureRandom; import java.security.SecureRandom;
import java.security.Provider.Service;
import java.util.Objects;
import java.util.Arrays;
import sun.security.provider.SunEntries; import sun.security.provider.SunEntries;
/** /**
* @test * @test
* @bug 8228613 * @bug 8228613 8246613
* @summary Ensure that the default SecureRandom algo matches * @summary Ensure that the default SecureRandom algo used is based
* SunEntries.DEF_SECURE_RANDOM_ALGO when SUN provider is used * on the registration ordering, and falls to next provider
* if none are found
* @modules java.base/sun.security.provider * @modules java.base/sun.security.provider
*/ */
public class DefaultAlgo { public class DefaultAlgo {
public static void main(String[] args) throws Exception { public static void main(String[] args) throws Exception {
SecureRandom sr = new SecureRandom(); String[] algos = { "A", "B", "C" };
String actualAlg = sr.getAlgorithm(); test3rdParty(algos);
out.println("Default SecureRandom algo: " + actualAlg); // reverse the order and re-check
if (sr.getProvider().getName().equals("SUN")) { String[] algosReversed = { "C", "B", "A" };
// when using Sun provider, compare and check if the algorithm test3rdParty(algosReversed);
// matches SunEntries.DEF_SECURE_RANDOM_ALGO }
if (actualAlg.equals(SunEntries.DEF_SECURE_RANDOM_ALGO)) {
out.println("Test Passed"); private static void test3rdParty(String[] algos) {
} else { Provider[] provs = {
throw new RuntimeException("Failed: Expected " + new SampleLegacyProvider(algos),
new SampleServiceProvider(algos)
};
for (Provider p : provs) {
checkDefault(p, algos);
}
}
// validate the specified SecureRandom obj to be from the specified
// provider and matches the specified algorithm
private static void validate(SecureRandom sr, String pName, String algo) {
if (!sr.getProvider().getName().equals(pName)) {
throw new RuntimeException("Failed provider check, exp: " +
pName + ", got " + sr.getProvider().getName());
}
if (!sr.getAlgorithm().equals(algo)) {
throw new RuntimeException("Failed algo check, exp: " +
algo + ", got " + sr.getAlgorithm());
}
}
private static void checkDefault(Provider p, String ... algos) {
out.println(p.getName() + " with " + Arrays.toString(algos));
int pos = Security.insertProviderAt(p, 1);
String pName = p.getName();
boolean isLegacy = pName.equals("SampleLegacy");
try {
if (isLegacy) {
for (String s : algos) {
validate(new SecureRandom(), pName, s);
p.remove("SecureRandom." + s);
out.println("removed " + s);
}
validate(new SecureRandom(), "SUN",
SunEntries.DEF_SECURE_RANDOM_ALGO); SunEntries.DEF_SECURE_RANDOM_ALGO);
} else {
validate(new SecureRandom(), pName, algos[0]);
}
out.println("=> Test Passed");
} finally {
if (pos != -1) {
Security.removeProvider(p.getName());
}
}
}
private static class SampleLegacyProvider extends Provider {
SampleLegacyProvider(String[] listOfSupportedRNGs) {
super("SampleLegacy", "1.0", "test provider using legacy put");
for (String s : listOfSupportedRNGs) {
put("SecureRandom." + s, "sun.security.provider.SecureRandom");
}
}
}
private static class SampleServiceProvider extends Provider {
SampleServiceProvider(String[] listOfSupportedRNGs) {
super("SampleService", "1.0", "test provider using putService");
for (String s : listOfSupportedRNGs) {
putService(new Provider.Service(this, "SecureRandom", s,
"sun.security.provider.SecureRandom", null, null));
} }
} else {
out.println("Skip test for non-Sun provider: " + sr.getProvider());
} }
} }
} }

View file

@ -303,59 +303,6 @@ public final class BasicTest {
HelloApp.executeLauncherAndVerifyOutput(cmd); HelloApp.executeLauncherAndVerifyOutput(cmd);
} }
@Parameter("Hello")
@Parameter("com.foo/com.foo.main.Aloha")
@Test
public void testJLinkRuntime(String javaAppDesc) throws IOException {
JavaAppDesc appDesc = JavaAppDesc.parse(javaAppDesc);
JPackageCommand cmd = JPackageCommand.helloAppImage(appDesc);
final String moduleName = appDesc.moduleName();
if (moduleName != null) {
// Build module jar.
cmd.executePrerequisiteActions();
}
final Path runtimeDir = TKit.createTempDirectory("runtime").resolve("data");
// List of modules required for test app.
final var modules = new String[] {
"java.base",
"java.desktop"
};
Executor jlink = getToolProvider(JavaTool.JLINK)
.saveOutput(false)
.addArguments(
"--add-modules", String.join(",", modules),
"--output", runtimeDir.toString(),
"--strip-debug",
"--no-header-files",
"--no-man-pages");
TKit.trace("jlink output BEGIN");
try (Stream<Path> paths = Files.walk(runtimeDir)) {
paths.filter(Files::isRegularFile)
.map(runtimeDir::relativize)
.map(Path::toString)
.forEach(TKit::trace);
}
TKit.trace("jlink output END");
if (moduleName != null) {
jlink.addArguments("--add-modules", moduleName, "--module-path",
Path.of(cmd.getArgumentValue("--module-path")).resolve(
"hello.jar").toString());
}
jlink.execute();
cmd.addArguments("--runtime-image", runtimeDir);
cmd.executeAndAssertHelloAppImageCreated();
}
private static Executor getJPackageToolProvider() { private static Executor getJPackageToolProvider() {
return getToolProvider(JavaTool.JPACKAGE); return getToolProvider(JavaTool.JPACKAGE);
} }

View file

@ -0,0 +1,142 @@
/*
* Copyright (c) 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
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.jpackage.tests;
import java.io.IOException;
import java.nio.file.Files;
import java.util.Collection;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;
import java.nio.file.Path;
import jdk.jpackage.test.Annotations.Parameters;
import jdk.jpackage.test.Annotations.Test;
import jdk.jpackage.test.Executor;
import jdk.jpackage.test.JPackageCommand;
import jdk.jpackage.test.JavaAppDesc;
import jdk.jpackage.test.JavaTool;
import jdk.jpackage.test.TKit;
/*
* @test
* @summary test '--runtime-image' option of jpackage
* @library ../../../../helpers
* @build jdk.jpackage.test.*
* @modules jdk.incubator.jpackage/jdk.incubator.jpackage.internal
* @compile CookedRuntimeTest.java
* @run main/othervm/timeout=360 -Xmx512m jdk.jpackage.test.Main
* --jpt-run=jdk.jpackage.tests.CookedRuntimeTest
*/
public final class CookedRuntimeTest {
public CookedRuntimeTest(String javaAppDesc, String jlinkOutputSubdir,
String runtimeSubdir) {
this.javaAppDesc = javaAppDesc;
this.jlinkOutputSubdir = Path.of(jlinkOutputSubdir);
this.runtimeSubdir = Path.of(runtimeSubdir);
}
@Test
public void test() throws IOException {
JavaAppDesc appDesc = JavaAppDesc.parse(javaAppDesc);
JPackageCommand cmd = JPackageCommand.helloAppImage(appDesc);
final String moduleName = appDesc.moduleName();
if (moduleName != null) {
// Build module jar.
cmd.executePrerequisiteActions();
}
final Path workDir = TKit.createTempDirectory("runtime").resolve("data");
final Path jlinkOutputDir = workDir.resolve(jlinkOutputSubdir);
Files.createDirectories(jlinkOutputDir.getParent());
// List of modules required for test app.
final var modules = new String[] {
"java.base",
"java.desktop"
};
Executor jlink = new Executor()
.setToolProvider(JavaTool.JLINK)
.dumpOutput()
.addArguments(
"--add-modules", String.join(",", modules),
"--output", jlinkOutputDir.toString(),
"--strip-debug",
"--no-header-files",
"--no-man-pages");
if (moduleName != null) {
jlink.addArguments("--add-modules", moduleName, "--module-path",
Path.of(cmd.getArgumentValue("--module-path")).resolve(
"hello.jar").toString());
}
jlink.execute();
TKit.trace("jlink output BEGIN");
try (Stream<Path> paths = Files.walk(jlinkOutputDir)) {
paths.filter(Files::isRegularFile)
.map(jlinkOutputDir::relativize)
.map(Path::toString)
.forEach(TKit::trace);
}
TKit.trace("jlink output END");
cmd.setArgumentValue("--runtime-image", workDir.resolve(runtimeSubdir));
cmd.executeAndAssertHelloAppImageCreated();
}
@Parameters
public static Collection data() {
final List<String> javaAppDescs = List.of("Hello",
"com.foo/com.foo.main.Aloha");
final List<String[]> paths = new ArrayList<>();
paths.add(new String[] { "", "" });
if (TKit.isOSX()) {
// On OSX jpackage should accept both runtime root and runtime home
// directories.
paths.add(new String[] { "Contents/Home", "" });
}
List<Object[]> data = new ArrayList<>();
for (var javaAppDesc : javaAppDescs) {
for (var pathCfg : paths) {
data.add(new Object[] { javaAppDesc, pathCfg[0], pathCfg[1] });
}
}
return data;
}
private final String javaAppDesc;
private final Path jlinkOutputSubdir;
private final Path runtimeSubdir;
}