Class BotanInstance

java.lang.Object
net.randombit.botan.jnr.BotanInstance

public final class BotanInstance extends Object
Singleton manager for the Botan native library instance with lazy initialization and error handling.

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:

  1. First call to singleton() triggers initialization
  2. 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)
  3. If found, library is loaded and BotanLibrary proxy is created
  4. If not found, UnsatisfiedLinkError is caught and stored in loadError
  5. 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 botan on 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:
  • Method Details

    • singleton

      public static BotanLibrary singleton()
      Returns a singleton instance of the BotanLibrary library.
      Returns:
      BotanLibrary singleton 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

      public static void checkNativeCall(int result, String method) throws NativeMethodException
      Checks whether a native lib call was successful.
      Parameters:
      result - int result from calling botan native
      method - the native method name for error reporting
      Throws:
      NativeMethodException - in case of error