From 7b0845d43d690751822d42f5b84e1f20c6bdab3f Mon Sep 17 00:00:00 2001 From: pavl-g Date: Sun, 4 Oct 2026 20:53:22 +0300 Subject: [PATCH 1/7] FileExtractor: introduced a file instance; deletes a blank physical file on-failure --- .../snaploader/filesystem/FileExtractor.java | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/snaploader/src/main/java/electrostatic4j/snaploader/filesystem/FileExtractor.java b/snaploader/src/main/java/electrostatic4j/snaploader/filesystem/FileExtractor.java index db394bb..6d7788f 100644 --- a/snaploader/src/main/java/electrostatic4j/snaploader/filesystem/FileExtractor.java +++ b/snaploader/src/main/java/electrostatic4j/snaploader/filesystem/FileExtractor.java @@ -66,6 +66,8 @@ public class FileExtractor implements OutputStreamProvider { */ protected String destination; + protected File extractableFile; + private static final int EOF = -1; /* End-of-filesystem */ /** @@ -103,6 +105,7 @@ public void initialize(int size) throws Exception { return; } this.fileOutputStream = new FileOutputStream(destination); + extractableFile = new File(destination); SnapLoaderLogger.log(Level.INFO, getClass().getName(), "initialize(int)", "File extractor initialized with hash key #" + getHashKey()); } catch (Exception e) { @@ -149,6 +152,14 @@ public void extract() throws IOException, FileNotFoundException { fileExtractionListener.onExtractionCompleted(this); } } catch (Exception e) { + // ensure deleting the blank created files + // by the Java Legacy FileOutputStream upon failure! + + // blank created files; create an opaque behavior + // that triggers an UnSatisfiedLinkError: file is too short + // as a result of trying to load corrupted files + // on the next runtime... + delete(); if (fileExtractionListener != null) { fileExtractionListener.onExtractionFailure(this, e); } @@ -160,6 +171,20 @@ public void extract() throws IOException, FileNotFoundException { } } + /** + * Deletes the associated file created by the Legacy + * Java OutputStream {@link FileExtractor#getFileOutputStream()}. + * + * @return true if the file has been deleted, false otherwise if the + * file is not defined or doesn't exist. + */ + protected boolean delete() { + if (extractableFile == null || !extractableFile.exists()) { + return false; + } + return extractableFile.delete(); + } + @Override public void close() throws Exception { if (fileOutputStream != null) { From 9d53202c62025117bf4104d9fb06b5a3a8210e34 Mon Sep 17 00:00:00 2001 From: pavl-g Date: Sun, 4 Oct 2026 20:54:45 +0300 Subject: [PATCH 2/7] NativeBinaryLoader: enabled the natural flow of retry-with-clean-extraction for the incremental loading criterion --- .../java/electrostatic4j/snaploader/NativeBinaryLoader.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/snaploader/src/main/java/electrostatic4j/snaploader/NativeBinaryLoader.java b/snaploader/src/main/java/electrostatic4j/snaploader/NativeBinaryLoader.java index 1731998..292ccc3 100644 --- a/snaploader/src/main/java/electrostatic4j/snaploader/NativeBinaryLoader.java +++ b/snaploader/src/main/java/electrostatic4j/snaploader/NativeBinaryLoader.java @@ -183,12 +183,13 @@ public NativeBinaryLoader loadLibrary(LoadingCriterion criterion) throws Excepti throw new IllegalArgumentException("Native library data structures cannot be null!"); } // commands and loads the library from the system directories - if (NativeVariant.Os.isAndroid() || criterion == LoadingCriterion.SYSTEM_LOAD) { + if (NativeVariant.Os.isAndroid() || + criterion == LoadingCriterion.SYSTEM_LOAD) { loadSystemBinary(nativeDynamicLibrary); loadingCriterion = LoadingCriterion.SYSTEM_LOAD; return this; } - if (criterion == LoadingCriterion.INCREMENTAL_LOADING && nativeDynamicLibrary.isExtracted()) { + if (criterion == LoadingCriterion.INCREMENTAL_LOADING) { loadBinary(nativeDynamicLibrary, criterion); loadingCriterion = LoadingCriterion.INCREMENTAL_LOADING; return this; From cd04c38d5654470681f79c88d5196fe9d356b3c5 Mon Sep 17 00:00:00 2001 From: pavl-g Date: Sun, 4 Oct 2026 20:55:23 +0300 Subject: [PATCH 3/7] snaploader-examples: introduced a standard way to deal with broken files --- .../examples/TestBrokenFeatures.java | 134 ++++++++++++++++++ 1 file changed, 134 insertions(+) create mode 100644 snaploader-examples/src/main/java/electrostatic4j/snaploader/examples/TestBrokenFeatures.java diff --git a/snaploader-examples/src/main/java/electrostatic4j/snaploader/examples/TestBrokenFeatures.java b/snaploader-examples/src/main/java/electrostatic4j/snaploader/examples/TestBrokenFeatures.java new file mode 100644 index 0000000..f9b1d9a --- /dev/null +++ b/snaploader-examples/src/main/java/electrostatic4j/snaploader/examples/TestBrokenFeatures.java @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2023-2024, 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; + +import electrostatic4j.snaploader.LibraryInfo; +import electrostatic4j.snaploader.NativeBinaryLoader; +import electrostatic4j.snaploader.filesystem.DirectoryPath; +import electrostatic4j.snaploader.filesystem.FileExtractionListener; +import electrostatic4j.snaploader.filesystem.FileExtractor; +import electrostatic4j.snaploader.filesystem.FileLocator; +import electrostatic4j.snaploader.platform.util.DefaultDynamicLibraries; +import electrostatic4j.snaploader.platform.NativeDynamicLibrary; +import electrostatic4j.snaploader.platform.util.NativeVariant; +import electrostatic4j.snaploader.platform.util.PlatformPredicate; +import electrostatic4j.snaploader.platform.util.DefaultPropertiesProvider; +import electrostatic4j.snaploader.LoadingCriterion; + +/** + * Tests basic features of the {@link NativeBinaryLoader} API. + * + * @author pavl_g + */ +public final class TestBrokenFeatures { + + protected static final LibraryInfo libraryInfo = new LibraryInfo(getJarFilePath(), + new DirectoryPath(DefaultDynamicLibraries.LINUX_X86.getPlatformDirectory()), + getLibraryBaseName(), + getLibrariesAbsolutePath()); + + protected static NativeBinaryLoader loader; + + public static final NativeDynamicLibrary[] libraries = new NativeDynamicLibrary[] { + DefaultDynamicLibraries.LINUX_X86, + new NativeDynamicLibrary("nonexistent/directory", "junk", PlatformPredicate.LINUX_X86_64), + new NativeDynamicLibrary("lib/windows/x86", "libjmealloc.dll", PlatformPredicate.WIN_X86), + new NativeDynamicLibrary("lib/windows/x86-64", "libjmealloc.dll", PlatformPredicate.WIN_X86_64), + DefaultDynamicLibraries.MAC_X86, + DefaultDynamicLibraries.MAC_X86_64, + }; + + public static void main(String[] args) throws Exception { + if (loader == null) { + loader = new NativeBinaryLoader(libraryInfo); + } + loader.registerNativeLibraries(libraries).initPlatformLibrary(); + loader.setLoggingEnabled(false); + loader.setRetryWithCleanExtraction(true); + /* Native dynamic library properties */ + printDetails(loader); + loader.setLibraryExtractionListener(new FileExtractionListener() { + @Override + public void onExtractionCompleted(FileExtractor fileExtractor) { + System.out.println("Extracted Successfully!"); + } + + @Override + public void onExtractionFailure(FileExtractor fileExtractor, Throwable throwable) { + System.err.println(throwable.toString()); + } + + @Override + public void onExtractionFinalization(FileExtractor fileExtractor, FileLocator fileLocator) { + System.out.println("Finalization State has been reached!"); + } + }); + loader.loadLibrary(LoadingCriterion.INCREMENTAL_LOADING); + } + + protected static void printDetails(NativeBinaryLoader loader) { + System.out.println("--------------------------------------------------------------"); + System.out.println("OS: " + NativeVariant.OS_NAME.getProperty()); + System.out.println("ARCH: " + NativeVariant.OS_ARCH.getProperty()); + System.out.println("VM: " + NativeVariant.JVM.getProperty()); + System.out.println("--------------------------------------------------------------"); + System.out.println("Jar Path: " + loader.getNativeDynamicLibrary().getJarPath()); + System.out.println("Library Directory: " + loader.getNativeDynamicLibrary().getPlatformDirectory()); + System.out.println("Compressed library path: " + loader.getNativeDynamicLibrary().getCompressedLibrary()); + System.out.println("Extracted library absolute path: " + loader.getNativeDynamicLibrary().getExtractedLibrary()); + System.out.println("Is Extracted: " + loader.getNativeDynamicLibrary().isExtracted()); + System.out.println("--------------------------------------------------------------"); + } + + protected static DirectoryPath getLibrariesAbsolutePath() { + return new DirectoryPath(DefaultPropertiesProvider.USER_DIR.getSystemProperty(), "libs"); + } + + protected static DirectoryPath getJarFilePath() { + return new DirectoryPath(getLibrariesAbsolutePath().getPath(), getJarFile()); + } + + protected static String getNativeDynamicLibraryPath() { + return getLibrariesAbsolutePath().getPath() + + DefaultPropertiesProvider.FILE_SEPARATOR.getSystemProperty() + + "lib" + getLibraryBaseName() + ".so"; + } + + protected static String getJarFile() { + return "jme3-alloc-desktop-1.0.0-pre-gamma-2.jar"; + } + + protected static String getLibraryBaseName() { + return "jmealloc"; + } +} \ No newline at end of file From e87ffc84a8f7f96af2753e12f2fa2e6f368f6982 Mon Sep 17 00:00:00 2001 From: pavl-g Date: Sun, 4 Oct 2026 21:51:52 +0300 Subject: [PATCH 4/7] NativeBinaryLoader: library existence utilities and exception --- .../snaploader/NativeBinaryLoader.java | 9 +++- .../platform/NativeDynamicLibrary.java | 7 +++ .../throwable/LibraryNotFoundException.java | 47 +++++++++++++++++++ 3 files changed, 61 insertions(+), 2 deletions(-) create mode 100644 snaploader/src/main/java/electrostatic4j/snaploader/throwable/LibraryNotFoundException.java diff --git a/snaploader/src/main/java/electrostatic4j/snaploader/NativeBinaryLoader.java b/snaploader/src/main/java/electrostatic4j/snaploader/NativeBinaryLoader.java index 292ccc3..785ab05 100644 --- a/snaploader/src/main/java/electrostatic4j/snaploader/NativeBinaryLoader.java +++ b/snaploader/src/main/java/electrostatic4j/snaploader/NativeBinaryLoader.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023-2025, The Electrostatic-Sandbox Distributed Simulation Framework, jSnapLoader + * Copyright (c) 2023-2026, The Electrostatic-Sandbox Distributed Simulation Framework, jSnapLoader * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -33,6 +33,7 @@ package electrostatic4j.snaploader; import java.io.File; +import java.io.FileNotFoundException; import java.io.IOException; import java.nio.file.FileSystems; import java.util.Arrays; @@ -49,6 +50,7 @@ import electrostatic4j.snaploader.platform.util.NativeVariant; import electrostatic4j.snaploader.platform.util.PropertiesControllerNamespace; import electrostatic4j.snaploader.platform.util.PropertiesController; +import electrostatic4j.snaploader.throwable.LibraryNotFoundException; import electrostatic4j.snaploader.throwable.LoadingRetryExhaustionException; import electrostatic4j.snaploader.throwable.UnSupportedSystemError; import electrostatic4j.snaploader.util.CallingStackMetaData; @@ -337,6 +339,9 @@ protected void loadSystemBinary(NativeDynamicLibrary dynamicLibrary) { */ protected void loadBinary(NativeDynamicLibrary library, LoadingCriterion loadingCriterion) throws Exception { try { + if (!nativeDynamicLibrary.exists()) { + throw new LibraryNotFoundException("Library " + nativeDynamicLibrary.getExtractedLibrary() + " not found!"); + } System.load(library.getExtractedLibrary()); SnapLoaderLogger.log(Level.INFO, getClass().getName(), "loadBinary", "Successfully loaded library: " + library.getExtractedLibrary()); @@ -344,7 +349,7 @@ protected void loadBinary(NativeDynamicLibrary library, LoadingCriterion loading nativeBinaryLoadingListener.onLoadingSuccess(this, new CallingStackMetaData(Thread.currentThread().getStackTrace()[1], loadingCriterion)); } - } catch (final UnsatisfiedLinkError error) { + } catch (final UnsatisfiedLinkError | FileNotFoundException error) { SnapLoaderLogger.log(Level.SEVERE, getClass().getName(), "loadBinary", "Cannot load the dynamic library: " + library.getExtractedLibrary(), error); if (nativeBinaryLoadingListener != null) { diff --git a/snaploader/src/main/java/electrostatic4j/snaploader/platform/NativeDynamicLibrary.java b/snaploader/src/main/java/electrostatic4j/snaploader/platform/NativeDynamicLibrary.java index b252cc7..c9a1148 100644 --- a/snaploader/src/main/java/electrostatic4j/snaploader/platform/NativeDynamicLibrary.java +++ b/snaploader/src/main/java/electrostatic4j/snaploader/platform/NativeDynamicLibrary.java @@ -146,6 +146,13 @@ public void initWithLibraryInfo(LibraryInfo libraryInfo) { } } + public boolean exists() { + if (jarPath == null || libraryFile == null || directoryPath == null) { + return false; + } + return new File(getExtractedLibrary()).exists(); + } + /** * Retrieves the absolute path for the jar compression as specified by the {@link LibraryInfo} API. * diff --git a/snaploader/src/main/java/electrostatic4j/snaploader/throwable/LibraryNotFoundException.java b/snaploader/src/main/java/electrostatic4j/snaploader/throwable/LibraryNotFoundException.java new file mode 100644 index 0000000..a9df55d --- /dev/null +++ b/snaploader/src/main/java/electrostatic4j/snaploader/throwable/LibraryNotFoundException.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2023-2026, 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.throwable; + +import java.io.FileNotFoundException; + +/** + * A business exception that is thrown to indicate that a library, that is + * tried to be loaded, doesn't exist. + * + * @author pavl_g + */ +public class LibraryNotFoundException extends FileNotFoundException { + public LibraryNotFoundException(String message) { + super(message); + } +} From d2a5d622f84544361838b6008292e12f7ecdccb8 Mon Sep 17 00:00:00 2001 From: pavl-g Date: Sun, 4 Oct 2026 21:52:27 +0300 Subject: [PATCH 5/7] TestBrokenFeatures: enabled logging --- .../snaploader/examples/TestBrokenFeatures.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/snaploader-examples/src/main/java/electrostatic4j/snaploader/examples/TestBrokenFeatures.java b/snaploader-examples/src/main/java/electrostatic4j/snaploader/examples/TestBrokenFeatures.java index f9b1d9a..9c29434 100644 --- a/snaploader-examples/src/main/java/electrostatic4j/snaploader/examples/TestBrokenFeatures.java +++ b/snaploader-examples/src/main/java/electrostatic4j/snaploader/examples/TestBrokenFeatures.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023-2024, The Electrostatic-Sandbox Distributed Simulation Framework, jSnapLoader + * Copyright (c) 2023-2026, The Electrostatic-Sandbox Distributed Simulation Framework, jSnapLoader * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -73,7 +73,7 @@ public static void main(String[] args) throws Exception { loader = new NativeBinaryLoader(libraryInfo); } loader.registerNativeLibraries(libraries).initPlatformLibrary(); - loader.setLoggingEnabled(false); + loader.setLoggingEnabled(true); loader.setRetryWithCleanExtraction(true); /* Native dynamic library properties */ printDetails(loader); From dde94d946486918bca16d0cffacdb7a3cafc4c6b Mon Sep 17 00:00:00 2001 From: pavl-g Date: Sun, 4 Oct 2026 21:54:24 +0300 Subject: [PATCH 6/7] workflows/build-test: added TestBrokenFeatures task --- .github/workflows/build-test.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index 8a484e4..89e79a9 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -104,5 +104,9 @@ jobs: - name: Run TestCpuFeatures run: ./gradlew :snaploader-examples:TestCpuFeatures :snaploader-examples:run + - name: Run TestBrokenFeatures + run: ./gradlew :snaploader-examples:ReflectiveLauncher :snaploader-examples:run --args="electrostatic4j.snaploader.examples.TestBrokenFeatures" + - name: List extracted files run: ls ./snaploader-examples/libs/ + From 7856370c49d18d8bafa428d042b3dea1ac8bd27c Mon Sep 17 00:00:00 2001 From: pavl-g Date: Sun, 4 Oct 2026 22:21:36 +0300 Subject: [PATCH 7/7] TestBrokenFeatures: added a standard way to deal with exceptions and errors --- .../examples/TestBrokenFeatures.java | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/snaploader-examples/src/main/java/electrostatic4j/snaploader/examples/TestBrokenFeatures.java b/snaploader-examples/src/main/java/electrostatic4j/snaploader/examples/TestBrokenFeatures.java index 9c29434..f738052 100644 --- a/snaploader-examples/src/main/java/electrostatic4j/snaploader/examples/TestBrokenFeatures.java +++ b/snaploader-examples/src/main/java/electrostatic4j/snaploader/examples/TestBrokenFeatures.java @@ -34,6 +34,7 @@ import electrostatic4j.snaploader.LibraryInfo; import electrostatic4j.snaploader.NativeBinaryLoader; +import electrostatic4j.snaploader.NativeBinaryLoadingListener; import electrostatic4j.snaploader.filesystem.DirectoryPath; import electrostatic4j.snaploader.filesystem.FileExtractionListener; import electrostatic4j.snaploader.filesystem.FileExtractor; @@ -44,6 +45,7 @@ import electrostatic4j.snaploader.platform.util.PlatformPredicate; import electrostatic4j.snaploader.platform.util.DefaultPropertiesProvider; import electrostatic4j.snaploader.LoadingCriterion; +import electrostatic4j.snaploader.util.CallingStackMetaData; /** * Tests basic features of the {@link NativeBinaryLoader} API. @@ -73,10 +75,27 @@ public static void main(String[] args) throws Exception { loader = new NativeBinaryLoader(libraryInfo); } loader.registerNativeLibraries(libraries).initPlatformLibrary(); - loader.setLoggingEnabled(true); + loader.setLoggingEnabled(false); loader.setRetryWithCleanExtraction(true); /* Native dynamic library properties */ printDetails(loader); + loader.setNativeBinaryLoadingListener(new NativeBinaryLoadingListener() { + @Override + public void onLoadingSuccess(NativeBinaryLoader nativeBinaryLoader, CallingStackMetaData callingStackMetaData) { + + } + + @Override + public void onLoadingFailure(NativeBinaryLoader nativeBinaryLoader, CallingStackMetaData callingStackMetaData) { + System.err.println(callingStackMetaData.getErrorCause()); + } + + @Override + public void onRetryCriterionExecution(NativeBinaryLoader nativeBinaryLoader, CallingStackMetaData callingStackMetaData) { + + } + }); + loader.setLibraryExtractionListener(new FileExtractionListener() { @Override public void onExtractionCompleted(FileExtractor fileExtractor) { @@ -93,6 +112,7 @@ public void onExtractionFinalization(FileExtractor fileExtractor, FileLocator fi System.out.println("Finalization State has been reached!"); } }); + loader.loadLibrary(LoadingCriterion.INCREMENTAL_LOADING); }