|
| 1 | +package com.etebase.client.internal; |
| 2 | + |
| 3 | +import java.io.IOException; |
| 4 | +import java.io.InputStream; |
| 5 | +import java.nio.file.Files; |
| 6 | +import java.nio.file.Path; |
| 7 | +import java.nio.file.StandardCopyOption; |
| 8 | +import java.util.Locale; |
| 9 | + |
| 10 | +public final class NativeLoader { |
| 11 | + private static final String LIB_NAME = "etebase_jni"; |
| 12 | + private static final String OVERRIDE_PROPERTY = "com.etebase.client.native.path"; |
| 13 | + |
| 14 | + private static final Object LOCK = new Object(); |
| 15 | + private static volatile boolean loaded = false; |
| 16 | + |
| 17 | + private NativeLoader() { |
| 18 | + } |
| 19 | + |
| 20 | + public static void load() { |
| 21 | + if (loaded) return; |
| 22 | + synchronized (LOCK) { |
| 23 | + if (loaded) return; |
| 24 | + String override = System.getProperty(OVERRIDE_PROPERTY); |
| 25 | + if (override != null && !override.isEmpty()) { |
| 26 | + System.load(override); |
| 27 | + } else { |
| 28 | + loadFromClasspath(); |
| 29 | + } |
| 30 | + loaded = true; |
| 31 | + } |
| 32 | + } |
| 33 | + |
| 34 | + private static void loadFromClasspath() { |
| 35 | + String os = detectOs(); |
| 36 | + String arch = detectArch(); |
| 37 | + String libFile = libFileFor(os); |
| 38 | + String resource = "/com/etebase/client/native/" + os + "-" + arch + "/" + libFile; |
| 39 | + |
| 40 | + try (InputStream in = NativeLoader.class.getResourceAsStream(resource)) { |
| 41 | + if (in == null) { |
| 42 | + throw new UnsatisfiedLinkError( |
| 43 | + "Etebase native library not found on classpath: " + resource |
| 44 | + + " (detected os=" + os + " arch=" + arch + "). " |
| 45 | + + "Either the com.etebase:client-jvm artifact is missing a native binary for this platform, " |
| 46 | + + "or set -D" + OVERRIDE_PROPERTY + "=/absolute/path/to/lib to override." |
| 47 | + ); |
| 48 | + } |
| 49 | + Path tmp = Files.createTempFile(LIB_NAME + "-", "-" + libFile); |
| 50 | + tmp.toFile().deleteOnExit(); |
| 51 | + Files.copy(in, tmp, StandardCopyOption.REPLACE_EXISTING); |
| 52 | + System.load(tmp.toAbsolutePath().toString()); |
| 53 | + } catch (IOException e) { |
| 54 | + UnsatisfiedLinkError err = new UnsatisfiedLinkError("Failed to extract Etebase native library from " + resource); |
| 55 | + err.initCause(e); |
| 56 | + throw err; |
| 57 | + } |
| 58 | + } |
| 59 | + |
| 60 | + private static String detectOs() { |
| 61 | + String name = System.getProperty("os.name", "").toLowerCase(Locale.ROOT); |
| 62 | + if (name.contains("mac") || name.contains("darwin")) return "macos"; |
| 63 | + if (name.contains("win")) return "windows"; |
| 64 | + if (name.contains("nux") || name.contains("nix")) return "linux"; |
| 65 | + throw new UnsatisfiedLinkError("Unsupported OS for Etebase: " + name); |
| 66 | + } |
| 67 | + |
| 68 | + private static String detectArch() { |
| 69 | + String arch = System.getProperty("os.arch", "").toLowerCase(Locale.ROOT); |
| 70 | + if (arch.equals("amd64") || arch.equals("x86_64")) return "x86_64"; |
| 71 | + if (arch.equals("aarch64") || arch.equals("arm64")) return "aarch64"; |
| 72 | + throw new UnsatisfiedLinkError("Unsupported CPU architecture for Etebase: " + arch); |
| 73 | + } |
| 74 | + |
| 75 | + private static String libFileFor(String os) { |
| 76 | + switch (os) { |
| 77 | + case "macos": return "lib" + LIB_NAME + ".dylib"; |
| 78 | + case "linux": return "lib" + LIB_NAME + ".so"; |
| 79 | + case "windows": return LIB_NAME + ".dll"; |
| 80 | + default: throw new AssertionError(os); |
| 81 | + } |
| 82 | + } |
| 83 | +} |
0 commit comments