Class BotanInstance
This class is responsible for loading the Botan native library through JNR-FFI and providing centralized access to the native function bindings. It implements thread-safe singleton pattern with lazy initialization and comprehensive error handling for library loading failures.
Purpose and Responsibilities
- Library Loading: Uses JNR-FFI LibraryLoader to load the native Botan library ("botan-3")
- Singleton Management: Ensures only one instance of BotanLibrary exists per JVM
- Lazy Initialization: Library is loaded on first access, not at class loading time
- Error Tracking: Captures and preserves library loading errors for later diagnosis
- Centralized Error Checking: Provides utility methods for validating native call results
Thread Safety
The singleton initialization uses double-checked locking with volatile field for thread-safe lazy initialization:
- First check without synchronization (fast path for common case)
- Synchronized block for initialization (ensures only one thread initializes)
- Second check inside synchronized block (prevents race conditions)
- Volatile NATIVE field ensures visibility across threads
Usage Patterns
Getting the Native Library Instance
// Get singleton instance
BotanLibrary lib = BotanInstance.singleton();
// Call native functions
String version = lib.botan_version_string();
int err = lib.botan_hash_init(hashRef, "SHA-256", 0);
Checking Library Availability
try {
BotanInstance.checkAvailability();
System.out.println("Botan library loaded successfully");
} catch (UnsatisfiedLinkError e) {
System.err.println("Failed to load Botan: " + e.getMessage());
// Handle missing library (e.g., fall back to another provider)
}
Error Checking for Native Calls
PointerByReference hashRef = new PointerByReference();
int err = lib.botan_hash_init(hashRef, "SHA-256", 0);
// Check for errors - throws NativeMethodException if err != 0
BotanInstance.checkNativeCall(err, "botan_hash_init");
// Or get error description manually
if (err != 0) {
String description = lib.botan_error_description(err);
throw new RuntimeException("Hash init failed: " + description);
}
Library Loading Process
The native library loading follows this sequence:
- First call to
singleton()triggers initialization - JNR-FFI LibraryLoader searches for "botan-3" library:
- System library paths (e.g., /usr/lib, /usr/local/lib)
- Paths specified in java.library.path system property
- Platform-specific library names (libbotan-3.so, libbotan-3.dylib, botan-3.dll)
- If found, library is loaded and BotanLibrary proxy is created
- If not found, UnsatisfiedLinkError is caught and stored in
loadError - Subsequent calls to
singleton()return the cached instance (or null if loading failed)
Error Handling Strategy
This class implements a deferred error handling approach:
- Loading Errors: Library loading failures are captured but not thrown immediately
- Silent Failure:
singleton()returns null if loading failed - Explicit Check:
checkAvailability()throws the original error when called - Rationale: Allows code to check availability without forcing exceptions at static init time
Native Call Error Codes
Botan native functions return integer error codes. Common codes include:
- 0: Success
- -1: Invalid argument
- -2: Bad flag
- -10: Not implemented
- -20: Bad MAC (authentication failure)
- -30: Insufficient buffer space
- -100: Unknown error
Use BotanLibrary.botan_error_description(int) or checkNativeCall(int, String)
to convert error codes to human-readable messages.
Integration with Provider
The BotanProvider calls checkAvailability() during construction to ensure the native
library is available before registering algorithms:
public BotanProvider() {
super(NAME, "", INFO);
// Will throw UnsatisfiedLinkError if library not available
BotanInstance.checkAvailability();
// Register algorithms...
addMdAlgorithm();
addMacAlgorithm();
// ...
}
Troubleshooting Library Loading
If the library fails to load:
- Check Installation: Ensure Botan 3.x is installed (e.g.,
brew install botanon macOS) - Check Version: Verify Botan version is 3.0.0 or higher
- Check Library Path: Set java.library.path to include Botan library directory:
java -Djava.library.path=/opt/homebrew/lib ...
- Check Platform: Ensure platform-specific library exists (libbotan-3.so, libbotan-3.dylib, etc.)
- Check Dependencies: Verify all native dependencies of Botan are available
Implementation Notes
- Final Class: Cannot be subclassed (singleton pattern enforcement)
- Private Constructor: Cannot be instantiated (utility class)
- Static Methods Only: All functionality provided through static methods
- Volatile Field: NATIVE field is volatile for safe publication across threads
- Error Preservation: Original UnsatisfiedLinkError is preserved for accurate diagnosis
- Since:
- 0.1.0
- Author:
- Yasser Aziza
- See Also:
-
BotanLibraryLibraryLoader
-
Method Summary
Modifier and TypeMethodDescriptionstatic voidChecks whether or not the native library was successfully loaded.static voidcheckNativeCall(int result, String method) Checks whether a native lib call was successful.static BotanLibraryReturns a singleton instance of theBotanLibrarylibrary.
-
Method Details
-
singleton
Returns a singleton instance of theBotanLibrarylibrary.- Returns:
BotanLibrarysingleton instance
-
checkAvailability
public static void checkAvailability()Checks whether or not the native library was successfully loaded.- Throws:
UnsatisfiedLinkError- if the library failed to load
-
checkNativeCall
Checks whether a native lib call was successful.- Parameters:
result- int result from calling botan nativemethod- the native method name for error reporting- Throws:
NativeMethodException- in case of error
-