Skip to content

Commit e699879

Browse files
committed
Close out NATS pending list: gate cleanup, capabilities API, metadata, ZSET guards
Five small follow-ups against the recently re-audited pending list: 1. RqueueMessageMetadataServiceImpl drops its redundant @conditional(RedisBackendCondition) — the per-class gate is redundant once RqueueRedisListenerConfig itself is conditional and component-scans the whole com.github.sonus21.rqueue.redis subtree. Last sibling to still carry it; matches the cleanup already applied to the other five web service impls. 2. RqueueQDetailServiceImpl.readFromZset / readFromZsetWithScore gain a backend-capability guard. Both used to reach into a Redis-shaped RqueueMessageTemplate even when the active broker is NATS — which has no ZSET-shaped scheduled / completion queue and would NPE through the template's null Redis path. The new requireScheduledIntrospection check inspects MessageBroker.capabilities().supportsScheduledIntrospection and surfaces a structured BackendCapabilityException (HTTP 501 via the existing advice) instead. readFromList stays unguarded because the LIST path is already routed through the broker SPI peek when a non-Redis broker is configured. 3. New GET /rqueue/api/v1/capabilities endpoint on both the imperative and reactive REST controllers. Returns the active broker's Capabilities record so the dashboard front-end can hide unsupported panels at boot rather than waiting for each call to 501. ObjectProvider<MessageBroker> keeps the dep optional — deployments that strip the bean fall back to Capabilities.REDIS_DEFAULTS, the historical behavior. 4. spring-boot-starter wires spring-boot-configuration-processor as an annotationProcessor so the build emits spring-configuration-metadata.json with all 36 rqueue.nats.* keys (verified after compile). Pure compile-time addition, no new runtime deps. (5 — confirming NatsBackendEndToEndIT on CI — is push-driven and covered by this push.) All 4 module unit suites still green: rqueue-core / rqueue-redis (57) / rqueue-web (39) / rqueue-nats (29). Assisted-By: Claude Code
1 parent d4e3265 commit e699879

5 files changed

Lines changed: 79 additions & 5 deletions

File tree

rqueue-redis/src/main/java/com/github/sonus21/rqueue/redis/web/RqueueMessageMetadataServiceImpl.java

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
package com.github.sonus21.rqueue.redis.web;
1818

1919
import com.github.sonus21.rqueue.common.RqueueLockManager;
20-
import com.github.sonus21.rqueue.config.RedisBackendCondition;
2120
import com.github.sonus21.rqueue.config.RqueueConfig;
2221
import com.github.sonus21.rqueue.core.RqueueMessage;
2322
import com.github.sonus21.rqueue.core.support.RqueueMessageUtils;
@@ -37,13 +36,17 @@
3736
import java.util.stream.Collectors;
3837
import lombok.extern.slf4j.Slf4j;
3938
import org.springframework.beans.factory.annotation.Autowired;
40-
import org.springframework.context.annotation.Conditional;
4139
import org.springframework.data.redis.core.DefaultTypedTuple;
4240
import org.springframework.data.redis.core.ZSetOperations.TypedTuple;
4341
import org.springframework.stereotype.Service;
4442
import reactor.core.publisher.Mono;
4543

