Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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);
}
}

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@

package electrostatic4j.snaploader;

import electrostatic4j.snaploader.util.CallingStackMetaData;

/**
* Represents an extraction/loading criterion type.
*
Expand Down Expand Up @@ -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)}.
* </p>
*/
SYSTEM_LOAD
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

/**
Expand Down Expand Up @@ -160,7 +161,7 @@ public NativeBinaryLoader initPlatformLibrary() throws UnSupportedSystemError {
* </p>
*
* <p>
* 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.
* </p>
*
Expand All @@ -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);
Expand All @@ -200,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;
}

/**
Expand All @@ -218,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<NativeDynamicLibrary> getRegisteredLibraries() {
Expand All @@ -230,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;
}

/**
Expand All @@ -272,14 +280,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));
}
}
}
Expand All @@ -289,28 +299,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) {
Expand Down Expand Up @@ -347,7 +361,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);
Expand Down Expand Up @@ -442,6 +456,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();
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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.
* <p>
* 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
*/
Expand All @@ -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,
Expand All @@ -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);
}
Original file line number Diff line number Diff line change
Expand Up @@ -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.
* <p>
* 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.
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
}
}