Skip to content
5 changes: 5 additions & 0 deletions dubbo-common/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,11 @@
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
</dependency>
<!-- JCTools core -->
<dependency>
<groupId>org.jctools</groupId>
<artifactId>jctools-core</artifactId>
</dependency>

<dependency>
<groupId>cglib</groupId>
Expand Down
1,772 changes: 1,636 additions & 136 deletions dubbo-common/src/main/java/org/apache/dubbo/common/timer/HashedWheelTimer.java

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,27 @@
package org.apache.dubbo.common.timer;

import org.apache.dubbo.common.utils.NamedThreadFactory;
import org.apache.dubbo.common.utils.SystemPropertyConfigUtils;

import java.lang.ref.WeakReference;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import sun.misc.Unsafe;

import static org.awaitility.Awaitility.await;

Expand Down Expand Up @@ -171,4 +183,181 @@ void stopTaskTest() throws InterruptedException {
// this will throw an exception
Assertions.assertThrows(RuntimeException.class, () -> timer.newTimeout(new EmptyTask(), 5, TimeUnit.SECONDS));
}

@Test
@DisplayName("Check whether sun.misc.Unsafe (used by latest Netty HashedWheelTimer) could be compiled or not.")
void unsafeTest() {
// attempt to access field Unsafe#theUnsafe
final Object maybeUnsafe = AccessController.doPrivileged(new PrivilegedAction<Object>() {
@Override
public Object run() {
try {
final Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe");
// We always want to try using Unsafe as the access still works on java9 as well and
// we need it for out native-transports and many optimizations.
Throwable cause = ReflectionUtil.trySetAccessible(unsafeField, false);
if (cause != null) {
return cause;
}
// the unsafe instance
return unsafeField.get(null);
} catch (NoSuchFieldException | IllegalAccessException | SecurityException e) {
return e;
} catch (NoClassDefFoundError e) {
// Also catch NoClassDefFoundError in case someone uses for example OSGI and it made
// Unsafe unloadable.
return e;
}
}
});

Assertions.assertInstanceOf(Unsafe.class, maybeUnsafe);
}

private static final class ReflectionUtil {

private ReflectionUtil() {}

/**
* Try to call {@link AccessibleObject#setAccessible(boolean)} but will catch any {@link SecurityException} and
* {@link java.lang.reflect.InaccessibleObjectException} and return it.
* The caller must check if it returns {@code null} and if not handle the returned exception.
*/
public static Throwable trySetAccessible(AccessibleObject object, boolean checkAccessible) {
if (checkAccessible && !explicitTryReflectionSetAccessible0()) {
return new UnsupportedOperationException("Reflective setAccessible(true) disabled");
}
try {
object.setAccessible(true);
return null;
} catch (SecurityException e) {
return e;
} catch (RuntimeException e) {
return handleInaccessibleObjectException(e);
}
}

private static RuntimeException handleInaccessibleObjectException(RuntimeException e) {
// JDK 9 can throw an inaccessible object exception here; since Netty compiles
// against JDK 7 and this exception was only added in JDK 9, we have to weakly
// check the type
if ("java.lang.reflect.InaccessibleObjectException"
.equals(e.getClass().getName())) {
return e;
}
throw e;
}

private static Class<?> fail(Class<?> type, String typeParamName) {
throw new IllegalStateException(
"cannot determine the type of the type parameter '" + typeParamName + "': " + type);
}

/**
* Resolve a type parameter of a class that is a subclass of the given parametrized superclass.
*
* @param object The object to resolve the type parameter for
* @param parametrizedSuperclass The parametrized superclass
* @param typeParamName The name of the type parameter to resolve
* @return The resolved type parameter
* @throws IllegalStateException if the type parameter could not be resolved
*/
public static Class<?> resolveTypeParameter(
final Object object, Class<?> parametrizedSuperclass, String typeParamName) {
final Class<?> thisClass = object.getClass();
Class<?> currentClass = thisClass;
for (; ; ) {
if (currentClass.getSuperclass() == parametrizedSuperclass) {
int typeParamIndex = -1;
TypeVariable<?>[] typeParams = currentClass.getSuperclass().getTypeParameters();
for (int i = 0; i < typeParams.length; i++) {
if (typeParamName.equals(typeParams[i].getName())) {
typeParamIndex = i;
break;
}
}

if (typeParamIndex < 0) {
throw new IllegalStateException(
"unknown type parameter '" + typeParamName + "': " + parametrizedSuperclass);
}

Type genericSuperType = currentClass.getGenericSuperclass();
if (!(genericSuperType instanceof ParameterizedType)) {
return Object.class;
}

Type[] actualTypeParams = ((ParameterizedType) genericSuperType).getActualTypeArguments();

Type actualTypeParam = actualTypeParams[typeParamIndex];
if (actualTypeParam instanceof ParameterizedType) {
actualTypeParam = ((ParameterizedType) actualTypeParam).getRawType();
}
if (actualTypeParam instanceof Class) {
return (Class<?>) actualTypeParam;
}
if (actualTypeParam instanceof GenericArrayType) {
Type componentType = ((GenericArrayType) actualTypeParam).getGenericComponentType();
if (componentType instanceof ParameterizedType) {
componentType = ((ParameterizedType) componentType).getRawType();
}
if (componentType instanceof Class) {
return Array.newInstance((Class<?>) componentType, 0)
.getClass();
}
}
if (actualTypeParam instanceof TypeVariable) {
// Resolved type parameter points to another type parameter.
TypeVariable<?> v = (TypeVariable<?>) actualTypeParam;
if (!(v.getGenericDeclaration() instanceof Class)) {
return Object.class;
}

currentClass = thisClass;
parametrizedSuperclass = (Class<?>) v.getGenericDeclaration();
typeParamName = v.getName();
if (parametrizedSuperclass.isAssignableFrom(thisClass)) {
continue;
}
return Object.class;
}

return fail(thisClass, typeParamName);
}
currentClass = currentClass.getSuperclass();
if (currentClass == null) {
return fail(thisClass, typeParamName);
}
}
}

private static boolean explicitTryReflectionSetAccessible0() {
// we disable reflective access
return Boolean.parseBoolean(SystemPropertyConfigUtils.getSystemProperty(
"io.netty.tryReflectionSetAccessible",
String.valueOf(javaVersion() < 9 || RUNNING_IN_NATIVE_IMAGE)));
}

private static final boolean RUNNING_IN_NATIVE_IMAGE =
SystemPropertyConfigUtils.getSystemProperty("org.graalvm.nativeimage.imagecode") != null;

private static int javaVersion() {
return majorVersion(SystemPropertyConfigUtils.getSystemProperty("java.specification.version", "1.6"));
}

private 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];
}
}
}
}
8 changes: 8 additions & 0 deletions dubbo-dependencies-bom/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,7 @@
<test_container_version>1.21.0</test_container_version>
<hessian_lite_version>4.0.4</hessian_lite_version>
<swagger_version>1.6.16</swagger_version>
<jctools_version>4.0.5</jctools_version>

