import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.util.concurrent.*; public class ProcessRunner implements Runnable { private ProcessBuilder processBuilder; private volatile Exception threadException; // Wyjątek przechowywany w tle, musi być volatile public ProcessRunner(ProcessBuilder processBuilder) { this.processBuilder = processBuilder; } @Override public void run() { try { Process process = processBuilder.start(); BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream())); BufferedReader errorReader = new BufferedReader(new InputStreamReader(process.getErrorStream())); // Logi wyjściowe działające w tle new Thread(() -> { String line; try { while ((line = reader.readLine()) != null) { System.out.println("LOG: " + line); } } catch (IOException e) { System.err.println("Error reading log: " + e.getMessage()); } }).start(); // Odczytywanie błędów i zapisywanie, ale nie przerywamy procesu String errorLine; while ((errorLine = errorReader.readLine()) != null) { System.err.println("ERROR: " + errorLine); threadException = new RuntimeException("Error detected in process: " + errorLine); } int exitCode = process.waitFor(); // Czekamy aż proces się zakończy if (exitCode != 0) { threadException = new RuntimeException("Process finished with non-zero exit code: " + exitCode); } } catch (IOException | InterruptedException e) { threadException = e; } } public Exception getThreadException() { return threadException; } } // Klasa główna public class MainApp { public static void main(String[] args) { // Tworzymy ExecutorService z pulą wątków ExecutorService executorService = Executors.newFixedThreadPool(2); // Tworzymy ProcessBuilder dla naszej aplikacji ProcessBuilder processBuilder = new ProcessBuilder("path_to_exe", "arg1", "arg2"); // Tworzymy obiekt ProcessRunner ProcessRunner processRunner = new ProcessRunner(processBuilder); // Składamy zadanie do wykonania w tle executorService.submit(processRunner); // Główna pętla, sprawdzająca stan procesu w tle co pewien czas ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1); scheduler.scheduleAtFixedRate(() -> { Exception e = processRunner.getThreadException(); if (e != null) { System.err.println("Error caught from process: " + e.getMessage()); // Obsługa błędów, ale proces nadal działa } }, 0, 1, TimeUnit.SECONDS); // Sprawdzanie co sekundę // Proces w tle działa, a główny wątek kontynuuje swoje działanie System.out.println("Main thread continues while process runs in the background."); // Upewnij się, że ExecutorService i Scheduler zostaną poprawnie zakończone Runtime.getRuntime().addShutdownHook(new Thread(() -> { executorService.shutdown(); scheduler.shutdown(); })); } } Kluczowe punkty rozwiązania: Proces działa równolegle: ExecutorService uruchamia proces w oddzielnym wątku, a ScheduledExecutorService sprawdza co pewien czas, czy wystąpił błąd. Raportowanie błędów bez zatrzymywania procesu: Wątek z procesem może raportować błędy, ale nie przerywa swojego działania. Główna pętla (za pomocą ScheduledExecutorService) sprawdza, czy w procesie pojawił się błąd i odpowiednio go obsługuje. Błędy są zgłaszane w tle: W przypadku wystąpienia błędu, wyjątek jest przechwytywany i można go obsłużyć (np. logować, wysyłać powiadomienia) bez zatrzymywania procesu. Proces działa niezależnie: Proces działa w tle bez ingerencji w główny wątek. Główny wątek aplikacji może działać normalnie, niezależnie od procesu w tle. Dodatkowe informacje: ScheduledExecutorService jest używany do okresowego sprawdzania, czy w procesie pojawił się błąd. volatile: Zmienna threadException jest oznaczona jako volatile, aby zapewnić poprawną widoczność zmiany wartości między wątkami. Shutdown: Runtime.getRuntime().addShutdownHook dodaje hook, który zamyka ExecutorService i Scheduler podczas zamykania aplikacji, zapewniając poprawne zakończenie wszystkich wątków. To podejście spełnia Twoje wymagania: proces działa w tle, błędy są zgłaszane, a proces nie zostaje przerwany w momencie wystąpienia błędu.