Electrostat-Lab / jSnapLoader

A high-performance cross-platform native Library loader API for JVM Applications
BSD 3-Clause "New" or "Revised" License
3 stars 2 forks source link
android cpp dynamic-libraries dynamic-loader electrostat-lab electrostatic-sandbox java linux macos shared-libraries windows

jSnapLoader Codacy Badge

[]() []()

A high-performance cross-platform dynamic library loader API for JVM Applications, with highly modifiable system-specific registration using platform predicates.

Software Specification:

Item Description Predicate Calculus
Problem Definition The main problem that entailed building this project had been to have something similar to the JME's NativeLibraryLoader, but for a better version with more capabilities (such as: loading libraries from external Jars and having a generic file extraction interface). Sample = $P = [\cup_{n = 0}^N P_n]$ - Formula (Mapping relations in the form antecedents to descendants) = $\forall{P}\exists{S}\ F(P, S)$
Generalized Approach The generalized approach was deemed insufficient; as it will not add on the JME's NativeLibraryLoader, and thus the project will be insignificant. The generalized approach entails building something that directly encapsulates concrete buffered byte stream calls to ZipFileSystem interfaces for the native libraries only within the same classpath, and that actually is a bad practice to build APIs on concretions, instead as the Ontology entails, abstractions and using levels of indirections should play the most part in developing a new system. Sample = $S = [\cup_{n = 0}^N S_n]$ - Formula (Mapping relations in the form descendants to antecedents) = $\forall{S}\exists{P}\ F(S, P)$
jSnapLoader-specific Approach The jSnapLoader-specific approach is quite ingenuine, nevertheless it's not unique. The essential design entails the basic use of the System-Entity-Structure (SES) Framework; which essentially decomposes the the bulky actions of the library, first from the perspective of behavior into deterministic finite-states, and then from the structural perspective into the components that execute these states; the net result is having a freely-floating decomposed components. The next step was regrouping those components (using common properties from predicate calculus) and finding abstractions for them, and finally capturing relations between those components and similar systems (i.e., FileSystem APIs). This structural way of thinking has resulted in the production of beneficial compilation units that could serve other purposes and not entailed specifically to serve native library extraction and loading. $\forall{P{loader}}\ \exists{S{loader}}\ F(P{loader}, S{loader})$
Framework Decomposition The decomposition had been successfully inducted into a FileLocator interface with a validation strategy, a FileExtractor interface with the full ability to control native IO resources, and a LibraryLoader interface with the ability to build and register platform predicates to support any new unsupported systems. $$F = \cup{n = 0}^N \ (P{loader}, S{loader})_{n}$$ $$= [(P{file-locator}, S{file-locator}), (P{file-extractor}, S{file-extractor}),$$ $$(P{library-locator}, S'_{file-locator}), (P{library-extractor}, S'_{file-extractor}), (P{library-loader}, S{library-loader})]$$ ;where $S'{clazz}$ S prime represents a solution of class 'clazz'.
Common problems, robustness, and re-usability of solutions To extend the robustness (rigidity) of the solution, multiple similar problems could utilize classes of solutions from the same kind; for instance, the problem library-locator could use a solution of the class file-locator; thus this could be explained by a uniqueness formula as regard to the formal language. $$\forall{p}\ \in{P},\ \exists{s} \in{S}\,\ \forall{s'} \in{S} [(p{loader}, s{loader}) \land\ (p{loader}, s'_{loader})\ \implies\ [s{loader} = s'_{loader}]$$
Framework Enhancements The PlatformPredicate interface wasn't planned from the start; it has been added as an enhancement for the library to accomodate the changes and the addition of new platforms robustly without changing the internal API. In fact, I give credits to Jolt-jni for implicitly opening the vision to add them. Furthermore, the addition of the FileLocalizingListener, the FileExtractionListener, and the NativeLibraryLoadingListener binds the user API to the framework API giving jSnapLoader a more added robustness value overtime. $$E = \cup{n = 0}^N \ (E{loader}, S{loader})_{n}$$ $$= [(E{system-detection-listeners}, S{system-detection-listeners}), (E{locator-listeners}, S{locator-listeners}), (E{extractor-listeners}, S{extractor-listeners}),$$ $$(E{loader-listeners}, S_{loader-listeners}), (E{system-exception}, S\{system-exception})]$$
Credits for other systems Credits should go for the jSnapLoader's users, Serial4j, Jolt-jni, and Electrostatic4j. Those implicitly and continously have pushed the API over to become better. ---

Software Architectural Paradigm:

Architectural-Paradigm-Placement

Quick Building and running examples:

┌─[pavl-machine@pavl-machine]─[/home/twisted/GradleProjects/jSnapLoader]
└──╼ $./gradlew clean && \
      ./gradlew build && \
      ./gradlew :snaploader-examples:run

BUILD SUCCESSFUL in 943ms
2 actionable tasks: 2 executed

BUILD SUCCESSFUL in 1s
7 actionable tasks: 7 executed

BUILD SUCCESSFUL in 1s
4 actionable tasks: 1 executed, 3 up-to-date

Run specific example by name:

┌─[pavl-machine@pavl-machine]─[/home/twisted/GradleProjects/jSnapLoader]
└──╼ $./gradlew :snaploader-examples:TestZipExtractor \
                :snaploader-examples:run

BUILD SUCCESSFUL in 1s
4 actionable tasks: 2 executed, 2 up-to-date

Plug-and-play usage:

Project build files:

[build.gradle]

dependencies {
    implementation "io.github.electrostat-lab:snaploader:1.0.0-stable"
}

[settings.gradle]

pluginManagement {
    repositories {
        google()
        mavenCentral()
        gradlePluginPortal()
    }
}
dependencyResolutionManagement {
    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
    repositories {
        google()
        mavenCentral()
    }
}
...
// project Gradle modules includes

Library Implementation:

1) The most straightforward way:

