diff --git a/src/main/java/com/axis/innovators/box/AxisInnovatorsBox.java b/src/main/java/com/axis/innovators/box/AxisInnovatorsBox.java index 378e70e..6ff62b7 100644 --- a/src/main/java/com/axis/innovators/box/AxisInnovatorsBox.java +++ b/src/main/java/com/axis/innovators/box/AxisInnovatorsBox.java @@ -378,14 +378,6 @@ public class AxisInnovatorsBox { main.progressBarManager.close(); - new Thread(() -> { - try { - Tray.init(); - } catch (RegisterTray.TrayException e) { - throw new RuntimeException(e); - } - }, "TrayThread").start(); - SwingUtilities.invokeLater(() -> { try { main.ex = new MainWindow(); @@ -408,6 +400,14 @@ public class AxisInnovatorsBox { throw new RuntimeException(e); } }); + + new Thread(main.thread.getThreadGroup() ,() -> { + try { + Tray.init(); + } catch (RegisterTray.TrayException e) { + throw new RuntimeException(e); + } + }, "TrayThread").start(); } catch (Exception e) { logger.error("Failed to load plugins", e); main.ex.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); diff --git a/src/main/java/com/axis/innovators/box/browser/BrowserWindow.java b/src/main/java/com/axis/innovators/box/browser/BrowserWindow.java index 757a2d0..ca978b5 100644 --- a/src/main/java/com/axis/innovators/box/browser/BrowserWindow.java +++ b/src/main/java/com/axis/innovators/box/browser/BrowserWindow.java @@ -154,26 +154,12 @@ public class BrowserWindow extends JFrame { private Component initializeCef(Builder builder) throws MalformedURLException { - if (!isInitialized && CefApp.getState() != CefApp.CefAppState.INITIALIZED) { + if (!isInitialized) { + isInitialized = true; try { - // 1. 初始化CEF设置(禁用多窗口) - CefSettings settings = new CefSettings(); - settings.windowless_rendering_enabled = false; - settings.javascript_flags = "--expose-gc"; - settings.log_severity = CefSettings.LogSeverity.LOGSEVERITY_VERBOSE; - String subprocessPath = FolderCreator.getLibraryFolder() + "/jcef/lib/win64/jcef_helper.exe"; - - // 验证子进程路径 - if (!new File(subprocessPath).exists()) { - throw new IllegalStateException("jcef_helper.exe not found at: " + subprocessPath); - } - settings.browser_subprocess_path = subprocessPath; - System.out.println("Subprocess Path: " + settings.browser_subprocess_path); - - // 2. 创建CefApp和Client(严格单例) - cefApp = CefApp.getInstance(settings); + this.cefApp = CefAppManager.getInstance(); + //CefAppManager.incrementBrowserCount(); client = cefApp.createClient(); - client.addDisplayHandler(new CefDisplayHandler (){ @Override public void onAddressChange(CefBrowser browser, CefFrame frame, String url) { @@ -210,14 +196,14 @@ public class BrowserWindow extends JFrame { ) { // 格式化输出到 Java 控制台 //if (level != CefSettings.LogSeverity.LOGSEVERITY_WARNING) { - String log = String.format( - "[Browser Console] %s %s (Line %d) -> %s", - getLogLevelSymbol(level), - source, - line, - message - ); - System.out.println(log); + String log = String.format( + "[Browser Console] %s %s (Line %d) -> %s", + getLogLevelSymbol(level), + source, + line, + message + ); + System.out.println(log); //} return false; } @@ -420,22 +406,19 @@ public class BrowserWindow extends JFrame { public void windowClosed(WindowEvent e) { browser.close(true); client.dispose(); - cefApp.dispose(); - isInitialized = false; } }); setVisible(true); - isInitialized = true; + }); return browserComponent; } catch (Exception e) { e.printStackTrace(); - isInitialized = false; JOptionPane.showMessageDialog(null, "初始化失败: " + e.getMessage(), "错误", JOptionPane.ERROR_MESSAGE); - System.exit(1); } } else { + isInitialized = false; SwingUtilities.invokeLater(() -> { dispose(); }); diff --git a/src/main/java/com/axis/innovators/box/browser/BrowserWindowJDialog.java b/src/main/java/com/axis/innovators/box/browser/BrowserWindowJDialog.java index 66552aa..f84cc20 100644 --- a/src/main/java/com/axis/innovators/box/browser/BrowserWindowJDialog.java +++ b/src/main/java/com/axis/innovators/box/browser/BrowserWindowJDialog.java @@ -170,9 +170,8 @@ public class BrowserWindowJDialog extends JDialog { isInitialized = true; try { this.cefApp = CefAppManager.getInstance(); - CefAppManager.incrementBrowserCount(); + //CefAppManager.incrementBrowserCount(); client = cefApp.createClient(); - client.addDisplayHandler(new CefDisplayHandler (){ @Override public void onAddressChange(CefBrowser browser, CefFrame frame, String url) { diff --git a/src/main/java/com/axis/innovators/box/browser/CefAppManager.java b/src/main/java/com/axis/innovators/box/browser/CefAppManager.java index 069bb94..5ab3595 100644 --- a/src/main/java/com/axis/innovators/box/browser/CefAppManager.java +++ b/src/main/java/com/axis/innovators/box/browser/CefAppManager.java @@ -3,23 +3,58 @@ package com.axis.innovators.box.browser; import com.axis.innovators.box.tools.FolderCreator; import org.cef.CefApp; import org.cef.CefSettings; +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.LogManager; import java.io.File; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; +/** + * 增强版CEF应用管理器 + * 特性: + * 1. 多级锁并发控制 + * 2. 设置冲突自动恢复 + * 3. 状态跟踪和验证 + * 4. 增强的异常处理 + * + * @author tzdwindows 7 + */ public class CefAppManager { - private static CefApp cefApp; - private static int browserCount = 0; - private static boolean isInitialized = false; + private static final Logger logger = LogManager.getLogger(CefAppManager.class); + private static volatile CefApp cefApp; + private static final CefSettings settings = new CefSettings(); + + // 状态跟踪 + private static final AtomicBoolean isInitialized = new AtomicBoolean(false); + private static final AtomicBoolean settingsApplied = new AtomicBoolean(false); + private static final AtomicBoolean isDisposing = new AtomicBoolean(false); + + // 并发控制 + private static final Lock initLock = new ReentrantLock(); + private static final Lock disposeLock = new ReentrantLock(); + private static final AtomicBoolean shutdownHookRegistered = new AtomicBoolean(false); static { - Runtime.getRuntime().addShutdownHook(new Thread(() -> { - disposeCefApp(); - })); + initializeDefaultSettings(); + registerShutdownHook(); } - public static synchronized CefApp getInstance() throws Exception { - // 关键修改:仅在第一次初始化时设置参数 - if (cefApp == null && !isInitialized) { - CefSettings settings = new CefSettings(); + + private static void registerShutdownHook() { + if (shutdownHookRegistered.compareAndSet(false, true)) { + Runtime.getRuntime().addShutdownHook(new Thread(() -> { + logger.info("JVM shutdown hook triggered"); + dispose(true); + })); + logger.debug("Shutdown hook registered successfully"); + } + } + + private static void initializeDefaultSettings() { + initLock.lock(); + try { settings.windowless_rendering_enabled = false; settings.javascript_flags = "--expose-gc"; settings.log_severity = CefSettings.LogSeverity.LOGSEVERITY_VERBOSE; @@ -28,38 +63,161 @@ public class CefAppManager { validateSubprocessPath(subprocessPath); settings.browser_subprocess_path = subprocessPath; - cefApp = CefApp.getInstance(settings); - isInitialized = true; - } else if (cefApp == null) { - // 后续调用使用无参数版本 - cefApp = CefApp.getInstance(); + logger.info("Default CEF settings initialized"); + } finally { + initLock.unlock(); + } + } + + public static CefApp getInstance() { + if (cefApp == null) { + if (initLock.tryLock()) { + try { + performSafeInitialization(); + } finally { + initLock.unlock(); + } + } else { + handleConcurrentInitialization(); + } } return cefApp; } + private static void performSafeInitialization() { + if (cefApp != null) return; + if (isDisposing.get()) { + throw new IllegalStateException("CEF is during disposal process"); + } + + try { + // 阶段1:启动CEF运行时 + if (!CefApp.startup(new String[0])) { + throw new IllegalStateException("CEF native startup failed"); + } + + // 阶段2:应用设置(仅首次) + if (settingsApplied.compareAndSet(false, true)) { + cefApp = CefApp.getInstance(settings); + logger.info("CEF initialized with custom settings"); + } else { + cefApp = CefApp.getInstance(); + logger.info("CEF reused existing instance"); + } + + isInitialized.set(true); + } catch (IllegalStateException ex) { + handleInitializationError(ex); + } + } + + private static void handleConcurrentInitialization() { + try { + if (initLock.tryLock(3, TimeUnit.SECONDS)) { + try { + if (cefApp == null) { + performSafeInitialization(); + } + } finally { + initLock.unlock(); + } + } + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + logger.warn("CEF initialization interrupted"); + } + } + + private static void handleInitializationError(IllegalStateException ex) { + if (ex.getMessage().contains("Settings can only be passed")) { + logger.warn("Settings conflict detected, recovering..."); + recoverFromSettingsConflict(); + } else if (ex.getMessage().contains("was terminated")) { + handleTerminatedState(ex); + } else { + logger.error("Critical CEF error", ex); + throw new RuntimeException("CEF initialization failed", ex); + } + } + + private static void recoverFromSettingsConflict() { + disposeLock.lock(); + try { + if (cefApp == null) { + cefApp = CefApp.getInstance(); + settingsApplied.set(true); + isInitialized.set(true); + logger.info("Recovered from settings conflict"); + } + } finally { + disposeLock.unlock(); + } + } + + private static void handleTerminatedState(IllegalStateException ex) { + disposeLock.lock(); + try { + logger.warn("CEF terminated state detected"); + dispose(false); + performEmergencyRecovery(); + } finally { + disposeLock.unlock(); + } + } + + private static void performEmergencyRecovery() { + try { + logger.info("Attempting emergency recovery..."); + CefApp.startup(new String[0]); + cefApp = CefApp.getInstance(); + isInitialized.set(true); + settingsApplied.set(true); + logger.info("Emergency recovery successful"); + } catch (Exception e) { + logger.error("Emergency recovery failed", e); + throw new RuntimeException("Unrecoverable CEF state", e); + } + } + + public static synchronized void dispose(boolean isShutdownHook) { + disposeLock.lock(); + try { + if (cefApp == null || isDisposing.get()) return; + + isDisposing.set(true); + try { + logger.info("Disposing CEF resources..."); + cefApp.dispose(); + + if (!isShutdownHook) { + cefApp = null; + isInitialized.set(false); + settingsApplied.set(false); + } + logger.info("CEF resources released"); + } catch (Exception e) { + logger.error("Disposal error", e); + } finally { + isDisposing.set(false); + } + } finally { + disposeLock.unlock(); + } + } + private static void validateSubprocessPath(String path) { File exeFile = new File(path); if (!exeFile.exists()) { - throw new IllegalStateException("jcef_helper.exe not found at: " + path); + String errorMsg = "JCEF helper executable missing: " + path; + logger.error(errorMsg); + throw new IllegalStateException(errorMsg); } + logger.debug("Validated JCEF helper at: {}", path); } - public static synchronized void incrementBrowserCount() { - browserCount++; - } - - public static synchronized void decrementBrowserCount() { - if (--browserCount <= 0) { - disposeCefApp(); - } - } - - private static void disposeCefApp() { - if (cefApp != null) { - cefApp.clearSchemeHandlerFactories(); - cefApp.dispose(); - cefApp = null; - isInitialized = false; - } + // 状态查询接口 + public static String getInitStatus() { + return String.format("Initialized: %s, SettingsApplied: %s, Disposing: %s", + isInitialized.get(), settingsApplied.get(), isDisposing.get()); } } \ No newline at end of file diff --git a/src/main/java/com/axis/innovators/box/browser/MainApplication.java b/src/main/java/com/axis/innovators/box/browser/MainApplication.java index b317bda..bd77c7d 100644 --- a/src/main/java/com/axis/innovators/box/browser/MainApplication.java +++ b/src/main/java/com/axis/innovators/box/browser/MainApplication.java @@ -42,8 +42,7 @@ public class MainApplication { * 弹出AI窗口 * @param parent 父窗口 */ - public static void popupAIWindow - (JFrame parent) { + public static void popupAIWindow(JFrame parent) { LM.loadLibrary(LM.CUDA); modelHandle = LM.llamaLoadModelFromFile(LM.DEEP_SEEK); ctxHandle = LM.createContext(modelHandle); @@ -108,11 +107,11 @@ public class MainApplication { Path filePath = Paths.get(path); - // 验证文件存在性 - if (!Files.exists(filePath)) { - callback.failure(404, "{\"code\":404,\"message\":\"文件未找到\"}"); - return true; - } + //// 验证文件存在性 + //if (!Files.exists(filePath)) { + // callback.failure(404, "{\"code\":404,\"message\":\"文件未找到\"}"); + // return true; + //} // 读取文件内容 String content = Files.readString(filePath, StandardCharsets.UTF_8); diff --git a/src/main/java/com/axis/innovators/box/util/Tray.java b/src/main/java/com/axis/innovators/box/util/Tray.java index 27cddc9..b14007c 100644 --- a/src/main/java/com/axis/innovators/box/util/Tray.java +++ b/src/main/java/com/axis/innovators/box/util/Tray.java @@ -43,9 +43,9 @@ public class Tray { load(new TrayLabels("启动 HTML 查看器", new Runnable() { @Override public void run() { - SwingUtilities.invokeLater(() -> { + //SwingUtilities.invokeLater(() -> { MainApplication.popupHTMLWindow(""); - }); + //}); } }));