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
@@ -75,12 +75,22 @@ public class CyberCoreDesktopApplication extends Application {
private static boolean developmentMode = false;
private static MainAppView mainAppView;
private static volatile CcspServer sharedCcspServer = null;
private static volatile CertificateManager sharedCertManager = null;
private static volatile SigningService sharedSigningService = null;
private CertificateManager certificateManager;
private SigningService signingService;
private CcspServer ccspServer;
private SystemTrayManager systemTrayManager;
private SmartCardWatcher smartCardWatcher;
public static void setSharedServices(CcspServer server, CertificateManager certManager, SigningService signingService) {
sharedCcspServer = server;
sharedCertManager = certManager;
sharedSigningService = signingService;
}
public static void setDevelopmentMode(boolean devMode) {
developmentMode = devMode;
}
@@ -93,10 +103,12 @@ public class CyberCoreDesktopApplication extends Application {
public void start(Stage primaryStage) {
logger.info("Iniciando interface gráfica JavaFX...");
Platform.setImplicitExit(false);
br.com.cybercore.signer.ui.prompt.JavaFxPromptListener.markToolkitInitialized();
br.com.cybercore.signer.ui.prompt.JavaFxPromptListener.setActiveStage(primaryStage);
// Inicializa serviços centrais reais da plataforma
this.certificateManager = CertificateManager.createDefault();
this.signingService = new SigningService(this.certificateManager);
// Inicializa ou reutiliza serviços centrais reais da plataforma
this.certificateManager = (sharedCertManager != null) ? sharedCertManager : CertificateManager.createDefault();
this.signingService = (sharedSigningService != null) ? sharedSigningService : new SigningService(this.certificateManager);
if (developmentMode) {
logger.info("Modo de desenvolvimento ativo (utilizando certificados reais da máquina).");
@@ -419,6 +431,12 @@ public class CyberCoreDesktopApplication extends Application {
}
private void initCcspServer(Stage stage) {
if (sharedCcspServer != null) {
this.ccspServer = sharedCcspServer;
logger.info("Vinculada interface desktop ao servidor CCSP/1 já ativo na porta {}", this.ccspServer.getPort());
return;
}
CcspUserPromptListener promptListener = new CcspUserPromptListener() {
@Override
public CompletableFuture<Boolean> requestApplicationAuthorization(String origin, String appName, String applicationId) {
@@ -0,0 +1,150 @@
package br.com.cybercore.signer.ui.prompt;
import br.com.cybercore.signer.core.domain.ManagedCertificate;
import br.com.cybercore.signer.core.domain.SigningContext;
import br.com.cybercore.signer.core.protocol.CcspRequest;
import br.com.cybercore.signer.core.protocol.CcspUserPromptListener;
import br.com.cybercore.signer.core.security.ApplicationRegistry;
import br.com.cybercore.signer.core.security.AuthorizedApplication;
import br.com.cybercore.signer.ui.view.ApplicationApprovalDialog;
import br.com.cybercore.signer.ui.view.SignaturePromptDialog;
import javafx.application.Platform;
import javafx.stage.Stage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.time.Instant;
import java.util.Base64;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* Listener desacoplado de prompts do CCSP para a interface JavaFX.
* Garante que o toolkit JavaFX só seja inicializado sob demanda (on-demand)
* quando houver um diálogo visual de confirmação a ser exibido ao usuário.
*/
public class JavaFxPromptListener implements CcspUserPromptListener {
private static final Logger logger = LoggerFactory.getLogger(JavaFxPromptListener.class);
private static final AtomicBoolean FX_TOOLKIT_INITIALIZED = new AtomicBoolean(false);
private static volatile Stage activeStage = null;
public static void setActiveStage(Stage stage) {
activeStage = stage;
}
public static void markToolkitInitialized() {
FX_TOOLKIT_INITIALIZED.set(true);
}
public static void runOnFxThread(Runnable action) {
if (FX_TOOLKIT_INITIALIZED.get()) {
Platform.runLater(action);
} else {
try {
Platform.startup(() -> {
FX_TOOLKIT_INITIALIZED.set(true);
Platform.setImplicitExit(false);
action.run();
});
} catch (IllegalStateException e) {
FX_TOOLKIT_INITIALIZED.set(true);
Platform.runLater(action);
}
}
}
@Override
public CompletableFuture<Boolean> requestApplicationAuthorization(String origin, String appName, String applicationId) {
CompletableFuture<Boolean> future = new CompletableFuture<>();
runOnFxThread(() -> {
try {
ApplicationApprovalDialog dialog = new ApplicationApprovalDialog(activeStage, origin, appName, applicationId);
dialog.showAndAwait().whenComplete((approved, err) -> {
if (err != null) {
future.complete(false);
} else {
future.complete(Boolean.TRUE.equals(approved));
}
});
} catch (Exception ex) {
logger.error("Erro ao exibir diálogo de autorização de aplicação: {}", ex.getMessage());
future.complete(false);
}
});
return future;
}
@Override
public CompletableFuture<Boolean> requestSignatureConfirmation(CcspRequest request, ManagedCertificate certificate) {
CompletableFuture<Boolean> future = new CompletableFuture<>();
runOnFxThread(() -> {
try {
byte[] digestBytes;
if (request.digest() != null && !request.digest().isBlank()) {
digestBytes = Base64.getDecoder().decode(request.digest());
} else if (request.pdf() != null && !request.pdf().isBlank()) {
byte[] rawPdf = Base64.getDecoder().decode(request.pdf());
digestBytes = MessageDigest.getInstance("SHA-256").digest(rawPdf);
} else if (request.challenge() != null && !request.challenge().isBlank()) {
digestBytes = MessageDigest.getInstance("SHA-256")
.digest(request.challenge().getBytes(StandardCharsets.UTF_8));
} else {
digestBytes = new byte[32];
}
SigningContext context = new SigningContext(
request.requestId(),
request.applicationId(),
digestBytes,
request.algorithm() != null ? request.algorithm() : "SHA256withRSA",
certificate.id(),
Instant.now()
);
String appName = ApplicationRegistry.getInstance().findById(request.applicationId())
.map(AuthorizedApplication::name)
.orElse(request.applicationId());
Long docSize = request.documentSize();
if (docSize == null && request.pdf() != null) {
try {
docSize = (long) Base64.getDecoder().decode(request.pdf()).length;
} catch (IllegalArgumentException ignored) {
}
} else if (docSize == null && request.xmlBase64() != null) {
try {
docSize = (long) Base64.getDecoder().decode(request.xmlBase64()).length;
} catch (IllegalArgumentException ignored) {
}
}
String requestOrigin = request.origin();
if (requestOrigin == null || requestOrigin.isBlank()) {
requestOrigin = ApplicationRegistry.getInstance().findById(request.applicationId())
.map(a -> String.join(", ", a.allowedOrigins()))
.orElse("Requisição Web Local");
}
SignaturePromptDialog dialog = new SignaturePromptDialog(
activeStage, appName, requestOrigin, certificate, context,
request.documentName(), request.documentType(), docSize, request.isAuthentication()
);
dialog.showAndAwait().whenComplete((approved, err) -> {
if (err != null) {
future.complete(false);
} else {
future.complete(Boolean.TRUE.equals(approved));
}
});
} catch (Exception ex) {
logger.error("Erro ao exibir diálogo de confirmação de assinatura: {}", ex.getMessage());
future.complete(false);
}
});
return future;
}
}