Acquire Java version simply
Motivation: The Java version is used for platform dependent logic. Yet, the logic for acquiring the Java version requires special permissions (the runtime permission "getClassLoader") that some downstream projects will never grant. As such, these projects are doomed to have Netty act is their Java major version is six. While there are ways to maintain the same logic without requiring these special permissions, the logic is needlessly complicated because it relies on loading classes that exist in version n but not version n - 1. This complexity can be removed. As a bonanza, the dangerous permission is no longer required. Modifications: Rather than attempting to load classes that exist in version n but not in version n - 1, we can just parse the Java specification version. This only requires a begign property (property permission "java.specification.version") and is simple. Result: Acquisition of the Java version is safe and simple.
This commit is contained in:
parent
3d7ae97359
commit
e00b797936
@ -38,12 +38,13 @@ import java.net.InetSocketAddress;
|
||||
import java.net.ServerSocket;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
import java.util.Deque;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentLinkedDeque;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
@ -1018,51 +1019,48 @@ public final class PlatformDependent {
|
||||
return false;
|
||||
}
|
||||
|
||||
@SuppressWarnings("LoopStatementThatDoesntLoop")
|
||||
private static int javaVersion0() {
|
||||
int javaVersion;
|
||||
final int majorVersion;
|
||||
|
||||
// Not really a loop
|
||||
for (;;) {
|
||||
// Android
|
||||
if (isAndroid()) {
|
||||
javaVersion = 6;
|
||||
break;
|
||||
}
|
||||
|
||||
try {
|
||||
Method getVersion = java.lang.Runtime.class.getMethod("version");
|
||||
Object version = getVersion.invoke(null);
|
||||
javaVersion = (Integer) version.getClass().getMethod("major").invoke(version);
|
||||
break;
|
||||
} catch (Throwable ignored) {
|
||||
// Ignore
|
||||
}
|
||||
|
||||
try {
|
||||
Class.forName("java.time.Clock", false, getClassLoader(Object.class));
|
||||
javaVersion = 8;
|
||||
break;
|
||||
} catch (Throwable ignored) {
|
||||
// Ignore
|
||||
}
|
||||
|
||||
try {
|
||||
Class.forName("java.util.concurrent.LinkedTransferQueue", false, getClassLoader(BlockingQueue.class));
|
||||
javaVersion = 7;
|
||||
break;
|
||||
} catch (Throwable ignored) {
|
||||
// Ignore
|
||||
}
|
||||
|
||||
javaVersion = 6;
|
||||
break;
|
||||
if (isAndroid()) {
|
||||
majorVersion = 6;
|
||||
} else {
|
||||
majorVersion = majorVersionFromJavaSpecificationVersion();
|
||||
}
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Java version: {}", javaVersion);
|
||||
logger.debug("Java version: {}", majorVersion);
|
||||
|
||||
return majorVersion;
|
||||
}
|
||||
|
||||
static int majorVersionFromJavaSpecificationVersion() {
|
||||
try {
|
||||
final String javaSpecVersion = AccessController.doPrivileged(new PrivilegedAction<String>() {
|
||||
@Override
|
||||
public String run() {
|
||||
return System.getProperty("java.specification.version");
|
||||
}
|
||||
});
|
||||
return majorVersion(javaSpecVersion);
|
||||
} catch (SecurityException e) {
|
||||
logger.debug("security exception while reading java.specification.version", e);
|
||||
return 6;
|
||||
}
|
||||
}
|
||||
|
||||
static int majorVersion(final String javaSpecVersion) {
|
||||
final String[] components = javaSpecVersion.split("\\.");
|
||||
final int[] version = new int[components.length];
|
||||
for (int i = 0; i < components.length; i++) {
|
||||
version[i] = Integer.parseInt(components[i]);
|
||||
}
|
||||
|
||||
if (version[0] == 1) {
|
||||
assert version[1] >= 6;
|
||||
return version[1];
|
||||
} else {
|
||||
return version[0];
|
||||
}
|
||||
return javaVersion;
|
||||
}
|
||||
|
||||
private static boolean hasUnsafe0() {
|
||||
|
@ -17,6 +17,7 @@ package io.netty.util.internal;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.security.Permission;
|
||||
import java.util.Random;
|
||||
|
||||
import static io.netty.util.internal.PlatformDependent.hashCodeAscii;
|
||||
@ -129,4 +130,40 @@ public class PlatformDependentTest {
|
||||
hashCodeAscii(string));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMajorVersionFromJavaSpecificationVersion() {
|
||||
final SecurityManager current = System.getSecurityManager();
|
||||
|
||||
try {
|
||||
System.setSecurityManager(new SecurityManager() {
|
||||
@Override
|
||||
public void checkPropertyAccess(String key) {
|
||||
if (key.equals("java.specification.version")) {
|
||||
// deny
|
||||
throw new SecurityException(key);
|
||||
}
|
||||
}
|
||||
|
||||
// so we can restore the security manager
|
||||
@Override
|
||||
public void checkPermission(Permission perm) {
|
||||
}
|
||||
});
|
||||
|
||||
assertEquals(6, PlatformDependent.majorVersionFromJavaSpecificationVersion());
|
||||
} finally {
|
||||
System.setSecurityManager(current);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMajorVersion() {
|
||||
assertEquals(6, PlatformDependent.majorVersion("1.6"));
|
||||
assertEquals(7, PlatformDependent.majorVersion("1.7"));
|
||||
assertEquals(8, PlatformDependent.majorVersion("1.8"));
|
||||
assertEquals(8, PlatformDependent.majorVersion("8"));
|
||||
assertEquals(9, PlatformDependent.majorVersion("1.9")); // early version of JDK 9 before Project Verona
|
||||
assertEquals(9, PlatformDependent.majorVersion("9"));
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user