Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion inventory-orders-service/.sdkmanrc
Original file line number Diff line number Diff line change
@@ -1 +1,4 @@
java=17.0.2-open
java=17.0.2-open

# Optional: uncomment to use a GraalVM distribution when working with native images
# java=17.0.8-graal
3 changes: 3 additions & 0 deletions inventory-orders-service/META-INF/MANIFEST.MF
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Manifest-Version: 1.0
Main-Class: io.flamingock.examples.inventory.InventoryOrdersApp

94 changes: 94 additions & 0 deletions inventory-orders-service/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,9 @@ This example showcases Flamingock’s ability to:
- [Prerequisites](#prerequisites)
- [Dependencies](#dependencies)
- [How to Run this Example](#how-to-run-this-example)
- [Option 1: Run the Application (Recommended)](#option-1-run-the-application-recommended)
- [Option 2: Run Tests](#option-2-run-tests)
- [Option 3: Run with GraalVM Native Image (Optional)](#option-3-run-with-graalvm-native-image-optional)
- [Proven Functionalities](#proven-functionalities)
- [Implemented Changes](#implemented-changes)
- [Contributing](#contributing)
Expand Down Expand Up @@ -211,6 +214,97 @@ Run the integration tests with Testcontainers (no Docker Compose needed):
./gradlew test
```

### Option 3: Run with GraalVM Native Image (Optional)

If you want to showcase Flamingock running as a GraalVM native image, you can follow these **optional** steps. The regular JVM flow above still works as-is.

For full details, see the official docs: https://docs.flamingock.io/frameworks/graalvm

#### 1. Use a GraalVM Java distribution

Using SDKMAN:

```bash
sdk env install # uses .sdkmanrc in this folder
sdk use java 22.0.2-graalce # or any installed GraalVM distribution compatible with your setup
```

The default `.sdkmanrc` keeps Java 17, but includes a commented example GraalVM version you can enable.

#### 2. Ensure GraalVM support dependencies are present

This example already includes the Flamingock GraalVM integration and resource configuration:

- `build.gradle.kts` contains:
- `implementation("io.flamingock:flamingock-graalvm:$flamingockVersion")`
- `resource-config.json` in the project root includes:
- `META-INF/flamingock/metadata.json` resources required at native-image time

If you copy this example to your own project, make sure you add the same pieces (or follow the docs linked above).

#### 3. Build the fat (uber) JAR

First build the application as usual, which also creates a **fat / uber JAR** bundling all runtime dependencies and a `Main-Class` manifest entry:

```bash
./gradlew clean build
```

The `jar` task in `build.gradle.kts` is configured like this:

```kotlin
tasks.named<Jar>("jar") {
manifest {
attributes["Main-Class"] = "io.flamingock.examples.inventory.InventoryOrdersApp"
}

duplicatesStrategy = DuplicatesStrategy.EXCLUDE

from(sourceSets.main.get().output)

from({
configurations.runtimeClasspath.get().map { if (it.isDirectory) it else zipTree(it) }
})
}
```

This produces an executable uber JAR under `build/libs/` (for example `build/libs/inventory-orders-service-1.0-SNAPSHOT.jar`).

> **Why this matters for GraalVM**
>
> GraalVM's `native-image -jar` mode expects a JAR that:
> - has a valid `Main-Class` manifest attribute, and
> - contains all the classes and dependencies reachable from that entry point.
>
> The fat/uber JAR configuration above ensures those requirements are met, which is essential for the native-image step to work reliably.

#### 4. Create the native image

From the project root, run (adjust the JAR name and output binary name if needed):

```bash
native-image \
--no-fallback \
--features=io.flamingock.graalvm.RegistrationFeature \
-H:ResourceConfigurationFiles=resource-config.json \
-H:+ReportExceptionStackTraces \
--initialize-at-build-time=org.slf4j.simple \
-jar build/libs/inventory-orders-service-1.0-SNAPSHOT.jar \
inventory-orders-service
```

This uses Flamingock's GraalVM feature to automatically register all required reflection metadata.

#### 5. Run the native image

With Docker Compose infrastructure already running (see [Option 1](#option-1-run-the-application-recommended)), start the native binary:

```bash
./inventory-orders-service
```

The application will execute the same Flamingock migrations as when running on the regular JVM, but with GraalVM-native startup and footprint characteristics.

## Troubleshooting

### Schema Registry Connection Issues
Expand Down
20 changes: 19 additions & 1 deletion inventory-orders-service/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ repositories {
group = "io.flamingock"
version = "1.0-SNAPSHOT"

val flamingockVersion = "1.0.0-beta.1"
val flamingockVersion = "1.0.0-beta.4"
logger.lifecycle("Building with flamingock version: $flamingockVersion")

val mongodbVersion = "5.5.1"
Expand All @@ -39,6 +39,10 @@ dependencies {
// Flamingock Dependencies
implementation(platform("io.flamingock:flamingock-community-bom:$flamingockVersion"))
implementation("io.flamingock:flamingock-community")
// Optional: enable GraalVM native image support for Flamingock
// See: https://docs.flamingock.io/frameworks/graalvm
// Uncomment
implementation("io.flamingock:flamingock-graalvm:$flamingockVersion")
annotationProcessor("io.flamingock:flamingock-processor:$flamingockVersion")

// MongoDB dependencies
Expand Down Expand Up @@ -73,6 +77,20 @@ application {
mainClass = "io.flamingock.examples.inventory.InventoryOrdersApp"
}

tasks.named<Jar>("jar") {
manifest {
attributes["Main-Class"] = "io.flamingock.examples.inventory.InventoryOrdersApp"
}

duplicatesStrategy = DuplicatesStrategy.EXCLUDE

from(sourceSets.main.get().output)

from({
configurations.runtimeClasspath.get().map { if (it.isDirectory) it else zipTree(it) }
})
}

tasks.withType<JavaCompile> {
options.compilerArgs.add("-parameters")
}
Expand Down
7 changes: 7 additions & 0 deletions inventory-orders-service/resource-config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"resources": {
"includes": [
{ "pattern": "META-INF/flamingock/metadata.json" }
]
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,13 @@

package io.flamingock.examples.inventory;

import com.mongodb.client.MongoClient;
import io.flamingock.api.annotations.EnableFlamingock;
import io.flamingock.api.annotations.Stage;
import io.flamingock.community.Flamingock;
import io.flamingock.community.mongodb.sync.driver.MongoDBSyncAuditStore;
import io.flamingock.examples.inventory.util.MongoDBUtil;
import io.flamingock.examples.inventory.util.TargetSystems;
import io.flamingock.internal.core.store.CommunityAuditStore;
import io.flamingock.targetystem.mongodb.sync.MongoDBSyncTargetSystem;

import static io.flamingock.examples.inventory.util.TargetSystems.DATABASE_NAME;

Expand All @@ -49,9 +48,9 @@ public static void main(String[] args) throws Exception {

//This could return any of the available community audit stores
private static CommunityAuditStore auditStore() {
MongoClient mongoClient = MongoDBUtil.getMongoClient("mongodb://localhost:27017/");
return new MongoDBSyncAuditStore(mongoClient, DATABASE_NAME);
MongoDBSyncTargetSystem targetSystem = TargetSystems.mongoDBSyncTargetSystem();
return MongoDBSyncAuditStore.from(targetSystem);
}


}
}
Original file line number Diff line number Diff line change
Expand Up @@ -149,9 +149,9 @@ static void beforeAll() throws Exception {
static class TestConfig {}

private static void runFlamingockMigrations(MongoClient mongoClient, KafkaSchemaManager schemaManager, LaunchDarklyClient launchDarklyClient) {
CommunityAuditStore auditStore = new MongoDBSyncAuditStore(mongoClient, DATABASE_NAME);

MongoDBSyncTargetSystem mongoTarget = new MongoDBSyncTargetSystem(MONGODB_TARGET_SYSTEM, mongoClient, DATABASE_NAME);
CommunityAuditStore auditStore = MongoDBSyncAuditStore.from(mongoTarget);

NonTransactionalTargetSystem kafkaTarget = new NonTransactionalTargetSystem(KAFKA_TARGET_SYSTEM).addDependency(schemaManager);
NonTransactionalTargetSystem flagTarget = new NonTransactionalTargetSystem(FEATURE_FLAG_TARGET_SYSTEM).addDependency(launchDarklyClient);

Expand Down Expand Up @@ -265,4 +265,4 @@ private void verifyChangeExecution(List<Document> auditLogs, String changeId) {
&& "APPLIED".equals(log.getString("state")));
assertTrue(hasApplied, "Change " + changeId + " should have APPLIED entry");
}
}
}