<snappy_java_version>1.1.10.7</snappy_java_version>
<bouncycastle-bcprov_version>1.81</bouncycastle-bcprov_version>
Expand Down Expand Up @@ -717,6 +718,13 @@
<version>${swagger_version}</version>
</dependency>

<!-- JCTools core -->
<dependency>
<groupId>org.jctools</groupId>
<artifactId>jctools-core</artifactId>
<version>${jctools_version}</version>
</dependency>

<!-- Test libs -->
<dependency>
<groupId>org.junit.jupiter</groupId>
Expand Down
4 changes: 4 additions & 0 deletions dubbo-distribution/dubbo-all-shaded/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -465,6 +465,10 @@
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
</dependency>
<dependency>
<groupId>org.jctools</groupId>
<artifactId>jctools-core</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
Expand Down
4 changes: 4 additions & 0 deletions dubbo-distribution/dubbo-all/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -465,6 +465,10 @@
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
</dependency>
<dependency>
<groupId>org.jctools</groupId>
<artifactId>jctools-core</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
*/
package org.apache.dubbo.remoting.buffer;

import java.nio.Buffer;
import java.nio.ByteBuffer;

import org.junit.jupiter.api.Assertions;
Expand Down Expand Up @@ -75,7 +76,8 @@ void testWrappedBuffer() {
channelBuffer = ChannelBuffers.wrappedBuffer(byteBuffer);
Assertions.assertTrue(channelBuffer instanceof ByteBufferBackedChannelBuffer);

byteBuffer.position(byteBuffer.limit());
// be compatible with jdk8 by casting byteBuffer's type to its parent class - `java.nio.Buffer`.
((Buffer) byteBuffer).position(byteBuffer.limit());
channelBuffer = ChannelBuffers.wrappedBuffer(byteBuffer);
Assertions.assertEquals(channelBuffer, EMPTY_BUFFER);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import org.apache.dubbo.common.utils.Assert;
import org.apache.dubbo.common.utils.CollectionUtils;

import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
Expand Down Expand Up @@ -67,7 +68,8 @@ public static int readRawVarint32(ByteBuffer byteBuffer) {
val = val << 7;
val = val | (byteBuffer.get(index) & 0x7F);
}
byteBuffer.position(currentPosition + varIntLength);
// be compatible with jdk8 by casting byteBuffer's type to its parent class - `java.nio.Buffer`.
((Buffer) byteBuffer).position(currentPosition + varIntLength);
return val;
}

Expand Down
17 changes: 1 addition & 16 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -811,22 +811,7 @@
</build>
</profile>

<profile>
<id>jdk9-compile</id>
<activation>
<jdk>[1.9,)</jdk>
</activation>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<release>8</release>
</configuration>
</plugin>
</plugins>
</build>
</profile>
<!-- does not set maven.compiler.release to avoid `Unsafe` compilation ERROR at jdk9+ -->

<profile>
<id>jdk9-jdk11-spotless</id>
Expand Down
Loading