diff --git a/.github/scripts/ci-policy.py b/.github/scripts/ci-policy.py
index 193efcc41..46e6b60c0 100644
--- a/.github/scripts/ci-policy.py
+++ b/.github/scripts/ci-policy.py
@@ -279,14 +279,38 @@ def require_current_pr(plan, fetch):
or live["head"]["ref"] != plan["branch"]
or live["base"]["repo"]["full_name"] != plan["repository"]):
raise StaleInputError("PR inputs changed; refresh the branch and start a new PR run")
- if "stack" in plan:
- # Native stacks can leave REST base.sha at an older snapshot. The actual
- # branch ref, rather than that snapshot, identifies the tested base.
+ if "baseBranch" in plan:
+ # Event and REST base.sha can be older snapshots. The actual branch
+ # ref, rather than those snapshots, identifies the tested base.
ref = fetch(f"repos/{plan['repository']}/git/ref/heads/{quote(plan['baseBranch'], safe='')}")
- if (live.get("stack") != plan["stack"] or live["base"].get("ref") != plan["baseBranch"]
+ if (live.get("stack") != plan.get("stack") or live["base"].get("ref") != plan["baseBranch"]
or ref.get("object", {}).get("sha") != plan["base"]
or live.get("merge_commit_sha") != plan["testedMergeSHA"]):
- raise StaleInputError("PR stack or canonical base changed; start a new PR run")
+ raise StaleInputError("PR merge or canonical base changed; start a new PR run")
+
+
+def record_current_merge(plan, event_pr, parents, fetch):
+ """Recover an older event base only from GitHub's current merge identity."""
+ try:
+ live = fetch(f"repos/{plan['repository']}/pulls/{plan['pr']}")
+ if live.get("stack") is not None:
+ record_stack_merge(plan, event_pr, parents, fetch)
+ return
+ branch = live["base"]["ref"]
+ if not branch or event_pr["base"]["ref"] != branch:
+ raise StaleInputError("PR target branch changed; start a new PR run")
+ ref = fetch(f"repos/{plan['repository']}/git/ref/heads/{quote(branch, safe='')}")
+ canonical = ref["object"]["sha"]
+ if (event_pr.get("stack") is not None
+ or ref["object"].get("type") != "commit" or not canonical
+ or live.get("merge_commit_sha") != plan["testedMergeSHA"]
+ or parents != [canonical, plan["head"]]
+ or git("merge-base", plan["base"], canonical) != plan["base"]):
+ raise StaleInputError("checkout lacks a valid current PR merge identity")
+ plan.update(base=canonical, baseBranch=branch)
+ require_current_pr(plan, fetch)
+ except (ValueError, KeyError, TypeError, AttributeError, subprocess.SubprocessError, OSError) as error:
+ raise StaleInputError("current PR merge identity could not be verified") from error
def record_stack_merge(plan, event_pr, parents, fetch):
@@ -343,7 +367,7 @@ def create_plan(project, event, repository, fetch=api, external_inputs=None):
raise StaleInputError("PR event has an empty input identity")
parents = git("show", "-s", "--format=%P", plan["testedMergeSHA"]).split()
if parents != [plan["base"], plan["head"]]:
- record_stack_merge(plan, pr, parents, fetch)
+ record_current_merge(plan, pr, parents, fetch)
else:
require_current_pr(plan, fetch)
# head/base objects must exist locally; workflow fetches both before planning.
@@ -424,8 +448,10 @@ def write_summary(plan, results=None):
def result_report(plan, results):
report = {key: plan[key] for key in ["schema", "repository", "project", "pr", "source", "branch", "base",
"head", "testedMergeSHA", "externalInputs"]}
+ if "baseBranch" in plan:
+ report["baseBranch"] = plan["baseBranch"]
if "stack" in plan:
- report.update(baseBranch=plan["baseBranch"], stack=plan["stack"])
+ report["stack"] = plan["stack"]
report.update(runID=int(os.environ.get("GITHUB_RUN_ID", "0")),
runAttempt=int(os.environ.get("GITHUB_RUN_ATTEMPT", "1")),
executed=plan["expected"], results=results)
diff --git a/.github/scripts/test_candidate_sdk.py b/.github/scripts/test_candidate_sdk.py
index 1621defeb..8d93d298c 100644
--- a/.github/scripts/test_candidate_sdk.py
+++ b/.github/scripts/test_candidate_sdk.py
@@ -105,6 +105,53 @@ def run(*arguments):
for path, original in original_poms.items():
self.assertEqual(original, path.read_bytes())
+ @unittest.skipUnless(os.environ.get("CANDIDATE_SDK_MAVEN_TEST"),
+ "Set CANDIDATE_SDK_MAVEN_TEST for the consumer-version regression")
+ def test_maven_consumers_resolve_the_selected_sdk_version(self):
+ # A floating SDK changes coordinates; the consumer's fixed default must
+ # not silently resolve a different release from another repository.
+ import zipfile
+ with tempfile.TemporaryDirectory() as temporary:
+ root = Path(temporary)
+ group = "org.example.candidatesdk.g" + uuid.uuid4().hex
+ repository = Path.home() / ".m2/repository" / group.replace(".", "/")
+ self.addCleanup(shutil.rmtree, repository, True)
+ artifact = repository / "sdk/1.8.0"
+ artifact.mkdir(parents=True)
+ header = '4.0.0'
+ (artifact / "sdk-1.8.0.pom").write_text(
+ header + '' + group + 'sdk'
+ '1.8.0')
+ with zipfile.ZipFile(artifact / "sdk-1.8.0.jar", "w") as jar:
+ jar.writestr("sdk-source.txt", "selected upstream revision")
+ (artifact / "_remote.repositories").write_text("sdk-1.8.0.pom>=\nsdk-1.8.0.jar>=\n")
+ pom = root / "pom.xml"
+ pom.write_text(header + 'org.example.consumerconsumer'
+ '11.7.0'
+ '' + group + ''
+ 'sdk${hugegraph.version}'
+ '')
+ settings = root / "settings.xml"
+ settings.write_text("")
+ command = [os.environ["CANDIDATE_SDK_MAVEN_TEST"], "-o", "-ntp", "--settings", str(settings),
+ "-f", str(pom), "org.apache.maven.plugins:maven-dependency-plugin:3.1.1:build-classpath",
+ "-Dmdep.outputFile=" + str(root / "classpath")]
+ env = dict(os.environ, MAVEN_ARGS="-Dhugegraph.version=1.8.0")
+ # Warm only Maven's public plugin dependencies when this opt-in
+ # regression runs on a fresh machine; coordinate checks stay offline.
+ prepared = subprocess.run([arg for arg in command if arg != "-o"], env=env,
+ capture_output=True, text=True, timeout=90)
+ self.assertEqual(0, prepared.returncode, prepared.stdout + prepared.stderr)
+ (root / "classpath").unlink()
+ env["MAVEN_ARGS"] = ""
+ wrong = subprocess.run(command, env=env, capture_output=True, text=True, timeout=90)
+ self.assertNotEqual(0, wrong.returncode, wrong.stdout + wrong.stderr)
+ self.assertIn(group + ":sdk:jar:1.7.0", wrong.stdout + wrong.stderr)
+ env["MAVEN_ARGS"] = "-Dhugegraph.version=1.8.0"
+ selected = subprocess.run(command, env=env, capture_output=True, text=True, timeout=90)
+ self.assertEqual(0, selected.returncode, selected.stdout + selected.stderr)
+ self.assertEqual(str(artifact / "sdk-1.8.0.jar"), (root / "classpath").read_text().strip())
+
class CandidateDistributionTest(unittest.TestCase):
def setUp(self):
@@ -113,7 +160,7 @@ def setUp(self):
context.start()
self.addCleanup(context.stop)
- def fixture(self, root):
+ def fixture(self, root, revision="1.7.0"):
repository = root / "m2"
artifacts, modules = [], []
for source_pom in sorted(sdk.REQUIRED_MODULES):
@@ -121,8 +168,8 @@ def fixture(self, root):
packaging = "pom" if (source_pom.count("/") == 1 and artifact != "hugegraph-struct") or source_pom == "pom.xml" else "jar"
files = []
for extension in (["pom", "jar"] if packaging == "jar" else ["pom"]):
- filename = artifact + "-1.7.0." + extension
- path = repository / "org/apache/hugegraph" / artifact / "1.7.0" / filename
+ filename = artifact + "-" + revision + "." + extension
+ path = repository / "org/apache/hugegraph" / artifact / revision / filename
path.parent.mkdir(parents=True, exist_ok=True)
path.write_bytes(filename.encode())
with (path.parent / "_remote.repositories").open("a") as origins:
@@ -131,14 +178,56 @@ def fixture(self, root):
files.append(relative)
artifacts.append({"path": relative, "sha256": sdk.digest(path), "source_reactor_install": True})
modules.append({"group_id": "org.apache.hugegraph", "artifact_id": artifact,
- "version": "1.7.0", "packaging": packaging,
+ "version": revision, "packaging": packaging,
"source_pom": source_pom, "files": files})
manifest = {"repository": sdk.REPOSITORY, "commit": self.source_commit,
- "source_revision": "1.7.0", "java_version": "17",
+ "source_revision": revision, "java_version": "17",
"required_sdk_modules": modules, "artifacts": artifacts}
self.write_manifest(repository, manifest)
return repository, manifest
+ def test_floating_sdk_version_preserves_verified_coordinates_and_maven_arguments(self):
+ with tempfile.TemporaryDirectory() as temporary:
+ root = Path(temporary)
+ repository, manifest = self.fixture(root, "1.8.0")
+ environment = root / "github-env"
+ # Fixed-baseline validation must still reject the newer revision.
+ with self.assertRaises(RuntimeError):
+ sdk.validate_sdk(repository)
+ original = "--settings settings.xml -Dmaven.repo.local=" + str(repository)
+ with patch.dict(os.environ, {"MAVEN_ARGS": original}):
+ sdk.configure_upstream(repository, environment)
+ lines = dict(line.split("=", 1) for line in environment.read_text().splitlines())
+ self.assertEqual("1.8.0", lines["CANDIDATE_SDK_VERSION"])
+ self.assertEqual(original + " -Dhugegraph.version=1.8.0", lines["MAVEN_ARGS"])
+ with patch.dict(os.environ, lines):
+ sdk.validate_sdk(repository)
+ distribution = self.distribution(root, repository, manifest)
+ sdk.validate_distribution(repository, distribution, "loader")
+ # Hash checks must remain enforced for the floating version.
+ jar = repository / next(a["path"] for a in manifest["artifacts"]
+ if a["path"].endswith(".jar"))
+ jar.write_bytes(b"changed SDK")
+ with self.assertRaises(RuntimeError):
+ sdk.validate_sdk(repository)
+ with self.assertRaises(RuntimeError):
+ sdk.configure_upstream(repository, root / "invalid-env")
+ self.assertFalse((root / "invalid-env").exists())
+
+ def test_configuration_and_distribution_cli_options_are_separate_steps(self):
+ with tempfile.TemporaryDirectory() as temporary:
+ root = Path(temporary)
+ repository, manifest = self.fixture(root, "1.8.0")
+ distribution = self.distribution(root, repository, manifest)
+ environment = root / "github-env"
+ result = subprocess.run(["python3", sdk.__file__, str(repository),
+ "--configure-upstream-env", str(environment),
+ "--distribution", str(distribution), "--module", "loader"],
+ capture_output=True, text=True, timeout=10)
+ self.assertEqual(2, result.returncode)
+ self.assertIn("cannot be combined", result.stderr)
+ self.assertFalse(environment.exists())
+
def write_manifest(self, repository, manifest):
(repository / "candidate-sdk-manifest.json").write_text(json.dumps(manifest))
diff --git a/.github/scripts/test_ci_policy.py b/.github/scripts/test_ci_policy.py
index 77e22fd97..2f722f6bc 100644
--- a/.github/scripts/test_ci_policy.py
+++ b/.github/scripts/test_ci_policy.py
@@ -421,6 +421,89 @@ def git(*args):
finally:
os.chdir(old)
+ def test_regular_pr_recovers_stale_event_base_with_real_git(self):
+ with tempfile.TemporaryDirectory() as directory:
+ root = Path(directory)
+ def git(*args):
+ return subprocess.check_output(["git", *args], cwd=root, text=True).strip()
+ git("init", "-q")
+ git("config", "user.email", "ci@example.invalid")
+ git("config", "user.name", "CI")
+ (root / "README.md").write_text("base")
+ (root / "hugegraph-hubble").mkdir()
+ (root / "hugegraph-hubble/README.md").write_text("packaged docs")
+ git("add", ".")
+ git("commit", "-qm", "base")
+ anchor = git("rev-parse", "HEAD")
+ git("checkout", "-qb", "feature")
+ (root / "hugegraph-loader").mkdir()
+ (root / "hugegraph-loader/A.java").write_text("source")
+ git("add", ".")
+ git("commit", "-qm", "feature")
+ head = git("rev-parse", "HEAD")
+ old_merge = git("commit-tree", git("rev-parse", "HEAD^{tree}"),
+ "-p", anchor, "-p", head, "-m", "old merge")
+ git("checkout", "-qb", "target", anchor)
+ (root / "README.md").write_text("advanced master")
+ git("commit", "-qam", "advance target")
+ canonical = git("rev-parse", "HEAD")
+ git("merge", "--no-ff", "-qm", "current merge", "feature")
+ merge = git("rev-parse", "HEAD")
+ live = self.live_pr()
+ live["head"]["sha"] = head
+ live["base"].update(sha=anchor, ref="target")
+ event = {"pull_request": json.loads(json.dumps(dict(live, number=7)))}
+ live.update(merge_commit_sha=merge)
+ ref = {"object": {"type": "commit", "sha": canonical}}
+ def fetch(path):
+ if path == "repos/apache/t/pulls/7":
+ return live
+ self.assertEqual("repos/apache/t/git/ref/heads/target", path)
+ return ref
+ old = os.getcwd()
+ try:
+ os.chdir(root)
+ # Neither event nor REST snapshot has stack metadata. The real
+ # checkout merges the current canonical target with the same head.
+ plan = policy.create_plan("toolchain", event, "apache/t", fetch)
+ self.assertEqual(canonical, plan["base"])
+ self.assertEqual("target", plan["baseBranch"])
+ self.assertNotIn("stack", plan)
+ self.assertEqual(["hubble", "loader"], plan["selected"])
+ results = {suite: {"result": "success"} for suite in plan["expected"]}
+ results.update(plan={"result": "success"}, fixture={"result": "success"},
+ **{"hubble-fixture": {"result": "success"}})
+ report = policy.gate(plan, results, fetch)
+ self.assertEqual(canonical, report["base"])
+ self.assertEqual("target", report["baseBranch"])
+ for key, value in [("merge_commit_sha", old_merge),
+ ("state", "closed"),
+ ("head", dict(live["head"], sha=anchor)),
+ ("base", dict(live["base"], ref="other"))]:
+ original = live[key]
+ live[key] = value
+ with self.subTest(field=key), self.assertRaises(policy.StaleInputError):
+ policy.create_plan("toolchain", event, "apache/t", fetch)
+ with self.subTest(gate=key), self.assertRaises(policy.StaleInputError):
+ policy.gate(plan, results, fetch)
+ live[key] = original
+ ref["object"]["sha"] = anchor
+ with self.assertRaises(policy.StaleInputError):
+ policy.create_plan("toolchain", event, "apache/t", fetch)
+ with self.assertRaises(policy.StaleInputError):
+ policy.gate(plan, results, fetch)
+ ref["object"]["sha"] = canonical
+ # A rerun of an old checkout cannot claim the fresh merge.
+ git("checkout", "--detach", "-q", old_merge)
+ with self.assertRaises(policy.StaleInputError):
+ policy.gate(plan, results, lambda _: dict(live, head=dict(live["head"], sha=anchor)))
+ stale_event = json.loads(json.dumps(event))
+ stale_event["pull_request"]["base"]["sha"] = canonical
+ with self.assertRaises(policy.StaleInputError):
+ policy.create_plan("toolchain", stale_event, "apache/t", fetch)
+ finally:
+ os.chdir(old)
+
def test_native_stack_canonical_base_and_gate_freshness(self):
# Reproduce GitHub native stacks: REST base.sha can remain at the old
# head, while the merge's first parent wraps the actual branch head.
diff --git a/.github/scripts/verify_candidate_image_sdk.py b/.github/scripts/verify_candidate_image_sdk.py
index 291ae46d6..aa7cb52ee 100644
--- a/.github/scripts/verify_candidate_image_sdk.py
+++ b/.github/scripts/verify_candidate_image_sdk.py
@@ -61,15 +61,15 @@ def source_commit():
return expected
-def validate_sdk(repository):
+def validate_sdk(repository, expected_revision=None):
try:
- return _validate_sdk(Path(repository))
+ return _validate_sdk(Path(repository), expected_revision)
except (OSError, ValueError, KeyError, TypeError, AttributeError) as error:
raise RuntimeError("Invalid candidate SDK manifest or artifact metadata (" +
type(error).__name__ + ")") from None
-def _validate_sdk(repository):
+def _validate_sdk(repository, expected_revision=None):
repository = Path(repository)
manifest = json.loads((repository / "candidate-sdk-manifest.json").read_text())
if (manifest["repository"], manifest["commit"]) != (REPOSITORY, source_commit()):
@@ -77,7 +77,10 @@ def _validate_sdk(repository):
java_version = manifest["java_version"]
if not isinstance(java_version, str) or not java_version.isdecimal():
raise ValueError("Invalid Java version format")
- if manifest["source_revision"] != "1.7.0" or java_version != "17":
+ revision = expected_revision or os.environ.get("CANDIDATE_SDK_VERSION", "1.7.0")
+ if not isinstance(revision, str) or not re.fullmatch(r"[A-Za-z0-9][A-Za-z0-9_.+-]*", revision):
+ raise ValueError("Invalid candidate SDK version")
+ if manifest["source_revision"] != revision or java_version != "17":
raise RuntimeError("SDK revision or Java version does not match the candidate contract")
modules = manifest["required_sdk_modules"]
if len(modules) != len(REQUIRED_MODULES) or {m["source_pom"] for m in modules} != REQUIRED_MODULES:
@@ -87,7 +90,7 @@ def _validate_sdk(repository):
raise RuntimeError("Duplicate SDK artifact paths")
required_files = set()
for module in modules:
- if module["group_id"] != "org.apache.hugegraph" or module["version"] != "1.7.0":
+ if module["group_id"] != "org.apache.hugegraph" or module["version"] != revision:
raise RuntimeError("Unexpected required SDK coordinates")
artifact = module["artifact_id"]
source_pom = module["source_pom"]
@@ -97,7 +100,7 @@ def _validate_sdk(repository):
if artifact != expected_artifact or module["packaging"] != ("pom" if parent else "jar"):
raise RuntimeError("Required SDK module does not match its source coordinates")
extensions = {"pom", "jar"} if module["packaging"] == "jar" else {"pom"}
- expected = {f"org/apache/hugegraph/{artifact}/1.7.0/{artifact}-1.7.0.{ext}"
+ expected = {f"org/apache/hugegraph/{artifact}/{revision}/{artifact}-{revision}.{ext}"
for ext in extensions}
if set(module["files"]) != expected:
raise RuntimeError("Required SDK module has missing or unexpected files")
@@ -160,16 +163,33 @@ def validate_distribution(repository, directory, module):
return actual
+def configure_upstream(repository, environment):
+ """Select consumer coordinates only after verifying this run's SDK artifacts."""
+ repository = Path(repository)
+ revision = json.loads((repository / "candidate-sdk-manifest.json").read_text())["source_revision"]
+ manifest, jars = validate_sdk(repository, revision)
+ with Path(environment).open("a") as stream:
+ stream.write(f"CANDIDATE_SDK_VERSION={revision}\n")
+ stream.write(f"MAVEN_ARGS={os.environ.get('MAVEN_ARGS', '')} -Dhugegraph.version={revision}\n")
+ return manifest, jars
+
+
if __name__ == "__main__":
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("repository", type=Path)
parser.add_argument("--distribution", type=Path)
+ parser.add_argument("--configure-upstream-env", type=Path)
parser.add_argument("--module", choices=("loader", "tools", "hubble"))
args = parser.parse_args()
if bool(args.distribution) != bool(args.module):
parser.error("--distribution and --module must be used together")
+ if args.configure_upstream_env and args.distribution:
+ parser.error("--configure-upstream-env cannot be combined with --distribution")
try:
- manifest, jars = validate_sdk(args.repository)
+ if args.configure_upstream_env:
+ manifest, jars = configure_upstream(args.repository, args.configure_upstream_env)
+ else:
+ manifest, jars = validate_sdk(args.repository)
if args.distribution:
validate_distribution(args.repository, args.distribution, args.module)
except RuntimeError as error:
diff --git a/.github/workflows/flink-cdc-ci.yml b/.github/workflows/flink-cdc-ci.yml
new file mode 100644
index 000000000..244cdf48b
--- /dev/null
+++ b/.github/workflows/flink-cdc-ci.yml
@@ -0,0 +1,110 @@
+name: "flink-cdc-java17-ci"
+
+on:
+ workflow_dispatch:
+ pull_request:
+ paths:
+ - hugegraph-loader/**
+ - hugegraph-client/**
+ - pom.xml
+ - .github/workflows/flink-cdc-ci.yml
+ - .github/actions/get-hugegraph-commit/**
+ - .github/actions/setup-candidate-sdk/**
+ push:
+ branches: [master, 'release-*']
+ paths:
+ - hugegraph-loader/**
+ - hugegraph-client/**
+ - pom.xml
+ - .github/workflows/flink-cdc-ci.yml
+ - .github/actions/get-hugegraph-commit/**
+ - .github/actions/setup-candidate-sdk/**
+
+permissions:
+ contents: read
+
+# Keep the newest PR run; preserve independent push, release and manual runs.
+concurrency:
+ group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.event.pull_request.number || github.run_id }}
+ cancel-in-progress: ${{ github.event_name == 'pull_request' }}
+
+jobs:
+ packaged-cdc-recovery:
+ runs-on: ubuntu-latest
+ timeout-minutes: 60
+ env:
+ MAVEN_OPTS: -Xmx768m -XX:ActiveProcessorCount=2
+ FLINK_VERSION: '2.2.1'
+ CDC_VERSION: '3.6.0-2.2'
+ steps:
+ - uses: actions/checkout@v7
+
+ - name: Prepare Java 17
+ uses: actions/setup-java@v5
+ with:
+ distribution: zulu
+ java-version: '17'
+
+ - name: Install locked same-source candidate SDK
+ id: candidate-sdk
+ uses: ./.github/actions/setup-candidate-sdk
+
+ - name: Validate acceptance tooling
+ run: |
+ node --test hugegraph-loader/src/test/scripts/flink-launcher.test.cjs
+ python3 -m unittest discover -s hugegraph-loader/assembly/travis/flink-cdc -p 'test_*.py'
+ python3 hugegraph-loader/assembly/travis/flink-cdc/verify.py
+
+ - name: Build actual Loader distribution job
+ run: |
+ mvn package -pl hugegraph-client,hugegraph-loader -am -DskipTests -Dmaven.javadoc.skip=true \
+ -Dflink.version="$FLINK_VERSION" -Dflinkcdc.version="$CDC_VERSION" -ntp
+ mvn test checkstyle:check -pl hugegraph-client,hugegraph-loader -am -DforkCount=0 \
+ -Dtest=FlinkSinkSerializationTest,HugeGraphSinkTest,HugeGraphDeserializationTest \
+ -Dsurefire.failIfNoSpecifiedTests=false \
+ -Dflink.version="$FLINK_VERSION" -Dflinkcdc.version="$CDC_VERSION" -ntp
+
+ - name: Obtain external test-only JDBC driver
+ run: |
+ mkdir -p "$RUNNER_TEMP/flink-test-drivers"
+ curl --fail --location --retry 3 \
+ https://repo.maven.apache.org/maven2/mysql/mysql-connector-java/8.0.28/mysql-connector-java-8.0.28.jar \
+ --output "$RUNNER_TEMP/flink-test-drivers/mysql-connector-java-8.0.28.jar"
+ # The harness verifies its pinned SHA-256 before starting any runtime.
+
+ - name: Link packaged CDC coordinator against the actual Flink runtime
+ run: |
+ jobs=(hugegraph-loader/target/*-shaded.jar)
+ [[ ${#jobs[@]} -eq 1 && -f "${jobs[0]}" ]]
+ docker run --rm --network none --memory 512m --entrypoint java \
+ -v "$PWD/${jobs[0]}:/job.jar:ro" \
+ -v "$PWD/hugegraph-loader/target/test-classes:/probe:ro" \
+ "flink:${FLINK_VERSION}-java17" -Xmx256m -XX:ActiveProcessorCount=2 -cp /probe \
+ org.apache.hugegraph.loader.flink.PackagedCdcClassLoadingProbe /job.jar /opt/flink/lib
+
+ - name: Verify packaged CDC snapshot and recovery
+ env:
+ SERVER_COMMIT: ${{ steps.candidate-sdk.outputs.commit_id }}
+ CANDIDATE_ARCHIVE: ${{ steps.candidate-sdk.outputs.archive }}
+ CANDIDATE_ARCHIVE_SHA256: ${{ steps.candidate-sdk.outputs.archive-sha256 }}
+ CANDIDATE_SDK_MANIFEST: ${{ steps.candidate-sdk.outputs.manifest }}
+ CANDIDATE_MAVEN_REPO: ${{ steps.candidate-sdk.outputs.maven-repo }}
+ run: |
+ jobs=(hugegraph-loader/target/*-shaded.jar)
+ [[ -f "$CANDIDATE_ARCHIVE" && -f "$CANDIDATE_SDK_MANIFEST" ]]
+ [[ ${#jobs[@]} -eq 1 && -f "${jobs[0]}" ]]
+ python3 hugegraph-loader/assembly/travis/flink-cdc/verify.py --run \
+ --server-archive "$CANDIDATE_ARCHIVE" --job-jar "${jobs[0]}" \
+ --server-archive-sha256 "$CANDIDATE_ARCHIVE_SHA256" --sdk-manifest "$CANDIDATE_SDK_MANIFEST" \
+ --maven-repo "$CANDIDATE_MAVEN_REPO" \
+ --jdbc-driver "$RUNNER_TEMP/flink-test-drivers/mysql-connector-java-8.0.28.jar" \
+ --server-sha "$SERVER_COMMIT" --toolchain-sha "$(git rev-parse HEAD)" \
+ --work-root "$RUNNER_TEMP" --evidence "$RUNNER_TEMP/flink-cdc-evidence"
+
+ - name: Upload non-sensitive runtime evidence
+ if: always()
+ uses: actions/upload-artifact@v7
+ with:
+ name: flink-cdc-java17-evidence
+ path: ${{ runner.temp }}/flink-cdc-evidence
+ if-no-files-found: warn
diff --git a/.github/workflows/upstream-compatibility.yml b/.github/workflows/upstream-compatibility.yml
index 19fdee8a6..e2e95d360 100644
--- a/.github/workflows/upstream-compatibility.yml
+++ b/.github/workflows/upstream-compatibility.yml
@@ -53,6 +53,13 @@ jobs:
ref: ${{ steps.upstream.outputs.commit_id }}
expected-commit: ${{ steps.upstream.outputs.commit_id }}
+ - name: Select verified SDK version for all consumer builds and tests
+ env:
+ CANDIDATE_REPO: ${{ steps.sdk.outputs.maven-repo }}
+ run: >-
+ python3 .github/scripts/verify_candidate_image_sdk.py "$CANDIDATE_REPO"
+ --configure-upstream-env "$GITHUB_ENV"
+
- name: Compile core consumers
run: |
mvn install -pl hugegraph-client,hugegraph-loader,hugegraph-tools,hugegraph-spark-connector -am -DskipTests -Dmaven.javadoc.skip=true -ntp
diff --git a/docs/ci.md b/docs/ci.md
index 1bc6897e0..c3057ebc0 100644
--- a/docs/ci.md
+++ b/docs/ci.md
@@ -70,3 +70,5 @@ actionlint
```
Upstream compatibility runs daily and on demand against the latest ASF master, resolved once to an immutable commit for each run. It reuses the SDK build, core compilation, Client HTTP tests, Loader and Hubble backend unit tests, and Tools functional tests; Hadoop, Spark/Flink engine matrices and frontend browser tests remain in module CI. This independent check does not change the locked Module validation baseline.
+
+The floating upstream check selects `hugegraph.version` from the verified SDK manifest for every consumer build and test. The same version is passed to SDK packaging verification through `CANDIDATE_SDK_VERSION`; provenance, local-install and artifact-hash checks remain enforced. This lets Server master advance its Maven version without accidentally testing a released SDK. Fixed-baseline jobs retain their default SDK version.
diff --git a/hugegraph-dist/release-docs/LICENSE b/hugegraph-dist/release-docs/LICENSE
index eeccf5fdb..71412b9d6 100644
--- a/hugegraph-dist/release-docs/LICENSE
+++ b/hugegraph-dist/release-docs/LICENSE
@@ -403,7 +403,6 @@ See licenses/ for text of these licenses.
(Apache License, Version 2.0) * opencsv (net.sf.opencsv:opencsv:2.3 - http://opencsv.sf.net)
(Apache License, Version 2.0) * snappy-java (org.xerial.snappy:snappy-java:1.1.8.2 - https://github.com/xerial/snappy-java)
(Apache License, Version 2.0) * snappy-java (org.xerial.snappy:snappy-java:1.1.8.4 - https://github.com/xerial/snappy-java)
- (Apache License, Version 2.0) * Jetty :: Asynchronous HTTP Client (org.eclipse.jetty:jetty-client:9.4.33.v20201020 - https://eclipse.org/jetty/jetty-client)
(Apache License, Version 2.0) * Jetty :: Asynchronous HTTP Client (org.eclipse.jetty:jetty-client:9.4.40.v20210413 - https://eclipse.org/jetty/jetty-client)
(Apache License, Version 2.0) * Jetty :: Http Utility (org.eclipse.jetty:jetty-http:9.4.40.v20210413 - https://eclipse.org/jetty/jetty-http)
(Apache License, Version 2.0) * Jetty :: IO Utility (org.eclipse.jetty:jetty-io:9.4.40.v20210413 - https://eclipse.org/jetty/jetty-io)
@@ -1084,3 +1083,153 @@ represented as byte-identical Maven Central 1.7.0 artifacts.
Apache-2.0 terms above
(Apache-2.0) org.apiguardian:apiguardian-api:1.1.2
licenses/LICENSE-jackson-datatype-jdk8.txt
+
+========================================================================
+Flink CDC runtime dependency additions
+========================================================================
+
+The exact runtime union adds the components below. Versions retained by other
+modules remain listed separately. Apache-2.0 terms are reproduced above;
+other complete grants and bundled attributions are in the referenced files.
+Eclipse/Jersey/HK2/JAX-RS components use EPL-2.0, Jetty uses Apache-2.0,
+JavaBeans Activation 1.1.1 uses CDDL-1.0, and Reflections uses WTFPL.
+Zstd JNI and its bundled native Zstandard library use BSD-2-Clause.
+Flink engine and MySQL JDBC driver are supplied by the deployment environment;
+the runtime dependency model and Loader shaded artifact exclude those inputs.
+The MySQL binlog connector is an Apache-2.0 component, distinct from the JDBC driver.
+Required full notices are reproduced in the main distribution NOTICE.
+The supplement also retains the actual shaded payload's inherited license
+resources for JAXB/Commons, gRPC native code, Groovy, PostgreSQL SCRAM/stringprep,
+DataNucleus ASM and the Arrow Apache header template. These preserve baseline
+terms and attributions; they are not new CDC dependency identities.
+
+ (Apache-2.0) at.yawk.lz4:lz4-java:1.10.1
+ Apache-2.0 terms above
+ (Apache-2.0) ch.qos.reload4j:reload4j:1.2.19
+ licenses/LICENSE-listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.txt
+ (Apache-2.0) ch.qos.reload4j:reload4j:1.2.25
+ licenses/LICENSE-listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.txt
+ (Apache-2.0) com.esri.geometry:esri-geometry-api:2.2.0
+ Apache-2.0 terms above
+ (Apache-2.0) com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.12.3
+ Apache-2.0 terms above
+ (Apache-2.0) com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.12.3
+ licenses/LICENSE-jackson-core.txt
+ (Apache-2.0) com.fasterxml.jackson.module:jackson-module-afterburner:2.21.4
+ licenses/LICENSE-flink-final-third-party.txt
+ (BSD-2-Clause) com.github.luben:zstd-jni:1.5.0-2
+ licenses/LICENSE-flink-final-third-party.txt, licenses/LICENSE-hubble-java17-third-party.txt
+ (BSD-2-Clause) com.github.luben:zstd-jni:1.5.0-4
+ licenses/LICENSE-flink-final-third-party.txt, licenses/LICENSE-hubble-java17-third-party.txt
+ (Apache-2.0) com.zaxxer:HikariCP:4.0.3
+ Apache-2.0 terms above
+ (Apache-2.0) com.zendesk:mysql-binlog-connector-java:0.27.2
+ Apache-2.0 terms above
+ (Apache-2.0) io.debezium:debezium-api:1.9.8.Final
+ Apache-2.0 terms above
+ (Apache-2.0) io.debezium:debezium-connector-mysql:1.9.8.Final
+ Apache-2.0 terms above
+ (Apache-2.0) io.debezium:debezium-core:1.9.8.Final
+ Apache-2.0 terms above
+ (Apache-2.0) io.debezium:debezium-ddl-parser:1.9.8.Final
+ Apache-2.0 terms above
+ (Apache-2.0) io.debezium:debezium-embedded:1.9.8.Final
+ Apache-2.0 terms above
+ (Apache-2.0) io.swagger.core.v3:swagger-annotations:2.2.8
+ licenses/LICENSE-flink-final-third-party.txt
+ (EPL-2.0) jakarta.ws.rs:jakarta.ws.rs-api:2.1.6
+ licenses/LICENSE-hubble-java17-third-party.txt
+ (CDDL-1.0) javax.activation:activation:1.1.1
+ licenses/LICENSE-flink-final-third-party.txt
+ (EPL-2.0) javax.ws.rs:javax.ws.rs-api:2.1.1
+ licenses/LICENSE-hubble-java17-third-party.txt
+ (MIT) net.sourceforge.argparse4j:argparse4j:0.7.0
+ licenses/LICENSE-flink-final-third-party.txt
+ (BSD-3-Clause) org.antlr:antlr4-runtime:4.8
+ licenses/LICENSE-java17-candidate-sdk.txt
+ (Apache-2.0) org.apache.flink:flink-cdc-common:3.6.0-2.2
+ Apache-2.0 terms above
+ (Apache-2.0) org.apache.flink:flink-cdc-flink2-compat:3.6.0-2.2
+ Apache-2.0 terms above
+ (Apache-2.0) org.apache.flink:flink-cdc-runtime:3.6.0-2.2
+ Apache-2.0 terms above
+ (Apache-2.0) org.apache.flink:flink-connector-debezium:3.6.0-2.2
+ Apache-2.0 terms above
+ (Apache-2.0) org.apache.flink:flink-connector-mysql-cdc:3.6.0-2.2
+ Apache-2.0 terms above
+ (Apache-2.0) org.apache.flink:flink-shaded-guava:31.1-jre-17.0
+ Apache-2.0 terms above
+ (Apache-2.0) org.apache.flink:flink-shaded-guava:33.4.0-jre-20.0
+ Apache-2.0 terms above
+ (Apache-2.0) org.apache.kafka:connect-api:3.2.0
+ Apache-2.0 terms above
+ (Apache-2.0) org.apache.kafka:connect-api:3.9.2
+ Apache-2.0 terms above
+ (Apache-2.0) org.apache.kafka:connect-file:3.2.0
+ Apache-2.0 terms above
+ (Apache-2.0) org.apache.kafka:connect-file:3.9.2
+ Apache-2.0 terms above
+ (Apache-2.0) org.apache.kafka:connect-json:3.2.0
+ Apache-2.0 terms above
+ (Apache-2.0) org.apache.kafka:connect-json:3.9.2
+ Apache-2.0 terms above
+ (Apache-2.0) org.apache.kafka:connect-runtime:3.2.0
+ Apache-2.0 terms above
+ (Apache-2.0) org.apache.kafka:connect-runtime:3.9.2
+ Apache-2.0 terms above
+ (Apache-2.0) org.apache.kafka:connect-transforms:3.2.0
+ Apache-2.0 terms above
+ (Apache-2.0) org.apache.kafka:connect-transforms:3.9.2
+ Apache-2.0 terms above
+ (Apache-2.0) org.apache.kafka:kafka-clients:3.2.0
+ Apache-2.0 terms above
+ (Apache-2.0) org.apache.kafka:kafka-clients:3.9.2
+ Apache-2.0 terms above
+ (Apache-2.0) org.apache.kafka:kafka-tools:3.2.0
+ Apache-2.0 terms above
+ (Apache-2.0) org.apache.maven:maven-artifact:3.8.4
+ Apache-2.0 terms above
+ (Apache-2.0) org.apache.maven:maven-artifact:3.9.6
+ Apache-2.0 terms above
+ (Apache-2.0) org.bitbucket.b_c:jose4j:0.7.9
+ Apache-2.0 terms above
+ (Apache-2.0) org.bitbucket.b_c:jose4j:0.9.6
+ Apache-2.0 terms above
+ (Apache-2.0) org.codehaus.plexus:plexus-utils:3.3.0
+ Apache-2.0 terms above, licenses/LICENSE-flink-final-third-party.txt
+ (Apache-2.0) org.codehaus.plexus:plexus-utils:3.5.1
+ Apache-2.0 terms above, licenses/LICENSE-flink-final-third-party.txt
+ (Apache-2.0) org.eclipse.jetty:jetty-alpn-client:12.0.36
+ Apache-2.0 terms above
+ (Apache-2.0) org.eclipse.jetty:jetty-client:12.0.36
+ Apache-2.0 terms above
+ (Apache-2.0) org.eclipse.jetty:jetty-client:9.4.44.v20210927
+ licenses/LICENSE-jetty-client.txt
+ (Apache-2.0) org.eclipse.jetty:jetty-continuation:9.4.44.v20210927
+ licenses/LICENSE-jetty-client.txt
+ (Apache-2.0) org.eclipse.jetty:jetty-continuation:9.4.57.v20241219
+ licenses/LICENSE-jetty-client.txt
+ (Apache-2.0) org.eclipse.jetty:jetty-servlets:9.4.44.v20210927
+ licenses/LICENSE-jetty-client.txt
+ (Apache-2.0) org.eclipse.jetty:jetty-servlets:9.4.57.v20241219
+ licenses/LICENSE-jetty-client.txt
+ (EPL-2.0) org.glassfish.hk2.external:aopalliance-repackaged:2.6.1
+ licenses/LICENSE-hubble-java17-third-party.txt
+ (EPL-2.0) org.glassfish.hk2.external:jakarta.inject:2.6.1
+ licenses/LICENSE-hubble-java17-third-party.txt
+ (EPL-2.0) org.glassfish.hk2:hk2-api:2.6.1
+ licenses/LICENSE-hubble-java17-third-party.txt
+ (EPL-2.0) org.glassfish.hk2:hk2-locator:2.6.1
+ licenses/LICENSE-hubble-java17-third-party.txt
+ (EPL-2.0) org.glassfish.hk2:hk2-utils:2.6.1
+ licenses/LICENSE-hubble-java17-third-party.txt
+ (EPL-2.0) org.glassfish.jersey.containers:jersey-container-servlet:2.34
+ licenses/LICENSE-hubble-java17-third-party.txt
+ (EPL-2.0) org.glassfish.jersey.containers:jersey-container-servlet:3.1.11
+ licenses/LICENSE-hubble-java17-third-party.txt
+ (EPL-2.0) org.glassfish.jersey.inject:jersey-hk2:2.34
+ licenses/LICENSE-hubble-java17-third-party.txt
+ (WTFPL) org.reflections:reflections:0.10.2
+ licenses/LICENSE-flink-final-third-party.txt
+ (WTFPL) org.reflections:reflections:0.9.12
+ licenses/LICENSE-flink-final-third-party.txt
diff --git a/hugegraph-dist/release-docs/NOTICE b/hugegraph-dist/release-docs/NOTICE
index 54a3c1dba..3e8a3d110 100644
--- a/hugegraph-dist/release-docs/NOTICE
+++ b/hugegraph-dist/release-docs/NOTICE
@@ -5371,3 +5371,510 @@ Copyright 2001-2019 The Apache Software Foundation
This product includes software developed at
The Apache Software Foundation (http://www.apache.org/).
+
+========================================================================
+Flink CDC runtime dependency notices
+========================================================================
+
+========================================================================
+Inherited shaded payload: jakarta.xml.bind:jakarta.xml.bind-api:2.3.2 / META-INF/NOTICE.md
+========================================================================
+
+# Notices for Eclipse Project for JAXB
+
+This content is produced and maintained by the Eclipse Project for JAXB project.
+
+* Project home: https://projects.eclipse.org/projects/ee4j.jaxb
+
+## Trademarks
+
+Eclipse Project for JAXB is a trademark of the Eclipse Foundation.
+
+## Copyright
+
+All content is the property of the respective authors or their employers. For
+more information regarding authorship of content, please consult the listed
+source code repository logs.
+
+## Declared Project Licenses
+
+This program and the accompanying materials are made available under the terms
+of the Eclipse Distribution License v. 1.0 which is available
+at http://www.eclipse.org/org/documents/edl-v10.php.
+
+SPDX-License-Identifier: BSD-3-Clause
+
+## Source Code
+
+The project maintains the following source code repositories:
+
+* https://github.com/eclipse-ee4j/jaxb-api
+
+## Third-party Content
+
+This project leverages the following third party content.
+
+None
+
+## Cryptography
+
+Content may contain encryption software. The country in which you are currently
+may have restrictions on the import, possession, and use, and/or re-export to
+another country, of encryption software. BEFORE using any encryption software,
+please check the country's laws, regulations and policies concerning the import,
+possession, or use, and re-export of encryption software, to see if this is
+permitted.
+
+========================================================================
+Inherited shaded payload: commons-lang:commons-lang:2.6 / META-INF/NOTICE.txt
+========================================================================
+
+Apache Commons Lang
+Copyright 2001-2011 The Apache Software Foundation
+
+This product includes software developed by
+The Apache Software Foundation (http://www.apache.org/).
+
+========================================================================
+com.fasterxml.jackson.module:jackson-module-afterburner:2.21.4 / META-INF/NOTICE
+========================================================================
+
+# Jackson JSON processor
+
+Jackson is a high-performance, Free/Open Source JSON processing library.
+It was originally written by Tatu Saloranta (tatu.saloranta@iki.fi), and has
+been in development since 2007.
+It is currently developed by a community of developers, as well as supported
+commercially by FasterXML.com.
+
+## Licensing
+
+Jackson core and extension components (as well their dependencies) may be licensed under
+different licenses.
+To find the details that apply to this artifact see the accompanying LICENSE file.
+For more information, including possible other licensing options, contact
+FasterXML.com (http://fasterxml.com).
+
+## Credits
+
+A list of contributors may be found from CREDITS file, which is included
+in some artifacts (usually source distributions); but is always available
+from the source code management (SCM) system project uses.
+
+========================================================================
+io.swagger.core.v3:swagger-annotations:2.2.8 / META-INF/NOTICE
+========================================================================
+
+Swagger Core - swagger-annotations
+Copyright (c) 2015. SmartBear Software Inc.
+Swagger Core - swagger-annotations is licensed under Apache 2.0 license.
+Copy of the Apache 2.0 license can be found in `LICENSE` file.
+
+========================================================================
+jakarta.ws.rs:jakarta.ws.rs-api:2.1.6 / META-INF/NOTICE.md
+========================================================================
+
+# Notices for the Jakarta RESTful Web Services Project
+
+This content is produced and maintained by the **Jakarta RESTful Web Services**
+project.
+
+* Project home: https://projects.eclipse.org/projects/ee4j.jaxrs
+
+## Trademarks
+
+**Jakarta RESTful Web Services** is a trademark of the Eclipse Foundation.
+
+## Copyright
+
+All content is the property of the respective authors or their employers. For
+more information regarding authorship of content, please consult the listed
+source code repository logs.
+
+## Declared Project Licenses
+
+This program and the accompanying materials are made available under the terms
+of the Eclipse Public License v. 2.0 which is available at
+http://www.eclipse.org/legal/epl-2.0. This Source Code may also be made
+available under the following Secondary Licenses when the conditions for such
+availability set forth in the Eclipse Public License v. 2.0 are satisfied: GNU
+General Public License, version 2 with the GNU Classpath Exception which is
+available at https://www.gnu.org/software/classpath/license.html.
+
+SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+
+## Source Code
+
+The project maintains the following source code repositories:
+
+* https://github.com/eclipse-ee4j/jaxrs-api
+
+## Third-party Content
+
+This project leverages the following third party content.
+
+javaee-api (7.0)
+
+* License: Apache-2.0 AND W3C
+
+JUnit (4.11)
+
+* License: Common Public License 1.0
+
+Mockito (2.16.0)
+
+* Project: http://site.mockito.org
+* Source: https://github.com/mockito/mockito/releases/tag/v2.16.0
+
+## Cryptography
+
+Content may contain encryption software. The country in which you are currently
+may have restrictions on the import, possession, and use, and/or re-export to
+another country, of encryption software. BEFORE using any encryption software,
+please check the country's laws, regulations and policies concerning the import,
+possession, or use, and re-export of encryption software, to see if this is
+permitted.
+
+
+========================================================================
+org.apache.flink:flink-cdc-common:3.6.0-2.2 / META-INF/NOTICE
+========================================================================
+
+
+flink-cdc-common
+Copyright 2026 The Apache Software Foundation
+
+This product includes software developed at
+The Apache Software Foundation (http://www.apache.org/).
+
+
+
+========================================================================
+org.apache.flink:flink-cdc-flink2-compat:3.6.0-2.2 / META-INF/NOTICE
+========================================================================
+
+
+flink-cdc-flink2-compat
+Copyright 2026 The Apache Software Foundation
+
+This product includes software developed at
+The Apache Software Foundation (http://www.apache.org/).
+
+
+
+========================================================================
+org.apache.flink:flink-cdc-runtime:3.6.0-2.2 / META-INF/NOTICE
+========================================================================
+
+
+flink-cdc-runtime
+Copyright 2026 The Apache Software Foundation
+
+This product includes software developed at
+The Apache Software Foundation (http://www.apache.org/).
+
+
+
+========================================================================
+org.apache.flink:flink-connector-debezium:3.6.0-2.2 / META-INF/NOTICE
+========================================================================
+
+
+flink-connector-debezium
+Copyright 2026 The Apache Software Foundation
+
+This product includes software developed at
+The Apache Software Foundation (http://www.apache.org/).
+
+
+
+========================================================================
+org.apache.flink:flink-connector-mysql-cdc:3.6.0-2.2 / META-INF/NOTICE
+========================================================================
+
+
+flink-connector-mysql-cdc
+Copyright 2026 The Apache Software Foundation
+
+This product includes software developed at
+The Apache Software Foundation (http://www.apache.org/).
+
+
+
+========================================================================
+org.apache.flink:flink-shaded-guava:31.1-jre-17.0 / META-INF/NOTICE
+========================================================================
+
+// ------------------------------------------------------------------
+// NOTICE file corresponding to the section 4d of The Apache License,
+// Version 2.0, in this case for Apache Flink-shaded
+// ------------------------------------------------------------------
+
+Apache Flink-shaded
+Copyright 2006-2023 The Apache Software Foundation
+
+This product includes software developed at
+The Apache Software Foundation (http://www.apache.org/).
+
+flink-shaded-guava-30
+Copyright 2014-2021 The Apache Software Foundation
+
+This project bundles the following dependencies under the Apache Software License 2.0 (http://www.apache.org/licenses/LICENSE-2.0.txt)
+
+- com.google.guava:guava:31.1-jre
+- com.google.guava:failureaccess:1.0.1
+
+
+========================================================================
+org.apache.flink:flink-shaded-guava:33.4.0-jre-20.0 / META-INF/NOTICE
+========================================================================
+
+// ------------------------------------------------------------------
+// NOTICE file corresponding to the section 4d of The Apache License,
+// Version 2.0, in this case for Apache Flink-shaded
+// ------------------------------------------------------------------
+
+Apache Flink-shaded
+Copyright 2006-2025 The Apache Software Foundation
+
+This product includes software developed at
+The Apache Software Foundation (http://www.apache.org/).
+
+flink-shaded-guava-33
+Copyright 2014-2023 The Apache Software Foundation
+
+This project bundles the following dependencies under the Apache Software License 2.0 (http://www.apache.org/licenses/LICENSE-2.0.txt)
+
+- com.google.guava:guava:33.4.0-jre
+- com.google.guava:failureaccess:1.0.2
+
+
+========================================================================
+org.apache.kafka:connect-api:3.2.0 / NOTICE
+org.apache.kafka:connect-file:3.2.0 / NOTICE
+org.apache.kafka:connect-json:3.2.0 / NOTICE
+org.apache.kafka:connect-runtime:3.2.0 / NOTICE
+org.apache.kafka:connect-transforms:3.2.0 / NOTICE
+org.apache.kafka:kafka-clients:3.2.0 / NOTICE
+org.apache.kafka:kafka-tools:3.2.0 / NOTICE
+========================================================================
+
+Apache Kafka
+Copyright 2022 The Apache Software Foundation.
+
+This product includes software developed at
+The Apache Software Foundation (https://www.apache.org/).
+
+This distribution has a binary dependency on jersey, which is available under the CDDL
+License. The source code of jersey can be found at https://github.com/jersey/jersey/.
+
+This distribution has a binary test dependency on jqwik, which is available under
+the Eclipse Public License 2.0. The source code can be found at
+https://github.com/jlink/jqwik.
+
+The streams-scala (streams/streams-scala) module was donated by Lightbend and the original code was copyrighted by them:
+Copyright (C) 2018 Lightbend Inc.
+Copyright (C) 2017-2018 Alexis Seigneurin.
+
+This project contains the following code copied from Apache Hadoop:
+clients/src/main/java/org/apache/kafka/common/utils/PureJavaCrc32C.java
+Some portions of this file Copyright (c) 2004-2006 Intel Corporation and licensed under the BSD license.
+
+This project contains the following code copied from Apache Hive:
+streams/src/main/java/org/apache/kafka/streams/state/internals/Murmur3.java
+
+========================================================================
+org.apache.kafka:connect-api:3.9.2 / NOTICE
+org.apache.kafka:connect-file:3.9.2 / NOTICE
+org.apache.kafka:connect-json:3.9.2 / NOTICE
+org.apache.kafka:connect-runtime:3.9.2 / NOTICE
+org.apache.kafka:connect-transforms:3.9.2 / NOTICE
+org.apache.kafka:kafka-clients:3.9.2 / NOTICE
+========================================================================
+
+Apache Kafka
+Copyright 2026 The Apache Software Foundation.
+
+This product includes software developed at
+The Apache Software Foundation (https://www.apache.org/).
+
+This distribution has a binary dependency on jersey, which is available under the CDDL
+License. The source code of jersey can be found at https://github.com/jersey/jersey/.
+
+This distribution has a binary test dependency on jqwik, which is available under
+the Eclipse Public License 2.0. The source code can be found at
+https://github.com/jlink/jqwik.
+
+The streams-scala (streams/streams-scala) module was donated by Lightbend and the original code was copyrighted by them:
+Copyright (C) 2018 Lightbend Inc.
+Copyright (C) 2017-2018 Alexis Seigneurin.
+
+This project contains the following code copied from Apache Hadoop:
+clients/src/main/java/org/apache/kafka/common/utils/PureJavaCrc32C.java
+Some portions of this file Copyright (c) 2004-2006 Intel Corporation and licensed under the BSD license.
+
+This project contains the following code copied from Apache Hive:
+streams/src/main/java/org/apache/kafka/streams/state/internals/Murmur3.java
+
+========================================================================
+org.apache.maven:maven-artifact:3.8.4 / META-INF/NOTICE
+========================================================================
+
+
+Maven Artifact
+Copyright 2001-2021 The Apache Software Foundation
+
+This product includes software developed at
+The Apache Software Foundation (http://www.apache.org/).
+
+
+
+========================================================================
+org.apache.maven:maven-artifact:3.9.6 / META-INF/NOTICE
+========================================================================
+
+Maven Artifact
+Copyright 2001-2023 The Apache Software Foundation
+
+
+This product includes software developed at
+The Apache Software Foundation (http://www.apache.org/).
+
+========================================================================
+org.codehaus.plexus:plexus-utils:3.3.0 / META-INF/NOTICE
+org.codehaus.plexus:plexus-utils:3.5.1 / META-INF/NOTICE
+========================================================================
+
+This product includes software developed by the Indiana University
+ Extreme! Lab (http://www.extreme.indiana.edu/).
+
+This product includes software developed by
+The Apache Software Foundation (http://www.apache.org/).
+
+This product includes software developed by
+ThoughtWorks (http://www.thoughtworks.com).
+
+This product includes software developed by
+javolution (http://javolution.org/).
+
+This product includes software developed by
+Rome (https://rome.dev.java.net/).
+
+========================================================================
+org.glassfish.jersey.containers:jersey-container-servlet:2.34 / META-INF/NOTICE.md
+org.glassfish.jersey.inject:jersey-hk2:2.34 / META-INF/NOTICE.md
+========================================================================
+
+# Notice for Jersey
+This content is produced and maintained by the Eclipse Jersey project.
+
+* Project home: https://projects.eclipse.org/projects/ee4j.jersey
+
+## Trademarks
+Eclipse Jersey is a trademark of the Eclipse Foundation.
+
+## Copyright
+
+All content is the property of the respective authors or their employers. For
+more information regarding authorship of content, please consult the listed
+source code repository logs.
+
+## Declared Project Licenses
+
+This program and the accompanying materials are made available under the terms
+of the Eclipse Public License v. 2.0 which is available at
+http://www.eclipse.org/legal/epl-2.0. This Source Code may also be made
+available under the following Secondary Licenses when the conditions for such
+availability set forth in the Eclipse Public License v. 2.0 are satisfied: GNU
+General Public License, version 2 with the GNU Classpath Exception which is
+available at https://www.gnu.org/software/classpath/license.html.
+
+SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+
+## Source Code
+The project maintains the following source code repositories:
+
+* https://github.com/eclipse-ee4j/jersey
+
+## Third-party Content
+
+Angular JS, v1.6.6
+* License MIT (http://www.opensource.org/licenses/mit-license.php)
+* Project: http://angularjs.org
+* Coyright: (c) 2010-2017 Google, Inc.
+
+aopalliance Version 1
+* License: all the source code provided by AOP Alliance is Public Domain.
+* Project: http://aopalliance.sourceforge.net
+* Copyright: Material in the public domain is not protected by copyright
+
+Bean Validation API 2.0.2
+* License: Apache License, 2.0
+* Project: http://beanvalidation.org/1.1/
+* Copyright: 2009, Red Hat, Inc. and/or its affiliates, and individual contributors
+* by the @authors tag.
+
+Hibernate Validator CDI, 6.1.2.Final
+* License: Apache License, 2.0
+* Project: https://beanvalidation.org/
+* Repackaged in org.glassfish.jersey.server.validation.internal.hibernate
+
+Bootstrap v3.3.7
+* License: MIT license (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+* Project: http://getbootstrap.com
+* Copyright: 2011-2016 Twitter, Inc
+
+Google Guava Version 18.0
+* License: Apache License, 2.0
+* Copyright (C) 2009 The Guava Authors
+
+javax.inject Version: 1
+* License: Apache License, 2.0
+* Copyright (C) 2009 The JSR-330 Expert Group
+
+Javassist Version 3.25.0-GA
+* License: Apache License, 2.0
+* Project: http://www.javassist.org/
+* Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
+
+Jackson JAX-RS Providers Version 2.10.1
+* License: Apache License, 2.0
+* Project: https://github.com/FasterXML/jackson-jaxrs-providers
+* Copyright: (c) 2009-2011 FasterXML, LLC. All rights reserved unless otherwise indicated.
+
+jQuery v1.12.4
+* License: jquery.org/license
+* Project: jquery.org
+* Copyright: (c) jQuery Foundation
+
+jQuery Barcode plugin 0.3
+* License: MIT & GPL (http://www.opensource.org/licenses/mit-license.php & http://www.gnu.org/licenses/gpl.html)
+* Project: http://www.pasella.it/projects/jQuery/barcode
+* Copyright: (c) 2009 Antonello Pasella antonello.pasella@gmail.com
+
+JSR-166 Extension - JEP 266
+* License: CC0
+* No copyright
+* Written by Doug Lea with assistance from members of JCP JSR-166 Expert Group and released to the public domain, as explained at http://creativecommons.org/publicdomain/zero/1.0/
+
+KineticJS, v4.7.1
+* License: MIT license (http://www.opensource.org/licenses/mit-license.php)
+* Project: http://www.kineticjs.com, https://github.com/ericdrowell/KineticJS
+* Copyright: Eric Rowell
+
+org.objectweb.asm Version 8.0
+* License: Modified BSD (http://asm.objectweb.org/license.html)
+* Copyright (c) 2000-2011 INRIA, France Telecom. All rights reserved.
+
+org.osgi.core version 6.0.0
+* License: Apache License, 2.0
+* Copyright (c) OSGi Alliance (2005, 2008). All Rights Reserved.
+
+org.glassfish.jersey.server.internal.monitoring.core
+* License: Apache License, 2.0
+* Copyright (c) 2015-2018 Oracle and/or its affiliates. All rights reserved.
+* Copyright 2010-2013 Coda Hale and Yammer, Inc.
+
+W3.org documents
+* License: W3C License
+* Copyright: Copyright (c) 1994-2001 World Wide Web Consortium, (Massachusetts Institute of Technology, Institut National de Recherche en Informatique et en Automatique, Keio University). All Rights Reserved. http://www.w3.org/Consortium/Legal/
diff --git a/hugegraph-dist/release-docs/licenses/LICENSE-flink-final-third-party.txt b/hugegraph-dist/release-docs/licenses/LICENSE-flink-final-third-party.txt
new file mode 100644
index 000000000..a19373df9
--- /dev/null
+++ b/hugegraph-dist/release-docs/licenses/LICENSE-flink-final-third-party.txt
@@ -0,0 +1,1154 @@
+Complete additional third-party license texts for the Flink CDC runtime.
+
+Exact resolved GAV/ZIP resources and release sources identify each grant.
+Identical full texts are reproduced once. Notices appear in the main NOTICE.
+The Zstandard native library uses its BSD option. Eclipse components use
+the EPL-2.0 option, Jetty uses Apache-2.0, and Reflections uses WTFPL.
+The inherited shade carries additional baseline resources (including the
+Arrow Apache header template); their original full text is retained below.
+They are coverage repairs for existing dependency identities, not CDC additions.
+Existing complete terms remain in the component files referenced by LICENSE.
+
+========================================================================
+Inherited shaded payload: org.apache.arrow:arrow-vector:2.0.0 / codegen/includes/license.ftl
+========================================================================
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+========================================================================
+Inherited shaded payload: io.grpc:grpc-netty-shaded:1.39.0 / META-INF/license/LICENSE.boringssl.txt
+========================================================================
+
+BoringSSL is a fork of OpenSSL. As such, large parts of it fall under OpenSSL
+licensing. Files that are completely new have a Google copyright and an ISC
+license. This license is reproduced at the bottom of this file.
+Contributors to BoringSSL are required to follow the CLA rules for Chromium:
+https://cla.developers.google.com/clas
+Files in third_party/ have their own licenses, as described therein. The MIT
+license, for third_party/fiat, which, unlike other third_party directories, is
+compiled into non-test libraries, is included below.
+The OpenSSL toolkit stays under a dual license, i.e. both the conditions of the
+OpenSSL License and the original SSLeay license apply to the toolkit. See below
+for the actual license texts. Actually both licenses are BSD-style Open Source
+licenses. In case of any license issues related to OpenSSL please contact
+openssl-core@openssl.org.
+The following are Google-internal bug numbers where explicit permission from
+some authors is recorded for use of their work. (This is purely for our own
+record keeping.)
+ 27287199
+ 27287880
+ 27287883
+ OpenSSL License
+ ---------------
+/* ====================================================================
+ * Copyright (c) 1998-2011 The OpenSSL Project. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ * software must display the following acknowledgment:
+ * "This product includes software developed by the OpenSSL Project
+ * for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission. For written permission, please contact
+ * openssl-core@openssl.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ * nor may "OpenSSL" appear in their names without prior written
+ * permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by the OpenSSL Project
+ * for use in the OpenSSL Toolkit (http://www.openssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com). This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com).
+ *
+ */
+ Original SSLeay License
+ -----------------------
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to. The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code. The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * "This product includes cryptographic software written by
+ * Eric Young (eay@cryptsoft.com)"
+ * The word 'cryptographic' can be left out if the rouines from the library
+ * being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ * the apps directory (application code) you must include an acknowledgement:
+ * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed. i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.]
+ */
+ISC license used for completely new code in BoringSSL:
+/* Copyright (c) 2015, Google Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
+The code in third_party/fiat carries the MIT license:
+Copyright (c) 2015-2016 the fiat-crypto authors (see
+https://github.com/mit-plv/fiat-crypto/blob/master/AUTHORS).
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+Licenses for support code
+-------------------------
+Parts of the TLS test suite are under the Go license. This code is not included
+in BoringSSL (i.e. libcrypto and libssl) when compiled, however, so
+distributing code linked against BoringSSL does not trigger this license:
+Copyright (c) 2009 The Go Authors. All rights reserved.
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+BoringSSL uses the Chromium test infrastructure to run a continuous build,
+trybots etc. The scripts which manage this, and the script for generating build
+metadata, are under the Chromium license. Distributing code linked against
+BoringSSL does not trigger this license.
+Copyright 2015 The Chromium Authors. All rights reserved.
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+========================================================================
+Inherited shaded payload: io.grpc:grpc-netty-shaded:1.39.0 / META-INF/license/LICENSE.aix-netbsd.txt
+========================================================================
+
+
+ LICENSE ISSUES
+ ==============
+
+ The OpenSSL toolkit stays under a double license, i.e. both the conditions of
+ the OpenSSL License and the original SSLeay license apply to the toolkit.
+ See below for the actual license texts.
+
+ OpenSSL License
+ ---------------
+
+/* ====================================================================
+ * Copyright (c) 1998-2019 The OpenSSL Project. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ * software must display the following acknowledgment:
+ * "This product includes software developed by the OpenSSL Project
+ * for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission. For written permission, please contact
+ * openssl-core@openssl.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ * nor may "OpenSSL" appear in their names without prior written
+ * permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by the OpenSSL Project
+ * for use in the OpenSSL Toolkit (http://www.openssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com). This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com).
+ *
+ */
+
+ Original SSLeay License
+ -----------------------
+
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to. The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code. The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * "This product includes cryptographic software written by
+ * Eric Young (eay@cryptsoft.com)"
+ * The word 'cryptographic' can be left out if the rouines from the library
+ * being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ * the apps directory (application code) you must include an acknowledgement:
+ * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed. i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.]
+ */
+
+
+========================================================================
+Inherited shaded payload: org.codehaus.groovy:groovy-all:2.4.21 / META-INF/licenses/antlr2-license.txt
+========================================================================
+
+ANTLR 2 License
+
+We reserve no legal rights to the ANTLR--it is fully in the public domain. An individual or company may do
+whatever they wish with source code distributed with ANTLR or the code generated by ANTLR, including the
+incorporation of ANTLR, or its output, into commerical software.
+
+We encourage users to develop software with ANTLR. However, we do ask that credit is given to us for
+developing ANTLR. By "credit", we mean that if you use ANTLR or incorporate any source code into one of your
+programs (commercial product, research project, or otherwise) that you acknowledge this fact somewhere in
+the documentation, research report, etc... If you like ANTLR and have developed a nice tool with the output,
+please mention that you developed it using ANTLR. In addition, we ask that the headers remain intact in our
+source code. As long as these guidelines are kept, we expect to continue enhancing this system and expect to
+make other tools available as they are completed.
+
+In countries where the Public Domain status of the work may not be valid, the author grants a copyright
+licence to the general public to deal in the work without restriction and permission to sublicence derivates
+under the terms of any (OSI approved) Open Source licence.
+
+The Python parser generator code under antlr/actions/python/ is covered by the 3-clause BSD licence (this
+part is included in the binary JAR files); the run-time part under lib/python/ is covered by the GNU GPL,
+version 3 or later (this part is not included in the binary JAR files). See [1] for the full details.
+
+https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=750643#80%22
+
+========================================================================
+Inherited shaded payload: org.codehaus.groovy:groovy-all:2.4.21 / META-INF/licenses/jline2-license.txt
+========================================================================
+
+Copyright (c) 2002-2012, the original author or authors.
+All rights reserved.
+
+http://www.opensource.org/licenses/bsd-license.php
+
+Redistribution and use in source and binary forms, with or
+without modification, are permitted provided that the following
+conditions are met:
+
+Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+
+Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with
+the distribution.
+
+Neither the name of JLine nor the names of its contributors
+may be used to endorse or promote products derived from this
+software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
+BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+OF THE POSSIBILITY OF SUCH DAMAGE.
+
+========================================================================
+Inherited shaded payload: org.codehaus.groovy:groovy-all:2.4.21 / META-INF/licenses/jsr223-license.txt
+========================================================================
+
+Copyright (c) 2006, Sun Microsystems, Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ - Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+
+ - Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+ - Neither the name of the Sun Microsystems, Inc. nor the names of
+ contributors may be used to endorse or promote products derived from this
+ software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGE.
+
+========================================================================
+Inherited shaded payload: org.codehaus.groovy:groovy-all:2.4.21 / META-INF/licenses/normalize-stylesheet-license.txt
+========================================================================
+
+Copyright (c) Nicolas Gallagher and Jonathan Neal
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+========================================================================
+Inherited shaded payload: org.postgresql:postgresql:42.4.1 / META-INF/licenses/com.ongres.scram/client-2.1/LICENSE
+Inherited shaded payload: org.postgresql:postgresql:42.4.1 / META-INF/licenses/com.ongres.scram/common-2.1/LICENSE
+========================================================================
+
+Copyright 2017, OnGres.
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice,
+this list of conditions and the following disclaimer in the documentation and/or
+other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+========================================================================
+Inherited shaded payload: org.postgresql:postgresql:42.4.1 / META-INF/licenses/com.ongres.stringprep/saslprep-1.1/LICENSE
+Inherited shaded payload: org.postgresql:postgresql:42.4.1 / META-INF/licenses/com.ongres.stringprep/stringprep-1.1/LICENSE
+========================================================================
+
+Copyright 2019, OnGres.
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice,
+this list of conditions and the following disclaimer in the documentation and/or
+other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+========================================================================
+Inherited shaded payload: org.datanucleus:datanucleus-core:4.1.17 / META-INF/ASM_LICENSE.txt
+========================================================================
+
+ObjectWeb ASM 4.1 is repackaged in this datanucleus-core jar under
+org.datanucleus.asm. The files in that package are licensed under
+the following terms
+=====================================================================
+Copyright (c) 2000-2011 INRIA, France Telecom
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+3. Neither the name of the copyright holders nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+THE POSSIBILITY OF SUCH DAMAGE.
+=====================================================================
+
+========================================================================
+com.fasterxml.jackson.module:jackson-module-afterburner:2.21.4 / META-INF/LICENSE
+========================================================================
+
+This copy of Jackson JSON processor `jackson-module-afterburner` module is licensed under the
+Apache (Software) License, version 2.0 ("the License").
+See the License for details about distribution rights, and the
+specific rights regarding derivative works.
+
+You may obtain a copy of the License at:
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Additional licensing information exists for following 3rd party library dependencies
+
+### ASM
+
+ASM: a very small and fast Java bytecode manipulation framework
+Copyright (c) 2000-2011 INRIA, France Telecom
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+3. Neither the name of the copyright holders nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+THE POSSIBILITY OF SUCH DAMAGE.
+
+
+========================================================================
+io.swagger.core.v3:swagger-annotations:2.2.8 / META-INF/LICENSE
+========================================================================
+
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright (c) 2015. SmartBear Software Inc.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+========================================================================
+org.codehaus.plexus:plexus-utils:3.3.0 / licenses/extreme.indiana.edu.license.TXT
+org.codehaus.plexus:plexus-utils:3.5.1 / licenses/extreme.indiana.edu.license.TXT
+========================================================================
+
+Indiana University Extreme! Lab Software License
+
+Version 1.1.1
+
+Copyright (c) 2002 Extreme! Lab, Indiana University. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the distribution.
+
+3. The end-user documentation included with the redistribution, if any,
+ must include the following acknowledgment:
+
+ "This product includes software developed by the Indiana University
+ Extreme! Lab (http://www.extreme.indiana.edu/)."
+
+Alternately, this acknowledgment may appear in the software itself,
+if and wherever such third-party acknowledgments normally appear.
+
+4. The names "Indiana University" and "Indiana University Extreme! Lab"
+must not be used to endorse or promote products derived from this
+software without prior written permission. For written permission,
+please contact http://www.extreme.indiana.edu/.
+
+5. Products derived from this software may not use "Indiana University"
+name nor may "Indiana University" appear in their name, without prior
+written permission of the Indiana University.
+
+THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+IN NO EVENT SHALL THE AUTHORS, COPYRIGHT HOLDERS OR ITS CONTRIBUTORS
+BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+========================================================================
+org.codehaus.plexus:plexus-utils:3.3.0 / licenses/javolution.license.TXT
+org.codehaus.plexus:plexus-utils:3.5.1 / licenses/javolution.license.TXT
+========================================================================
+
+Javolution - Java(TM) Solution for Real-Time and Embedded Systems
+Copyright (c) 2006, Javolution (http://javolution.org)
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+========================================================================
+https://raw.githubusercontent.com/spdx/license-list-data/main/text/CDDL-1.0.txt
+========================================================================
+
+COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL)
+Version 1.0
+
+1. Definitions.
+
+1.1. “Contributor” means each individual or entity that creates or contributes to the creation of Modifications.
+
+1.2. “Contributor Version” means the combination of the Original Software, prior Modifications used by a Contributor (if any), and the Modifications made by that particular Contributor.
+
+1.3. “Covered Software” means (a) the Original Software, or (b) Modifications, or (c) the combination of files containing Original Software with files containing Modifications, in each case including portions thereof.
+
+1.4. “Executable” means the Covered Software in any form other than Source Code.
+
+1.5. “Initial Developer” means the individual or entity that first makes Original Software available under this License.
+
+1.6. “Larger Work” means a work which combines Covered Software or portions thereof with code not governed by the terms of this License.
+
+1.7. “License” means this document.
+
+1.8. “Licensable” means having the right to grant, to the maximum extent possible, whether at the time of the initial grant or subsequently acquired, any and all of the rights conveyed herein.
+
+1.9. “Modifications” means the Source Code and Executable form of any of the following:
+
+ A. Any file that results from an addition to, deletion from or modification of the contents of a file containing Original Software or previous Modifications;
+
+ B. Any new file that contains any part of the Original Software or previous Modification; or
+
+ C. Any new file that is contributed or otherwise made available under the terms of this License.
+
+1.10. “Original Software” means the Source Code and Executable form of computer software code that is originally released under this License.
+
+1.11. “Patent Claims” means any patent claim(s), now owned or hereafter acquired, including without limitation, method, process, and apparatus claims, in any patent Licensable by grantor.
+
+1.12. “Source Code” means (a) the common form of computer software code in which modifications are made and (b) associated documentation included in or with such code.
+
+1.13. “You” (or “Your”) means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License. For legal entities, “You” includes any entity which controls, is controlled by, or is under common control with You. For purposes of this definition, “control” means (a) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (b) ownership of more than fifty percent (50%) of the outstanding shares or beneficial ownership of such entity.
+
+2. License Grants.
+
+2.1. The Initial Developer Grant.
+Conditioned upon Your compliance with Section 3.1 below and subject to third party intellectual property claims, the Initial Developer hereby grants You a world-wide, royalty-free, non-exclusive license:
+
+ (a) under intellectual property rights (other than patent or trademark) Licensable by Initial Developer, to use, reproduce, modify, display, perform, sublicense and distribute the Original Software (or portions thereof), with or without Modifications, and/or as part of a Larger Work; and
+
+ (b) under Patent Claims infringed by the making, using or selling of Original Software, to make, have made, use, practice, sell, and offer for sale, and/or otherwise dispose of the Original Software (or portions thereof).
+
+ (c) The licenses granted in Sections 2.1(a) and (b) are effective on the date Initial Developer first distributes or otherwise makes the Original Software available to a third party under the terms of this License.
+
+ (d) Notwithstanding Section 2.1(b) above, no patent license is granted: (1) for code that You delete from the Original Software, or (2) for infringements caused by: (i) the modification of the Original Software, or (ii) the combination of the Original Software with other software or devices.
+
+2.2. Contributor Grant.
+Conditioned upon Your compliance with Section 3.1 below and subject to third party intellectual property claims, each Contributor hereby grants You a world-wide, royalty-free, non-exclusive license:
+
+ (a) under intellectual property rights (other than patent or trademark) Licensable by Contributor to use, reproduce, modify, display, perform, sublicense and distribute the Modifications created by such Contributor (or portions thereof), either on an unmodified basis, with other Modifications, as Covered Software and/or as part of a Larger Work; and
+
+ (b) under Patent Claims infringed by the making, using, or selling of Modifications made by that Contributor either alone and/or in combination with its Contributor Version (or portions of such combination), to make, use, sell, offer for sale, have made, and/or otherwise dispose of: (1) Modifications made by that Contributor (or portions thereof); and (2) the combination of Modifications made by that Contributor with its Contributor Version (or portions of such combination).
+
+ (c) The licenses granted in Sections 2.2(a) and 2.2(b) are effective on the date Contributor first distributes or otherwise makes the Modifications available to a third party.
+
+ (d) Notwithstanding Section 2.2(b) above, no patent license is granted: (1) for any code that Contributor has deleted from the Contributor Version; (2) for infringements caused by: (i) third party modifications of Contributor Version, or (ii) the combination of Modifications made by that Contributor with other software (except as part of the Contributor Version) or other devices; or (3) under Patent Claims infringed by Covered Software in the absence of Modifications made by that Contributor.
+
+3. Distribution Obligations.
+
+3.1. Availability of Source Code.
+Any Covered Software that You distribute or otherwise make available in Executable form must also be made available in Source Code form and that Source Code form must be distributed only under the terms of this License. You must include a copy of this License with every copy of the Source Code form of the Covered Software You distribute or otherwise make available. You must inform recipients of any such Covered Software in Executable form as to how they can obtain such Covered Software in Source Code form in a reasonable manner on or through a medium customarily used for software exchange.
+
+3.2. Modifications.
+The Modifications that You create or to which You contribute are governed by the terms of this License. You represent that You believe Your Modifications are Your original creation(s) and/or You have sufficient rights to grant the rights conveyed by this License.
+
+3.3. Required Notices.
+You must include a notice in each of Your Modifications that identifies You as the Contributor of the Modification. You may not remove or alter any copyright, patent or trademark notices contained within the Covered Software, or any notices of licensing or any descriptive text giving attribution to any Contributor or the Initial Developer.
+
+3.4. Application of Additional Terms.
+You may not offer or impose any terms on any Covered Software in Source Code form that alters or restricts the applicable version of this License or the recipients’ rights hereunder. You may choose to offer, and to charge a fee for, warranty, support, indemnity or liability obligations to one or more recipients of Covered Software. However, you may do so only on Your own behalf, and not on behalf of the Initial Developer or any Contributor. You must make it absolutely clear that any such warranty, support, indemnity or liability obligation is offered by You alone, and You hereby agree to indemnify the Initial Developer and every Contributor for any liability incurred by the Initial Developer or such Contributor as a result of warranty, support, indemnity or liability terms You offer.
+
+3.5. Distribution of Executable Versions.
+You may distribute the Executable form of the Covered Software under the terms of this License or under the terms of a license of Your choice, which may contain terms different from this License, provided that You are in compliance with the terms of this License and that the license for the Executable form does not attempt to limit or alter the recipient’s rights in the Source Code form from the rights set forth in this License. If You distribute the Covered Software in Executable form under a different license, You must make it absolutely clear that any terms which differ from this License are offered by You alone, not by the Initial Developer or Contributor. You hereby agree to indemnify the Initial Developer and every Contributor for any liability incurred by the Initial Developer or such Contributor as a result of any such terms You offer.
+
+3.6. Larger Works.
+You may create a Larger Work by combining Covered Software with other code not governed by the terms of this License and distribute the Larger Work as a single product. In such a case, You must make sure the requirements of this License are fulfilled for the Covered Software.
+
+4. Versions of the License.
+
+4.1. New Versions.
+Sun Microsystems, Inc. is the initial license steward and may publish revised and/or new versions of this License from time to time. Each version will be given a distinguishing version number. Except as provided in Section 4.3, no one other than the license steward has the right to modify this License.
+
+4.2. Effect of New Versions.
+You may always continue to use, distribute or otherwise make the Covered Software available under the terms of the version of the License under which You originally received the Covered Software. If the Initial Developer includes a notice in the Original Software prohibiting it from being distributed or otherwise made available under any subsequent version of the License, You must distribute and make the Covered Software available under the terms of the version of the License under which You originally received the Covered Software. Otherwise, You may also choose to use, distribute or otherwise make the Covered Software available under the terms of any subsequent version of the License published by the license steward.
+
+4.3. Modified Versions.
+When You are an Initial Developer and You want to create a new license for Your Original Software, You may create and use a modified version of this License if You: (a) rename the license and remove any references to the name of the license steward (except to note that the license differs from this License); and (b) otherwise make it clear that the license contains terms which differ from this License.
+
+5. DISCLAIMER OF WARRANTY.
+
+COVERED SOFTWARE IS PROVIDED UNDER THIS LICENSE ON AN “AS IS” BASIS, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES THAT THE COVERED SOFTWARE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED SOFTWARE IS WITH YOU. SHOULD ANY COVERED SOFTWARE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF ANY COVERED SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER.
+
+6. TERMINATION.
+
+6.1. This License and the rights granted hereunder will terminate automatically if You fail to comply with terms herein and fail to cure such breach within 30 days of becoming aware of the breach. Provisions which, by their nature, must remain in effect beyond the termination of this License shall survive.
+
+6.2. If You assert a patent infringement claim (excluding declaratory judgment actions) against Initial Developer or a Contributor (the Initial Developer or Contributor against whom You assert such claim is referred to as “Participant”) alleging that the Participant Software (meaning the Contributor Version where the Participant is a Contributor or the Original Software where the Participant is the Initial Developer) directly or indirectly infringes any patent, then any and all rights granted directly or indirectly to You by such Participant, the Initial Developer (if the Initial Developer is not the Participant) and all Contributors under Sections 2.1 and/or 2.2 of this License shall, upon 60 days notice from Participant terminate prospectively and automatically at the expiration of such 60 day notice period, unless if within such 60 day period You withdraw Your claim with respect to the Participant Software against such Participant either unilaterally or pursuant to a written agreement with Participant.
+
+6.3. In the event of termination under Sections 6.1 or 6.2 above, all end user licenses that have been validly granted by You or any distributor hereunder prior to termination (excluding licenses granted to You by any distributor) shall survive termination.
+
+7. LIMITATION OF LIABILITY.
+
+UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE INITIAL DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF COVERED SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO ANY PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOST PROFITS, LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY RESULTING FROM SUCH PARTY’S NEGLIGENCE TO THE EXTENT APPLICABLE LAW PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THIS EXCLUSION AND LIMITATION MAY NOT APPLY TO YOU.
+
+8. U.S. GOVERNMENT END USERS.
+
+The Covered Software is a “commercial item,” as that term is defined in 48 C.F.R. 2.101 (Oct. 1995), consisting of “commercial computer software” (as that term is defined at 48 C.F.R. § 252.227-7014(a)(1)) and “commercial computer software documentation” as such terms are used in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48 C.F.R. 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 (June 1995), all U.S. Government End Users acquire Covered Software with only those rights set forth herein. This U.S. Government Rights clause is in lieu of, and supersedes, any other FAR, DFAR, or other clause or provision that addresses Government rights in computer software under this License.
+
+9. MISCELLANEOUS.
+
+This License represents the complete agreement concerning subject matter hereof. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. This License shall be governed by the law of the jurisdiction specified in a notice contained within the Original Software (except to the extent applicable law, if any, provides otherwise), excluding such jurisdiction’s conflict-of-law provisions. Any litigation relating to this License shall be subject to the jurisdiction of the courts located in the jurisdiction and venue specified in a notice contained within the Original Software, with the losing party responsible for costs, including, without limitation, court costs and reasonable attorneys’ fees and expenses. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any law or regulation which provides that the language of a contract shall be construed against the drafter shall not apply to this License. You agree that You alone are responsible for compliance with the United States export administration regulations (and the export control laws and regulation of any other countries) when You use, distribute or otherwise make available any Covered Software.
+
+10. RESPONSIBILITY FOR CLAIMS.
+
+As between Initial Developer and the Contributors, each party is responsible for claims and damages arising, directly or indirectly, out of its utilization of rights under this License and You agree to work with Initial Developer and Contributors to distribute such responsibility on an equitable basis. Nothing herein is intended or shall be deemed to constitute any admission of liability.
+
+========================================================================
+https://raw.githubusercontent.com/argparse4j/argparse4j/argparse4j-0.7.0/LICENSE.txt
+========================================================================
+
+/*
+ * Copyright (C) 2011, 2014, 2015 Tatsuhiro Tsujikawa
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+========================================================================
+https://raw.githubusercontent.com/ronmamo/reflections/0.9.12/COPYING.txt
+https://raw.githubusercontent.com/ronmamo/reflections/0.10.2/COPYING.txt
+========================================================================
+
+ DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
+ Version 2, December 2004
+
+ Copyright (C) 2004 Sam Hocevar
+
+ Everyone is permitted to copy and distribute verbatim or modified
+ copies of this license document, and changing it is allowed as long
+ as the name is changed.
+
+ DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. You just DO WHAT THE FUCK YOU WANT TO.
+
+
+========================================================================
+https://raw.githubusercontent.com/luben/zstd-jni/v1.5.0-2/src/main/native/LICENSE
+https://raw.githubusercontent.com/luben/zstd-jni/v1.5.0-4/src/main/native/LICENSE
+========================================================================
+
+BSD License
+
+For Zstandard software
+
+Copyright (c) 2016-present, Facebook, Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+ * Neither the name Facebook nor the names of its contributors may be used to
+ endorse or promote products derived from this software without specific
+ prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/hugegraph-dist/scripts/dependency/known-dependencies.txt b/hugegraph-dist/scripts/dependency/known-dependencies.txt
index 1e8dce0c0..e7e022733 100644
--- a/hugegraph-dist/scripts/dependency/known-dependencies.txt
+++ b/hugegraph-dist/scripts/dependency/known-dependencies.txt
@@ -1,8 +1,10 @@
+HikariCP-4.0.3.jar
HikariCP-6.3.3.jar
HikariCP-java7-2.4.12.jar
ST4-4.0.4.jar
accessors-smart-2.4.2.jar
accessors-smart-2.5.2.jar
+activation-1.1.1.jar
aircompressor-0.10.jar
aircompressor-0.21.jar
angus-activation-2.0.3.jar
@@ -15,11 +17,14 @@ ansj_seg-5.1.6.jar
ant-1.9.1.jar
ant-launcher-1.9.1.jar
antlr-runtime-3.5.2.jar
+antlr4-runtime-4.8.jar
antlr4-runtime-4.9.1.jar
aopalliance-1.0.jar
+aopalliance-repackaged-2.6.1.jar
aopalliance-repackaged-3.0.6.jar
apache-curator-2.12.0.pom
apiguardian-api-1.1.2.jar
+argparse4j-0.7.0.jar
arrow-format-0.8.0.jar
arrow-format-2.0.0.jar
arrow-memory-0.8.0.jar
@@ -67,6 +72,16 @@ commons-math3-3.4.1.jar
commons-net-3.9.0.jar
commons-pool-1.6.jar
commons-text-1.10.0.jar
+connect-api-3.2.0.jar
+connect-api-3.9.2.jar
+connect-file-3.2.0.jar
+connect-file-3.9.2.jar
+connect-json-3.2.0.jar
+connect-json-3.9.2.jar
+connect-runtime-3.2.0.jar
+connect-runtime-3.9.2.jar
+connect-transforms-3.2.0.jar
+connect-transforms-3.9.2.jar
cucumber-core-7.21.1.jar
cucumber-expressions-18.0.1.jar
cucumber-gherkin-7.21.1.jar
@@ -82,6 +97,11 @@ curator-recipes-2.13.0.jar
curator-recipes-4.2.0.jar
datanucleus-core-4.1.17.jar
datatable-7.21.1.jar
+debezium-api-1.9.8.Final.jar
+debezium-connector-mysql-1.9.8.Final.jar
+debezium-core-1.9.8.Final.jar
+debezium-ddl-parser-1.9.8.Final.jar
+debezium-embedded-1.9.8.Final.jar
disruptor-3.3.6.jar
disruptor-3.3.7.jar
dnsjava-2.1.7.jar
@@ -94,7 +114,7 @@ elasticsearch-java-7.16.1.jar
elasticsearch-rest-client-7.16.1.jar
error_prone_annotations-2.3.3.jar
error_prone_annotations-2.3.4.jar
-esri-geometry-api-2.0.0.jar
+esri-geometry-api-2.2.0.jar
exp4j-0.4.8.jar
failsafe-2.4.1.jar
failureaccess-1.0.1.jar
@@ -102,6 +122,13 @@ fastutil-8.5.9.jar
findbugs-annotations-1.3.9-1.jar
flatbuffers-1.2.0-3f79e055.jar
flatbuffers-java-1.9.0.jar
+flink-cdc-common-3.6.0-2.2.jar
+flink-cdc-flink2-compat-3.6.0-2.2.jar
+flink-cdc-runtime-3.6.0-2.2.jar
+flink-connector-debezium-3.6.0-2.2.jar
+flink-connector-mysql-cdc-3.6.0-2.2.jar
+flink-shaded-guava-31.1-jre-17.0.jar
+flink-shaded-guava-33.4.0-jre-20.0.jar
fst-2.50.jar
fury-core-0.9.0.jar
generex-1.0.2.jar
@@ -199,8 +226,11 @@ hive-storage-api-2.7.0.jar
hive-storage-api-2.7.2.jar
hive-upgrade-acid-3.1.3.jar
hive-vector-code-gen-3.1.3.jar
+hk2-api-2.6.1.jar
hk2-api-3.0.6.jar
+hk2-locator-2.6.1.jar
hk2-locator-3.0.6.jar
+hk2-utils-2.6.1.jar
hk2-utils-3.0.6.jar
hppc-0.7.1.jar
hppc-0.7.2.jar
@@ -232,7 +262,9 @@ jackson-databind-2.12.3.jar
jackson-databind-2.15.2.jar
jackson-databind-2.21.4.jar
jackson-dataformat-yaml-2.21.4.jar
+jackson-datatype-jdk8-2.12.3.jar
jackson-datatype-jdk8-2.21.4.jar
+jackson-datatype-jsr310-2.12.3.jar
jackson-datatype-jsr310-2.21.4.jar
jackson-jaxrs-1.9.2.jar
jackson-jaxrs-base-2.12.3.jar
@@ -242,6 +274,7 @@ jackson-jaxrs-json-provider-2.12.3.jar
jackson-jaxrs-json-provider-2.15.2.jar
jackson-jaxrs-json-provider-2.21.4.jar
jackson-mapper-asl-1.9.13.jar
+jackson-module-afterburner-2.21.4.jar
jackson-module-jaxb-annotations-2.12.3.jar
jackson-module-jaxb-annotations-2.15.2.jar
jackson-module-jaxb-annotations-2.21.4.jar
@@ -252,8 +285,10 @@ jakarta.activation-api-1.2.1.jar
jakarta.activation-api-1.2.2.jar
jakarta.activation-api-2.1.4.jar
jakarta.annotation-api-2.1.1.jar
+jakarta.inject-2.6.1.jar
jakarta.inject-api-2.0.1.jar
jakarta.json-2.0.1.jar
+jakarta.ws.rs-api-2.1.6.jar
jakarta.ws.rs-api-3.1.0.jar
jakarta.xml.bind-api-2.3.2.jar
jakarta.xml.bind-api-2.3.3.jar
@@ -276,6 +311,7 @@ javax.json-1.0.jar
javax.servlet-api-3.1.0.jar
javax.servlet.jsp-2.3.2.jar
javax.servlet.jsp-api-2.3.1.jar
+javax.ws.rs-api-2.1.1.jar
javolution-5.5.1.jar
jaxb-api-2.2.11.jar
jaxb-api-2.2.12.jar
@@ -295,10 +331,13 @@ jersey-apache-connector-3.1.11.jar
jersey-client-1.19.jar
jersey-client-3.1.11.jar
jersey-common-3.1.11.jar
+jersey-container-servlet-2.34.jar
+jersey-container-servlet-3.1.11.jar
jersey-container-servlet-core-2.25.1.jar
jersey-container-servlet-core-3.1.11.jar
jersey-core-1.19.jar
jersey-guice-1.19.jar
+jersey-hk2-2.34.jar
jersey-hk2-3.1.11.jar
jersey-json-1.19.jar
jersey-server-1.19.jar
@@ -306,8 +345,12 @@ jersey-servlet-1.19.jar
jetcd-common-0.5.9.jar
jetcd-core-0.5.9.jar
jettison-1.1.jar
-jetty-client-9.4.33.v20201020.jar
+jetty-alpn-client-12.0.36.jar
+jetty-client-12.0.36.jar
jetty-client-9.4.40.v20210413.jar
+jetty-client-9.4.44.v20210927.jar
+jetty-continuation-9.4.44.v20210927.jar
+jetty-continuation-9.4.57.v20241219.jar
jetty-http-12.0.36.jar
jetty-http-9.4.40.v20210413.jar
jetty-io-12.0.36.jar
@@ -319,6 +362,8 @@ jetty-security-9.4.40.v20210413.jar
jetty-server-12.0.36.jar
jetty-server-9.4.40.v20210413.jar
jetty-servlet-9.4.40.v20210413.jar
+jetty-servlets-9.4.44.v20210927.jar
+jetty-servlets-9.4.57.v20241219.jar
jetty-util-12.0.36.jar
jetty-util-9.4.40.v20210413.jar
jetty-util-ajax-12.0.36.jar
@@ -336,6 +381,8 @@ jline-3.9.0.jar
jna-5.12.1.jar
joda-time-2.10.8.jar
joni-2.1.11.jar
+jose4j-0.7.9.jar
+jose4j-0.9.6.jar
jpam-1.1.jar
jraft-core-1.3.14.jar
jsch-0.1.55.jar
@@ -351,6 +398,9 @@ jsr305-3.0.2.jar
jsr311-api-1.1.1.jar
jul-to-slf4j-2.0.18.jar
junit-xml-formatter-0.7.1.jar
+kafka-clients-3.2.0.jar
+kafka-clients-3.9.2.jar
+kafka-tools-3.2.0.jar
kerb-admin-1.0.1.jar
kerb-admin-2.0.1.jar
kerb-client-1.0.1.jar
@@ -428,7 +478,10 @@ lucene-core-8.11.2.jar
lucene-queries-4.7.2.jar
lucene-queryparser-4.7.2.jar
lucene-sandbox-4.7.2.jar
+lz4-java-1.10.1.jar
lz4-java-1.4.0.jar
+maven-artifact-3.8.4.jar
+maven-artifact-3.9.6.jar
memory-0.9.0.jar
messages-27.2.0.jar
metrics-core-4.2.0.jar
@@ -451,6 +504,7 @@ mybatis-plus-spring-boot-autoconfigure-3.5.17.jar
mybatis-plus-spring-boot-native-image-3.5.17.jar
mybatis-plus-spring-boot3-starter-3.5.17.jar
mybatis-spring-3.0.5.jar
+mysql-binlog-connector-java-0.27.2.jar
netty-3.10.6.Final.jar
netty-all-4.1.135.Final.jar
netty-all-4.1.65.Final.jar
@@ -518,6 +572,8 @@ parboiled-core-1.1.8.jar
parboiled-core-1.2.0.jar
parquet-hadoop-bundle-1.10.0.jar
perfmark-api-0.23.0.jar
+plexus-utils-3.3.0.jar
+plexus-utils-3.5.1.jar
postgresql-42.4.1.jar
postgresql-42.7.11.jar
proto-google-common-protos-2.0.1.jar
@@ -528,6 +584,10 @@ protobuf-java-3.19.6.jar
protobuf-java-util-3.17.2.jar
query-13.6.0.jar
re2j-1.1.jar
+reflections-0.10.2.jar
+reflections-0.9.12.jar
+reload4j-1.2.19.jar
+reload4j-1.2.25.jar
sfm-converter-8.2.1.jar
sfm-csv-8.2.1.jar
sfm-map-8.2.1.jar
@@ -567,6 +627,7 @@ spring-web-6.2.19.jar
spring-webmvc-6.2.19.jar
stax-api-1.0.1.jar
stax2-api-4.2.1.jar
+swagger-annotations-2.2.8.jar
tag-expressions-6.1.2.jar
testng-xml-formatter-0.3.1.jar
threeten-extra-1.5.0.jar
@@ -585,3 +646,5 @@ xz-1.8.jar
zjsonpatch-0.3.0.jar
zookeeper-3.6.2.jar
zookeeper-jute-3.6.2.jar
+zstd-jni-1.5.0-2.jar
+zstd-jni-1.5.0-4.jar
diff --git a/hugegraph-loader/README.md b/hugegraph-loader/README.md
index 04b2d5606..44c346198 100644
--- a/hugegraph-loader/README.md
+++ b/hugegraph-loader/README.md
@@ -133,3 +133,5 @@ Server 1.7 compatibility CI uses the pinned official ASF 1.7.0 binary archive. T
## 5. License
hugegraph-loader is licensed under Apache 2.0 License.
+
+See the [Flink CDC Java 17 validation](docs/flink-cdc-java17.md) for its current limits and launcher syntax.
diff --git a/hugegraph-loader/assembly/descriptor/assembly.xml b/hugegraph-loader/assembly/descriptor/assembly.xml
index f67f1e832..5a9cf46f3 100644
--- a/hugegraph-loader/assembly/descriptor/assembly.xml
+++ b/hugegraph-loader/assembly/descriptor/assembly.xml
@@ -73,6 +73,54 @@
falseruntimefalse
+
+
+ ch.qos.reload4j:reload4j
+ com.esri.geometry:esri-geometry-api
+ com.fasterxml.jackson.datatype:jackson-datatype-jdk8
+ com.fasterxml.jackson.datatype:jackson-datatype-jsr310
+ com.fasterxml.jackson.jaxrs:jackson-jaxrs-base
+ com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider
+ com.zaxxer:HikariCP
+ com.zendesk:mysql-binlog-connector-java
+ io.debezium:debezium-api
+ io.debezium:debezium-connector-mysql
+ io.debezium:debezium-core
+ io.debezium:debezium-ddl-parser
+ io.debezium:debezium-embedded
+ jakarta.ws.rs:jakarta.ws.rs-api
+ javax.activation:activation
+ javax.ws.rs:javax.ws.rs-api
+ javax.xml.bind:jaxb-api
+ net.sourceforge.argparse4j:argparse4j
+ org.antlr:antlr4-runtime
+ org.apache.flink:flink-cdc-common
+ org.apache.flink:flink-cdc-flink2-compat
+ org.apache.flink:flink-cdc-runtime
+ org.apache.flink:flink-connector-debezium
+ org.apache.flink:flink-connector-mysql-cdc
+ org.apache.flink:flink-shaded-guava
+ org.apache.kafka:connect-api
+ org.apache.kafka:connect-file
+ org.apache.kafka:connect-json
+ org.apache.kafka:connect-runtime
+ org.apache.kafka:connect-transforms
+ org.apache.kafka:kafka-tools
+ org.apache.maven:maven-artifact
+ org.bitbucket.b_c:jose4j
+ org.codehaus.plexus:plexus-utils
+ org.eclipse.jetty:jetty-continuation
+ org.eclipse.jetty:jetty-servlets
+ org.glassfish.hk2.external:aopalliance-repackaged
+ org.glassfish.hk2.external:jakarta.inject
+ org.glassfish.hk2:hk2-api
+ org.glassfish.hk2:hk2-locator
+ org.glassfish.hk2:hk2-utils
+ org.glassfish.jersey.containers:jersey-container-servlet
+ org.glassfish.jersey.inject:jersey-hk2
+ org.reflections:reflections
+ *:*:jar:*
diff --git a/hugegraph-loader/assembly/static/bin/hugegraph-flinkcdc-loader.sh b/hugegraph-loader/assembly/static/bin/hugegraph-flinkcdc-loader.sh
index 3932ac9a4..ce812fbaf 100755
--- a/hugegraph-loader/assembly/static/bin/hugegraph-flinkcdc-loader.sh
+++ b/hugegraph-loader/assembly/static/bin/hugegraph-flinkcdc-loader.sh
@@ -18,20 +18,36 @@
set -euo pipefail
BIN_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
-APP_DIR=$(dirname "${BIN_DIR}")
-LIB_DIR=${APP_DIR}/lib
-ASSEMBLY_JAR_NAME=$(find "${LIB_DIR}" -name 'hugegraph-loader*.jar')
+APP_DIR=$(dirname "$BIN_DIR")
+if [[ -z "${FLINK_HOME:-}" || ! -x "$FLINK_HOME/bin/flink" ]]; then
+ echo "FLINK_HOME must point to a Flink installation" >&2
+ exit 1
+fi
+shopt -s nullglob
+ASSEMBLY_JARS=("$APP_DIR"/lib/*hugegraph-loader*-shaded.jar)
+if [[ ${#ASSEMBLY_JARS[@]} -ne 1 ]]; then
+ echo "Expected exactly one shaded HugeGraph Loader jar in $APP_DIR/lib" >&2
+ exit 1
+fi
-# get hugegraph_params and engine_params
-source "$BIN_DIR"/get-params.sh
-get_params "$@"
-echo "engine_params: $ENGINE_PARAMS"
-echo "hugegraph_params: $HUGEGRAPH_PARAMS"
+# Without a separator all arguments belong to Loader. To set Flink options:
+# hugegraph-flinkcdc-loader.sh -p 1 -- --file mapping.json --graph hugegraph
+ENGINE_PARAMS=()
+HUGEGRAPH_PARAMS=("$@")
+for arg in "$@"; do
+ if [[ "$arg" == "--" ]]; then
+ HUGEGRAPH_PARAMS=()
+ while [[ "$1" != "--" ]]; do
+ ENGINE_PARAMS+=("$1")
+ shift
+ done
+ shift
+ HUGEGRAPH_PARAMS=("$@")
+ break
+ fi
+done
-CMD=${FLINK_HOME}/bin/flink run \
- "${ENGINE_PARAMS}" \
+# Do not echo arguments: they can contain authentication credentials.
+exec "$FLINK_HOME/bin/flink" run ${ENGINE_PARAMS[@]+"${ENGINE_PARAMS[@]}"} \
-c org.apache.hugegraph.loader.flink.HugeGraphFlinkCDCLoader \
- "${ASSEMBLY_JAR_NAME}" "${HUGEGRAPH_PARAMS}"
-
-echo "${CMD}"
-exec "${CMD}"
+ "${ASSEMBLY_JARS[0]}" ${HUGEGRAPH_PARAMS[@]+"${HUGEGRAPH_PARAMS[@]}"}
diff --git a/hugegraph-loader/assembly/static/bin/hugegraph-loader.sh b/hugegraph-loader/assembly/static/bin/hugegraph-loader.sh
index dcb0ba0d8..fcd7fe62a 100755
--- a/hugegraph-loader/assembly/static/bin/hugegraph-loader.sh
+++ b/hugegraph-loader/assembly/static/bin/hugegraph-loader.sh
@@ -69,7 +69,8 @@ CP="$CP":$(find -L ${LIB} -name 'hugegraph*.jar' | sort | tr '\n' ':')
# Add the remaining jars in lib.
CP="$CP":$(find -L ${LIB} -name '*.jar' \
\! -name 'hugegraph*' \
- \! -name 'log4j-slf4j-impl*.jar' | sort | tr '\n' ':')
+ \! -name 'log4j-slf4j-impl*.jar' \
+ \! -name 'apache-hugegraph-loader-*-shaded.jar' | sort | tr '\n' ':')
export LOADER_CLASSPATH="${CLASSPATH:-}:$CP"
diff --git a/hugegraph-loader/assembly/travis/flink-cdc/test_verify.py b/hugegraph-loader/assembly/travis/flink-cdc/test_verify.py
new file mode 100644
index 000000000..33bd12d78
--- /dev/null
+++ b/hugegraph-loader/assembly/travis/flink-cdc/test_verify.py
@@ -0,0 +1,154 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import gzip
+import hashlib
+import io
+import json
+from pathlib import Path
+import subprocess
+import sys
+import tempfile
+from types import SimpleNamespace
+import unittest
+from unittest.mock import patch
+import xml.etree.ElementTree as ET
+
+from verify import Acceptance, GRAPHS, normalize, redact, request, set_property, validate_candidate_sdk, validate_jdbc_driver
+
+
+class AcceptanceHelpersTest(unittest.TestCase):
+ def test_identity_api_probe_uses_owned_graph_port_and_isolated_repository(self):
+ with tempfile.TemporaryDirectory() as directory:
+ args = SimpleNamespace(work_root=directory, evidence=directory + "/evidence",
+ server_sha="server", toolchain_sha="toolchain", maven_repo=directory + "/m2")
+ acceptance = Acceptance(args)
+ suite = ET.Element("testsuite", tests="1", failures="0", errors="0", skipped="0")
+ ET.SubElement(suite, "testcase", name="testIdentityChangesDeletesAndReplay")
+ with patch.object(acceptance, "run", return_value="probe output") as run, \
+ patch("verify.ET.parse", return_value=ET.ElementTree(suite)) as report:
+ acceptance.verify_identity_api()
+ command = run.call_args.args[0]
+ self.assertEqual(command[:3], ["mvn", "-o", "test"])
+ self.assertEqual(command[command.index("-pl") + 1], "hugegraph-client,hugegraph-loader")
+ self.assertIn("-Dtest=HugeGraphIdentityIntegrationTest", command)
+ self.assertIn("-Dmaven.repo.local=" + str(Path(args.maven_repo).resolve()), command)
+ self.assertIn("-Dflink.identity.integration=true", command)
+ self.assertIn("-Dflink.identity.port=38080", command)
+ self.assertIn("-Dflink.identity.graph=" + GRAPHS[0], command)
+ self.assertIn(acceptance.prefix + "-identity.xml", str(report.call_args.args[0]))
+ self.assertEqual(acceptance.report["phases"]["identity_api_probe"]["counts"]["skipped"], 0)
+
+ def test_identity_api_probe_rejects_skipped_missing_or_failed_test(self):
+ with tempfile.TemporaryDirectory() as directory:
+ args = SimpleNamespace(work_root=directory, evidence=directory + "/evidence",
+ server_sha="server", toolchain_sha="toolchain", maven_repo=directory + "/m2")
+ acceptance = Acceptance(args)
+ for counts, name in (({"tests": "1", "skipped": "1"}, "testIdentityChangesDeletesAndReplay"),
+ ({"tests": "0"}, "other"),
+ ({"tests": "1", "failures": "1"}, "testIdentityChangesDeletesAndReplay"),
+ ({"tests": "1", "errors": "1"}, "testIdentityChangesDeletesAndReplay"),
+ ({"tests": "1"}, "other")):
+ with self.subTest(counts=counts, name=name):
+ suite = ET.Element("testsuite", **counts)
+ ET.SubElement(suite, "testcase", name=name)
+ with patch.object(acceptance, "run", return_value="probe output"), \
+ patch("verify.ET.parse", return_value=ET.ElementTree(suite)), \
+ self.assertRaisesRegex(AssertionError, "without failures or skips"):
+ acceptance.verify_identity_api()
+ self.assertNotIn("identity_api_probe", acceptance.report["phases"])
+
+ def test_identity_api_failure_stops_before_starting_flink_mysql(self):
+ with tempfile.TemporaryDirectory() as directory:
+ archive = Path(directory) / "server.tar.gz"
+ archive.write_bytes(b"test-only-archive")
+ manifest = Path(directory) / "candidate-sdk.json"
+ manifest.write_text(json.dumps({"commit": "server"}))
+ args = SimpleNamespace(work_root=directory, evidence=directory + "/evidence",
+ server_sha="server", toolchain_sha="toolchain", sdk_manifest=str(manifest),
+ server_archive=str(archive), jdbc_driver="driver",
+ server_archive_sha256=hashlib.sha256(archive.read_bytes()).hexdigest())
+ acceptance = Acceptance(args)
+ with patch("verify.validate_jdbc_driver", return_value={}), patch("verify.socket.socket"), \
+ patch.object(acceptance, "start_server") as server, \
+ patch.object(acceptance, "verify_identity_api", side_effect=RuntimeError("API probe failed")), \
+ patch.object(acceptance, "start_flink_mysql") as engine, \
+ self.assertRaisesRegex(RuntimeError, "API probe failed"):
+ acceptance.verify()
+ server.assert_called_once_with()
+ engine.assert_not_called()
+
+ def test_null_removal_is_distinct_from_literal_null_and_stale_value(self):
+ source = normalize([{"id": 2, "name": "two", "age": 22, "city": None}])
+ empty = normalize([{"id": 2, "properties": {"name": "two", "age": 22}}], graph=True)
+ self.assertEqual(source, empty)
+ for value in ("null", "old-city"):
+ stale = normalize([{"id": 2, "properties": {"name": "two", "age": 22, "city": value}}], graph=True)
+ self.assertNotEqual(source, stale)
+
+ def test_duplicate_ids_are_not_hidden_by_dictionary_conversion(self):
+ with self.assertRaises(AssertionError):
+ normalize([{"id": 1}, {"id": 1}])
+
+ def test_graph_id_type_and_unexpected_properties_fail(self):
+ with self.assertRaises(AssertionError):
+ normalize([{"id": "1", "properties": {}}], graph=True)
+ with self.assertRaises(AssertionError):
+ normalize([{"id": 1, "properties": {"extra": "unexpected"}}], graph=True)
+
+ def test_decodes_compressed_graph_api_response(self):
+ response = io.BytesIO(gzip.compress(b'{"vertices": []}'))
+ response.headers = {"Content-Encoding": "gzip"}
+ with patch("verify.urllib.request.urlopen", return_value=response):
+ self.assertEqual(request("http://fixture/vertices"), {"vertices": []})
+
+ def test_redacts_credential_fields(self):
+ self.assertNotIn("fake-value", redact('password=fake-value token:fake-value "token":"fake-value"'))
+
+ def test_property_replacement_does_not_create_multiple_active_values(self):
+ with tempfile.TemporaryDirectory() as directory:
+ path = Path(directory) / "server.properties"
+ path.write_text("#store=example\nstore=old\n")
+ set_property(path, "store", "new")
+ set_property(path, "port", "38080")
+ self.assertEqual(path.read_text().count("store=new"), 1)
+ self.assertNotIn("store=old", path.read_text())
+ self.assertIn("port=38080", path.read_text())
+
+ def test_rejects_wrong_external_driver_before_runtime_start(self):
+ with tempfile.TemporaryDirectory() as directory:
+ path = Path(directory) / "driver.jar"
+ path.write_bytes(b"wrong-driver")
+ with self.assertRaisesRegex(AssertionError, "pinned mysql:mysql-connector-java:8.0.28"):
+ validate_jdbc_driver(path)
+
+ def test_rejects_sdk_manifest_from_another_server_source(self):
+ with tempfile.TemporaryDirectory() as directory:
+ path = Path(directory) / "candidate-sdk.json"
+ selected = "a" * 40
+ path.write_text(json.dumps({"commit": "b" * 40}))
+ with self.assertRaisesRegex(AssertionError, "selected server source"):
+ validate_candidate_sdk(path, selected)
+ path.write_text(json.dumps({"commit": selected}))
+ self.assertEqual(validate_candidate_sdk(path, selected)["commit"], selected)
+
+ def test_default_mode_does_not_start_or_require_docker(self):
+ script = Path(__file__).with_name("verify.py")
+ output = subprocess.check_output([sys.executable, str(script)], env={"PATH": ""}, text=True)
+ self.assertEqual(json.loads(output)["mode"], "plan")
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/hugegraph-loader/assembly/travis/flink-cdc/verify.py b/hugegraph-loader/assembly/travis/flink-cdc/verify.py
new file mode 100644
index 000000000..9e6c27c2f
--- /dev/null
+++ b/hugegraph-loader/assembly/travis/flink-cdc/verify.py
@@ -0,0 +1,475 @@
+#!/usr/bin/env python3
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Linux-only, test-owned Flink/MySQL/server acceptance. Default is a no-I/O plan."""
+
+import argparse
+import gzip
+import hashlib
+import json
+import os
+from pathlib import Path
+import re
+import shutil
+import signal
+import socket
+import subprocess
+import sys
+import tempfile
+import time
+import urllib.request
+from urllib.parse import urlsplit
+import uuid
+import xml.etree.ElementTree as ET
+
+FLINK_IMAGE = "flink:2.2.1-java17"
+MYSQL_IMAGE = "mysql:8.0.44"
+JDBC_COORDINATE = "mysql:mysql-connector-java:8.0.28"
+JDBC_SHA256 = "a00ccdf537ff50e50067b989108c2235197ffb65e197149bbb669db843cd1c3e"
+GRAPHS = ("flink_initial", "flink_rebuild")
+SERVER_URL = "http://127.0.0.1:38080"
+FLINK_URL = "http://127.0.0.1:18081"
+
+
+def validate_jdbc_driver(path):
+ digest = hashlib.sha256(Path(path).read_bytes()).hexdigest()
+ if digest != JDBC_SHA256:
+ raise AssertionError("External test JDBC driver does not match pinned " + JDBC_COORDINATE)
+ return {"coordinate": JDBC_COORDINATE, "sha256": digest, "distribution": "external-test-runtime-only"}
+
+
+def validate_candidate_sdk(path, source_commit):
+ manifest = json.loads(Path(path).read_text())
+ if manifest.get("commit") != source_commit:
+ raise AssertionError("Candidate SDK manifest does not match the selected server source")
+ return manifest
+
+
+def redact(text):
+ return re.sub(r"(?i)((?:password|token|authorization)[\"']?\s*[=:]\s*)[^\s,;]+",
+ r"\1[REDACTED]", text)
+
+
+def normalize(rows, graph=False):
+ result = {}
+ for row in rows:
+ if graph and type(row["id"]) is not int:
+ raise AssertionError("Expected numeric graph ID")
+ key = str(row["id"])
+ if key in result:
+ raise AssertionError("Duplicate graph/source ID: " + key)
+ props = row["properties"] if graph else row
+ if graph and set(props) - {"name", "age", "city"}:
+ raise AssertionError("Unexpected graph properties")
+ result[key] = {name: props.get(name) for name in ("name", "age", "city")}
+ return result
+
+
+def request(url, body=None):
+ data = None if body is None else json.dumps(body).encode()
+ req = urllib.request.Request(url, data=data, headers={"Content-Type": "application/json"})
+ with urllib.request.urlopen(req, timeout=10) as response:
+ body = response.read()
+ if response.headers.get("Content-Encoding", "").lower() == "gzip":
+ body = gzip.decompress(body)
+ return json.loads(body)
+
+
+def wait_for(description, probe, seconds=180):
+ deadline = time.monotonic() + seconds
+ last_error = "condition not satisfied"
+ while time.monotonic() < deadline:
+ try:
+ value = probe()
+ if value:
+ return value
+ except (OSError, ValueError, RuntimeError) as error:
+ last_error = str(error)
+ time.sleep(1)
+ raise TimeoutError(description + ": " + redact(last_error))
+
+
+def set_property(path, key, value):
+ text = path.read_text()
+ pattern = re.compile(r"^\s*" + re.escape(key) + r"\s*=.*$", re.MULTILINE)
+ entry = key + "=" + value
+ path.write_text(pattern.sub(entry, text) if pattern.search(text) else text + "\n" + entry + "\n")
+
+
+class Acceptance:
+ def __init__(self, args):
+ self.args = args
+ self.work = Path(tempfile.mkdtemp(prefix="hg-flink-", dir=args.work_root))
+ self.evidence = Path(args.evidence).resolve()
+ self.evidence.mkdir(parents=True, exist_ok=True)
+ self.prefix = "hg-flink-" + uuid.uuid4().hex[:10]
+ self.containers = []
+ self.server_process = None
+ self.server_log = None
+ self.report = {"status": "running", "phases": {}, "server_sha": args.server_sha,
+ "toolchain_sha": args.toolchain_sha, "work": str(self.work)}
+
+ def run(self, command, *, cwd=None, env=None, timeout=300, input_text=None):
+ result = subprocess.run(command, cwd=cwd, env=env, input=input_text,
+ text=True, capture_output=True, timeout=timeout)
+ if result.returncode:
+ raise RuntimeError(f"{command[0]} exited {result.returncode}: " +
+ redact((result.stdout + result.stderr)[-6000:]))
+ return result.stdout
+
+ def save(self, name, value):
+ (self.evidence / (name + ".json")).write_text(json.dumps(value, indent=2) + "\n")
+
+ def phase(self, name, value):
+ self.report["phases"][name] = value
+ self.save("result", self.report)
+
+ def start_server(self):
+ extracted = self.work / "server"
+ extracted.mkdir()
+ self.run(["tar", "xzf", str(Path(self.args.server_archive).resolve()), "-C", str(extracted)])
+ roots = list(extracted.glob("apache-hugegraph-*/"))
+ if len(roots) != 1:
+ raise AssertionError("Expected one server distribution")
+ self.server = roots[0]
+ conf = self.server / "conf"
+ template = (conf / "graphs/hugegraph.properties").read_text()
+ # This is a newly extracted distribution inside this run's private directory.
+ for old in (conf / "graphs").glob("*.properties"):
+ old.unlink()
+ for graph in GRAPHS:
+ props = conf / "graphs" / (graph + ".properties")
+ props.write_text(template)
+ set_property(props, "store", graph)
+ set_property(props, "backend", "rocksdb")
+ set_property(props, "rocksdb.data_path", str(self.work / (graph + "-data")))
+ set_property(props, "rocksdb.wal_path", str(self.work / (graph + "-wal")))
+ set_property(conf / "rest-server.properties", "restserver.url", SERVER_URL)
+ set_property(conf / "rest-server.properties", "gremlinserver.url", "http://127.0.0.1:18182")
+ yaml = conf / "gremlin-server.yaml"
+ yaml.write_text(re.sub(r"^#?port:.*$", "port: 18182", yaml.read_text(), flags=re.MULTILINE))
+ env = dict(os.environ, JAVA_TOOL_OPTIONS="-Xmx512m -XX:ActiveProcessorCount=2")
+ self.run(["bash", "bin/init-store.sh"], cwd=self.server, env=env)
+ self.server_log = (self.work / "server-console.log").open("w")
+ self.server_process = subprocess.Popen(
+ ["bash", "bin/start-hugegraph.sh", "-d", "false", "-j",
+ "-Xms256m -Xmx512m -XX:ActiveProcessorCount=2", "-t", "120"],
+ cwd=self.server, env=env, stdout=self.server_log, stderr=subprocess.STDOUT,
+ start_new_session=True)
+ wait_for("server graphs", lambda: request(self.graph_url(GRAPHS[0]) + "/schema/propertykeys"))
+ for graph in GRAPHS:
+ url = self.graph_url(graph) + "/schema/"
+ for name, kind in (("name", "TEXT"), ("age", "INT"), ("city", "TEXT")):
+ request(url + "propertykeys", {"name": name, "data_type": kind, "cardinality": "SINGLE"})
+ request(url + "vertexlabels", {"name": "cdc_person", "id_strategy": "CUSTOMIZE_NUMBER",
+ "properties": ["name", "age", "city"], "nullable_keys": ["city"]})
+ self.phase("server_ready", {"graphs": GRAPHS, "java": self.java_version()})
+
+ @staticmethod
+ def java_version():
+ result = subprocess.run([str(Path(os.environ["JAVA_HOME"]) / "bin/java"), "-version"],
+ text=True, capture_output=True, check=True)
+ if not re.search(r'version "17[.\"]', result.stderr):
+ raise AssertionError("Server JVM must be Java 17")
+ return result.stderr.strip()
+
+ @staticmethod
+ def graph_url(graph):
+ return SERVER_URL + "/graphspaces/DEFAULT/graphs/" + graph
+
+ def verify_identity_api(self):
+ # Use only the freshly extracted Server owned by this acceptance run.
+ root = Path(__file__).resolve().parents[4]
+ suffix = self.prefix + "-identity"
+ port = urlsplit(SERVER_URL).port
+ test = "org.apache.hugegraph.loader.flink.HugeGraphIdentityIntegrationTest"
+ output = self.run(["mvn", "-o", "test", "-pl", "hugegraph-client,hugegraph-loader", "-am", "-ntp",
+ "-Dmaven.repo.local=" + str(Path(self.args.maven_repo).resolve()),
+ "-Dtest=HugeGraphIdentityIntegrationTest", "-Dsurefire.failIfNoSpecifiedTests=false",
+ "-DforkCount=0", "-Dflink.identity.integration=true",
+ "-Dflink.identity.port=" + str(port), "-Dflink.identity.graph=" + GRAPHS[0],
+ "-Dsurefire.reportNameSuffix=" + suffix], cwd=root, timeout=300)
+ (self.evidence / "identity-api-maven.log").write_text(redact(output[-60000:]))
+ report = root / "hugegraph-loader/target/surefire-reports" / ("TEST-" + test + "-" + suffix + ".xml")
+ suite = ET.parse(report).getroot()
+ counts = {name: int(suite.get(name, "0")) for name in ("tests", "failures", "errors", "skipped")}
+ if counts["tests"] != 1 or any(counts[name] for name in ("failures", "errors", "skipped")) or \
+ not any(case.get("name") == "testIdentityChangesDeletesAndReplay" for case in suite.findall("testcase")):
+ raise AssertionError("Identity API probe must execute its test without failures or skips")
+ self.phase("identity_api_probe", {"test": test, "counts": counts, "port": port, "graph": GRAPHS[0],
+ "report_suffix": suffix, "scope": "SourceRecord-to-Graph-API; not Flink edge E2E"})
+
+ def container(self, suffix, image, arguments, environment, mounts=(), memory="1200m"):
+ name = self.prefix + "-" + suffix
+ command = ["docker", "run", "-d", "--name", name, "--network", "host", "--memory", memory,
+ "--label", "hugegraph.flink.acceptance=" + self.prefix]
+ for key, value in environment.items():
+ command.extend(["-e", key + "=" + value])
+ for mount in mounts:
+ command.extend(["-v", mount])
+ # Record first so a partially successful Docker invocation is still cleaned up.
+ self.containers.append(name)
+ self.run(command + [image] + arguments)
+ return name
+
+ def mysql(self, sql):
+ return self.run(["docker", "exec", "-i", self.mysql_name, "mysql", "-uroot", "--protocol=TCP",
+ "-h127.0.0.1", "-P13306", "--raw", "--batch", "--skip-column-names"],
+ input_text=sql)
+
+ def start_flink_mysql(self):
+ for image in (FLINK_IMAGE, MYSQL_IMAGE):
+ self.run(["docker", "pull", image], timeout=600)
+ self.save("images", {image: json.loads(self.run(["docker", "image", "inspect", image,
+ "--format", "{{json .RepoDigests}} "])) for image in (FLINK_IMAGE, MYSQL_IMAGE)})
+ self.mysql_name = self.container("mysql", MYSQL_IMAGE,
+ ["--port=13306", "--bind-address=127.0.0.1", "--server-id=1", "--log-bin=mysql-bin",
+ "--binlog-format=ROW", "--binlog-row-image=FULL"], {"MYSQL_ALLOW_EMPTY_PASSWORD": "yes"},
+ memory="768m")
+ wait_for("MySQL ready", lambda: self.mysql("SELECT 1;"))
+ settings = self.mysql("SELECT @@version, @@binlog_format, @@binlog_row_image, @@log_bin;").strip()
+ self.save("mysql-binlog", {"settings": settings})
+ if settings.split("\t")[-3:] != ["ROW", "FULL", "1"]:
+ raise AssertionError("MySQL must have ROW/FULL binary logging enabled")
+ self.mysql("CREATE DATABASE flink_fixture; CREATE TABLE flink_fixture.person "
+ "(id INT PRIMARY KEY, name VARCHAR(50) NOT NULL, age INT NOT NULL, city VARCHAR(50)); "
+ "INSERT INTO flink_fixture.person VALUES (1,'one',21,'a'),(2,'two',22,'b'),(3,'three',23,NULL);")
+ job = self.work / "job"
+ (job / "lib").mkdir(parents=True)
+ (job / "bin").mkdir()
+ jar = Path(self.args.job_jar).resolve()
+ destination = job / "lib" / jar.name
+ try:
+ os.link(jar, destination)
+ except OSError:
+ shutil.copyfile(jar, destination)
+ with jar.open("rb") as stream:
+ self.report["job_sha256"] = hashlib.file_digest(stream, "sha256").hexdigest()
+ shutil.copyfile(Path(__file__).parents[2] / "static/bin/hugegraph-flinkcdc-loader.sh",
+ job / "bin/hugegraph-flinkcdc-loader.sh")
+ mapping = {"version": "2.0", "structs": [{"id": "mysql", "input": {
+ "type": "jdbc", "vendor": "mysql", "url": "jdbc:mysql://127.0.0.1:13306",
+ "database": "flink_fixture", "table": "person", "username": "root", "password": "",
+ "header": ["id", "name", "age", "city"]}, "vertices": [{"label": "cdc_person", "id": "id",
+ "update_strategies": {"name": "OVERRIDE", "age": "OVERRIDE", "city": "OVERRIDE"}}]}]}
+ (job / "mapping.json").write_text(json.dumps(mapping))
+ checkpoints = self.work / "checkpoints"
+ checkpoints.mkdir(mode=0o777)
+ checkpoints.chmod(0o777)
+ config = "\n".join(["jobmanager.rpc.address: 127.0.0.1", "jobmanager.rpc.port: 16123",
+ "rest.address: 127.0.0.1", "rest.bind-address: 127.0.0.1", "rest.port: 18081",
+ "blob.server.port: 16124", "taskmanager.bind-host: 127.0.0.1", "taskmanager.host: 127.0.0.1",
+ "taskmanager.rpc.port: 16122", "taskmanager.numberOfTaskSlots: 1", "parallelism.default: 1",
+ "jobmanager.memory.process.size: 1024m", "taskmanager.memory.process.size: 1536m",
+ "state.checkpoints.dir: file:///checkpoints", "state.savepoints.dir: file:///checkpoints",
+ "execution.checkpointing.externalized-checkpoint-retention: RETAIN_ON_CANCELLATION",
+ "restart-strategy.type: fixed-delay", "restart-strategy.fixed-delay.attempts: 5",
+ "restart-strategy.fixed-delay.delay: 1 s"])
+ environment = {"FLINK_PROPERTIES": config, "JAVA_TOOL_OPTIONS": "-XX:ActiveProcessorCount=2"}
+ shared = [str(checkpoints) + ":/checkpoints",
+ str(Path(self.args.jdbc_driver).resolve()) + ":/opt/flink/lib/mysql-connector-java-8.0.28.jar:ro"]
+ self.jm = self.container("jm", FLINK_IMAGE, ["jobmanager"], environment,
+ shared + [str(job) + ":/job:ro"])
+ # Worker deliberately has no mapping-file or Loader JAR mount: Flink must ship the job.
+ self.tm = self.container("tm", FLINK_IMAGE, ["taskmanager"], environment, shared, memory="1800m")
+ wait_for("TaskManager registered", lambda: request(FLINK_URL + "/taskmanagers")["taskmanagers"])
+ for name in (self.jm, self.tm):
+ result = self.run(["docker", "exec", name, "sh", "-c", "java -version 2>&1"])
+ if not re.search(r'version "17[.\"]', result):
+ raise AssertionError("Flink JVM is not Java 17")
+ self.phase(name.rsplit("-", 1)[-1] + "_java", result.strip())
+
+ def submit(self, graph, savepoint=None):
+ options = ["-d", "-p", "1"]
+ if savepoint:
+ options += ["-s", savepoint]
+ output = self.run(["docker", "exec", self.jm, "bash", "/job/bin/hugegraph-flinkcdc-loader.sh"] +
+ options + ["--", "--file", "/job/mapping.json", "--graph", graph, "--host", "127.0.0.1",
+ "--port", "38080", "--cdc-sink-parallelism", "1", "--cdc-flush-interval", "0"])
+ match = re.search(r"JobID\s+([0-9a-f]{32})", output)
+ if not match:
+ raise AssertionError("Flink did not return a submitted JobID: " + redact(output))
+ job = match.group(1)
+ def running():
+ state = request(FLINK_URL + "/jobs/" + job)["state"]
+ if state in ("FAILED", "CANCELED", "FINISHED"):
+ raise AssertionError("CDC job unexpectedly " + state)
+ return state == "RUNNING"
+ wait_for("job running", running)
+ self.save("job-" + job, request(FLINK_URL + "/jobs/" + job))
+ return job
+
+ def source_rows(self):
+ lines = self.mysql("SELECT JSON_OBJECT('id',id,'name',name,'age',age,'city',city) "
+ "FROM flink_fixture.person ORDER BY id;").splitlines()
+ return normalize([json.loads(line) for line in lines])
+
+ def graph_rows(self, graph):
+ data = request(self.graph_url(graph) + "/graph/vertices?label=cdc_person&limit=100")
+ return normalize(data["vertices"], graph=True)
+
+ def compare(self, phase, graph, job):
+ expected = self.source_rows()
+ observed = {}
+
+ def probe():
+ nonlocal observed
+ state = request(FLINK_URL + "/jobs/" + job)["state"]
+ if state in ("FAILED", "CANCELED", "FINISHED"):
+ raise AssertionError("CDC job unexpectedly " + state)
+ observed = self.graph_rows(graph)
+ return observed == expected
+
+ try:
+ wait_for(phase, probe)
+ finally:
+ self.save(phase + "-rows", {"expected": expected, "actual": observed, "job": job})
+ self.phase(phase, {"job": job, "rows": len(expected)})
+
+ def checkpoint(self, job, after=-1):
+ def probe():
+ state = request(FLINK_URL + "/jobs/" + job + "/checkpoints")
+ completed = state.get("latest", {}).get("completed")
+ return completed if completed and completed["id"] > after else None
+ return wait_for("completed checkpoint", probe)
+
+ def cancel(self, job):
+ self.run(["docker", "exec", self.jm, "/opt/flink/bin/flink", "cancel", job])
+ wait_for("job canceled", lambda: request(FLINK_URL + "/jobs/" + job)["state"] == "CANCELED")
+
+ def verify(self):
+ self.report["external_jdbc_driver"] = validate_jdbc_driver(self.args.jdbc_driver)
+ manifest = validate_candidate_sdk(self.args.sdk_manifest, self.args.server_sha)
+ self.save("candidate-sdk", manifest)
+ with Path(self.args.sdk_manifest).open("rb") as stream:
+ self.report["sdk_manifest_sha256"] = hashlib.file_digest(stream, "sha256").hexdigest()
+ with Path(self.args.server_archive).open("rb") as stream:
+ self.report["server_archive_sha256"] = hashlib.file_digest(stream, "sha256").hexdigest()
+ if self.report["server_archive_sha256"] != self.args.server_archive_sha256:
+ raise AssertionError("Server archive does not match the same-source SDK action output")
+ for port in (38080, 18182, 13306, 18081, 16123, 16124, 16122):
+ with socket.socket() as probe:
+ probe.bind(("127.0.0.1", port))
+ self.start_server()
+ self.verify_identity_api()
+ self.start_flink_mysql()
+ job = self.submit(GRAPHS[0])
+ self.compare("snapshot", GRAPHS[0], job)
+ checkpoint = self.checkpoint(job)
+ savepoint_output = self.run(["docker", "exec", self.jm, "/opt/flink/bin/flink", "savepoint", job,
+ "file:///checkpoints"])
+ match = re.search(r"(file:/+checkpoints/savepoint-[^\s]+)", savepoint_output)
+ if not match:
+ raise AssertionError("Flink did not return a savepoint path")
+ savepoint = match.group(1).rstrip(".")
+ self.phase("snapshot_checkpoint", {"checkpoint": checkpoint, "savepoint": savepoint})
+ self.mysql("INSERT INTO flink_fixture.person VALUES(4,'four',24,'d'); "
+ "UPDATE flink_fixture.person SET name='updated',age=42,city=NULL WHERE id=2; "
+ "DELETE FROM flink_fixture.person WHERE id=1;")
+ self.compare("changes_and_null", GRAPHS[0], job)
+ previous = request(FLINK_URL + "/taskmanagers")["taskmanagers"][0]["id"]
+ self.run(["docker", "restart", self.tm])
+ self.mysql("INSERT INTO flink_fixture.person VALUES(5,'five',25,'e'); "
+ "UPDATE flink_fixture.person SET city='after-restart' WHERE id=3;")
+ registered = wait_for("replacement TaskManager", lambda: [tm for tm in
+ request(FLINK_URL + "/taskmanagers")["taskmanagers"] if tm["id"] != previous])
+ self.compare("taskmanager_restart", GRAPHS[0], job)
+ self.phase("restart_checkpoint", {"previous_taskmanager": previous, "replacement": registered,
+ "checkpoint": self.checkpoint(job, checkpoint["id"])})
+ # Debezium emits DELETE + CREATE for a changed database primary key.
+ self.mysql("UPDATE flink_fixture.person SET id=30 WHERE id=3;")
+ self.compare("primary_key_change", GRAPHS[0], job)
+ # Force deterministic replay, including an already-applied delete, from the older savepoint.
+ self.cancel(job)
+ job = self.submit(GRAPHS[0], savepoint)
+ # A new binlog marker prevents already-correct graph data from passing before replay catches up.
+ self.mysql("UPDATE flink_fixture.person SET name='after-replay' WHERE id=5;")
+ self.compare("savepoint_replay", GRAPHS[0], job)
+ self.phase("replay_checkpoint", self.checkpoint(job))
+ self.cancel(job)
+ self.mysql("DELETE FROM flink_fixture.person WHERE id=2; "
+ "INSERT INTO flink_fixture.person VALUES(6,'six',26,NULL); "
+ "UPDATE flink_fixture.person SET name='during-downtime' WHERE id=4;")
+ if self.graph_rows(GRAPHS[1]):
+ raise AssertionError("Rebuild target must start empty")
+ job = self.submit(GRAPHS[1])
+ self.compare("fresh_target_rebuild", GRAPHS[1], job)
+ self.phase("rebuild_checkpoint", self.checkpoint(job))
+ self.cancel(job)
+ self.report["status"] = "passed"
+
+ def cleanup(self):
+ for name in self.containers:
+ owner = subprocess.run(["docker", "inspect", "--format",
+ '{{index .Config.Labels "hugegraph.flink.acceptance"}}', name], text=True, capture_output=True)
+ if owner.returncode or owner.stdout.strip() != self.prefix:
+ continue
+ logs = subprocess.run(["docker", "logs", "--tail", "500", name], text=True, capture_output=True)
+ (self.evidence / (name.rsplit("-", 1)[-1] + ".log")).write_text(redact(logs.stdout + logs.stderr))
+ subprocess.run(["docker", "rm", "-f", name], capture_output=True)
+ if self.server_process and self.server_process.poll() is None:
+ os.killpg(self.server_process.pid, signal.SIGTERM)
+ try:
+ self.server_process.wait(timeout=15)
+ except subprocess.TimeoutExpired:
+ os.killpg(self.server_process.pid, signal.SIGKILL)
+ if self.server_log:
+ self.server_log.close()
+ (self.evidence / "server.log").write_text(redact((self.work / "server-console.log").read_text()[-60000:]))
+ self.save("result", self.report)
+
+
+def main():
+ parser = argparse.ArgumentParser(description=__doc__)
+ parser.add_argument("--run", action="store_true", help="start test-owned Linux runtime resources")
+ parser.add_argument("--server-archive")
+ parser.add_argument("--server-archive-sha256", help="archive hash from the same-source SDK action")
+ parser.add_argument("--sdk-manifest", help="manifest from the same-source SDK action")
+ parser.add_argument("--maven-repo", help="isolated repository from the same-source SDK action, for the API probe")
+ parser.add_argument("--job-jar")
+ parser.add_argument("--jdbc-driver", help="external mysql-connector-java 8.0.28 JAR; never bundled in the job")
+ parser.add_argument("--server-sha", default="unknown")
+ parser.add_argument("--toolchain-sha", default="unknown")
+ parser.add_argument("--work-root", default=tempfile.gettempdir())
+ parser.add_argument("--evidence", default="flink-cdc-evidence")
+ args = parser.parse_args()
+ if not args.run:
+ print(json.dumps({"mode": "plan", "images": [FLINK_IMAGE, MYSQL_IMAGE], "graphs": GRAPHS,
+ "phases": ["identity_api_probe", "snapshot", "changes_and_null", "taskmanager_restart", "primary_key_change",
+ "savepoint_replay", "fresh_target_rebuild"]}, indent=2))
+ return
+ if sys.platform != "linux" or not all((args.server_archive, args.server_archive_sha256,
+ args.sdk_manifest, args.maven_repo, args.job_jar, args.jdbc_driver)):
+ parser.error("--run requires Linux, --server-archive, --server-archive-sha256, "
+ "--sdk-manifest, --maven-repo, --job-jar and --jdbc-driver")
+ if not Path(args.maven_repo).is_dir():
+ parser.error("--maven-repo must be an existing isolated repository")
+ if not re.fullmatch(r"[0-9a-f]{64}", args.server_archive_sha256):
+ parser.error("--run requires a full server archive SHA-256")
+ if not all(re.fullmatch(r"[0-9a-fA-F]{40}", sha) for sha in (args.server_sha, args.toolchain_sha)):
+ parser.error("--run requires full server and Toolchain commit SHAs")
+ acceptance = Acceptance(args)
+ try:
+ acceptance.verify()
+ except Exception as error:
+ acceptance.report.update(status="failed", error=redact(str(error)))
+ raise
+ finally:
+ acceptance.cleanup()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/hugegraph-loader/docs/flink-cdc-java17.md b/hugegraph-loader/docs/flink-cdc-java17.md
new file mode 100644
index 000000000..1b92b3b2e
--- /dev/null
+++ b/hugegraph-loader/docs/flink-cdc-java17.md
@@ -0,0 +1,101 @@
+# Flink CDC on Java 17
+
+This integration targets Flink 2.2.1 and MySQL CDC 3.6.0-2.2 on Java 17. CI first checks rejection of vertex and edge identity changes, deletion and replay through a Graph API probe against its test-owned, same-source candidate Server. It then checks a bounded vertex job against real MySQL and Flink, covering snapshots, insert/update/NULL/delete, primary-key changes, TaskManager restart, savepoint replay and fresh-target rebuild. The API probe does not verify edge delivery through Flink/MySQL; broader mappings and edge engine coverage require separate validation. These bounded checks do not establish supported production deployment.
+
+## Building
+
+The root POM pins the Flink and CDC versions. Compile and test the adaptation with:
+
+```bash
+mvn test -pl hugegraph-loader \
+ -Dtest=FlinkSinkSerializationTest,HugeGraphSinkTest,HugeGraphDeserializationTest
+node --test hugegraph-loader/src/test/scripts/flink-launcher.test.cjs
+```
+
+Build and install the matching Java 17 Client and its server-side dependencies first. The Flink runtime remains provided by the cluster. The CDC connector is a runtime part of the job and is included in the Loader shading inputs. CI validates the packaged vertex job with the external JDBC driver and matching Client in its pinned Flink 2.2.1 Java 17 image. CDC's Flink 2 compatibility jar includes duplicate Flink API classes. The POM places the provided Flink core before CDC so compilation and tests use the public 2.2 Sink interface. The packaged class-loading probe checks that the CDC coordinator and Guava 31 load from the job JAR, while Guava 33 and Flink's `Source` API load from the cluster's `flink-dist-2.2.1.jar`. Clusters with different dependencies or class-loading settings require separate validation; passing the compile-time contract test alone is insufficient.
+
+## Launch arguments
+
+Set `FLINK_HOME` to the selected Flink installation. The submission JVM, JobManager and TaskManager must all run Java 17. Preserve the distribution's `env.java.opts.all` module-access options; do not replace them with a shorter list. The launcher selects exactly one `*hugegraph-loader*-shaded.jar` in `lib`. Spark retains the same shared job artifact path. The ordinary Loader launcher excludes this shaded job from its classpath, and the assembly omits CDC-only loose dependencies. Kafka clients and their compression dependency remain available to the ordinary Kafka reader; common dependency versions are retained for the shared shaded job.
+
+CDC rejects `--dry-run true` and `--use-prefilter true` before creating a sink or opening a client. It does not implement preview writes or ingestion deduplication. UPDATE requires explicit non-empty mapping `update_strategies` and an unchanged graph identity. Identity-changing UPDATE events fail before any mapping is buffered or written; remote failures in supported writes still require replay/recovery.
+
+Without a separator, all arguments are Loader options:
+
+```bash
+bin/hugegraph-flinkcdc-loader.sh --file mapping.json --graph hugegraph
+```
+
+Place Flink options before `--` and Loader options after it. Paths with spaces remain individual arguments; the launcher does not print argument values.
+
+```bash
+bin/hugegraph-flinkcdc-loader.sh -p 1 -- \
+ --file "my mappings/mysql.json" --graph hugegraph --cdc-sink-parallelism 1
+```
+
+Each mapping uses one source reader and one sink writer to preserve mutation order. `--cdc-sink-parallelism` defaults to 1 and other values are rejected before building the pipeline. Both operators explicitly use parallelism 1, including when Flink's global parallelism is unset or `-p 2` is supplied before `--`. This serial topology does not support parallel CDC writers. After `--`, Loader's `-p` is the HugeGraph server port, not Flink parallelism. The launcher and configuration serialization can transport other numeric values; the CDC entry point enforces the supported topology when it receives them. Both Flink submission and direct JVM invocation report invalid sink settings as failures.
+
+The submitting process validates the mapping file and Loader options. Sink configuration is serialized to workers without requiring that file to exist on each worker. The sink uses the public Sink V2 writer lifecycle. Checkpoint flush delegates to the existing graph output logic; this does not add transactional graph writes or an exactly-once guarantee.
+
+## Remaining runtime coverage
+
+Edge CDC, broader mapping configurations and custom cluster environments still need runtime acceptance beyond the bounded vertex fixture. For those cases, compare complete IDs and property values through snapshot, mutations, worker recovery and replay against real MySQL and HugeGraph instances.
+
+A fresh snapshot into an existing graph does not by itself remove graph records deleted during downtime: controlled rebuild needs an explicit fresh-target or reconciliation procedure. Do not delete user graph data as an implicit migration step.
+
+
+## Reproducible Linux acceptance
+
+The `flink-cdc-java17-ci` workflow uses the shared same-source SDK action aligned with ASF Server master. It installs the matching Common/Core/PD/Store SDK and Server distribution dependency closure in a fresh Maven repository, then packages the Loader job. Its runtime stage is separate from compilation and unit-test success.
+
+To inspect the test plan without touching Docker or starting services:
+
+```bash
+python3 hugegraph-loader/assembly/travis/flink-cdc/verify.py
+```
+
+Actual execution requires an isolated Linux host, Docker, Python 3.11+, Maven and Java 17. Provide the same-source server archive, its populated isolated Maven repository and packaged shaded Loader JAR. The API probe uses offline Maven against that repository:
+
+```bash
+python3 hugegraph-loader/assembly/travis/flink-cdc/verify.py --run \
+ --server-archive /path/to/apache-hugegraph.tar.gz \
+ --server-archive-sha256 \
+ --sdk-manifest /path/to/candidate-sdk-manifest.json \
+ --maven-repo /path/to/candidate-m2 \
+ --job-jar /path/to/hugegraph-loader-shaded.jar \
+ --jdbc-driver /path/to/mysql-connector-java-8.0.28.jar \
+ --server-sha --toolchain-sha \
+ --evidence /path/to/test-evidence
+```
+
+The harness reserves loopback ports 38080/18182 (Server), 13306 (MySQL), and 18081/16123/16124/16122 (Flink), refusing occupied ports. It creates a new Server directory and two test graphs, plus uniquely labelled containers. MySQL uses an empty test-only root password, binds to loopback, and never points at an existing database. Cleanup removes only the containers carrying this run's ownership label and stops its own server process group. Do not use production data or hosts.
+
+JobManager and TaskManager run in separate Java 17 containers. Only JobManager mounts the mapping file and packaged job; TaskManager must receive the actual job through Flink. The test compares numeric vertex IDs and all mapped properties against MySQL after snapshot, insert/update/NULL/delete, SQL primary-key changes, TaskManager restart, forced replay from an earlier savepoint, and a fresh-target rebuild after downtime. A new binlog marker after restore prevents pre-existing correct graph data from passing before replay has caught up. Successful checkpoints are required.
+
+The harness rejects a manifest from another source commit or an archive differing from the SDK action's SHA-256 before starting runtime resources. Evidence includes the SDK manifest, source commits, archive/job hashes, image digests, JVM versions, checkpoint/savepoint metadata, row comparisons and sanitized logs. A failed or unexecuted runtime stage is not compatibility evidence. The current acceptance fixture covers vertex CDC; edge CDC and broader mapping configurations still need separate runtime coverage before declaring the entire mode supported.
+
+## Identity and delete behavior
+
+UPDATE events retain both row images. Configure MySQL with `binlog_row_image=FULL`; a missing before image fails explicitly. The sink derives PRIMARY_KEY vertex IDs through the same Loader builder path used for edge endpoints. It resolves edge IDs through the public adjacency API using source, label and sort keys, then matches the target. Queries preserve literal sort-key strings and paginate when needed. An already absent element is accepted when a delete is replayed.
+
+UPDATE supports only unchanged vertex IDs, edge endpoints and sort keys, including identities added or removed by unfolded mappings. The sink checks all non-skipped mappings before any write and rejects changes with an explicit error, preserving the old elements and incident edges. Atomic identity migration is tracked in [#793](https://github.com/apache/hugegraph-toolchain/issues/793). Separate DELETE/CREATE events retain their existing non-transactional semantics; this guard does not make Debezium database primary-key changes atomic. Source-to-graph identity mappings must be unambiguous; independent rows must not compete for one graph identity.
+
+CDC converts non-null source fields to strings before Loader mapping. Configure `null_values` with string markers such as `"0"` or `""`; a numeric marker such as `0` does not match the source string `"0"`. Nullable property updates follow the Loader mapping's selected fields, ignored fields and null markers.
+
+Run the opt-in REST integration probe against a test-only Server:
+
+```bash
+mvn test -pl hugegraph-client,hugegraph-loader -am \
+ -Dtest=HugeGraphIdentityIntegrationTest -Dsurefire.failIfNoSpecifiedTests=false \
+ -Dflink.identity.integration=true -Dflink.identity.port=8080 -Dflink.identity.graph=hugegraph
+```
+
+The probe uses UUID-prefixed schemas in the selected `DEFAULT` graph (default `hugegraph`) and removes only those fixtures. CI selects `flink_initial` on its owned Server at port 38080 and requires a fresh, uniquely suffixed Surefire report with one passing test and zero skips before starting Flink/MySQL. A probe failure fails the acceptance run. It passes real Debezium `SourceRecord` objects through the deserializer and sink. It checks rejection of CUSTOMIZE_NUMBER and PRIMARY_KEY vertex identity changes, preservation of incident edges, UUID vertex delete, rejection of edge endpoint/sort-key changes, same-identity updates, duplicate replay and deletion, and deletion beyond a 100-edge query page, including a literal `P.gt(1)` sort key. This verifies the Graph API contract, not delivery through a running Flink/MySQL cluster.
+
+The candidate Server's existing edge-create API still rejects UUID endpoints when `check_vertex=true`: JSON UUID IDs arrive as strings and are looked up without converting the endpoint schema type. UUID edge ingestion is therefore not verified by this probe; this separate Server API limitation must be resolved before claiming that mapping supported.
+
+## External driver and bundled component licenses
+
+MySQL Connector/J remains an external, provided dependency. Install a compatible driver in the Flink cluster's `lib` directory on both JobManager and TaskManager; it is not included in the Loader shaded JAR or release archive. The test harness uses `mysql:mysql-connector-java:8.0.28` only as an external test-runtime file, checks its pinned SHA-256 before starting services, and mounts it read-only into both containers. CI downloads this file separately and does not upload it with the released job or evidence.
+
+The Loader's Flink CDC dependency aggregate includes [Activation 1.1.1](https://javaee.github.io/activation/) under CDDL 1.0; [HK2 2.6.1](https://eclipse-ee4j.github.io/glassfish-hk2/) (including its repackaged AOP Alliance and Jakarta Inject components), [JAX-RS APIs 2.1.1/2.1.6](https://github.com/jakartaee/rest), and [Jersey Servlet 2.34/3.1.11 and HK2 integration 2.34](https://eclipse-ee4j.github.io/jersey/) under their EPL 2.0 license option. See the distribution `LICENSE` and `NOTICE` for the full terms and additional embedded-component notices.
diff --git a/hugegraph-loader/pom.xml b/hugegraph-loader/pom.xml
index ed2d7183c..50d4ec1ca 100644
--- a/hugegraph-loader/pom.xml
+++ b/hugegraph-loader/pom.xml
@@ -118,45 +118,41 @@
-
- com.ververica
- flink-connector-mysql-cdc
- ${flinkcdc.version}
- ${flink.scope}
-
-
- org.apache.flink
- flink-java
- ${flink.version}
- ${flink.scope}
-
+
org.apache.flink
- flink-streaming-java_${scala.version}
+ flink-core${flink.version}${flink.scope}org.apache.flink
- flink-table-api-java-bridge_${scala.version}
- ${flink.version}
- ${flink.scope}
+ flink-connector-mysql-cdc
+ ${flinkcdc.version}
+
+
+
+
+ mysql
+ mysql-connector-java
+
+ org.apache.flink
- flink-table-planner-blink_${scala.version}
+ flink-streaming-java${flink.version}${flink.scope}org.apache.flink
- flink-clients_${scala.version}
+ flink-clients${flink.version}${flink.scope}org.apache.flink
- flink-runtime-web_${scala.version}
+ flink-runtime-web${flink.version}${flink.scope}
@@ -672,7 +668,7 @@
org.apache.maven.pluginsmaven-shade-plugin
- 3.4.1
+ 3.6.1package
@@ -682,6 +678,10 @@
false${final.name}-shaded
+
+
+ org.apache.flink:flink-shaded-guava:31.1-jre-17.0
+ com.google.common.base
diff --git a/hugegraph-loader/src/main/java/org/apache/hugegraph/loader/builder/EdgeBuilder.java b/hugegraph-loader/src/main/java/org/apache/hugegraph/loader/builder/EdgeBuilder.java
index 950100187..c0e64c59e 100644
--- a/hugegraph-loader/src/main/java/org/apache/hugegraph/loader/builder/EdgeBuilder.java
+++ b/hugegraph-loader/src/main/java/org/apache/hugegraph/loader/builder/EdgeBuilder.java
@@ -118,6 +118,16 @@ public List build(String[] names, Object[] values) {
return edges;
}
+ public Map identityProperties(Edge edge) {
+ Map properties = new HashMap<>();
+ for (String key : this.edgeLabel.sortKeys()) {
+ Object value = edge.property(key);
+ E.checkArgumentNotNull(value, "The edge identity requires sort key '%s'", key);
+ properties.put(key, value);
+ }
+ return properties;
+ }
+
private EdgeKVPairs newEdgeKVPairs() {
EdgeKVPairs kvPairs = new EdgeKVPairs();
kvPairs.source = this.newKVPairs(this.sourceLabel,
diff --git a/hugegraph-loader/src/main/java/org/apache/hugegraph/loader/builder/VertexBuilder.java b/hugegraph-loader/src/main/java/org/apache/hugegraph/loader/builder/VertexBuilder.java
index 8d006f368..277a66acd 100644
--- a/hugegraph-loader/src/main/java/org/apache/hugegraph/loader/builder/VertexBuilder.java
+++ b/hugegraph-loader/src/main/java/org/apache/hugegraph/loader/builder/VertexBuilder.java
@@ -56,6 +56,15 @@ public VertexMapping mapping() {
@Override
public List build(String[] names, Object[] values) {
+ return this.build(names, values, true);
+ }
+
+ // Reuse the endpoint identity path, without properties or the ingestion prefilter.
+ public List buildIdentity(String[] names, Object[] values) {
+ return this.build(names, values, false);
+ }
+
+ private List build(String[] names, Object[] values, boolean withProperty) {
VertexKVPairs kvPairs = null;
// If it's Vertex OLAP properties, VertexOlapKVPairs parsing is needed
if (this.verifyOlapVertexBuilder()) {
@@ -67,7 +76,7 @@ public List build(String[] names, Object[] values) {
kvPairs.headerCaseSensitive(this.headerCaseSensitive());
kvPairs.extractFromVertex(names, values);
- return kvPairs.buildVertices(true);
+ return kvPairs.buildVertices(withProperty);
}
@Override
diff --git a/hugegraph-loader/src/main/java/org/apache/hugegraph/loader/flink/HugeGraphDeserialization.java b/hugegraph-loader/src/main/java/org/apache/hugegraph/loader/flink/HugeGraphDeserialization.java
index 7bed7774d..2a4869c42 100644
--- a/hugegraph-loader/src/main/java/org/apache/hugegraph/loader/flink/HugeGraphDeserialization.java
+++ b/hugegraph-loader/src/main/java/org/apache/hugegraph/loader/flink/HugeGraphDeserialization.java
@@ -32,7 +32,7 @@
import org.apache.hugegraph.util.Log;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
-import com.ververica.cdc.debezium.DebeziumDeserializationSchema;
+import org.apache.flink.cdc.debezium.DebeziumDeserializationSchema;
import io.debezium.data.Envelope;
@@ -42,6 +42,10 @@ public class HugeGraphDeserialization implements DebeziumDeserializationSchema collector) {
+ // Debezium can emit a tombstone after a delete. It has no graph mutation.
+ if (sourceRecord.value() == null) {
+ return;
+ }
ObjectMapper mapper = new ObjectMapper();
ObjectNode result = mapper.createObjectNode();
@@ -62,20 +66,37 @@ public void deserialize(SourceRecord sourceRecord, Collector collector)
throw new IllegalArgumentException(
"The type of `op` should be 'c' 'r' 'u' 'd' only");
}
+ result.set(Constants.CDC_DATA, row(mapper, data));
+ if (operation == Envelope.Operation.UPDATE) {
+ Struct before = value.getStruct("before");
+ if (before == null) {
+ throw new IllegalArgumentException("CDC UPDATE requires a full before image");
+ }
+ result.set("before", row(mapper, before));
+ }
+ result.put(Constants.CDC_OP, op);
+ LOG.debug("Loaded CDC event: op={}, dataFields={}, beforeFields={}", op,
+ result.get(Constants.CDC_DATA).size(),
+ result.has("before") ? result.get("before").size() : 0);
+ collector.collect(result.toString());
+ }
+
+ private static ObjectNode row(ObjectMapper mapper, Struct data) {
ObjectNode rootNode = mapper.createObjectNode();
if (data != null) {
Schema afterSchema = data.schema();
List afterFields = afterSchema.fields();
for (Field field : afterFields) {
Object afterValue = data.get(field);
- rootNode.put(field.name(), afterValue.toString());
+ if (afterValue == null) {
+ rootNode.putNull(field.name());
+ } else {
+ rootNode.put(field.name(), afterValue.toString());
+ }
}
}
- result.set(Constants.CDC_DATA, rootNode);
- result.put(Constants.CDC_OP, op);
- LOG.debug("Loaded data: {}", result);
- collector.collect(result.toString());
+ return rootNode;
}
@Override
diff --git a/hugegraph-loader/src/main/java/org/apache/hugegraph/loader/flink/HugeGraphFlinkCDCLoader.java b/hugegraph-loader/src/main/java/org/apache/hugegraph/loader/flink/HugeGraphFlinkCDCLoader.java
index 09ccded6d..f766bc591 100644
--- a/hugegraph-loader/src/main/java/org/apache/hugegraph/loader/flink/HugeGraphFlinkCDCLoader.java
+++ b/hugegraph-loader/src/main/java/org/apache/hugegraph/loader/flink/HugeGraphFlinkCDCLoader.java
@@ -33,7 +33,8 @@
import org.slf4j.Logger;
import org.apache.hugegraph.util.Log;
-import com.ververica.cdc.connectors.mysql.source.MySqlSource;
+import org.apache.hugegraph.util.E;
+import org.apache.flink.cdc.connectors.mysql.source.MySqlSource;
public class HugeGraphFlinkCDCLoader {
@@ -45,15 +46,18 @@ public class HugeGraphFlinkCDCLoader {
public HugeGraphFlinkCDCLoader(String[] args) {
this.options = args;
this.loadOptions = LoadOptions.parseOptions(args);
+ HugeGraphOutputFormat.validateOptions(this.loadOptions);
+ E.checkArgument(this.loadOptions.sinkParallelism == 1,
+ "CDC ordered writes require --cdc-sink-parallelism 1");
}
public static void main(String[] args) {
HugeGraphFlinkCDCLoader loader;
try {
loader = new HugeGraphFlinkCDCLoader(args);
- } catch (Throwable e) {
+ } catch (RuntimeException e) {
Printer.printError("Failed to start loading", e);
- return;
+ throw e;
}
loader.load();
}
@@ -67,10 +71,7 @@ public void load() {
MySqlSource mysqlSource = buildMysqlSource(struct);
DataStreamSource source =
env.fromSource(mysqlSource, WatermarkStrategy.noWatermarks(), "MySQL Source");
-
- HugeGraphOutputFormat