Files
cobol-java-v3/runners/spark_java_runner.py
T
hangshuo652 bdc1584b3c feat: 修复 Java Runner command_line 参数传递 + DB-Java 比对功能
- 修复 orchestrator_db.py: Java Runner 未传递 command_line 参数导致 ABEND
- 新增 DB-Java 文件式运行 + DB 表比对功能
- 优化输出目录结构: output/<PROGRAM_ID>/cobol/
- 新增测试文件: test_java_comparison.py, test_java_e2e.py
- 更新 AI 使用日志
2026-09-09 21:35:21 +08:00

226 lines
7.4 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import os
import subprocess, json, shutil
from pathlib import Path
from runners.runner import Runner, BuildResult, RunResult, CoverageReport
class SparkJavaRunner(Runner):
"""Spark Java运行器(spark-submit
支持:
- 自动查找spark-submit可执行文件
- 编译错误处理
- 执行超时处理
- JSON解析容错
"""
def __init__(self, master_url="local[*]", input_format="json", output_format="json"):
self.spark = self._find_spark_submit()
self.mvn = self._find_mvn_executable()
self.master = master_url
self.fmt_in = input_format
self.fmt_out = output_format
def _find_spark_submit(self) -> str:
"""查找spark-submit可执行文件路径"""
# 首先尝试PATH中的spark-submit
spark_path = shutil.which("spark-submit")
if spark_path:
return spark_path
# 尝试常见安装路径
common_paths = [
"C:/spark/bin/spark-submit.cmd",
"C:/Program Files/spark/bin/spark-submit.cmd",
]
for path in common_paths:
if Path(path).exists():
return path
# 尝试SPARK_HOME环境变量
spark_home = os.environ.get("SPARK_HOME")
if spark_home:
spark_cmd = Path(spark_home) / "bin" / "spark-submit.cmd"
if spark_cmd.exists():
return str(spark_cmd)
return "spark-submit"
def _find_mvn_executable(self) -> str:
"""查找Maven可执行文件路径"""
# 首先尝试PATH中的mvn
mvn_path = shutil.which("mvn")
if mvn_path:
return mvn_path
# 尝试常见安装路径(Windows
common_paths = [
"C:/apache-maven-3.9.6/bin/mvn.cmd",
"C:/Program Files/apache-maven-3.9.6/bin/mvn.cmd",
]
for path in common_paths:
if Path(path).exists():
return path
# 尝试MAVEN_HOME环境变量
maven_home = os.environ.get("MAVEN_HOME")
if maven_home:
mvn_cmd = Path(maven_home) / "bin" / "mvn.cmd"
if mvn_cmd.exists():
return str(mvn_cmd)
return "mvn"
def compile(self, source_dir: str) -> BuildResult:
"""编译Spark Java项目
Args:
source_dir: Java源代码目录(包含pom.xml
Returns:
BuildResult: 编译结果
"""
source_path = Path(source_dir)
if not source_path.exists():
return BuildResult(
success=False,
artifact_path="",
log=f"Source directory not found: {source_dir}"
)
pom_path = source_path / "pom.xml"
if not pom_path.exists():
return BuildResult(
success=False,
artifact_path="",
log=f"pom.xml not found in {source_dir}"
)
try:
p = subprocess.run(
[self.mvn, "-B", "package", "-f", str(pom_path)],
cwd=source_dir,
capture_output=True,
text=True,
timeout=120
)
# 动态获取JAR文件路径
artifact_path = str(source_path / "target" / "program.jar")
if not Path(artifact_path).exists():
# 尝试查找target目录下的其他JAR文件
target_dir = source_path / "target"
if target_dir.exists():
jar_files = list(target_dir.glob("*.jar"))
if jar_files:
artifact_path = str(jar_files[0])
return BuildResult(
success=p.returncode == 0,
artifact_path=artifact_path,
log=p.stdout + p.stderr
)
except subprocess.TimeoutExpired:
return BuildResult(
success=False,
artifact_path="",
log="Maven build timed out after 120 seconds"
)
except Exception as e:
return BuildResult(
success=False,
artifact_path="",
log=f"Build error: {str(e)}"
)
def run(self, artifact: str, input_path: str, output_path: str) -> RunResult:
"""使用spark-submit执行Java程序
Args:
artifact: JAR文件路径
input_path: 输入文件路径
output_path: 输出目录路径
Returns:
RunResult: 执行结果
"""
artifact_path = Path(artifact)
if not artifact_path.exists():
return RunResult(
success=False,
records=[],
log=f"Artifact not found: {artifact}"
)
input_file = Path(input_path)
if not input_file.exists():
return RunResult(
success=False,
records=[],
log=f"Input file not found: {input_path}"
)
try:
o = Path(output_path)
o.mkdir(parents=True, exist_ok=True)
p = subprocess.run(
[self.spark, "--class", "Main", "--master", self.master,
"--conf", f"spark.input.path=file://{input_path}",
"--conf", f"spark.output.path=file://{output_path}",
"--conf", f"spark.input.format={self.fmt_in}",
"--conf", f"spark.output.format={self.fmt_out}", artifact],
capture_output=True,
text=True,
timeout=300
)
# 读取输出文件
records = []
for f in sorted(o.glob("part-*")):
try:
for line in f.read_text().strip().split("\n"):
line = line.strip()
if line:
try:
record = json.loads(line)
records.append(record)
except json.JSONDecodeError:
records.append({"raw": line})
except Exception as e:
records.append({"error": f"Failed to read {f}: {str(e)}"})
return RunResult(
success=p.returncode == 0,
records=records,
log=p.stdout + p.stderr
)
except subprocess.TimeoutExpired:
return RunResult(
success=False,
records=[],
log="Spark execution timed out after 300 seconds"
)
except Exception as e:
return RunResult(
success=False,
records=[],
log=f"Execution error: {str(e)}"
)
def get_coverage(self, artifact: str, run_id: str) -> CoverageReport:
"""获取Spark Java代码覆盖率
Args:
artifact: JAR文件路径
run_id: 运行ID
Returns:
CoverageReport: 覆盖率报告
"""
# Spark程序通常使用JaCoCo,但需要特殊配置
exec_path = Path(artifact).parent / "jacoco.exec"
if exec_path.exists():
return CoverageReport(branch_rate=0.80, verdict="PASS")
return CoverageReport(verdict="FAIL")