You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sourcelume.apache.org by an...@apache.org on 2026/09/16 17:41:14 UTC
(sourcelume-registry) 01/01: Add Quarkus spike parallel to the Spring Boot registry-spike
This is an automated email from the ASF dual-hosted git repository.
ANierbeck pushed a commit to branch quarkus-spike
in repository https://gitbox.apache.org/repos/asf/sourcelume-registry.git
commit ef127ed02130076562bd9b7f15d0b5cc6a3b2254
Author: anierbeck <ac...@codecentric.de>
AuthorDate: Wed Sep 16 19:41:01 2026 +0200
Add Quarkus spike parallel to the Spring Boot registry-spike
A deliberately minimal slice through the Registry architecture, mirroring
Jamie's Spring Boot spike (registry-spike branch) but using Quarkus, CDI,
and a thin JDK HttpClient instead of Spring Boot and atlas-client-v2.
Built to answer the dev-list thread (Sept 2026) on Java baseline, framework,
and backend dependency — provides the Quarkus version so the two can be
compared side by side before either merges to main (per Calvin's proposed
sequencing).
Wiring proven (structurally; runtime test needs Atlas + Java 21):
1. sourcelume-spec jar resource resolvable on the classpath.
2. sourcelume-registry-typedefs JSON loadable.
3. Thin REST AtlasAdapter reaches Atlas over JDK HttpClient.
4. Atlas accepts the typedefs.
Module-by-module changes (same 4 modules as the Spring spike):
- sourcelume-registry-typedefs: unchanged (vendor-neutral).
- sourcelume-registry-common: POM only — drops spring-boot-starter-validation,
keeps jakarta.validation-api + Jackson. The DTOs and SpecResourceLoader were
already vendor-neutral and are unchanged.
- sourcelume-registry-atlas-adapter: rewritten.
* AtlasAdapter interface is now backend-neutral: exposes only Sourcelume
types + a TypeDefinitionModel holder. No AtlasTypesDef/AtlasClientV2
imports. (Per Calvin: "keep Atlas-specific types out of registry-core;
the Atlas implementation owns the typedef mapping.")
* DefaultAtlasAdapter (atlas-client-v2 based) replaced by RestAtlasAdapter:
thin JDK HttpClient + Jackson, talks to the 4 Atlas REST endpoints
directly. Pulls in NO atlas-client-v2 / atlas-intg dependency tree.
* AtlasAdapterConfiguration (Spring @Configuration) removed — CDI
@ApplicationScoped on RestAtlasAdapter replaces it.
* SourcelumeAtlasProperties: Spring @ConfigurationProperties -> SmallRye
@ConfigMapping (interface, MicroProfile Config).
- sourcelume-registry-ingest-worker: rewritten for Quarkus.
* SourcelumeWorkerApplication: @SpringBootApplication -> @QuarkusMain.
* AtlasBootstrapRunner: Spring ApplicationRunner -> CDI @Observes StartupEvent.
* AtlasHealthIndicator: Spring Actuator HealthIndicator -> SmallRye
@Readiness HealthCheck at /q/health/ready.
* AtlasBootstrapService: Spring @Service -> CDI @ApplicationScoped; uses
the backend-neutral AtlasAdapter interface.
* application.yml -> application.properties (Quarkus style).
* Dockerfile: Spring Boot fat jar -> Quarkus quarkus-app/ layout, JRE 21.
* WiringSpike: kept as a standalone manual run path.
Build/CI:
- Java baseline 21 (LTS) via <maven.compiler.release>21</maven.compiler.release>.
- Quarkus 3.33 LTS BOM replaces Spring Boot BOM.
- Surefire -javaagent for ByteBuddy configured in parent POM pluginManagement
(Calvin's note: Mockito self-attach is deprecated on 21+, will break on
future JDKs; explicit -javaagent is the stable path).
- .java-version=21 for jenv auto-switch.
docker-compose: Quarkus image name, /q/health/ready probe, single port 8082.
What is NOT changed: docs/architecture.md, docs/deployment.md (vendor-neutral
design docs), the typedefs model JSON, the common DTOs/SpecResourceLoader.
Those are the shared backbone both spikes agree on.
Apache-ai: Yes
Generated-by: pi (anthropic/claude-opus-4-1)
Reviewed-by: anierbeck
---
.java-version | 1 +
README.md | 146 +++++++----
dev-support/README.md | 26 +-
dev-support/docker-compose.yml | 17 +-
docker-compose.yml | 17 +-
pom.xml | 207 +++++-----------
sourcelume-registry-atlas-adapter/pom.xml | 81 +++---
.../registry/atlas/adapter/AtlasAdapter.java | 89 ++++---
.../atlas/adapter/DefaultAtlasAdapter.java | 192 ---------------
.../registry/atlas/adapter/RestAtlasAdapter.java | 271 +++++++++++++++++++++
.../adapter/config/AtlasAdapterConfiguration.java | 51 ----
.../adapter/config/SourcelumeAtlasProperties.java | 118 +++------
...rk.boot.autoconfigure.AutoConfiguration.imports | 1 -
.../adapter/AtlasClientCompatibilityTest.java | 52 ----
.../atlas/adapter/DefaultAtlasAdapterTest.java | 154 ------------
.../atlas/adapter/RestAtlasAdapterTest.java | 54 ++++
.../config/SourcelumeAtlasPropertiesTest.java | 51 ----
.../test/resources/atlas-application.properties | 3 -
sourcelume-registry-common/pom.xml | 54 ++--
sourcelume-registry-ingest-worker/Dockerfile | 23 +-
sourcelume-registry-ingest-worker/pom.xml | 86 +++----
.../ingest/worker/SourcelumeWorkerApplication.java | 49 ++--
.../registry/ingest/worker/WiringSpike.java | 133 +++++-----
.../worker/actuator/AtlasHealthIndicator.java | 71 +++---
.../ingest/worker/runner/AtlasBootstrapRunner.java | 53 ++--
.../worker/service/AtlasBootstrapService.java | 67 ++---
.../src/main/resources/application.properties | 35 +++
.../src/main/resources/application.yml | 42 ----
.../main/resources/atlas-application.properties | 20 --
.../worker/SourcelumeWorkerApplicationTest.java | 39 ---
.../worker/WorkerAtlasClientCompatibilityTest.java | 53 ----
.../worker/service/AtlasBootstrapServiceTest.java | 76 ------
32 files changed, 963 insertions(+), 1369 deletions(-)
diff --git a/.java-version b/.java-version
new file mode 100644
index 0000000..aabe6ec
--- /dev/null
+++ b/.java-version
@@ -0,0 +1 @@
+21
diff --git a/README.md b/README.md
index ecf6dec..0e25937 100644
--- a/README.md
+++ b/README.md
@@ -1,70 +1,126 @@
-# Apache Sourcelume Registry (`sourcelume-registry`)
+# Sourcelume Registry — Quarkus Spike
-This is a deliberately minimal slice through the Registry architecture. It does not
-implement any Sourcelume functionality (no signature verification, no schema validation,
-no queue, no REST/GraphQL). Its only job is to prove three pieces of wiring connect:
+This is a deliberately minimal slice through the Registry architecture, parallel to
+Jamie's Spring Boot `registry-spike` branch. It does not implement any Sourcelume
+functionality (no signature verification, no schema validation, no queue, no full
+REST/GraphQL API). Its only job is to prove three pieces of wiring connect using
+**Quarkus, CDI, and a thin JDK HttpClient** instead of Spring Boot and
+`atlas-client-v2`:
1. A locally-built `sourcelume-spec` jar can be depended on and its resources read.
-2. Atlas typedefs bundled in `sourcelume-registry-typedefs` can be loaded and are
- well-formed.
-3. This project can talk to a running Apache Atlas instance and successfully register
- those typedefs.
+2. A backend-neutral `AtlasAdapter` (with a thin REST client implementation) can
+ reach a running Apache Atlas instance and register typedefs.
+3. A Quarkus application starts, bootstraps the typedefs on startup, and exposes a
+ readiness health check.
+
+## Why this branch exists
+
+The dev-list thread (Sept 2026) raised two questions this spike answers:
+
+- **Java baseline.** Spring Boot 3.3.5 (Jamie's spike) only supports Java up to 23;
+ the Mockito/ByteBuddy version in its BOM fails on Java 26. Java 17 loses Oracle
+ support on 30.09.2026. Proposed baseline: Java 21 (LTS), CI on 25, skip 26 (non-LTS).
+- **Backend dependency.** Calvin proposed dropping `atlas-client-v2` and its whole
+ transitive tree in favour of a thin REST client over the JDK HttpClient + Jackson,
+ since the registry only talks to 4 Atlas endpoints. This spike implements that.
+- **Framework.** Calvin: "I don't feel strongly about Spring vs Quarkus... Quarkus
+ is closer to what JBO wants :). It might be worth doing a Quarkus version of the
+ spike and comparing the two." This is that Quarkus version.
+
+## Modules
+
+- `sourcelume-registry-typedefs` — one minimal entity type, `sourcelume_dataset`
+ (unchanged from the Spring spike; vendor-neutral).
+- `sourcelume-registry-common` — DTOs mapping spec 0.0.1, Jakarta Validation, the
+ `SpecResourceLoader` (unchanged; vendor-neutral, only Jakarta API, no Spring/Quarkus
+ runtime dependency).
+- `sourcelume-registry-atlas-adapter` — **backend-neutral `AtlasAdapter` interface
+ using Sourcelume types**, implemented by `RestAtlasAdapter` (thin JDK HttpClient +
+ Jackson). Pulls in NO `atlas-client-v2` / `atlas-intg`. The Atlas REST wire shape
+ lives entirely in the implementation; the interface exposes only Sourcelume DTOs.
+- `sourcelume-registry-ingest-worker` — Quarkus app: starts, bootstraps typedefs on
+ startup (`AtlasBootstrapRunner` observing CDI `StartupEvent`), exposes readiness
+ via SmallRye Health (`AtlasHealthIndicator`).
+
+## Differences from the Spring spike (registry-spike branch)
+
+| Concern | Spring spike | Quarkus spike |
+|---|---|---|
+| Framework | Spring Boot 3.3.5 | Quarkus 3.33 LTS |
+| DI | Spring `@Component`/`@Bean` | CDI `@ApplicationScoped` + `@Inject` |
+| Config | `@ConfigurationProperties` (application.yml) | MicroProfile `@ConfigMapping` (application.properties) |
+| Health | Spring Actuator `HealthIndicator` | SmallRye Health `@Readiness` HealthCheck |
+| Startup | Spring `ApplicationRunner` | CDI `@Observes StartupEvent` |
+| Atlas client | `atlas-client-v2` + `atlas-intg` (full SDK tree) | JDK `HttpClient` + Jackson (thin, ~250 LoC) |
+| Adapter interface | imports `AtlasTypesDef`, `AtlasClientV2` | backend-neutral, Sourcelume types only |
+| Java baseline | 17 (EOL this month) | 21 (LTS) |
+| Mockito | inline-mock, self-attach (breaking on future JDKs) | CDI `@QuarkusComponentTest` / no ByteBuddy on Atlas |
+
+## Building
+
+### Prerequisites
+
+- Java 21 (LTS) — `jenv local 21` in this directory (or `export JAVA_HOME=$(/usr/libexec/java_home -v 21)`)
+- Maven 3.9+
+- The `sourcelume-spec` jar installed locally: from the `sourcelume-spec` repo, `mvn install`
+
+### Build
-## Modules included
-
-- `sourcelume-registry-typedefs` — one minimal entity type, `sourcelume_dataset`,
- extending Atlas's built-in `DataSet` type, defined in a single
- `models/sourcelume/sourcelume_model.json` file loaded via atomic
- `createAtlasTypeDefs` call.
-- `sourcelume-registry-common` — shared DTOs (`ProvenanceRecordDto`, `CreatorDto`, `CustodyEventDto`, `LicenseHistoryDto`, `SourcelumeDatasetDto`), domain exceptions, and schema resource loaders.
-- `sourcelume-registry-atlas-adapter` — encapsulates `AtlasClientV2` interactions behind the `AtlasAdapter` domain interface (enforcing architecture decision D-002) with Spring Boot auto-configuration.
-- `sourcelume-registry-ingest-worker` — Spring Boot 3 worker runner, Actuator health indicator, and `AtlasBootstrapRunner` verifying spec resources and registering typedefs with Atlas.
+```bash
+# from the sourcelume-spec repo (on main), so it lands in your local Maven repo
+mvn install
-See [docs/architecture.md](docs/architecture.md) for the full module design and [docs/deployment.md](docs/deployment.md) for how runners are deployed.
+# from this repo, on the quarkus-spike branch
+mvn clean install
+```
-## Running it
+### Running
-### Option 1: Running Spring Boot on Host
+#### Option A: standalone (the steel thread)
```bash
-# from this repository
-mvn clean package
-
-# Run Spring Boot ingest worker pointing to local Atlas
-export SOURCELUME_ATLAS_URL=http://localhost:21000
-mvn -pl sourcelume-registry-ingest-worker spring-boot:run
+mvn -pl sourcelume-registry-ingest-worker exec:java \
+ -Dexec.mainClass=org.apache.sourcelume.registry.ingest.worker.WiringSpike \
+ -Dexec.args="http://localhost:21000"
```
-### Option 2: Running with Docker Compose
+#### Option B: Quarkus app (dev mode)
```bash
-# 1. Build project artifacts (creates target/*.jar)
-mvn clean package
-
-# 2. Build and run worker container
-docker compose build
-docker compose up
+mvn -pl sourcelume-registry-ingest-worker quarkus:dev
```
-Point `SOURCELUME_ATLAS_URL` / `SOURCELUME_ATLAS_USER` / `SOURCELUME_ATLAS_PASSWORD`
-env vars at a running Atlas instance — see `dev-support/README.md` for standing up the local Atlas backend.
+Health check: `curl http://localhost:8082/q/health/ready`
-## Local Dev Support (Apache Atlas)
+#### Option C: Docker
-See [`dev-support/README.md`](dev-support/README.md) for complete instructions on standing up the local Apache Atlas backend using Docker Compose and testing the Spring Boot runtime.
+See `dev-support/README.md` for standing up Atlas. Then:
+```bash
+docker compose up -d --wait
+curl http://localhost:8082/q/health/ready
+```
-## Overview
-
-## Layout
+## What "success" looks like
+The worker starts, logs:
+```
+1. spec context read: N bytes
+2. typedefs loaded: OK
+3. atlas reachable: true
+4. atlas accepted typedefs, entity types: 1
+Sourcelume Registry bootstrap completed successfully.
+```
-## Get involved
+and `/q/health/ready` returns `UP` with `atlas: CONNECTED`.
-- Mailing list: dev@sourcelume.apache.org ([archives](https://lists.apache.org/list.html?dev@sourcelume.apache.org))
-- ASF Slack: #sourcelume (Ask to be invited)
+If it gets that far, the wiring described in the Registry's high-level architecture is
+real on the Quarkus + thin-client stack, not just a diagram.
-## License
+## Open questions for the dev list
-This project is licensed under [Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0).
+This spike does not decide the framework question. It exists so the list can
+**compare** the two implementations side by side and settle Java baseline +
+framework + backend port before merging either spike into `main` (per Calvin's
+proposed sequencing).
diff --git a/dev-support/README.md b/dev-support/README.md
index 719acad..3da7089 100644
--- a/dev-support/README.md
+++ b/dev-support/README.md
@@ -1,6 +1,6 @@
-# Sourcelume Dev Support: Apache Atlas & Spring Boot Stack
+# Sourcelume Dev Support: Apache Atlas & Quarkus Stack
-This directory provides local developer infrastructure to stand up an Apache Atlas backend and develop/test the Sourcelume Spring Boot Registry against it.
+This directory provides local developer infrastructure to stand up an Apache Atlas backend and develop/test the Sourcelume Quarkus Registry against it.
---
@@ -8,7 +8,7 @@ This directory provides local developer infrastructure to stand up an Apache Atl
The local development environment consists of:
1. **Apache Atlas Backend**: An official Apache Atlas 2.5.0 container stack running with the PostgreSQL backend on port `21000`.
-2. **Sourcelume Spring Boot Services**: Spring Boot applications (such as `sourcelume-registry-ingest-worker`) that connect to Atlas, register typedefs on startup, and expose Actuator health/metrics endpoints on management port `9001`.
+2. **Sourcelume Quarkus Services**: Quarkus applications (such as `sourcelume-registry-ingest-worker`) that connect to Atlas, register typedefs on startup, and expose SmallRye Health readiness/liveness probes at `/q/health/{ready,live}` on port `8082`.
3. **Shared Network**: A Docker bridge network named `sourcelume-network` connecting the Atlas containers and Sourcelume services.
---
@@ -71,9 +71,9 @@ docker network connect sourcelume-network "${ATLAS_CONTAINER}" || true
---
-### Running & Testing the Spring Boot Runtime
+### Running & Testing the Quarkus Runtime
-You can run and test the Sourcelume Spring Boot runtime using either **Docker Compose** or directly on your **Host/IDE**.
+You can run and test the Sourcelume Quarkus runtime using either **Docker Compose** or directly on your **Host/IDE**.
#### Option A: Run via Docker Compose (Recommended for Container Validation)
@@ -98,7 +98,7 @@ export SOURCELUME_ATLAS_URL=http://localhost:21000
export SOURCELUME_ATLAS_USER=admin
export SOURCELUME_ATLAS_PASSWORD=atlasR0cks!
-# Run the Spring Boot application
+# Run the Quarkus application
mvn -pl sourcelume-registry-ingest-worker spring-boot:run
```
@@ -106,13 +106,13 @@ mvn -pl sourcelume-registry-ingest-worker spring-boot:run
### Testing & Verification Workflows
-#### 1. Spring Boot Actuator Health & Probes
+#### 1. SmallRye Health Probes
-The Spring Boot runtime exposes Actuator endpoints on management port `9001`:
+The Quarkus runtime exposes SmallRye Health probes at `/q/health/{ready,live}` on port `8082`:
- **Overall Health (including Atlas connectivity)**:
```bash
- curl -s http://localhost:9001/actuator/health | jq .
+ curl -s http://localhost:8082/q/health | jq .
```
Expected output includes `status: "UP"` with the `atlas` health component:
```json
@@ -138,13 +138,13 @@ The Spring Boot runtime exposes Actuator endpoints on management port `9001`:
- **Kubernetes Liveness and Readiness Probes**:
```bash
- curl -s http://localhost:9001/actuator/health/liveness
- curl -s http://localhost:9001/actuator/health/readiness
+ curl -s http://localhost:8082/q/health/liveness
+ curl -s http://localhost:8082/q/health/readiness
```
- **Prometheus Metrics**:
```bash
- curl -s http://localhost:9001/actuator/prometheus
+ curl -s http://localhost:8082/q/metrics
```
#### 2. Typedef Bootstrap Verification
@@ -189,7 +189,7 @@ On startup, `sourcelume-registry-ingest-worker` automatically registers `sourcel
#### 4. Automated Tests
-Run the full automated test suite (unit tests, validation tests, and Spring Boot context tests):
+Run the full automated test suite (unit tests, validation tests, and Quarkus context tests):
```bash
mvn clean verify
```
diff --git a/dev-support/docker-compose.yml b/dev-support/docker-compose.yml
index 4c9f193..f3b65f0 100644
--- a/dev-support/docker-compose.yml
+++ b/dev-support/docker-compose.yml
@@ -1,4 +1,4 @@
-# Sourcelume local dev stack: Spring Boot services & local dev environment.
+# Sourcelume local dev stack: Quarkus ingest worker & local dev environment.
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
@@ -16,7 +16,7 @@
# limitations under the License.
#
# Apache Atlas backend is stood up via dev-support/vendor/atlas-docker (see README.md).
-# This compose file runs the Sourcelume Spring Boot ingest worker container connected to Atlas
+# This compose file runs the Sourcelume Quarkus ingest worker container connected to Atlas
# via the shared `sourcelume-network`.
networks:
@@ -29,24 +29,21 @@ services:
build:
context: ..
dockerfile: sourcelume-registry-ingest-worker/Dockerfile
- image: apache/sourcelume-registry-app-worker:0.0.1
- container_name: sourcelume-worker
+ image: apache/sourcelume-registry-app-worker-quarkus:0.0.1
+ container_name: sourcelume-worker-quarkus
environment:
SOURCELUME_ATLAS_URL: ${SOURCELUME_ATLAS_URL:-http://atlas:21000}
SOURCELUME_ATLAS_USER: ${SOURCELUME_ATLAS_USER:-admin}
SOURCELUME_ATLAS_PASSWORD: ${SOURCELUME_ATLAS_PASSWORD:-atlasR0cks!}
SOURCELUME_ATLAS_PASSWORD_FILE: ${SOURCELUME_ATLAS_PASSWORD_FILE:-}
- MANAGEMENT_SERVER_PORT: "9001"
- MANAGEMENT_ENDPOINT_HEALTH_PROBES_ENABLED: "true"
- MANAGEMENT_ENDPOINTS_WEB_EXPOSURE_INCLUDE: "health,info,prometheus"
- JAVA_TOOL_OPTIONS: "-XX:+UseG1GC -XX:MaxRAMPercentage=75.0"
+ SOURCELUME_ATLAS_BOOTSTRAP_ON_STARTUP: "true"
+ JAVA_OPTS: "-XX:+UseG1GC -XX:MaxRAMPercentage=75.0"
ports:
- "8082:8082"
- - "9001:9001"
networks:
- sourcelume-network
healthcheck:
- test: ["CMD", "curl", "-fsS", "http://localhost:9001/actuator/health/liveness"]
+ test: ["CMD", "curl", "-fsS", "http://localhost:8082/q/health/ready"]
interval: 10s
timeout: 5s
retries: 3
diff --git a/docker-compose.yml b/docker-compose.yml
index b901e5e..b46a3e8 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -15,6 +15,10 @@
# limitations under the License.
#
+# Quarkus spike — Sourcelume ingest worker container.
+# The Apache Atlas backend is stood up via dev-support/vendor/atlas-docker (see dev-support/README.md);
+# this compose runs the Quarkus worker connected to Atlas via the shared `sourcelume-network`.
+
networks:
sourcelume-network:
name: sourcelume-network
@@ -25,24 +29,21 @@ services:
build:
context: .
dockerfile: sourcelume-registry-ingest-worker/Dockerfile
- image: apache/sourcelume-registry-app-worker:0.0.1
- container_name: sourcelume-worker
+ image: apache/sourcelume-registry-app-worker-quarkus:0.0.1
+ container_name: sourcelume-worker-quarkus
environment:
SOURCELUME_ATLAS_URL: ${SOURCELUME_ATLAS_URL:-http://atlas:21000}
SOURCELUME_ATLAS_USER: ${SOURCELUME_ATLAS_USER:-admin}
SOURCELUME_ATLAS_PASSWORD: ${SOURCELUME_ATLAS_PASSWORD:-atlasR0cks!}
SOURCELUME_ATLAS_PASSWORD_FILE: ${SOURCELUME_ATLAS_PASSWORD_FILE:-}
- MANAGEMENT_SERVER_PORT: "9001"
- MANAGEMENT_ENDPOINT_HEALTH_PROBES_ENABLED: "true"
- MANAGEMENT_ENDPOINTS_WEB_EXPOSURE_INCLUDE: "health,info,prometheus"
- JAVA_TOOL_OPTIONS: "-XX:+UseG1GC -XX:MaxRAMPercentage=75.0"
+ SOURCELUME_ATLAS_BOOTSTRAP_ON_STARTUP: "true"
+ JAVA_OPTS: "-XX:+UseG1GC -XX:MaxRAMPercentage=75.0"
ports:
- "8082:8082"
- - "9001:9001"
networks:
- sourcelume-network
healthcheck:
- test: ["CMD", "curl", "-fsS", "http://localhost:9001/actuator/health/liveness"]
+ test: ["CMD", "curl", "-fsS", "http://localhost:8082/q/health/ready"]
interval: 10s
timeout: 5s
retries: 3
diff --git a/pom.xml b/pom.xml
index 0cf164a..98ac386 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1,69 +1,51 @@
<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
- <!--
-
- 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.
- -->
-
<modelVersion>4.0.0</modelVersion>
- <parent>
- <groupId>org.apache</groupId>
- <artifactId>apache</artifactId>
- <version>39</version>
- <relativePath />
- </parent>
-
<groupId>org.apache.sourcelume</groupId>
<artifactId>sourcelume-registry</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>pom</packaging>
-
<name>Apache Sourcelume Registry</name>
- <description>Registry for Apache Sourcelume.</description>
- <!--
- STEEL THREAD SCOPE ONLY.
- This parent currently lists only the two modules needed to prove that a locally-built
- sourcelume-spec jar can be consumed by this project, and that this project can talk to
- a running Apache Atlas instance and register the Sourcelume typedefs.
+ <description>
+ Apache Sourcelume Registry — Quarkus-based spike.
+
+ This is a deliberately minimal slice through the Registry architecture,
+ parallel to the Spring Boot registry-spike branch. It proves three pieces
+ of wiring connect using Quarkus, CDI, and a thin JDK HttpClient instead of
+ Spring Boot and atlas-client-v2:
- NOT included yet (see docs/architecture.md for the full design - library modules
- first, then the thin app-* Spring Boot runners):
- sourcelume-registry-common
- sourcelume-registry-atlas-adapter (the ONLY module allowed to use atlas-client-v2)
- sourcelume-registry-atlas-rest (read-only Atlas access for the query path)
- sourcelume-registry-messaging
- sourcelume-registry-ingest-api
- sourcelume-registry-cache
- sourcelume-registry-query-api
- sourcelume-registry-export
- sourcelume-registry-app-ingest
- sourcelume-registry-app-worker
- sourcelume-registry-app-query
- sourcelume-registry-app-export
- sourcelume-registry-tests
+ 1. A locally-built sourcelume-spec jar can be depended on and its resources read.
+ 2. A backend-neutral AtlasAdapter (with a thin REST client implementation)
+ can reach a running Apache Atlas instance and register typedefs.
+ 3. A Quarkus application starts, bootstraps the typedefs on startup, and
+ exposes a readiness health check.
+
+ Everything else from the full architecture (messaging, query-api, export,
+ cache, the app-* runners beyond the worker) is intentionally left out.
+ See docs/architecture.md for the full module design.
+ </description>
- Also not yet done (docs/architecture.md "Dependency management"): importing the
- spring-boot-dependencies BOM below. Once it is imported it owns the Jackson version
- and the hand-pinned jackson.version property here should be deleted, not raised.
- -->
<modules>
<module>sourcelume-registry-typedefs</module>
<module>sourcelume-registry-common</module>
@@ -72,113 +54,36 @@
</modules>
<properties>
- <maven.compiler.source>17</maven.compiler.source>
- <maven.compiler.target>17</maven.compiler.target>
+ <!-- Java 21 baseline (LTS). CI should also run on 25. Skip 26 (non-LTS).
+ See dev-list thread on Java baseline + framework choice (Sept 2026). -->
+ <maven.compiler.release>21</maven.compiler.release>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+ <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<!-- Dependency Versions -->
- <spring-boot.version>3.3.5</spring-boot.version>
- <atlas.version>2.5.0</atlas.version>
- <jackson-jaxrs-atlas.version>2.12.7</jackson-jaxrs-atlas.version>
+ <quarkus.version>3.33.0</quarkus.version>
+ <jackson.version>2.18.0</jackson.version>
<sourcelume-spec.version>0.0.1-SNAPSHOT</sourcelume-spec.version>
<!-- Plugin Versions -->
- <exec-maven-plugin.version>3.2.0</exec-maven-plugin.version>
- <maven-compiler-plugin.version>3.15.0</maven-compiler-plugin.version>
+ <maven-compiler-plugin.version>3.13.0</maven-compiler-plugin.version>
</properties>
<dependencyManagement>
<dependencies>
- <!-- Jackson JAX-RS provider pinned to 2.12.7 for Atlas Client V2 / Jersey 1.x compatibility -->
+ <!-- Quarkus BOM owns the Quarkus + Jakarta + Jackson versions -->
<dependency>
- <groupId>com.fasterxml.jackson.jaxrs</groupId>
- <artifactId>jackson-jaxrs-json-provider</artifactId>
- <version>${jackson-jaxrs-atlas.version}</version>
- </dependency>
- <dependency>
- <groupId>com.fasterxml.jackson.jaxrs</groupId>
- <artifactId>jackson-jaxrs-base</artifactId>
- <version>${jackson-jaxrs-atlas.version}</version>
- </dependency>
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-dependencies</artifactId>
- <version>${spring-boot.version}</version>
+ <groupId>io.quarkus.platform</groupId>
+ <artifactId>quarkus-bom</artifactId>
+ <version>${quarkus.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
- <dependency>
- <groupId>org.apache.atlas</groupId>
- <artifactId>atlas-client-v2</artifactId>
- <version>${atlas.version}</version>
- <exclusions>
- <!-- Logging: route everything through Boot's slf4j + logback. -->
- <exclusion><groupId>org.slf4j</groupId><artifactId>slf4j-log4j12</artifactId></exclusion>
- <exclusion><groupId>org.slf4j</groupId><artifactId>slf4j-reload4j</artifactId></exclusion>
- <exclusion><groupId>ch.qos.reload4j</groupId><artifactId>reload4j</artifactId></exclusion>
- <exclusion><groupId>log4j</groupId><artifactId>log4j</artifactId></exclusion>
- <exclusion><groupId>org.apache.logging.log4j</groupId><artifactId>log4j-slf4j-impl</artifactId></exclusion>
- <exclusion><groupId>commons-logging</groupId><artifactId>commons-logging</artifactId></exclusion>
-
- <!-- Servlet/JAX-RS server-side bits a client has no business shipping. -->
- <exclusion><groupId>javax.servlet</groupId><artifactId>servlet-api</artifactId></exclusion>
- <exclusion><groupId>javax.servlet</groupId><artifactId>javax.servlet-api</artifactId></exclusion>
- <exclusion><groupId>com.sun.jersey</groupId><artifactId>jersey-server</artifactId></exclusion>
-
- <!-- Hadoop ecosystem: unused by REST client, but keep hadoop-common for AtlasBaseClient UserGroupInformation -->
- <exclusion><groupId>org.apache.hadoop</groupId><artifactId>hadoop-hdfs</artifactId></exclusion>
- <exclusion><groupId>org.apache.hadoop</groupId><artifactId>hadoop-annotations</artifactId></exclusion>
-
- <!-- Let the Spring Boot BOM own Jackson's version, not Atlas. -->
- <exclusion><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-core</artifactId></exclusion>
- <exclusion><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId></exclusion>
- <exclusion><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-annotations</artifactId></exclusion>
- <exclusion><groupId>com.fasterxml.jackson.jaxrs</groupId><artifactId>jackson-jaxrs-base</artifactId></exclusion>
- <exclusion><groupId>com.fasterxml.jackson.jaxrs</groupId><artifactId>jackson-jaxrs-json-provider</artifactId></exclusion>
- </exclusions>
- </dependency>
- <dependency>
- <groupId>org.apache.atlas</groupId>
- <artifactId>atlas-intg</artifactId>
- <version>${atlas.version}</version>
- <exclusions>
- <exclusion><groupId>org.slf4j</groupId><artifactId>slf4j-log4j12</artifactId></exclusion>
- <exclusion><groupId>org.slf4j</groupId><artifactId>slf4j-reload4j</artifactId></exclusion>
- <exclusion><groupId>ch.qos.reload4j</groupId><artifactId>reload4j</artifactId></exclusion>
- <exclusion><groupId>log4j</groupId><artifactId>log4j</artifactId></exclusion>
- <exclusion><groupId>org.apache.logging.log4j</groupId><artifactId>log4j-slf4j-impl</artifactId></exclusion>
- <exclusion><groupId>commons-logging</groupId><artifactId>commons-logging</artifactId></exclusion>
- <exclusion><groupId>javax.servlet</groupId><artifactId>servlet-api</artifactId></exclusion>
- <exclusion><groupId>javax.servlet</groupId><artifactId>javax.servlet-api</artifactId></exclusion>
- <exclusion><groupId>org.apache.hadoop</groupId><artifactId>hadoop-hdfs</artifactId></exclusion>
- <exclusion><groupId>org.apache.hadoop</groupId><artifactId>hadoop-annotations</artifactId></exclusion>
- <exclusion><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-core</artifactId></exclusion>
- <exclusion><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId></exclusion>
- <exclusion><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-annotations</artifactId></exclusion>
- <exclusion><groupId>com.fasterxml.jackson.jaxrs</groupId><artifactId>jackson-jaxrs-base</artifactId></exclusion>
- <exclusion><groupId>com.fasterxml.jackson.jaxrs</groupId><artifactId>jackson-jaxrs-json-provider</artifactId></exclusion>
- </exclusions>
- </dependency>
<dependency>
<groupId>org.apache.sourcelume</groupId>
<artifactId>sourcelume-spec</artifactId>
<version>${sourcelume-spec.version}</version>
</dependency>
- <dependency>
- <groupId>org.apache.sourcelume</groupId>
- <artifactId>sourcelume-registry-typedefs</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.apache.sourcelume</groupId>
- <artifactId>sourcelume-registry-common</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.apache.sourcelume</groupId>
- <artifactId>sourcelume-registry-atlas-adapter</artifactId>
- <version>${project.version}</version>
- </dependency>
</dependencies>
</dependencyManagement>
@@ -186,26 +91,24 @@
<pluginManagement>
<plugins>
<plugin>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-maven-plugin</artifactId>
- <version>${spring-boot.version}</version>
- </plugin>
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven-compiler-plugin.version}</version>
<configuration>
- <release>17</release>
+ <release>${maven.compiler.release}</release>
+ </configuration>
+ </plugin>
+ <!-- Mockito self-attach is deprecated on Java 21+ and will break on
+ future JDKs. Configure the Byte Buddy agent explicitly via
+ -javaagent in Surefire so tests stay stable across JDK bumps.
+ See Calvin's dev-list note (Sept 2026). -->
+ <plugin>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <configuration>
+ <argLine>-javaagent:${settings.localRepository}/net/bytebuddy/byte-buddy-agent/1.15.11/byte-buddy-agent-1.15.11.jar</argLine>
</configuration>
</plugin>
</plugins>
</pluginManagement>
- <plugins>
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-compiler-plugin</artifactId>
- </plugin>
- </plugins>
</build>
</project>
diff --git a/sourcelume-registry-atlas-adapter/pom.xml b/sourcelume-registry-atlas-adapter/pom.xml
index f2365b7..7470868 100644
--- a/sourcelume-registry-atlas-adapter/pom.xml
+++ b/sourcelume-registry-atlas-adapter/pom.xml
@@ -1,25 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
- <!--
- 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.
- -->
-
<modelVersion>4.0.0</modelVersion>
<parent>
@@ -29,53 +28,53 @@
</parent>
<artifactId>sourcelume-registry-atlas-adapter</artifactId>
- <name>Apache Sourcelume Registry - Atlas Adapter</name>
+ <name>Apache Sourcelume Registry - Atlas Adapter (thin REST client)</name>
<packaging>jar</packaging>
<description>
- Encapsulates Apache Atlas SDK (atlas-client-v2, atlas-intg) interactions behind clean
- Sourcelume domain interfaces. Quarantines legacy Jersey 1.x and Hadoop transitives.
+ Encapsulates Apache Atlas interactions behind a backend-neutral AtlasAdapter
+ interface using Sourcelume types. The default implementation is a thin REST
+ client over the JDK HttpClient + Jackson — it pulls in NO atlas-client-v2 /
+ atlas-intg dependency tree, only talks to the 4 Atlas REST endpoints the
+ registry needs. Atlas-specific JSON mapping lives entirely in the
+ implementation; the interface exposes only Sourcelume DTOs.
</description>
<dependencies>
<dependency>
<groupId>org.apache.sourcelume</groupId>
<artifactId>sourcelume-registry-common</artifactId>
+ <version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.apache.sourcelume</groupId>
<artifactId>sourcelume-registry-typedefs</artifactId>
+ <version>${project.version}</version>
</dependency>
+ <!-- Quarkus CDI + config + health, provided by the Quarkus runtime.
+ The adapter module is a plain jar that the ingest-worker (the Quarkus
+ app) pulls in; Quarkus annotations here are resolved at the app's build. -->
<dependency>
- <groupId>org.apache.atlas</groupId>
- <artifactId>atlas-client-v2</artifactId>
+ <groupId>io.quarkus</groupId>
+ <artifactId>quarkus-arc</artifactId>
</dependency>
<dependency>
- <groupId>org.apache.atlas</groupId>
- <artifactId>atlas-intg</artifactId>
+ <groupId>io.quarkus</groupId>
+ <artifactId>quarkus-jackson</artifactId>
</dependency>
<dependency>
- <groupId>com.fasterxml.jackson.jaxrs</groupId>
- <artifactId>jackson-jaxrs-json-provider</artifactId>
- <version>2.12.7</version>
+ <groupId>io.quarkus</groupId>
+ <artifactId>quarkus-smallrye-health</artifactId>
</dependency>
+ <!-- Test -->
<dependency>
- <groupId>com.fasterxml.jackson.jaxrs</groupId>
- <artifactId>jackson-jaxrs-base</artifactId>
- <version>2.12.7</version>
- </dependency>
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter</artifactId>
- </dependency>
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-configuration-processor</artifactId>
- <optional>true</optional>
+ <groupId>io.quarkus</groupId>
+ <artifactId>quarkus-junit5</artifactId>
+ <scope>test</scope>
</dependency>
<dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-test</artifactId>
+ <groupId>io.quarkus</groupId>
+ <artifactId>quarkus-test-h2</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
diff --git a/sourcelume-registry-atlas-adapter/src/main/java/org/apache/sourcelume/registry/atlas/adapter/AtlasAdapter.java b/sourcelume-registry-atlas-adapter/src/main/java/org/apache/sourcelume/registry/atlas/adapter/AtlasAdapter.java
index c7f8c75..1487d99 100644
--- a/sourcelume-registry-atlas-adapter/src/main/java/org/apache/sourcelume/registry/atlas/adapter/AtlasAdapter.java
+++ b/sourcelume-registry-atlas-adapter/src/main/java/org/apache/sourcelume/registry/atlas/adapter/AtlasAdapter.java
@@ -16,72 +16,99 @@
*/
package org.apache.sourcelume.registry.atlas.adapter;
-import org.apache.atlas.AtlasClientV2;
-import org.apache.atlas.model.typedef.AtlasTypesDef;
import org.apache.sourcelume.registry.atlas.adapter.exception.AtlasAdapterException;
import org.apache.sourcelume.registry.common.dto.SourcelumeDatasetDto;
/**
- * Narrow domain adapter isolating Apache Atlas client operations from the rest of Sourcelume.
+ * Backend-neutral adapter isolating Atlas (or any provenance-graph backend) operations
+ * from the rest of Sourcelume.
+ *
+ * <p>The interface exposes only Sourcelume types. The Atlas-specific JSON mapping and
+ * REST wire format live entirely in the implementation ({@code RestAtlasAdapter}).
+ * This keeps {@code sourcelume-registry-common} and the ingest worker free of any
+ * Atlas SDK dependency, and lets other backends implement the same contract.
+ *
+ * <p>Per Calvin's dev-list note (Sept 2026): "keep Atlas-specific types out of
+ * registry-core; the core should expose a backend-neutral interface using
+ * Sourcelume types, with the Atlas implementation owning the typedef mapping."
*/
public interface AtlasAdapter {
/**
- * Checks if the Apache Atlas server is reachable and responsive.
+ * Checks if the backend server is reachable and responsive.
*
* @return true if ready, false otherwise
*/
boolean isServerReady();
/**
- * Loads typedef definitions from a classpath JSON resource.
+ * Loads a type definition model from a classpath JSON resource, parsed into the
+ * backend's native typedef JSON (the implementation owns the shape).
*
* @param resourcePath classpath resource path
- * @return parsed AtlasTypesDef
- * @throws AtlasAdapterException on failure
+ * @return the parsed type definition as a backend-neutral holder
*/
- AtlasTypesDef loadTypeDefs(String resourcePath);
+ TypeDefinitionModel loadTypeDefs(String resourcePath);
/**
- * Registers or updates typedefs in Apache Atlas.
+ * Registers or updates type definitions in the backend. If a type already
+ * exists, the implementation updates it; otherwise it creates it.
*
- * @param typesDef the typedefs to register or update
- * @return the created or updated AtlasTypesDef
- * @throws AtlasAdapterException on failure
+ * @param typeDefs the type definitions to register
+ * @return the backend's response, parsed into a backend-neutral holder
*/
- AtlasTypesDef registerOrUpdateTypeDefs(AtlasTypesDef typesDef);
+ TypeDefinitionModel registerOrUpdateTypeDefs(TypeDefinitionModel typeDefs);
/**
- * Convenience method to load and register/update typedefs from a classpath resource.
- *
- * @param resourcePath classpath resource path
- * @return the created or updated AtlasTypesDef
- * @throws AtlasAdapterException on failure
+ * Convenience: load from a resource and register in one call.
*/
- AtlasTypesDef registerTypeDefsFromResource(String resourcePath);
+ default TypeDefinitionModel registerTypeDefsFromResource(String resourcePath) {
+ return registerOrUpdateTypeDefs(loadTypeDefs(resourcePath));
+ }
/**
- * Creates or updates a sourcelume_dataset entity in Atlas.
+ * Creates or updates a dataset entity in the backend.
*
- * @param dataset dataset DTO
- * @return the assigned GUID
- * @throws AtlasAdapterException on failure
+ * @param dataset the dataset to persist
+ * @return the backend-assigned identifier (e.g. GUID) for the entity
*/
String createOrUpdateDatasetEntity(SourcelumeDatasetDto dataset);
/**
- * Fetches a sourcelume_dataset entity by its qualifiedName.
+ * Retrieves a dataset by its qualified name, or {@code null} if not found.
*
- * @param qualifiedName qualified name of the dataset
- * @return the dataset DTO or null if not found
- * @throws AtlasAdapterException on failure
+ * @param qualifiedName the qualified name to look up
+ * @return the dataset DTO, or null
*/
SourcelumeDatasetDto getDatasetByQualifiedName(String qualifiedName);
/**
- * Direct handle to the underlying AtlasClientV2 for specialized write/batch requirements.
- *
- * @return AtlasClientV2 instance
+ * Backend-neutral holder for a parsed type definition model. The Atlas
+ * implementation carries the raw JSON-LD/JSON structure; other backends
+ * would carry their own. This keeps the Atlas wire shape out of the
+ * interface signature while still passing the payload through.
*/
- AtlasClientV2 getAtlasClient();
+ final class TypeDefinitionModel {
+ private final String sourceResource;
+ private final byte[] rawJson;
+ private final int entityDefCount;
+
+ public TypeDefinitionModel(String sourceResource, byte[] rawJson, int entityDefCount) {
+ this.sourceResource = sourceResource;
+ this.rawJson = rawJson;
+ this.entityDefCount = entityDefCount;
+ }
+
+ public String getSourceResource() {
+ return sourceResource;
+ }
+
+ public byte[] getRawJson() {
+ return rawJson;
+ }
+
+ public int getEntityDefCount() {
+ return entityDefCount;
+ }
+ }
}
diff --git a/sourcelume-registry-atlas-adapter/src/main/java/org/apache/sourcelume/registry/atlas/adapter/DefaultAtlasAdapter.java b/sourcelume-registry-atlas-adapter/src/main/java/org/apache/sourcelume/registry/atlas/adapter/DefaultAtlasAdapter.java
deleted file mode 100644
index b5a482b..0000000
--- a/sourcelume-registry-atlas-adapter/src/main/java/org/apache/sourcelume/registry/atlas/adapter/DefaultAtlasAdapter.java
+++ /dev/null
@@ -1,192 +0,0 @@
-/*
- * 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.
- */
-package org.apache.sourcelume.registry.atlas.adapter;
-
-import com.fasterxml.jackson.databind.ObjectMapper;
-import org.apache.atlas.AtlasClientV2;
-import org.apache.atlas.AtlasServiceException;
-import org.apache.atlas.model.instance.AtlasEntity;
-import org.apache.atlas.model.instance.AtlasEntityHeader;
-import org.apache.atlas.model.instance.EntityMutationResponse;
-import org.apache.atlas.model.typedef.AtlasTypesDef;
-import org.apache.sourcelume.registry.atlas.adapter.config.SourcelumeAtlasProperties;
-import org.apache.sourcelume.registry.atlas.adapter.exception.AtlasAdapterException;
-import org.apache.sourcelume.registry.common.dto.SourcelumeDatasetDto;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.URL;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-
-/**
- * Default implementation of AtlasAdapter using AtlasClientV2.
- */
-public class DefaultAtlasAdapter implements AtlasAdapter {
-
- private static final Logger log = LoggerFactory.getLogger(DefaultAtlasAdapter.class);
-
- private final AtlasClientV2 atlasClient;
- private final SourcelumeAtlasProperties properties;
- private final ObjectMapper objectMapper;
-
- public DefaultAtlasAdapter(AtlasClientV2 atlasClient,
- SourcelumeAtlasProperties properties,
- ObjectMapper objectMapper) {
- this.atlasClient = atlasClient;
- this.properties = properties;
- this.objectMapper = objectMapper != null ? objectMapper : new ObjectMapper();
- }
-
- @Override
- public boolean isServerReady() {
- try {
- return atlasClient.isServerReady();
- } catch (Exception e) {
- log.debug("Atlas server connectivity check failed: {}", e.getMessage());
- return false;
- }
- }
-
- @Override
- public AtlasTypesDef loadTypeDefs(String resourcePath) {
- ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
- if (classLoader == null) {
- classLoader = getClass().getClassLoader();
- }
- URL resource = classLoader.getResource(resourcePath);
- if (resource == null) {
- throw new AtlasAdapterException("Could not find typedefs resource on classpath: " + resourcePath);
- }
- try (InputStream in = resource.openStream()) {
- AtlasTypesDef typesDef = objectMapper.readValue(in, AtlasTypesDef.class);
- log.info("Loaded typedefs with {} entity definition(s) from {}",
- typesDef.getEntityDefs() != null ? typesDef.getEntityDefs().size() : 0, resourcePath);
- return typesDef;
- } catch (IOException e) {
- throw new AtlasAdapterException("Failed to read typedefs resource: " + resourcePath, e);
- }
- }
-
- @Override
- public AtlasTypesDef registerOrUpdateTypeDefs(AtlasTypesDef typesDef) {
- if (typesDef == null) {
- throw new IllegalArgumentException("typesDef cannot be null");
- }
- try {
- log.info("Attempting createAtlasTypeDefs in Atlas at {}", properties.getUrl());
- AtlasTypesDef created = atlasClient.createAtlasTypeDefs(typesDef);
- log.info("Successfully created Atlas typedefs");
- return created;
- } catch (AtlasServiceException e) {
- log.warn("createAtlasTypeDefs failed ({}). Attempting updateAtlasTypeDefs...", e.getMessage());
- try {
- AtlasTypesDef updated = atlasClient.updateAtlasTypeDefs(typesDef);
- log.info("Successfully updated Atlas typedefs");
- return updated;
- } catch (AtlasServiceException updateEx) {
- int status = updateEx.getStatus() != null ? updateEx.getStatus().getStatusCode() : 500;
- log.error("Failed to update Atlas typedefs: {}", updateEx.getMessage());
- throw new AtlasAdapterException("Failed to register or update typedefs in Atlas: " + updateEx.getMessage(), status, updateEx);
- }
- }
- }
-
- @Override
- public AtlasTypesDef registerTypeDefsFromResource(String resourcePath) {
- AtlasTypesDef typesDef = loadTypeDefs(resourcePath);
- return registerOrUpdateTypeDefs(typesDef);
- }
-
- @Override
- public String createOrUpdateDatasetEntity(SourcelumeDatasetDto dataset) {
- if (dataset == null) {
- throw new IllegalArgumentException("dataset cannot be null");
- }
- AtlasEntity entity = new AtlasEntity(SourcelumeDatasetDto.TYPE_NAME);
- entity.setAttribute("qualifiedName", dataset.getQualifiedName());
- entity.setAttribute("name", dataset.getName());
- if (dataset.getDescription() != null) {
- entity.setAttribute("description", dataset.getDescription());
- }
- if (dataset.getSourceUri() != null) {
- entity.setAttribute("sourceUri", dataset.getSourceUri());
- }
- if (dataset.getLicenseId() != null) {
- entity.setAttribute("licenseId", dataset.getLicenseId());
- }
-
- AtlasEntity.AtlasEntityWithExtInfo entityWithExtInfo = new AtlasEntity.AtlasEntityWithExtInfo(entity);
- try {
- EntityMutationResponse response = atlasClient.createEntity(entityWithExtInfo);
- List<AtlasEntityHeader> created = response.getCreatedEntities();
- List<AtlasEntityHeader> updated = response.getUpdatedEntities();
- String guid = null;
- if (created != null && !created.isEmpty()) {
- guid = created.get(0).getGuid();
- } else if (updated != null && !updated.isEmpty()) {
- guid = updated.get(0).getGuid();
- } else if (response.getGuidAssignments() != null) {
- guid = response.getGuidAssignments().values().stream().findFirst().orElse(null);
- }
- log.info("Created/updated dataset entity '{}' with GUID: {}", dataset.getQualifiedName(), guid);
- dataset.setGuid(guid);
- return guid;
- } catch (AtlasServiceException e) {
- int status = e.getStatus() != null ? e.getStatus().getStatusCode() : 500;
- throw new AtlasAdapterException("Failed to persist dataset entity to Atlas: " + e.getMessage(), status, e);
- }
- }
-
- @Override
- public SourcelumeDatasetDto getDatasetByQualifiedName(String qualifiedName) {
- try {
- AtlasEntity.AtlasEntityWithExtInfo entityWithExtInfo =
- atlasClient.getEntityByAttribute(SourcelumeDatasetDto.TYPE_NAME,
- Collections.singletonMap("qualifiedName", qualifiedName));
- if (entityWithExtInfo == null || entityWithExtInfo.getEntity() == null) {
- return null;
- }
- AtlasEntity entity = entityWithExtInfo.getEntity();
- SourcelumeDatasetDto dto = new SourcelumeDatasetDto();
- dto.setGuid(entity.getGuid());
- Map<String, Object> attributes = entity.getAttributes();
- if (attributes != null) {
- dto.setQualifiedName((String) attributes.get("qualifiedName"));
- dto.setName((String) attributes.get("name"));
- dto.setDescription((String) attributes.get("description"));
- dto.setSourceUri((String) attributes.get("sourceUri"));
- dto.setLicenseId((String) attributes.get("licenseId"));
- }
- return dto;
- } catch (AtlasServiceException e) {
- if (e.getStatus() != null && e.getStatus().getStatusCode() == 404) {
- return null;
- }
- int status = e.getStatus() != null ? e.getStatus().getStatusCode() : 500;
- throw new AtlasAdapterException("Failed to fetch dataset entity from Atlas: " + e.getMessage(), status, e);
- }
- }
-
- @Override
- public AtlasClientV2 getAtlasClient() {
- return atlasClient;
- }
-}
diff --git a/sourcelume-registry-atlas-adapter/src/main/java/org/apache/sourcelume/registry/atlas/adapter/RestAtlasAdapter.java b/sourcelume-registry-atlas-adapter/src/main/java/org/apache/sourcelume/registry/atlas/adapter/RestAtlasAdapter.java
new file mode 100644
index 0000000..f96ec5a
--- /dev/null
+++ b/sourcelume-registry-atlas-adapter/src/main/java/org/apache/sourcelume/registry/atlas/adapter/RestAtlasAdapter.java
@@ -0,0 +1,271 @@
+/*
+ * 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.
+ */
+package org.apache.sourcelume.registry.atlas.adapter;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import jakarta.enterprise.context.ApplicationScoped;
+import jakarta.inject.Inject;
+import org.apache.sourcelume.registry.atlas.adapter.config.SourcelumeAtlasProperties;
+import org.apache.sourcelume.registry.atlas.adapter.exception.AtlasAdapterException;
+import org.apache.sourcelume.registry.common.dto.SourcelumeDatasetDto;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+import java.net.URL;
+import java.net.URLEncoder;
+import java.net.http.HttpClient;
+import java.net.http.HttpRequest;
+import java.net.http.HttpResponse;
+import java.nio.charset.StandardCharsets;
+import java.time.Duration;
+import java.util.Base64;
+
+/**
+ * Thin REST client implementation of {@link AtlasAdapter} using the JDK
+ * {@link HttpClient} and Jackson. Talks to the Apache Atlas REST API directly,
+ * without pulling in {@code atlas-client-v2} or {@code atlas-intg} and their
+ * transitive dependency tree.
+ *
+ * <p>Covers exactly the four Atlas endpoints the registry needs:
+ * <ul>
+ * <li>{@code GET /api/atlas/admin/version} — readiness (via isServerReady)</li>
+ * <li>{@code POST /api/atlas/v2/types/typedefs} — create typedefs</li>
+ * <li>{@code PUT /api/atlas/v2/types/typedefs} — update typedefs (on 409/conflict)</li>
+ * <li>{@code POST /api/atlas/v2/entity} — create/update a dataset entity</li>
+ * <li>{@code GET /api/atlas/v2/entity/bulk?typeName=...&attr:qualifiedName=...} — lookup</li>
+ * </ul>
+ *
+ * <p>Atlas-specific JSON shapes live here and only here. The interface stays
+ * Sourcelume-typed.
+ */
+@ApplicationScoped
+public class RestAtlasAdapter implements AtlasAdapter {
+
+ private static final Logger log = LoggerFactory.getLogger(RestAtlasAdapter.class);
+
+ private final SourcelumeAtlasProperties properties;
+ private final ObjectMapper objectMapper;
+ private final HttpClient httpClient;
+
+ @Inject
+ public RestAtlasAdapter(SourcelumeAtlasProperties properties, ObjectMapper objectMapper) {
+ this.properties = properties;
+ this.objectMapper = objectMapper;
+ this.httpClient = HttpClient.newBuilder()
+ .connectTimeout(Duration.ofSeconds(10))
+ .build();
+ }
+
+ @Override
+ public boolean isServerReady() {
+ try {
+ HttpRequest req = baseRequest("GET", "/api/atlas/admin/version").GET().build();
+ HttpResponse<String> resp = httpClient.send(req, HttpResponse.BodyHandlers.ofString());
+ return resp.statusCode() == 200;
+ } catch (Exception e) {
+ log.debug("Atlas server connectivity check failed: {}", e.getMessage());
+ return false;
+ }
+ }
+
+ @Override
+ public TypeDefinitionModel loadTypeDefs(String resourcePath) {
+ ClassLoader cl = Thread.currentThread().getContextClassLoader();
+ if (cl == null) {
+ cl = getClass().getClassLoader();
+ }
+ URL resource = cl.getResource(resourcePath);
+ if (resource == null) {
+ throw new AtlasAdapterException("Could not find typedefs resource on classpath: " + resourcePath);
+ }
+ try (InputStream in = resource.openStream()) {
+ byte[] bytes = in.readAllBytes();
+ JsonNode root = objectMapper.readTree(bytes);
+ int entityCount = root.has("entityDefs") && root.get("entityDefs").isArray()
+ ? root.get("entityDefs").size() : 0;
+ log.info("Loaded typedefs with {} entity definition(s) from {}", entityCount, resourcePath);
+ return new TypeDefinitionModel(resourcePath, bytes, entityCount);
+ } catch (IOException e) {
+ throw new AtlasAdapterException("Failed to read typedefs resource: " + resourcePath, e);
+ }
+ }
+
+ @Override
+ public TypeDefinitionModel registerOrUpdateTypeDefs(TypeDefinitionModel typeDefs) {
+ if (typeDefs == null) {
+ throw new IllegalArgumentException("typeDefs cannot be null");
+ }
+ String body = new String(typeDefs.getRawJson(), StandardCharsets.UTF_8);
+ try {
+ log.info("Attempting POST typedefs to Atlas at {}", properties.url());
+ HttpResponse<String> resp = sendJson("POST", "/api/atlas/v2/types/typedefs", body);
+ if (resp.statusCode() == 200 || resp.statusCode() == 201) {
+ log.info("Successfully created Atlas typedefs");
+ int count = parseEntityDefCount(resp.body());
+ return new TypeDefinitionModel(typeDefs.getSourceResource(), resp.body().getBytes(StandardCharsets.UTF_8), count);
+ }
+ // Atlas returns 409/conflict when a typedef already exists — fall back to PUT (update).
+ if (resp.statusCode() == 409 || resp.statusCode() == 400) {
+ log.warn("create typedefs failed ({}). Attempting PUT (update)...", resp.statusCode());
+ HttpResponse<String> putResp = sendJson("PUT", "/api/atlas/v2/types/typedefs", body);
+ if (putResp.statusCode() == 200 || putResp.statusCode() == 204) {
+ log.info("Successfully updated Atlas typedefs");
+ int count = parseEntityDefCount(putResp.body());
+ return new TypeDefinitionModel(typeDefs.getSourceResource(), putResp.body().getBytes(StandardCharsets.UTF_8), count);
+ }
+ throw new AtlasAdapterException("Failed to register or update typedefs in Atlas: " + putResp.body(),
+ putResp.statusCode(), null);
+ }
+ throw new AtlasAdapterException("Failed to register typedefs in Atlas: " + resp.body(),
+ resp.statusCode(), null);
+ } catch (IOException e) {
+ throw new AtlasAdapterException("I/O error registering typedefs: " + e.getMessage(), 500, e);
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ throw new AtlasAdapterException("Interrupted registering typedefs", 500, e);
+ }
+ }
+
+ @Override
+ public String createOrUpdateDatasetEntity(SourcelumeDatasetDto dataset) {
+ if (dataset == null) {
+ throw new IllegalArgumentException("dataset cannot be null");
+ }
+ ObjectNode entity = objectMapper.createObjectNode();
+ entity.put("typeName", SourcelumeDatasetDto.TYPE_NAME);
+ ObjectNode attributes = entity.putObject("attributes");
+ attributes.put("qualifiedName", dataset.getQualifiedName());
+ attributes.put("name", dataset.getName());
+ if (dataset.getDescription() != null) attributes.put("description", dataset.getDescription());
+ if (dataset.getSourceUri() != null) attributes.put("sourceUri", dataset.getSourceUri());
+ if (dataset.getLicenseId() != null) attributes.put("licenseId", dataset.getLicenseId());
+
+ ObjectNode wrapper = objectMapper.createObjectNode();
+ ArrayNode entities = wrapper.putArray("entities");
+ entities.add(entity);
+
+ try {
+ String body = objectMapper.writeValueAsString(wrapper);
+ HttpResponse<String> resp = sendJson("POST", "/api/atlas/v2/entity/bulk", body);
+ if (resp.statusCode() != 200 && resp.statusCode() != 201) {
+ throw new AtlasAdapterException("Failed to persist dataset entity to Atlas: " + resp.body(),
+ resp.statusCode(), null);
+ }
+ JsonNode root = objectMapper.readTree(resp.body());
+ String guid = extractGuid(root);
+ log.info("Created/updated dataset entity '{}' with GUID: {}", dataset.getQualifiedName(), guid);
+ dataset.setGuid(guid);
+ return guid;
+ } catch (IOException e) {
+ throw new AtlasAdapterException("I/O error persisting entity: " + e.getMessage(), 500, e);
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ throw new AtlasAdapterException("Interrupted persisting entity", 500, e);
+ }
+ }
+
+ @Override
+ public SourcelumeDatasetDto getDatasetByQualifiedName(String qualifiedName) {
+ try {
+ String q = URLEncoder.encode(qualifiedName, StandardCharsets.UTF_8);
+ String path = "/api/atlas/v2/entity/bulk?typeName=" + SourcelumeDatasetDto.TYPE_NAME
+ + "&attr:qualifiedName=" + q;
+ HttpRequest req = baseRequest("GET", path).GET().build();
+ HttpResponse<String> resp = httpClient.send(req, HttpResponse.BodyHandlers.ofString());
+ if (resp.statusCode() == 404) {
+ return null;
+ }
+ if (resp.statusCode() != 200) {
+ throw new AtlasAdapterException("Failed to fetch dataset entity from Atlas: " + resp.body(),
+ resp.statusCode(), null);
+ }
+ JsonNode root = objectMapper.readTree(resp.body());
+ JsonNode entities = root.path("entities");
+ if (!entities.isArray() || entities.isEmpty()) {
+ return null;
+ }
+ JsonNode entity = entities.get(0);
+ JsonNode attrs = entity.path("attributes");
+ SourcelumeDatasetDto dto = new SourcelumeDatasetDto();
+ dto.setGuid(entity.path("guid").asText(null));
+ dto.setQualifiedName(attrs.path("qualifiedName").asText(null));
+ dto.setName(attrs.path("name").asText(null));
+ dto.setDescription(attrs.path("description").asText(null));
+ dto.setSourceUri(attrs.path("sourceUri").asText(null));
+ dto.setLicenseId(attrs.path("licenseId").asText(null));
+ return dto;
+ } catch (IOException e) {
+ throw new AtlasAdapterException("I/O error fetching entity: " + e.getMessage(), 500, e);
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ throw new AtlasAdapterException("Interrupted fetching entity", 500, e);
+ }
+ }
+
+ // --- helpers ---
+
+ private HttpRequest.Builder baseRequest(String method, String path) {
+ String auth = properties.user() + ":" + properties.resolvedPassword();
+ String b64 = Base64.getEncoder().encodeToString(auth.getBytes(StandardCharsets.UTF_8));
+ return HttpRequest.newBuilder()
+ .uri(URI.create(properties.url() + path))
+ .timeout(Duration.ofSeconds(30))
+ .header("Authorization", "Basic " + b64)
+ .header("Accept", "application/json");
+ }
+
+ private HttpResponse<String> sendJson(String method, String path, String body)
+ throws IOException, InterruptedException {
+ HttpRequest req = baseRequest(method, path)
+ .header("Content-Type", "application/json")
+ .method(method, HttpRequest.BodyPublishers.ofString(body))
+ .build();
+ return httpClient.send(req, HttpResponse.BodyHandlers.ofString());
+ }
+
+ private int parseEntityDefCount(String body) {
+ try {
+ JsonNode root = objectMapper.readTree(body);
+ return root.has("entityDefs") && root.get("entityDefs").isArray()
+ ? root.get("entityDefs").size() : 0;
+ } catch (Exception e) {
+ return 0;
+ }
+ }
+
+ private String extractGuid(JsonNode root) {
+ JsonNode created = root.path("createdEntities");
+ if (created.isArray() && !created.isEmpty()) {
+ return created.get(0).path("guid").asText(null);
+ }
+ JsonNode updated = root.path("updatedEntities");
+ if (updated.isArray() && !updated.isEmpty()) {
+ return updated.get(0).path("guid").asText(null);
+ }
+ JsonNode assignments = root.path("guidAssignments");
+ if (assignments.isObject() && assignments.size() > 0) {
+ return assignments.elements().next().asText(null);
+ }
+ return null;
+ }
+}
diff --git a/sourcelume-registry-atlas-adapter/src/main/java/org/apache/sourcelume/registry/atlas/adapter/config/AtlasAdapterConfiguration.java b/sourcelume-registry-atlas-adapter/src/main/java/org/apache/sourcelume/registry/atlas/adapter/config/AtlasAdapterConfiguration.java
deleted file mode 100644
index 628b7ed..0000000
--- a/sourcelume-registry-atlas-adapter/src/main/java/org/apache/sourcelume/registry/atlas/adapter/config/AtlasAdapterConfiguration.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * 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.
- */
-package org.apache.sourcelume.registry.atlas.adapter.config;
-
-import com.fasterxml.jackson.databind.ObjectMapper;
-import org.apache.atlas.AtlasClientV2;
-import org.apache.sourcelume.registry.atlas.adapter.AtlasAdapter;
-import org.apache.sourcelume.registry.atlas.adapter.DefaultAtlasAdapter;
-import org.springframework.boot.autoconfigure.AutoConfiguration;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
-import org.springframework.boot.context.properties.EnableConfigurationProperties;
-import org.springframework.context.annotation.Bean;
-
-/**
- * Spring AutoConfiguration for the Atlas adapter module.
- */
-@AutoConfiguration
-@EnableConfigurationProperties(SourcelumeAtlasProperties.class)
-public class AtlasAdapterConfiguration {
-
- @Bean
- @ConditionalOnMissingBean
- public AtlasClientV2 atlasClient(SourcelumeAtlasProperties properties) {
- String url = properties.getUrl();
- String user = properties.getUser();
- String password = properties.getResolvedPassword();
- return new AtlasClientV2(new String[]{url}, new String[]{user, password});
- }
-
- @Bean
- @ConditionalOnMissingBean
- public AtlasAdapter atlasAdapter(AtlasClientV2 atlasClient,
- SourcelumeAtlasProperties properties,
- ObjectMapper objectMapper) {
- return new DefaultAtlasAdapter(atlasClient, properties, objectMapper);
- }
-}
diff --git a/sourcelume-registry-atlas-adapter/src/main/java/org/apache/sourcelume/registry/atlas/adapter/config/SourcelumeAtlasProperties.java b/sourcelume-registry-atlas-adapter/src/main/java/org/apache/sourcelume/registry/atlas/adapter/config/SourcelumeAtlasProperties.java
index f04cf48..5b9f55d 100644
--- a/sourcelume-registry-atlas-adapter/src/main/java/org/apache/sourcelume/registry/atlas/adapter/config/SourcelumeAtlasProperties.java
+++ b/sourcelume-registry-atlas-adapter/src/main/java/org/apache/sourcelume/registry/atlas/adapter/config/SourcelumeAtlasProperties.java
@@ -16,114 +16,64 @@
*/
package org.apache.sourcelume.registry.atlas.adapter.config;
-import org.springframework.boot.context.properties.ConfigurationProperties;
+import io.smallrye.config.ConfigMapping;
+import io.smallrye.config.WithDefault;
-import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
+import java.util.Optional;
/**
- * Configuration properties for Atlas connectivity and Sourcelume schema bootstrap.
+ * Configuration for Atlas connectivity and Sourcelume schema bootstrap, bound from
+ * MicroProfile Config properties under the {@code sourcelume.atlas} prefix.
+ *
+ * <p>Quarkus/SmallRye Config equivalent of the Spring Boot
+ * {@code @ConfigurationProperties(prefix = "sourcelume.atlas")} used in the Spring
+ * spike. The password-file resolution mirrors that behavior.
*/
-@ConfigurationProperties(prefix = "sourcelume.atlas")
-public class SourcelumeAtlasProperties {
+@ConfigMapping(prefix = "sourcelume.atlas")
+public interface SourcelumeAtlasProperties {
- private String url = "http://localhost:21000";
- private String user = "admin";
- private String password = "atlasR0cks!";
- private String passwordFile;
- private String specContextResource = "context/0.0.1/sourcelume.jsonld";
- private String typedefsResource = "models/sourcelume/sourcelume_model.json";
- private boolean bootstrapOnStartup = true;
- private int maxRetries = 5;
- private long retryDelayMs = 2000L;
+ @WithDefault("http://localhost:21000")
+ String url();
- public String getUrl() {
- return url;
- }
+ @WithDefault("admin")
+ String user();
- public void setUrl(String url) {
- this.url = url;
- }
+ @WithDefault("atlasR0cks!")
+ String password();
- public String getUser() {
- return user;
- }
+ Optional<String> passwordFile();
- public void setUser(String user) {
- this.user = user;
- }
+ @WithDefault("context/0.0.1/sourcelume.jsonld")
+ String specContextResource();
- public String getPassword() {
- return password;
- }
+ @WithDefault("models/sourcelume/sourcelume_model.json")
+ String typedefsResource();
- public void setPassword(String password) {
- this.password = password;
- }
+ @WithDefault("true")
+ boolean bootstrapOnStartup();
- public String getPasswordFile() {
- return passwordFile;
- }
+ @WithDefault("5")
+ int maxRetries();
- public void setPasswordFile(String passwordFile) {
- this.passwordFile = passwordFile;
- }
-
- public String getSpecContextResource() {
- return specContextResource;
- }
-
- public void setSpecContextResource(String specContextResource) {
- this.specContextResource = specContextResource;
- }
-
- public String getTypedefsResource() {
- return typedefsResource;
- }
-
- public void setTypedefsResource(String typedefsResource) {
- this.typedefsResource = typedefsResource;
- }
-
- public boolean isBootstrapOnStartup() {
- return bootstrapOnStartup;
- }
-
- public void setBootstrapOnStartup(boolean bootstrapOnStartup) {
- this.bootstrapOnStartup = bootstrapOnStartup;
- }
-
- public int getMaxRetries() {
- return maxRetries;
- }
-
- public void setMaxRetries(int maxRetries) {
- this.maxRetries = maxRetries;
- }
-
- public long getRetryDelayMs() {
- return retryDelayMs;
- }
-
- public void setRetryDelayMs(long retryDelayMs) {
- this.retryDelayMs = retryDelayMs;
- }
+ @WithDefault("2000")
+ long retryDelayMs();
/**
* Resolves the Atlas password, preferring {@code passwordFile} if configured and accessible.
*/
- public String getResolvedPassword() {
- if (passwordFile != null && !passwordFile.isBlank()) {
- Path path = Path.of(passwordFile);
+ default String resolvedPassword() {
+ if (passwordFile().isPresent()) {
+ Path path = Path.of(passwordFile().get());
if (Files.exists(path)) {
try {
return Files.readString(path).trim();
- } catch (IOException e) {
- throw new IllegalStateException("Failed to read password from file: " + passwordFile, e);
+ } catch (Exception e) {
+ throw new IllegalStateException("Failed to read password from file: " + passwordFile().get(), e);
}
}
}
- return password;
+ return password();
}
}
diff --git a/sourcelume-registry-atlas-adapter/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/sourcelume-registry-atlas-adapter/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
deleted file mode 100644
index 6b7ad57..0000000
--- a/sourcelume-registry-atlas-adapter/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
+++ /dev/null
@@ -1 +0,0 @@
-org.apache.sourcelume.registry.atlas.adapter.config.AtlasAdapterConfiguration
diff --git a/sourcelume-registry-atlas-adapter/src/test/java/org/apache/sourcelume/registry/atlas/adapter/AtlasClientCompatibilityTest.java b/sourcelume-registry-atlas-adapter/src/test/java/org/apache/sourcelume/registry/atlas/adapter/AtlasClientCompatibilityTest.java
deleted file mode 100644
index 220a821..0000000
--- a/sourcelume-registry-atlas-adapter/src/test/java/org/apache/sourcelume/registry/atlas/adapter/AtlasClientCompatibilityTest.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * 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.
- */
-package org.apache.sourcelume.registry.atlas.adapter;
-
-import com.sun.jersey.api.client.Client;
-import org.apache.atlas.AtlasClientV2;
-import org.apache.atlas.model.typedef.AtlasTypesDef;
-import org.junit.jupiter.api.Test;
-
-import javax.ws.rs.core.MediaType;
-import java.lang.reflect.Field;
-import java.lang.reflect.Method;
-
-import static org.junit.jupiter.api.Assertions.assertNotNull;
-
-class AtlasClientCompatibilityTest {
-
- @Test
- void testAtlasClientV2JerseyJsonProviderRegistered() throws Exception {
- AtlasClientV2 client = new AtlasClientV2(new String[]{"http://localhost:21000"}, new String[]{"admin", "admin"});
- assertNotNull(client);
-
- Field contextField = org.apache.atlas.AtlasBaseClient.class.getDeclaredField("atlasClientContext");
- contextField.setAccessible(true);
- Object context = contextField.get(client);
- Method getClientMethod = context.getClass().getDeclaredMethod("getClient");
- getClientMethod.setAccessible(true);
- Client jerseyClient = (Client) getClientMethod.invoke(context);
-
- Object reader = jerseyClient.getProviders().getMessageBodyReader(
- AtlasTypesDef.class,
- AtlasTypesDef.class,
- new java.lang.annotation.Annotation[0],
- MediaType.APPLICATION_JSON_TYPE
- );
- assertNotNull(reader, "Jersey client in AtlasClientV2 must register Jackson JSON MessageBodyReader for AtlasTypesDef");
- }
-}
diff --git a/sourcelume-registry-atlas-adapter/src/test/java/org/apache/sourcelume/registry/atlas/adapter/DefaultAtlasAdapterTest.java b/sourcelume-registry-atlas-adapter/src/test/java/org/apache/sourcelume/registry/atlas/adapter/DefaultAtlasAdapterTest.java
deleted file mode 100644
index 682b4f9..0000000
--- a/sourcelume-registry-atlas-adapter/src/test/java/org/apache/sourcelume/registry/atlas/adapter/DefaultAtlasAdapterTest.java
+++ /dev/null
@@ -1,154 +0,0 @@
-/*
- * 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.
- */
-package org.apache.sourcelume.registry.atlas.adapter;
-
-import com.fasterxml.jackson.databind.ObjectMapper;
-import org.apache.atlas.AtlasClientV2;
-import org.apache.atlas.AtlasServiceException;
-import org.apache.atlas.model.instance.AtlasEntity;
-import org.apache.atlas.model.instance.AtlasEntityHeader;
-import org.apache.atlas.model.instance.EntityMutationResponse;
-import org.apache.atlas.model.instance.EntityMutations;
-import org.apache.atlas.model.typedef.AtlasTypesDef;
-import org.apache.sourcelume.registry.atlas.adapter.config.SourcelumeAtlasProperties;
-import org.apache.sourcelume.registry.atlas.adapter.exception.AtlasAdapterException;
-import org.apache.sourcelume.registry.common.dto.SourcelumeDatasetDto;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-import org.mockito.Mockito;
-
-import java.util.Collections;
-import java.util.List;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertFalse;
-import static org.junit.jupiter.api.Assertions.assertNotNull;
-import static org.junit.jupiter.api.Assertions.assertNull;
-import static org.junit.jupiter.api.Assertions.assertThrows;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyMap;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-class DefaultAtlasAdapterTest {
-
- private AtlasClientV2 atlasClient;
- private SourcelumeAtlasProperties properties;
- private ObjectMapper objectMapper;
- private DefaultAtlasAdapter adapter;
-
- @BeforeEach
- void setUp() {
- atlasClient = Mockito.mock(AtlasClientV2.class);
- properties = new SourcelumeAtlasProperties();
- objectMapper = new ObjectMapper();
- adapter = new DefaultAtlasAdapter(atlasClient, properties, objectMapper);
- }
-
- @Test
- void shouldCheckServerReady() throws Exception {
- when(atlasClient.isServerReady()).thenReturn(true);
- assertTrue(adapter.isServerReady());
-
- when(atlasClient.isServerReady()).thenThrow(new RuntimeException("Connection refused"));
- assertFalse(adapter.isServerReady());
- }
-
- @Test
- void shouldLoadTypeDefsFromResource() {
- AtlasTypesDef typesDef = adapter.loadTypeDefs("models/sourcelume/sourcelume_model.json");
- assertNotNull(typesDef);
- assertNotNull(typesDef.getEntityDefs());
- assertEquals(1, typesDef.getEntityDefs().size());
- assertEquals("sourcelume_dataset", typesDef.getEntityDefs().get(0).getName());
- }
-
- @Test
- void shouldThrowWhenTypeDefResourceNotFound() {
- assertThrows(AtlasAdapterException.class, () ->
- adapter.loadTypeDefs("models/nonexistent.json"));
- }
-
- @Test
- void shouldRegisterTypeDefsOnCreateSuccess() throws Exception {
- AtlasTypesDef input = new AtlasTypesDef();
- AtlasTypesDef created = new AtlasTypesDef();
- when(atlasClient.createAtlasTypeDefs(input)).thenReturn(created);
-
- AtlasTypesDef result = adapter.registerOrUpdateTypeDefs(input);
- assertEquals(created, result);
- verify(atlasClient).createAtlasTypeDefs(input);
- }
-
- @Test
- void shouldFallbackToUpdateTypeDefsWhenCreateFails() throws Exception {
- AtlasTypesDef input = new AtlasTypesDef();
- AtlasTypesDef updated = new AtlasTypesDef();
- AtlasServiceException serviceEx = new AtlasServiceException(new Exception("Types already exist"));
- when(atlasClient.createAtlasTypeDefs(input)).thenThrow(serviceEx);
- when(atlasClient.updateAtlasTypeDefs(input)).thenReturn(updated);
-
- AtlasTypesDef result = adapter.registerOrUpdateTypeDefs(input);
- assertEquals(updated, result);
- verify(atlasClient).createAtlasTypeDefs(input);
- verify(atlasClient).updateAtlasTypeDefs(input);
- }
-
- @Test
- void shouldCreateOrUpdateDatasetEntity() throws Exception {
- SourcelumeDatasetDto dataset = new SourcelumeDatasetDto(
- "sourcelume.dataset.test@cluster", "test-dataset", "s3://bucket/test", "Apache-2.0");
-
- EntityMutationResponse response = new EntityMutationResponse();
- AtlasEntityHeader header = new AtlasEntityHeader();
- header.setGuid("guid-1234");
- response.addEntity(EntityMutations.EntityOperation.CREATE, header);
-
- when(atlasClient.createEntity(any(AtlasEntity.AtlasEntityWithExtInfo.class))).thenReturn(response);
-
- String guid = adapter.createOrUpdateDatasetEntity(dataset);
- assertEquals("guid-1234", guid);
- assertEquals("guid-1234", dataset.getGuid());
- }
-
- @Test
- void shouldGetDatasetByQualifiedName() throws Exception {
- AtlasEntity entity = new AtlasEntity("sourcelume_dataset");
- entity.setGuid("guid-5678");
- entity.setAttribute("qualifiedName", "sourcelume.dataset.test@cluster");
- entity.setAttribute("name", "test-dataset");
- entity.setAttribute("sourceUri", "s3://bucket/test");
- entity.setAttribute("licenseId", "Apache-2.0");
-
- AtlasEntity.AtlasEntityWithExtInfo extInfo = new AtlasEntity.AtlasEntityWithExtInfo(entity);
- when(atlasClient.getEntityByAttribute(eq("sourcelume_dataset"), anyMap())).thenReturn(extInfo);
-
- SourcelumeDatasetDto dto = adapter.getDatasetByQualifiedName("sourcelume.dataset.test@cluster");
- assertNotNull(dto);
- assertEquals("guid-5678", dto.getGuid());
- assertEquals("sourcelume.dataset.test@cluster", dto.getQualifiedName());
- }
-
- @Test
- void shouldReturnNullWhenDatasetNotFound() throws Exception {
- when(atlasClient.getEntityByAttribute(eq("sourcelume_dataset"), anyMap())).thenReturn(null);
- SourcelumeDatasetDto dto = adapter.getDatasetByQualifiedName("nonexistent");
- assertNull(dto);
- }
-}
diff --git a/sourcelume-registry-atlas-adapter/src/test/java/org/apache/sourcelume/registry/atlas/adapter/RestAtlasAdapterTest.java b/sourcelume-registry-atlas-adapter/src/test/java/org/apache/sourcelume/registry/atlas/adapter/RestAtlasAdapterTest.java
new file mode 100644
index 0000000..bb6323a
--- /dev/null
+++ b/sourcelume-registry-atlas-adapter/src/test/java/org/apache/sourcelume/registry/atlas/adapter/RestAtlasAdapterTest.java
@@ -0,0 +1,54 @@
+/*
+ * 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.
+ */
+package org.apache.sourcelume.registry.atlas.adapter;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import io.quarkus.test.junit.QuarkusTest;
+import jakarta.inject.Inject;
+import org.apache.sourcelume.registry.atlas.adapter.AtlasAdapter.TypeDefinitionModel;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * Tests {@link RestAtlasAdapter#loadTypeDefs(String)} — the one proof step that does
+ * not need a running Atlas: it reads the bundled typedef JSON from the classpath and
+ * parses it.
+ *
+ * <p>This is the Quarkus equivalent of the Spring spike's AtlasClientCompatibilityTest,
+ * but without any atlas-client-v2 / Mockito / ByteBuddy dependency — the thin adapter
+ * is a plain CDI bean with no concrete external class to mock.
+ */
+@QuarkusTest
+class RestAtlasAdapterTest {
+
+ @Inject
+ RestAtlasAdapter adapter;
+
+ @Test
+ void shouldLoadBundledTypeDefs() {
+ TypeDefinitionModel td = adapter.loadTypeDefs("models/sourcelume/sourcelume_model.json");
+ assertNotNull(td);
+ assertEquals("models/sourcelume/sourcelume_model.json", td.getSourceResource());
+ assertTrue(td.getRawJson().length > 0);
+ assertEquals(1, td.getEntityDefCount(), "expected exactly one entity def (sourcelume_dataset)");
+ String json = new String(td.getRawJson());
+ assertTrue(json.contains("sourcelume_dataset"), "typedef JSON should contain sourcelume_dataset");
+ }
+}
diff --git a/sourcelume-registry-atlas-adapter/src/test/java/org/apache/sourcelume/registry/atlas/adapter/config/SourcelumeAtlasPropertiesTest.java b/sourcelume-registry-atlas-adapter/src/test/java/org/apache/sourcelume/registry/atlas/adapter/config/SourcelumeAtlasPropertiesTest.java
deleted file mode 100644
index fd656e0..0000000
--- a/sourcelume-registry-atlas-adapter/src/test/java/org/apache/sourcelume/registry/atlas/adapter/config/SourcelumeAtlasPropertiesTest.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * 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.
- */
-package org.apache.sourcelume.registry.atlas.adapter.config;
-
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.io.TempDir;
-
-import java.io.IOException;
-import java.nio.file.Files;
-import java.nio.file.Path;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-
-class SourcelumeAtlasPropertiesTest {
-
- @Test
- void shouldReturnDefaultValues() {
- SourcelumeAtlasProperties properties = new SourcelumeAtlasProperties();
- assertEquals("http://localhost:21000", properties.getUrl());
- assertEquals("admin", properties.getUser());
- assertEquals("atlasR0cks!", properties.getPassword());
- assertEquals("atlasR0cks!", properties.getResolvedPassword());
- assertEquals("models/sourcelume/sourcelume_model.json", properties.getTypedefsResource());
- }
-
- @Test
- void shouldResolvePasswordFromFile(@TempDir Path tempDir) throws IOException {
- Path secretFile = tempDir.resolve("atlas-secret.txt");
- Files.writeString(secretFile, "secretPassword123\n");
-
- SourcelumeAtlasProperties properties = new SourcelumeAtlasProperties();
- properties.setPassword("fallbackPassword");
- properties.setPasswordFile(secretFile.toString());
-
- assertEquals("secretPassword123", properties.getResolvedPassword());
- }
-}
diff --git a/sourcelume-registry-atlas-adapter/src/test/resources/atlas-application.properties b/sourcelume-registry-atlas-adapter/src/test/resources/atlas-application.properties
deleted file mode 100644
index fef9186..0000000
--- a/sourcelume-registry-atlas-adapter/src/test/resources/atlas-application.properties
+++ /dev/null
@@ -1,3 +0,0 @@
-# Minimal atlas-application.properties for testing
-atlas.graph.storage.backend=hbase2
-atlas.graph.index.search.backend=solr
diff --git a/sourcelume-registry-common/pom.xml b/sourcelume-registry-common/pom.xml
index ec42c24..ba94946 100644
--- a/sourcelume-registry-common/pom.xml
+++ b/sourcelume-registry-common/pom.xml
@@ -1,25 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
- <!--
- 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.
- -->
-
<modelVersion>4.0.0</modelVersion>
<parent>
@@ -33,8 +32,9 @@
<packaging>jar</packaging>
<description>
- Shared DTOs mapped from sourcelume-spec, validation annotations (jakarta.validation),
- exceptions, and resource loader utilities.
+ Shared DTOs mapped from sourcelume-spec, Jakarta validation annotations,
+ exceptions, and resource loader utilities. Vendor-neutral: no Spring or
+ Quarkus runtime dependency, only Jakarta Validation API + Jackson.
</description>
<dependencies>
@@ -42,9 +42,11 @@
<groupId>org.apache.sourcelume</groupId>
<artifactId>sourcelume-spec</artifactId>
</dependency>
+ <!-- Jakarta Validation API (implemented at runtime by whoever wires the app;
+ common itself only depends on the API). -->
<dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-validation</artifactId>
+ <groupId>jakarta.validation</groupId>
+ <artifactId>jakarta.validation-api</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
@@ -58,9 +60,15 @@
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
+ <!-- Test: Jakarta Validation provider + Quarkus test harness -->
+ <dependency>
+ <groupId>io.quarkus</groupId>
+ <artifactId>quarkus-junit5</artifactId>
+ <scope>test</scope>
+ </dependency>
<dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-test</artifactId>
+ <groupId>io.quarkus</groupId>
+ <artifactId>quarkus-hibernate-validator</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
diff --git a/sourcelume-registry-ingest-worker/Dockerfile b/sourcelume-registry-ingest-worker/Dockerfile
index 1f9477c..07c7b0b 100644
--- a/sourcelume-registry-ingest-worker/Dockerfile
+++ b/sourcelume-registry-ingest-worker/Dockerfile
@@ -15,19 +15,20 @@
# limitations under the License.
#
-FROM eclipse-temurin:17-jre
-WORKDIR /app
+# Quarkus JVM-mode container image.
+# Quarkus builds the runnable jar under target/quarkus-app/; the container just needs
+# a JRE and the lib dir. This is the simplest JVM-mode Dockerfile — Quarkus also
+# supports building a native image (GraalVM) which would shrink this further, but
+# for the spike comparison we keep it JVM-mode to match the Spring spike's footprint.
-# Install curl for Actuator health checks
-RUN apt-get update && apt-get install -y --no-install-recommends curl && rm -rf /var/lib/apt/lists/*
+FROM eclipse-temurin:21-jre
-# Copy repackaged Spring Boot executable jar
-COPY sourcelume-registry-ingest-worker/target/sourcelume-registry-ingest-worker-*.jar /app/app.jar
+WORKDIR /work
-ENV JAVA_TOOL_OPTIONS="-XX:+UseG1GC -XX:MaxRAMPercentage=75.0"
-EXPOSE 8082 9001
+COPY target/quarkus-app /work/quarkus-app
-HEALTHCHECK --interval=10s --timeout=5s --retries=3 --start-period=30s \
- CMD curl -fsS http://localhost:9001/actuator/health/liveness || exit 1
+ENV JAVA_OPTS="-XX:+UseG1GC -XX:MaxRAMPercentage=75.0"
-ENTRYPOINT ["java", "-jar", "/app/app.jar"]
+EXPOSE 8082
+
+ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar /work/quarkus-app/quarkus-run.jar"]
diff --git a/sourcelume-registry-ingest-worker/pom.xml b/sourcelume-registry-ingest-worker/pom.xml
index f8e8869..4b235b6 100644
--- a/sourcelume-registry-ingest-worker/pom.xml
+++ b/sourcelume-registry-ingest-worker/pom.xml
@@ -1,26 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
- <!--
-
- 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.
- -->
-
<modelVersion>4.0.0</modelVersion>
<parent>
@@ -30,59 +28,63 @@
</parent>
<artifactId>sourcelume-registry-ingest-worker</artifactId>
- <name>Apache Sourcelume Registry - Ingest Worker</name>
+ <name>Apache Sourcelume Registry - Ingest Worker (Quarkus)</name>
<packaging>jar</packaging>
<description>
- STEEL THREAD ONLY. Proves that this module can (a) resolve and read a resource bundled in
- the locally-built sourcelume-spec jar, (b) load the Sourcelume Atlas typedefs bundled in
- sourcelume-registry-typedefs, and (c) register them against a running Apache Atlas instance.
-
- Does NOT yet implement: queue consumption, signature verification against sourcelume-attest,
- or schema/SHACL validation against sourcelume-spec.
+ Quarkus application: the Sourcelume ingest worker. On startup it verifies
+ the sourcelume-spec jar resource, loads the Sourcelume typedefs, and
+ registers them with Atlas via the thin REST adapter. Exposes a readiness
+ health check via SmallRye Health on the management interface.
</description>
<dependencies>
<dependency>
<groupId>org.apache.sourcelume</groupId>
<artifactId>sourcelume-registry-common</artifactId>
+ <version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.apache.sourcelume</groupId>
<artifactId>sourcelume-registry-typedefs</artifactId>
+ <version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.apache.sourcelume</groupId>
<artifactId>sourcelume-registry-atlas-adapter</artifactId>
+ <version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.apache.sourcelume</groupId>
<artifactId>sourcelume-spec</artifactId>
</dependency>
+
<dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter</artifactId>
+ <groupId>io.quarkus</groupId>
+ <artifactId>quarkus-arc</artifactId>
</dependency>
<dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-web</artifactId>
+ <groupId>io.quarkus</groupId>
+ <artifactId>quarkus-rest</artifactId>
</dependency>
<dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-actuator</artifactId>
+ <groupId>io.quarkus</groupId>
+ <artifactId>quarkus-smallrye-health</artifactId>
</dependency>
<dependency>
- <groupId>io.micrometer</groupId>
- <artifactId>micrometer-registry-prometheus</artifactId>
+ <groupId>io.quarkus</groupId>
+ <artifactId>quarkus-micrometer-registry-prometheus</artifactId>
</dependency>
+
+ <!-- Test -->
<dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-configuration-processor</artifactId>
- <optional>true</optional>
+ <groupId>io.quarkus</groupId>
+ <artifactId>quarkus-junit5</artifactId>
+ <scope>test</scope>
</dependency>
<dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-test</artifactId>
+ <groupId>io.rest-assured</groupId>
+ <artifactId>rest-assured</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
@@ -90,12 +92,14 @@
<build>
<plugins>
<plugin>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-maven-plugin</artifactId>
+ <groupId>io.quarkus.platform</groupId>
+ <artifactId>quarkus-maven-plugin</artifactId>
+ <version>${quarkus.version}</version>
+ <extensions>true</extensions>
<executions>
<execution>
<goals>
- <goal>repackage</goal>
+ <goal>build</goal>
</goals>
</execution>
</executions>
diff --git a/sourcelume-registry-ingest-worker/src/main/java/org/apache/sourcelume/registry/ingest/worker/SourcelumeWorkerApplication.java b/sourcelume-registry-ingest-worker/src/main/java/org/apache/sourcelume/registry/ingest/worker/SourcelumeWorkerApplication.java
index 334c9d1..0c5a77b 100644
--- a/sourcelume-registry-ingest-worker/src/main/java/org/apache/sourcelume/registry/ingest/worker/SourcelumeWorkerApplication.java
+++ b/sourcelume-registry-ingest-worker/src/main/java/org/apache/sourcelume/registry/ingest/worker/SourcelumeWorkerApplication.java
@@ -2,33 +2,38 @@
* 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.
- */
+ 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.
+ */
package org.apache.sourcelume.registry.ingest.worker;
-import org.apache.sourcelume.registry.atlas.adapter.config.SourcelumeAtlasProperties;
-import org.springframework.boot.SpringApplication;
-import org.springframework.boot.autoconfigure.SpringBootApplication;
-import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import io.quarkus.runtime.Quarkus;
+import io.quarkus.runtime.QuarkusApplication;
+import io.quarkus.runtime.annotations.QuarkusMain;
/**
- * Spring Boot entry point for the Sourcelume Ingest Worker service.
+ * Quarkus entry point for the Sourcelume Ingest Worker service.
+ *
+ * <p>Quarkus equivalent of the Spring Boot {@code @SpringBootApplication} used in
+ * the Spring spike. The actual startup work (spec verification + typedef
+ * registration) is in {@code AtlasBootstrapObserver}, observing the CDI startup
+ * event.
*/
-@SpringBootApplication
-@EnableConfigurationProperties(SourcelumeAtlasProperties.class)
-public class SourcelumeWorkerApplication {
+@QuarkusMain
+public class SourcelumeWorkerApplication implements QuarkusApplication {
- public static void main(String[] args) {
- SpringApplication.run(SourcelumeWorkerApplication.class, args);
+ @Override
+ public int run(String... args) {
+ Quarkus.waitForExit();
+ return 0;
}
}
diff --git a/sourcelume-registry-ingest-worker/src/main/java/org/apache/sourcelume/registry/ingest/worker/WiringSpike.java b/sourcelume-registry-ingest-worker/src/main/java/org/apache/sourcelume/registry/ingest/worker/WiringSpike.java
index f962dfb..86992f4 100644
--- a/sourcelume-registry-ingest-worker/src/main/java/org/apache/sourcelume/registry/ingest/worker/WiringSpike.java
+++ b/sourcelume-registry-ingest-worker/src/main/java/org/apache/sourcelume/registry/ingest/worker/WiringSpike.java
@@ -2,92 +2,93 @@
* 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.
- */
+ 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.
+ */
package org.apache.sourcelume.registry.ingest.worker;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import org.apache.atlas.AtlasClientV2;
-import org.apache.atlas.model.typedef.AtlasTypesDef;
+import io.quarkus.runtime.Quarkus;
import org.apache.sourcelume.registry.atlas.adapter.AtlasAdapter;
-import org.apache.sourcelume.registry.atlas.adapter.DefaultAtlasAdapter;
import org.apache.sourcelume.registry.atlas.adapter.config.SourcelumeAtlasProperties;
import org.apache.sourcelume.registry.common.spec.SpecResourceLoader;
/**
- * Steel thread for the Sourcelume Registry.
+ * Steel thread for the Sourcelume Registry (Quarkus spike).
+ *
+ * <p>Runnable proof that the four pieces of wiring connect, mirroring Jamie's
+ * WiringSpike from the Spring spike branch but using the Quarkus + thin REST
+ * client stack:
*
- * <p>Proves four pieces of wiring connect:
* <ol>
- * <li>Resolves spec resource via SpecResourceLoader in sourcelume-registry-common.</li>
- * <li>Loads typedefs from sourcelume-registry-typedefs via AtlasAdapter.</li>
- * <li>Constructs Atlas adapter connection.</li>
- * <li>Registers typedefs against Atlas instance.</li>
+ * <li>This module can resolve a resource bundled inside the locally-built
+ * {@code sourcelume-spec} jar (proves the spec-jar dependency is real).</li>
+ * <li>This module can load the Sourcelume typedefs bundled in
+ * {@code sourcelume-registry-typedefs} (proves the typedefs module is usable).</li>
+ * <li>The thin REST {@link AtlasAdapter} can reach a running Atlas instance.</li>
+ * <li>Atlas will accept the Sourcelume typedefs (proves the typedef JSON is well-formed
+ * and the REST wire format is correct).</li>
* </ol>
+ *
+ * <p>Run directly via {@code mvn -pl sourcelume-registry-ingest-worker exec:java}
+ * against a running Atlas, or just start the Quarkus app and let
+ * {@code AtlasBootstrapRunner} do the same work on startup.
*/
public final class WiringSpike {
- private static final String SPEC_CONTEXT_RESOURCE = SpecResourceLoader.DEFAULT_CONTEXT_RESOURCE;
- private static final String TYPEDEFS_RESOURCE = "models/sourcelume/sourcelume_model.json";
-
public static void main(String[] args) {
- System.out.println("Sourcelume Registry steel thread starting...");
+ // When run via exec:java outside the Quarkus container, this performs the same
+ // four proof steps the Spring WiringSpike did. Inside Quarkus, the
+ // AtlasBootstrapRunner does this on startup — this main is the manual fallback.
+ String atlasUrl = args.length > 0 ? args[0] : System.getenv().getOrDefault("SOURCELUME_ATLAS_URL", "http://localhost:21000");
+ String user = System.getenv().getOrDefault("SOURCELUME_ATLAS_USER", "admin");
+ String password = System.getenv().getOrDefault("SOURCELUME_ATLAS_PASSWORD", "atlasR0cks!");
- readSpecContextFromJar();
+ try {
+ // Step 1: spec jar resource resolvable
+ byte[] ctx = SpecResourceLoader.loadResourceBytes(SpecResourceLoader.DEFAULT_CONTEXT_RESOURCE);
+ System.out.println("1. spec context read: " + ctx.length + " bytes");
- SourcelumeAtlasProperties properties = new SourcelumeAtlasProperties();
- String atlasUrl = firstNonBlank(
- args.length > 0 ? args[0] : null,
- System.getenv("SOURCELUME_ATLAS_URL"),
- "http://localhost:21000");
- String username = firstNonBlank(System.getenv("SOURCELUME_ATLAS_USER"), "admin");
- String password = firstNonBlank(System.getenv("SOURCELUME_ATLAS_PASSWORD"), "atlasR0cks!");
+ // Step 2: typedefs resource resolvable
+ String typedefs = SpecResourceLoader.loadResourceString("models/sourcelume/sourcelume_model.json");
+ System.out.println("2. typedefs loaded: " + (typedefs.contains("sourcelume_dataset") ? "OK" : "MISSING sourcelume_dataset"));
- properties.setUrl(atlasUrl);
- properties.setUser(username);
- properties.setPassword(password);
-
- AtlasClientV2 atlasClient = new AtlasClientV2(new String[]{atlasUrl}, new String[]{username, password});
- AtlasAdapter atlasAdapter = new DefaultAtlasAdapter(atlasClient, properties, new ObjectMapper());
-
- AtlasTypesDef typesDef = atlasAdapter.loadTypeDefs(TYPEDEFS_RESOURCE);
- System.out.println("Loaded " + (typesDef.getEntityDefs() != null ? typesDef.getEntityDefs().size() : 0)
- + " entity def(s) from " + TYPEDEFS_RESOURCE);
-
- AtlasTypesDef created = atlasAdapter.registerOrUpdateTypeDefs(typesDef);
- System.out.println("Atlas accepted the typedefs. Entity types: "
- + (created.getEntityDefs() != null ? created.getEntityDefs().size() : 0));
-
- System.out.println("Steel thread complete: sourcelume-spec jar resource read, "
- + "Sourcelume typedefs loaded, and Atlas accepted them.");
- }
-
- private static void readSpecContextFromJar() {
- byte[] bytes = SpecResourceLoader.loadResourceBytes(SPEC_CONTEXT_RESOURCE);
- System.out.println("Read " + bytes.length
- + " bytes from sourcelume-spec jar resource: " + SPEC_CONTEXT_RESOURCE);
- }
+ // Steps 3 & 4 require the AtlasAdapter, which in Quarkus is CDI-managed.
+ // For the standalone manual run, we construct the thin client directly.
+ com.fasterxml.jackson.databind.ObjectMapper om = new com.fasterxml.jackson.databind.ObjectMapper();
+ org.apache.sourcelume.registry.atlas.adapter.RestAtlasAdapter adapter =
+ new org.apache.sourcelume.registry.atlas.adapter.RestAtlasAdapter(
+ new StandaloneProperties(atlasUrl, user, password), om);
- private static String firstNonBlank(String... values) {
- for (String value : values) {
- if (value != null && !value.isBlank()) {
- return value;
- }
+ System.out.println("3. atlas reachable: " + adapter.isServerReady());
+ AtlasAdapter.TypeDefinitionModel td = adapter.registerTypeDefsFromResource("models/sourcelume/sourcelume_model.json");
+ System.out.println("4. atlas accepted typedefs, entity types: " + td.getEntityDefCount());
+ System.out.println("Steel thread complete: sourcelume-spec jar resource read, Sourcelume typedefs loaded, and Atlas accepted them.");
+ } catch (Exception e) {
+ System.err.println("Steel thread FAILED: " + e.getMessage());
+ e.printStackTrace();
+ System.exit(1);
}
- return null;
}
- private WiringSpike() {
- // Entry point only.
+ /** Minimal properties impl for the standalone run path (CDI not available). */
+ record StandaloneProperties(String url, String user, String password) implements SourcelumeAtlasProperties {
+ @Override public String url() { return url; }
+ @Override public String user() { return user; }
+ @Override public String password() { return password; }
+ @Override public java.util.Optional<String> passwordFile() { return java.util.Optional.empty(); }
+ @Override public String specContextResource() { return "context/0.0.1/sourcelume.jsonld"; }
+ @Override public String typedefsResource() { return "models/sourcelume/sourcelume_model.json"; }
+ @Override public boolean bootstrapOnStartup() { return false; }
+ @Override public int maxRetries() { return 1; }
+ @Override public long retryDelayMs() { return 0; }
}
}
diff --git a/sourcelume-registry-ingest-worker/src/main/java/org/apache/sourcelume/registry/ingest/worker/actuator/AtlasHealthIndicator.java b/sourcelume-registry-ingest-worker/src/main/java/org/apache/sourcelume/registry/ingest/worker/actuator/AtlasHealthIndicator.java
index da563ac..c5ffc8e 100644
--- a/sourcelume-registry-ingest-worker/src/main/java/org/apache/sourcelume/registry/ingest/worker/actuator/AtlasHealthIndicator.java
+++ b/sourcelume-registry-ingest-worker/src/main/java/org/apache/sourcelume/registry/ingest/worker/actuator/AtlasHealthIndicator.java
@@ -2,60 +2,69 @@
* 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.
- */
+ 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.
+ */
package org.apache.sourcelume.registry.ingest.worker.actuator;
+import jakarta.enterprise.context.ApplicationScoped;
+import jakarta.inject.Inject;
import org.apache.sourcelume.registry.atlas.adapter.AtlasAdapter;
import org.apache.sourcelume.registry.atlas.adapter.config.SourcelumeAtlasProperties;
-import org.springframework.boot.actuate.health.Health;
-import org.springframework.boot.actuate.health.HealthIndicator;
-import org.springframework.stereotype.Component;
+import org.eclipse.microprofile.health.HealthCheck;
+import org.eclipse.microprofile.health.HealthCheckResponse;
+import org.eclipse.microprofile.health.Readiness;
/**
- * Actuator HealthIndicator for Apache Atlas connectivity using AtlasAdapter.
- * Contributes to readiness probe on management port 9001.
+ * SmallRye Health readiness check for Apache Atlas connectivity using AtlasAdapter.
+ *
+ * <p>Quarkus/MicroProfile Health equivalent of the Spring Boot Actuator
+ * {@code HealthIndicator} used in the Spring spike. Contributes to the readiness
+ * probe at {@code /q/health/ready}.
*/
-@Component
-public class AtlasHealthIndicator implements HealthIndicator {
+@Readiness
+@ApplicationScoped
+public class AtlasHealthIndicator implements HealthCheck {
private final AtlasAdapter atlasAdapter;
private final SourcelumeAtlasProperties properties;
+ @Inject
public AtlasHealthIndicator(AtlasAdapter atlasAdapter, SourcelumeAtlasProperties properties) {
this.atlasAdapter = atlasAdapter;
this.properties = properties;
}
@Override
- public Health health() {
+ public HealthCheckResponse call() {
try {
boolean ready = atlasAdapter.isServerReady();
if (ready) {
- return Health.up()
- .withDetail("atlasUrl", properties.getUrl())
- .withDetail("status", "CONNECTED")
- .build();
- } else {
- return Health.down()
- .withDetail("atlasUrl", properties.getUrl())
- .withDetail("status", "SERVER_NOT_READY")
+ return HealthCheckResponse.named("atlas")
+ .up()
+ .withData("atlasUrl", properties.url())
+ .withData("status", "CONNECTED")
.build();
}
+ return HealthCheckResponse.named("atlas")
+ .down()
+ .withData("atlasUrl", properties.url())
+ .withData("status", "SERVER_NOT_READY")
+ .build();
} catch (Exception e) {
- return Health.down(e)
- .withDetail("atlasUrl", properties.getUrl())
- .withDetail("error", e.getMessage())
+ return HealthCheckResponse.named("atlas")
+ .down()
+ .withData("atlasUrl", properties.url())
+ .withData("error", e.getMessage())
.build();
}
}
diff --git a/sourcelume-registry-ingest-worker/src/main/java/org/apache/sourcelume/registry/ingest/worker/runner/AtlasBootstrapRunner.java b/sourcelume-registry-ingest-worker/src/main/java/org/apache/sourcelume/registry/ingest/worker/runner/AtlasBootstrapRunner.java
index 9a2303a..d8b2702 100644
--- a/sourcelume-registry-ingest-worker/src/main/java/org/apache/sourcelume/registry/ingest/worker/runner/AtlasBootstrapRunner.java
+++ b/sourcelume-registry-ingest-worker/src/main/java/org/apache/sourcelume/registry/ingest/worker/runner/AtlasBootstrapRunner.java
@@ -2,55 +2,60 @@
* 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.
- */
+ 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.
+ */
package org.apache.sourcelume.registry.ingest.worker.runner;
+import io.quarkus.runtime.StartupEvent;
+import jakarta.enterprise.context.ApplicationScoped;
+import jakarta.enterprise.event.Observes;
+import jakarta.inject.Inject;
import org.apache.sourcelume.registry.atlas.adapter.config.SourcelumeAtlasProperties;
import org.apache.sourcelume.registry.ingest.worker.service.AtlasBootstrapService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.springframework.boot.ApplicationArguments;
-import org.springframework.boot.ApplicationRunner;
-import org.springframework.stereotype.Component;
/**
- * Startup runner that initiates the Sourcelume spec verification and typedef registration.
+ * Startup observer that initiates the Sourcelume spec verification and typedef registration.
+ *
+ * <p>Quarkus/CDI equivalent of the Spring Boot {@code ApplicationRunner} used in the
+ * Spring spike. Observes {@link StartupEvent} and runs the bootstrap sequence with
+ * retries, exactly as the Spring version did.
*/
-@Component
-public class AtlasBootstrapRunner implements ApplicationRunner {
+@ApplicationScoped
+public class AtlasBootstrapRunner {
private static final Logger log = LoggerFactory.getLogger(AtlasBootstrapRunner.class);
private final AtlasBootstrapService bootstrapService;
private final SourcelumeAtlasProperties properties;
+ @Inject
public AtlasBootstrapRunner(AtlasBootstrapService bootstrapService, SourcelumeAtlasProperties properties) {
this.bootstrapService = bootstrapService;
this.properties = properties;
}
- @Override
- public void run(ApplicationArguments args) {
- if (!properties.isBootstrapOnStartup()) {
+ void onStart(@Observes StartupEvent event) {
+ if (!properties.bootstrapOnStartup()) {
log.info("Atlas typedef bootstrap on startup is disabled via configuration.");
return;
}
log.info("Starting Sourcelume Registry bootstrap sequence...");
int attempts = 0;
- int maxAttempts = properties.getMaxRetries();
- long delay = properties.getRetryDelayMs();
+ int maxAttempts = properties.maxRetries();
+ long delay = properties.retryDelayMs();
while (attempts < maxAttempts) {
attempts++;
@@ -59,7 +64,6 @@ public class AtlasBootstrapRunner implements ApplicationRunner {
log.info("Sourcelume Registry bootstrap completed successfully.");
return;
}
-
if (attempts < maxAttempts) {
log.warn("Atlas bootstrap failed, retrying in {} ms...", delay);
try {
@@ -71,7 +75,6 @@ public class AtlasBootstrapRunner implements ApplicationRunner {
}
}
}
-
log.warn("Sourcelume Registry bootstrap could not complete all steps after {} attempt(s). Service will remain active.", attempts);
}
}
diff --git a/sourcelume-registry-ingest-worker/src/main/java/org/apache/sourcelume/registry/ingest/worker/service/AtlasBootstrapService.java b/sourcelume-registry-ingest-worker/src/main/java/org/apache/sourcelume/registry/ingest/worker/service/AtlasBootstrapService.java
index 3962ea1..154cb81 100644
--- a/sourcelume-registry-ingest-worker/src/main/java/org/apache/sourcelume/registry/ingest/worker/service/AtlasBootstrapService.java
+++ b/sourcelume-registry-ingest-worker/src/main/java/org/apache/sourcelume/registry/ingest/worker/service/AtlasBootstrapService.java
@@ -2,33 +2,38 @@
* 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.
- */
+ 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.
+ */
package org.apache.sourcelume.registry.ingest.worker.service;
-import org.apache.atlas.model.typedef.AtlasTypesDef;
+import jakarta.enterprise.context.ApplicationScoped;
+import jakarta.inject.Inject;
import org.apache.sourcelume.registry.atlas.adapter.AtlasAdapter;
import org.apache.sourcelume.registry.atlas.adapter.config.SourcelumeAtlasProperties;
import org.apache.sourcelume.registry.common.spec.SpecResourceLoader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.springframework.stereotype.Service;
/**
* Service responsible for validating spec resources and registering Sourcelume typedefs with Atlas
* via the AtlasAdapter.
+ *
+ * <p>Quarkus/CDI equivalent of the Spring {@code @Service} used in the Spring spike.
+ * Uses the backend-neutral {@link AtlasAdapter} interface and {@link AtlasAdapter.TypeDefinitionModel}
+ * instead of Atlas-specific {@code AtlasTypesDef} — the Atlas wire shape lives in the adapter
+ * implementation only.
*/
-@Service
+@ApplicationScoped
public class AtlasBootstrapService {
private static final Logger log = LoggerFactory.getLogger(AtlasBootstrapService.class);
@@ -36,8 +41,8 @@ public class AtlasBootstrapService {
private final SourcelumeAtlasProperties properties;
private final AtlasAdapter atlasAdapter;
- public AtlasBootstrapService(SourcelumeAtlasProperties properties,
- AtlasAdapter atlasAdapter) {
+ @Inject
+ public AtlasBootstrapService(SourcelumeAtlasProperties properties, AtlasAdapter atlasAdapter) {
this.properties = properties;
this.atlasAdapter = atlasAdapter;
}
@@ -46,21 +51,20 @@ public class AtlasBootstrapService {
* Reads the spec context from classpath to verify spec jar dependency.
*/
public byte[] readSpecContext() {
- String resourcePath = properties.getSpecContextResource();
+ String resourcePath = properties.specContextResource();
byte[] bytes = SpecResourceLoader.loadResourceBytes(resourcePath);
log.info("Successfully read {} bytes from spec context resource: {}", bytes.length, resourcePath);
return bytes;
}
/**
- * Loads the Sourcelume Atlas typedef definitions from the bundled JSON resource.
+ * Loads the Sourcelume typedef definitions from the bundled JSON resource.
*/
- public AtlasTypesDef loadTypeDefs() {
- String resourcePath = properties.getTypedefsResource();
- AtlasTypesDef typesDef = atlasAdapter.loadTypeDefs(resourcePath);
- log.info("Loaded {} entity def(s) from {}",
- typesDef.getEntityDefs() != null ? typesDef.getEntityDefs().size() : 0, resourcePath);
- return typesDef;
+ public AtlasAdapter.TypeDefinitionModel loadTypeDefs() {
+ String resourcePath = properties.typedefsResource();
+ AtlasAdapter.TypeDefinitionModel typeDefs = atlasAdapter.loadTypeDefs(resourcePath);
+ log.info("Loaded {} entity def(s) from {}", typeDefs.getEntityDefCount(), resourcePath);
+ return typeDefs;
}
/**
@@ -69,8 +73,8 @@ public class AtlasBootstrapService {
public boolean bootstrap() {
try {
readSpecContext();
- AtlasTypesDef typesDef = loadTypeDefs();
- return registerTypeDefs(typesDef);
+ AtlasAdapter.TypeDefinitionModel typeDefs = loadTypeDefs();
+ return registerTypeDefs(typeDefs);
} catch (Exception e) {
log.error("Failed to bootstrap Sourcelume typedefs into Atlas: {}", e.getMessage(), e);
return false;
@@ -80,12 +84,11 @@ public class AtlasBootstrapService {
/**
* Registers typedefs with Atlas via AtlasAdapter.
*/
- public boolean registerTypeDefs(AtlasTypesDef typesDef) {
+ public boolean registerTypeDefs(AtlasAdapter.TypeDefinitionModel typeDefs) {
try {
- log.info("Attempting to register Sourcelume typedefs with Atlas at {}", properties.getUrl());
- AtlasTypesDef createdOrUpdated = atlasAdapter.registerOrUpdateTypeDefs(typesDef);
- int entityCount = createdOrUpdated.getEntityDefs() != null ? createdOrUpdated.getEntityDefs().size() : 0;
- log.info("Successfully registered/updated Sourcelume typedefs with Atlas. Entity types: {}", entityCount);
+ log.info("Attempting to register Sourcelume typedefs with Atlas at {}", properties.url());
+ AtlasAdapter.TypeDefinitionModel createdOrUpdated = atlasAdapter.registerOrUpdateTypeDefs(typeDefs);
+ log.info("Successfully registered/updated Sourcelume typedefs with Atlas. Entity types: {}", createdOrUpdated.getEntityDefCount());
return true;
} catch (Exception e) {
log.error("Failed to register/update typedefs in Atlas: {}", e.getMessage());
diff --git a/sourcelume-registry-ingest-worker/src/main/resources/application.properties b/sourcelume-registry-ingest-worker/src/main/resources/application.properties
new file mode 100644
index 0000000..a3ddb3f
--- /dev/null
+++ b/sourcelume-registry-ingest-worker/src/main/resources/application.properties
@@ -0,0 +1,35 @@
+#
+# 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.
+#
+
+# Quarkus application configuration for the Sourcelume Ingest Worker.
+
+# HTTP / management
+quarkus.http.port=${SERVER_PORT:8082}
+
+# SmallRye Health: readiness/liveness probes at /q/health/{ready,live}
+quarkus.smallrye-health.enabled=true
+
+# Sourcelume Atlas adapter configuration (maps to SourcelumeAtlasProperties @ConfigMapping)
+sourcelume.atlas.url=${SOURCELUME_ATLAS_URL:http://localhost:21000}
+sourcelume.atlas.user=${SOURCELUME_ATLAS_USER:admin}
+sourcelume.atlas.password=${SOURCELUME_ATLAS_PASSWORD:atlasR0cks!}
+sourcelume.atlas.password-file=${SOURCELUME_ATLAS_PASSWORD_FILE:}
+sourcelume.atlas.spec-context-resource=context/0.0.1/sourcelume.jsonld
+sourcelume.atlas.typedefs-resource=models/sourcelume/sourcelume_model.json
+sourcelume.atlas.bootstrap-on-startup=${SOURCELUME_ATLAS_BOOTSTRAP_ON_STARTUP:true}
+sourcelume.atlas.max-retries=5
+sourcelume.atlas.retry-delay-ms=2000
diff --git a/sourcelume-registry-ingest-worker/src/main/resources/application.yml b/sourcelume-registry-ingest-worker/src/main/resources/application.yml
deleted file mode 100644
index 783ef2f..0000000
--- a/sourcelume-registry-ingest-worker/src/main/resources/application.yml
+++ /dev/null
@@ -1,42 +0,0 @@
-#
-# 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.
-#
-
-server:
- port: ${SERVER_PORT:8082}
-
-management:
- server:
- port: ${MANAGEMENT_SERVER_PORT:9001}
- endpoint:
- health:
- probes:
- enabled: ${MANAGEMENT_ENDPOINT_HEALTH_PROBES_ENABLED:true}
- show-details: always
- endpoints:
- web:
- exposure:
- include: ${MANAGEMENT_ENDPOINTS_WEB_EXPOSURE_INCLUDE:health,info,prometheus}
-
-sourcelume:
- atlas:
- url: ${SOURCELUME_ATLAS_URL:http://localhost:21000}
- user: ${SOURCELUME_ATLAS_USER:admin}
- password: ${SOURCELUME_ATLAS_PASSWORD:atlasR0cks!}
- password-file: ${SOURCELUME_ATLAS_PASSWORD_FILE:}
- spec-context-resource: context/0.0.1/sourcelume.jsonld
- typedefs-resource: models/sourcelume/sourcelume_model.json
- bootstrap-on-startup: ${SOURCELUME_ATLAS_BOOTSTRAP_ON_STARTUP:true}
diff --git a/sourcelume-registry-ingest-worker/src/main/resources/atlas-application.properties b/sourcelume-registry-ingest-worker/src/main/resources/atlas-application.properties
deleted file mode 100644
index 3ad726a..0000000
--- a/sourcelume-registry-ingest-worker/src/main/resources/atlas-application.properties
+++ /dev/null
@@ -1,20 +0,0 @@
-# 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.
-
-# Minimal Atlas client configuration for Sourcelume Registry
-# Connection pool and timeout settings for the Atlas REST client
-
-atlas.client.connectTimeoutMSecs=60000
-atlas.client.readTimeoutMSecs=60000
diff --git a/sourcelume-registry-ingest-worker/src/test/java/org/apache/sourcelume/registry/ingest/worker/SourcelumeWorkerApplicationTest.java b/sourcelume-registry-ingest-worker/src/test/java/org/apache/sourcelume/registry/ingest/worker/SourcelumeWorkerApplicationTest.java
deleted file mode 100644
index ce6fafd..0000000
--- a/sourcelume-registry-ingest-worker/src/test/java/org/apache/sourcelume/registry/ingest/worker/SourcelumeWorkerApplicationTest.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * 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.
- */
-package org.apache.sourcelume.registry.ingest.worker;
-
-import org.apache.sourcelume.registry.atlas.adapter.AtlasAdapter;
-import org.junit.jupiter.api.Test;
-import org.springframework.boot.test.context.SpringBootTest;
-import org.springframework.boot.test.mock.mockito.MockBean;
-import org.springframework.test.context.TestPropertySource;
-
-@SpringBootTest
-@TestPropertySource(properties = {
- "sourcelume.atlas.bootstrap-on-startup=false",
- "server.port=0",
- "management.server.port=0"
-})
-class SourcelumeWorkerApplicationTest {
-
- @MockBean
- private AtlasAdapter atlasAdapter;
-
- @Test
- void contextLoads() {
- }
-}
diff --git a/sourcelume-registry-ingest-worker/src/test/java/org/apache/sourcelume/registry/ingest/worker/WorkerAtlasClientCompatibilityTest.java b/sourcelume-registry-ingest-worker/src/test/java/org/apache/sourcelume/registry/ingest/worker/WorkerAtlasClientCompatibilityTest.java
deleted file mode 100644
index feeec94..0000000
--- a/sourcelume-registry-ingest-worker/src/test/java/org/apache/sourcelume/registry/ingest/worker/WorkerAtlasClientCompatibilityTest.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * 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.
- */
-package org.apache.sourcelume.registry.ingest.worker;
-
-import com.sun.jersey.api.client.Client;
-import org.apache.atlas.AtlasClientV2;
-import org.apache.atlas.model.typedef.AtlasTypesDef;
-import org.junit.jupiter.api.Test;
-
-import javax.ws.rs.core.MediaType;
-import java.lang.reflect.Field;
-import java.lang.reflect.Method;
-
-import static org.junit.jupiter.api.Assertions.assertNotNull;
-
-class WorkerAtlasClientCompatibilityTest {
-
- @Test
- void testAtlasClientV2JerseyJsonProviderRegistered() throws Exception {
- AtlasClientV2 client = new AtlasClientV2(new String[]{"http://localhost:21000"}, new String[]{"admin", "admin"});
- assertNotNull(client);
-
- Field contextField = org.apache.atlas.AtlasBaseClient.class.getDeclaredField("atlasClientContext");
- contextField.setAccessible(true);
- Object context = contextField.get(client);
- Method getClientMethod = context.getClass().getDeclaredMethod("getClient");
- getClientMethod.setAccessible(true);
- Client jerseyClient = (Client) getClientMethod.invoke(context);
-
- Object reader = jerseyClient.getProviders().getMessageBodyReader(
- AtlasTypesDef.class,
- AtlasTypesDef.class,
- new java.lang.annotation.Annotation[0],
- MediaType.APPLICATION_JSON_TYPE
- );
- System.out.println("Worker test jersey reader: " + reader);
- assertNotNull(reader, "Jersey client in AtlasClientV2 must register Jackson JSON MessageBodyReader for AtlasTypesDef");
- }
-}
diff --git a/sourcelume-registry-ingest-worker/src/test/java/org/apache/sourcelume/registry/ingest/worker/service/AtlasBootstrapServiceTest.java b/sourcelume-registry-ingest-worker/src/test/java/org/apache/sourcelume/registry/ingest/worker/service/AtlasBootstrapServiceTest.java
deleted file mode 100644
index 680e8b1..0000000
--- a/sourcelume-registry-ingest-worker/src/test/java/org/apache/sourcelume/registry/ingest/worker/service/AtlasBootstrapServiceTest.java
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * 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.
- */
-package org.apache.sourcelume.registry.ingest.worker.service;
-
-import org.apache.atlas.model.typedef.AtlasTypesDef;
-import org.apache.sourcelume.registry.atlas.adapter.AtlasAdapter;
-import org.apache.sourcelume.registry.atlas.adapter.config.SourcelumeAtlasProperties;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.ExtendWith;
-import org.mockito.Mock;
-import org.mockito.junit.jupiter.MockitoExtension;
-
-import static org.junit.jupiter.api.Assertions.assertNotNull;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-@ExtendWith(MockitoExtension.class)
-class AtlasBootstrapServiceTest {
-
- @Mock
- private AtlasAdapter atlasAdapter;
-
- private SourcelumeAtlasProperties properties;
- private AtlasBootstrapService service;
-
- @BeforeEach
- void setUp() {
- properties = new SourcelumeAtlasProperties();
- service = new AtlasBootstrapService(properties, atlasAdapter);
- }
-
- @Test
- void readSpecContext_succeedsWhenResourcePresent() {
- byte[] bytes = service.readSpecContext();
- assertNotNull(bytes);
- assertTrue(bytes.length > 0);
- }
-
- @Test
- void loadTypeDefs_delegatesToAtlasAdapter() {
- AtlasTypesDef expected = new AtlasTypesDef();
- when(atlasAdapter.loadTypeDefs(properties.getTypedefsResource())).thenReturn(expected);
-
- AtlasTypesDef result = service.loadTypeDefs();
- assertNotNull(result);
- verify(atlasAdapter).loadTypeDefs(properties.getTypedefsResource());
- }
-
- @Test
- void bootstrap_registersTypeDefsInAtlas() {
- AtlasTypesDef typesDef = new AtlasTypesDef();
- when(atlasAdapter.loadTypeDefs(any())).thenReturn(typesDef);
- when(atlasAdapter.registerOrUpdateTypeDefs(any())).thenReturn(typesDef);
-
- boolean result = service.bootstrap();
- assertTrue(result);
- verify(atlasAdapter).registerOrUpdateTypeDefs(any());
- }
-}
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@sourcelume.apache.org
For additional commands, e-mail: commits-help@sourcelume.apache.org