From 59e62eaf2a4210fcb9f54aad1ba3c323a5f343d3 Mon Sep 17 00:00:00 2001 From: dolamasa1 Date: Fri, 7 Nov 2025 00:29:46 +0200 Subject: [PATCH] more server connection handlings --- .../java/raven/messenger/Application.java | 53 +++++- .../java/raven/messenger/api/ApiService.java | 167 +++++++++++++++--- .../api/exception/GlobalErrorHandler.java | 44 ++++- .../api/request/RequestFileMonitor.java | 106 ++++++++--- .../raven/messenger/util/ErrorReporter.java | 78 ++++++++ 5 files changed, 387 insertions(+), 61 deletions(-) create mode 100644 messenger-client/src/main/java/raven/messenger/util/ErrorReporter.java diff --git a/messenger-client/src/main/java/raven/messenger/Application.java b/messenger-client/src/main/java/raven/messenger/Application.java index 9e67186..701211b 100644 --- a/messenger-client/src/main/java/raven/messenger/Application.java +++ b/messenger-client/src/main/java/raven/messenger/Application.java @@ -7,6 +7,7 @@ import com.formdev.flatlaf.util.UIScale; import raven.messenger.manager.DialogManager; import raven.messenger.manager.FormsManager; +import raven.messenger.util.ErrorReporter; import javax.swing.*; import java.awt.*; @@ -24,21 +25,59 @@ private void init() { setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setSize(UIScale.scale(new Dimension(1200, 700))); setMinimumSize(UIScale.scale(new Dimension(750, 500))); + + // Enhanced window listener with error handling addWindowListener(new WindowAdapter() { @Override public void windowOpened(WindowEvent e) { - FormsManager.getInstance().initApplication(Application.this); + try { + FormsManager.getInstance().initApplication(Application.this); + } catch (Exception ex) { + ErrorReporter.handleException("Failed to initialize application", ex, true); + } + } + + @Override + public void windowClosing(WindowEvent e) { + // Clean up resources on close + cleanup(); } }); + DialogManager.getInstance().init(this); setLocationRelativeTo(null); } + private void cleanup() { + try { + // Add any cleanup logic here + System.out.println("Application cleanup completed"); + } catch (Exception e) { + ErrorReporter.handleException("Error during application cleanup", e, false); + } + } + public static void main(String[] args) { - FlatRobotoFont.install(); - FlatLaf.registerCustomDefaultsSource("raven.messenger.themes"); - UIManager.put("defaultFont", new Font(FlatRobotoFont.FAMILY, Font.PLAIN, 13)); - FlatMacDarkLaf.setup(); - EventQueue.invokeLater(() -> new Application().setVisible(true)); + // Set up global exception handler + Thread.setDefaultUncaughtExceptionHandler((thread, throwable) -> { + ErrorReporter.handleException("Uncaught exception in thread: " + thread.getName(), + throwable, true); + }); + + try { + FlatRobotoFont.install(); + FlatLaf.registerCustomDefaultsSource("raven.messenger.themes"); + UIManager.put("defaultFont", new Font(FlatRobotoFont.FAMILY, Font.PLAIN, 13)); + FlatMacDarkLaf.setup(); + EventQueue.invokeLater(() -> { + try { + new Application().setVisible(true); + } catch (Exception e) { + ErrorReporter.handleException("Failed to create application window", e, true); + } + }); + } catch (Exception e) { + ErrorReporter.handleException("Failed to initialize application theme", e, true); + } } -} +} \ No newline at end of file diff --git a/messenger-client/src/main/java/raven/messenger/api/ApiService.java b/messenger-client/src/main/java/raven/messenger/api/ApiService.java index 697003f..72cf5d3 100644 --- a/messenger-client/src/main/java/raven/messenger/api/ApiService.java +++ b/messenger-client/src/main/java/raven/messenger/api/ApiService.java @@ -4,23 +4,29 @@ import io.restassured.builder.RequestSpecBuilder; import io.restassured.http.ContentType; import io.restassured.http.Cookies; +import io.restassured.response.Response; import raven.messenger.api.exception.GlobalErrorHandler; import raven.messenger.socket.SocketService; import raven.messenger.store.CookieManager; +import raven.messenger.util.ErrorReporter; import java.io.IOException; +import java.util.concurrent.TimeUnit; public class ApiService { private static ApiService instance; public static final String API_VERSION = "6"; - public static final String IP = "http://localhost"; - - // Enable this for connect to online server // public static final String IP = "http://52.221.189.33"; + // Connection testing constants + private static final int CONNECTION_TIMEOUT = 10; + private static final int SOCKET_TIMEOUT = 30; + private static final int MAX_RETRIES = 3; + private static final long RETRY_DELAY_MS = 1000; + public static ApiService getInstance() { if (instance == null) { instance = new ApiService(); @@ -29,41 +35,158 @@ public static ApiService getInstance() { } private ApiService() { + // Configure timeouts + RestAssured.config = RestAssured.config() + .httpClient(io.restassured.config.HttpClientConfig.httpClientConfig() + .setParam("http.connection.timeout", CONNECTION_TIMEOUT * 1000) + .setParam("http.socket.timeout", SOCKET_TIMEOUT * 1000)); } public boolean init() { - Cookies cookies = getCookie(); - installConfig(cookies); - return cookies != null; + try { + Cookies cookies = getCookie(); + installConfig(cookies); + + // Test backend connection + if (!testBackendConnection()) { + ErrorReporter.reportError("Backend Connection Failed", + "Cannot connect to backend server at: " + IP, false); + return false; + } + + return cookies != null; + } catch (Exception e) { + ErrorReporter.handleException("Failed to initialize API service", e, false); + return false; + } } public void installConfig(Cookies cookies) { - RestAssured.reset(); - RestAssured.requestSpecification = new RequestSpecBuilder() - .setContentType(ContentType.JSON) - .setBaseUri(IP) - .addHeader("VERSION", API_VERSION) - .setBasePath("api") - .setPort(5000) - .setAuth(RestAssured.preemptive().basic("user", "raven-messenger-server")) - .addFilter(new GlobalErrorHandler()) - .build(); - if (cookies != null) { - CookieManager.getInstance().setCookieString(cookies.toString()); - RestAssured.requestSpecification.cookies(cookies); + try { + RestAssured.reset(); + RestAssured.requestSpecification = new RequestSpecBuilder() + .setContentType(ContentType.JSON) + .setBaseUri(IP) + .addHeader("VERSION", API_VERSION) + .setBasePath("api") + .setPort(5000) + .setAuth(RestAssured.preemptive().basic("user", "raven-messenger-server")) + .addFilter(new GlobalErrorHandler()) + .build(); + + if (cookies != null) { + CookieManager.getInstance().setCookieString(cookies.toString()); + RestAssured.requestSpecification.cookies(cookies); + } + } catch (Exception e) { + ErrorReporter.handleException("Failed to install API configuration", e, false); + } + } + + /** + * Test backend connection with retry mechanism + */ + public boolean testBackendConnection() { + for (int attempt = 1; attempt <= MAX_RETRIES; attempt++) { + try { + System.out.println("Testing backend connection (attempt " + attempt + "/" + MAX_RETRIES + ")..."); + + Response response = RestAssured.given() + .baseUri(IP) + .port(5000) + .basePath("api") + .when() + .get("/health") // You might want to create a health endpoint + .then() + .extract() + .response(); + + if (response.getStatusCode() == 200) { + System.out.println("✓ Backend connection successful"); + return true; + } else { + System.out.println("✗ Backend responded with status: " + response.getStatusCode()); + } + } catch (Exception e) { + System.out.println("✗ Connection attempt " + attempt + " failed: " + e.getMessage()); + + if (attempt < MAX_RETRIES) { + try { + Thread.sleep(RETRY_DELAY_MS); + } catch (InterruptedException ie) { + Thread.currentThread().interrupt(); + break; + } + } + } } + return false; + } + + /** + * Enhanced connection test with detailed diagnostics + */ + public ConnectionTestResult testConnectionWithDiagnostics() { + ConnectionTestResult result = new ConnectionTestResult(); + + try { + // Test basic connectivity + long startTime = System.currentTimeMillis(); + Response response = RestAssured.given() + .when() + .get("/test") // Replace with an actual test endpoint + .then() + .extract() + .response(); + long endTime = System.currentTimeMillis(); + + result.responseTime = endTime - startTime; + result.statusCode = response.getStatusCode(); + result.success = response.getStatusCode() == 200; + result.message = "Connection test completed"; + + } catch (Exception e) { + result.success = false; + result.message = "Connection failed: " + e.getMessage(); + result.exception = e; + } + + return result; } public void closeAll() { - CookieManager.getInstance().clearCookie(); - SocketService.getInstance().close(); + try { + CookieManager.getInstance().clearCookie(); + SocketService.getInstance().close(); + System.out.println("API service cleanup completed"); + } catch (Exception e) { + ErrorReporter.handleException("Error during API service cleanup", e, false); + } } public Cookies getCookie() { try { return CookieManager.getInstance().getCookie(); } catch (IOException | ClassNotFoundException e) { + ErrorReporter.handleException("Failed to retrieve cookies", e, false); return null; } } -} + + /** + * Inner class for connection test results + */ + public static class ConnectionTestResult { + public boolean success; + public String message; + public long responseTime; + public int statusCode; + public Exception exception; + + @Override + public String toString() { + return String.format("Connection Test: %s | Status: %d | Response Time: %dms | Message: %s", + success ? "SUCCESS" : "FAILED", statusCode, responseTime, message); + } + } +} \ No newline at end of file diff --git a/messenger-client/src/main/java/raven/messenger/api/exception/GlobalErrorHandler.java b/messenger-client/src/main/java/raven/messenger/api/exception/GlobalErrorHandler.java index 635cc95..636b3d4 100644 --- a/messenger-client/src/main/java/raven/messenger/api/exception/GlobalErrorHandler.java +++ b/messenger-client/src/main/java/raven/messenger/api/exception/GlobalErrorHandler.java @@ -8,6 +8,7 @@ import raven.messenger.api.ApiService; import raven.messenger.login.Login; import raven.messenger.manager.FormsManager; +import raven.messenger.util.ErrorReporter; public class GlobalErrorHandler implements OrderedFilter { @@ -17,12 +18,41 @@ public int getOrder() { } @Override - public Response filter(FilterableRequestSpecification filterableRequestSpecification, FilterableResponseSpecification filterableResponseSpecification, FilterContext filterContext) { - Response response = filterContext.next(filterableRequestSpecification, filterableResponseSpecification); - if (response.getStatusCode() == 401) { - ApiService.getInstance().closeAll(); - FormsManager.getInstance().showForm(new Login(null)); + public Response filter(FilterableRequestSpecification request, FilterableResponseSpecification response, FilterContext context) { + try { + Response resp = context.next(request, response); + int statusCode = resp.getStatusCode(); + + // Log all non-200 responses for debugging + if (statusCode != 200) { + System.err.println("API Error - Status: " + statusCode + + " | URL: " + request.getURI() + + " | Method: " + request.getMethod()); + + if (statusCode >= 400) { + String responseBody = resp.getBody().asString(); + System.err.println("Response body: " + (responseBody.length() > 500 ? + responseBody.substring(0, 500) + "..." : responseBody)); + } + } + + if (statusCode == 401) { + handleUnauthorized(); + } else if (statusCode >= 500) { + ErrorReporter.reportError("Server Error", + "Server returned error: " + statusCode + "\nURL: " + request.getURI(), false); + } + + return resp; + } catch (Exception e) { + ErrorReporter.handleException("Error in API request filter", e, false); + throw e; } - return response; } -} + + private void handleUnauthorized() { + System.err.println("Authentication failed (401), redirecting to login..."); + ApiService.getInstance().closeAll(); + FormsManager.getInstance().showForm(new Login(null)); + } +} \ No newline at end of file diff --git a/messenger-client/src/main/java/raven/messenger/api/request/RequestFileMonitor.java b/messenger-client/src/main/java/raven/messenger/api/request/RequestFileMonitor.java index 5f485f7..cbe72a0 100644 --- a/messenger-client/src/main/java/raven/messenger/api/request/RequestFileMonitor.java +++ b/messenger-client/src/main/java/raven/messenger/api/request/RequestFileMonitor.java @@ -1,10 +1,13 @@ package raven.messenger.api.request; +import raven.messenger.util.ErrorReporter; + import java.io.*; import java.nio.file.Files; import java.nio.file.Path; import java.util.HashSet; import java.util.Set; +import java.util.concurrent.atomic.AtomicLong; public abstract class RequestFileMonitor { @@ -33,73 +36,125 @@ public RequestFileMonitor(String fileName, File savePath) { private String fileName; private File savePath; - private boolean run; - private double downloadSize; - private double oldDownloadSize; + private volatile boolean run; + private AtomicLong downloadSize = new AtomicLong(0); + private AtomicLong oldDownloadSize = new AtomicLong(0); private Thread progressThread; public synchronized void save(InputStream inputStream, long length) { if (run) { + System.err.println("File monitor is already running for: " + fileName); return; } if (fileSets.contains(fileName)) { + System.err.println("File is already being processed: " + fileName); return; } + fileSets.add(fileName); new Thread(() -> { Path tempPath = null; try { run = true; - // Default 4096 final int BUFFER_SIZE = 4096; byte[] downloadBuffer = new byte[BUFFER_SIZE]; - tempPath = Files.createTempFile("temp_", "_" + savePath.getName()); + + // Create temp file - FIXED: Use system temp directory instead of trying to use savePath's parent + String prefix = "temp_" + System.currentTimeMillis() + "_"; + tempPath = Files.createTempFile(prefix, "_" + savePath.getName()); File tempFile = tempPath.toFile(); tempFile.deleteOnExit(); - OutputStream out = new FileOutputStream(tempFile); - int downloadBytesRead; - initEvent(length); - while ((downloadBytesRead = inputStream.read(downloadBuffer)) != -1) { - out.write(downloadBuffer, 0, downloadBytesRead); - downloadSize += downloadBytesRead; + + System.out.println("Starting file download: " + fileName + " -> " + savePath.getAbsolutePath()); + System.out.println("Temp file: " + tempFile.getAbsolutePath()); + System.out.println("Expected size: " + length + " bytes"); + + try (OutputStream out = new FileOutputStream(tempFile)) { + initEvent(length); + int downloadBytesRead; + while ((downloadBytesRead = inputStream.read(downloadBuffer)) != -1) { + out.write(downloadBuffer, 0, downloadBytesRead); + downloadSize.addAndGet(downloadBytesRead); + } } - out.close(); + inputStream.close(); run = false; - progressThread.join(); + + if (progressThread != null) { + progressThread.join(5000); // Wait up to 5 seconds for progress thread + } + + System.out.println("File download completed: " + fileName); + } catch (IOException | InterruptedException e) { + ErrorReporter.handleException("Error during file download: " + fileName, e, false); fileSets.remove(fileName); error(e); } finally { run = false; if (tempPath != null) { done(tempPath); + } else { + fileSets.remove(fileName); } progressThread = null; } - }).start(); + }, "FileDownload-" + fileName).start(); } public void done(Path tempPath) { try { + // Verify temp file exists and has content + if (!Files.exists(tempPath) || Files.size(tempPath) == 0) { + throw new IOException("Temp file is empty or doesn't exist"); + } + + // Ensure parent directory exists - FIXED: Use proper File methods + File parentDir = savePath.getParentFile(); + if (parentDir != null && !parentDir.exists()) { + parentDir.mkdirs(); + } + + // Move temp file to final location + Files.move(tempPath, savePath.toPath(), java.nio.file.StandardCopyOption.REPLACE_EXISTING); fileSets.remove(fileName); - Files.copy(tempPath, savePath.toPath()); + + System.out.println("File successfully saved: " + savePath.getAbsolutePath()); + System.out.println("Final file size: " + savePath.length() + " bytes"); + done(savePath); } catch (IOException e) { + fileSets.remove(fileName); + ErrorReporter.handleException("Failed to finalize file: " + fileName, e, false); error(e); } } private void initEvent(long length) { progressThread = new Thread(() -> { - while (run) { - sleep(1); - if (oldDownloadSize != downloadSize) { - oldDownloadSize = downloadSize; - responseMonitor(length, (long) downloadSize); + System.out.println("Starting progress monitoring for: " + fileName); + while (run && !Thread.currentThread().isInterrupted()) { + sleep(100); // Reduced frequency to avoid excessive CPU usage + long currentSize = downloadSize.get(); + long oldSize = oldDownloadSize.get(); + + if (oldSize != currentSize) { + oldDownloadSize.set(currentSize); + responseMonitor(length, currentSize); + + // Log progress every 10% or 1MB + if (length > 0) { + int percent = (int) ((currentSize * 100) / length); + if (percent % 10 == 0 && percent > 0) { + System.out.println(String.format("Download progress: %d%% (%d/%d bytes)", + percent, currentSize, length)); + } + } } } - }); + System.out.println("Progress monitoring ended for: " + fileName); + }, "ProgressMonitor-" + fileName); progressThread.start(); } @@ -113,11 +168,12 @@ public boolean isRunning() { public abstract void error(Exception e); - private void sleep(long l) { + private void sleep(long millis) { try { - Thread.sleep(l); + Thread.sleep(millis); } catch (InterruptedException e) { - System.err.println(e); + Thread.currentThread().interrupt(); + System.err.println("Progress monitoring interrupted for: " + fileName); } } -} +} \ No newline at end of file diff --git a/messenger-client/src/main/java/raven/messenger/util/ErrorReporter.java b/messenger-client/src/main/java/raven/messenger/util/ErrorReporter.java new file mode 100644 index 0000000..38b5e3b --- /dev/null +++ b/messenger-client/src/main/java/raven/messenger/util/ErrorReporter.java @@ -0,0 +1,78 @@ +package raven.messenger.util; + +import javax.swing.*; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.text.SimpleDateFormat; +import java.util.Date; + +public class ErrorReporter { + + private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + + public static void handleException(String message, Throwable throwable, boolean showDialog) { + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + throwable.printStackTrace(pw); + + String errorDetails = String.format( + "Time: %s%nMessage: %s%nError: %s%nStack Trace:%n%s", + DATE_FORMAT.format(new Date()), + message, + throwable.getMessage(), + sw.toString() + ); + + // Log to console + System.err.println("=== ERROR REPORT ==="); + System.err.println(errorDetails); + System.err.println("===================="); + + // Show dialog if requested + if (showDialog && !SwingUtilities.isEventDispatchThread()) { + SwingUtilities.invokeLater(() -> showErrorDialog(message, errorDetails)); + } else if (showDialog) { + showErrorDialog(message, errorDetails); + } + } + + public static void reportError(String title, String message, boolean showDialog) { + String errorDetails = String.format( + "Time: %s%nTitle: %s%nMessage: %s", + DATE_FORMAT.format(new Date()), + title, + message + ); + + System.err.println("=== ERROR ==="); + System.err.println(errorDetails); + System.err.println("============="); + + if (showDialog) { + if (!SwingUtilities.isEventDispatchThread()) { + SwingUtilities.invokeLater(() -> showErrorDialog(title, message)); + } else { + showErrorDialog(title, message); + } + } + } + + private static void showErrorDialog(String title, String message) { + JTextArea textArea = new JTextArea(message); + textArea.setEditable(false); + textArea.setWrapStyleWord(true); + textArea.setLineWrap(true); + textArea.setCaretPosition(0); + textArea.setBackground(UIManager.getColor("Panel.background")); + + JScrollPane scrollPane = new JScrollPane(textArea); + scrollPane.setPreferredSize(new java.awt.Dimension(500, 300)); + + JOptionPane.showMessageDialog( + null, + scrollPane, + title, + JOptionPane.ERROR_MESSAGE + ); + } +} \ No newline at end of file