Skip to content

Commit 3961345

Browse files
authored
Merge pull request #42 from Electrostat-Lab/techdemo-api
Techdemo api: Adds a techdemo API demonstrating best practices
2 parents a7bb08c + e8dcf7c commit 3961345

File tree

7 files changed

+510
-27
lines changed

7 files changed

+510
-27
lines changed
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package electrostatic4j.snaploader.examples;
2+
3+
import electrostatic4j.snaploader.LibraryInfo;
4+
import electrostatic4j.snaploader.LoadingCriterion;
5+
import electrostatic4j.snaploader.examples.api.NativeDllLoader;
6+
import electrostatic4j.snaploader.filesystem.DirectoryPath;
7+
import electrostatic4j.snaploader.platform.NativeDynamicLibrary;
8+
import electrostatic4j.snaploader.platform.util.DefaultDynamicLibraries;
9+
import electrostatic4j.snaploader.platform.util.PlatformPredicate;
10+
11+
public final class TestNativeDllLoader {
12+
public static void main(String[] args) throws Exception {
13+
final NativeDynamicLibrary[] baseLibs = new NativeDynamicLibrary[] {
14+
DefaultDynamicLibraries.ANDROID_ALL,
15+
new NativeDynamicLibrary("linux/x86-64/com/github/stephengoldd", PlatformPredicate.LINUX_X86_64),
16+
new NativeDynamicLibrary("windows/x86-64/com/github/stephengoldd", PlatformPredicate.WIN_X86_64),
17+
};
18+
19+
final NativeDynamicLibrary[] cpuEnhancedLibs = new NativeDynamicLibrary[]{
20+
DefaultDynamicLibraries.ANDROID_ALL,
21+
new NativeDynamicLibrary("linux/x86-64-fma/com/github/stephengold", new PlatformPredicate(PlatformPredicate.LINUX_X86_64,
22+
"avx", "avx2", "bmi1", "f16c", "fma", "sse4_1", "sse4_2")),
23+
new NativeDynamicLibrary("windows/x86-64-avx2/com/github/stephengold", new PlatformPredicate(PlatformPredicate.WIN_X86_64,
24+
"avx", "avx2", "sse4_1", "sse4_2")),
25+
};
26+
final LibraryInfo info = new LibraryInfo(new DirectoryPath("linux/x86-64/com/github/stephengold"),
27+
"joltjnid", DirectoryPath.USER_DIR);
28+
final NativeDllLoader nativeDllLoader = new NativeDllLoader(baseLibs, cpuEnhancedLibs, info, true, true);
29+
nativeDllLoader.loadCpuEnhancedLibs(LoadingCriterion.INCREMENTAL_LOADING);
30+
}
31+
}

snaploader-examples/src/main/java/electrostatic4j/snaploader/examples/api/NativeDllLoader.java

Lines changed: 351 additions & 0 deletions
Large diffs are not rendered by default.

snaploader/src/main/java/electrostatic4j/snaploader/LoadingCriterion.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@
3232

3333
package electrostatic4j.snaploader;
3434

35+
import electrostatic4j.snaploader.util.CallingStackMetaData;
36+
3537
/**
3638
* Represents an extraction/loading criterion type.
3739
*
@@ -127,7 +129,8 @@ public enum LoadingCriterion {
127129
* This approach requires the library to be present on the system beforehand.
128130
* If the library is missing, the loading process will fail with an {@code UnsatisfiedLinkError}.
129131
* To ensure compatibility across different systems, consider providing a fallback
130-
* mechanism to extract the library dynamically when needed via {@link NativeBinaryLoadingListener#onLoadingFailure(NativeBinaryLoader)}.
132+
* mechanism to extract the library dynamically whenever required
133+
* via {@link NativeBinaryLoadingListener#onLoadingFailure(NativeBinaryLoader, CallingStackMetaData)}.
131134
* </p>
132135
*/
133136
SYSTEM_LOAD

snaploader/src/main/java/electrostatic4j/snaploader/NativeBinaryLoader.java

