Skip to content

Commit 394755b

Browse files
authored
GH-1007: fix: does not break class loading if direct buffer allocator is not available (#1008)
## What's Changed The Direct Buffer is not always needed to use Arrow memory, however, we cannot load MemoryUtil class if we don't set: ``` --add-opens=java.base/java.nio=org.apache.arrow.memory.core,ALL-UNNAMED ``` Which is not always needed/possible. This fix proposes to catch the `InaccessibleObjectException` to not avoiding the load of the class. The directBuffer is, in any case not available and a `UnsupportedOperationException` will be throw as it is in the existing code Closes #1007 .
1 parent 39b0593 commit 394755b

File tree

2 files changed

+26
-14
lines changed

2 files changed

+26
-14
lines changed

memory/memory-core/src/main/java/org/apache/arrow/memory/util/MemoryUtil.java

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
import java.lang.reflect.Constructor;
2020
import java.lang.reflect.Field;
21+
import java.lang.reflect.InaccessibleObjectException;
2122
import java.lang.reflect.InvocationTargetException;
2223
import java.nio.ByteBuffer;
2324
import java.nio.ByteOrder;
@@ -81,9 +82,18 @@ public Object run() {
8182
BYTE_ARRAY_BASE_OFFSET = UNSAFE.arrayBaseOffset(byte[].class);
8283

8384
// get the offset of the address field in a java.nio.Buffer object
85+
long maybeOffset;
8486
Field addressField = java.nio.Buffer.class.getDeclaredField("address");
85-
addressField.setAccessible(true);
86-
BYTE_BUFFER_ADDRESS_OFFSET = UNSAFE.objectFieldOffset(addressField);
87+
try {
88+
addressField.setAccessible(true);
89+
maybeOffset = UNSAFE.objectFieldOffset(addressField);
90+
} catch (InaccessibleObjectException e) {
91+
maybeOffset = -1;
92+
logger.debug(
93+
"Cannot access the address field of java.nio.Buffer. DirectBuffer operations wont be available",
94+
e);
95+
}
96+
BYTE_BUFFER_ADDRESS_OFFSET = maybeOffset;
8797

8898
Constructor<?> directBufferConstructor;
8999
long address = -1;
@@ -109,6 +119,9 @@ public Object run() {
109119
} catch (SecurityException e) {
110120
logger.debug("Cannot get constructor for direct buffer allocation", e);
111121
return e;
122+
} catch (InaccessibleObjectException e) {
123+
logger.debug("Cannot get constructor for direct buffer allocation", e);
124+
return e;
112125
}
113126
}
114127
});
@@ -156,7 +169,11 @@ public Object run() {
156169
* @return address of the underlying memory.
157170
*/
158171
public static long getByteBufferAddress(ByteBuffer buf) {
159-
return UNSAFE.getLong(buf, BYTE_BUFFER_ADDRESS_OFFSET);
172+
if (BYTE_BUFFER_ADDRESS_OFFSET != -1) {
173+
return UNSAFE.getLong(buf, BYTE_BUFFER_ADDRESS_OFFSET);
174+
}
175+
throw new UnsupportedOperationException(
176+
"Byte buffer address cannot be obtained because sun.misc.Unsafe or java.nio.DirectByteBuffer.<init>(long, int) is not available");
160177
}
161178

162179
private MemoryUtil() {}

memory/memory-core/src/test/java/org/apache/arrow/memory/TestOpens.java

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -20,32 +20,27 @@
2020
import static org.junit.jupiter.api.Assertions.assertTrue;
2121
import static org.junit.jupiter.api.condition.JRE.JAVA_16;
2222

23+
import org.apache.arrow.memory.util.MemoryUtil;
2324
import org.junit.jupiter.api.Test;
2425
import org.junit.jupiter.api.condition.EnabledForJreRange;
2526

2627
public class TestOpens {
27-
/** Instantiating the RootAllocator should poke MemoryUtil and fail. */
28+
/** Accessing MemoryUtil.directBuffer should fail as add-opens is not configured. */
2829
@Test
2930
@EnabledForJreRange(min = JAVA_16)
3031
public void testMemoryUtilFailsLoudly() {
3132
// This test is configured by Maven to run WITHOUT add-opens. So this should fail on JDK16+
3233
// (where JEP396 means that add-opens is required to access JDK internals).
3334
// The test will likely fail in your IDE if it doesn't correctly pick this up.
34-
Throwable e =
35-
assertThrows(
36-
Throwable.class,
37-
() -> {
38-
BufferAllocator allocator = new RootAllocator();
39-
allocator.close();
40-
});
35+
Throwable e = assertThrows(Throwable.class, () -> MemoryUtil.directBuffer(0, 10));
4136
boolean found = false;
4237
while (e != null) {
43-
e = e.getCause();
44-
if (e instanceof RuntimeException
45-
&& e.getMessage().contains("Failed to initialize MemoryUtil")) {
38+
if (e instanceof UnsupportedOperationException
39+
&& e.getMessage().contains("java.nio.DirectByteBuffer.<init>(long, int) not available")) {
4640
found = true;
4741
break;
4842
}
43+
e = e.getCause();
4944
}
5045
assertTrue(found, "Expected exception was not thrown");
5146
}

0 commit comments

Comments
 (0)