diff --git a/android-application/.gitignore b/android-application/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/android-application/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/android-application/build.gradle b/android-application/build.gradle new file mode 100644 index 0000000..bb75e62 --- /dev/null +++ b/android-application/build.gradle @@ -0,0 +1,56 @@ +plugins { + id 'com.android.application' +} + +android { + namespace 'electrostat.lab.snaploaderapp' + compileSdk 34 + + defaultConfig { + applicationId "electrostat.lab.snaploaderapp" + minSdk 24 + targetSdk 34 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + packagingOptions { + exclude 'META-INF/AL2.0' + exclude 'META-INF/LGPL2.1' + } +} + +dependencies { + + implementation (project(path: ':snaploader')) { + exclude group: 'com.github.oshi', module: 'oshi-core' + } + + implementation 'net.java.dev.jna:jna:5.16.0@aar' + +// implementation (project(path: ':snaploader-examples')) { +// exclude group: 'com.github.oshi', module: 'oshi-core' +// exclude group: 'io.github.software-hardware-codesign', module: 'jme3-alloc-desktop' +// } + + implementation 'io.github.software-hardware-codesign:jme3-alloc-android:1.0.0-pre-gamma-2' + + implementation 'androidx.appcompat:appcompat:1.6.1' + implementation 'com.google.android.material:material:1.8.0' + implementation 'androidx.constraintlayout:constraintlayout:2.1.4' + testImplementation 'junit:junit:4.13.2' + androidTestImplementation 'androidx.test.ext:junit:1.1.5' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' +} \ No newline at end of file diff --git a/android-application/proguard-rules.pro b/android-application/proguard-rules.pro new file mode 100644 index 0000000..d539d6d --- /dev/null +++ b/android-application/proguard-rules.pro @@ -0,0 +1,24 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile +-dontwarn java.awt.* +-keep class com.sun.jna.* { *; } +-keepclassmembers class * extends com.sun.jna.* { public *; } \ No newline at end of file diff --git a/android-application/src/main/AndroidManifest.xml b/android-application/src/main/AndroidManifest.xml new file mode 100644 index 0000000..8f9ed28 --- /dev/null +++ b/android-application/src/main/AndroidManifest.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android-application/src/main/java/electrostat/lab/snaploaderapp/MainActivity.java b/android-application/src/main/java/electrostat/lab/snaploaderapp/MainActivity.java new file mode 100644 index 0000000..280c3b7 --- /dev/null +++ b/android-application/src/main/java/electrostat/lab/snaploaderapp/MainActivity.java @@ -0,0 +1,66 @@ +package electrostat.lab.snaploaderapp; + +import android.os.Bundle; +import android.widget.RelativeLayout; +import androidx.annotation.Nullable; +import androidx.appcompat.app.AppCompatActivity; +import electrostatic4j.snaploader.LibraryInfo; +import electrostatic4j.snaploader.LoadingCriterion; +import electrostatic4j.snaploader.NativeBinaryLoader; +import electrostatic4j.snaploader.filesystem.DirectoryPath; +import electrostatic4j.snaploader.platform.NativeDynamicLibrary; +import electrostatic4j.snaploader.platform.util.DefaultDynamicLibraries; +import electrostatic4j.snaploader.platform.util.NativeVariant; +import electrostatic4j.snaploader.platform.util.PlatformPredicate; + +/** + * Tests jSnapLoader on Android Runtime. + * + * @author pavl_g. + */ +public class MainActivity extends AppCompatActivity { + static { + // tries to load the android library + try { + final LibraryInfo libraryInfo = new LibraryInfo(new DirectoryPath("lib/independent"), "jmealloc", DirectoryPath.USER_DIR); + final NativeDynamicLibrary[] libraries = new NativeDynamicLibrary[] { + DefaultDynamicLibraries.LINUX_X86, + DefaultDynamicLibraries.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, + DefaultDynamicLibraries.ANDROID_ALL, + }; + final NativeBinaryLoader loader = new NativeBinaryLoader(libraryInfo); + loader.registerNativeLibraries(libraries).initPlatformLibrary(); + loader.setLoggingEnabled(true); + loader.setRetryWithCleanExtraction(true); + /* Native dynamic library properties */ + printDetails(loader); + loader.loadLibrary(LoadingCriterion.INCREMENTAL_LOADING); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(new RelativeLayout(getApplicationContext())); + } + + public 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("--------------------------------------------------------------"); + } +} diff --git a/android-application/src/main/res/drawable/ic_launcher_background.xml b/android-application/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000..07d5da9 --- /dev/null +++ b/android-application/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/android-application/src/main/res/drawable/ic_launcher_foreground.xml b/android-application/src/main/res/drawable/ic_launcher_foreground.xml new file mode 100644 index 0000000..2b068d1 --- /dev/null +++ b/android-application/src/main/res/drawable/ic_launcher_foreground.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/android-application/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/android-application/src/main/res/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 0000000..6f3b755 --- /dev/null +++ b/android-application/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/android-application/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/android-application/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml new file mode 100644 index 0000000..6f3b755 --- /dev/null +++ b/android-application/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/android-application/src/main/res/mipmap-hdpi/ic_launcher.webp b/android-application/src/main/res/mipmap-hdpi/ic_launcher.webp new file mode 100644 index 0000000..c209e78 Binary files /dev/null and b/android-application/src/main/res/mipmap-hdpi/ic_launcher.webp differ diff --git a/android-application/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/android-application/src/main/res/mipmap-hdpi/ic_launcher_round.webp new file mode 100644 index 0000000..b2dfe3d Binary files /dev/null and b/android-application/src/main/res/mipmap-hdpi/ic_launcher_round.webp differ diff --git a/android-application/src/main/res/mipmap-mdpi/ic_launcher.webp b/android-application/src/main/res/mipmap-mdpi/ic_launcher.webp new file mode 100644 index 0000000..4f0f1d6 Binary files /dev/null and b/android-application/src/main/res/mipmap-mdpi/ic_launcher.webp differ diff --git a/android-application/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/android-application/src/main/res/mipmap-mdpi/ic_launcher_round.webp new file mode 100644 index 0000000..62b611d Binary files /dev/null and b/android-application/src/main/res/mipmap-mdpi/ic_launcher_round.webp differ diff --git a/android-application/src/main/res/mipmap-xhdpi/ic_launcher.webp b/android-application/src/main/res/mipmap-xhdpi/ic_launcher.webp new file mode 100644 index 0000000..948a307 Binary files /dev/null and b/android-application/src/main/res/mipmap-xhdpi/ic_launcher.webp differ diff --git a/android-application/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/android-application/src/main/res/mipmap-xhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..1b9a695 Binary files /dev/null and b/android-application/src/main/res/mipmap-xhdpi/ic_launcher_round.webp differ diff --git a/android-application/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/android-application/src/main/res/mipmap-xxhdpi/ic_launcher.webp new file mode 100644 index 0000000..28d4b77 Binary files /dev/null and b/android-application/src/main/res/mipmap-xxhdpi/ic_launcher.webp differ diff --git a/android-application/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/android-application/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..9287f50 Binary files /dev/null and b/android-application/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp differ diff --git a/android-application/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/android-application/src/main/res/mipmap-xxxhdpi/ic_launcher.webp new file mode 100644 index 0000000..aa7d642 Binary files /dev/null and b/android-application/src/main/res/mipmap-xxxhdpi/ic_launcher.webp differ diff --git a/android-application/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/android-application/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..9126ae3 Binary files /dev/null and b/android-application/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp differ diff --git a/android-application/src/main/res/values-night/themes.xml b/android-application/src/main/res/values-night/themes.xml new file mode 100644 index 0000000..db8e1ea --- /dev/null +++ b/android-application/src/main/res/values-night/themes.xml @@ -0,0 +1,10 @@ + + + + \ No newline at end of file diff --git a/android-application/src/main/res/values/colors.xml b/android-application/src/main/res/values/colors.xml new file mode 100644 index 0000000..f8c6127 --- /dev/null +++ b/android-application/src/main/res/values/colors.xml @@ -0,0 +1,10 @@ + + + #FFBB86FC + #FF6200EE + #FF3700B3 + #FF03DAC5 + #FF018786 + #FF000000 + #FFFFFFFF + \ No newline at end of file diff --git a/android-application/src/main/res/values/strings.xml b/android-application/src/main/res/values/strings.xml new file mode 100644 index 0000000..75db84a --- /dev/null +++ b/android-application/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + SnapLoaderApplication + \ No newline at end of file diff --git a/android-application/src/main/res/values/themes.xml b/android-application/src/main/res/values/themes.xml new file mode 100644 index 0000000..fc359ae --- /dev/null +++ b/android-application/src/main/res/values/themes.xml @@ -0,0 +1,10 @@ + + + + \ No newline at end of file diff --git a/android-application/src/test/java/electrostat/lab/snaploaderapp/ExampleUnitTest.java b/android-application/src/test/java/electrostat/lab/snaploaderapp/ExampleUnitTest.java new file mode 100644 index 0000000..716f341 --- /dev/null +++ b/android-application/src/test/java/electrostat/lab/snaploaderapp/ExampleUnitTest.java @@ -0,0 +1,17 @@ +package electrostat.lab.snaploaderapp; + +import org.junit.Test; + +import static org.junit.Assert.*; + +/** + * Example local unit test, which will execute on the development machine (host). + * + * @see Testing documentation + */ +public class ExampleUnitTest { + @Test + public void addition_isCorrect() { + assertEquals(4, 2 + 2); + } +} \ No newline at end of file diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..bc41aa9 --- /dev/null +++ b/build.gradle @@ -0,0 +1,4 @@ +plugins { + id 'com.android.application' version '7.4.1' apply false + id 'com.android.library' version '7.4.1' apply false +} \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index e545368..4e00663 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1 +1,23 @@ version=SNAPSHOT + +# Project-wide Gradle settings. +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true +# AndroidX package structure to make it clearer which packages are bundled with the +# Android operating system, and which are packaged with your app's APK +# https://developer.android.com/topic/libraries/support-library/androidx-rn +android.useAndroidX=true +# Enables namespacing of each library's R class so that its R class includes only the +# resources declared in the library itself and none from the library's dependencies, +# thereby reducing the size of the R class for that library +android.nonTransitiveRClass=true diff --git a/local.properties b/local.properties new file mode 100644 index 0000000..a9061f3 --- /dev/null +++ b/local.properties @@ -0,0 +1,8 @@ +## This file must *NOT* be checked into Version Control Systems, +# as it contains information specific to your local configuration. +# +# Location of the SDK. This is only used by Gradle. +# For customization when using a Version Control System, please read the +# header note. +#Mon Mar 10 05:50:11 CDT 2025 +sdk.dir=/media/pavl/pavl-g/Android/Sdk diff --git a/settings.gradle b/settings.gradle index b9c05fa..cfebde5 100644 --- a/settings.gradle +++ b/settings.gradle @@ -16,3 +16,4 @@ dependencyResolutionManagement { rootProject.name = 'jSnapLoader' include('snaploader') include('snaploader-examples') +include(':android-application') diff --git a/snaploader-examples/build.gradle b/snaploader-examples/build.gradle index 989f057..57044df 100644 --- a/snaploader-examples/build.gradle +++ b/snaploader-examples/build.gradle @@ -103,4 +103,5 @@ dependencies { runtimeOnly 'com.github.stephengold:jolt-jni-Linux64_fma:0.9.7:DebugSp' runtimeOnly 'com.github.stephengold:jolt-jni-Windows64:0.9.7:DebugSp' runtimeOnly 'com.github.stephengold:jolt-jni-Windows64_avx2:0.9.7:DebugSp' + runtimeOnly 'io.github.software-hardware-codesign:jme3-alloc-desktop:1.0.0-pre-gamma-2' } \ No newline at end of file diff --git a/snaploader-examples/src/main/java/electrostatic4j/snaploader/examples/TestCrossPlatformClassPathLoading.java b/snaploader-examples/src/main/java/electrostatic4j/snaploader/examples/TestCrossPlatformClassPathLoading.java new file mode 100644 index 0000000..5ad38dd --- /dev/null +++ b/snaploader-examples/src/main/java/electrostatic4j/snaploader/examples/TestCrossPlatformClassPathLoading.java @@ -0,0 +1,87 @@ +/* + * 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.examples; + +import electrostatic4j.snaploader.LibraryInfo; +import electrostatic4j.snaploader.LoadingCriterion; +import electrostatic4j.snaploader.NativeBinaryLoader; +import electrostatic4j.snaploader.filesystem.DirectoryPath; +import electrostatic4j.snaploader.platform.NativeDynamicLibrary; +import electrostatic4j.snaploader.platform.util.DefaultDynamicLibraries; +import electrostatic4j.snaploader.platform.util.NativeVariant; +import electrostatic4j.snaploader.platform.util.PlatformPredicate; + +/** + * Examines the cross-platform capabilities of jSnapLoader. + * + * @author pavl_g. + */ +public class TestCrossPlatformClassPathLoading { + public static void main(String[] args) throws Exception { + + final LibraryInfo libraryInfo = new LibraryInfo(new DirectoryPath("lib/independent"), "jmealloc", DirectoryPath.USER_DIR); + + final NativeDynamicLibrary[] libraries = new NativeDynamicLibrary[] { + DefaultDynamicLibraries.ANDROID_ALL, + DefaultDynamicLibraries.LINUX_X86, + DefaultDynamicLibraries.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, + }; + + final NativeBinaryLoader loader = new NativeBinaryLoader(libraryInfo); + + loader.registerNativeLibraries(libraries).initPlatformLibrary(); + loader.setLoggingEnabled(true); + loader.setRetryWithCleanExtraction(true); + /* Native dynamic library properties */ + printDetails(loader); + loader.loadLibrary(LoadingCriterion.INCREMENTAL_LOADING); + } + + public 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("--------------------------------------------------------------"); + } +} diff --git a/snaploader/src/main/java/electrostatic4j/snaploader/ConcurrentNativeBinaryLoader.java b/snaploader/src/main/java/electrostatic4j/snaploader/ConcurrentNativeBinaryLoader.java index b4db18a..1226689 100644 --- a/snaploader/src/main/java/electrostatic4j/snaploader/ConcurrentNativeBinaryLoader.java +++ b/snaploader/src/main/java/electrostatic4j/snaploader/ConcurrentNativeBinaryLoader.java @@ -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 @@ -35,6 +35,7 @@ import java.util.List; import java.util.concurrent.locks.ReentrantLock; import electrostatic4j.snaploader.platform.NativeDynamicLibrary; +import electrostatic4j.snaploader.throwable.UnSupportedSystemError; /** * A thread-safe implementation for the NativeBinaryLoader. @@ -56,7 +57,31 @@ public class ConcurrentNativeBinaryLoader extends NativeBinaryLoader { public ConcurrentNativeBinaryLoader(final List registeredLibraries, final LibraryInfo libraryInfo) { super(registeredLibraries, libraryInfo); } - + + @Override + public NativeBinaryLoader initPlatformLibrary() throws UnSupportedSystemError { + try { + /* CRITICAL SECTION STARTS */ + lock.lock(); + return super.initPlatformLibrary(); + } finally { + lock.unlock(); + /* CRITICAL SECTION ENDS */ + } + } + + @Override + public NativeBinaryLoader loadLibrary(LoadingCriterion criterion) throws Exception { + try { + /* CRITICAL SECTION STARTS */ + lock.lock(); + return super.loadLibrary(criterion); + } finally { + lock.unlock(); + /* CRITICAL SECTION ENDS */ + } + } + @Override protected void cleanExtractBinary(NativeDynamicLibrary library) throws Exception { try { diff --git a/snaploader/src/main/java/electrostatic4j/snaploader/LoadingCriterion.java b/snaploader/src/main/java/electrostatic4j/snaploader/LoadingCriterion.java index 715f80b..e7fdf31 100644 --- a/snaploader/src/main/java/electrostatic4j/snaploader/LoadingCriterion.java +++ b/snaploader/src/main/java/electrostatic4j/snaploader/LoadingCriterion.java @@ -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 @@ -47,5 +47,88 @@ public enum LoadingCriterion { /** * Extracts the native binary only if the current binary isn't present on the extraction directory. */ - INCREMENTAL_LOADING; + INCREMENTAL_LOADING, + + /** + * Commands to load a native dynamic library from the system directories. + *

+ * This criterion instructs the loader to search for the native library in predefined + * system locations, relying on the operating system's dynamic linker to resolve the library. + * The library must be pre-installed and accessible through standard system paths. + *

+ * + * System Library Search Paths + *

The specific directories searched depend on the operating system:

+ * + * + * Usage Considerations + *

+ * 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)}. + *