Lines changed: 38 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
import electrostatic4j.snaploader.platform.util.NativeVariant;
4848
import electrostatic4j.snaploader.throwable.LoadingRetryExhaustionException;
4949
import electrostatic4j.snaploader.throwable.UnSupportedSystemError;
50+
import electrostatic4j.snaploader.util.CallingStackMetaData;
5051
import electrostatic4j.snaploader.util.SnapLoaderLogger;
5152

5253
/**
@@ -160,7 +161,7 @@ public NativeBinaryLoader initPlatformLibrary() throws UnSupportedSystemError {
160161
* </p>
161162
*
162163
* <p>
163-
* Fallback loading routines can be implemented as needed via {@link NativeBinaryLoadingListener#onLoadingFailure(NativeBinaryLoader)}
164+
* Fallback loading routines can be implemented as needed via {@link NativeBinaryLoadingListener#onLoadingFailure(NativeBinaryLoader, CallingStackMetaData)}
164165
* and are left for the user applications.
165166
* </p>
166167
*
@@ -179,7 +180,7 @@ public NativeBinaryLoader loadLibrary(LoadingCriterion criterion) throws Excepti
179180
return this;
180181
}
181182
if (criterion == LoadingCriterion.INCREMENTAL_LOADING && nativeDynamicLibrary.isExtracted()) {
182-
loadBinary(nativeDynamicLibrary);
183+
loadBinary(nativeDynamicLibrary, criterion);
183184
return this;
184185
}
185186
cleanExtractBinary(nativeDynamicLibrary);
@@ -200,8 +201,9 @@ public NativeDynamicLibrary getNativeDynamicLibrary() {
200201
*
201202
* @param loggingEnabled true to enable logging, false otherwise
202203
*/
203-
public void setLoggingEnabled(boolean loggingEnabled) {
204+
public NativeBinaryLoader setLoggingEnabled(boolean loggingEnabled) {
204205
SnapLoaderLogger.setLoggingEnabled(loggingEnabled);
206+
return this;
205207
}
206208

207209
/**
@@ -218,8 +220,9 @@ public boolean isRetryWithCleanExtraction() {
218220
*
219221
* @param retryWithCleanExtraction true to enable the flag, false otherwise
220222
*/
221-
public void setRetryWithCleanExtraction(boolean retryWithCleanExtraction) {
223+
public NativeBinaryLoader setRetryWithCleanExtraction(boolean retryWithCleanExtraction) {
222224
this.retryWithCleanExtraction = retryWithCleanExtraction;
225+
return this;
223226
}
224227

225228
public List<NativeDynamicLibrary> getRegisteredLibraries() {
@@ -230,36 +233,41 @@ public NativeBinaryLoadingListener getNativeBinaryLoadingListener() {
230233
return nativeBinaryLoadingListener;
231234
}
232235

233-
public void setNativeBinaryLoadingListener(NativeBinaryLoadingListener nativeBinaryLoadingListener) {
236+
public NativeBinaryLoader setNativeBinaryLoadingListener(NativeBinaryLoadingListener nativeBinaryLoadingListener) {
234237
this.nativeBinaryLoadingListener = nativeBinaryLoadingListener;
238+
return this;
235239
}
236240

237241
public SystemDetectionListener getSystemDetectionListener() {
238242
return systemDetectionListener;
239243
}
240244

241-
public void setSystemDetectionListener(SystemDetectionListener systemDetectionListener) {
245+
public NativeBinaryLoader setSystemDetectionListener(SystemDetectionListener systemDetectionListener) {
242246
this.systemDetectionListener = systemDetectionListener;
247+
return this;
243248
}
244249

245250
public FileExtractionListener getLibraryExtractionListener() {
246251
return libraryExtractionListener;
247252
}
248253

249-
public void setLibraryExtractionListener(FileExtractionListener libraryExtractionListener) {
254+
public NativeBinaryLoader setLibraryExtractionListener(FileExtractionListener libraryExtractionListener) {
250255
this.libraryExtractionListener = libraryExtractionListener;
256+
return this;
251257
}
252258

253259
public FileLocalizingListener getLibraryLocalizingListener() {
254260
return libraryLocalizingListener;
255261
}
256262

257-
public void setLibraryLocalizingListener(FileLocalizingListener libraryLocalizingListener) {
263+
public NativeBinaryLoader setLibraryLocalizingListener(FileLocalizingListener libraryLocalizingListener) {
258264
this.libraryLocalizingListener = libraryLocalizingListener;
265+
return this;
259266
}
260267

261-
public void setMaxNumberOfLoadingFailure(int maxNumberOfLoadingFailure) {
268+
public NativeBinaryLoader setMaxNumberOfLoadingFailure(int maxNumberOfLoadingFailure) {
262269
this.maxNumberOfLoadingFailure = Math.abs(maxNumberOfLoadingFailure);
270+
return this;
263271
}
264272

265273
/**
@@ -272,14 +280,16 @@ protected void loadSystemBinary() {
272280
SnapLoaderLogger.log(Level.INFO, getClass().getName(), "loadSystemBinary", "Successfully loaded library from the system: "
273281
+ libraryInfo.getBaseName());
274282
if (nativeBinaryLoadingListener != null) {
275-
nativeBinaryLoadingListener.onLoadingSuccess(this);
283+
nativeBinaryLoadingListener.onLoadingSuccess(this,
284+
new CallingStackMetaData(Thread.currentThread().getStackTrace()[1], LoadingCriterion.SYSTEM_LOAD));
276285
}
277286
} catch (UnsatisfiedLinkError e) {
278287
SnapLoaderLogger.log(Level.SEVERE, getClass().getName(), "loadSystemBinary", "Cannot load the dynamic library from the system: "
279288
+ libraryInfo.getBaseName(), e);
280289
// fire failure routine for fallback criteria
281290
if (nativeBinaryLoadingListener != null) {
282-
nativeBinaryLoadingListener.onLoadingFailure(this);
291+
nativeBinaryLoadingListener.onLoadingFailure(this,
292+
new CallingStackMetaData(Thread.currentThread().getStackTrace()[1], LoadingCriterion.SYSTEM_LOAD, e));
283293
}
284294
}
285295
}
@@ -289,28 +299,32 @@ protected void loadSystemBinary() {
289299
* native library data structure defining the directory path.
290300
*
291301
* @param library the platform-specific library to load
302+
* @param loadingCriterion pass the loading criterion condition to the calling stack metadata structure
292303
* @throws IOException in case the binary to be extracted is not found on the specified jar
293304
* @throws LoadingRetryExhaustionException if the number of loading failure exceeds the specified
294305
* number.
295306
*/
296-
protected void loadBinary(NativeDynamicLibrary library) throws Exception {
307+
protected void loadBinary(NativeDynamicLibrary library, LoadingCriterion loadingCriterion) throws Exception {
297308
try {
298309
System.load(library.getExtractedLibrary());
299310
SnapLoaderLogger.log(Level.INFO, getClass().getName(), "loadBinary", "Successfully loaded library: "
300311
+ library.getExtractedLibrary());
301312
if (nativeBinaryLoadingListener != null) {
302-
nativeBinaryLoadingListener.onLoadingSuccess(this);
313+
nativeBinaryLoadingListener.onLoadingSuccess(this,
314+
new CallingStackMetaData(Thread.currentThread().getStackTrace()[1], loadingCriterion));
303315
}
304316
} catch (final UnsatisfiedLinkError error) {
305317
SnapLoaderLogger.log(Level.SEVERE, getClass().getName(), "loadBinary", "Cannot load the dynamic library: "
306318
+ library.getExtractedLibrary(), error);
307319
if (nativeBinaryLoadingListener != null) {
308-
nativeBinaryLoadingListener.onLoadingFailure(this);
320+
nativeBinaryLoadingListener.onLoadingFailure(this,
321+
new CallingStackMetaData(Thread.currentThread().getStackTrace()[1], loadingCriterion, error));
309322
}
310323
/* Retry with clean extract */
311324
if (isRetryWithCleanExtraction()) {
312325
if (nativeBinaryLoadingListener != null) {
313-
nativeBinaryLoadingListener.onRetryCriterionExecution(this);
326+
nativeBinaryLoadingListener.onRetryCriterionExecution(this,
327+
new CallingStackMetaData(Thread.currentThread().getStackTrace()[1], loadingCriterion));
314328
}
315329
// limit the number of retries to maxNumberOfLoadingFailure
316330
if (numberOfLoadingFailure >= maxNumberOfLoadingFailure) {
@@ -347,7 +361,7 @@ public void onExtractionCompleted(FileExtractor fileExtractor) {
347361
SnapLoaderLogger.log(Level.INFO, getClass().getName(), "cleanExtractBinary",
348362
"Extracted successfully to " + library.getExtractedLibrary());
349363
// load the native binary
350-
loadBinary(library);
364+
loadBinary(library, LoadingCriterion.CLEAN_EXTRACTION);
351365
} catch (Exception e) {
352366
SnapLoaderLogger.log(Level.SEVERE, getClass().getName(), "cleanExtractBinary",
353367
"Error while loading the binary!", e);
@@ -442,6 +456,14 @@ public void onFileLocalizationFailure(FileLocator locator, Throwable throwable)
442456
if (libraryLocalizingListener != null) {
443457
libraryLocalizingListener.onFileLocalizationFailure(locator, throwable);
444458
}
459+
460+
// make use of the loader listeners
461+
if (nativeBinaryLoadingListener != null) {
462+
// a file locator and extractor loader is always a CLEAN_EXTRACTION regarding
463+
// the loading criterion
464+
nativeBinaryLoadingListener.onLoadingFailure(NativeBinaryLoader.this,
465+
new CallingStackMetaData(Thread.currentThread().getStackTrace()[1], LoadingCriterion.CLEAN_EXTRACTION, throwable));
466+
}
445467
}
446468
});
447469
return (LibraryLocator) extractor.getFileLocator();

snaploader/src/main/java/electrostatic4j/snaploader/NativeBinaryLoadingListener.java

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2023-2024, The Electrostatic-Sandbox Distributed Simulation Framework, jSnapLoader
2+
* Copyright (c) 2023-2025, The Electrostatic-Sandbox Distributed Simulation Framework, jSnapLoader
33
* All rights reserved.
44
*
55
* Redistribution and use in source and binary forms, with or without
@@ -33,13 +33,14 @@
3333
package electrostatic4j.snaploader;
3434

3535
import electrostatic4j.snaploader.platform.NativeDynamicLibrary;
36+
import electrostatic4j.snaploader.util.CallingStackMetaData;
3637

3738
/**
3839
* Provides executable functions binding the user applications to
3940
* the loading lifecycle.
4041
* <p>
4142
* Note: All the functions on this interface are dispatched
42-
* by the {@link NativeBinaryLoader#loadBinary(NativeDynamicLibrary)}.
43+
* by the {@link NativeBinaryLoader#loadBinary(NativeDynamicLibrary, LoadingCriterion)}
4344
*
4445
* @author pavl_g
4546
*/
@@ -48,16 +49,19 @@ public interface NativeBinaryLoadingListener {
4849
/**
4950
* Dispatched when loading the system-specific binary has succeeded.
5051
*
51-
* @param nativeBinaryLoader the dispatching loader
52+
* @param nativeBinaryLoader the dispatching loader.
53+
* @param callingStackMetaData a data structure representing the meta data of the calling stack.
5254
*/
53-
void onLoadingSuccess(NativeBinaryLoader nativeBinaryLoader);
55+
void onLoadingSuccess(NativeBinaryLoader nativeBinaryLoader, CallingStackMetaData callingStackMetaData);
5456

5557
/**
5658
* Dispatched when loading the system-specific binary has failed.
5759
*
58-
* @param nativeBinaryLoader the dispatching loader
60+
* @param nativeBinaryLoader the dispatching loader.
61+
* @param callingStackMetaData a data structure representing the meta data of the calling stack.
5962
*/
60-
void onLoadingFailure(NativeBinaryLoader nativeBinaryLoader);
63+
void onLoadingFailure(NativeBinaryLoader nativeBinaryLoader,
64+
CallingStackMetaData callingStackMetaData);
6165

6266
/**
6367
* Dispatched when loading the system-specific binary has failed,
@@ -66,7 +70,8 @@ public interface NativeBinaryLoadingListener {
6670
* Note: this dispatching function could be overridden to add
6771
* your own anti-failure mechanisms (i.e., Retry Criterion).
6872
*
69-
* @param nativeBinaryLoader the dispatching loader
73+
* @param nativeBinaryLoader the dispatching loader.
74+
* @param callingStackMetaData a data structure representing the meta data of the calling stack.
7075
*/
71-
void onRetryCriterionExecution(NativeBinaryLoader nativeBinaryLoader);
76+
void onRetryCriterionExecution(NativeBinaryLoader nativeBinaryLoader, CallingStackMetaData callingStackMetaData);
7277
}

snaploader/src/main/java/electrostatic4j/snaploader/throwable/UnSupportedSystemError.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,13 @@
3333
package electrostatic4j.snaploader.throwable;
3434

3535
/**
36-
* A business error of type {@link UnsatisfiedLinkError} to indicate an unsupported system.
36+
* A business error of type {@link Error} to indicate an unsupported system.
3737
* <p>
3838
* This error is thrown when all the user-defined platform predicates are not met!
3939
*
4040
* @author pavl_g
4141
*/
42-
public class UnSupportedSystemError extends UnsatisfiedLinkError {
42+
public class UnSupportedSystemError extends Error {
4343

4444
/**
4545
* Thrown if the system detects an unsupported system binaries of the current OS.
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
/*
2+
* Copyright (c) 2023-2025, The Electrostatic-Sandbox Distributed Simulation Framework, jSnapLoader
3+
* All rights reserved.
4+
*
5+
* Redistribution and use in source and binary forms, with or without
6+
* modification, are permitted provided that the following conditions are
7+
* met:
8+
*
9+
* * Redistributions of source code must retain the above copyright
10+
* notice, this list of conditions and the following disclaimer.
11+
*
12+
* * Redistributions in binary form must reproduce the above copyright
13+
* notice, this list of conditions and the following disclaimer in the
14+
* documentation and/or other materials provided with the distribution.
15+
*
16+
* * Neither the name of 'Electrostatic-Sandbox' nor the names of its contributors
17+
* may be used to endorse or promote products derived from this software
18+
* without specific prior written permission.
19+
*
20+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21+
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22+
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23+
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24+
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25+
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26+
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27+
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28+
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29+
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30+
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31+
*/
32+
33+
package electrostatic4j.snaploader.util;
34+
35+
import electrostatic4j.snaploader.LoadingCriterion;
36+
37+
/**
38+
* A record-like structure representing immutable
39+
* state objects for a calling stack invoking the
40+
* library loading.
41+
*
42+
* @author pavl_g.
43+
*/
44+
public final class CallingStackMetaData {
45+
private final StackTraceElement callingStack;
46+
private final LoadingCriterion loadingCriterion;
47+
private Throwable errorCause;
48+
49+
public CallingStackMetaData(StackTraceElement callingStack, LoadingCriterion loadingCriterion,
50+
Throwable errorCause) {
51+
this(callingStack, loadingCriterion);
52+
this.errorCause = errorCause;
53+
}
54+
55+
public CallingStackMetaData(StackTraceElement callingStack, LoadingCriterion loadingCriterion) {
56+
this.callingStack = callingStack;
57+
this.loadingCriterion = loadingCriterion;
58+
}
59+
60+
public LoadingCriterion getLoadingCriterion() {
61+
return loadingCriterion;
62+
}
63+
64+
public StackTraceElement getCallingStack() {
65+
return callingStack;
66+
}
67+
68+
public Throwable getErrorCause() {
69+
return errorCause;
70+
}
71+
}

0 commit comments

Comments
 (0)