|
| 1 | +package fi.hsl.jore.importer.feature.batch.stop_registry; |
| 2 | + |
| 3 | +import java.io.BufferedReader; |
| 4 | +import java.io.File; |
| 5 | +import java.io.InputStreamReader; |
| 6 | +import java.nio.charset.StandardCharsets; |
| 7 | +import java.util.Map; |
| 8 | +import java.util.List; |
| 9 | +import java.util.concurrent.TimeUnit; |
| 10 | +import org.slf4j.Logger; |
| 11 | +import org.slf4j.LoggerFactory; |
| 12 | +import org.springframework.batch.core.scope.context.ChunkContext; |
| 13 | +import org.springframework.batch.core.step.StepContribution; |
| 14 | +import org.springframework.batch.core.step.tasklet.Tasklet; |
| 15 | +import org.springframework.batch.infrastructure.repeat.RepeatStatus; |
| 16 | + |
| 17 | +/** |
| 18 | + * Runs the external Python "stop-registry importer" script as a Spring Batch tasklet, streaming the script's |
| 19 | + * stdout/stderr line-by-line through SLF4J so that its output is intermixed with the Java application's normal logs. |
| 20 | + */ |
| 21 | +public class RunStopRegistryImporterTasklet implements Tasklet { |
| 22 | + |
| 23 | + private static final Logger LOG = LoggerFactory.getLogger("stop-registry-importer"); |
| 24 | + |
| 25 | + private final String pythonCommand; |
| 26 | + private final String scriptPath; |
| 27 | + private final String workingDir; |
| 28 | + private final long timeoutHours; |
| 29 | + |
| 30 | + public RunStopRegistryImporterTasklet( |
| 31 | + final String pythonCommand, |
| 32 | + final String scriptPath, |
| 33 | + final String workingDir, |
| 34 | + final long timeoutHours) { |
| 35 | + this.pythonCommand = pythonCommand; |
| 36 | + this.scriptPath = scriptPath; |
| 37 | + this.workingDir = workingDir; |
| 38 | + this.timeoutHours = timeoutHours; |
| 39 | + } |
| 40 | + |
| 41 | + @Override |
| 42 | + public RepeatStatus execute(final StepContribution contribution, final ChunkContext chunkContext) throws Exception { |
| 43 | + |
| 44 | + LOG.info("Starting stop-registry importer script: {} (cwd={}, command={})", scriptPath, workingDir, pythonCommand); |
| 45 | + |
| 46 | + final ProcessBuilder pb = new ProcessBuilder(List.of(pythonCommand, "-u", scriptPath)) |
| 47 | + .directory(new File(workingDir)) |
| 48 | + .redirectErrorStream(true); |
| 49 | + // Force unbuffered output from Python so logs appear in near real time. |
| 50 | + final Map<String, String> environment = pb.environment(); |
| 51 | + environment.put("PYTHONUNBUFFERED", "1"); |
| 52 | + environment.put("STOP_REGISTRY_IMPORTER_USE_DOTENV", "0"); |
| 53 | + |
| 54 | + final Process process = pb.start(); |
| 55 | + |
| 56 | + try (BufferedReader reader = |
| 57 | + new BufferedReader(new InputStreamReader(process.getInputStream(), StandardCharsets.UTF_8))) { |
| 58 | + String line; |
| 59 | + while ((line = reader.readLine()) != null) { |
| 60 | + LOG.info(line); |
| 61 | + } |
| 62 | + } |
| 63 | + |
| 64 | + final boolean finished = process.waitFor(timeoutHours, TimeUnit.HOURS); |
| 65 | + if (!finished) { |
| 66 | + process.destroyForcibly(); |
| 67 | + throw new IllegalStateException("Stop-registry importer timed out after " + timeoutHours + " hour(s)"); |
| 68 | + } |
| 69 | + final int exitCode = process.exitValue(); |
| 70 | + LOG.info("Stop-registry importer exited with code {}", exitCode); |
| 71 | + if (exitCode != 0) { |
| 72 | + throw new IllegalStateException("Stop-registry importer failed, exit code " + exitCode); |
| 73 | + } |
| 74 | + return RepeatStatus.FINISHED; |
| 75 | + } |
| 76 | +} |
0 commit comments