5151 JAVA_SDK_EXAMPLE_LIBS_PATH ,
5252 JAVA_SDK_MAVEN_CACHE_PATH ,
5353 JAVA_SDK_ROOT_PATH ,
54+ JAVA_TEST_BUNDLE_DAGS_PATH ,
55+ JAVA_TEST_BUNDLE_LIBS_PATH ,
56+ JAVA_TEST_BUNDLE_ROOT_PATH ,
5457 KAFKA_DIR_PATH ,
5558 LANG_SDK_NATIVE_TOOLCHAIN ,
5659 LOCALSTACK_PATH ,
@@ -376,25 +379,30 @@ def _setup_java_sdk_integration(dot_env_file, tmp_dir):
376379 console .print ("[yellow]Publishing Java SDK artifacts to local Maven repository..." )
377380 _run_java_sdk_gradle (JAVA_SDK_ROOT_PATH , "publishToMavenLocal" , "-PskipSigning=true" , native = native )
378381
379- # The example and scala_spark_example are independent Gradle builds that both
380- # consume the SDK artifact published above, so build them concurrently. Sharing
381- # a writable Gradle user home between concurrent builds is safe because each
382- # build can ping the other's lock-owner port over one shared loopback - the
383- # host's own in native mode, --network=host in the container path (see the
384- # helper's docstring); publishToMavenLocal has already unpacked the shared
385- # wrapper distribution, so neither build races to fetch it.
382+ # The example, scala_spark_example, and java-test-bundle are independent
383+ # Gradle builds that all consume the SDK artifact published above, so build
384+ # them concurrently. Sharing a writable Gradle user home between concurrent
385+ # builds is safe because each build can ping the other's lock-owner port over
386+ # one shared loopback - the host's own in native mode, --network=host in the
387+ # container path (see the helper's docstring); publishToMavenLocal has
388+ # already unpacked the shared wrapper distribution, so no build races to
389+ # fetch it.
386390 #
387391 # The Gradle `bundle` task is a Copy that never prunes its destination, so
388392 # JARs from an earlier build linger. A stale dependency JAR with its own
389393 # Main-Class would make JavaCoordinator's Main-Class discovery ambiguous, so
390394 # start each bundle from an empty directory.
391395 rmtree (JAVA_SDK_EXAMPLE_LIBS_PATH , ignore_errors = True )
392396 rmtree (SCALA_SPARK_EXAMPLE_LIBS_PATH , ignore_errors = True )
397+ rmtree (JAVA_TEST_BUNDLE_LIBS_PATH , ignore_errors = True )
393398 toolchain = "host toolchain" if native else "eclipse-temurin:17-jdk"
394- console .print (f"[yellow]Building Java SDK and Scala Spark example bundles concurrently ({ toolchain } )..." )
399+ console .print (
400+ f"[yellow]Building Java SDK, Scala Spark, and test-fixture bundles concurrently ({ toolchain } )..."
401+ )
395402 example_bundle_workdirs = [
396403 JAVA_SDK_ROOT_PATH / "example" ,
397404 JAVA_SDK_ROOT_PATH / "scala_spark_example" ,
405+ JAVA_TEST_BUNDLE_ROOT_PATH ,
398406 ]
399407 with ThreadPoolExecutor (max_workers = len (example_bundle_workdirs )) as pool :
400408 bundle_builds = [
@@ -411,19 +419,21 @@ def _setup_java_sdk_integration(dot_env_file, tmp_dir):
411419 # expose them to the worker, and each JavaCoordinator globs its own dir.
412420 copytree (JAVA_SDK_EXAMPLE_LIBS_PATH , tmp_dir / "java-jars" )
413421 copytree (SCALA_SPARK_EXAMPLE_LIBS_PATH , tmp_dir / "scala-jars" )
422+ copytree (JAVA_TEST_BUNDLE_LIBS_PATH , tmp_dir / "java-test-jars" )
414423
415424 # Copy the Java SDK example Dag files so Airflow can discover them.
416425 copyfile (JAVA_SDK_EXAMPLE_DAGS_PATH / "java_examples.py" , tmp_dir / "dags" / "java_examples.py" )
417426 copyfile (
418427 SCALA_SPARK_EXAMPLE_DAGS_PATH / "scala_spark_examples.py" ,
419428 tmp_dir / "dags" / "scala_spark_examples.py" ,
420429 )
430+ copyfile (JAVA_TEST_BUNDLE_DAGS_PATH / "java_test_dags.py" , tmp_dir / "dags" / "java_test_dags.py" )
421431
422432 # Keep the bundle JARs out of the build context: Dockerfile.java only adds a
423433 # JRE and copies nothing from the context, so without this docker build would
424434 # tar and stream the bundles (hundreds of MB of Spark JARs) to the daemon for
425435 # nothing. The JARs reach the worker via the compose bind-mounts, not the image.
426- (tmp_dir / ".dockerignore" ).write_text ("java-jars/\n scala-jars/\n " )
436+ (tmp_dir / ".dockerignore" ).write_text ("java-jars/\n scala-jars/\n java-test-jars/ \ n " )
427437
428438 # Build a local Docker image that extends DOCKER_IMAGE with a JRE.
429439 # We do this explicitly so testcontainers' DockerCompose.start() does not
@@ -445,10 +455,11 @@ def _setup_java_sdk_integration(dot_env_file, tmp_dir):
445455 check = True ,
446456 )
447457
448- # Two JavaCoordinators on the same worker image, one bundle per queue. The
449- # scala-jdk entry pins main_class (Spark's large classpath makes Main-Class
450- # discovery ambiguous) and carries Spark's Java 17 module openings, a small
451- # driver heap, and a longer startup timeout for its large dependency classpath.
458+ # One JavaCoordinator per queue on the same worker image, each serving its
459+ # own bundle. The scala-jdk entry pins main_class (Spark's large classpath
460+ # makes Main-Class discovery ambiguous) and carries Spark's Java 17 module
461+ # openings, a small driver heap, and a longer startup timeout for its large
462+ # dependency classpath.
452463 coordinator_config = json .dumps (
453464 {
454465 "java-jdk" : {
@@ -464,9 +475,15 @@ def _setup_java_sdk_integration(dot_env_file, tmp_dir):
464475 "task_startup_timeout" : 60.0 ,
465476 },
466477 },
478+ "java-test-jdk" : {
479+ "classpath" : "airflow.sdk.coordinators.java.JavaCoordinator" ,
480+ "kwargs" : {"jars_root" : ["/opt/airflow/java-test-jars" ]},
481+ },
467482 }
468483 )
469- queue_to_coordinator = json .dumps ({"java" : "java-jdk" , "scala" : "scala-jdk" })
484+ queue_to_coordinator = json .dumps (
485+ {"java" : "java-jdk" , "scala" : "scala-jdk" , "java-test" : "java-test-jdk" }
486+ )
470487
471488 # Connection expected by the Java example bundle tasks. The JSON form
472489 # covers all connection fields, in particular the port: wire integers
0 commit comments