Fix: Blockiere System.exit() mit SecurityManager statt s9api

Problem: s9api Klassen nicht im Classpath verfügbar (NoClassDefFoundError)
Root Cause: Saxon's Transform.main() ruft System.exit() auf

Lösung: Custom SecurityManager der System.exit() blockiert
- NoExitSecurityManager: checkExit() wirft SecurityException
- Fängt SecurityException ab wenn Saxon System.exit() versucht
- Extrahiert Fehlermeldung aus Saxon's stderr
- Worker bleibt am Leben und kann weitere Jobs verarbeiten

Dieser Ansatz funktioniert mit jeder Saxon-Version ohne s9api-Abhängigkeiten.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
2025-12-28 16:04:23 +01:00
parent 6fcf706d96
commit 40b778b41b
+77 -46
View File
@@ -17,20 +17,33 @@ logger = logging.getLogger(__name__)
# Java-Worker-Code (wird zur Laufzeit kompiliert)
SAXON_WORKER_JAVA = """
import net.sf.saxon.s9api.*;
import javax.xml.transform.stream.StreamSource;
import net.sf.saxon.Transform;
import java.io.*;
import java.util.*;
public class SaxonWorker {
// Custom SecurityManager to block System.exit()
static class NoExitSecurityManager extends SecurityManager {
@Override
public void checkPermission(java.security.Permission perm) {
// Allow everything
}
@Override
public void checkExit(int status) {
// Block System.exit() by throwing an exception
throw new SecurityException("System.exit() blocked by SaxonWorker");
}
}
public static void main(String[] args) {
// Install SecurityManager to prevent System.exit()
System.setSecurityManager(new NoExitSecurityManager());
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
String line;
// Create Saxon Processor (reusable)
Processor processor = new Processor(false);
System.err.println("SaxonWorker started and ready (using s9api)");
System.err.println("SaxonWorker started and ready (System.exit blocked)");
System.err.flush();
try {
@@ -62,67 +75,85 @@ public class SaxonWorker {
String xslStylesheet = parts[1];
String outputFo = parts[2];
System.err.println("DEBUG: Files - XML: " + sourceXml);
System.err.println("DEBUG: Files - XSL: " + xslStylesheet);
System.err.println("DEBUG: Files - OUT: " + outputFo);
System.err.println("DEBUG: Building Saxon args...");
System.err.flush();
// Parse parameters if present
Map<String, String> xsltParams = new HashMap<>();
// Build Saxon arguments
List<String> saxonArgs = new ArrayList<>();
saxonArgs.add("-s:" + sourceXml);
saxonArgs.add("-xsl:" + xslStylesheet);
saxonArgs.add("-o:" + outputFo);
// Add parameters if present
if (parts.length > 3 && !parts[3].isEmpty()) {
String[] params = parts[3].split("\\\\|\\\\|\\\\|");
for (String param : params) {
if (!param.isEmpty() && param.contains("=")) {
String[] kv = param.split("=", 2);
xsltParams.put(kv[0], kv[1]);
if (!param.isEmpty()) {
saxonArgs.add(param);
}
}
}
System.err.println("DEBUG: Parameters: " + xsltParams.size());
System.err.println("DEBUG: Running Saxon transformation...");
System.err.flush();
System.err.println("DEBUG: Compiling stylesheet...");
System.err.flush();
// Redirect Saxon output to stderr to avoid polluting stdout
PrintStream oldOut = System.out;
PrintStream oldErr = System.err;
ByteArrayOutputStream saxonOut = new ByteArrayOutputStream();
ByteArrayOutputStream saxonErr = new ByteArrayOutputStream();
// Compile stylesheet (s9api)
XsltCompiler compiler = processor.newXsltCompiler();
XsltExecutable executable = compiler.compile(new StreamSource(new File(xslStylesheet)));
try {
System.setOut(new PrintStream(saxonOut));
System.setErr(new PrintStream(saxonErr));
System.err.println("DEBUG: Creating transformer...");
System.err.flush();
// Run Saxon transformation
// If Saxon calls System.exit(), our SecurityManager will throw SecurityException
Transform.main(saxonArgs.toArray(new String[0]));
// Create transformer
Xslt30Transformer transformer = executable.load30();
// Restore streams
System.setOut(oldOut);
System.setErr(oldErr);
// Set parameters
if (!xsltParams.isEmpty()) {
Map<QName, XdmValue> params = new HashMap<>();
for (Map.Entry<String, String> param : xsltParams.entrySet()) {
params.put(new QName(param.getKey()), XdmValue.makeValue(param.getValue()));
}
transformer.setStylesheetParameters(params);
}
System.err.println("DEBUG: Running transformation...");
System.err.flush();
// Run transformation
Serializer serializer = processor.newSerializer(new File(outputFo));
transformer.transform(new StreamSource(new File(sourceXml)), serializer);
System.err.println("DEBUG: Transformation completed successfully");
System.err.flush();
oldErr.println("DEBUG: Saxon transformation completed");
oldErr.flush();
// Send success response
System.out.println("OK");
System.out.flush();
} catch (SaxonApiException e) {
System.err.println("DEBUG: Saxon API exception: " + e.getClass().getName());
System.err.flush();
e.printStackTrace(System.err);
} catch (SecurityException e) {
// System.exit() was called by Saxon - treat as error
System.setOut(oldOut);
System.setErr(oldErr);
oldErr.println("DEBUG: Saxon tried to call System.exit() (blocked)");
oldErr.flush();
// Check Saxon's output for error details
String saxonOutput = saxonErr.toString();
String errorMsg = "Transformation failed";
if (!saxonOutput.isEmpty()) {
errorMsg = saxonOutput.trim();
// Limit error message length
if (errorMsg.length() > 200) {
errorMsg = errorMsg.substring(0, 200) + "...";
}
}
System.out.println("ERROR: " + errorMsg);
System.out.flush();
} catch (Exception e) {
System.setOut(oldOut);
System.setErr(oldErr);
oldErr.println("DEBUG: Saxon exception: " + e.getClass().getName());
oldErr.flush();
e.printStackTrace(oldErr);
System.out.println("ERROR: " + (e.getMessage() != null ? e.getMessage() : e.getClass().getName()));
System.out.flush();
}
} catch (Exception e) {
System.err.println("DEBUG: Job processing exception: " + e.getClass().getName());
System.err.flush();