Skip to content

Commit 42de9d4

Browse files
jasontedornormanmaurer
authored andcommitted
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.
1 parent 9ba6480 commit 42de9d4

File tree

2 files changed

+96
-37
lines changed

2 files changed

+96
-37
lines changed

common/src/main/java/io/netty/util/internal/PlatformDependent.java

Lines changed: 35 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -38,12 +38,13 @@
3838
import java.net.ServerSocket;
3939
import java.nio.ByteBuffer;
4040
import java.nio.ByteOrder;
41+
import java.security.AccessController;
42+
import java.security.PrivilegedAction;
4143
import java.util.Deque;
4244
import java.util.List;
4345
import java.util.Locale;
4446
import java.util.Map;
4547
import java.util.Queue;
46-
import java.util.concurrent.BlockingQueue;
4748
import java.util.concurrent.ConcurrentHashMap;
4849
import java.util.concurrent.ConcurrentLinkedDeque;
4950
import java.util.concurrent.ConcurrentMap;
@@ -773,51 +774,48 @@ private static boolean isRoot0() {
773774
return false;
774775
}
775776

776-
@SuppressWarnings("LoopStatementThatDoesntLoop")
777777
private static int javaVersion0() {
778-
int javaVersion;
778+
final int majorVersion;
779779

780-
// Not really a loop
781-
for (;;) {
782-
// Android
783-
if (isAndroid()) {
784-
javaVersion = 6;
785-
break;
786-
}
780+
if (isAndroid()) {
781+
majorVersion = 6;
782+
} else {
783+
majorVersion = majorVersionFromJavaSpecificationVersion();
784+
}
787785

788-
try {
789-
Method getVersion = java.lang.Runtime.class.getMethod("version");
790-
Object version = getVersion.invoke(null);
791-
javaVersion = (Integer) version.getClass().getMethod("major").invoke(version);
792-
break;
793-
} catch (Throwable ignored) {
794-
// Ignore
795-
}
786+
logger.debug("Java version: {}", majorVersion);
796787

797-
try {
798-
Class.forName("java.time.Clock", false, getClassLoader(Object.class));
799-
javaVersion = 8;
800-
break;
801-
} catch (Throwable ignored) {
802-
// Ignore
803-
}
788+
return majorVersion;
789+
}
804790

805-
try {
806-
Class.forName("java.util.concurrent.LinkedTransferQueue", false, getClassLoader(BlockingQueue.class));
807-
javaVersion = 7;
808-
break;
809-
} catch (Throwable ignored) {
810-
// Ignore
811-
}
791+
static int majorVersionFromJavaSpecificationVersion() {
792+
try {
793+
final String javaSpecVersion = AccessController.doPrivileged(new PrivilegedAction<String>() {
794+
@Override
795+
public String run() {
796+
return System.getProperty("java.specification.version");
797+
}
798+
});
799+
return majorVersion(javaSpecVersion);
800+
} catch (SecurityException e) {
801+
logger.debug("security exception while reading java.specification.version", e);
802+
return 6;
803+
}
804+
}
812805

813-
javaVersion = 6;
814-
break;
806+
static int majorVersion(final String javaSpecVersion) {
807+
final String[] components = javaSpecVersion.split("\\.");
808+
final int[] version = new int[components.length];
809+
for (int i = 0; i < components.length; i++) {
810+
version[i] = Integer.parseInt(components[i]);
815811
}
816812

817-
if (logger.isDebugEnabled()) {
818-
logger.debug("Java version: {}", javaVersion);
813+
if (version[0] == 1) {
814+
assert version[1] >= 6;
815+
return version[1];
816+
} else {
817+
return version[0];
819818
}
820-
return javaVersion;
821819
}
822820

823821
private static boolean hasUnsafe0() {
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/*
2+
* Copyright 2015 The Netty Project
3+
*
4+
* The Netty Project licenses this file to you under the Apache License,
5+
* version 2.0 (the "License"); you may not use this file except in compliance
6+
* with the License. You may obtain a copy of the License at:
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12+
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13+
* License for the specific language governing permissions and limitations
14+
* under the License.
15+
*/
16+
package io.netty.util.internal;
17+
18+
import org.junit.Test;
19+
20+
import java.security.Permission;
21+
22+
import static org.junit.Assert.assertEquals;
23+
24+
public class PlatformDependentTest {
25+
26+
@Test
27+
public void testMajorVersionFromJavaSpecificationVersion() {
28+
final SecurityManager current = System.getSecurityManager();
29+
30+
try {
31+
System.setSecurityManager(new SecurityManager() {
32+
@Override
33+
public void checkPropertyAccess(String key) {
34+
if (key.equals("java.specification.version")) {
35+
// deny
36+
throw new SecurityException(key);
37+
}
38+
}
39+
40+
// so we can restore the security manager
41+
@Override
42+
public void checkPermission(Permission perm) {
43+
}
44+
});
45+
46+
assertEquals(6, PlatformDependent.majorVersionFromJavaSpecificationVersion());
47+
} finally {
48+
System.setSecurityManager(current);
49+
}
50+
}
51+
52+
@Test
53+
public void testMajorVersion() {
54+
assertEquals(6, PlatformDependent.majorVersion("1.6"));
55+
assertEquals(7, PlatformDependent.majorVersion("1.7"));
56+
assertEquals(8, PlatformDependent.majorVersion("1.8"));
57+
assertEquals(8, PlatformDependent.majorVersion("8"));
58+
assertEquals(9, PlatformDependent.majorVersion("1.9")); // early version of JDK 9 before Project Verona
59+
assertEquals(9, PlatformDependent.majorVersion("9"));
60+
}
61+
}

0 commit comments

Comments
 (0)