diff --git a/src/main/java/com/bonus/boot/manager/manager/utils/SigarLibraryLoader.java b/src/main/java/com/bonus/boot/manager/manager/utils/SigarLibraryLoader.java new file mode 100644 index 0000000..b8f5b83 --- /dev/null +++ b/src/main/java/com/bonus/boot/manager/manager/utils/SigarLibraryLoader.java @@ -0,0 +1,258 @@ +package com.bonus.boot.manager.manager.utils; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.annotation.Order; +import org.springframework.core.env.Environment; +import org.springframework.stereotype.Component; + +import javax.annotation.PostConstruct; +import java.io.File; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.StandardCopyOption; + +/** + * Sigar 库加载器 + * 优先使用 yml 配置的路径,如果配置路径不存在,则从 classpath 加载 + */ +@Component +@Order(Integer.MIN_VALUE) +public class SigarLibraryLoader { + + private static volatile boolean libraryLoaded = false; + private static final Object loadLock = new Object(); + + @Autowired(required = false) + private Environment environment; + + /** + * 静态初始化块:在类加载时就尝试加载库 + * 这确保在 Sigar 类被加载之前库已经加载 + * 如果失败,静默处理(因为配置路径在静态初始化时不可用),后续的 @PostConstruct 会重试 + */ + static { + try { + loadSigarNativeLibraryStatic(); + } catch (Exception e) { + // 静默处理,因为配置路径在静态初始化时不可用 + // 后续的 @PostConstruct 方法会使用配置路径重试 + // 不抛出异常,允许类加载继续 + } + } + + /** + * 在 Bean 初始化时加载库 + * 此时 Spring 环境已初始化,可以使用配置的路径 + * 这是主要的加载方式,因为配置路径在静态初始化时不可用 + */ + @PostConstruct + public void loadLibrary() { + if (!libraryLoaded) { + synchronized (loadLock) { + if (!libraryLoaded) { + try { + doLoadLibrary(); + libraryLoaded = true; + System.out.println("Sigar library loaded successfully"); + } catch (Exception e) { + System.err.println("ERROR: Failed to load Sigar library: " + e.getMessage()); + e.printStackTrace(); + // 不设置 libraryLoaded = true,允许后续重试 + } + } + } + } + } + + /** + * 静态方法:在类加载时加载库 + */ + private static void loadSigarNativeLibraryStatic() { + if (!libraryLoaded) { + synchronized (loadLock) { + if (!libraryLoaded) { + doLoadLibraryStatic(); + libraryLoaded = true; + } + } + } + } + + /** + * 静态方法:实际加载库的逻辑(仅从 classpath,因为配置不可用) + */ + private static void doLoadLibraryStatic() { + try { + String osName = System.getProperty("os.name").toLowerCase(); + String arch = System.getProperty("os.arch").toLowerCase(); + String libraryName = getLibraryName(osName, arch); + loadFromClasspathStatic(libraryName); + } catch (Exception e) { + throw new RuntimeException("Failed to load Sigar native library in static initializer", e); + } + } + + /** + * 获取库文件名(根据操作系统和架构) + */ + private static String getLibraryName(String osName, String arch) { + if (osName.contains("win")) { + return arch.contains("64") ? "sigar-amd64-winnt.dll" : "sigar-x86-winnt.dll"; + } else if (osName.contains("linux")) { + return arch.contains("64") ? "libsigar-amd64-linux.so" : "libsigar-x86-linux.so"; + } else if (osName.contains("mac")) { + return "libsigar-universal64-macosx.dylib"; + } else { + throw new UnsupportedOperationException("Unsupported operating system: " + osName); + } + } + + /** + * 静态方法:从 classpath 加载库 + * 尝试多个类加载器以确保能找到资源 + */ + private static void loadFromClasspathStatic(String libraryName) { + InputStream inputStream = null; + + // 尝试多个类加载器 + ClassLoader[] classLoaders = new ClassLoader[] { + SigarLibraryLoader.class.getClassLoader(), + Thread.currentThread().getContextClassLoader(), + ClassLoader.getSystemClassLoader() + }; + + for (ClassLoader classLoader : classLoaders) { + if (classLoader != null) { + try { + inputStream = classLoader.getResourceAsStream(libraryName); + if (inputStream != null) { + System.out.println("Found library using classLoader: " + classLoader.getClass().getName()); + break; + } + } catch (Exception e) { + // 继续尝试下一个类加载器 + } + } + } + + if (inputStream == null) { + // 尝试使用绝对路径(从 resources 根目录) + for (ClassLoader classLoader : classLoaders) { + if (classLoader != null) { + try { + inputStream = classLoader.getResourceAsStream("/" + libraryName); + if (inputStream != null) { + System.out.println("Found library using absolute path with classLoader: " + classLoader.getClass().getName()); + break; + } + } catch (Exception e) { + // 继续尝试下一个类加载器 + } + } + } + } + + if (inputStream == null) { + throw new RuntimeException("Sigar native library not found in classpath: " + libraryName + + ". Please ensure the DLL file is in src/main/resources/ directory."); + } + + try { + File tempFile = File.createTempFile("sigar", getFileExtension(libraryName)); + tempFile.deleteOnExit(); + + Files.copy(inputStream, tempFile.toPath(), StandardCopyOption.REPLACE_EXISTING); + inputStream.close(); + + System.load(tempFile.getAbsolutePath()); + System.out.println("Successfully loaded Sigar library from classpath (static): " + libraryName + " -> " + tempFile.getAbsolutePath()); + + } catch (Exception e) { + if (inputStream != null) { + try { + inputStream.close(); + } catch (Exception ignored) { + } + } + throw new RuntimeException("Failed to load Sigar library from classpath: " + libraryName, e); + } + } + + /** + * 获取文件扩展名 + */ + private static String getFileExtension(String filename) { + int lastDot = filename.lastIndexOf('.'); + return lastDot == -1 ? "" : filename.substring(lastDot); + } + + /** + * 实际加载库的逻辑(实例方法,此时 Spring 环境已初始化) + * 优先使用配置的路径,如果配置路径不存在,则从 classpath 加载 + */ + private void doLoadLibrary() { + try { + String osName = System.getProperty("os.name").toLowerCase(); + String arch = System.getProperty("os.arch").toLowerCase(); + + String libraryName = getLibraryName(osName, arch); + String configuredPath = getConfiguredLibraryPath(osName, arch); + + // 优先使用配置的路径 + if (configuredPath != null && !configuredPath.trim().isEmpty()) { + File libraryFile = new File(configuredPath); + if (libraryFile.exists()) { + System.load(configuredPath); + System.out.println("Successfully loaded Sigar library from configured path: " + configuredPath); + return; + } else { + System.err.println("Configured Sigar library path not found: " + configuredPath + ", falling back to classpath"); + } + } + + // 备用方案:从 classpath 加载 + loadFromClasspath(libraryName); + + } catch (Exception e) { + throw new RuntimeException("Failed to load Sigar native library", e); + } + } + + /** + * 从配置中获取库文件路径 + */ + private String getConfiguredLibraryPath(String osName, String arch) { + if (environment == null) { + return null; + } + + String pathKey; + if (osName.contains("win")) { + pathKey = arch.contains("64") ? "sigar.library-paths.windows64" : "sigar.library-paths.windows32"; + } else if (osName.contains("linux")) { + pathKey = arch.contains("64") ? "sigar.library-paths.linux64" : "sigar.library-paths.linux32"; + } else if (osName.contains("mac")) { + pathKey = "sigar.library-paths.macos"; + } else { + return null; + } + + return environment.getProperty(pathKey); + } + + + /** + * 从 classpath 加载库(实例方法,复用静态方法的逻辑) + */ + private void loadFromClasspath(String libraryName) { + loadFromClasspathStatic(libraryName); + } + + /** + * 检查库是否已加载 + */ + public static boolean isLibraryLoaded() { + return libraryLoaded; + } +} +