fixes and timeouts

This commit is contained in:
Anthony Stirling 2024-01-10 00:33:07 +00:00
parent ef12c2f892
commit e717d83f75
3 changed files with 52 additions and 13 deletions

View file

@ -35,15 +35,17 @@ public class FileToPdf {
command.add("weasyprint"); command.add("weasyprint");
} else { } else {
command.add("wkhtmltopdf"); command.add("wkhtmltopdf");
command.add("--enable-local-file-access");
} }
command.add(tempInputFile.toString()); command.add(tempInputFile.toString());
command.add(tempOutputFile.toString()); command.add(tempOutputFile.toString());
ProcessExecutorResult returnCode; ProcessExecutorResult returnCode;
if (fileName.endsWith(".zip")) { if (fileName.endsWith(".zip")) {
if (htmlFormatsInstalled) { if (htmlFormatsInstalled) {
command.add("--allow"); // command.add(1, "--allow");
command.add(tempOutputFile.getParent().toString()); // command.add(2, tempInputFile.getParent().toString());
} }
returnCode = returnCode =
ProcessExecutor.getInstance(ProcessExecutor.Processes.WEASYPRINT) ProcessExecutor.getInstance(ProcessExecutor.Processes.WEASYPRINT)

View file

@ -4,15 +4,22 @@ import java.io.BufferedReader;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.io.InterruptedIOException;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Semaphore; import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ProcessExecutor { public class ProcessExecutor {
private static final Logger logger = LoggerFactory.getLogger(ProcessExecutor.class);
public enum Processes { public enum Processes {
LIBRE_OFFICE, LIBRE_OFFICE,
OCR_MY_PDF, OCR_MY_PDF,
@ -26,7 +33,7 @@ public class ProcessExecutor {
private static final Map<Processes, ProcessExecutor> instances = new ConcurrentHashMap<>(); private static final Map<Processes, ProcessExecutor> instances = new ConcurrentHashMap<>();
public static ProcessExecutor getInstance(Processes processType) { public static ProcessExecutor getInstance(Processes processType) {
return getInstance(processType, false); return getInstance(processType, true);
} }
public static ProcessExecutor getInstance(Processes processType, boolean liveUpdates) { public static ProcessExecutor getInstance(Processes processType, boolean liveUpdates) {
@ -43,16 +50,29 @@ public class ProcessExecutor {
case INSTALL_APP -> 1; case INSTALL_APP -> 1;
case CALIBRE -> 1; case CALIBRE -> 1;
}; };
return new ProcessExecutor(semaphoreLimit, liveUpdates);
long timeoutMinutes =
switch (key) {
case LIBRE_OFFICE -> 30;
case OCR_MY_PDF -> 30;
case PYTHON_OPENCV -> 30;
case GHOSTSCRIPT -> 5;
case WEASYPRINT -> 30;
case INSTALL_APP -> 60;
case CALIBRE -> 30;
};
return new ProcessExecutor(semaphoreLimit, liveUpdates, timeoutMinutes);
}); });
} }
private final Semaphore semaphore; private final Semaphore semaphore;
private final boolean liveUpdates; private final boolean liveUpdates;
private long timeoutDuration;
private ProcessExecutor(int semaphoreLimit, boolean liveUpdates) { private ProcessExecutor(int semaphoreLimit, boolean liveUpdates, long timeout) {
this.semaphore = new Semaphore(semaphoreLimit); this.semaphore = new Semaphore(semaphoreLimit);
this.liveUpdates = liveUpdates; this.liveUpdates = liveUpdates;
this.timeoutDuration = timeout;
} }
public ProcessExecutorResult runCommandWithOutputHandling(List<String> command) public ProcessExecutorResult runCommandWithOutputHandling(List<String> command)
@ -62,12 +82,12 @@ public class ProcessExecutor {
public ProcessExecutorResult runCommandWithOutputHandling( public ProcessExecutorResult runCommandWithOutputHandling(
List<String> command, File workingDirectory) throws IOException, InterruptedException { List<String> command, File workingDirectory) throws IOException, InterruptedException {
int exitCode = 1;
String messages = ""; String messages = "";
int exitCode = 1;
semaphore.acquire(); semaphore.acquire();
try { try {
System.out.print("Running command: " + String.join(" ", command)); logger.info("Running command: " + String.join(" ", command));
ProcessBuilder processBuilder = new ProcessBuilder(command); ProcessBuilder processBuilder = new ProcessBuilder(command);
// Use the working directory if it's set // Use the working directory if it's set
@ -91,8 +111,11 @@ public class ProcessExecutor {
String line; String line;
while ((line = errorReader.readLine()) != null) { while ((line = errorReader.readLine()) != null) {
errorLines.add(line); errorLines.add(line);
if (liveUpdates) System.out.println(line); if (liveUpdates) logger.info(line);
} }
} catch (InterruptedIOException e) {
logger.warn(
"Error reader thread was interrupted due to timeout.");
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
} }
@ -109,8 +132,11 @@ public class ProcessExecutor {
String line; String line;
while ((line = outputReader.readLine()) != null) { while ((line = outputReader.readLine()) != null) {
outputLines.add(line); outputLines.add(line);
if (liveUpdates) System.out.println(line); if (liveUpdates) logger.info(line);
} }
} catch (InterruptedIOException e) {
logger.warn(
"Error reader thread was interrupted due to timeout.");
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
} }
@ -120,8 +146,17 @@ public class ProcessExecutor {
outputReaderThread.start(); outputReaderThread.start();
// Wait for the conversion process to complete // Wait for the conversion process to complete
exitCode = process.waitFor(); boolean finished = process.waitFor(timeoutDuration, TimeUnit.MINUTES);
if (!finished) {
// Terminate the process
process.destroy();
// Interrupt the reader threads
errorReaderThread.interrupt();
outputReaderThread.interrupt();
throw new IOException("Process timeout exceeded.");
}
exitCode = process.exitValue();
// Wait for the reader threads to finish // Wait for the reader threads to finish
errorReaderThread.join(); errorReaderThread.join();
outputReaderThread.join(); outputReaderThread.join();
@ -130,13 +165,13 @@ public class ProcessExecutor {
if (outputLines.size() > 0) { if (outputLines.size() > 0) {
String outputMessage = String.join("\n", outputLines); String outputMessage = String.join("\n", outputLines);
messages += outputMessage; messages += outputMessage;
System.out.println("Command output:\n" + outputMessage); logger.info("Command output:\n" + outputMessage);
} }
if (errorLines.size() > 0) { if (errorLines.size() > 0) {
String errorMessage = String.join("\n", errorLines); String errorMessage = String.join("\n", errorLines);
messages += errorMessage; messages += errorMessage;
System.out.println("Command error output:\n" + errorMessage); logger.warn("Command error output:\n" + errorMessage);
if (exitCode != 0) { if (exitCode != 0) {
throw new IOException( throw new IOException(
"Command process failed with exit code " "Command process failed with exit code "

View file

@ -13,7 +13,9 @@ system:
defaultLocale: 'en-US' # Set the default language (e.g. 'de-DE', 'fr-FR', etc) defaultLocale: 'en-US' # Set the default language (e.g. 'de-DE', 'fr-FR', etc)
googlevisibility: false # 'true' to allow Google visibility (via robots.txt), 'false' to disallow googlevisibility: false # 'true' to allow Google visibility (via robots.txt), 'false' to disallow
enableAlphaFunctionality: false # Set to enable functionality which might need more testing before it fully goes live (This feature might make no changes) enableAlphaFunctionality: false # Set to enable functionality which might need more testing before it fully goes live (This feature might make no changes)
# customExternalPort: 8000 used for when port mappings do not work correctly customApplications:
installBookFormats: false # Installs Calibre for book format conversion (For non docker it must be manually downloaded but will need to be true to show in UI)
installAdvancedHtmlToPDF: false # DO NOT USE EXTERNALLY, NOT SAFE! Install wkHtmlToPDF (For non docker it must be manually downloaded but will need to be true to show in UI)
#ui: #ui:
# appName: exampleAppName # Application's visible name # appName: exampleAppName # Application's visible name