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/ + 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..a382c6c --- /dev/null +++ b/snaploader-examples/src/main/java/electrostatic4j/snaploader/examples/TestBrokenFeatures.java @@ -0,0 +1,160 @@ +/* + * 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 '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 java.io.PrintWriter; + +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; +import electrostatic4j.snaploader.filesystem.FileLocalizingListener; +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; +import electrostatic4j.snaploader.throwable.FileExtractionProcessingException; +import electrostatic4j.snaploader.throwable.FilesystemResourceInitializationException; +import electrostatic4j.snaploader.throwable.FilesystemResourceScavengingException; +import electrostatic4j.snaploader.throwable.LoadingRetryExhaustionException; +import electrostatic4j.snaploader.util.CallingStackMetaData; + +/** + * 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 FilesystemResourceScavengingException, LoadingRetryExhaustionException, FilesystemResourceInitializationException, FileExtractionProcessingException { + if (loader == null) { + loader = new NativeBinaryLoader(libraryInfo); + } + loader.registerNativeLibraries(libraries).initPlatformLibrary(); + loader.setLoggingEnabled(true); + 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) { + System.out.println("Extracted Successfully!"); + } + + @Override + public void onExtractionFinalization(FileExtractor fileExtractor, FileLocator fileLocator) { + System.out.println("Finalization State has been reached!"); + } + }); + + try { + loader.loadLibrary(LoadingCriterion.INCREMENTAL_LOADING); + } catch (Exception e) { + e.printStackTrace(System.err); + } + } + + 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 diff --git a/snaploader-examples/src/main/java/electrostatic4j/snaploader/examples/TestFilesystemMemoryLeak.java b/snaploader-examples/src/main/java/electrostatic4j/snaploader/examples/TestFilesystemMemoryLeak.java index 3eb4b4d..aff10ce 100644 --- a/snaploader-examples/src/main/java/electrostatic4j/snaploader/examples/TestFilesystemMemoryLeak.java +++ b/snaploader-examples/src/main/java/electrostatic4j/snaploader/examples/TestFilesystemMemoryLeak.java @@ -62,11 +62,6 @@ public void onExtractionCompleted(FileExtractor fileExtractor) { } - @Override - public void onExtractionFailure(FileExtractor fileExtractor, Throwable throwable) { - - } - @Override public void onExtractionFinalization(FileExtractor fileExtractor, FileLocator fileLocator) { Logger.getLogger(TestZipExtractor.class.getName()) diff --git a/snaploader-examples/src/main/java/electrostatic4j/snaploader/examples/TestZipExtractor.java b/snaploader-examples/src/main/java/electrostatic4j/snaploader/examples/TestZipExtractor.java index 2089a1e..28149d7 100644 --- a/snaploader-examples/src/main/java/electrostatic4j/snaploader/examples/TestZipExtractor.java +++ b/snaploader-examples/src/main/java/electrostatic4j/snaploader/examples/TestZipExtractor.java @@ -61,11 +61,6 @@ public void onExtractionCompleted(FileExtractor fileExtractor) { } - @Override - public void onExtractionFailure(FileExtractor fileExtractor, Throwable throwable) { - - } - @Override public void onExtractionFinalization(FileExtractor fileExtractor, FileLocator fileLocator) { try { 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 index f5bb372..2629e59 100644 --- a/snaploader-examples/src/main/java/electrostatic4j/snaploader/examples/api/NativeDllLoader.java +++ b/snaploader-examples/src/main/java/electrostatic4j/snaploader/examples/api/NativeDllLoader.java @@ -309,10 +309,6 @@ public void onRetryCriterionExecution(NativeBinaryLoader nativeBinaryLoader, Cal 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 diff --git a/snaploader/src/main/java/electrostatic4j/snaploader/ConcurrentNativeBinaryLoader.java b/snaploader/src/main/java/electrostatic4j/snaploader/ConcurrentNativeBinaryLoader.java index 1226689..665268e 100644 --- a/snaploader/src/main/java/electrostatic4j/snaploader/ConcurrentNativeBinaryLoader.java +++ b/snaploader/src/main/java/electrostatic4j/snaploader/ConcurrentNativeBinaryLoader.java @@ -35,6 +35,10 @@ import java.util.List; import java.util.concurrent.locks.ReentrantLock; import electrostatic4j.snaploader.platform.NativeDynamicLibrary; +import electrostatic4j.snaploader.throwable.FileExtractionProcessingException; +import electrostatic4j.snaploader.throwable.FilesystemResourceInitializationException; +import electrostatic4j.snaploader.throwable.FilesystemResourceScavengingException; +import electrostatic4j.snaploader.throwable.LoadingRetryExhaustionException; import electrostatic4j.snaploader.throwable.UnSupportedSystemError; /** @@ -71,7 +75,7 @@ public NativeBinaryLoader initPlatformLibrary() throws UnSupportedSystemError { } @Override - public NativeBinaryLoader loadLibrary(LoadingCriterion criterion) throws Exception { + public NativeBinaryLoader loadLibrary(LoadingCriterion criterion) throws FilesystemResourceScavengingException, LoadingRetryExhaustionException, FilesystemResourceInitializationException, FileExtractionProcessingException { try { /* CRITICAL SECTION STARTS */ lock.lock(); @@ -83,7 +87,7 @@ public NativeBinaryLoader loadLibrary(LoadingCriterion criterion) throws Excepti } @Override - protected void cleanExtractBinary(NativeDynamicLibrary library) throws Exception { + protected void cleanExtractBinary(NativeDynamicLibrary library) throws FilesystemResourceScavengingException, FileExtractionProcessingException, FilesystemResourceInitializationException { try { /* CRITICAL SECTION STARTS */ lock.lock(); diff --git a/snaploader/src/main/java/electrostatic4j/snaploader/NativeBinaryLoader.java b/snaploader/src/main/java/electrostatic4j/snaploader/NativeBinaryLoader.java index 1731998..8e053db 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 @@ -44,11 +44,14 @@ import electrostatic4j.snaploader.filesystem.FileLocalizingListener; import electrostatic4j.snaploader.filesystem.FileLocator; import electrostatic4j.snaploader.library.LibraryExtractor; -import electrostatic4j.snaploader.library.LibraryLocator; import electrostatic4j.snaploader.platform.NativeDynamicLibrary; import electrostatic4j.snaploader.platform.util.NativeVariant; import electrostatic4j.snaploader.platform.util.PropertiesControllerNamespace; import electrostatic4j.snaploader.platform.util.PropertiesController; +import electrostatic4j.snaploader.throwable.FileExtractionProcessingException; +import electrostatic4j.snaploader.throwable.FilesystemResourceInitializationException; +import electrostatic4j.snaploader.throwable.FilesystemResourceScavengingException; +import electrostatic4j.snaploader.throwable.LibraryNotFoundException; import electrostatic4j.snaploader.throwable.LoadingRetryExhaustionException; import electrostatic4j.snaploader.throwable.UnSupportedSystemError; import electrostatic4j.snaploader.util.CallingStackMetaData; @@ -176,19 +179,19 @@ public NativeBinaryLoader initPlatformLibrary() throws UnSupportedSystemError { * @param criterion the initial loading criterion, either {@link LoadingCriterion#INCREMENTAL_LOADING}, {@link LoadingCriterion#CLEAN_EXTRACTION} * or {@link LoadingCriterion#SYSTEM_LOAD} for loading native dlls from system directories. * @return this instance for chained invocations - * @throws IOException if the library to extract is not present in the jar filesystem */ - public NativeBinaryLoader loadLibrary(LoadingCriterion criterion) throws Exception { + public NativeBinaryLoader loadLibrary(LoadingCriterion criterion) throws FilesystemResourceScavengingException, LoadingRetryExhaustionException, FilesystemResourceInitializationException, FileExtractionProcessingException { if (nativeDynamicLibrary == null || libraryInfo == null) { 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; @@ -330,12 +333,15 @@ protected void loadSystemBinary(NativeDynamicLibrary dynamicLibrary) { * * @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 FilesystemResourceInitializationException 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, LoadingCriterion loadingCriterion) throws Exception { + protected void loadBinary(NativeDynamicLibrary library, LoadingCriterion loadingCriterion) throws FilesystemResourceInitializationException, FilesystemResourceScavengingException, LoadingRetryExhaustionException, FileExtractionProcessingException { 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()); @@ -343,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 (Throwable error) { SnapLoaderLogger.log(Level.SEVERE, getClass().getName(), "loadBinary", "Cannot load the dynamic library: " + library.getExtractedLibrary(), error); if (nativeBinaryLoadingListener != null) { @@ -372,10 +378,10 @@ protected void loadBinary(NativeDynamicLibrary library, LoadingCriterion loading * Cleanly extracts and loads the native binary to the current [user.dir]. * * @param library the platform-specific library to extract and load - * @throws IOException in case the binary to be extracted is not found on the specified jar, or an + * @throws FilesystemResourceInitializationException in case the binary to be extracted is not found on the specified jar, or an * interrupted I/O operation has occurred */ - protected void cleanExtractBinary(NativeDynamicLibrary library) throws Exception { + protected void cleanExtractBinary(NativeDynamicLibrary library) throws FilesystemResourceInitializationException, FilesystemResourceScavengingException, FileExtractionProcessingException { libraryExtractor = initializeLibraryExtractor(library); SnapLoaderLogger.log(Level.INFO, getClass().getName(), "cleanExtractBinary", "File extractor handler initialized!"); @@ -403,17 +409,6 @@ public void onExtractionCompleted(FileExtractor fileExtractor) { } } - @Override - public void onExtractionFailure(FileExtractor fileExtractor, Throwable throwable) { - SnapLoaderLogger.log(Level.SEVERE, getClass().getName(), - "cleanExtractBinary", "Extraction has failed!", throwable); - - // bind the extraction lifecycle to the user application - if (libraryExtractionListener != null) { - libraryExtractionListener.onExtractionFailure(fileExtractor, throwable); - } - } - @Override public void onExtractionFinalization(FileExtractor fileExtractor, FileLocator fileLocator) { try { @@ -432,6 +427,7 @@ public void onExtractionFinalization(FileExtractor fileExtractor, FileLocator fi } } }); + libraryExtractor.extract(); } @@ -441,61 +437,23 @@ public void onExtractionFinalization(FileExtractor fileExtractor, FileLocator fi * * @param library the native dynamic library to load * @return a new FileExtractor object that represents an output stream provider - * @throws IOException if the jar filesystem to be located is not found, or if the extraction destination is not found + * @throws FilesystemResourceInitializationException if the jar filesystem to be located is not found, or if the extraction destination is not found */ - protected FileExtractor initializeLibraryExtractor(NativeDynamicLibrary library) throws Exception { - FileExtractor extractor; - if (library.getJarPath() != null) { - // use an extractor with the external jar routine - extractor = new LibraryExtractor(new JarFile(library.getJarPath()), library.getCompressedLibrary(), library.getExtractedLibrary()); - } else { - // use an extractor with the classpath routine - extractor = new LibraryExtractor(library.getCompressedLibrary(), library.getExtractedLibrary()); - } - extractor.initialize(0); - final LibraryLocator fileLocator = preInitLibraryLocator(extractor); - fileLocator.initialize(0); - return extractor; - } - - protected LibraryLocator preInitLibraryLocator(FileExtractor extractor) { - extractor.getFileLocator().setFileLocalizingListener(new FileLocalizingListener() { - @Override - public void onFileLocalizationSuccess(FileLocator locator) { - SnapLoaderLogger.log(Level.INFO, getClass().getName(), "preInitLibraryLocator", - "Locating native libraries has succeeded!"); - - // bind the library locator lifecycle to the user application - if (libraryLocalizingListener != null) { - libraryLocalizingListener.onFileLocalizationSuccess(locator); - } - } - - @Override - public void onFileLocalizationFailure(FileLocator locator, Throwable throwable) { - SnapLoaderLogger.log(Level.SEVERE, getClass().getName(), "preInitLibraryLocator", - "Locating native libraries has failed!", throwable); + protected FileExtractor initializeLibraryExtractor(NativeDynamicLibrary library) throws FilesystemResourceInitializationException, FilesystemResourceScavengingException { + FileExtractor extractor; + if (library.getJarPath() != null) { try { - extractor.close(); - } catch (Exception e) { - SnapLoaderLogger.log(Level.SEVERE, getClass().getName(), - "initializeLibraryExtractor", "File locator closure failed!", e); - } - - // bind the library locator lifecycle to the user application - 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)); + // use an extractor with the external jar routine + extractor = new LibraryExtractor(new JarFile(library.getJarPath()), library.getCompressedLibrary(), library.getExtractedLibrary()); + } catch (IOException e) { + throw new FilesystemResourceInitializationException("Error while initializing the file extractor!", e); } + } else { + // use an extractor with the classpath routine + extractor = new LibraryExtractor(library.getCompressedLibrary(), library.getExtractedLibrary()); } - }); - return (LibraryLocator) extractor.getFileLocator(); + extractor.initialize(0); + + return extractor; } } diff --git a/snaploader/src/main/java/electrostatic4j/snaploader/filesystem/ConcurrentFileExtractor.java b/snaploader/src/main/java/electrostatic4j/snaploader/filesystem/ConcurrentFileExtractor.java index 522e979..fb917f3 100644 --- a/snaploader/src/main/java/electrostatic4j/snaploader/filesystem/ConcurrentFileExtractor.java +++ b/snaploader/src/main/java/electrostatic4j/snaploader/filesystem/ConcurrentFileExtractor.java @@ -35,6 +35,9 @@ import java.io.IOException; import java.util.concurrent.locks.ReentrantLock; +import electrostatic4j.snaploader.throwable.FileExtractionProcessingException; +import electrostatic4j.snaploader.throwable.FilesystemResourceScavengingException; + /** * A thread-safe implementation of the filesystem extractor API. * @@ -65,7 +68,7 @@ protected ConcurrentFileExtractor() { } @Override - public void extract() throws IOException { + public void extract() throws FileExtractionProcessingException, FilesystemResourceScavengingException { try { /* CRITICAL SECTION STARTS */ lock.lock(); diff --git a/snaploader/src/main/java/electrostatic4j/snaploader/filesystem/FileExtractionListener.java b/snaploader/src/main/java/electrostatic4j/snaploader/filesystem/FileExtractionListener.java index 5d30450..72090d9 100644 --- a/snaploader/src/main/java/electrostatic4j/snaploader/filesystem/FileExtractionListener.java +++ b/snaploader/src/main/java/electrostatic4j/snaploader/filesystem/FileExtractionListener.java @@ -55,15 +55,6 @@ public interface FileExtractionListener { */ void onExtractionCompleted(FileExtractor fileExtractor); - /** - * Dispatched when the extraction process has failed with a throwable - * component. - * - * @param fileExtractor the extractor in-command. - * @param throwable the throwable captured from the FileExtractor API. - */ - void onExtractionFailure(FileExtractor fileExtractor, Throwable throwable); - /** * Dispatched when the extraction process is finalized, at this point, manually * freeing active resources should be attained. diff --git a/snaploader/src/main/java/electrostatic4j/snaploader/filesystem/FileExtractor.java b/snaploader/src/main/java/electrostatic4j/snaploader/filesystem/FileExtractor.java index db394bb..ecd3eda 100644 --- a/snaploader/src/main/java/electrostatic4j/snaploader/filesystem/FileExtractor.java +++ b/snaploader/src/main/java/electrostatic4j/snaploader/filesystem/FileExtractor.java @@ -32,10 +32,11 @@ package electrostatic4j.snaploader.filesystem; +import electrostatic4j.snaploader.throwable.FileExtractionProcessingException; import electrostatic4j.snaploader.throwable.FilesystemResourceInitializationException; +import electrostatic4j.snaploader.throwable.FilesystemResourceScavengingException; import electrostatic4j.snaploader.util.SnapLoaderLogger; import electrostatic4j.snaploader.util.StreamObjectValidator; - import java.io.*; import java.util.logging.Level; @@ -66,6 +67,8 @@ public class FileExtractor implements OutputStreamProvider { */ protected String destination; + protected File extractableFile; + private static final int EOF = -1; /* End-of-filesystem */ /** @@ -86,7 +89,11 @@ protected FileExtractor() { } @Override - public void initialize(int size) throws Exception { + public void initialize(int size) throws FilesystemResourceInitializationException, + FilesystemResourceScavengingException { + if (fileLocator == null) { + throw new IllegalArgumentException("File locator cannot be null!"); + } // 1) sanity-check for double initializing // 2) sanity-check for pre-initialization using other routines if (this.fileOutputStream != null) { @@ -95,6 +102,7 @@ public void initialize(int size) throws Exception { return; } try { + fileLocator.initialize(size); if (size > 0) { this.fileOutputStream = new BufferedOutputStream( new FileOutputStream(destination), size); @@ -103,10 +111,16 @@ 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) { - close(); + try { + close(); + } catch (Exception cause) { + throw new FilesystemResourceScavengingException( + "Failed to close the file extractor handler #" + getHashKey(), e); + } throw new FilesystemResourceInitializationException( "Failed to initialize the file extractor handler #" + getHashKey(), e); } @@ -120,11 +134,8 @@ public void initialize(int size) throws Exception { * attained either through the extraction completed and failure listeners, or through a try-with * resources. * - * @throws IOException if the input/output streams has failed or an interrupted I/O operation has occurred. - * @throws FileNotFoundException if the file locator has failed to locate the file inside the compression - * for the extraction process. */ - public void extract() throws IOException, FileNotFoundException { + public void extract() throws FileExtractionProcessingException, FilesystemResourceScavengingException { try { /* uses buffered streams */ /* buffered byte streams provide a constant memory allocation @@ -149,9 +160,20 @@ public void extract() throws IOException, FileNotFoundException { fileExtractionListener.onExtractionCompleted(this); } } catch (Exception e) { - if (fileExtractionListener != null) { - fileExtractionListener.onExtractionFailure(this, 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(); + try { + close(); + } catch (Exception ex) { + throw new FilesystemResourceScavengingException(); } + throw new FileExtractionProcessingException("", e); // release the native resources anyway! } finally { if (fileExtractionListener != null) { @@ -160,6 +182,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. + */ + public boolean delete() { + if (extractableFile == null || !extractableFile.exists()) { + return false; + } + return extractableFile.delete(); + } + @Override public void close() throws Exception { if (fileOutputStream != null) { diff --git a/snaploader/src/main/java/electrostatic4j/snaploader/filesystem/FileLocalizingListener.java b/snaploader/src/main/java/electrostatic4j/snaploader/filesystem/FileLocalizingListener.java index ce8244e..4d546ae 100644 --- a/snaploader/src/main/java/electrostatic4j/snaploader/filesystem/FileLocalizingListener.java +++ b/snaploader/src/main/java/electrostatic4j/snaploader/filesystem/FileLocalizingListener.java @@ -48,14 +48,4 @@ public interface FileLocalizingListener { * @param locator the associated file locator object. */ void onFileLocalizationSuccess(FileLocator locator); - - /** - * Dispatched whenever the file input stream is invalid, hence the specified - * file cannot be localized for the extraction process. - * - * @param locator the associated file locator object. - * @param throwable the throwable object, typically indicating a failure - * to locate the file inside the compression. - */ - void onFileLocalizationFailure(FileLocator locator, Throwable throwable); } diff --git a/snaploader/src/main/java/electrostatic4j/snaploader/filesystem/FileLocator.java b/snaploader/src/main/java/electrostatic4j/snaploader/filesystem/FileLocator.java index 40aef50..83cd9f9 100644 --- a/snaploader/src/main/java/electrostatic4j/snaploader/filesystem/FileLocator.java +++ b/snaploader/src/main/java/electrostatic4j/snaploader/filesystem/FileLocator.java @@ -33,6 +33,7 @@ package electrostatic4j.snaploader.filesystem; import electrostatic4j.snaploader.throwable.FilesystemResourceInitializationException; +import electrostatic4j.snaploader.throwable.FilesystemResourceScavengingException; import electrostatic4j.snaploader.util.SnapLoaderLogger; import electrostatic4j.snaploader.util.StreamObjectValidator; @@ -111,10 +112,10 @@ protected FileLocator() { * * @param size the size of the buffered IO in bytes or zero * for auto filesystem size - * @throws IOException if an I/O error has occurred. */ @Override - public void initialize(int size) throws IOException { + public void initialize(int size) throws FilesystemResourceInitializationException, + FilesystemResourceScavengingException { // 1) sanity-check for double initializing if (this.fileInputStream != null) { SnapLoaderLogger.log(Level.INFO, getClass().getName(), "initialize(int)", @@ -138,21 +139,22 @@ public void initialize(int size) throws IOException { fileLocalizingListener.onFileLocalizationSuccess(this); } } catch (Exception e) { - close(); + try { + close(); + } catch (IOException ex) { + throw new FilesystemResourceScavengingException("File locator resources closure failed!", ex); + } // fire the failure listener when file localization fails and pass // the causative exception - if (fileLocalizingListener != null) { - fileLocalizingListener.onFileLocalizationFailure(this, e); - } + throw new FilesystemResourceInitializationException("File locator initialization failed!", e); } } /** * Commands for the classpath routines. * - * @throws FilesystemResourceInitializationException if the classpath routine fails to locate the file. */ - protected void classPathRoutine() throws FilesystemResourceInitializationException { + protected void classPathRoutine() { SnapLoaderLogger.log(Level.INFO, getClass().getName(), "initialize(int)", "File locator initialized using classpath routine with hash key #" + getHashKey()); // Use the AppClassLoader, a BuiltinClassLoader to get the resources from the classpath @@ -174,7 +176,7 @@ protected void classPathRoutine() throws FilesystemResourceInitializationExcepti * (warning: file expansion and truncation rules are applied). * @throws IOException if an I/O error has occurred. */ - protected void externalCompressionRoutine(int size) throws IOException { + protected void externalCompressionRoutine(int size) throws IOException, FilesystemResourceInitializationException { final ZipEntry zipEntry = compression.getEntry(filePath); StreamObjectValidator.validateAndThrow(zipEntry, StreamObjectValidator.COMPRESSION_FILE_LOCALIZING_FAIL); if (size > 0) { diff --git a/snaploader/src/main/java/electrostatic4j/snaploader/platform/NativeDynamicLibrary.java b/snaploader/src/main/java/electrostatic4j/snaploader/platform/NativeDynamicLibrary.java index b252cc7..7c658c5 100644 --- a/snaploader/src/main/java/electrostatic4j/snaploader/platform/NativeDynamicLibrary.java +++ b/snaploader/src/main/java/electrostatic4j/snaploader/platform/NativeDynamicLibrary.java @@ -39,6 +39,7 @@ import electrostatic4j.snaploader.platform.util.NativeVariant; import electrostatic4j.snaploader.platform.util.PlatformPredicate; import electrostatic4j.snaploader.platform.util.DefaultPropertiesProvider; +import electrostatic4j.snaploader.throwable.LibraryInitException; /** * Represents a filesystem to a platform-specific binary inside @@ -146,6 +147,13 @@ public void initWithLibraryInfo(LibraryInfo libraryInfo) { } } + public boolean exists() throws LibraryInitException { + if (libraryFile == null) { + throw new LibraryInitException("Library file name cannot be null!"); + } + 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/FileExtractionProcessingException.java b/snaploader/src/main/java/electrostatic4j/snaploader/throwable/FileExtractionProcessingException.java new file mode 100644 index 0000000..9ae5302 --- /dev/null +++ b/snaploader/src/main/java/electrostatic4j/snaploader/throwable/FileExtractionProcessingException.java @@ -0,0 +1,23 @@ +package electrostatic4j.snaploader.throwable; + +public class FileExtractionProcessingException extends RuntimeException { + public FileExtractionProcessingException(String message) { + super(message); + } + + public FileExtractionProcessingException() { + } + + public FileExtractionProcessingException(String message, Throwable cause) { + super(message, cause); + } + + public FileExtractionProcessingException(Throwable cause) { + super(cause); + } + + public FileExtractionProcessingException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + } + +} diff --git a/snaploader/src/main/java/electrostatic4j/snaploader/throwable/LibraryInitException.java b/snaploader/src/main/java/electrostatic4j/snaploader/throwable/LibraryInitException.java new file mode 100644 index 0000000..035a5ef --- /dev/null +++ b/snaploader/src/main/java/electrostatic4j/snaploader/throwable/LibraryInitException.java @@ -0,0 +1,7 @@ +package electrostatic4j.snaploader.throwable; + +public class LibraryInitException extends RuntimeException { + public LibraryInitException(String message) { + super(message); + } +} 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); + } +} diff --git a/snaploader/src/main/java/electrostatic4j/snaploader/util/StreamObjectValidator.java b/snaploader/src/main/java/electrostatic4j/snaploader/util/StreamObjectValidator.java index cae68d7..c116e5f 100644 --- a/snaploader/src/main/java/electrostatic4j/snaploader/util/StreamObjectValidator.java +++ b/snaploader/src/main/java/electrostatic4j/snaploader/util/StreamObjectValidator.java @@ -48,14 +48,14 @@ public final class StreamObjectValidator { private StreamObjectValidator() { } - public static void validateAndThrow(final StreamProvider object, final String message, final Throwable cause) { + public static void validateAndThrow(final StreamProvider object, final String message, final Throwable cause) throws FilesystemResourceInitializationException { if (object != null) { return; } throw new FilesystemResourceInitializationException(message, cause); } - public static void validateAndThrow(final Object object, final String message) { + public static void validateAndThrow(final Object object, final String message) throws FilesystemResourceInitializationException { if (object != null) { return; }