final LibraryInfo info = new LibraryInfo(new DirectoryPath("lib/independent"), "basename", DirectoryPath.USER_DIR);
// or
// final LibraryInfo info = new LibraryInfo(DirectoryPath.CLASS_PATH,
//      new DirectoryPath("lib/independent"), "basename", DirectoryPath.USER_DIR);
final NativeBinaryLoader loader = new NativeBinaryLoader(info);
final NativeDynamicLibrary[] libraries = new NativeDynamicLibrary[] {
      new NativeDynamicLibrary("lib/linux/x86-64", PlatformPredicate.LINUX_X86_64),
      new NativeDynamicLibrary("lib/macos/arm-64", PlatformPredicate.MACOS_ARM_64),
      new NativeDynamicLibrary("lib/macos/x86-64", PlatformPredicate.MACOS_X86_64),
      new NativeDynamicLibrary("lib/win/x86-64", PlatformPredicate.WIN_X86_64)
      ...
};
loader.registerNativeLibraries(libraries).initPlatformLibrary();
loader.setLoggingEnabled(true);
loader.setRetryWithCleanExtraction(true);
try {
      loader.loadLibrary(LoadingCriterion.INCREMENTAL_LOADING);
} catch (IOException e) {
      Logger.getLogger(NativeBinaryLoader.class.getName()
            .log(Level.SEVERE, "Native loader has failed!", e);
}

2) A Superior control:

import java.nio.file.Path;
import java.nio.file.Paths;
...
// compatible with Java 8, Since 1.7
final Path compression = Paths.get(PropertiesProvider.USER_DIR.getSystemProperty(), "libs", "electrostatic4j.jar");
// create extraction path directory if not exists
final Path extractionPath = Files.createDirectories(Paths.get(PropertiesProvider.USER_DIR.getSystemProperty(), "libs", "natives"));
final LibraryInfo info = new LibraryInfo(new DirectoryPath(compression.toString()), new DirectoryPath("lib/independent"), "electrostatic4j", new DirectoryPath(extractionPath.toString()));
final NativeBinaryLoader loader = new NativeBinaryLoader(info);
final NativeDynamicLibrary[] libraries = new NativeDynamicLibrary[] {
      new NativeDynamicLibrary("lib/linux/x86-64", PlatformPredicate.LINUX_X86_64),
      new NativeDynamicLibrary("lib/macos/arm-64", PlatformPredicate.MACOS_ARM_64),
      new NativeDynamicLibrary("lib/macos/x86-64", PlatformPredicate.MACOS_X86_64),
      new NativeDynamicLibrary("lib/win/x86-64", PlatformPredicate.WIN_X86_64)
      ...
};
loader.registerNativeLibraries(libraries).initPlatformLibrary();
loader.setLoggingEnabled(true);
loader.setRetryWithCleanExtraction(true);
try {
      loader.loadLibrary(LoadingCriterion.INCREMENTAL_LOADING);
} catch (IOException e) {
      Logger.getLogger(NativeBinaryLoader.class.getName()
            .log(Level.SEVERE, "Native loader has failed!", e);
}

3) Full control (external Jar localizing, platform predicates, and platform-independent extraction paths):

Appendix:

Features:

Documentation-list:

Credits:

Projects:

Those projects had contributed implicitly and/or explicitly and must be recognized:

People:

I owe these people a cup of coffee for their gracious contributions, when we eventually meet:

Pure Science: