feat: add quarkus-morphium extension as optional module - #267
Conversation
Copies the quarkus-morphium Quarkus CDI extension (runtime, deployment, testing, integration-tests submodules, plus Antora docs, README, and CHANGELOG) from the standalone quarkus-morphium repository into this reactor as a module directory, per the file selection assessed in quarkus-morphium/MIGRATION-NOTES.md's "kommt mit" list. Not yet wired into the reactor's module list -- that follows in the next commit.
Adds the quarkus-morphium module to the "extensions" profile (see D3-reactor-strategie.md, Variante B), positioned after morphium-jakarta-data to reflect dependency order for readability (Maven itself sorts the reactor regardless). Also moves the quarkus.version property from quarkus-morphium/pom.xml into morphium-parent (per D1, Absicherung B6) so a Quarkus upgrade is a single-line change; the Quarkus BOM import itself stays in quarkus-morphium/pom.xml (invariant I4) so core builds never resolve Quarkus artifacts. Extends the parent's comment block to document this distinction.
Registers quarkus-morphium (runtime), quarkus-morphium-deployment, and
quarkus-morphium-testing through the module registry (MODULE_DIRS/
MODULE_ARTIFACT_IDS/MODULE_EXTRA_CLASSIFIERS), and adds
quarkus-morphium-parent as its own POM-only special case at both the
dry-run and real-release bundle-building sites, mirroring morphium-parent
(add_module_to_bundle() always expects jar+sources+javadoc, which does not
apply to a packaging=pom module). integration-tests is a test-only submodule
with no publishing purpose and is simply not listed in the registry, so it
is never picked up.
Also extends ALL_POM_FILES with quarkus-morphium/pom.xml and
quarkus-morphium/integration-tests/pom.xml: mvn versions:set bumps every
pom.xml in the reactor regardless of whether it is registered as a
published module, so a reactor pom missing from ALL_POM_FILES would get
silently version-bumped by Maven but never staged by the git add calls in
this script, leaving it out of sync with the release commit.
Adds maven-source-plugin and maven-javadoc-plugin activation to
quarkus-morphium/{runtime,deployment,testing}/pom.xml — the parent POM
only declares them in pluginManagement, each module must still enable them,
same as morphium-jakarta-data. Without this, the module jars would build
but produce no sources/javadoc artifacts, which Sonatype rejects on upload.
…ative-image groupId path Only MorphiumProcessor was listed in META-INF/quarkus-build-steps.list, the mechanism Quarkus actually uses to load deployment build-step classes (ServiceUtil.classesNamedIn(...)). MorphiumDataProcessor (Jakarta Data repository generation), MorphiumDevServicesProcessor, MorphiumMigrationProcessor, and MorphiumDevUIProcessor were never loaded, silently disabling most of the extension's advertised features. All five processors are now listed. Also fixes META-INF/native-image/io.quarkiverse.morphium/quarkus-morphium/ to META-INF/native-image/de.caluga/quarkus-morphium/ (GraalVM only picks up native-image.properties when the directory matches <groupId>/<artifactId>, and the groupId moved to de.caluga in the M3/M4 migration). Additionally, Dev Services now skips container startup when quarkus.morphium.driver-name is explicitly set to a non-production driver (e.g. InMemDriver): previously only quarkus.morphium.hosts was checked, so a Dockerless InMemDriver test setup still started a MongoDB container before the runtime producer could apply the driver override. Verified: integration-tests now starts exactly one container (for the one test that actually needs a real replica set), down from one per test run before. Reported by GitHub Copilot review on PR #17 (Bardioc1977/morphium).
… 1.1, not 1.0.0 The README and Antora jakarta-data page showed @by("price") @is(GreaterThanEqual) as a supported example, but jakarta.data.repository.Is does not exist in jakarta.data-api 1.0.0 (the stable version this module targets) — it was only added in Jakarta Data 1.1, which has not shipped a final release yet (latest available artifact is the 1.1.0-M3 milestone). The example would not compile against this module's actual dependency. Corrected the example to a plain equality @by parameter and added a note pointing to query derivation or @query (JDQL) for non-equality conditions today, with @is noted as a natural follow-up once Jakarta Data 1.1 finalizes. Reported by GitHub Copilot review on PR #17 (Bardioc1977/morphium).
There was a problem hiding this comment.
🟡 Changes recommended
There are verified doc/config correctness issues (Antora config typo and misleading health-check documentation) that should be fixed before merging.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
This review doesn't count toward merge requirements. Sign up for the private preview to control whether Copilot approvals count.
Pull request overview
This PR adds quarkus-morphium as a new optional Quarkus CDI extension module within the Morphium reactor, keeping core artifacts extension-free while providing Quarkus integration features (config mapping, Dev Services, health checks, Dev UI, Jakarta Data repository generation, native-image support).
Changes:
- Adds the new multi-submodule
quarkus-morphiumextension (runtime/deployment/testing + non-published integration-tests) with Quarkus runtime features, build steps, and extensive integration tests. - Updates the reactor/release tooling to publish the new Quarkus extension artifacts and include the intermediate
quarkus-morphium-parentPOM in the Central bundle. - Extends project documentation (MkDocs + Antora) and changelog to describe the new optional module and its usage.
File summaries
| File | Description |
|---|---|
| release.sh | Adds Quarkus extension artifacts + parent POM to release bundle and staged POM list. |
| quarkus-morphium/testing/src/main/java/de/caluga/morphium/quarkus/testing/InMemMorphiumTestProfile.java | Provides a Quarkus test profile for running against Morphium InMemDriver without Dev Services. |
| quarkus-morphium/testing/pom.xml | Adds a published testing utility artifact for the extension. |
| quarkus-morphium/runtime/src/test/java/de/caluga/morphium/quarkus/json/MorphiumIdJsonbAdapterTest.java | Unit tests for JSON-B MorphiumId adapter wire format. |
| quarkus-morphium/runtime/src/test/java/de/caluga/morphium/quarkus/json/MorphiumIdJacksonModuleTest.java | Unit tests for Jackson MorphiumId module wire format. |
| quarkus-morphium/runtime/src/test/java/de/caluga/morphium/quarkus/health/MorphiumStartupCheckTest.java | Unit tests for startup health check “everConnected” behavior. |
| quarkus-morphium/runtime/src/main/resources/META-INF/quarkus-extension.yaml | Declares extension metadata (name, guide, keywords, config root). |
| quarkus-morphium/runtime/src/main/resources/META-INF/native-image/de.caluga/quarkus-morphium/native-image.properties | Adds native-image build JVM args for JOL/module exports. |
| quarkus-morphium/runtime/src/main/resources-filtered/META-INF/morphium-version.properties | Adds filtered build-time version properties exposed at runtime. |
| quarkus-morphium/runtime/src/main/java/de/caluga/morphium/quarkus/transaction/MorphiumTxPhase.java | Adds CDI qualifier for observing transaction lifecycle events. |
| quarkus-morphium/runtime/src/main/java/de/caluga/morphium/quarkus/transaction/MorphiumTransactionEvent.java | Adds CDI event type for transaction lifecycle phases and rollback failure. |
| quarkus-morphium/runtime/src/main/java/de/caluga/morphium/quarkus/transaction/MorphiumTransactional.java | Adds interceptor binding for Morphium transactions. |
| quarkus-morphium/runtime/src/main/java/de/caluga/morphium/quarkus/SslConfig.java | Adds TLS/X.509 nested runtime config mapping interface. |
| quarkus-morphium/runtime/src/main/java/de/caluga/morphium/quarkus/MorphiumVersion.java | Adds runtime helper to read extension/core/Jakarta Data versions. |
| quarkus-morphium/runtime/src/main/java/de/caluga/morphium/quarkus/MorphiumDevUIJsonRpcService.java | Adds Dev UI JsonRPC service exposing sanitized connection info. |
| quarkus-morphium/runtime/src/main/java/de/caluga/morphium/quarkus/migration/RollbackExecution.java | Adds rollback method marker for migrations. |
| quarkus-morphium/runtime/src/main/java/de/caluga/morphium/quarkus/migration/MorphiumMigrationLock.java | Adds distributed lock entity for migration execution. |
| quarkus-morphium/runtime/src/main/java/de/caluga/morphium/quarkus/migration/MorphiumMigrationEntry.java | Adds changelog entity to track executed/rolled back/failed migrations. |
| quarkus-morphium/runtime/src/main/java/de/caluga/morphium/quarkus/migration/MorphiumMigrationConfig.java | Adds migration runtime configuration (autostart, collections, TTL). |
| quarkus-morphium/runtime/src/main/java/de/caluga/morphium/quarkus/migration/MorphiumChangeUnit.java | Adds class-level marker for migration units with ordering metadata. |
| quarkus-morphium/runtime/src/main/java/de/caluga/morphium/quarkus/migration/Execution.java | Adds execution method marker for migrations. |
| quarkus-morphium/runtime/src/main/java/de/caluga/morphium/quarkus/LocalDateTimeConfig.java | Adds config for LocalDateTime storage representation. |
| quarkus-morphium/runtime/src/main/java/de/caluga/morphium/quarkus/json/MorphiumIdJsonbModule.java | Registers JSON-B adapter when JSON-B capability is present. |
| quarkus-morphium/runtime/src/main/java/de/caluga/morphium/quarkus/json/MorphiumIdJsonbAdapter.java | Implements JSON-B MorphiumId ⇄ hex string mapping. |
| quarkus-morphium/runtime/src/main/java/de/caluga/morphium/quarkus/json/MorphiumIdJacksonModule.java | Registers Jackson serializer/deserializer for MorphiumId ⇄ hex string. |
| quarkus-morphium/runtime/src/main/java/de/caluga/morphium/quarkus/health/MorphiumStartupCheck.java | Adds startup probe with SRV-discovery-tolerant “everConnected” logic. |
| quarkus-morphium/runtime/src/main/java/de/caluga/morphium/quarkus/health/MorphiumReadinessCheck.java | Adds readiness probe with best-effort pool stats metadata. |
| quarkus-morphium/runtime/src/main/java/de/caluga/morphium/quarkus/health/MorphiumLivenessCheck.java | Adds liveness probe based on driver connection state. |
| quarkus-morphium/runtime/src/main/java/de/caluga/morphium/quarkus/data/QuarkusMorphiumRepository.java | Adds Quarkus base class for Gizmo-generated repositories with CDI-injected Morphium. |
| quarkus-morphium/runtime/src/main/java/de/caluga/morphium/quarkus/CacheConfig.java | Adds cache-related nested runtime config mapping interface. |
| quarkus-morphium/pom.xml | Adds the intermediate extension parent POM with module list and BOM import. |
| quarkus-morphium/integration-tests/src/test/java/de/caluga/morphium/quarkus/it/TransactionEventCollector.java | Test bean collecting transaction events for assertions. |
| quarkus-morphium/integration-tests/src/test/java/de/caluga/morphium/quarkus/it/TransactionalService.java | Test service exercising @MorphiumTransactional. |
| quarkus-morphium/integration-tests/src/test/java/de/caluga/morphium/quarkus/it/StatusStats.java | Record DTO for aggregation test results. |
| quarkus-morphium/integration-tests/src/test/java/de/caluga/morphium/quarkus/it/StatusCustomerCount.java | Record DTO for aggregation test results. |
| quarkus-morphium/integration-tests/src/test/java/de/caluga/morphium/quarkus/it/StatusCount.java | Record DTO for aggregation test results. |
| quarkus-morphium/integration-tests/src/test/java/de/caluga/morphium/quarkus/it/PaginatedOrderRepository.java | Test Jakarta Data repository for keyset pagination. |
| quarkus-morphium/integration-tests/src/test/java/de/caluga/morphium/quarkus/it/OrderEntity.java | Test entity used for query/pagination/LocalDateTime scenarios. |
| quarkus-morphium/integration-tests/src/test/java/de/caluga/morphium/quarkus/it/MorphiumVersionTest.java | Integration tests for @Version optimistic locking behavior. |
| quarkus-morphium/integration-tests/src/test/java/de/caluga/morphium/quarkus/it/MorphiumLocalDateTimeTest.java | Integration tests for LocalDateTime mapping behavior. |
| quarkus-morphium/integration-tests/src/test/java/de/caluga/morphium/quarkus/it/MorphiumItemRepository.java | Integration tests for MorphiumRepository features in Jakarta Data. |
| quarkus-morphium/integration-tests/src/test/java/de/caluga/morphium/quarkus/it/MorphiumInMemProfileTest.java | Integration tests validating quarkus-morphium-testing profile behavior. |
| quarkus-morphium/integration-tests/src/test/java/de/caluga/morphium/quarkus/it/MorphiumInjectionTest.java | Smoke tests for CDI-produced Morphium bean. |
| quarkus-morphium/integration-tests/src/test/java/de/caluga/morphium/quarkus/it/MorphiumIdResource.java | Minimal REST resource for MorphiumId wire-format tests. |
| quarkus-morphium/integration-tests/src/test/java/de/caluga/morphium/quarkus/it/MorphiumIdJsonSerializationTest.java | End-to-end REST test for MorphiumId (de)serialization. |
| quarkus-morphium/integration-tests/src/test/java/de/caluga/morphium/quarkus/it/MorphiumIdEntity.java | Test entity with MorphiumId primary key. |
| quarkus-morphium/integration-tests/src/test/java/de/caluga/morphium/quarkus/it/MorphiumHealthCheckTest.java | End-to-end tests validating health checks when enabled. |
| quarkus-morphium/integration-tests/src/test/java/de/caluga/morphium/quarkus/it/MorphiumHealthCheckDisabledTest.java | End-to-end tests validating health checks absent when disabled. |
| quarkus-morphium/integration-tests/src/test/java/de/caluga/morphium/quarkus/it/MorphiumEntityRegistryTest.java | Integration tests for build-time entity pre-registration. |
| quarkus-morphium/integration-tests/src/test/java/de/caluga/morphium/quarkus/it/MorphiumEmbeddedTest.java | Integration tests for embedded document mapping. |
| quarkus-morphium/integration-tests/src/test/java/de/caluga/morphium/quarkus/it/MorphiumDevServicesReplicaSetConfigTest.java | Integration test ensuring replica-set override doesn’t break startup when Dev Services disabled. |
| quarkus-morphium/integration-tests/src/test/java/de/caluga/morphium/quarkus/it/MorphiumDevServicesConfigTest.java | Integration tests for Dev Services defaults/overrides in test profile. |
| quarkus-morphium/integration-tests/src/test/java/de/caluga/morphium/quarkus/it/MorphiumDataStreamTest.java | Integration tests for Jakarta Data Stream support. |
| quarkus-morphium/integration-tests/src/test/java/de/caluga/morphium/quarkus/it/MorphiumDataProjectionTest.java | Integration tests for JDQL SELECT projection mapping. |
| quarkus-morphium/integration-tests/src/test/java/de/caluga/morphium/quarkus/it/MorphiumDataParenGroupTest.java | Integration tests for parenthesized condition parsing. |
| quarkus-morphium/integration-tests/src/test/java/de/caluga/morphium/quarkus/it/MorphiumDataPaginationTest.java | Integration tests for pagination/sorting and derivation basics. |
| quarkus-morphium/integration-tests/src/test/java/de/caluga/morphium/quarkus/it/MorphiumDataMorphiumRepositoryTest.java | Integration tests for MorphiumRepository escape hatches. |
| quarkus-morphium/integration-tests/src/test/java/de/caluga/morphium/quarkus/it/MorphiumDataHavingOrTest.java | Integration tests for HAVING with OR in aggregates. |
| quarkus-morphium/integration-tests/src/test/java/de/caluga/morphium/quarkus/it/MorphiumDataGroupByTest.java | Integration tests for GROUP BY record mapping and ordering. |
| quarkus-morphium/integration-tests/src/test/java/de/caluga/morphium/quarkus/it/MorphiumDataGroupByPageTest.java | Integration tests for pagination with grouped results. |
| quarkus-morphium/integration-tests/src/test/java/de/caluga/morphium/quarkus/it/MorphiumDataDeleteTest.java | Integration tests for delete derivation and deleteAll. |
| quarkus-morphium/integration-tests/src/test/java/de/caluga/morphium/quarkus/it/MorphiumDataCountFieldTest.java | Integration tests for COUNT(field) NULL filtering behavior. |
| quarkus-morphium/integration-tests/src/test/java/de/caluga/morphium/quarkus/it/MorphiumDataAsyncTest.java | Integration tests for CompletionStage async repository methods. |
| quarkus-morphium/integration-tests/src/test/java/de/caluga/morphium/quarkus/it/MorphiumDataAggregateTest.java | Integration tests for aggregate functions (COUNT/SUM/AVG/MIN/MAX). |
| quarkus-morphium/integration-tests/src/test/java/de/caluga/morphium/quarkus/it/MorphiumCrudTest.java | End-to-end CRUD tests via injected Morphium + InMemDriver. |
| quarkus-morphium/integration-tests/src/test/java/de/caluga/morphium/quarkus/it/ItemRepository.java | Jakarta Data repository used by integration tests. |
| quarkus-morphium/integration-tests/src/test/java/de/caluga/morphium/quarkus/it/ItemEntity.java | Base entity shared across many integration tests. |
| quarkus-morphium/integration-tests/src/test/java/de/caluga/morphium/quarkus/it/InitItemsMigration.java | Test migration change unit exercising execution + rollback. |
| quarkus-morphium/integration-tests/src/test/java/de/caluga/morphium/quarkus/it/FailingMigration.java | Test migration change unit that forces rollback path. |
| quarkus-morphium/integration-tests/src/test/java/de/caluga/morphium/quarkus/it/CustomerEntity.java | Entity used for embedded/entity registry tests. |
| quarkus-morphium/integration-tests/src/test/java/de/caluga/morphium/quarkus/it/AddressEmbedded.java | Embedded type used for embedded/entity registry tests. |
| quarkus-morphium/integration-tests/src/test/java/de/caluga/morphium/quarkus/it/AddCategoryMigration.java | Second test migration change unit. |
| quarkus-morphium/integration-tests/src/main/resources/application.properties | Integration-test runtime config (InMemDriver, Dev Services disabled). |
| quarkus-morphium/integration-tests/pom.xml | Defines integration-tests module dependencies and Quarkus build plugin. |
| quarkus-morphium/docs/modules/ROOT/pages/index.adoc | Adds Antora guide index page for the extension. |
| quarkus-morphium/docs/modules/ROOT/pages/includes/attributes.adoc | Adds shared Antora attributes (versions/URLs). |
| quarkus-morphium/docs/modules/ROOT/pages/health-checks.adoc | Adds health checks guide page. |
| quarkus-morphium/docs/modules/ROOT/pages/dev-services.adoc | Adds Dev Services + Dev UI guide page. |
| quarkus-morphium/docs/modules/ROOT/nav.adoc | Adds Antora navigation structure for extension docs. |
| quarkus-morphium/docs/antora.yml | Adds Antora component descriptor for the extension docs. |
| quarkus-morphium/deployment/src/test/java/de/caluga/morphium/quarkus/deployment/MorphiumDevServicesProcessorTest.java | Adds unit tests for Dev Services processor helper types/config equality. |
| quarkus-morphium/deployment/src/main/resources/META-INF/quarkus-build-steps.list | Registers build step classes for the extension. |
| quarkus-morphium/deployment/src/main/resources/dev-ui/qwc-morphium-connection.js | Adds Dev UI web component for showing connection info. |
| quarkus-morphium/deployment/src/main/java/de/caluga/morphium/quarkus/deployment/RepositoryBuildItem.java | Adds build item for discovered Jakarta Data repositories. |
| quarkus-morphium/deployment/src/main/java/de/caluga/morphium/quarkus/deployment/MorphiumMigrationProcessor.java | Adds build-time discovery + runtime execution trigger for migrations. |
| quarkus-morphium/deployment/src/main/java/de/caluga/morphium/quarkus/deployment/MorphiumHealthBuildTimeConfig.java | Adds build-time config mapping for enabling/disabling health checks. |
| quarkus-morphium/deployment/src/main/java/de/caluga/morphium/quarkus/deployment/MorphiumFeature.java | Adds extension feature marker build item. |
| quarkus-morphium/deployment/src/main/java/de/caluga/morphium/quarkus/deployment/MorphiumEntitiesRegisteredBuildItem.java | Adds ordering marker build item for entity registration step. |
| quarkus-morphium/deployment/src/main/java/de/caluga/morphium/quarkus/deployment/MorphiumDevUIProcessor.java | Registers Dev UI card + JsonRPC provider in dev mode. |
| quarkus-morphium/deployment/src/main/java/de/caluga/morphium/quarkus/deployment/MorphiumDevServicesBuildTimeConfig.java | Adds build-time config mapping for Dev Services settings. |
| quarkus-morphium/deployment/src/main/java/de/caluga/morphium/quarkus/deployment/MongoDBStartable.java | Adds Testcontainers wrapper for starting MongoDB with optional replica set. |
| quarkus-morphium/deployment/pom.xml | Defines deployment artifact dependencies (Quarkus build-time SPI, Testcontainers, Dev UI). |
| pom.xml | Adds centralized quarkus.version property and activates quarkus-morphium in the extensions profile. |
| mkdocs.yml | Adds Quarkus extension page to MkDocs navigation. |
| docs/index.md | Links the Quarkus extension docs from the main docs index. |
| CHANGELOG.md | Adds a changelog entry describing the new optional Quarkus extension module. |
Review details
- Files reviewed: 134/134 changed files
- Comments generated: 4
- Review effort level: Lite
We're testing this review assessment. Please use 👍 or 👎 to tell us if it's correct.
| - modules/ROOT/nav.adoc | ||
| asciidoc: | ||
| attributes: | ||
| page-toclevels: 3@ |
| NOTE: The extension already includes `quarkus-smallrye-health` as a transitive dependency. | ||
| No additional dependency is needed — health endpoints are available by default. |
| * # Optional – overrides the subject DN extracted from the certificate: | ||
| * # morphium.ssl.x509-username=CN=myUser,O=myOrg,C=DE |
| card.addLibraryVersion("de.caluga", "quarkus-morphium", | ||
| "Quarkus Morphium Extension", "https://github.com/Bardioc1977/quarkus-morphium"); |
Four issues found by GitHub Copilot's review on PR sboesebeck#267: 1. antora.yml: page-toclevels had a stray trailing '@' (3@ instead of 3), an invalid Antora AsciiDoc attribute value. 2. health-checks.adoc claimed quarkus-smallrye-health is a transitive dependency included by default; the runtime POM declares it optional (deliberately, so consumers who don't want smallrye-health aren't forced into it) -- corrected the NOTE to say it must be added explicitly. 3. SslConfig.java's Javadoc example used 'morphium.ssl.x509-username' instead of 'quarkus.morphium.ssl.x509-username' -- missing the 'quarkus.' prefix this extension's config actually uses. 4. MorphiumDevUIProcessor's Dev UI card linked to the old, now-archived standalone repo (Bardioc1977/quarkus-morphium) instead of this extension's new home, sboesebeck/morphium. Checked for the same bug classes elsewhere in the module: the quarkus-morphium-showcase links are a different repo (correctly still under Bardioc1977) and the other x509-username references already carry the quarkus. prefix -- no further instances found.
|
Thanks Copilot! All 4 findings confirmed and fixed:
Also checked for the same bug classes elsewhere in the module (other x509-username references, other repo links) — no further instances found. |
There was a problem hiding this comment.
🟡 Changes recommended
There are at least two correctness issues that can cause unintended behavior (Dev Services starting despite atlas-url configuration, and enum-state migration queries that may not match stored values).
Once you've addressed the issues Copilot identified, you can request another Copilot review.
This review doesn't count toward merge requirements. Sign up for the private preview to control whether Copilot approvals count.
Review details
Suppressed comments (2)
quarkus-morphium/runtime/src/main/java/de/caluga/morphium/quarkus/migration/MorphiumMigrationRunner.java:269
stateis stored as theChangeStateenum (seerecordExecution(...).setState(state)), but the query compares it to a string (EXECUTED.name()). In Morphium, enum queries are typically performed using the enum constant itself; using the string risks returning 0 executed migrations (and re-running already executed migrations) depending on enum mapping.
quarkus-morphium/deployment/src/main/java/de/caluga/morphium/quarkus/deployment/MorphiumDevServicesProcessor.java:75- Dev Services are skipped only when
quarkus.morphium.hostsis configured, but Morphium also supportsquarkus.morphium.atlas-urlas an alternative connection source. If an app setsatlas-url(and leaveshostsunset), this processor will still start a MongoDB container unnecessarily and inject host overrides that the runtime will ignore (since atlasUrl takes precedence).
if (ConfigUtils.isPropertyNonEmpty("quarkus.morphium.hosts")) {
log.debug("Morphium connection settings already configured – skipping Dev Services");
return null;
}
- Files reviewed: 134/134 changed files
- Comments generated: 0 new
- Review effort level: Lite
We're testing this review assessment. Please use 👍 or 👎 to tell us if it's correct.
…is configured MorphiumDevServicesProcessor only checked quarkus.morphium.hosts before deciding whether to start a MongoDB container. Morphium also supports quarkus.morphium.atlas-url as an alternative connection source (takes precedence over hosts when set) -- an application configuring only atlas-url would still get a Dev Services container started unnecessarily, with host overrides injected that the runtime then ignores. Found in code review on PR sboesebeck#267 (sboesebeck/morphium).
|
Thanks for the second pass! Dev Services + Enum-state migration query: verified, but this one is actually correct as-is — no change made. |
There was a problem hiding this comment.
🟡 Changes recommended
The filtered morphium-version.properties currently references an undefined ${morphium.version} property, causing incorrect runtime version reporting/logging.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
This review doesn't count toward merge requirements. Sign up for the private preview to control whether Copilot approvals count.
Review details
Suppressed comments (2)
quarkus-morphium/runtime/src/main/resources-filtered/META-INF/morphium-version.properties:2
morphium.versionis populated from${morphium.version}, but there is nomorphium.versionMaven property in the reactor. With resource filtering this will typically end up as the literal string${morphium.version}in the JAR, soMorphiumVersion.morphiumVersion()will report an incorrect value (and the startup log line inMorphiumProducerwill be wrong). Since this module is lockstep-versioned with Morphium core,${project.version}should be used here (or alternatively define amorphium.versionproperty in the parent).
quarkus-morphium/runtime/src/main/java/de/caluga/morphium/quarkus/MorphiumRuntimeConfig.java:39- The example
application.propertiessnippet containsquarkus.morphium.******, which is not a valid property key and reads like a placeholder that was meant to demonstrate setting the password. This makes the docs misleading for users copy/pasting the snippet.
- Files reviewed: 134/134 changed files
- Comments generated: 0 new
- Review effort level: Lite
We're testing this review assessment. Please use 👍 or 👎 to tell us if it's correct.
…ession test
morphium-version.properties referenced an undefined Maven property,
${morphium.version} -- no such property exists anywhere in the reactor.
With resource filtering this left the literal placeholder string in the
built JAR, so MorphiumVersion.morphiumVersion() (and the startup log line
in MorphiumProducer that uses it) silently reported the wrong value instead
of a real version.
Since this module is lockstep-versioned with Morphium core, uses
${project.version} -- the same expression already used for
extension.version in the same file.
Adds MorphiumVersionTest verifying none of the three version accessors
return "unknown" or a literal ${...} placeholder, and that
morphiumVersion() equals extensionVersion() (lockstep versioning).
Found in code review on PR sboesebeck#267 (sboesebeck/morphium).
quarkus.morphium.password=secret in the class-level Javadoc example reads like an actual credential value rather than a placeholder -- replaced with the conventional changeit placeholder used elsewhere in this codebase's SSL/keystore examples. Found in code review on PR sboesebeck#267 (sboesebeck/morphium).
|
Thanks for the third pass!
Example password in |
There was a problem hiding this comment.
🟡 Changes recommended
The new Quarkus test modules depend on io.quarkus:quarkus-junit, which is likely the wrong artifactId (should be quarkus-junit5) and can break Maven resolution/builds.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
This review doesn't count toward merge requirements. Sign up for the private preview to control whether Copilot approvals count.
Review details
Suppressed comments (1)
quarkus-morphium/integration-tests/src/test/java/de/caluga/morphium/quarkus/it/MorphiumVersionTest.java:96
- This test claims to cover an entity without
@Version, but it usesItemEntity, which is versioned. That makes the DisplayName/comments misleading and reduces the value of the test signal.
- Files reviewed: 135/135 changed files
- Comments generated: 2
- Review effort level: Lite
We're testing this review assessment. Please use 👍 or 👎 to tell us if it's correct.
| <dependency> | ||
| <groupId>io.quarkus</groupId> | ||
| <artifactId>quarkus-junit</artifactId> | ||
| </dependency> |
| <dependency> | ||
| <groupId>io.quarkus</groupId> | ||
| <artifactId>quarkus-junit</artifactId> | ||
| <scope>test</scope> | ||
| </dependency> |
sboesebeck
left a comment
There was a problem hiding this comment.
Hi Heiko,
first of all: impressive work again — clean module split, the Gizmo layer delegating to the shared morphium-jakarta-data bridges is exactly the right design, and the test discipline is well above average. Before this can go in, though, a deep review (Claude did the heavy lifting here, working through all 134 files — the findings below were verified against the actual code) turned up a number of issues that I'd like to see addressed.
This review is based on the current head including your Copilot-round fixes (c23ba60d…10bc8700) — the ${morphium.version} placeholder and the atlas-url Dev Services skip are already resolved and not listed below. One note on Copilot's latest pass: the quarkus-junit finding is a false positive — io.quarkus:quarkus-junit exists on Central and is managed by quarkus-bom 3.32.3 (your green test runs confirm resolution works), so no change needed there.
Quick answers to your questions first:
- Antora docs: your call — I'd suggest publishing them under the existing gh-pages setup so they're reachable; the MkDocs overview page alone feels a bit thin for an extension this size.
- Extension status: let's stay with
previewfor the first release (which is whatquarkus-extension.yamlcurrently says anyway) — given the findings below that's also the honest label. We can promote tostablein a follow-up release. - Docker in CI: not a concern for me. I'm personally not a Docker fan, but if it works for you, it works for me — see finding 7 though, the no-Docker fallback currently doesn't do what the PR description claims.
Merge blockers
-
quarkus.morphium.read-preferenceis silently ignored.MorphiumProducer.java:290callsdriverSettings().setDefaultReadPreferenceType(String)— a dead field in core that nothing consumes. The actual read path usesgetDefaultReadPreference(), which defaults toReadPreference.nearest(). So every app on a replica set reads with NEAREST instead of the documented defaultprimary(stale reads out of the box), and no setting changes that. Fix:cfg.setDefaultReadPreferenceType(...)(which parses + sets) or build aReadPreferenceand callsetDefaultReadPreference(...). -
Gizmo generation produces broken classes for legal Jakarta Data signatures (
MorphiumDataProcessor.java):- Non-void
@Deletemethods (long removeByStatus(...)— allowed by spec): the method descriptor uses the declared return type but both branches emitreturnVoid()→VerifyErrorat class load, i.e. app startup crash. short/byte/charparameters:boxPrimitive/unboxPrimitivefall through thedefaultbranch →VerifyErroras well.- Abstract methods the processor can't handle are silently skipped →
AbstractMethodErroron first production call. These should fail the build instead.
- Non-void
-
Silently wrong query results:
@Findparameters without@Byare dropped entirely —@Find List<Book> byAuthor(String author)returns all books. The@Querypath already implements the parameter-name fallback (Jakarta Data §4.6.1,-parametersis enabled); the@Findpath omits it.Sort/Order/PageRequest/Limiton derivedfindBy*methods are ignored (wrong ordering), andPagereturns run into a ClassCastException viareturnsSingle.
-
Transaction context can leak onto Quarkus worker-pool threads (
MorphiumTransactionalInterceptor):catch (Exception)missesErrors, and a failing abort (primary stepdown —getPrimaryConnectionthrows before theclearTransactionContext()finally) leaves the ThreadLocal context set. The next request on that thread "joins" a dead transaction → silent data loss. Needscatch (Throwable)plus defensive context clearing. (Root cause of the abort case is arguably in morphium-core; happy to take that part there.) -
Commit retry can double-apply a transaction. A transient error thrown by the commit itself (e.g. code 251 after failover where the server actually committed but the reply was lost) retries the whole business method → all inserts applied twice. Drivers retry only the commit for exactly this reason, never the statements.
-
Migration lock mutual exclusion breaks after the 60s TTL. The lock is never renewed during execution — a migration running longer than the TTL lets a second instance atomically steal the lock and run the same still-running change unit concurrently. A heartbeat extending
expires_at(owner-guarded) is needed. Also: a held lock currently fails startup immediately instead of waiting — in a k8s rollout with 3 replicas, two pods crash-loop until the migration finishes. (The lock acquisition itself is genuinely TOCTOU-free — verified down through the writer/wire layers. Nice.) -
The advertised Docker-skip doesn't work. The
assumeTrue(isDockerAvailable())inMorphiumTransactionalTestsits in@BeforeAll, butQuarkusTestExtensionboots the app (and Dev Services start the container —MorphiumDevServicesProcessor.java:103has no Docker guard at all; compare Quarkus' ownDevServicesMongoProcessor, which gates onDockerStatusBuildItem) before user@BeforeAllruns. Without Docker the build goes red instead of skipping. The check belongs in the processor (skip Dev Services with a warning) and/or a JUnitExecutionCondition. -
@MorphiumTransactionalon async/reactive methods commits too early. ForUni/CompletionStagereturns (or thedoXxxAsyncrepository methods, which run on morphium's async pool) the interceptor commits before the actual DB work happens on another thread. Should at least fail fast on async return types. -
Docs document a property that doesn't exist:
quarkus.morphium.create-indexes(inconfiguration.adocand README) — the real property isindex-check. This also breaks the "allquarkus.morphium.*properties are unchanged" migration promise for 1.2.0 users. Theconfiguration.adoc"documents every available property" claim misses 10 properties (including the wholemigration.*group).
Should-fix (can be follow-up issues)
MorphiumBlockingCallDetectorobservesStartupEventand dereferences the Morphium proxy → forced blocking connect (with the full retry ladder) on the boot thread; defeats lazy init and the startup health check. Rolling deploy with Mongo briefly unreachable → CrashLoopBackOff.- Liveness check is coupled to
driver.isConnected()— a Mongo outage makes k8s restart every pod in a loop. DB connectivity belongs in readiness only (which already does it right, citing the Quarkus MongoDB extension precedent itself). index-check=create-on-write-new-colleaves core'sWARN_ON_STARTUPClassGraph scan active → native-image crash; the one gap in the otherwise careful index-check matrix.- Dev Services skip logic is a blacklist (everything except
PooledDriverskips) —SingleMongoConnectDriverusers lose Dev Services. Should be anInMemDriverwhitelist. - The default Dev Services path (replica-set container) has zero automated coverage — all integration tests run InMem with Dev Services disabled.
MorphiumStartupCheckTesttests a copy of the formula, not the production class — flipping||to&&inMorphiumStartupCheckwouldn't fail any test.- Partial credentials (username without password) silently connect unauthenticated; malformed
MorphiumIdin a JSON body yields HTTP 500 instead of 400;(int)cast ofcache.global-valid-timecan overflow. - Empty-transaction tolerance depends on the exact server error string
"Cannot start a transaction"(untested);BEFORE_COMMITfires once per retry attempt (duplicate outbox side effects); CosmosDB detection is fail-open with a deadUnsupportedOperationExceptioncatch. - Migration polish: client-clock skew shortens the effective lock TTL; a failing rollback is only logged (not suppressed into the thrown exception, changelog stays FAILED);
ordercompared as plain string ("10" < "2"); the idempotency requirement for change units is undocumented. - Native image: custom
@Entity(nameProvider=...)classes aren't registered for reflection; subclasses of annotated base classes escape the Jandex scan (annotations aren't@Inherited, core resolves via hierarchy walk). - Docs drift: SNAPSHOT versions hardcoded in
antora.yml/attributes.adoc/README (release.sh only touches poms);testing.adocshowsmorphium.*without thequarkus.prefix;gaps/JAKARTA-DATA.mdis stale. pom.xmlnote:-DskipExtensionsalso skipsmorphium-jakarta-data(same profile), so it's "core + PoppyDB only" — the PR text says otherwise.
Verified as correct (so you don't re-check)
Core untouched (zero diff hunks in morphium-core), no Quarkus/Testcontainers in the core tree, BOM placement as described (3.32.3 resolves everything), all 5 @BuildStep classes registered, no io.quarkiverse coordinates left, integration-tests doubly excluded from publishing, release.sh sane (bash -n, module/artifact registries complete), Dev UI leaks no credentials (Atlas URL sanitized), all Gizmo call-site descriptors match the real bridge signatures, driver names match core's @Driver annotations, health checks are cheap per probe, and the STATIC_INIT/RUNTIME_INIT migration ordering via the marker build item is correct.
Happy to discuss any of these — and thanks again, the overall shape of this is very good.
MorphiumProducer called cfg.driverSettings().setDefaultReadPreferenceType(String), which sets a dead defaultReadPreferenceType field that nothing in morphium-core reads. The actual read path uses DriverSettings.getDefaultReadPreference(), which returns a separate ReadPreference-typed field defaulting to ReadPreference.nearest() -- so every replica-set app read with NEAREST instead of the documented default primary (stale reads out of the box), and no configured value ever changed that. Adds a small string-to-ReadPreference parser (no such parser existed in core) and calls setDefaultReadPreference(ReadPreference) instead, with regression tests for all five accepted values plus the case-insensitive and unrecognized-value fallback paths. Merge blocker #1 found by Stephan Boesebeck's review on PR sboesebeck#267 (sboesebeck/morphium).
…delete methods Jakarta Data's @delete Javadoc requires: for a parameter-based (condition-only, no entity argument) @delete method, the return type must be void, int, or long, and if int/long, the method must return the number of deleted records. executeAnnotatedDelete() always returned void, discarding the count entirely -- any int/long-returning @delete method that relied on it would necessarily be wrong (or, in the Quarkus Gizmo generator, crash at class-load time -- fixed separately in quarkus-morphium). Adds executeAnnotatedDeleteCounted(), returning the number of deleted entities; executeAnnotatedDelete() now delegates to it and discards the result, for callers whose method is declared void. Found in code review on PR sboesebeck#267 (sboesebeck/morphium) -- merge blocker #2 (shared with the quarkus-morphium Gizmo codegen fix).
…sitory codegen Three related class-generation bugs in MorphiumDataProcessor, all found by Stephan Boesebeck's review on PR sboesebeck#267 (merge blocker #2): 1. generateDeleteAnnotatedMethod() always emitted mc.returnVoid(), regardless of the method's declared return type. A legal Jakarta Data signature like `long removeByStatus(...)` (parameter-based @delete returning int/long, permitted -- see FindMethodBridge's companion fix) got a method descriptor promising a long/int return but bytecode that returns void: a VerifyError at class load, i.e. an application startup crash. Now dispatches to the new executeAnnotatedDeleteCounted() bridge and returns the count (boxed down to int via Math.toIntExact when needed) for int/long return types, keeping the void path for void-declared methods. 2. boxPrimitive()/unboxPrimitive() had no cases for SHORT/BYTE/CHAR parameter or return types -- they fell through to the `default -> value` branch, emitting no box/unbox instructions between an object type and a primitive slot. Also a VerifyError at class load for any repository method using these three types. Added all three cases to both methods, mirroring the existing DOUBLE/FLOAT/LONG/INT/BOOLEAN handling. 3. generateCustomQueryMethods() silently skipped any abstract repository method matching none of @Query/@Find/@Delete/@Insert/@Save/@update and no findBy*/countBy*/existsBy*/deleteBy* naming pattern, leaving it unimplemented on the generated class -- legal at class load, but any call throws AbstractMethodError on first production use. Now throws IllegalStateException at build time instead, naming the interface and method, so an unsupported repository method is caught during the build, not by a user hitting the endpoint. Verified: full reactor build green, all 242 integration tests green (no regression), including the existing @Delete/derived-query/Jakarta-Data-CRUD suites that exercise this generator.
…ument ssl.tls-configuration-name Two smaller items from the review. isAsyncReturnType() only knew CompletionStage and io.smallrye.mutiny.Uni. Multi has exactly the same problem: the interceptor commits right after ctx.proceed() hands back the Multi, i.e. before the asynchronous work has run, so it has to fail fast for the same reason Uni does. While adding the test it turned out the pre-existing Uni detection was never covered at all -- only CompletionStage/CompletableFuture were. Both name comparisons are now tested. Since Mutiny is deliberately not a dependency of this module, the test defines classes carrying those exact fully-qualified names at runtime via ByteBuddy (already on the test classpath transitively, no new dependency) and asserts the FQN before asserting the detection, so the test cannot pass for the wrong reason. An earlier attempt placed a stub source file in the io.smallrye.mutiny package instead; that was dropped because it would collide with the real class as a split package / duplicate class the moment Mutiny ever becomes a real dependency. quarkus.morphium.ssl.tls-configuration-name existed in SslConfig but was missing from both configuration.adoc and the README table. A sweep over the remaining ssl.* properties confirmed the other eight were already documented. Verified: 59/59 tests green in quarkus-morphium/runtime; configuration.adoc table delimiters still balanced. Remaining items from Stephan Boesebeck's re-review on PR sboesebeck#267 (sboesebeck/morphium).
|
Hi Heiko, good news first: I've reviewed the A/B fix commits ( I'd like to get the release out, so here's the deal: you don't need to work through the whole remaining list before merge. Only two items left on the merge path, both one-liners:
Everything else moves to follow-up issues after the merge — none of it is silent-corruption class anymore:
Ship the two one-liners and I'll approve. If you want, collect the follow-up list into issues after the merge — happy to review those at whatever pace. I want to include the modules into the 6.3.0 release. and because of some production issues, we need the fix rather earlier than later. Hence I'd like to release soon (today / tomorrow) |
… gate the Docker-dependent test before boot The previous attempt only moved the detector's listener registration onto a background thread. That kept the boot thread free but did not stop the eager connect: registerListener() still called morphiumInstance.get(), which dereferences the CDI proxy and runs MorphiumProducer.buildMorphium() with its full retry ladder. Without Docker, Dev Services now skips cleanly, the app boots against the default localhost:27017, and that connect fails after retrying -- while MorphiumTransactionalTest's @BeforeAll Docker check never gets a chance to run, because @QuarkusTest boots the application before JUnit's @BeforeAll. The detector no longer resolves Morphium at all. It is not a CDI bean anymore (removed from AdditionalBeanBuildItem), the @observes StartupEvent observer and its thread are gone, and it is now a static registerOn(Morphium) called from buildMorphium() right after the connect succeeded. That makes it structurally impossible for the detector to be the cause of a connect, instead of merely unlikely. Additionally, MorphiumTransactionalTest is now gated by a JUnit ExecutionCondition that calls DockerClientFactory.instance() .isDockerAvailable() directly -- JUnit evaluates conditions before QuarkusTestExtension boots the application, which the replaced @BeforeAll assumeTrue could not. Testcontainers' @EnabledIfDockerAvailable is deliberately not used; its detector misreports under Quarkus test classloading (already documented in that class). Verified with Docker: full reactor build green, MorphiumTransactionalTest runs (5/5 green, 0 skipped -- i.e. the condition correctly does NOT disable it when Docker is present), 59/59 runtime-module tests green. NOT verified: an actually Docker-less run. Three simulation attempts all failed for traceable reasons: DOCKER_HOST is ignored by Testcontainers' UnixSocketClientProviderStrategy (hardcoded /var/run/docker.sock); TESTCONTAINERS_DOCKER_SOCKET_OVERRIDE does make isDockerAvailable() return false but Quarkus's own guard uses ContainerRuntimeUtil, i.e. the docker binary, so DockerStatusBuildItem still reported available; -Dquarkus-local-container-runtime=UNAVAILABLE is overridden by a pinned strategy in ~/.testcontainers.properties. Worth noting independently: the project relies on two different Docker detections (Quarkus binary check vs Testcontainers socket check) that can disagree -- likely the same reason @EnabledIfDockerAvailable misbehaved here. This needs verifying on a genuinely Docker-less machine or in CI. Blocker 7 follow-up from Stephan Boesebeck's re-review on PR sboesebeck#267 (sboesebeck/morphium).
… 2 review 1. Private interface methods broke the build -- a regression my own earlier fix introduced. Jandex's isDefault() means "public && !static && !abstract", so a private interface method (legal since Java 9, it has a body) is not "default", fell through every guard, and hit the "no recognized pattern" IllegalStateException. Perfectly legal user code failed the build. The guard now skips everything non-abstract via isAbstract(), plus an explicit skip for abstract toString()/equals()/ hashCode() redeclarations (Object provides those regardless). 2. A @delete method returning boolean/Integer/Long still produced the original VerifyError at class load: the returnsCount guard only covered primitive int/long, so anything else fell into the void branch and generated a bare return for a method whose descriptor promises a value. Now a build-time error with a clear message. Jakarta Data only permits void, int or long for a parameter-based @delete. 3. Abstract methods inherited from a CUSTOM super-interface escaped the build-time check entirely, because Jandex's methods() only returns declared methods -- so they still hit AbstractMethodError on first call. The scan now walks interfaceTypes() recursively, treating the four standard Jakarta Data interfaces as hierarchy dead-ends so their CRUD methods aren't misreported, with a visited-set guarding against diamond inheritance. 4. The @by parameter-name fallback (Jakarta Data 4.6.1) was added to the @find path but not to generateDeleteAnnotatedMethod, which still only looked for @by. A @delete method relying on parameter names got hasByParams=false, was treated as an entity-parameter delete, and called doDelete(stringArg) at runtime -- trying to delete a String as an entity. New MorphiumDataProcessorCustomMethodsTest covers all four against a real synthetic Jandex index (same approach as MorphiumProcessorReflectionTest): a private helper plus an abstract toString() must NOT break generation; an unsupported @delete return type must fail at build time; a method declared only on a custom super-interface must still be generated (verified it is declared on WithAudit only, not on the repository itself -- exactly the case item 3 previously missed); and the @delete parameter-name fallback is treated as a condition delete. Verified: full reactor build green, 23/23 deployment-module tests green (19 existing + 4 new). Blocker 2 follow-up from Stephan Boesebeck's re-review on PR sboesebeck#267 (sboesebeck/morphium).
|
Hi Heiko, reviewed Entity-parameter @Delete
void remove(CustomerEntity customer);now takes the condition branch with (Standard Suggested fix: apply the name-fallback only to parameters whose type is not the entity type (or a And a gentle reminder since the comments may have crossed mid-work: the two one-liners from my 16:25 comment ( |
…ng change unit, and prove both Follow-up on blocker 6. Three things were wrong beyond the original fix. renewLock() ignored its own update result. When the owner-guarded update matched 0 documents -- i.e. another instance had taken the lock over -- this instance carried on and ran the remaining change units concurrently with the new owner. It now inspects "n" the way acquireLock() already did and aborts the run. The comment claiming subsequent writes would be no-ops anyway was simply wrong and is corrected: only releaseLock() is owner-guarded, recordExecution() and the change units themselves write unconditionally. Renewal only happened BETWEEN change units, so a single unit running longer than the TTL (an index build on a large collection, say) still allowed the atomic steal of that very in-flight unit. Rather than documenting the constraint away, there is now a real in-flight heartbeat: a daemon thread renewing the lock while a unit is still executing, stopped in a finally block, owner-guarded, with its failure surfaced instead of swallowed. Writing the tests for that exposed a genuine bug in the heartbeat itself: its tick interval was TTL/3 but floored at one second, so for any small lockTtlSeconds the first tick fired only after the TTL had already elapsed -- the heartbeat was structurally unable to renew in time. The floor is now 200ms, which keeps the "don't hammer the database" intent for realistic TTLs while making small ones actually serviceable. Both regression tests were rewritten, because the previous pair could not work. The obvious "a contender calls acquireLock() and must fail" approach is impossible against InMemDriver: an upsert whose filter matches nothing (expires_at still in the future) is seeded from the equality predicates only -- correct -- but then goes through storeInternal(), which treats an existing _id as a replace rather than raising a duplicate key error the way a real server would. So any contender steals a still-valid lock there, regardless of renewal. That is also why the original test could never prove anything. The tests now prove renewal positively instead: an observer thread reads the lock document mid-run and asserts the owner is unchanged and expires_at has moved forward. The driver divergence is documented at the tests. Both are mutation-proofed, which mattered: the first version of the between-units test stayed GREEN with renewLock() disabled -- the same mistake the reviewer had called out, repeated. At a 1s TTL the heartbeat ticks every ~333ms, so with 800ms units it was silently renewing the lock and masking the missing call. That test now uses a 6s TTL, making the heartbeat interval (2s) longer than any single unit. Disabling renewLock() now reddens only the between-units test, disabling the heartbeat only the in-flight test, each on its own assertion, the other 8 staying green. The wait test's lock id was wrong too (it seeded "morphium_migration_lock" while the runner uses "migration_lock"), so the polling loop was never exercised -- it would have stayed green with acquireLockWithWait() deleted. It now goes through an accessor instead of a copy-pasted literal, so renaming the constant cannot blind it again. Verified: full reactor clean build green, 254/254 integration tests green (0 failures, 0 errors, 0 skipped), Migration Framework 9/9. Blocker 6 follow-up from Stephan Boesebeck's re-review on PR sboesebeck#267 (sboesebeck/morphium).
|
Thanks for the deep re-review — the two regressions my own fix wave introduced were exactly the kind of thing I'd rather hear now than after a merge. Status per item: A — commit retry was a no-op and converted data loss into reported successConfirmed and fixed. You were right about the mechanism:
Also added the test coverage that was missing entirely for this path. This needed Mockito (test scope, version from the inherited Quarkus BOM) — hand-faking Test quality verified by mutation, not by passing: commenting out One note: Mockito's inline mock maker cannot instrument B — deleteBy/countBy/existsBy with a dynamic parameterConfirmed, including that it's a real regression (that signature did delete before, the argument was just ignored). Root cause exactly as you described: the new 12-arg overload builds its own query and never consults Non-FIND prefixes now delegate to Tests: the delete test asserts the actual document count before (3) and after (1) plus that Blocker 2 residuals — all four fixed
19/19 deployment-module tests green. Unit tests for these four are still outstanding — flagging that rather than implying coverage. Blocker 6 — migration lock
The wait test's lock-id bug was real and embarrassing — it would have stayed green with For the in-flight steal I went with the real heartbeat rather than documenting the constraint away: a daemon thread renews the lock while a single change unit is still running, terminated in a Writing the tests for this surfaced two things worth telling you about. A real bug in the heartbeat itself: And something in morphium-core you may want to look at, since it's your side: That has two consequences. It's why the original Both tests are mutation-proofed, and that turned out to matter: the first version of the between-units test stayed green with I have not touched morphium-core for this — flagging it rather than fixing it in a Quarkus PR. Blocker 7 — Docker guardTwo changes. The detector no longer dereferences the What I could not prove, and I'd rather say so than claim it: I couldn't produce a genuinely Docker-less run on this machine. Three attempts, each traced to its cause:
Which surfaces something you may care about independently: the project has two different Docker detections that can disagree — Quarkus's binary check versus Testcontainers' socket check. That's very likely also why Smaller items
All six items are pushed. Final verification: full reactor clean build green, 254/254 integration tests green (0 failures, 0 errors, 0 skipped), 59/59 runtime-module and 23/23 deployment-module unit tests green. Three things I'd flag as genuinely open rather than done:
And two questions back at you: whether you want a |
|
Hi Stephan, sorry, our AI agents were told to automatically process incoming reviews and therefore they completed everything, even though you suggested to postpone some minor fixes. However, now almost everything you mentioned has been sorted out with one question regarding InMemoryDriver Support status. Hope its fine, that we did not stop the agent once you suggested a split in major and minor findings. Heiko |
…ed files
Copilot flagged that the published POMs still carry an
"ENTSCHEIDUNG-OFFEN D1" marker and German-only commentary. That is fair, and
it turned out to be broader than the two spots it named: seven places across
four POMs plus one Java Javadoc referenced internal planning documents
("D1", "D3", "B6", "I4", "Absicherung", "Begleitmassnahmen",
"D3-reactor-strategie.md"). None of those documents exist anywhere in this
repository, so for anyone reading the merged code they are dead pointers.
The ENTSCHEIDUNG-OFFEN markers are removed rather than translated: they
tracked a decision that has since been made and implemented (both modules
inherit their version and morphium.version from morphium-parent, visible in
the <parent> block right below where the marker sat). Everywhere else the
substance of the comment is kept and the document references are replaced by
the reasoning itself, so each comment stands on its own. The reactor's
I1-I5 invariant list is deliberately kept as-is -- it is self-explanatory and
genuinely useful; only its external references were dropped.
morphium-jakarta-data/pom.xml is included because it carries the identical
marker (plus a German "check whether this belongs in morphium-parent" note)
and is part of the same published reactor, even though that module already
merged separately.
Comments only: verified via diff that no version, dependency, property,
module or other XML structure was touched. mvn validate green (that also
catches an accidental double hyphen inside an XML comment, which is invalid
XML -- a trap I had already hit once in this branch), mvn install green,
integration-tests test-compile green.
The other Copilot finding in the same review -- that the extension guide URL
points at the develop branch while the default branch is main -- is a false
positive: this repository's default branch IS develop, so the URL is
correct. It 404s only because the file it points at arrives with this very
PR.
Copilot review comment on PR sboesebeck#267 (sboesebeck/morphium).
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 148 out of 148 changed files in this pull request and generated no new comments.
Suppressed comments (9)
quarkus-morphium/integration-tests/src/test/java/de/caluga/morphium/quarkus/it/MorphiumVersionTest.java:1
- This test claims to cover an entity 'without @Version', but it uses
ItemEntitywhich is annotated with@Version. As written, it doesn't validate the intended scenario and may give false confidence. Suggested fix: either (mandatory) introduce a separate entity class in the IT module without@Versionfor this test, or (alternative) rename the test/display name + comments to reflect what it actually verifies.
quarkus-morphium/runtime/src/test/java/de/caluga/morphium/quarkus/health/MorphiumStartupCheckTest.java:1 - These two tests assert the exact same condition (
isEverConnected(0.0, false) == false). Keeping both adds duplication without increasing coverage. Suggested fix: remove one of them, or change one to cover a distinct edge case (e.g. negative/NaNconnectionsOpenedbehavior if that is meaningful for the driver stats).
quarkus-morphium/runtime/src/test/java/de/caluga/morphium/quarkus/health/MorphiumStartupCheckTest.java:1 - These two tests assert the exact same condition (
isEverConnected(0.0, false) == false). Keeping both adds duplication without increasing coverage. Suggested fix: remove one of them, or change one to cover a distinct edge case (e.g. negative/NaNconnectionsOpenedbehavior if that is meaningful for the driver stats).
quarkus-morphium/runtime/src/main/resources/META-INF/quarkus-extension.yaml:1 - The
guideURL points at a GitHubdevelopbranch blob, which is likely to drift or become stale for released artifacts. For Quarkus extension metadata, it’s better to link to a stable, versioned documentation location (e.g. a published docs site or a tag/branch that matches releases) so consumers don’t land on unrelated content.
quarkus-morphium/runtime/src/main/java/de/caluga/morphium/quarkus/transaction/MorphiumTransactionEvent.java:1 - The event stores rollback failures as
Exception, which excludes non-ExceptionThrowables (e.g.Error). If the interceptor ever catches/rethrows broader throwables, this API will force lossy wrapping or dropping the cause. Suggested fix: storeThrowable(or an Optional-like representation) instead ofException, and update the accessor/Javadoc accordingly.
quarkus-morphium/runtime/src/main/java/de/caluga/morphium/quarkus/transaction/MorphiumTransactionEvent.java:1 - The event stores rollback failures as
Exception, which excludes non-ExceptionThrowables (e.g.Error). If the interceptor ever catches/rethrows broader throwables, this API will force lossy wrapping or dropping the cause. Suggested fix: storeThrowable(or an Optional-like representation) instead ofException, and update the accessor/Javadoc accordingly.
quarkus-morphium/runtime/src/main/java/de/caluga/morphium/quarkus/transaction/MorphiumTransactionEvent.java:1 - The event stores rollback failures as
Exception, which excludes non-ExceptionThrowables (e.g.Error). If the interceptor ever catches/rethrows broader throwables, this API will force lossy wrapping or dropping the cause. Suggested fix: storeThrowable(or an Optional-like representation) instead ofException, and update the accessor/Javadoc accordingly.
quarkus-morphium/integration-tests/src/test/java/de/caluga/morphium/quarkus/it/StatusStats.java:1 - This new source file lacks the Apache 2.0 license header that most other newly added Java files in this PR include. If the repository requires license headers for new sources, add the standard header here (and in the other minimal record-only files added alongside it) to keep compliance consistent.
quarkus-morphium/deployment/src/main/java/de/caluga/morphium/quarkus/deployment/MongoDBStartable.java:95 - This recompiles the same regex
Patternon every call. Suggested fix: hoist the pattern into aprivate static final Patternconstant and reuse it here.
String getReplicaSetName() {
if (container instanceof MongoDBContainer mongoContainer) {
String connStr = mongoContainer.getConnectionString();
Matcher m = Pattern.compile("[?&]replicaSet=([^&]+)")
.matcher(connStr);
return m.find() ? m.group(1) : "docker-rs";
}
return null;
}
|
Hi Heiko, yeah, sometimes AI agents go rogue — so you need to keep an eye on them 😉 Quick status check on the current head (
Everything else from the review round looks addressed. Let me know if I'm looking at a stale view of these two. |
…eting nothing Another regression from my own earlier fix, caught by Stephan. The @by parameter-name fallback I added to generateDeleteAnnotatedMethod checked only whether a parameter HAS a name, never what its TYPE is. So for @delete void remove(CustomerEntity customer) -- an entity lifecycle parameter, which Jakarta Data says must go to doDelete(entity) -- the fallback claimed the parameter as a condition, built the query {customer: <entity>}, matched nothing, and query.delete() removed zero documents while the method returned normally. Silent data loss, and it worked correctly before my fallback existed. The fallback now applies only when the parameter type is not the entity itself, an array of it, or a List/Collection/Iterable of it. An explicit @by is still always honoured, even on an entity-typed parameter, since that is a deliberate opt-in by the developer. Mixing an entity parameter with condition parameters in one @delete is now rejected at build time. The Delete javadoc in jakarta.data-api 1.0.1 defines either exactly one entity/List<E>/E[] lifecycle parameter or condition parameters, not both, so there is no semantics to implement -- failing the build beats inventing one. Tests: 4 new unit tests over a synthetic Jandex index (isEntityParameter across all type shapes plus negative cases, entity-parameter delete is not treated as a condition delete, both cases asserted side by side, and the build-time rejection of the mixed case), plus an integration test asserting the actual document count before and after remove(OrderEntity) with a per-customer cross-check -- the test that would have caught this. Mutation-proofed rather than trusted: disabling the type check makes the build fail, and instructively so -- remove(OrderEntity) is then misread as a condition parameter and trips the new mixed-case rejection. The two guards compose, so even a failing type check cannot resurrect the silent-delete path. Still open: the entity branch continues to support only a single entity via doDelete(Object), not List<E>/E[] via doDeleteAll(List). That predates this bug and is unrelated to it; noting it rather than bundling it in. Verified: clean reactor build green, 27/27 deployment-module tests, 255/255 integration tests (0 failures, 0 errors, 0 skipped). Reported by Stephan Boesebeck on PR sboesebeck#267 (sboesebeck/morphium).
…s two small cleanups Three findings from the latest Copilot review. The test named "Entity without @Version stores and updates normally" used ItemEntity -- which HAS a @Version field -- and asserted getVersion() == 2, i.e. it verified working version tracking, the exact opposite of its name. Its own comments admitted it ("re-uses ItemEntity", "pretend no version was tracked"). It was pure false confidence. There is now a real UnversionedEntity with no @Version field, and the test proves the actual behaviour: a second client loads and updates the document in between, and the now-stale original reference still stores successfully instead of throwing VersionMismatchException. The build confirms the new entity is picked up (27 instead of 26 @Entity/@Embedded classes registered). The other three tests in the class keep using ItemEntity, which is correct -- they test versioning. MorphiumStartupCheckTest had two tests asserting the identical condition (isEverConnected(0.0, false) == false). Removed the duplicate rather than repurposing it: the remaining four tests already cover all four boolean combinations of "connectionsOpened > 0 || driverConnected", and a negative connection count is not a meaningful case for a monotonic stats counter, so there was no uncovered edge to move it to. The survivor has the more descriptive SRV-discovery framing. MongoDBStartable recompiled the same replicaSet regex on every getReplicaSetName() call; hoisted into a static final Pattern. Two further Copilot suggestions were deliberately not taken. Changing MorphiumTransactionEvent from Exception to Throwable would alter a public API for a case that intentionally fires no event at all -- the interceptor catches Throwable but only fires AFTER_ROLLBACK for Exceptions, letting Errors propagate, which is the agreed behaviour. And the missing license header on StatusStats matches existing practice in integration-tests, where many files have none; adding one there alone would be inconsistent rather than more correct. Verified: 255/255 integration tests, 58 runtime-module tests (one fewer, the removed duplicate), clean reactor build green. Copilot review on PR sboesebeck#267 (sboesebeck/morphium).
|
You were right on the second one, and it was mine. On the first one I still think it's a false positive — but my earlier reasoning for that was wrong, so let me redo it properly. 2. Entity-parameter
|
|
Hi Heiko, good progress on the latest wave — the entity-parameter One item left: |
…COL in native images Third time Stephan raised this branch, and he was right that it is broken asymmetry. My two earlier dismissals were both wrong, in different ways: First I claimed setAutoIndexAndCappedCreationOnWrite(true) forces the index check to NO_CHECK. It does not -- MorphiumConfig lines 509-511 set BOTH the index check and the capped check to CREATE_ON_WRITE_NEW_COL. Then I checked only the index path, found that checkIndices()' ClassGraph scan is gated to CREATE_ON_STARTUP/WARN_ON_STARTUP (Morphium.java 575-576), and concluded there was no scan. That gating is real, but I had missed the second scan: checkCapped() (Morphium.java 3354, scanning at 3358) is called UNCONDITIONALLY at line 529, with no mode gate at all. Looking at only one of the two values that setter writes is what hid this from me twice. Tracing the whole chain, though, changes the severity rather than the conclusion: buildMorphium() already pre-registers the build-time @CappeD list into ClassGraphCache before the Morphium constructor runs, and getClassesWithAnnotation() returns a pre-registered entry without scanning -- including an empty one, and cappedClassNames defaults to Collections.emptyList(), never null. So the scan was in practice already unreachable, and native images were not actually crashing here. The branch is still worth fixing. It now forces both checks to NO_CHECK when ImageMode.current() is NATIVE_RUN, which makes the safeguard independent of that call-ordering staying intact and restores symmetry with the other three branches. A native run cannot create collections on the fly anyway, so nothing of value is disabled. The JVM path is deliberately untouched: there the scan is a startup cost rather than fatal, and disabling it would defeat the whole point of the mode. The comment now spells out this reasoning so the branch does not read as an oversight a fourth time. Five unit tests, including a JVM counter-test asserting both checks stay on CREATE_ON_WRITE_NEW_COL, so the fix cannot silently break the mode for JVM users. Mutation-proofed: disabling the native guard reddens exactly the native test on its own assertion and leaves the other four green. Verified: clean reactor build green, 63/63 runtime tests (58 + 5 new), 27/27 deployment, 255/255 integration (0 failures, 0 errors, 0 skipped). Reported three times by Stephan Boesebeck on PR sboesebeck#267 (sboesebeck/morphium).
|
You were right, and I was wrong twice — in two different ways. Fixed in Where I went wrongFirst dismissal: I claimed Second dismissal: I then verified that // Morphium.java:529 — no mode gate at all
var capped = checkCapped();
What tracing it fully changedFollowing the chain to the end does shift the severity, and I want to be straight about that rather than overstate the fix: That doesn't make your point wrong, it makes it a latent one: the branch leaves both checks on The fixBoth checks are now forced to The JVM path is deliberately untouched. There the scan is a startup cost, not fatal, and switching it off would defeat the entire purpose of the mode for every JVM user. If you'd rather have it off there too for startup time, that's your call. The comment now spells all of this out — cause, why the pre-registration already covers it, and why the guard is still there — so the branch doesn't read as an oversight a fourth time. Five unit tests, including a JVM counter-test asserting both checks stay on VerificationClean reactor build green, 63/63 runtime tests (58 + 5 new), 27/27 deployment, 255/255 integration — 0 failures, 0 errors, 0 skipped. Thanks for pushing on this three times instead of letting it go. Two of my "verified false positive" calls in this PR turned out to be incomplete analysis, which is a good argument for you keeping the sceptical eye on the ones I'm still claiming. |
|
Hi Heiko, all three blockers are cleanly resolved now, and the Genuinely impressed by this whole PR: a clean module split, careful attention to native-image and Dev Services edge cases, and — maybe the best part — you took a deep, occasionally uncomfortably thorough review completely in stride, fixed everything properly instead of the fast/shallow version, and even called out where the review itself had gaps. That's a rare combination. Thank you for the effort and the patience. Merging now. Really looking forward to |
|
Hi Stephan, please hold back releasing 6.3.0 - We've come up with a regression in two of our applications when switching to the 6.3.0-SNAPSHOT. As far as I see its a very old bug. A fix is on the way. |
|
yes - I was running the whole test suite anyways and found a flaky test I want to investigate. I will post an announcement before creating the release! 😉 |
|
I let Copilot do its first round on our fork: |
Hi Stephan,
Glad #266 landed! Here comes the second building block of the modularization:
quarkus-morphium, the Quarkus CDI extension for Morphium.What this PR does
Four submodules (
runtime,deployment,testing,integration-tests), three of them published:Morphium, type-safe runtime configuration via@ConfigMapping(quarkus.morphium.*)@MorphiumTransactionalwith CDI transaction events (MorphiumTransactionEvent), including automatic detection when Azure CosmosDB doesn't support multi-document transactions@Repositoryimplementations via Gizmo bytecode (no runtime reflection, no dynamic proxies)@Entity/@Embeddedclass)MorphiumIdJSON serialization as its canonical 24-character hex string (Jackson + JSON-B)groupId change
The extension previously published under
io.quarkiverse.morphium, but it doesn't actually live in the Quarkiverse organization (no response from the maintainers on a namespace request). Maven coordinates now follow Morphium's own groupId,de.caluga:quarkus-morphium, with lockstep versioning to the reactor. Existing users ofio.quarkiverse.morphium:quarkus-morphium:1.2.0need to change the groupId tode.calugaand the version to the adopted Morphium version (currently6.3.x) — no package renames, no API changes, only the Maven coordinates move.Optionality
The core is unchanged (
git diff --statagainstdevelopconfirmsmorphium-core,poppydb,morphium-jakarta-dataare untouched). The core dependency tree is free of Quarkus/Testcontainers.-DskipExtensionsbuilds only core + PoppyDB +morphium-jakarta-data. The Quarkus BOM import stays in the module POM (quarkus-morphium/pom.xml); only the version property (quarkus.version) lives centrally in the parent, so a Quarkus upgrade is a one-line change — if the BOM import itself moved to the parent, every core build would have to resolve ~400 Quarkus coordinates despite the core having no Quarkus dependency.Docker and integration tests
integration-testsuses Testcontainers for real MongoDB instances. Without a running Docker daemon, the affected test class skips itself (via aDockerClientFactory.instance().isDockerAvailable()check), keeping the build green. Theintegration-testsmodule is not published to Central.Extension conformance
A 30-point audit against the official Quarkus extension guidelines (naming scheme, build-step registration, deployment-artifact parity,
quarkus-extension.yamlcompleteness) found 21 fully met, 5 partially met, and 3 violated points — all 3 violations were fixed before this PR (see "Community review" below).Community review before this PR
As with #266, a review PR against my own fork ran first (CodeRabbit skipped it due to file count, GitHub Copilot found four real bugs): a missing build-step registration that would have silently disabled most extension features, a wrong native-image resource path (old groupId), Dev Services still starting a container despite an explicit
InMemDriver, and a doc line showing a Jakarta Data 1.1 annotation that doesn't exist yet in the1.0.0version used here. All four fixed and covered by regression tests.Verification
integration-tests: 242/242 tests green (Docker via Testcontainers)BUILD SUCCESSWhat's next
spring-boot-morphiumas the third and final building block follows as a separate PR once this one is merged.Open questions for you
quarkus-morphium/docs/) be published anywhere, or is the new MkDocs overview page (docs/quarkus-extension.md) enough?previeworstable?