46-
@Conditional(RedisBackendCondition.class)
44+
/**
45+
* Redis-shaped impl. Backend gating is handled at module level via
46+
* {@code RqueueRedisListenerConfig.@Conditional(RedisBackendCondition)} + its
47+
* {@code @ComponentScan} of {@code com.github.sonus21.rqueue.redis} — a per-class
48+
* {@code @Conditional} would be redundant.
49+
*/
4750
@Service
4851
@Slf4j
4952
public class RqueueMessageMetadataServiceImpl implements RqueueMessageMetadataService {

rqueue-spring-boot-starter/build.gradle

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,11 @@ dependencies {
5555
compileOnly project(":rqueue-nats")
5656
compileOnly "io.nats:jnats:${natsVersion}"
5757

58+
// Generates spring-configuration-metadata.json from @ConfigurationProperties classes
59+
// (RqueueNatsProperties primarily) so IDE autocomplete and the Spring Boot metadata viewer
60+
// surface every rqueue.nats.* key. Processor only — no runtime dep added.
61+
annotationProcessor "org.springframework.boot:spring-boot-configuration-processor:${springBootVersion}"
62+
5863
testImplementation project(":rqueue-spring-common-test")
5964
testImplementation project(":rqueue-nats")
6065
testImplementation "org.springframework.boot:spring-boot-starter-test:${springBootVersion}"

rqueue-web/src/main/java/com/github/sonus21/rqueue/web/controller/ReactiveRqueueRestController.java

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,9 @@ public class ReactiveRqueueRestController extends BaseReactiveController {
6565
private final RqueueUtilityService rqueueUtilityService;
6666
private final RqueueSystemManagerService rqueueQManagerService;
6767
private final RqueueJobService rqueueJobService;
68+
private final org.springframework.beans.factory.ObjectProvider<
69+
com.github.sonus21.rqueue.core.spi.MessageBroker>
70+
messageBrokerProvider;
6871

6972
@Autowired
7073
public ReactiveRqueueRestController(
@@ -73,13 +76,17 @@ public ReactiveRqueueRestController(
7376
RqueueUtilityService rqueueUtilityService,
7477
RqueueSystemManagerService rqueueQManagerService,
7578
RqueueWebConfig rqueueWebConfig,
76-
RqueueJobService rqueueJobService) {
79+
RqueueJobService rqueueJobService,
80+
org.springframework.beans.factory.ObjectProvider<
81+
com.github.sonus21.rqueue.core.spi.MessageBroker>
82+
messageBrokerProvider) {
7783
super(rqueueWebConfig);
7884
this.rqueueDashboardChartService = rqueueDashboardChartService;
7985
this.rqueueQDetailService = rqueueQDetailService;
8086
this.rqueueUtilityService = rqueueUtilityService;
8187
this.rqueueQManagerService = rqueueQManagerService;
8288
this.rqueueJobService = rqueueJobService;
89+
this.messageBrokerProvider = messageBrokerProvider;
8390
}
8491

8592
@PostMapping("chart")
@@ -215,4 +222,19 @@ public Mono<DataSelectorResponse> aggregateDataCounter(
215222
}
216223
return null;
217224
}
225+
226+
/** Reactive twin of {@code RqueueRestController#capabilities}. See javadoc there. */
227+
@GetMapping("capabilities")
228+
@ResponseBody
229+
public Mono<com.github.sonus21.rqueue.core.spi.Capabilities> capabilities(
230+
ServerHttpResponse response) {
231+
if (!isEnabled(response)) {
232+
return Mono.empty();
233+
}
234+
com.github.sonus21.rqueue.core.spi.MessageBroker broker = messageBrokerProvider.getIfAvailable();
235+
return Mono.just(
236+
broker == null
237+
? com.github.sonus21.rqueue.core.spi.Capabilities.REDIS_DEFAULTS
238+
: broker.capabilities());
239+
}
218240
}

rqueue-web/src/main/java/com/github/sonus21/rqueue/web/controller/RqueueRestController.java

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
package com.github.sonus21.rqueue.web.controller;
1818

1919
import com.github.sonus21.rqueue.config.RqueueWebConfig;
20+
import com.github.sonus21.rqueue.core.spi.Capabilities;
21+
import com.github.sonus21.rqueue.core.spi.MessageBroker;
2022
import com.github.sonus21.rqueue.exception.ProcessingException;
2123
import com.github.sonus21.rqueue.models.enums.AggregationType;
2224
import com.github.sonus21.rqueue.models.request.ChartDataRequest;
@@ -44,6 +46,7 @@
4446
import jakarta.servlet.http.HttpServletResponse;
4547
import jakarta.validation.Valid;
4648
import jakarta.validation.constraints.NotEmpty;
49+
import org.springframework.beans.factory.ObjectProvider;
4750
import org.springframework.beans.factory.annotation.Autowired;
4851
import org.springframework.context.annotation.Conditional;
4952
import org.springframework.web.bind.annotation.GetMapping;
@@ -64,6 +67,7 @@ public class RqueueRestController extends BaseController {
6467
private final RqueueUtilityService rqueueUtilityService;
6568
private final RqueueSystemManagerService rqueueQManagerService;
6669
private final RqueueJobService rqueueJobService;
70+
private final ObjectProvider<MessageBroker> messageBrokerProvider;
6771

6872
@Autowired
6973
public RqueueRestController(
@@ -72,13 +76,15 @@ public RqueueRestController(
7276
RqueueUtilityService rqueueUtilityService,
7377
RqueueSystemManagerService rqueueQManagerService,
7478
RqueueWebConfig rqueueWebConfig,
75-
RqueueJobService rqueueJobService) {
79+
RqueueJobService rqueueJobService,
80+
ObjectProvider<MessageBroker> messageBrokerProvider) {
7681
super(rqueueWebConfig);
7782
this.rqueueDashboardChartService = rqueueDashboardChartService;
7883
this.rqueueQDetailService = rqueueQDetailService;
7984
this.rqueueUtilityService = rqueueUtilityService;
8085
this.rqueueQManagerService = rqueueQManagerService;
8186
this.rqueueJobService = rqueueJobService;
87+
this.messageBrokerProvider = messageBrokerProvider;
8288
}
8389

8490
@PostMapping("chart")
@@ -212,4 +218,25 @@ public DataSelectorResponse aggregateDataCounter(
212218
}
213219
return null;
214220
}
221+
222+
/**
223+
* Reports the active broker's capability flags (delayed enqueue, scheduled introspection,
224+
* cron jobs, primary-handler dispatch). The dashboard front-end reads this once at boot to
225+
* hide panels the backend cannot service rather than relying on per-call 501 responses.
226+
*
227+
* <p>Sourced from {@link MessageBroker#capabilities()} — the Redis broker answers with
228+
* {@link Capabilities#REDIS_DEFAULTS} (everything true) and the NATS broker answers
229+
* with everything false in v1. {@link ObjectProvider} keeps the broker dependency optional
230+
* so deployments that strip the bean still get a sane payload (defaults to Redis, the
231+
* historical behavior).
232+
*/
233+
@GetMapping("capabilities")
234+
@ResponseBody
235+
public Capabilities capabilities(HttpServletResponse response) {
236+
if (!isEnable(response)) {
237+
return null;
238+
}
239+
MessageBroker broker = messageBrokerProvider.getIfAvailable();
240+
return broker == null ? Capabilities.REDIS_DEFAULTS : broker.capabilities();
241+
}
215242
}

rqueue-web/src/main/java/com/github/sonus21/rqueue/web/service/impl/RqueueQDetailServiceImpl.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,7 @@ public List<NavTab> getNavTabs(QueueConfig queueConfig) {
223223

224224
private List<TypedTuple<RqueueMessage>> readFromZset(
225225
String name, int pageNumber, int itemPerPage) {
226+
requireScheduledIntrospection("readFromZset");
226227
long start = pageNumber * (long) itemPerPage;
227228
long end = start + itemPerPage - 1;
228229

@@ -242,11 +243,27 @@ private List<TypedTuple<RqueueMessage>> readFromList(
242243

243244
private List<TypedTuple<RqueueMessage>> readFromZetWithScore(
244245
String name, int pageNumber, int itemPerPage) {
246+
requireScheduledIntrospection("readFromZsetWithScore");
245247
long start = pageNumber * (long) itemPerPage;
246248
long end = start + itemPerPage - 1;
247249
return rqueueMessageTemplate.readFromZsetWithScore(name, start, end);
248250
}
249251

252+
/**
253+
* Guard for ZSET-shaped lookups that the redis backend services natively but no other backend
254+
* does. Backends that report {@code !supportsScheduledIntrospection()} surface a structured 501
255+
* via {@code BackendCapabilityException} instead of NPE-ing through a Redis-shaped template
256+
* with no Redis connection.
257+
*/
258+
private void requireScheduledIntrospection(String op) {
259+
if (messageBroker != null && !messageBroker.capabilities().supportsScheduledIntrospection()) {
260+
throw new com.github.sonus21.rqueue.exception.BackendCapabilityException(
261+
messageBroker.getClass().getSimpleName(),
262+
op,
263+
"broker does not expose scheduled / completion ZSET introspection");
264+
}
265+
}
266+
250267
private List<TableRow> buildRows(
251268
List<TypedTuple<RqueueMessage>> rqueueMessages, RowBuilder rowBuilder) {
252269
if (CollectionUtils.isEmpty(rqueueMessages)) {

0 commit comments

Comments
 (0)