This is an automated email from the ASF dual-hosted git repository.

richox pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/auron.git


The following commit(s) were added to refs/heads/master by this push:
     new 52139936 [AURON-1332] Introduce JniBridge and AuronAdaptor for 
auron-core module (#1333)
52139936 is described below

commit 52139936ca4712de6b964b4ff674f97f1287d318
Author: zhangmang <[email protected]>
AuthorDate: Sun Sep 28 17:47:37 2025 +0800

    [AURON-1332] Introduce JniBridge and AuronAdaptor for auron-core module 
(#1333)
    
    * [AURON-1332] Introduce JniBridge and AuronAdaptor for common module
    
    * [AURON #1332] Introduce JniBridge and AuronAdaptor for auron-core module
    
    * remove pom dependency
    
    * add AuronTestUtils
    
    * fix scala verison
    
    * add auron prefix
---
 auron-core/pom.xml                                 |  38 +++++
 .../auron/configuration/AuronConfiguration.java    |  16 ++
 .../java/org/apache/auron/jni/AuronAdaptor.java    | 122 ++++++++++++++
 .../apache/auron/jni/AuronCallNativeWrapper.java   | 187 +++++++++++++++++++++
 .../main/java/org/apache/auron/jni/JniBridge.java  |  99 +++++++++++
 .../apache/auron/memory/OnHeapSpillManager.java    | 125 ++++++++++++++
 .../java/org/apache/auron/metric/MetricNode.java   |  50 ++++++
 .../org/apache/auron/jni/AuronAdaptorTest.java     |  41 +++++
 .../org/apache/auron/jni/MockAuronAdaptor.java     |  35 ++++
 9 files changed, 713 insertions(+)

diff --git a/auron-core/pom.xml b/auron-core/pom.xml
index fd07f7b0..67d6cfc9 100644
--- a/auron-core/pom.xml
+++ b/auron-core/pom.xml
@@ -37,6 +37,44 @@
       <version>1.3.9</version>
     </dependency>
 
+    <dependency>
+      <groupId>org.apache.arrow</groupId>
+      <artifactId>arrow-c-data</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.arrow</groupId>
+      <artifactId>arrow-compression</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.arrow</groupId>
+      <artifactId>arrow-memory-unsafe</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.arrow</groupId>
+      <artifactId>arrow-vector</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.auron</groupId>
+      <artifactId>proto</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.auron</groupId>
+      <artifactId>hadoop-shim_${scalaVersion}</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.hadoop</groupId>
+      <artifactId>hadoop-client-api</artifactId>
+      <scope>provided</scope>
+    </dependency>
+
     <dependency>
       <groupId>junit</groupId>
       <artifactId>junit</artifactId>
diff --git 
a/auron-core/src/main/java/org/apache/auron/configuration/AuronConfiguration.java
 
b/auron-core/src/main/java/org/apache/auron/configuration/AuronConfiguration.java
index 33861fa8..cd04569e 100644
--- 
a/auron-core/src/main/java/org/apache/auron/configuration/AuronConfiguration.java
+++ 
b/auron-core/src/main/java/org/apache/auron/configuration/AuronConfiguration.java
@@ -23,6 +23,22 @@ import java.util.Optional;
  */
 public abstract class AuronConfiguration {
 
+    public static final ConfigOption<Integer> BATCH_SIZE = 
ConfigOptions.key("auron.batchSize")
+            .description("Suggested batch size for arrow batches.")
+            .intType()
+            .defaultValue(10000);
+
+    public static final ConfigOption<Double> MEMORY_FRACTION = 
ConfigOptions.key("auron.memoryFraction")
+            .description("Suggested fraction of off-heap memory used in native 
execution. "
+                    + "actual off-heap memory usage is expected to be 
spark.executor.memoryOverhead * fraction.")
+            .doubleType()
+            .defaultValue(0.6);
+
+    public static final ConfigOption<String> NATIVE_LOG_LEVEL = 
ConfigOptions.key("auron.native.log.level")
+            .description("Log level for native execution.")
+            .stringType()
+            .defaultValue("info");
+
     public abstract <T> Optional<T> getOptional(ConfigOption<T> option);
 
     public abstract <T> Optional<T> getOptional(String key);
diff --git a/auron-core/src/main/java/org/apache/auron/jni/AuronAdaptor.java 
b/auron-core/src/main/java/org/apache/auron/jni/AuronAdaptor.java
new file mode 100644
index 00000000..4c1d27b8
--- /dev/null
+++ b/auron-core/src/main/java/org/apache/auron/jni/AuronAdaptor.java
@@ -0,0 +1,122 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.auron.jni;
+
+import java.io.File;
+import java.io.IOException;
+import org.apache.auron.configuration.AuronConfiguration;
+import org.apache.auron.memory.OnHeapSpillManager;
+
+/**
+ * Auron Adaptor abstraction for Auron Native engine.
+ * Each engine must implement its own {@link AuronAdaptor} to interact with 
the Auron Native engine.
+ */
+public abstract class AuronAdaptor {
+
+    /**
+     * The static instance of the AuronAdaptor.
+     */
+    private static AuronAdaptor INSTANCE = null;
+
+    /**
+     * Initializes a static instance of the AuronAdaptor.
+     *
+     * @param auronAdaptor The implementation of AuronAdaptor to be set as the 
static instance.
+     */
+    public static synchronized void initInstance(AuronAdaptor auronAdaptor) {
+        if (INSTANCE == null) {
+            INSTANCE = auronAdaptor;
+        }
+    }
+
+    /**
+     * Retrieves the static instance of the AuronAdaptor.
+     *
+     * @return The current AuronAdaptor instance, or null if none has been set.
+     */
+    public static AuronAdaptor getInstance() {
+        return INSTANCE;
+    }
+
+    /**
+     * Loads the native Auron library (libauron.dylib or equivalent).
+     * <p>This method must be implemented by subclasses to provide the native 
library loading logic.</p>
+     */
+    public abstract void loadAuronLib();
+
+    /**
+     * Retrieves the limit for JVM total memory usage.
+     *
+     * @return The maximum allowed memory usage (in bytes), defaulting to 
Long.MAX_VALUE.
+     */
+    public long getJVMTotalMemoryLimited() {
+        return Long.MAX_VALUE;
+    }
+
+    /**
+     * Checks if a task is currently running, false to abort native 
computation.
+     *
+     * @return true if a task is running, false otherwise. Default 
implementation returns true.
+     */
+    public boolean isTaskRunning() {
+        return true;
+    }
+
+    /**
+     * Creates a temporary file for direct write spill-to-disk operations.
+     *
+     * @return Absolute path of the created temporary file.
+     * @throws IOException If the temporary file cannot be created.
+     */
+    public String getDirectWriteSpillToDiskFile() throws IOException {
+        File tempFile = File.createTempFile("auron-spill-", ".tmp");
+        tempFile.deleteOnExit();
+        return tempFile.getAbsolutePath();
+    }
+
+    /**
+     * Retrieves the context classloader of the current thread.
+     *
+     * @return The context classloader of the current thread.
+     */
+    public Object getThreadContext() {
+        return Thread.currentThread().getContextClassLoader();
+    }
+
+    /**
+     * Sets the context classloader for the current thread.
+     *
+     * @param context The classloader to set as the context classloader.
+     */
+    public void setThreadContext(Object context) {
+        Thread.currentThread().setContextClassLoader((ClassLoader) context);
+    }
+
+    /**
+     * Retrieves the on-heap spill manager implementation.
+     *
+     * @return An instance of OnHeapSpillManager, defaulting to a disabled 
manager.
+     */
+    public OnHeapSpillManager getOnHeapSpillManager() {
+        return OnHeapSpillManager.getDisabledOnHeapSpillManager();
+    }
+
+    /**
+     * Retrieves the AuronConfiguration, It bundles the corresponding engine's 
Config.
+     */
+    public abstract AuronConfiguration getAuronConfiguration();
+}
diff --git 
a/auron-core/src/main/java/org/apache/auron/jni/AuronCallNativeWrapper.java 
b/auron-core/src/main/java/org/apache/auron/jni/AuronCallNativeWrapper.java
new file mode 100644
index 00000000..cbdf1f86
--- /dev/null
+++ b/auron-core/src/main/java/org/apache/auron/jni/AuronCallNativeWrapper.java
@@ -0,0 +1,187 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.auron.jni;
+
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Consumer;
+import org.apache.arrow.c.ArrowArray;
+import org.apache.arrow.c.ArrowSchema;
+import org.apache.arrow.c.CDataDictionaryProvider;
+import org.apache.arrow.c.Data;
+import org.apache.arrow.memory.BufferAllocator;
+import org.apache.arrow.vector.VectorSchemaRoot;
+import org.apache.arrow.vector.types.pojo.Schema;
+import org.apache.auron.configuration.AuronConfiguration;
+import org.apache.auron.metric.MetricNode;
+import org.apache.auron.protobuf.PartitionId;
+import org.apache.auron.protobuf.PhysicalPlanNode;
+import org.apache.auron.protobuf.TaskDefinition;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class AuronCallNativeWrapper {
+
+    private static final Logger LOG = 
LoggerFactory.getLogger(AuronCallNativeWrapper.class);
+
+    private final PhysicalPlanNode nativePlan;
+    private final MetricNode metrics;
+    private final BufferAllocator arrowAllocator;
+    private final int partitionId;
+    private final int stageId;
+    private final int taskId;
+    private final AtomicReference<Throwable> error = new 
AtomicReference<>(null);
+    private final CDataDictionaryProvider dictionaryProvider = new 
CDataDictionaryProvider();
+    private Schema arrowSchema;
+    private long nativeRuntimePtr;
+    private Consumer<VectorSchemaRoot> batchConsumer;
+
+    // initialize native environment
+    static {
+        LOG.info("Initializing native environment (batchSize="
+                + 
AuronAdaptor.getInstance().getAuronConfiguration().get(AuronConfiguration.BATCH_SIZE)
 + ", "
+                + "memoryFraction="
+                + 
AuronAdaptor.getInstance().getAuronConfiguration().get(AuronConfiguration.MEMORY_FRACTION)
 + ")");
+
+        // arrow configuration
+        System.setProperty("arrow.struct.conflict.policy", "CONFLICT_APPEND");
+
+        // preload JNI bridge classes
+        try {
+            Class.forName("org.apache.auron.jni.JniBridge");
+        } catch (ClassNotFoundException e) {
+            throw new RuntimeException("Cannot load JniBridge class", e);
+        }
+
+        AuronAdaptor.getInstance().loadAuronLib();
+        Runtime.getRuntime().addShutdownHook(new Thread(JniBridge::onExit));
+    }
+
+    public AuronCallNativeWrapper(
+            BufferAllocator arrowAllocator,
+            PhysicalPlanNode nativePlan,
+            MetricNode metrics,
+            int partitionId,
+            int stageId,
+            int taskId,
+            long nativeMemory) {
+        this.arrowAllocator = arrowAllocator;
+        this.nativePlan = nativePlan;
+        this.metrics = metrics;
+        this.partitionId = partitionId;
+        this.stageId = stageId;
+        this.taskId = taskId;
+
+        LOG.warn("Start executing native plan");
+        this.nativeRuntimePtr = JniBridge.callNative(
+                nativeMemory,
+                
AuronAdaptor.getInstance().getAuronConfiguration().get(AuronConfiguration.NATIVE_LOG_LEVEL),
+                this);
+    }
+
+    /**
+     * Loads and processes the next batch of data from the native plan.
+     * <p>
+     * This method attempts to fetch the next batch of data from the native 
execution runtime.
+     * If successful, it passes the data to the provided consumer callback. If 
the native runtime
+     * pointer is invalid (0), or if there are no more batches, the method 
returns false.
+     * </p>
+     *
+     * @param batchConsumer The consumer lambda that processes the loaded data 
batch.
+     *            This lambda receives a `VectorSchemaRoot` containing the 
batch data.
+     * @return true If a batch was successfully loaded and processed; false if 
no more batches are available.
+     * @throws RuntimeException If the native runtime encounters an error 
during batch processing.
+     */
+    public boolean loadNextBatch(Consumer<VectorSchemaRoot> batchConsumer) {
+        // load next batch
+        try {
+            this.batchConsumer = batchConsumer;
+            if (nativeRuntimePtr != 0 && 
JniBridge.nextBatch(nativeRuntimePtr)) {
+                return true;
+            }
+        } finally {
+            // if error has been set, throw set error instead of this caught 
exception
+            checkError();
+        }
+
+        // if no more batches, close the native runtime and return false
+        close();
+        return false;
+    }
+
+    protected MetricNode getMetrics() {
+        return metrics;
+    }
+
+    protected void importSchema(long ffiSchemaPtr) {
+        try (ArrowSchema ffiSchema = ArrowSchema.wrap(ffiSchemaPtr)) {
+            arrowSchema = Data.importSchema(arrowAllocator, ffiSchema, 
dictionaryProvider);
+        }
+    }
+
+    protected void importBatch(long ffiArrayPtr) {
+        if (nativeRuntimePtr == 0) {
+            throw new RuntimeException("Native runtime is finalized");
+        }
+
+        try (ArrowArray ffiArray = ArrowArray.wrap(ffiArrayPtr)) {
+            try (VectorSchemaRoot root = VectorSchemaRoot.create(arrowSchema, 
arrowAllocator)) {
+                Data.importIntoVectorSchemaRoot(arrowAllocator, ffiArray, 
root, dictionaryProvider);
+                batchConsumer.accept(root);
+            }
+        }
+    }
+
+    protected void setError(Throwable error) {
+        this.error.set(error);
+    }
+
+    protected void checkError() {
+        Throwable throwable = error.getAndSet(null);
+        if (throwable != null) {
+            close();
+            throw new RuntimeException(throwable);
+        }
+    }
+
+    protected byte[] getRawTaskDefinition() {
+        PartitionId partition = PartitionId.newBuilder()
+                .setPartitionId(partitionId)
+                .setStageId(stageId)
+                .setTaskId(taskId)
+                .build();
+
+        TaskDefinition taskDefinition = TaskDefinition.newBuilder()
+                .setTaskId(partition)
+                .setPlan(nativePlan)
+                .build();
+
+        return taskDefinition.toByteArray();
+    }
+
+    private synchronized void close() {
+        if (nativeRuntimePtr != 0) {
+            JniBridge.finalizeNative(nativeRuntimePtr);
+            nativeRuntimePtr = 0;
+            try {
+                dictionaryProvider.close();
+            } catch (Exception e) {
+                LOG.error("Error closing dictionary provider", e);
+            }
+            checkError();
+        }
+    }
+}
diff --git a/auron-core/src/main/java/org/apache/auron/jni/JniBridge.java 
b/auron-core/src/main/java/org/apache/auron/jni/JniBridge.java
new file mode 100644
index 00000000..85831550
--- /dev/null
+++ b/auron-core/src/main/java/org/apache/auron/jni/JniBridge.java
@@ -0,0 +1,99 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.auron.jni;
+
+import java.io.IOException;
+import java.lang.management.BufferPoolMXBean;
+import java.lang.management.ManagementFactory;
+import java.net.URI;
+import java.util.List;
+import java.util.concurrent.ConcurrentHashMap;
+import org.apache.auron.hadoop.fs.FSDataInputWrapper;
+import org.apache.auron.hadoop.fs.FSDataOutputWrapper;
+import org.apache.auron.memory.OnHeapSpillManager;
+import org.apache.hadoop.fs.FileSystem;
+import org.apache.hadoop.fs.Path;
+
+/**
+ * This class is the entry point for the JNI bridge.
+ */
+@SuppressWarnings("unused")
+public class JniBridge {
+    public static final ConcurrentHashMap<String, Object> resourcesMap = new 
ConcurrentHashMap<>();
+
+    private static final List<BufferPoolMXBean> directMXBeans =
+            ManagementFactory.getPlatformMXBeans(BufferPoolMXBean.class);
+
+    public static native long callNative(long initNativeMemory, String 
logLevel, AuronCallNativeWrapper wrapper);
+
+    public static native boolean nextBatch(long ptr);
+
+    public static native void finalizeNative(long ptr);
+
+    public static native void onExit();
+
+    public static ClassLoader getContextClassLoader() {
+        return Thread.currentThread().getContextClassLoader();
+    }
+
+    public static void setContextClassLoader(ClassLoader cl) {
+        Thread.currentThread().setContextClassLoader(cl);
+    }
+
+    public static Object getResource(String key) {
+        return resourcesMap.remove(key);
+    }
+
+    public static FSDataInputWrapper openFileAsDataInputWrapper(FileSystem fs, 
String path) throws Exception {
+        // the path is a URI string, so we need to convert it to a URI object
+        return FSDataInputWrapper.wrap(fs.open(new Path(new URI(path))));
+    }
+
+    public static FSDataOutputWrapper createFileAsDataOutputWrapper(FileSystem 
fs, String path) throws Exception {
+        return FSDataOutputWrapper.wrap(fs.create(new Path(new URI(path))));
+    }
+
+    public static long getDirectMemoryUsed() {
+        return directMXBeans.stream()
+                .mapToLong(BufferPoolMXBean::getTotalCapacity)
+                .sum();
+    }
+
+    public static OnHeapSpillManager getTaskOnHeapSpillManager() {
+        return AuronAdaptor.getInstance().getOnHeapSpillManager();
+    }
+
+    public static long getTotalMemoryLimited() {
+        return AuronAdaptor.getInstance().getJVMTotalMemoryLimited();
+    }
+
+    public static boolean isTaskRunning() {
+        return AuronAdaptor.getInstance().isTaskRunning();
+    }
+
+    public static Object getThreadContext() {
+        return AuronAdaptor.getInstance().getThreadContext();
+    }
+
+    public static void setThreadContext(Object tc) {
+        AuronAdaptor.getInstance().setThreadContext(tc);
+    }
+
+    public static String getDirectWriteSpillToDiskFile() throws IOException {
+        return AuronAdaptor.getInstance().getDirectWriteSpillToDiskFile();
+    }
+}
diff --git 
a/auron-core/src/main/java/org/apache/auron/memory/OnHeapSpillManager.java 
b/auron-core/src/main/java/org/apache/auron/memory/OnHeapSpillManager.java
new file mode 100644
index 00000000..5f98f8e1
--- /dev/null
+++ b/auron-core/src/main/java/org/apache/auron/memory/OnHeapSpillManager.java
@@ -0,0 +1,125 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.auron.memory;
+
+import java.nio.ByteBuffer;
+
+/**
+ * Interface for managing on-heap spill operations.
+ * This interface provides methods to handle memory spilling to disk when 
on-heap memory is insufficient.
+ */
+public abstract class OnHeapSpillManager {
+
+    /**
+     * Check if on-heap memory is available for allocation.
+     *
+     * @return true if on-heap memory is available, false otherwise
+     */
+    abstract boolean isOnHeapAvailable();
+
+    /**
+     * Create a new spill operation and return its identifier.
+     *
+     * @return spill identifier for the newly created spill
+     */
+    abstract int newSpill();
+
+    /**
+     * Write data from a ByteBuffer to the spill identified by spillId.
+     *
+     * @param spillId the identifier of the spill to write to
+     * @param buffer the ByteBuffer containing data to be written
+     */
+    abstract void writeSpill(int spillId, ByteBuffer buffer);
+
+    /**
+     * Read data from the spill identified by spillId into the provided 
ByteBuffer.
+     *
+     * @param spillId the identifier of the spill to read from
+     * @param buffer the ByteBuffer to read data into
+     * @return the number of bytes actually read
+     */
+    abstract int readSpill(int spillId, ByteBuffer buffer);
+
+    /**
+     * Get the disk usage in bytes for the spill identified by spillId.
+     *
+     * @param spillId the identifier of the spill
+     * @return the disk usage in bytes
+     */
+    abstract long getSpillDiskUsage(int spillId);
+
+    /**
+     * Get the total disk I/O time in nanoseconds for the spill identified by 
spillId.
+     *
+     * @param spillId the identifier of the spill
+     * @return the disk I/O time in nanoseconds
+     */
+    abstract long getSpillDiskIOTime(int spillId);
+
+    /**
+     * Release and clean up resources associated with the spill identified by 
spillId.
+     *
+     * @param spillId the identifier of the spill to release
+     */
+    abstract void releaseSpill(int spillId);
+
+    /**
+     * Get the disabled on-heap spill manager instance.
+     *
+     * @return the disabled on-heap spill manager instance
+     */
+    public static OnHeapSpillManager getDisabledOnHeapSpillManager() {
+        return new OnHeapSpillManager() {
+
+            @Override
+            boolean isOnHeapAvailable() {
+                return false;
+            }
+
+            @Override
+            int newSpill() {
+                throw new UnsupportedOperationException();
+            }
+
+            @Override
+            void writeSpill(int spillId, ByteBuffer buffer) {
+                throw new UnsupportedOperationException();
+            }
+
+            @Override
+            int readSpill(int spillId, ByteBuffer buffer) {
+                throw new UnsupportedOperationException();
+            }
+
+            @Override
+            long getSpillDiskUsage(int spillId) {
+                throw new UnsupportedOperationException();
+            }
+
+            @Override
+            long getSpillDiskIOTime(int spillId) {
+                throw new UnsupportedOperationException();
+            }
+
+            @Override
+            void releaseSpill(int spillId) {
+                throw new UnsupportedOperationException();
+            }
+        };
+    }
+}
diff --git a/auron-core/src/main/java/org/apache/auron/metric/MetricNode.java 
b/auron-core/src/main/java/org/apache/auron/metric/MetricNode.java
new file mode 100644
index 00000000..06e517fb
--- /dev/null
+++ b/auron-core/src/main/java/org/apache/auron/metric/MetricNode.java
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.auron.metric;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Abstract class representing a metric node in the Auron system.
+ * This class provides functionality for hierarchical metrics tracking.
+ */
+public abstract class MetricNode {
+    private final List<MetricNode> children = new ArrayList<>();
+
+    public MetricNode(List<MetricNode> children) {
+        this.children.addAll(children);
+    }
+
+    /**
+     * Gets a child metric node with the specified ID.
+     *
+     * @param id The identifier for the child node
+     * @return The child MetricNode associated with the given ID
+     */
+    public MetricNode getChild(int id) {
+        return children.get(id);
+    }
+
+    /**
+     * Adds a metric value with a specified name.
+     *
+     * @param name The name of the metric
+     * @param value The value to add for the metric (as a long)
+     */
+    public abstract void add(String name, long value);
+}
diff --git 
a/auron-core/src/test/java/org/apache/auron/jni/AuronAdaptorTest.java 
b/auron-core/src/test/java/org/apache/auron/jni/AuronAdaptorTest.java
new file mode 100644
index 00000000..d24b5591
--- /dev/null
+++ b/auron-core/src/test/java/org/apache/auron/jni/AuronAdaptorTest.java
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.auron.jni;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import org.apache.auron.configuration.AuronConfiguration;
+import org.junit.Test;
+
+/**
+ * This is a test class for {@link AuronAdaptor}.
+ */
+public class AuronAdaptorTest {
+
+    @Test
+    public void testLoadAuronLib() {
+        MockAuronAdaptor auronAdaptor = new MockAuronAdaptor();
+        AuronAdaptor.initInstance(auronAdaptor);
+        AuronAdaptor.getInstance().loadAuronLib();
+        assertNotNull(AuronAdaptor.getInstance().getAuronConfiguration());
+        AuronConfiguration auronConfig = 
AuronAdaptor.getInstance().getAuronConfiguration();
+        assertEquals(auronConfig.getInteger(AuronConfiguration.BATCH_SIZE), 
10000);
+        
assertEquals(auronConfig.getDouble(AuronConfiguration.MEMORY_FRACTION), 0.6);
+        
assertEquals(auronConfig.getString(AuronConfiguration.NATIVE_LOG_LEVEL), 
"info");
+    }
+}
diff --git 
a/auron-core/src/test/java/org/apache/auron/jni/MockAuronAdaptor.java 
b/auron-core/src/test/java/org/apache/auron/jni/MockAuronAdaptor.java
new file mode 100644
index 00000000..c2cac51b
--- /dev/null
+++ b/auron-core/src/test/java/org/apache/auron/jni/MockAuronAdaptor.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.auron.jni;
+
+import org.apache.auron.configuration.AuronConfiguration;
+import org.apache.auron.configuration.MockAuronConfiguration;
+
+/**
+ * This is a mock class for testing the AuronAdaptor.
+ */
+public class MockAuronAdaptor extends AuronAdaptor {
+    @Override
+    public void loadAuronLib() {
+        // Mock implementation, no need to load auron library
+    }
+
+    @Override
+    public AuronConfiguration getAuronConfiguration() {
+        return new MockAuronConfiguration();
+    }
+}

Reply via email to