From 37ab5afa9c87d919a3ad04106748f0f32252391e Mon Sep 17 00:00:00 2001
From: pavl-x86-machine
Date: Sun, 16 Mar 2025 16:53:46 -0500
Subject: [PATCH 1/6] NativeDllLoader: a techdemo API showcasing anti-failure
mechanisms
---
.../examples/TestNativeDllLoader.java | 31 ++
.../examples/api/NativeDllLoader.java | 351 ++++++++++++++++++
2 files changed, 382 insertions(+)
create mode 100644 snaploader-examples/src/main/java/electrostatic4j/snaploader/examples/TestNativeDllLoader.java
create mode 100644 snaploader-examples/src/main/java/electrostatic4j/snaploader/examples/api/NativeDllLoader.java
diff --git a/snaploader-examples/src/main/java/electrostatic4j/snaploader/examples/TestNativeDllLoader.java b/snaploader-examples/src/main/java/electrostatic4j/snaploader/examples/TestNativeDllLoader.java
new file mode 100644
index 0000000..3f77d51
--- /dev/null
+++ b/snaploader-examples/src/main/java/electrostatic4j/snaploader/examples/TestNativeDllLoader.java
@@ -0,0 +1,31 @@
+package electrostatic4j.snaploader.examples;
+
+import electrostatic4j.snaploader.LibraryInfo;
+import electrostatic4j.snaploader.LoadingCriterion;
+import electrostatic4j.snaploader.examples.api.NativeDllLoader;
+import electrostatic4j.snaploader.filesystem.DirectoryPath;
+import electrostatic4j.snaploader.platform.NativeDynamicLibrary;
+import electrostatic4j.snaploader.platform.util.DefaultDynamicLibraries;
+import electrostatic4j.snaploader.platform.util.PlatformPredicate;
+
+public final class TestNativeDllLoader {
+ public static void main(String[] args) throws Exception {
+ final NativeDynamicLibrary[] baseLibs = new NativeDynamicLibrary[] {
+ DefaultDynamicLibraries.ANDROID_ALL,
+ new NativeDynamicLibrary("linux/x86-64/com/github/stephengoldd", PlatformPredicate.LINUX_X86_64),
+ new NativeDynamicLibrary("windows/x86-64/com/github/stephengoldd", PlatformPredicate.WIN_X86_64),
+ };
+
+ final NativeDynamicLibrary[] cpuEnhancedLibs = new NativeDynamicLibrary[]{
+ DefaultDynamicLibraries.ANDROID_ALL,
+ new NativeDynamicLibrary("linux/x86-64-fma/com/github/stephengold", new PlatformPredicate(PlatformPredicate.LINUX_X86_64,
+ "avx", "avx2", "bmi1", "f16c", "fma", "sse4_1", "sse4_2")),
+ new NativeDynamicLibrary("windows/x86-64-avx2/com/github/stephengold", new PlatformPredicate(PlatformPredicate.WIN_X86_64,
+ "avx", "avx2", "sse4_1", "sse4_2")),
+ };
+ final LibraryInfo info = new LibraryInfo(new DirectoryPath("linux/x86-64/com/github/stephengold"),
+ "joltjnid", DirectoryPath.USER_DIR);
+ final NativeDllLoader nativeDllLoader = new NativeDllLoader(baseLibs, cpuEnhancedLibs, info, true, true);
+ nativeDllLoader.loadCpuEnhancedLibs(LoadingCriterion.INCREMENTAL_LOADING);
+ }
+}
diff --git a/snaploader-examples/src/main/java/electrostatic4j/snaploader/examples/api/NativeDllLoader.java b/snaploader-examples/src/main/java/electrostatic4j/snaploader/examples/api/NativeDllLoader.java
new file mode 100644
index 0000000..c62ad8d
--- /dev/null
+++ b/snaploader-examples/src/main/java/electrostatic4j/snaploader/examples/api/NativeDllLoader.java
@@ -0,0 +1,351 @@
+/*
+ * Copyright (c) 2023-2025, The Electrostatic-Sandbox Distributed Simulation Framework, jSnapLoader
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'AvrSandbox' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package electrostatic4j.snaploader.examples.api;
+
+import com.github.stephengold.joltjni.Jolt;
+import java.util.List;
+import java.util.logging.Level;
+import electrostatic4j.snaploader.ConcurrentNativeBinaryLoader;
+import electrostatic4j.snaploader.LibraryInfo;
+import electrostatic4j.snaploader.LoadingCriterion;
+import electrostatic4j.snaploader.NativeBinaryLoader;
+import electrostatic4j.snaploader.NativeBinaryLoadingListener;
+import electrostatic4j.snaploader.filesystem.FileLocalizingListener;
+import electrostatic4j.snaploader.filesystem.FileLocator;
+import electrostatic4j.snaploader.platform.NativeDynamicLibrary;
+import electrostatic4j.snaploader.throwable.FilesystemResourceInitializationException;
+import electrostatic4j.snaploader.throwable.LoadingRetryExhaustionException;
+import electrostatic4j.snaploader.throwable.UnSupportedSystemError;
+import electrostatic4j.snaploader.util.CallingStackMetaData;
+import electrostatic4j.snaploader.util.SnapLoaderLogger;
+
+/**
+ * A suggested cross-platform algorithm to use fallback mechanisms for Jolt-Jni.
+ *
+ *
+ * - This is an efficient implementation that follows best
+ * practices for software engineering, and the computational theory.
+ * - This is a theoretical implementation technique.
+ * - Feel free to copy-paste to your projects and modify as required.
+ * - Open issues as required.
+ *
+ *
+ *
+ * @author pavl_g.
+ */
+public class NativeDllLoader implements NativeBinaryLoadingListener, FileLocalizingListener {
+
+ /**
+ * Instance reference for the associated binary loader.
+ */
+ protected final NativeBinaryLoader loader;
+ /**
+ * Instance reference for libraries with base features
+ * for the {@link NativeDllLoader#loadBaseLibraries(LoadingCriterion)}
+ * as a fallback mechanism.
+ */
+ protected NativeDynamicLibrary[] baseLibs;
+ /**
+ * Instance reference for libraries with CPU-based specific features
+ * for the {@link NativeDllLoader#loadCpuEnhancedLibs(LoadingCriterion)} routine.
+ */
+ protected NativeDynamicLibrary[] cpuEnhancedLibs;
+
+ /**
+ * Instantiates a DLL Loader wrapper setting its logging, retry criteria,
+ * and setting up its loading listeners.
+ *
+ * @param baseLibs the base feature libraries group (not null).
+ * @param cpuEnhancedLibs the cpu enhanced libraries group (not null).
+ * @param info the cross-platform library info metadata (not null).
+ * @param enableLogging true to enable snap-loader logger and failure logs (default: false).
+ * @param enableRetryCriterion true to enable retrying when loading from a jar file (default: false).
+ * @throws IllegalArgumentException if the caller stack has passed undefined library groups and/or
+ * undefined library information reference.
+ */
+ public NativeDllLoader(NativeDynamicLibrary[] baseLibs, NativeDynamicLibrary[] cpuEnhancedLibs,
+ LibraryInfo info, boolean enableLogging, boolean enableRetryCriterion) {
+ this(info, enableLogging, enableRetryCriterion);
+
+ if (baseLibs == null || cpuEnhancedLibs == null) {
+ throw new IllegalArgumentException("Libraries groups cannot be null!");
+ }
+
+ this.baseLibs = baseLibs;
+ this.cpuEnhancedLibs = cpuEnhancedLibs;
+ }
+
+ /**
+ * Instantiates a DLL Loader wrapper setting its logging, retry criteria,
+ * and setting up its loading listeners.
+ *
+ * @param enableLogging enables the API level logger (default: false).
+ * @param enableRetryCriterion enables retrying with clean extraction (default: false).
+ * when loading failure for {@link LoadingCriterion#INCREMENTAL_LOADING}
+ * routine.
+ * @throws IllegalArgumentException if the caller stack has passed an undefined library information
+ * reference.
+ */
+ public NativeDllLoader(LibraryInfo info, boolean enableLogging, boolean enableRetryCriterion) {
+ if (info == null) {
+ throw new IllegalArgumentException("Cannot proceed with no library information!");
+ }
+ loader = new ConcurrentNativeBinaryLoader(List.of(), info);
+ loader.setLoggingEnabled(enableLogging);
+ loader.setRetryWithCleanExtraction(enableRetryCriterion);
+ loader.setNativeBinaryLoadingListener(this);
+ loader.setLibraryLocalizingListener(this);
+ }
+
+ /**
+ * Loads the base defined libraries providing an anti-failure routine
+ * for loading from archive commands. This routine is thread-safe with other object routines.
+ *
+ *
+ * Possible execution stack:
+ * Legend:
+ *
+ * ">>" represents the initial state of the automata
+ * "*" represents the terminal state of the automata; after
+ * which the stack is owned by another machine.
+ * "**" represents an imminent failure signal.
+ * "->" represents a machine transitional delta.
+ *
+ *
+ * - Case 1:
+ *
>> loadBaseLibraries(LoadingCriterion.INCREMENTAL_LOADING) ->
+ * if (failure-cause == UnSupportedSystemError) -> exit() -> **.
+ *
+ *
+ * - Case 2:
+ *
>> loadBaseLibraries(LoadingCriterion.INCREMENTAL_LOADING) ->
+ * if (failure-cause == LoadingRetryExhaustionException) -> onLoadingFailure()
+ * -> loadBaseLibraries(LoadingCriterion.SYSTEM_LOAD) ->
+ * if (failure-cause == UnsatisfiedLinkError) -> exit() -> **.
+ *
+ *
+ *
+ *
+ *
+ * @param criterion the loading criterion, it's recommended to start with
+ * {@link LoadingCriterion#INCREMENTAL_LOADING}; moreover
+ * start with the {@link NativeDllLoader#loadCpuEnhancedLibs(LoadingCriterion)} (not null).
+ * @throws Exception if I/O event or thread signal interrupt occurs.
+ */
+ public synchronized void loadBaseLibraries(LoadingCriterion criterion) throws Exception {
+ if (criterion == null) {
+ throw new IllegalArgumentException("Cannot proceed with null loading criterion!");
+ }
+ try {
+ loader.registerNativeLibraries(baseLibs)
+ .initPlatformLibrary()
+ .loadLibrary(criterion);
+ } catch (UnSupportedSystemError e) {
+ signalImminentFailure(
+ new CallingStackMetaData(Thread.currentThread().getStackTrace()[1], criterion, e));
+ } catch (LoadingRetryExhaustionException e) {
+ // re-route retry failure to the same anti-failure routine
+ this.onLoadingFailure(loader,
+ new CallingStackMetaData(Thread.currentThread().getStackTrace()[1], criterion, e));
+ }
+ }
+
+ /**
+ * Loads the registered CPU-enhanced libraries. Providing an anti-failure routine
+ * for loading from archive commands. This is thread-safe with other routines.
+ *
+ *
+ * Possible execution stack:
+ *
+ * - Case 1 (Notice UnSupportedSystemError is thrown from the initPlatformLibrary
+ * during which system selection is performed):
+ *
>> exec: loadCpuEnhancedLibs(LoadingCriterion.INCREMENTAL_LOADING) ->
+ * if (failure-cause == UnSupportedSystemError) -> onLoadingFailure ->
+ * exec: loadBaseLibraries(LoadingCriterion.INCREMENTAL_LOADING) -> *.
+ *
+ *
+ * - Case 2:
+ *
>> exec: loadCpuEnhancedLibs(LoadingCriterion.INCREMENTAL_LOADING) ->
+ * if (failure-cause == LoadingRetryExhaustionException) -> loadCpuEnhancedLibs(LoadingCriterion.SYSTEM_LOAD)
+ * if (failure-cause == UnsatisfiedLinkError) -> onLoadingFailure -> exec: loadBaseLibraries(LoadingCriterion.INCREMENTAL_LOADING)
+ * -> *.
+ *
+ *
+ * - Case 3:
+ *
+ * >> ...Some states... -> Any other failure cause -> exit() -> *.
+ *
+ *
+ *
+ *
+ *
+ * @param criterion the type of loading; it's recommended to start with {@link LoadingCriterion#INCREMENTAL_LOADING}.
+ * @throws Exception if I/O event or thread signal interrupt occurs.
+ */
+ public synchronized void loadCpuEnhancedLibs(LoadingCriterion criterion) throws Exception {
+ if (criterion == null) {
+ throw new IllegalArgumentException("Cannot proceed with null loading criterion!");
+ }
+ try {
+ loader.registerNativeLibraries(cpuEnhancedLibs)
+ .initPlatformLibrary()
+ .loadLibrary(criterion);
+ } catch (UnSupportedSystemError e) {
+ // re-route system not found and retry failure to the same anti-failure routine
+ this.onLoadingFailure(loader,
+ new CallingStackMetaData(Thread.currentThread().getStackTrace()[1], criterion, e));
+ } catch (LoadingRetryExhaustionException e) {
+ // retry with SYSTEM_LOAD
+ // notice that LoadingRetryExhaustionException will never
+ // happen with LoadingCriterion.SYSTEM_LOAD
+ // the LoadingRetryExhaustionException is thrown as a result
+ // of greater than 2 times throwing "UnSatisfiedLinkError" on the
+ // INCREMENTAL Loading Stack (i.e., extracting stack).
+ loadCpuEnhancedLibs(LoadingCriterion.SYSTEM_LOAD);
+ }
+ }
+
+ @Override
+ public void onLoadingSuccess(NativeBinaryLoader nativeBinaryLoader, CallingStackMetaData callingStackMetaData) {
+ // initialize Jolt-Jni and physics update system
+ // handling lifecycle to the internal Jolt-Physics Native
+
+ // copied from Jolt-Jni Example
+ String configuration = Jolt.getConfigurationString();
+ /*
+ * Depending which native library was loaded, the configuration string
+ * should be one of the following:
+ *
+ * On LINUX_X86_64 platforms, either
+ * Single precision x86 64-bit with instructions: SSE2 SSE4.1 SSE4.2 AVX AVX2 F16C LZCNT TZCNT FMADD (Debug Renderer) (16-bit ObjectLayer) (Assertions) (ObjectStream) (Debug) (C++ RTTI) (C++ Exceptions)
+ * or
+ * Single precision x86 64-bit with instructions: SSE2 (Debug Renderer) (16-bit ObjectLayer) (Assertions) (ObjectStream) (Debug) (C++ RTTI) (C++ Exceptions)
+ *
+ * On WIN_X86_64 platforms, either
+ * Single precision x86 64-bit with instructions: SSE2 SSE4.1 SSE4.2 AVX AVX2 F16C LZCNT TZCNT (FP Exceptions) (Debug Renderer) (16-bit ObjectLayer) (Assertions) (ObjectStream) (Debug) (C++ RTTI) (C++ Exceptions)
+ * or
+ * Single precision x86 64-bit with instructions: SSE2 (FP Exceptions) (Debug Renderer) (16-bit ObjectLayer) (Assertions) (ObjectStream) (Debug) (C++ RTTI) (C++ Exceptions)
+ */
+ System.out.println(configuration);
+ }
+
+ @Override
+ public synchronized void onLoadingFailure(NativeBinaryLoader nativeBinaryLoader, CallingStackMetaData callingStackMetaData) {
+ // validate input!
+ if (callingStackMetaData == null || callingStackMetaData.getCallingStack() == null ||
+ callingStackMetaData.getErrorCause() == null || callingStackMetaData.getLoadingCriterion() == null) {
+ throw new IllegalArgumentException("Failure stack metadata structure cannot be null!");
+ }
+
+ final String callingMethod = callingStackMetaData.getCallingStack().getMethodName();
+ // log calling stack!
+ SnapLoaderLogger.log(Level.INFO, callingStackMetaData.getCallingStack().getClassName(),
+ callingStackMetaData.getCallingStack().getMethodName(),
+ "Failure Stack", callingStackMetaData.getErrorCause());
+
+ try {
+ if (callingStackMetaData.getErrorCause() instanceof LoadingRetryExhaustionException) {
+ if (callingMethod.contains("loadCpuEnhancedLibs")) {
+ // try cpu enhanced libs from system directory
+ loadCpuEnhancedLibs(LoadingCriterion.SYSTEM_LOAD);
+ } else if (callingMethod.contains("loadBaseLibraries")) {
+ // try loading base libraries from system directories if loading is exhausted!
+ loadBaseLibraries(LoadingCriterion.SYSTEM_LOAD);
+ }
+ } else if (callingStackMetaData.getErrorCause() instanceof UnsatisfiedLinkError) {
+ if (callingMethod.contains("loadCpuEnhancedLibs")) {
+ // no retry criteria?
+ // or loading from system directory?
+ // Exit the loadCpuEnhancedLibs stack frames!
+ loadBaseLibraries(LoadingCriterion.INCREMENTAL_LOADING);
+ } else if (callingMethod.contains("loadBaseLibraries")) {
+ signalImminentFailure(callingStackMetaData);
+ }
+ } else if (callingStackMetaData.getErrorCause() instanceof FilesystemResourceInitializationException) {
+ if (callingMethod.contains("onFileLocalizationFailure")) {
+ loadCpuEnhancedLibs(LoadingCriterion.SYSTEM_LOAD);
+ }
+ }
+ } catch (Exception e) {
+ signalImminentFailure(callingStackMetaData);
+ }
+ }
+
+ @Override
+ public void onRetryCriterionExecution(NativeBinaryLoader nativeBinaryLoader, CallingStackMetaData callingStackMetaData) {
+ }
+
+ @Override
+ public void onFileLocalizationSuccess(FileLocator locator) {
+ }
+
+ @Override
+ public void onFileLocalizationFailure(FileLocator locator, Throwable throwable) {
+ }
+
+ /**
+ * Sets base feature libraries group strong reference. This command
+ * will only take effect before dispatching the loading
+ * routines. Warning: Non-thread safe.
+ *
+ * @param baseLibs the new base libraries groups reference.
+ */
+ public void setBaseLibs(NativeDynamicLibrary[] baseLibs) {
+ this.baseLibs = baseLibs;
+ }
+
+ /**
+ * Sets the cpu-specific enhanced libraries group strong reference. This command
+ * will only take effect before dispatching the loading routines. Warning: Non-thread safe.
+ *
+ * @param cpuEnhancedLibs the new enhanced libraries groups reference.
+ */
+ public void setCpuEnhancedLibs(NativeDynamicLibrary[] cpuEnhancedLibs) {
+ this.cpuEnhancedLibs = cpuEnhancedLibs;
+ }
+
+ /**
+ * Signals an imminent failure disposing the application process with an
+ * error code formed from the hashcode of the causing throwable on the
+ * calling stack.
+ *
+ * @param callingStackMetaData a calling stack metadata structure strong reference.
+ */
+ protected void signalImminentFailure(CallingStackMetaData callingStackMetaData) {
+ SnapLoaderLogger.log(Level.SEVERE, callingStackMetaData.getCallingStack().getClassName(),
+ callingStackMetaData.getCallingStack().getMethodName(),
+ "Imminent Failure", callingStackMetaData.getErrorCause());
+ // signal an imminent failure and crash the application
+ Runtime.getRuntime().exit(-callingStackMetaData.getErrorCause().hashCode());
+ }
+}
From 84bcd57cf387b9eb187f21033d69ad2c453fea19 Mon Sep 17 00:00:00 2001
From: pavl-g
Date: Sun, 16 Mar 2025 16:57:21 -0500
Subject: [PATCH 2/6] NativeBinaryLoadingListener: inspecting the failing
calling stack using CallingStackMetaData
---
.../NativeBinaryLoadingListener.java | 21 +++---
.../snaploader/util/CallingStackMetaData.java | 71 +++++++++++++++++++
2 files changed, 84 insertions(+), 8 deletions(-)
create mode 100644 snaploader/src/main/java/electrostatic4j/snaploader/util/CallingStackMetaData.java
diff --git a/snaploader/src/main/java/electrostatic4j/snaploader/NativeBinaryLoadingListener.java b/snaploader/src/main/java/electrostatic4j/snaploader/NativeBinaryLoadingListener.java
index a4791b1..9f7cd0d 100644
--- a/snaploader/src/main/java/electrostatic4j/snaploader/NativeBinaryLoadingListener.java
+++ b/snaploader/src/main/java/electrostatic4j/snaploader/NativeBinaryLoadingListener.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2023-2024, The Electrostatic-Sandbox Distributed Simulation Framework, jSnapLoader
+ * Copyright (c) 2023-2025, The Electrostatic-Sandbox Distributed Simulation Framework, jSnapLoader
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -33,13 +33,14 @@
package electrostatic4j.snaploader;
import electrostatic4j.snaploader.platform.NativeDynamicLibrary;
+import electrostatic4j.snaploader.util.CallingStackMetaData;
/**
* Provides executable functions binding the user applications to
* the loading lifecycle.
*
* Note: All the functions on this interface are dispatched
- * by the {@link NativeBinaryLoader#loadBinary(NativeDynamicLibrary)}.
+ * by the {@link NativeBinaryLoader#loadBinary(NativeDynamicLibrary, LoadingCriterion)}
*
* @author pavl_g
*/
@@ -48,16 +49,19 @@ public interface NativeBinaryLoadingListener {
/**
* Dispatched when loading the system-specific binary has succeeded.
*
- * @param nativeBinaryLoader the dispatching loader
+ * @param nativeBinaryLoader the dispatching loader.
+ * @param callingStackMetaData a data structure representing the meta data of the calling stack.
*/
- void onLoadingSuccess(NativeBinaryLoader nativeBinaryLoader);
+ void onLoadingSuccess(NativeBinaryLoader nativeBinaryLoader, CallingStackMetaData callingStackMetaData);
/**
* Dispatched when loading the system-specific binary has failed.
*
- * @param nativeBinaryLoader the dispatching loader
+ * @param nativeBinaryLoader the dispatching loader.
+ * @param callingStackMetaData a data structure representing the meta data of the calling stack.
*/
- void onLoadingFailure(NativeBinaryLoader nativeBinaryLoader);
+ void onLoadingFailure(NativeBinaryLoader nativeBinaryLoader,
+ CallingStackMetaData callingStackMetaData);
/**
* Dispatched when loading the system-specific binary has failed,
@@ -66,7 +70,8 @@ public interface NativeBinaryLoadingListener {
* Note: this dispatching function could be overridden to add
* your own anti-failure mechanisms (i.e., Retry Criterion).
*
- * @param nativeBinaryLoader the dispatching loader
+ * @param nativeBinaryLoader the dispatching loader.
+ * @param callingStackMetaData a data structure representing the meta data of the calling stack.
*/
- void onRetryCriterionExecution(NativeBinaryLoader nativeBinaryLoader);
+ void onRetryCriterionExecution(NativeBinaryLoader nativeBinaryLoader, CallingStackMetaData callingStackMetaData);
}
diff --git a/snaploader/src/main/java/electrostatic4j/snaploader/util/CallingStackMetaData.java b/snaploader/src/main/java/electrostatic4j/snaploader/util/CallingStackMetaData.java
new file mode 100644
index 0000000..45bade2
--- /dev/null
+++ b/snaploader/src/main/java/electrostatic4j/snaploader/util/CallingStackMetaData.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2023-2025, The Electrostatic-Sandbox Distributed Simulation Framework, jSnapLoader
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'Electrostatic-Sandbox' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package electrostatic4j.snaploader.util;
+
+import electrostatic4j.snaploader.LoadingCriterion;
+
+/**
+ * A record-like structure representing immutable
+ * state objects for a calling stack invoking the
+ * library loading.
+ *
+ * @author pavl_g.
+ */
+public final class CallingStackMetaData {
+ private final StackTraceElement callingStack;
+ private final LoadingCriterion loadingCriterion;
+ private Throwable errorCause;
+
+ public CallingStackMetaData(StackTraceElement callingStack, LoadingCriterion loadingCriterion,
+ Throwable errorCause) {
+ this(callingStack, loadingCriterion);
+ this.errorCause = errorCause;
+ }
+
+ public CallingStackMetaData(StackTraceElement callingStack, LoadingCriterion loadingCriterion) {
+ this.callingStack = callingStack;
+ this.loadingCriterion = loadingCriterion;
+ }
+
+ public LoadingCriterion getLoadingCriterion() {
+ return loadingCriterion;
+ }
+
+ public StackTraceElement getCallingStack() {
+ return callingStack;
+ }
+
+ public Throwable getErrorCause() {
+ return errorCause;
+ }
+}
From 2b2e37a6a352ba70a978a3c4f8d976d4ecb075c5 Mon Sep 17 00:00:00 2001
From: pavl-g
Date: Sun, 16 Mar 2025 16:58:35 -0500
Subject: [PATCH 3/6] UnSupportedSystemError: updated the root class antecedent
tree to Error
---
.../snaploader/throwable/UnSupportedSystemError.java | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/snaploader/src/main/java/electrostatic4j/snaploader/throwable/UnSupportedSystemError.java b/snaploader/src/main/java/electrostatic4j/snaploader/throwable/UnSupportedSystemError.java
index 8f919dc..b77baa2 100644
--- a/snaploader/src/main/java/electrostatic4j/snaploader/throwable/UnSupportedSystemError.java
+++ b/snaploader/src/main/java/electrostatic4j/snaploader/throwable/UnSupportedSystemError.java
@@ -33,13 +33,13 @@
package electrostatic4j.snaploader.throwable;
/**
- * A business error of type {@link UnsatisfiedLinkError} to indicate an unsupported system.
+ * A business error of type {@link Error} to indicate an unsupported system.
*
* This error is thrown when all the user-defined platform predicates are not met!
*
* @author pavl_g
*/
-public class UnSupportedSystemError extends UnsatisfiedLinkError {
+public class UnSupportedSystemError extends Error {
/**
* Thrown if the system detects an unsupported system binaries of the current OS.
From 7fb59a0170ec9ad3c336a6ff0d21ce4ad615ecc0 Mon Sep 17 00:00:00 2001
From: pavl-g
Date: Sun, 16 Mar 2025 16:59:01 -0500
Subject: [PATCH 4/6] NativeBinaryLoader: applied API changes
---
.../snaploader/NativeBinaryLoader.java | 33 ++++++++++++++-----
1 file changed, 24 insertions(+), 9 deletions(-)
diff --git a/snaploader/src/main/java/electrostatic4j/snaploader/NativeBinaryLoader.java b/snaploader/src/main/java/electrostatic4j/snaploader/NativeBinaryLoader.java
index 4db2668..bc29713 100644
--- a/snaploader/src/main/java/electrostatic4j/snaploader/NativeBinaryLoader.java
+++ b/snaploader/src/main/java/electrostatic4j/snaploader/NativeBinaryLoader.java
@@ -47,6 +47,7 @@
import electrostatic4j.snaploader.platform.util.NativeVariant;
import electrostatic4j.snaploader.throwable.LoadingRetryExhaustionException;
import electrostatic4j.snaploader.throwable.UnSupportedSystemError;
+import electrostatic4j.snaploader.util.CallingStackMetaData;
import electrostatic4j.snaploader.util.SnapLoaderLogger;
/**
@@ -160,7 +161,7 @@ public NativeBinaryLoader initPlatformLibrary() throws UnSupportedSystemError {
*
*
*
- * Fallback loading routines can be implemented as needed via {@link NativeBinaryLoadingListener#onLoadingFailure(NativeBinaryLoader)}
+ * Fallback loading routines can be implemented as needed via {@link NativeBinaryLoadingListener#onLoadingFailure(NativeBinaryLoader, CallingStackMetaData)}
* and are left for the user applications.
*
*
@@ -179,7 +180,7 @@ public NativeBinaryLoader loadLibrary(LoadingCriterion criterion) throws Excepti
return this;
}
if (criterion == LoadingCriterion.INCREMENTAL_LOADING && nativeDynamicLibrary.isExtracted()) {
- loadBinary(nativeDynamicLibrary);
+ loadBinary(nativeDynamicLibrary, criterion);
return this;
}
cleanExtractBinary(nativeDynamicLibrary);
@@ -272,14 +273,16 @@ protected void loadSystemBinary() {
SnapLoaderLogger.log(Level.INFO, getClass().getName(), "loadSystemBinary", "Successfully loaded library from the system: "
+ libraryInfo.getBaseName());
if (nativeBinaryLoadingListener != null) {
- nativeBinaryLoadingListener.onLoadingSuccess(this);
+ nativeBinaryLoadingListener.onLoadingSuccess(this,
+ new CallingStackMetaData(Thread.currentThread().getStackTrace()[1], LoadingCriterion.SYSTEM_LOAD));
}
} catch (UnsatisfiedLinkError e) {
SnapLoaderLogger.log(Level.SEVERE, getClass().getName(), "loadSystemBinary", "Cannot load the dynamic library from the system: "
+ libraryInfo.getBaseName(), e);
// fire failure routine for fallback criteria
if (nativeBinaryLoadingListener != null) {
- nativeBinaryLoadingListener.onLoadingFailure(this);
+ nativeBinaryLoadingListener.onLoadingFailure(this,
+ new CallingStackMetaData(Thread.currentThread().getStackTrace()[1], LoadingCriterion.SYSTEM_LOAD, e));
}
}
}
@@ -289,28 +292,32 @@ protected void loadSystemBinary() {
* native library data structure defining the directory path.
*
* @param library the platform-specific library to load
+ * @param loadingCriterion pass the loading criterion condition to the calling stack metadata structure
* @throws IOException in case the binary to be extracted is not found on the specified jar
* @throws LoadingRetryExhaustionException if the number of loading failure exceeds the specified
* number.
*/
- protected void loadBinary(NativeDynamicLibrary library) throws Exception {
+ protected void loadBinary(NativeDynamicLibrary library, LoadingCriterion loadingCriterion) throws Exception {
try {
System.load(library.getExtractedLibrary());
SnapLoaderLogger.log(Level.INFO, getClass().getName(), "loadBinary", "Successfully loaded library: "
+ library.getExtractedLibrary());
if (nativeBinaryLoadingListener != null) {
- nativeBinaryLoadingListener.onLoadingSuccess(this);
+ nativeBinaryLoadingListener.onLoadingSuccess(this,
+ new CallingStackMetaData(Thread.currentThread().getStackTrace()[1], loadingCriterion));
}
} catch (final UnsatisfiedLinkError error) {
SnapLoaderLogger.log(Level.SEVERE, getClass().getName(), "loadBinary", "Cannot load the dynamic library: "
+ library.getExtractedLibrary(), error);
if (nativeBinaryLoadingListener != null) {
- nativeBinaryLoadingListener.onLoadingFailure(this);
+ nativeBinaryLoadingListener.onLoadingFailure(this,
+ new CallingStackMetaData(Thread.currentThread().getStackTrace()[1], loadingCriterion, error));
}
/* Retry with clean extract */
if (isRetryWithCleanExtraction()) {
if (nativeBinaryLoadingListener != null) {
- nativeBinaryLoadingListener.onRetryCriterionExecution(this);
+ nativeBinaryLoadingListener.onRetryCriterionExecution(this,
+ new CallingStackMetaData(Thread.currentThread().getStackTrace()[1], loadingCriterion));
}
// limit the number of retries to maxNumberOfLoadingFailure
if (numberOfLoadingFailure >= maxNumberOfLoadingFailure) {
@@ -347,7 +354,7 @@ public void onExtractionCompleted(FileExtractor fileExtractor) {
SnapLoaderLogger.log(Level.INFO, getClass().getName(), "cleanExtractBinary",
"Extracted successfully to " + library.getExtractedLibrary());
// load the native binary
- loadBinary(library);
+ loadBinary(library, LoadingCriterion.CLEAN_EXTRACTION);
} catch (Exception e) {
SnapLoaderLogger.log(Level.SEVERE, getClass().getName(), "cleanExtractBinary",
"Error while loading the binary!", e);
@@ -442,6 +449,14 @@ public void onFileLocalizationFailure(FileLocator locator, Throwable throwable)
if (libraryLocalizingListener != null) {
libraryLocalizingListener.onFileLocalizationFailure(locator, throwable);
}
+
+ // make use of the loader listeners
+ if (nativeBinaryLoadingListener != null) {
+ // a file locator and extractor loader is always a CLEAN_EXTRACTION regarding
+ // the loading criterion
+ nativeBinaryLoadingListener.onLoadingFailure(NativeBinaryLoader.this,
+ new CallingStackMetaData(Thread.currentThread().getStackTrace()[1], LoadingCriterion.CLEAN_EXTRACTION, throwable));
+ }
}
});
return (LibraryLocator) extractor.getFileLocator();
From dd919cf914caec8d63475b079dd3e0f48d72d836 Mon Sep 17 00:00:00 2001
From: pavl-g
Date: Mon, 17 Mar 2025 06:42:28 -0500
Subject: [PATCH 5/6] NativeBinaryLoader: chainable setters
---
.../snaploader/NativeBinaryLoader.java | 21 ++++++++++++-------
1 file changed, 14 insertions(+), 7 deletions(-)
diff --git a/snaploader/src/main/java/electrostatic4j/snaploader/NativeBinaryLoader.java b/snaploader/src/main/java/electrostatic4j/snaploader/NativeBinaryLoader.java
index bc29713..bcbc63c 100644
--- a/snaploader/src/main/java/electrostatic4j/snaploader/NativeBinaryLoader.java
+++ b/snaploader/src/main/java/electrostatic4j/snaploader/NativeBinaryLoader.java
@@ -201,8 +201,9 @@ public NativeDynamicLibrary getNativeDynamicLibrary() {
*
* @param loggingEnabled true to enable logging, false otherwise
*/
- public void setLoggingEnabled(boolean loggingEnabled) {
+ public NativeBinaryLoader setLoggingEnabled(boolean loggingEnabled) {
SnapLoaderLogger.setLoggingEnabled(loggingEnabled);
+ return this;
}
/**
@@ -219,8 +220,9 @@ public boolean isRetryWithCleanExtraction() {
*
* @param retryWithCleanExtraction true to enable the flag, false otherwise
*/
- public void setRetryWithCleanExtraction(boolean retryWithCleanExtraction) {
+ public NativeBinaryLoader setRetryWithCleanExtraction(boolean retryWithCleanExtraction) {
this.retryWithCleanExtraction = retryWithCleanExtraction;
+ return this;
}
public List getRegisteredLibraries() {
@@ -231,36 +233,41 @@ public NativeBinaryLoadingListener getNativeBinaryLoadingListener() {
return nativeBinaryLoadingListener;
}
- public void setNativeBinaryLoadingListener(NativeBinaryLoadingListener nativeBinaryLoadingListener) {
+ public NativeBinaryLoader setNativeBinaryLoadingListener(NativeBinaryLoadingListener nativeBinaryLoadingListener) {
this.nativeBinaryLoadingListener = nativeBinaryLoadingListener;
+ return this;
}
public SystemDetectionListener getSystemDetectionListener() {
return systemDetectionListener;
}
- public void setSystemDetectionListener(SystemDetectionListener systemDetectionListener) {
+ public NativeBinaryLoader setSystemDetectionListener(SystemDetectionListener systemDetectionListener) {
this.systemDetectionListener = systemDetectionListener;
+ return this;
}
public FileExtractionListener getLibraryExtractionListener() {
return libraryExtractionListener;
}
- public void setLibraryExtractionListener(FileExtractionListener libraryExtractionListener) {
+ public NativeBinaryLoader setLibraryExtractionListener(FileExtractionListener libraryExtractionListener) {
this.libraryExtractionListener = libraryExtractionListener;
+ return this;
}
public FileLocalizingListener getLibraryLocalizingListener() {
return libraryLocalizingListener;
}
- public void setLibraryLocalizingListener(FileLocalizingListener libraryLocalizingListener) {
+ public NativeBinaryLoader setLibraryLocalizingListener(FileLocalizingListener libraryLocalizingListener) {
this.libraryLocalizingListener = libraryLocalizingListener;
+ return this;
}
- public void setMaxNumberOfLoadingFailure(int maxNumberOfLoadingFailure) {
+ public NativeBinaryLoader setMaxNumberOfLoadingFailure(int maxNumberOfLoadingFailure) {
this.maxNumberOfLoadingFailure = Math.abs(maxNumberOfLoadingFailure);
+ return this;
}
/**
From e8dcf7c33bbf06e9555f1b2ef57300c805a1baa6 Mon Sep 17 00:00:00 2001
From: pavl-g
Date: Mon, 17 Mar 2025 06:45:02 -0500
Subject: [PATCH 6/6] LoadingCriterion: fixed JavaDocs method references
---
.../java/electrostatic4j/snaploader/LoadingCriterion.java | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/snaploader/src/main/java/electrostatic4j/snaploader/LoadingCriterion.java b/snaploader/src/main/java/electrostatic4j/snaploader/LoadingCriterion.java
index e7fdf31..3d722ee 100644
--- a/snaploader/src/main/java/electrostatic4j/snaploader/LoadingCriterion.java
+++ b/snaploader/src/main/java/electrostatic4j/snaploader/LoadingCriterion.java
@@ -32,6 +32,8 @@
package electrostatic4j.snaploader;
+import electrostatic4j.snaploader.util.CallingStackMetaData;
+
/**
* Represents an extraction/loading criterion type.
*
@@ -127,7 +129,8 @@ public enum LoadingCriterion {
* This approach requires the library to be present on the system beforehand.
* If the library is missing, the loading process will fail with an {@code UnsatisfiedLinkError}.
* To ensure compatibility across different systems, consider providing a fallback
- * mechanism to extract the library dynamically when needed via {@link NativeBinaryLoadingListener#onLoadingFailure(NativeBinaryLoader)}.
+ * mechanism to extract the library dynamically whenever required
+ * via {@link NativeBinaryLoadingListener#onLoadingFailure(NativeBinaryLoader, CallingStackMetaData)}.
*
*/
SYSTEM_LOAD