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. + *

+ *

+ *

+ * + * @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. + * + *

+ *

+ * + * @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: + *

+ *

+ * + * @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