+ */ + SYSTEM_LOAD } diff --git a/snaploader/src/main/java/electrostatic4j/snaploader/NativeBinaryLoader.java b/snaploader/src/main/java/electrostatic4j/snaploader/NativeBinaryLoader.java index d6e2894..4db2668 100644 --- a/snaploader/src/main/java/electrostatic4j/snaploader/NativeBinaryLoader.java +++ b/snaploader/src/main/java/electrostatic4j/snaploader/NativeBinaryLoader.java @@ -37,7 +37,6 @@ import java.util.List; import java.util.jar.JarFile; import java.util.logging.Level; -import java.lang.UnsatisfiedLinkError; import electrostatic4j.snaploader.filesystem.FileExtractionListener; import electrostatic4j.snaploader.filesystem.FileExtractor; import electrostatic4j.snaploader.filesystem.FileLocalizingListener; @@ -51,9 +50,9 @@ import electrostatic4j.snaploader.util.SnapLoaderLogger; /** - * A cross-platform utility for extracting and loading native binaries based on + * A cross-platform utility for extracting and loading native binaries based on * the variant properties (OS + ARCH + VM). - * + * * @author pavl_g. */ public class NativeBinaryLoader { @@ -111,12 +110,12 @@ public NativeBinaryLoader registerNativeLibraries(NativeDynamicLibrary[] nativeD /** * Initializes the platform-dependent native dynamic library. - * + * * @return this instance for chained invocations * @throws UnSupportedSystemError if the OS is not supported by jSnapLoader */ public NativeBinaryLoader initPlatformLibrary() throws UnSupportedSystemError { - final boolean[] isSystemFound = new boolean[] {false}; + final boolean[] isSystemFound = new boolean[]{false}; // search for the compatible library using the predefined predicate // a predicate is a conditional statement composed of multiple propositions // representing the complete system variant (OS + ARCH + VM). @@ -151,14 +150,34 @@ public NativeBinaryLoader initPlatformLibrary() throws UnSupportedSystemError { } /** - * Extracts and load the system and the architecture-specific library from the output jar to the [user.dir] - * according to a loading criterion (incremental-load or clean-extract). - * - * @param criterion the initial loading criterion, either {@link LoadingCriterion#INCREMENTAL_LOADING} or {@link LoadingCriterion#CLEAN_EXTRACTION} + * Extracts and loads the system and the architecture-specific library from the output jar to a specified directory + * according to a loading criterion (incremental-load or clean-extract). The directory is determined by the selected + * {@link NativeDynamicLibrary} from the registered platform libraries. + * + *

+ * Note: The default loading action for Android Systems is loading the libraries from the System directories (i.e., /lib/ABI inside the APK + * packages), while the default loading action for other desktop systems is determined by the user through this parameterized function. + *

+ * + *

+ * Fallback loading routines can be implemented as needed via {@link NativeBinaryLoadingListener#onLoadingFailure(NativeBinaryLoader)} + * and are left for the user applications. + *

+ * + * @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 { + 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) { + loadSystemBinary(); + return this; + } if (criterion == LoadingCriterion.INCREMENTAL_LOADING && nativeDynamicLibrary.isExtracted()) { loadBinary(nativeDynamicLibrary); return this; @@ -166,10 +185,10 @@ public NativeBinaryLoader loadLibrary(LoadingCriterion criterion) throws Excepti cleanExtractBinary(nativeDynamicLibrary); return this; } - + /** * Retrieves the native dynamic library object representing the library to extract and load. - * + * * @return an object representing the platform-dependent native dynamic library */ public NativeDynamicLibrary getNativeDynamicLibrary() { @@ -185,84 +204,99 @@ public void setLoggingEnabled(boolean loggingEnabled) { SnapLoaderLogger.setLoggingEnabled(loggingEnabled); } - /** - * Enables the retry with clean extraction after a load failure, default value is false. - * - * @param retryWithCleanExtraction true to enable the flag, false otherwise - */ - public void setRetryWithCleanExtraction(boolean retryWithCleanExtraction) { - this.retryWithCleanExtraction = retryWithCleanExtraction; - } - /** * Tests the retry with clean extraction flag, default value is false. - * + * * @return true if enabled, false otherwise */ public boolean isRetryWithCleanExtraction() { return retryWithCleanExtraction; } - public List getRegisteredLibraries() { - return registeredLibraries; + /** + * Enables the retry with clean extraction after a load failure, default value is false. + * + * @param retryWithCleanExtraction true to enable the flag, false otherwise + */ + public void setRetryWithCleanExtraction(boolean retryWithCleanExtraction) { + this.retryWithCleanExtraction = retryWithCleanExtraction; } - public void setNativeBinaryLoadingListener(NativeBinaryLoadingListener nativeBinaryLoadingListener) { - this.nativeBinaryLoadingListener = nativeBinaryLoadingListener; + public List getRegisteredLibraries() { + return registeredLibraries; } public NativeBinaryLoadingListener getNativeBinaryLoadingListener() { return nativeBinaryLoadingListener; } - public void setSystemDetectionListener(SystemDetectionListener systemDetectionListener) { - this.systemDetectionListener = systemDetectionListener; + public void setNativeBinaryLoadingListener(NativeBinaryLoadingListener nativeBinaryLoadingListener) { + this.nativeBinaryLoadingListener = nativeBinaryLoadingListener; } public SystemDetectionListener getSystemDetectionListener() { return systemDetectionListener; } - public void setLibraryExtractionListener(FileExtractionListener libraryExtractionListener) { - this.libraryExtractionListener = libraryExtractionListener; + public void setSystemDetectionListener(SystemDetectionListener systemDetectionListener) { + this.systemDetectionListener = systemDetectionListener; } public FileExtractionListener getLibraryExtractionListener() { return libraryExtractionListener; } - public void setLibraryLocalizingListener(FileLocalizingListener libraryLocalizingListener) { - this.libraryLocalizingListener = libraryLocalizingListener; + public void setLibraryExtractionListener(FileExtractionListener libraryExtractionListener) { + this.libraryExtractionListener = libraryExtractionListener; } public FileLocalizingListener getLibraryLocalizingListener() { return libraryLocalizingListener; } + public void setLibraryLocalizingListener(FileLocalizingListener libraryLocalizingListener) { + this.libraryLocalizingListener = libraryLocalizingListener; + } + public void setMaxNumberOfLoadingFailure(int maxNumberOfLoadingFailure) { this.maxNumberOfLoadingFailure = Math.abs(maxNumberOfLoadingFailure); } /** - * Loads a native binary using the platform-dependent object, for Android; - * the library is loaded by its basename (variant is managed internally by the android sdk). - * + * Loads a native binary from the system directories into the process virtual + * address space using the library basename in a platform-dependent way. + */ + protected void loadSystemBinary() { + try { + System.loadLibrary(libraryInfo.getBaseName()); + SnapLoaderLogger.log(Level.INFO, getClass().getName(), "loadSystemBinary", "Successfully loaded library from the system: " + + libraryInfo.getBaseName()); + if (nativeBinaryLoadingListener != null) { + nativeBinaryLoadingListener.onLoadingSuccess(this); + } + } 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); + } + } + } + + /** + * Loads a native binary into the virtual process address space from a specified + * native library data structure defining the directory path. + * * @param library the platform-specific library to load - * @throws IOException in case the binary to be extracted is not found on the specified jar + * @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 { try { - /* sanity-check for android java vm (the dalvik) */ - if (NativeVariant.Os.isAndroid()) { - System.loadLibrary(libraryInfo.getBaseName()); - SnapLoaderLogger.log(Level.INFO, getClass().getName(),"loadBinary", "Successfully loaded library for Android: " - + library.getExtractedLibrary()); - return; - } System.load(library.getExtractedLibrary()); - SnapLoaderLogger.log(Level.INFO, getClass().getName(),"loadBinary", "Successfully loaded library: " + SnapLoaderLogger.log(Level.INFO, getClass().getName(), "loadBinary", "Successfully loaded library: " + library.getExtractedLibrary()); if (nativeBinaryLoadingListener != null) { nativeBinaryLoadingListener.onLoadingSuccess(this); @@ -292,7 +326,7 @@ protected void loadBinary(NativeDynamicLibrary library) throws Exception { /** * 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 * interrupted I/O operation has occurred @@ -300,7 +334,7 @@ protected void loadBinary(NativeDynamicLibrary library) throws Exception { protected void cleanExtractBinary(NativeDynamicLibrary library) throws Exception { libraryExtractor = initializeLibraryExtractor(library); SnapLoaderLogger.log(Level.INFO, getClass().getName(), "cleanExtractBinary", - "File extractor handler initialized!"); + "File extractor handler initialized!"); /* CLEAR RESOURCES AND RESET OBJECTS ON-EXTRACTION */ libraryExtractor.setExtractionListener(new FileExtractionListener() { @Override @@ -360,8 +394,8 @@ public void onExtractionFinalization(FileExtractor fileExtractor, FileLocator fi /** * Initializes a filesystem extractor object * if the filesystem extractor object associated with this loader isn't defined. - * - * @param library the native dynamic library to load + * + * @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 */ diff --git a/snaploader/src/main/java/electrostatic4j/snaploader/platform/util/DefaultDynamicLibraries.java b/snaploader/src/main/java/electrostatic4j/snaploader/platform/util/DefaultDynamicLibraries.java index 8cb23e2..4ff7660 100644 --- a/snaploader/src/main/java/electrostatic4j/snaploader/platform/util/DefaultDynamicLibraries.java +++ b/snaploader/src/main/java/electrostatic4j/snaploader/platform/util/DefaultDynamicLibraries.java @@ -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 @@ -44,6 +44,12 @@ */ public class DefaultDynamicLibraries { + /** + * Represents a System Directory Android Library. + */ + public static NativeDynamicLibrary ANDROID_ALL = + new NativeDynamicLibrary("", PlatformPredicate.ANDROID); + /** * Represents a linux x86 binary with 64-bit instruction set. */ diff --git a/snaploader/src/main/java/electrostatic4j/snaploader/platform/util/NativeVariant.java b/snaploader/src/main/java/electrostatic4j/snaploader/platform/util/NativeVariant.java index 07a10b8..ea09558 100644 --- a/snaploader/src/main/java/electrostatic4j/snaploader/platform/util/NativeVariant.java +++ b/snaploader/src/main/java/electrostatic4j/snaploader/platform/util/NativeVariant.java @@ -87,6 +87,7 @@ public enum NativeVariant { private static final String Linux = "Linux"; private static final String Windows = "Windows"; private static final String Mac = "Mac"; + private static final String WebOs = "WebOs"; private static final String Dalvik = "Dalvik"; private final String property; @@ -136,6 +137,37 @@ public static boolean isMac() { public static boolean isAndroid() { return JVM.getProperty().contains(NativeVariant.Dalvik); } + + /** + * Tests whether the current system runtime is Desktop. A + * system runtime is said to be desktop if it's not an Android, + * and not Web-OS, and not iOS. + * + * @return true if the current runtime is a desktop binary, false otherwise. + */ + public static boolean isDesktop() { + return !isAndroid() && !isWebOs() && !isIos(); + } + + /** + * Contemplated to test whether the current system runtime is + * a web os. + * + * @return true if web-os; false otherwise. Current value is constantly false. + */ + public static boolean isWebOs() { + return false; + } + + /** + * Contemplated to test whether the current system runtime is + * an iOS os. + * + * @return true if iOS; false otherwise. Current value is constantly false. + */ + public static boolean isIos() { + return false; + } } /** diff --git a/snaploader/src/main/java/electrostatic4j/snaploader/platform/util/PackageVariant.java b/snaploader/src/main/java/electrostatic4j/snaploader/platform/util/PackageVariant.java new file mode 100644 index 0000000..bdfa918 --- /dev/null +++ b/snaploader/src/main/java/electrostatic4j/snaploader/platform/util/PackageVariant.java @@ -0,0 +1,114 @@ +/* + * 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.platform.util; + +/** + * A namespace class exposing checks for the native window + * currently in runtime; this could give some more insights + * about the type of the framework the application is running + * on. + * + * @author pavl_g. + */ +public final class PackageVariant { + + private static final String FLUTTER_ACTIVITY_PATH = "io.flutter.embedding.android.FlutterActivity"; + private static final String JME_CONTEXT_PATH = "com.jme3.system.JmeContext"; + private static final String ANDROID_CONTEXT_PATH = "android.content.Context"; + private static final String JAVA_FX_CONTEXT_PATH = "javafx.application.Application"; + private static final String JAVA_AWT_CONTEXT_PATH = "java.awt.Frame"; + private static final String GWT_CONTEXT_PATH = "com.google.gwt.user.client.Window"; + + private PackageVariant() { + } + + public static boolean hasJavaFxWindow() { + try { + return ClassLoader.getSystemClassLoader().loadClass(PackageVariant.JAVA_FX_CONTEXT_PATH) != null; + } catch (ClassNotFoundException e) { + return false; + } + } + + public static boolean hasJavaAWTWindow() { + try { + return ClassLoader.getSystemClassLoader().loadClass(PackageVariant.JAVA_AWT_CONTEXT_PATH) != null; + } catch (ClassNotFoundException e) { + return false; + } + } + + public static boolean hasAndroidActivity() { + try { + return ClassLoader.getSystemClassLoader().loadClass(PackageVariant.ANDROID_CONTEXT_PATH) != null; + } catch (ClassNotFoundException e) { + return false; + } + } + + public static boolean hasGWTContext() { + try { + return ClassLoader.getSystemClassLoader().loadClass(PackageVariant.GWT_CONTEXT_PATH) != null; + } catch (ClassNotFoundException e) { + return false; + } + } + + public static boolean hasJmeContext() { + try { + return ClassLoader.getSystemClassLoader().loadClass(PackageVariant.JME_CONTEXT_PATH) != null; + } catch (ClassNotFoundException e) { + return false; + } + } + + public static boolean hasFlutterActivity() { + try { + return ClassLoader.getSystemClassLoader().loadClass(PackageVariant.FLUTTER_ACTIVITY_PATH) != null; + } catch (ClassNotFoundException e) { + return false; + } + } + + public static boolean hasAndroidActivityOnly() { + return hasAndroidActivity() && !hasFlutterActivity(); + } + + public static boolean hasJmeAndroidContext() { + return hasAndroidActivity() && hasJmeContext(); + } + + public static boolean hasJmeFlutterContext() { + return hasFlutterActivity() && hasJmeContext(); + } +} \ No newline at end of file diff --git a/snaploader/src/main/java/electrostatic4j/snaploader/platform/util/PlatformPredicate.java b/snaploader/src/main/java/electrostatic4j/snaploader/platform/util/PlatformPredicate.java index 25de2c2..acf6aeb 100644 --- a/snaploader/src/main/java/electrostatic4j/snaploader/platform/util/PlatformPredicate.java +++ b/snaploader/src/main/java/electrostatic4j/snaploader/platform/util/PlatformPredicate.java @@ -42,24 +42,34 @@ public final class PlatformPredicate { /** - * Alias object for Linux on X86 Chipset. + * Alias object for Linux on X86 Desktop Chipset. */ - public static final PlatformPredicate LINUX_X86 = new PlatformPredicate(NativeVariant.Os.isLinux() && NativeVariant.Cpu.isX86()); + public static final PlatformPredicate LINUX_X86 = new PlatformPredicate(NativeVariant.Os.isDesktop() && + NativeVariant.Os.isLinux() && NativeVariant.Cpu.isX86()); /** - * Alias object for Linux on X86-64 Chipset. + * Alias object for Android all variants (i.e., x86, AARCH64, ARM32) + * when using {@link Runtime#loadLibrary(String)}. */ - public static final PlatformPredicate LINUX_X86_64 = new PlatformPredicate(NativeVariant.Os.isLinux() && NativeVariant.Cpu.isAMD() && NativeVariant.Cpu.is64()); + public static final PlatformPredicate ANDROID = new PlatformPredicate(NativeVariant.Os.isAndroid()); /** - * Alias object for Linux on arm-32 Chipset. + * Alias object for Linux on X86-64 Desktop Chipset. */ - public static final PlatformPredicate LINUX_ARM_32 = new PlatformPredicate(NativeVariant.Os.isLinux() && NativeVariant.Cpu.isARM()); + public static final PlatformPredicate LINUX_X86_64 = new PlatformPredicate(NativeVariant.Os.isDesktop() && + NativeVariant.Os.isLinux() && NativeVariant.Cpu.isAMD() && NativeVariant.Cpu.is64()); /** - * Alias object for Linux on arm-64 Chipset. + * Alias object for Linux on arm-32 Desktop Chipset. */ - public static final PlatformPredicate LINUX_ARM_64 = new PlatformPredicate(NativeVariant.Os.isLinux() && NativeVariant.Cpu.isARM() && NativeVariant.Cpu.is64()); + public static final PlatformPredicate LINUX_ARM_32 = new PlatformPredicate(NativeVariant.Os.isDesktop() && + NativeVariant.Os.isLinux() && NativeVariant.Cpu.isARM()); + + /** + * Alias object for Linux on arm-64 Desktop Chipset. + */ + public static final PlatformPredicate LINUX_ARM_64 = new PlatformPredicate(NativeVariant.Os.isDesktop() && + NativeVariant.Os.isLinux() && NativeVariant.Cpu.isARM() && NativeVariant.Cpu.is64()); /** * Alias object for Linux on RiscV-32 Chipset. @@ -139,7 +149,7 @@ public PlatformPredicate(boolean predicate) { * with one or more instruction-set extensions. The result is true if and * only if the base predicate is true and all named extensions are present. * - * @param base a pre-existing predicate (not null) + * @param base a pre-existing predicate (not null) * @param isaExtensions names of required ISA extensions */ public PlatformPredicate(PlatformPredicate base, String... isaExtensions) {