perf(app): decouple javafx toolkit for fast ccsp startup on demand

This commit is contained in:
Claudecio Martins
2026-10-20 15:52:50 -03:00
parent bac691aae1
commit 051614a919
4 changed files with 229 additions and 39 deletions
@@ -100,37 +100,18 @@ public class CyberCoreSignerApplication {
logger.error("ALERTA CRÍTICO: Engine criptográfica JCA/JCE não está totalmente operacional!");
}
// Se o ambiente tiver suporte gráfico e não for headless forçado, abre a interface JavaFX
boolean isHeadless = headlessMode || GraphicsEnvironment.isHeadless();
if (isHeadless) {
logger.info("Executando em modo console / headless (sem interface gráfica).");
startHeadlessServer();
} else {
try {
logger.info("Iniciando interface gráfica desktop...");
CyberCoreDesktopApplication.setDevelopmentMode(developmentMode);
Runtime.getRuntime().addShutdownHook(new Thread(() ->
logger.warn("JVM em encerramento (sinal externo, System.exit ou fim do aplicativo)."),
"cybercore-shutdown-diagnostic"));
Application.launch(CyberCoreDesktopApplication.class, args);
} catch (Throwable t) {
logger.warn("Não foi possível inicializar ambiente gráfico ({}). Continuando em modo console.", t.getMessage());
startHeadlessServer();
}
// 1. Inicializa o núcleo e o servidor CCSP imediatamente (alta velocidade)
CertificateManager certManager = CertificateManager.createDefault();
if (developmentMode) {
logger.info("Modo de desenvolvimento ativo (com certificados reais do sistema).");
}
SigningService signingService = new SigningService(certManager);
br.com.cybercore.signer.core.protocol.CcspUserPromptListener promptListener =
new br.com.cybercore.signer.ui.prompt.JavaFxPromptListener();
logger.info("CyberCore Signer finalizado.");
}
private static void startHeadlessServer() {
try {
CertificateManager certManager = CertificateManager.createDefault();
if (developmentMode) {
logger.info("Modo de desenvolvimento ativo (com certificados reais do sistema).");
}
SigningService signingService = new SigningService(certManager);
runningServer = new CcspServer(configuredPort, certManager, signingService, br.com.cybercore.signer.core.security.ApplicationRegistry.getInstance(), null);
runningServer = new CcspServer(configuredPort, certManager, signingService,
br.com.cybercore.signer.core.security.ApplicationRegistry.getInstance(), promptListener);
runningServer.start();
logger.info("Servidor local CCSP/1 pronto em http://127.0.0.1:{}", runningServer.getPort());
@@ -138,22 +119,50 @@ public class CyberCoreSignerApplication {
logger.info("Encerrando servidor local CCSP/1...");
stopServer();
}));
// Mantém processo vivo para atender requisições web, a menos que em modo no-wait
if (!noWaitMode) {
Thread.currentThread().join();
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} catch (java.net.BindException e) {
logger.error("ALERTA DE COLISÃO DE PORTA: A porta {} já está em uso por outra instância do CyberCore Signer ou outro serviço. Para usar uma porta alternativa, utilize o parâmetro --port <porta> ou defina a variável de ambiente CYBERCORE_PORT.", configuredPort);
} catch (Exception e) {
if (e.getCause() instanceof java.net.BindException || (e.getMessage() != null && e.getMessage().contains("Address already in use"))) {
logger.error("ALERTA DE COLISÃO DE PORTA: A porta {} já está em uso por outra instância do CyberCore Signer ou outro serviço. Para usar uma porta alternativa, utilize o parâmetro --port <porta> ou defina a variável de ambiente CYBERCORE_PORT.", configuredPort);
} else {
logger.error("Falha ao inicializar servidor local CCSP/1 no modo console: {}", e.getMessage(), e);
logger.error("Falha ao inicializar servidor local CCSP/1: {}", e.getMessage(), e);
}
}
// 2. Se o ambiente tiver suporte gráfico e não for headless forçado, abre a interface JavaFX
boolean isHeadless = headlessMode || GraphicsEnvironment.isHeadless();
if (!isHeadless) {
try {
logger.info("Iniciando interface gráfica desktop desacoplada...");
CyberCoreDesktopApplication.setDevelopmentMode(developmentMode);
CyberCoreDesktopApplication.setSharedServices(runningServer, certManager, signingService);
Thread uiThread = new Thread(() -> {
try {
Application.launch(CyberCoreDesktopApplication.class, args);
} catch (Throwable t) {
logger.warn("Interface gráfica finalizada ou indisponível: {}", t.getMessage());
}
}, "cybercore-desktop-ui");
uiThread.setDaemon(false);
uiThread.start();
} catch (Throwable t) {
logger.warn("Não foi possível inicializar ambiente gráfico ({}). Continuando em modo serviço.", t.getMessage());
}
} else {
logger.info("Executando em modo console / headless (sem interface gráfica).");
}
// Mantém processo vivo para atender requisições web, a menos que em modo no-wait
if (!noWaitMode) {
try {
Thread.currentThread().join();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
logger.info("CyberCore Signer finalizado.");
}
public static void parseArguments(String[] args) {
@@ -34,4 +34,17 @@ class ArchitectureTest {
rule.check(importedClasses);
}
@Test
@DisplayName("O módulo Core não deve acessar o ciclo de vida do toolkit JavaFX (Application ou Platform)")
void coreShouldNotAccessJavaFxLifecycle() {
ArchRule rule = noClasses()
.that().resideInAPackage("br.com.cybercore.signer.core..")
.should().dependOnClassesThat()
.haveFullyQualifiedName("javafx.application.Application")
.orShould().dependOnClassesThat()
.haveFullyQualifiedName("javafx.application.Platform");
rule.check(importedClasses);
}
}