Skip to content
Draft
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 21 additions & 2 deletions src/main/java/org/apache/commons/io/input/ByteBufferCleaner.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

package org.apache.commons.io.input;

import java.lang.management.ManagementFactory;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.nio.ByteBuffer;
Expand Down Expand Up @@ -89,7 +90,7 @@ public void clean(final ByteBuffer buffer) throws ReflectiveOperationException {
*/
static void clean(final ByteBuffer buffer) {
try {
if (buffer.isDirect()) {
if (INSTANCE != null && buffer.isDirect()) {
Buffers.clearWritable(buffer);
INSTANCE.clean(buffer);
}
Expand All @@ -98,7 +99,10 @@ static void clean(final ByteBuffer buffer) {
}
}

private static Cleaner getCleaner() {
static Cleaner getCleaner() {
if (!unsafeMemoryAccessAllowed()) {
return null;
}
try {
return new Java8Cleaner();
} catch (final Exception e) {
Expand All @@ -110,6 +114,21 @@ private static Cleaner getCleaner() {
}
}

private static boolean unsafeMemoryAccessAllowed() {
final int version;
try {
version = Integer.parseInt(ManagementFactory.getRuntimeMXBean().getSpecVersion());
} catch (final RuntimeException e) {
return true;
}
if (version < 23) {
return true;
}
// see https://openjdk.org/jeps/471
return ManagementFactory.getRuntimeMXBean().getInputArguments().stream()
.anyMatch(arg -> arg.equals("--sun-misc-unsafe-memory-access=allow"));
}

/**
* Tests if were able to load a suitable cleaner for the current JVM. Attempting to call {@code ByteBufferCleaner#clean(ByteBuffer)} when this method
* returns false will result in an exception.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,25 @@
*/
package org.apache.commons.io.input;

import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.condition.JRE.JAVA_23;

import java.lang.management.ManagementFactory;
import java.lang.management.RuntimeMXBean;
import java.nio.ByteBuffer;
import java.util.Arrays;

import org.apache.commons.lang3.RandomUtils;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.EnabledForJreRange;
import org.junit.jupiter.api.condition.JRE;
import org.mockito.MockedStatic;
import org.mockito.Mockito;

/**
* Tests {@code ByteBufferCleaner}.
Expand All @@ -46,8 +59,38 @@ void testCleanFull() {
}

@Test
void testCleanNonDirectBuffer() {
assertDoesNotThrow(() -> ByteBufferCleaner.clean(ByteBuffer.allocate(10)));
}

@Test
@EnabledForJreRange(max = JAVA_23)
void testCleanNullBuffer() {
assertThrows(IllegalStateException.class, () -> ByteBufferCleaner.clean(null));
}

@Test
@EnabledForJreRange(max = JAVA_23)
void testSupported() {
assertTrue(ByteBufferCleaner.isSupported(), "ByteBufferCleaner does not work on this platform, please investigate and fix");
}

@Test
@EnabledForJreRange(min = JAVA_23)
void testUnsupportedByDefaultOnJava23() {
assertNull(ByteBufferCleaner.getCleaner());
assertFalse(ByteBufferCleaner.isSupported(), "ByteBufferCleaner does not work on this platform, please investigate and fix");
}

@Test
@EnabledForJreRange(min = JAVA_23)
void testSupportedIfUnsafeAllowedJava23() {
final RuntimeMXBean mockBean = Mockito.mock(RuntimeMXBean.class);
Mockito.when(mockBean.getSpecVersion()).thenReturn("23");
Mockito.when(mockBean.getInputArguments()).thenReturn(Arrays.asList("java", "--sun-misc-unsafe-memory-access=allow", "-version"));
try (final MockedStatic<ManagementFactory> managementFactory = Mockito.mockStatic(ManagementFactory.class)) {
managementFactory.when(ManagementFactory::getRuntimeMXBean).thenReturn(mockBean);
assertNotNull(ByteBufferCleaner.getCleaner());
}
}
}
Loading