Repository navigation
Feature/overall enhancements - #1
Merged
Merged
Conversation
…isation
Phase 0 – Repository restructure:
- Add root build.gradle (Spring Boot 4.0.4 BOM, Spotless 7.0.3, Java 21 toolchain,
Maven Central/GPG publication, javadoc gate)
- Add root settings.gradle with multi-module include for common, minio, redis
- Remove per-module settings.gradle files (spring-boot-starter-minio,
spring-boot-starter-redis) — conflicts with root multi-module build
- Update spring-boot-starter-minio/build.gradle: BOM-managed deps, common module
via `api project(':spring-boot-starter-common')`
- Update spring-boot-starter-redis/build.gradle: same pattern, Java 21 toolchain,
jackson modules added, actuator/aop/aspectj as compileOnly
Phase 0 – spring-boot-starter-common module (new):
- StarterException / StarterConfigurationException — shared unchecked exception hierarchy
- StarterBean — informational marker annotation
- MeterRegistryUtils — Micrometer tag/status constants
- HealthDetails — fluent builder for actuator detail maps (null-safe, copy-on-return)
- Unit tests: StarterExceptionTest, HealthDetailsTest
Phase 1 – Redis starter modernisation:
- Replace @configuration with @autoConfiguration + @ConditionalOnClass(RedisConnectionFactory)
- Add META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
- Remove deprecated META-INF/spring.factories
- Remove CachingConfigurerSupport (deleted in Spring Framework 7.x); use CachingConfigurer
- All auto-configured beans guarded with @ConditionalOnMissingBean for consumer override
- Extract buildObjectMapper() private helper — eliminates three duplicate ObjectMapper builds
- RedisConfigurationProperties: add host, port, metricName, cacheTtlDays with full Javadoc
- Add RedisOperationException extending StarterException (replaces misnamed MinioException files)
- Delete MinioException / MinioFetchException from wrong module (redis)
- Add RedisHealthIndicator (actuator HealthIndicator, PING-based check)
- Conditionally registered via @ConditionalOnProperty
management.health.redis-custom.enabled (default true)
- Uses HealthDetails builder from spring-boot-starter-common
- Unit tests: 4 tests covering UP/DOWN paths and detail-map content
- Add RedisMetricsAspect (AOP @around on all RedisUtil public methods)
- Wraps each call in a Micrometer Timer tagged with operation + status
- Metric name driven by RedisConfigurationProperties.metricName (default: redis.operations)
- Unit tests: 4 tests covering success timer, error timer, operation tag, custom metric name
- Wire both beans into RedisAutoConfiguration
- Health indicator: @ConditionalOnClass(HealthIndicator.class) + @ConditionalOnMissingBean
- Metrics aspect: @ConditionalOnClass(MeterRegistry) + @ConditionalOnBean(MeterRegistry.class)
- Both remain opt-out via @ConditionalOnMissingBean for consumer override
- Update redis build.gradle: add actuator, micrometer-core, aspectjweaver as testImplementation
Redis integration tests (tagged 'integration', use redis:7.2-alpine container): - RedisUtilIntegrationTest: store/retrieve, TTL expiry, Duration-based TTL, delete - RedisLockUtilIntegrationTest: acquire, second-acquire blocked, release then reacquire, wrong-requestId cannot release another owner's lock (Lua atomicity verified) - RedisHealthIndicatorIntegrationTest: UP status, PONG response detail, component tag MinIO integration tests (tagged 'integration', use MinIOContainer via testcontainers-minio): - MinioStorageServiceIntegrationTest: bucket auto-create, upload + list, remove object, list buckets Build changes: - spring-boot-starter-redis/build.gradle: add spring-boot-testcontainers, testcontainers:junit-jupiter as testImplementation - spring-boot-starter-minio/build.gradle: add spring-boot-testcontainers, testcontainers:junit-jupiter, testcontainers:minio as testImplementation
… CI/CD workflows Phase 4 — Maven Central publishing (already in root build.gradle from Phase 0): - OSSRH repository URL toggled on SNAPSHOT vs release via version suffix - In-memory GPG signing guarded on GPG_PRIVATE_KEY presence (no-op locally) - Both mavenJava publications include sources + javadoc jars Phase 5 — GitHub Actions: - .github/workflows/ci.yml: matrix build (Java 21), unit tests, javadoc gate, Spotless lint check, integration-tests job (Testcontainers, tagged 'integration') - .github/workflows/release.yml: triggered on v*.*.* tags, publishes to OSSRH via OSSRH_USERNAME/PASSWORD + GPG_PRIVATE_KEY/GPG_PASSPHRASE secrets, creates GitHub Release with auto-generated notes - .github/CODEOWNERS: default owner @saumilpatel - .github/PULL_REQUEST_TEMPLATE.md: checklist covering tests, JavaDoc, Spotless, CHANGELOG
…NG, CHANGELOG Root README: - Project overview, badge strip (CI, Maven Central, License, Java 21, Spring Boot 4.x) - Starter table with links and versions - Quick-start snippets (Gradle + Maven) - Design philosophy (consumer-first, no schema mgmt, observable by default, Testcontainers) - Project structure tree, build commands spring-boot-starter-redis/README.md: - Full property reference table (host, port, metric-name, cache-ttl-days) - Auto-configured beans table with @ConditionalOnMissingBean notes - RedisUtil usage example with key method signatures - RedisLockUtil usage example with Lua script explanation - Spring Cache integration with key generator format - Health indicator JSON output + disable snippet - Micrometer Prometheus output sample - Override pattern example spring-boot-starter-minio/README.md: - Full property reference table - StorageService API table and usage example - Health indicator + metrics sections - Proxy support documentation - Docker quick-start snippet spring-boot-starter-common/README.md: - Exception hierarchy, annotation, MeterRegistryUtils constants, HealthDetails usage CONTRIBUTING.md: - Java 21 coding standards, Spring auto-configuration patterns - JavaDoc format requirements (enforced by CI javadoc task) - Unit test + integration test conventions - New starter checklist - Conventional Commits format - Release process CHANGELOG.md: - [Unreleased] section documenting all Phase 0–6 changes - [2.0.0] minio + [1.0.0] redis historical entries
- Root settings.gradle + build.gradle (Spring Boot 4.0.4, Java 21, shared Spotless/signing/publication config) - Full spring-boot-starter-common module: StarterException, StarterConfigurationException, StarterBean, MeterRegistryUtils, HealthDetails — all with professional Javadoc + unit tests - Updated both existing build.gradle files to Spring Boot 4.0.4 with api dep on common - Removed standalone settings.gradle from each subproject - Deletes MinioException.java + MinioFetchException.java (wrong package, wrong name) - Creates RedisOperationException extends StarterException - Deletes deprecated spring.factories → creates AutoConfiguration.imports - Rewrites RedisKeyGenerator to implements CachingConfigurer (Spring Framework 7.x compat) - Rewrites RedisConfigurationProperties with host, port, metricName, cacheTtlDays fields - Rewrites RedisAutoConfiguration with @autoConfiguration, @ConditionalOnMissingBean on all beans, extracted buildObjectMapper(), TTL from properties
…, Redis+in-memory backends - @ratelimit annotation (method/class level) with requests, per (TimeUnit), key, name attributes - RateLimiter interface with tryConsume(key, maxRequests, windowSeconds) - RedisTokenBucketRateLimiter: atomic Lua sliding-window script over sorted set, fail-open on Redis error - InMemorySlidingWindowRateLimiter: ConcurrentHashMap + synchronized Deque<Long>, JVM-local fallback - RateLimitAspect: @around AOP, resolves method/class annotation, named-limit lookup, IP+method default key - RateLimitExceptionHandler: HTTP 429 with Retry-After header and structured JSON body - RateLimitAutoConfiguration: @ConditionalOnMissingBean wiring, Redis preferred over in-memory - RateLimitProperties: spring.rate-limit.* with named-limits map - Unit tests: InMemorySlidingWindowRateLimiterTest (bucket isolation, expiry), RateLimitExceededExceptionTest - Comprehensive README with config table, usage examples, observability section
- Package update: io.github.saumilp.starters - Maven group update : io.github.saumilp.starters - GitHub Repo update: github.com/SaumilP - Email update: email2saumil2024@gmail.com - CODEOWNERS handle update: @SaumilP All Java source trees moved to new directory layout. All imports, AutoConfiguration.imports, and spring.factories updated.
…dis store, 409 concurrent-duplicate handling
…s (log/JPA/composite), ActorResolver
…reEnabled, file-based provider
…nt, retry, Micrometer metrics
… schema-per-tenant, TenantResolutionFilter
…pre-signed URLs, health indicator
…ty, Kafka/RabbitMQ relay, @scheduled poller
… per starter Adds a complete examples/ directory with one runnable Spring Boot 4 application per starter. Each sub-module includes: build.gradle, application class, controller/service, application.yml, docker-compose.yml for local infrastructure, and README.md. Examples: redis, minio, rate-limiting, idempotency, audit-log, feature-flags, llm-client, multitenancy, aws-s3 (LocalStack), outbox (PostgreSQL + Kafka). Top-level examples/README.md cross-references all examples with infra requirements.
…hub.saumilp.starters.examples
…e.kts) Replaces all 23 Groovy DSL build files with Kotlin DSL equivalents: - settings.gradle → settings.gradle.kts - build.gradle (root) → build.gradle.kts - 11 starter module build.gradle → build.gradle.kts - 10 example sub-module build.gradle → build.gradle.kts
- Move gradlew/gradlew.bat from spring-boot-starter-redis/ to project root
- Add gradle/wrapper/gradle-wrapper.jar + gradle-wrapper.properties (Gradle 8.14.3)
— Spring Boot 4.0.4 requires Gradle 8.14+
- Unblock gradle-wrapper.jar from .gitignore (*.jar negation)
- Remove core plugins (java-library, maven-publish, signing) from root plugins {}
block — core plugins cannot use apply false in Kotlin DSL
- Add Foojay toolchain resolver to settings.gradle.kts for JDK 21 auto-provisioning
- Fix CI cache key hash pattern: **/*.gradle → **/*.gradle.kts
- Remove invalid docker:dind service from integration-tests job (Docker is
pre-installed on ubuntu-latest; Testcontainers uses /var/run/docker.sock)
- Simplify integration-tests run command to target only redis and minio modules
Replace io.spring.dependency-management BOM import (configure<> inside
subprojects{} is unreliable in Kotlin DSL when plugin is applied dynamically)
with Gradle-native platform() BOM on api/annotationProcessor/testImplementation/
testRuntimeOnly configurations in the starter subprojects block.
Examples are unchanged — they apply io.spring.dependency-management in their
own plugins block alongside org.springframework.boot, which auto-imports the BOM.
Also replace the minio starter's custom provided configuration with compileOnly
so spring-aop and micrometer-core get their versions resolved through the BOM.
…endencyHandler function
Spring Boot 4.0 removed/restructured several APIs that required updates: Health API (spring-boot-health 4.0): - HealthIndicator, Health, Status moved from org.springframework.boot.actuate.health to org.springframework.boot.health.contributor - ConditionalOnEnabledHealthIndicator, HealthContributorAutoConfiguration moved to org.springframework.boot.health.autoconfigure.contributor AOP starter removed: - spring-boot-starter-aop no longer exists in Spring Boot 4.0 - Replaced with direct dependencies: spring-aop + aspectjweaver - Affected: audit-log, feature-flags, rate-limiting starters and their examples Jackson 3.x migration (tools.jackson): - RedisIdempotencyStore: ObjectMapper/JacksonException now from tools.jackson - RedisAutoConfiguration: add explicit jackson-databind 2.x dep (needed by Spring Data Redis Jackson2JsonRedisSerializer legacy API) - RedisUtil: remove internal org.springframework.boot.configurationprocessor.json usage (dead code that built an unused JSONObject) - MinioStorageServiceImpl: remove com.sun.java.accessibility.util.EventID hack, replace with literal 0; drop unused jackson import Example fixes: - S3Controller: fix method signatures to match actual S3StorageService interface - BetaController: add explicit dev.openfeature:sdk dep (not exported by starter) - CacheController: fix del() method name (not delete()) - LlmClientAutoConfiguration: fix @PARAM name mismatch in javadoc
… 2.x Testcontainers 2.0 changes: - Artifact IDs gained a testcontainers- prefix: junit-jupiter -> testcontainers-junit-jupiter, minio -> testcontainers-minio - @testcontainers(disabledWithoutDocker=true) replaced by the new @EnabledIfDockerAvailable annotation; applied to all four integration tests so they skip gracefully when Docker is unavailable Spring Boot 4.0 autoconfigure relocation: - RedisAutoConfiguration renamed to DataRedisAutoConfiguration and moved to org.springframework.boot.data.redis.autoconfigure (spring-boot-data-redis) - Updated all three Redis integration tests Other test fixes: - multitenancy: add spring-boot-starter-web to testImplementation so jakarta.servlet HttpServletRequest is on the test classpath - outbox: add public setCreatedAt to OutboxEvent (only field lacking a setter) and update OutboxEventRelayTest to build fixtures via setters instead of the protected onCreate() JPA lifecycle callback (inaccessible cross-package)
…vadoc CI unit-test fix: - The "Run unit tests" step ran ./gradlew test, which executed the @tag("integration") Testcontainers tests. Docker IS available in CI, so @EnabledIfDockerAvailable did not skip them, and the MinIO container failed to pull (pinned image tag RELEASE.2024-01-01T00-00-00Z no longer exists -> NotFoundException). - The root `test` task now excludes the "integration" tag; a dedicated `integrationTest` task runs only integration-tagged tests. CI's integration job now calls :spring-boot-starter-redis:integrationTest and :spring-boot-starter-minio:integrationTest. - Updated the MinIO test image to a valid tag (RELEASE.2025-07-23T15-54-02Z). Flaky unit test fix: - HealthDetails.build() returned Map.copyOf(), which has no iteration-order guarantee, so HealthDetailsTest.should_preserveInsertionOrder was order-dependent. Now returns an unmodifiable LinkedHashMap copy, preserving the insertion order the class (and test) intends. Javadoc: - Documented the public API across all library modules: interface methods and params (StorageService), configuration-property accessors, exception and auto-configuration constructors, health indicators, and annotation elements. Fixes all ~300 missing-doc warnings. - Example apps are demonstration code and not published, so their Javadoc task is disabled. - RedisUtil/RedisLockUtil (internal helpers over the deprecated byte[] RedisConnection API) are excluded from generated Javadoc; runtime code is untouched. Remaining warnings are third-party deprecation/unchecked/removal notes the Javadoc tool surfaces from its compile pass and cannot be told to suppress. Javadoc now builds cleanly (0 doc warnings, down from 338).
The @container is static (shared across all tests in the class) and every test used the same LOCK_KEY. The @AfterEach cleanup called releaseLock(LOCK_KEY, "cleanup-id"), but releaseLock only deletes the key when the stored value matches the requestId — its by-design safety semantics. Since "cleanup-id" never matches the actual holder (e.g. "request-1"), the cleanup was a no-op and the lock leaked between tests with its TTL still active. As a result should_acquireLock_when_keyIsAvailable failed whenever it ran after a test that left the lock held (e.g. should_rejectSecondAcquire), because SET_IF_ABSENT could not acquire the still-present key. Cleanup now deletes the key unconditionally so each test starts from a clean state.
MinioAutoConfiguration created the properties with a plain @bean returning `new MinioConfigurationProperties()` and relied on the ConfigurationProperties binding post-processor (registered by ConfigurationPropertiesAutoConfiguration) to populate it. In the integration test's minimal context (@SpringBootTest(classes = MinioAutoConfiguration.class)) that auto-config is not active, so `spring.minio.*` never bound and getUrl() returned null. MinioClient.builder().endpoint(null) then threw IllegalArgumentException in HttpUtils.validateNotNull (HttpUtils.java:84), failing the application context and all four tests. Switch to the standard pattern: @EnableConfigurationProperties on the auto-configuration (which registers the binding infrastructure and a single bound bean), inject MinioConfigurationProperties into minioClient(), and drop the unbound @bean factory method. Also removed the erroneous self-referential @EnableConfigurationProperties from the properties class. Behaviour is unchanged for full Spring Boot applications and now also correct in the minimal test context.
The test built the object path as Path.of(TEST_BUCKET, TEST_OBJECT), but the StorageService.upload(Path, ...) overload treats the Path as the object key within the default bucket (it already targets clientProps.getBucket()). The key therefore became "integration-test-bucket/test/hello.txt" while the assertion looked for "test/hello.txt", failing should_uploadAndRetrieveObject_when_fileUploaded at line 92. Use Path.of(TEST_OBJECT) so the object key matches the expected value.
listObjectNames() lists via a non-recursive ListObjectsArgs (recursive defaults
to false), so MinIO groups a nested key like "test/hello.txt" under the common
prefix "test/" and returns that pseudo-directory instead of the full key. The
upload succeeded but the assertion contains("test/hello.txt") failed because the
listing returned "test/".
Use a flat key ("hello.txt") that appears directly in the non-recursive listing,
matching the implementation's documented behaviour.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This PR completes a full-scale overhaul of the spring-boot-starters mono-repo, adding nine new Spring Boot starters, ten runnable example sub-modules, Testcontainers integration tests, Maven Central publishing config, GitHub Actions CI/CD pipelines, and a migration of the entire Gradle build from Groovy DSL to Kotlin DSL.
What Changed
Build System
build.gradle.kts/settings.gradle.ktsbuild.gradle.ktsuses configure<> extension accessors, tasks.named, tasks.withType, and proper Kotlin DSL signing/publishing blocksPackage & Namespace
New Starters (9 added)
Existing Starters Enhanced
RedisHealthIndicator,RedisLockUtil, Testcontainers integration testsMinioHealthIndicator,MinioMetricsConfiguration, extended StorageService API, Testcontainers integration testsExample Applications (10 added)
Each example lives under examples/-example/ and sh
Examples: redis, minio, rate-limiting, idempotency, audit-log, feature-flags, llm-client, multitenancy, aws-s3, outbox
CI/CD & Publishing
Documentation
Tech Stack
Test Plan