Skip to content

define default beans using @Resource annotation#2559

Closed
rrayst wants to merge 7 commits into
masterfrom
define-default-bean-by-annotation
Closed

define default beans using @Resource annotation#2559
rrayst wants to merge 7 commits into
masterfrom
define-default-bean-by-annotation

Conversation

@rrayst
Copy link
Copy Markdown
Member

@rrayst rrayst commented Jan 7, 2026

Summary by CodeRabbit

  • New Features

    • Annotation processor now recognizes Jakarta Resource annotations and emits resource metadata for resource-backed components.
  • Refactor

    • Core components wired via registry-driven, on-demand resource provisioning with constructor-based DI and lifecycle initialization.
  • Chores

    • Bean registry API changed to a fallback-only registration method (breaking contract).
  • Tests

    • Tests updated to align with the new bean registry contract.

✏️ Tip: You can customize this high-level summary in your review settings.

@rrayst
Copy link
Copy Markdown
Member Author

rrayst commented Jan 7, 2026

/ok-to-test

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jan 7, 2026

📝 Walkthrough

Walkthrough

Collects jakarta @Resource-annotated elements at compile time and emits per-main resources.txt; introduces runtime "fallback" bean registration API and implementation that lazily defines resource beans via suppliers/constructors; annotates many core classes with @Resource and wires constructor-based resource instantiation into DefaultMainComponents.

Changes

Cohort / File(s) Summary
Bean Registry API & Impl
annot/src/main/java/com/predic8/membrane/annot/beanregistry/BeanRegistry.java, annot/src/main/java/com/predic8/membrane/annot/beanregistry/BeanRegistryImplementation.java, annot/src/main/java/com/predic8/membrane/annot/beanregistry/SpringContextAdapter.java
Replaced registerIfAbsent(Class<T>, Supplier<T>) (returns T) with registerFallbackIfAbsent(Class<T>, Supplier<T>) (void). Added fallback definers (FallbackBeanDefiner), fallback-driven lookups in getBean(s), and hasDefinitionFor(...). Adapter updated to new API.
Annotation processor & resource output
annot/src/main/java/com/predic8/membrane/annot/SpringConfigurationXSDGeneratingAnnotationProcessor.java, annot/src/main/java/com/predic8/membrane/annot/generator/ResourceInfo.java, annot/src/main/java/com/predic8/membrane/annot/model/Model.java
Processor now collects @Resource elements into Model.resources; new ResourceInfo writes per-main resources.txt; status messages include resource counts.
DefaultMainComponents — resource wiring & instantiation
core/src/main/java/com/predic8/membrane/core/router/DefaultMainComponents.java
Added defineResourceBeans() and helpers (loadResourceClasses, chooseConstructor, fillParameterList, createResource) to discover resource classes, register fallback suppliers and instantiate resources via prioritized constructors; registry lookups used for core component access and during init/registry assignment.
Core classes annotated @Resource
core/src/main/java/com/predic8/membrane/core/... (e.g., util/DNSCache.java, util/TimerManager.java, transport/http/HttpTransport.java, transport/http/HttpClientFactory.java, transport/http/client/HttpClientConfiguration.java, transport/http/streampump/Statistics.java, kubernetes/client/KubernetesClientFactory.java, exchangestore/LimitedMemoryExchangeStore.java, interceptor/FlowController.java)
Added jakarta.annotation.Resource annotations/imports to mark components as resources for DI/registry provisioning; no algorithmic changes.
Constructor-based DI additions
core/src/main/java/com/predic8/membrane/core/proxies/RuleManager.java, core/src/main/java/com/predic8/membrane/core/resolver/ResolverMap.java
Added @Resource and new public constructors annotated with @Priority(1) to support constructor injection; ResolverMap new ctor delegates and registers a RuleResolver.
Router lifecycle change
core/src/main/java/com/predic8/membrane/core/router/DefaultRouter.java
Marked init() with @PostConstruct.
Tests adjusted
core/src/test/java/com/predic8/membrane/core/kubernetes/GenericYamlParserTest.java
Updated test TestBeanRegistry to override registerFallbackIfAbsent(...) (void) instead of the old returning T method.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant AP as AnnotationProcessor
    participant Model as Model
    participant ResGen as ResourceInfo
    participant FS as resources.txt
    Note over AP,Model: Compile-time: collect `@Resource`
    AP->>Model: add Resource TypeElements
    AP->>ResGen: write(model)
    ResGen->>FS: create/write per-main resources.txt
Loading
sequenceDiagram
    autonumber
    participant DMC as DefaultMainComponents
    participant Registry as BeanRegistry
    participant Fallback as FallbackBeanDefiner
    participant Supplier as Supplier (creates instance)
    participant Registry2 as BeanRegistry (for constructor params)
    participant Component as ResourceComponent

    Note over DMC,Registry: Runtime: register fallbacks
    DMC->>Registry: registerFallbackIfAbsent(type, supplier)
    Registry->>Fallback: store definer

    Note over Component,Registry: On-demand resolution
    Component->>Registry: getBean(type)
    Registry->>Fallback: find compatible definer
    Fallback->>Supplier: invoke supplier (may request params)
    Supplier->>Registry2: resolve constructor params via getBean(...)
    Supplier-->>Fallback: constructed instance
    Fallback->>Registry: register(instance)
    Registry-->>Component: return instance
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

Suggested reviewers

  • rrayst

Poem

🐰 I nibble lines of resources.txt at dawn,

Fallbacks curl till some constructor's drawn,
Registry hums — a spring of beans reborn,
Hops of code stitch dusk to morning lawn,
A tiny rabbit cheers where resources spawn.

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 7.55% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'define default beans using @resource annotation' directly and clearly describes the primary change across the entire changeset: adding @resource annotations to multiple classes and implementing bean registration infrastructure to support resource-based bean definition.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

📜 Recent review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b58a59e and b56bed6.

📒 Files selected for processing (1)
  • annot/src/main/java/com/predic8/membrane/annot/beanregistry/SpringContextAdapter.java
🚧 Files skipped from review as they are similar to previous changes (1)
  • annot/src/main/java/com/predic8/membrane/annot/beanregistry/SpringContextAdapter.java
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Automated tests
  • GitHub Check: Analyze (java)

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
annot/src/main/java/com/predic8/membrane/annot/SpringConfigurationXSDGeneratingAnnotationProcessor.java (1)

52-53: Verify @SupportedAnnotationTypes covers jakarta.annotation.Resource.

The processor uses @SupportedAnnotationTypes(value = {"com.predic8.membrane.annot.*"}) which only covers the com.predic8.membrane.annot package. However, the new code processes jakarta.annotation.Resource which is outside this package. The processor may not be triggered for classes only annotated with @Resource.

Proposed fix
-@SupportedAnnotationTypes(value = {"com.predic8.membrane.annot.*"})
+@SupportedAnnotationTypes(value = {"com.predic8.membrane.annot.*", "jakarta.annotation.Resource"})
annot/src/main/java/com/predic8/membrane/annot/beanregistry/BeanRegistryImplementation.java (1)

179-186: Unsynchronized iteration over synchronizedList may throw ConcurrentModificationException.

The fallbacks list is a synchronizedList, but iterating with a for-each loop without external synchronization is not thread-safe per the Collections.synchronizedList contract.

Suggested fix
     public List<Object> getBeans() {
-        for (FallbackBeanDefiner fallbackBeanDefiner : fallbacks) {
-            fallbackBeanDefiner.defineIfNecessary();
+        synchronized (fallbacks) {
+            for (FallbackBeanDefiner fallbackBeanDefiner : fallbacks) {
+                fallbackBeanDefiner.defineIfNecessary();
+            }
         }
         return bcs.values().stream().filter(bd -> !bd.getDefinition().isComponent())
🤖 Fix all issues with AI agents
In
@annot/src/main/java/com/predic8/membrane/annot/beanregistry/BeanRegistryImplementation.java:
- Around line 76-93: The race exists because
FallbackBeanDefiner.defineIfNecessary() only synchronizes on the individual
definer, allowing two definers for the same type to both observe
registry.hasDefinitionFor(clazz) == false and create duplicate beans; fix by
serializing definitions across definers for the same target by moving the
critical section to a shared lock or atomic map operation: e.g., synchronize on
the shared registry object (use synchronized(registry) { if
(!registry.hasDefinitionFor(clazz)) { register... } }) inside either
defineIfNecessary() or define(), or replace the check/register sequence with a
thread-safe computeIfAbsent-style operation on a central ConcurrentHashMap keyed
by clazz that calls the supplier to produce and then calls
registry.register(...) only once; update uses of
FallbackBeanDefiner.defineIfNecessary(), FallbackBeanDefiner.define(),
registry.hasDefinitionFor(...) and registry.register(...) accordingly.
- Around line 206-214: The code in BeanRegistryImplementation returns
List.of(fallbackBeanDefiner.defineIfNecessary()) which throws NPE if
defineIfNecessary() returns null and also iterates over the fallbacks list
without synchronization; fix by calling e.g. Object bean =
fallbackBeanDefiner.defineIfNecessary(); if (bean == null) continue; then return
(List<T>) of(bean); and ensure safe iteration over fallbacks by synchronizing on
the fallbacks collection or iterating over a defensive copy (e.g. for
(FallbackBeanDefiner f : new ArrayList<>(fallbacks)) ...) so concurrent
modifications are avoided.
- Around line 223-230: In BeanRegistryImplementation (the method shown), avoid
Optional.of(null) and unsafe unsynchronized iteration: when calling
fallbackBeanDefiner.defineIfNecessary() capture its result in a local variable,
synchronize iteration over the fallbacks collection (e.g.,
synchronized(fallbacks) { ... } ) to avoid concurrent-modification issues, and
return Optional.ofNullable((T) result) instead of Optional.of((T) result) so a
null result yields Optional.empty().

In
@core/src/main/java/com/predic8/membrane/core/router/DefaultMainComponents.java:
- Around line 60-89: The plain boolean beansDefined is not thread-safe; replace
it with a thread-safe flag (e.g., a private final AtomicBoolean beansDefined =
new AtomicBoolean(false)) and atomically set/check it in defineResourceBeans by
using compareAndSet(false, true) so only one thread proceeds to register
resources; update the defineResourceBeans method to return immediately when
compareAndSet fails and add the required import for
java.util.concurrent.atomic.AtomicBoolean.
- Around line 120-126: The chooseConstructor method incorrectly filters out all
constructors when none have @Priority and then indexes into an empty array, and
it returns the highest numeric priority instead of the highest-priority (lowest
numeric) one; fix by after filtering constructors with Streams.of(...).filter(c
-> c.isAnnotationPresent(Priority.class)) check if the filtered array is empty
and if so continue using the original constructors array, then sort the
(possibly original or filtered) constructors by Comparator.comparingInt(c ->
c.getAnnotation(Priority.class).value()) and return the first element
(constructors[0]) so the lowest numeric @Priority value wins; keep the
early-return for the single-constructor case in chooseConstructor.
🧹 Nitpick comments (6)
core/src/main/java/com/predic8/membrane/core/transport/http/HttpClientFactory.java (1)

16-16: Unused import.

The MCElement import is added but the annotation is not used on the class. Consider removing this import unless it's intended for future use.

🧹 Proposed fix
-import com.predic8.membrane.annot.MCElement;
 import com.predic8.membrane.core.transport.http.client.HttpClientConfiguration;
annot/src/main/java/com/predic8/membrane/annot/generator/ResourceInfo.java (1)

16-19: Remove unused imports.

ProcessingException, Doc, and Doc.Entry are imported but not used in this class.

Proposed fix
-import com.predic8.membrane.annot.ProcessingException;
-import com.predic8.membrane.annot.model.*;
-import com.predic8.membrane.annot.model.doc.Doc;
-import com.predic8.membrane.annot.model.doc.Doc.Entry;
+import com.predic8.membrane.annot.model.MainInfo;
+import com.predic8.membrane.annot.model.Model;
annot/src/main/java/com/predic8/membrane/annot/beanregistry/BeanRegistryImplementation.java (1)

259-261: Consider preventing duplicate fallback registrations for the same type.

Multiple fallbacks for the same type can be registered, which could lead to unexpected behavior. Consider checking if a fallback for the type already exists.

Suggested improvement
     public <T> void registerFallbackIfAbsent(Class<T> type, Supplier<T> supplier) {
+        synchronized (fallbacks) {
+            boolean alreadyRegistered = fallbacks.stream()
+                .anyMatch(f -> f.clazz().equals(type));
+            if (alreadyRegistered)
+                return;
+        }
         fallbacks.add(new FallbackBeanDefiner(this, type, supplier));
     }
core/src/main/java/com/predic8/membrane/core/router/DefaultMainComponents.java (3)

91-97: Provide a descriptive error message for missing resource file.

If resources.txt is missing, requireNonNull throws an NPE with no context. Consider adding an error message.

Suggested improvement
     private List<String> loadResourceClasses() {
-        try (InputStream is = requireNonNull(getClass().getResourceAsStream("/com/predic8/membrane/core/config/spring/resources.txt"))) {
+        String resourcePath = "/com/predic8/membrane/core/config/spring/resources.txt";
+        try (InputStream is = requireNonNull(getClass().getResourceAsStream(resourcePath),
+                "Resource file not found: " + resourcePath)) {
             return new BufferedReader(new InputStreamReader(is, UTF_8)).lines().toList();

99-110: Consolidate exception handling and preserve root cause.

The three catch blocks are identical. Use multi-catch and unwrap InvocationTargetException to expose the actual cause.

Suggested fix
     private Object createResource(Class<?> aClass) {
         try {
             Constructor<?> constructor = chooseConstructor(aClass.getConstructors());
             return constructor.newInstance(fillParameterList(constructor));
-        } catch (InstantiationException e) {
-            throw new RuntimeException(e);
-        } catch (IllegalAccessException e) {
-            throw new RuntimeException(e);
-        } catch (InvocationTargetException e) {
-            throw new RuntimeException(e);
+        } catch (InstantiationException | IllegalAccessException e) {
+            throw new RuntimeException("Failed to create resource: " + aClass.getName(), e);
+        } catch (InvocationTargetException e) {
+            throw new RuntimeException("Failed to create resource: " + aClass.getName(), e.getCause());
         }
     }

112-118: Provide context in exception when dependency resolution fails.

orElseThrow() throws NoSuchElementException with no message. When a parameter can't be resolved, include the type for easier debugging.

Suggested fix
     private Object @NotNull [] fillParameterList(Constructor<?> constructor) {
         Object[] parameters = new Object[constructor.getParameterCount()];
         for (int i = 0; i < parameters.length; i++) {
-            parameters[i] = registry.getBean(constructor.getParameterTypes()[i]).orElseThrow();
+            Class<?> paramType = constructor.getParameterTypes()[i];
+            parameters[i] = registry.getBean(paramType)
+                .orElseThrow(() -> new NoSuchElementException(
+                    "No bean found for constructor parameter: " + paramType.getName() +
+                    " in " + constructor.getDeclaringClass().getName()));
         }
         return parameters;
     }
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0abb692 and cfa2637.

📒 Files selected for processing (20)
  • annot/src/main/java/com/predic8/membrane/annot/SpringConfigurationXSDGeneratingAnnotationProcessor.java
  • annot/src/main/java/com/predic8/membrane/annot/beanregistry/BeanRegistry.java
  • annot/src/main/java/com/predic8/membrane/annot/beanregistry/BeanRegistryImplementation.java
  • annot/src/main/java/com/predic8/membrane/annot/beanregistry/SpringContextAdapter.java
  • annot/src/main/java/com/predic8/membrane/annot/generator/ResourceInfo.java
  • annot/src/main/java/com/predic8/membrane/annot/model/Model.java
  • core/src/main/java/com/predic8/membrane/core/exchangestore/LimitedMemoryExchangeStore.java
  • core/src/main/java/com/predic8/membrane/core/interceptor/FlowController.java
  • core/src/main/java/com/predic8/membrane/core/kubernetes/client/KubernetesClientFactory.java
  • core/src/main/java/com/predic8/membrane/core/proxies/RuleManager.java
  • core/src/main/java/com/predic8/membrane/core/resolver/ResolverMap.java
  • core/src/main/java/com/predic8/membrane/core/router/DefaultMainComponents.java
  • core/src/main/java/com/predic8/membrane/core/router/DefaultRouter.java
  • core/src/main/java/com/predic8/membrane/core/transport/http/HttpClientFactory.java
  • core/src/main/java/com/predic8/membrane/core/transport/http/HttpTransport.java
  • core/src/main/java/com/predic8/membrane/core/transport/http/client/HttpClientConfiguration.java
  • core/src/main/java/com/predic8/membrane/core/transport/http/streampump/Statistics.java
  • core/src/main/java/com/predic8/membrane/core/util/DNSCache.java
  • core/src/main/java/com/predic8/membrane/core/util/TimerManager.java
  • core/src/test/java/com/predic8/membrane/core/kubernetes/GenericYamlParserTest.java
🧰 Additional context used
🧠 Learnings (4)
📚 Learning: 2026-01-07T12:43:52.805Z
Learnt from: rrayst
Repo: membrane/api-gateway PR: 2529
File: annot/src/main/java/com/predic8/membrane/annot/beanregistry/SpringContextAdapter.java:50-53
Timestamp: 2026-01-07T12:43:52.805Z
Learning: SpringContextAdapter in annot/src/main/java/com/predic8/membrane/annot/beanregistry/SpringContextAdapter.java is only used during tests, not in production code, so stub implementations throwing UnsupportedOperationException are acceptable.

Applied to files:

  • core/src/main/java/com/predic8/membrane/core/transport/http/client/HttpClientConfiguration.java
  • annot/src/main/java/com/predic8/membrane/annot/SpringConfigurationXSDGeneratingAnnotationProcessor.java
  • core/src/test/java/com/predic8/membrane/core/kubernetes/GenericYamlParserTest.java
  • core/src/main/java/com/predic8/membrane/core/router/DefaultRouter.java
  • annot/src/main/java/com/predic8/membrane/annot/beanregistry/SpringContextAdapter.java
  • annot/src/main/java/com/predic8/membrane/annot/beanregistry/BeanRegistry.java
  • core/src/main/java/com/predic8/membrane/core/exchangestore/LimitedMemoryExchangeStore.java
  • annot/src/main/java/com/predic8/membrane/annot/beanregistry/BeanRegistryImplementation.java
📚 Learning: 2026-01-07T12:40:41.122Z
Learnt from: rrayst
Repo: membrane/api-gateway PR: 2556
File: annot/src/main/java/com/predic8/membrane/annot/beanregistry/BeanRegistryImplementation.java:79-82
Timestamp: 2026-01-07T12:40:41.122Z
Learning: In BeanRegistryImplementation.start(), component beans (beans where isComponent() returns true) are intentionally instantiated during startup. The filtering of component beans in methods like getBeans() serves a different purpose—those methods return user-facing beans, not internal components. Component beans can and should be created without issue.

Applied to files:

  • core/src/main/java/com/predic8/membrane/core/router/DefaultMainComponents.java
📚 Learning: 2026-01-03T19:24:51.595Z
Learnt from: predic8
Repo: membrane/api-gateway PR: 2529
File: annot/src/main/java/com/predic8/membrane/annot/beanregistry/BeanRegistryImplementation.java:168-172
Timestamp: 2026-01-03T19:24:51.595Z
Learning: In BeanRegistryImplementation, the `getOrCreate(BeanRegistry, Grammar)` method on BeanContainer is guaranteed to always return a non-null object or throw an exception, so null filtering after calling getOrCreate is not necessary.

Applied to files:

  • annot/src/main/java/com/predic8/membrane/annot/beanregistry/BeanRegistry.java
📚 Learning: 2026-01-03T19:24:48.014Z
Learnt from: predic8
Repo: membrane/api-gateway PR: 2529
File: annot/src/main/java/com/predic8/membrane/annot/beanregistry/BeanRegistryImplementation.java:168-172
Timestamp: 2026-01-03T19:24:48.014Z
Learning: In BeanRegistryImplementation.java, the getOrCreate(BeanRegistry, Grammar) method on BeanContainer is guaranteed to return a non-null object or throw an exception. Do not perform or rely on null-filtering after calling getOrCreate; remove any post-call null checks and rely on the contract. If you need extra safety, consider an assertion or explicit exception path for unexpected nulls, but prefer not to branch on null after getOrCreate.

Applied to files:

  • annot/src/main/java/com/predic8/membrane/annot/beanregistry/BeanRegistryImplementation.java
🧬 Code graph analysis (10)
core/src/main/java/com/predic8/membrane/core/transport/http/HttpClientFactory.java (5)
core/src/main/java/com/predic8/membrane/core/config/security/acme/DnsOperatorAcmeValidation.java (1)
  • MCElement (18-33)
core/src/main/java/com/predic8/membrane/core/config/security/acme/MemoryStorage.java (1)
  • MCElement (22-37)
core/src/main/java/com/predic8/membrane/core/config/security/acme/KubernetesStorage.java (1)
  • MCElement (22-91)
core/src/main/java/com/predic8/membrane/core/transport/http/streampump/Statistics.java (1)
  • Resource (18-25)
core/src/main/java/com/predic8/membrane/core/util/TimerManager.java (1)
  • Resource (26-47)
core/src/main/java/com/predic8/membrane/core/transport/http/client/HttpClientConfiguration.java (6)
core/src/main/java/com/predic8/membrane/core/exchangestore/LimitedMemoryExchangeStore.java (1)
  • Resource (38-374)
core/src/main/java/com/predic8/membrane/core/kubernetes/client/KubernetesClientFactory.java (1)
  • Resource (32-60)
core/src/main/java/com/predic8/membrane/core/resolver/ResolverMap.java (1)
  • Resource (50-327)
core/src/main/java/com/predic8/membrane/core/transport/http/HttpClientFactory.java (1)
  • Resource (34-78)
core/src/main/java/com/predic8/membrane/core/transport/http/streampump/Statistics.java (1)
  • Resource (18-25)
core/src/main/java/com/predic8/membrane/core/util/DNSCache.java (1)
  • Resource (29-81)
core/src/main/java/com/predic8/membrane/core/transport/http/streampump/Statistics.java (10)
core/src/main/java/com/predic8/membrane/core/exchangestore/LimitedMemoryExchangeStore.java (1)
  • Resource (38-374)
core/src/main/java/com/predic8/membrane/core/interceptor/FlowController.java (1)
  • Resource (56-162)
core/src/main/java/com/predic8/membrane/core/kubernetes/client/KubernetesClientFactory.java (1)
  • Resource (32-60)
core/src/main/java/com/predic8/membrane/core/proxies/RuleManager.java (1)
  • Resource (36-341)
core/src/main/java/com/predic8/membrane/core/resolver/ResolverMap.java (1)
  • Resource (50-327)
core/src/main/java/com/predic8/membrane/core/transport/http/HttpClientFactory.java (1)
  • Resource (34-78)
core/src/main/java/com/predic8/membrane/core/transport/http/HttpTransport.java (1)
  • Resource (49-305)
core/src/main/java/com/predic8/membrane/core/transport/http/client/HttpClientConfiguration.java (1)
  • Resource (49-228)
core/src/main/java/com/predic8/membrane/core/util/DNSCache.java (1)
  • Resource (29-81)
core/src/main/java/com/predic8/membrane/core/util/TimerManager.java (1)
  • Resource (26-47)
core/src/main/java/com/predic8/membrane/core/kubernetes/client/KubernetesClientFactory.java (10)
core/src/main/java/com/predic8/membrane/core/exchangestore/LimitedMemoryExchangeStore.java (1)
  • Resource (38-374)
core/src/main/java/com/predic8/membrane/core/interceptor/FlowController.java (1)
  • Resource (56-162)
core/src/main/java/com/predic8/membrane/core/proxies/RuleManager.java (1)
  • Resource (36-341)
core/src/main/java/com/predic8/membrane/core/resolver/ResolverMap.java (1)
  • Resource (50-327)
core/src/main/java/com/predic8/membrane/core/transport/http/HttpClientFactory.java (1)
  • Resource (34-78)
core/src/main/java/com/predic8/membrane/core/transport/http/HttpTransport.java (1)
  • Resource (49-305)
core/src/main/java/com/predic8/membrane/core/transport/http/client/HttpClientConfiguration.java (1)
  • Resource (49-228)
core/src/main/java/com/predic8/membrane/core/transport/http/streampump/Statistics.java (1)
  • Resource (18-25)
core/src/main/java/com/predic8/membrane/core/util/DNSCache.java (1)
  • Resource (29-81)
core/src/main/java/com/predic8/membrane/core/util/TimerManager.java (1)
  • Resource (26-47)
core/src/main/java/com/predic8/membrane/core/interceptor/FlowController.java (8)
core/src/main/java/com/predic8/membrane/core/exchangestore/LimitedMemoryExchangeStore.java (1)
  • Resource (38-374)
core/src/main/java/com/predic8/membrane/core/kubernetes/client/KubernetesClientFactory.java (1)
  • Resource (32-60)
core/src/main/java/com/predic8/membrane/core/proxies/RuleManager.java (1)
  • Resource (36-341)
core/src/main/java/com/predic8/membrane/core/resolver/ResolverMap.java (1)
  • Resource (50-327)
core/src/main/java/com/predic8/membrane/core/transport/http/HttpTransport.java (1)
  • Resource (49-305)
core/src/main/java/com/predic8/membrane/core/transport/http/client/HttpClientConfiguration.java (1)
  • Resource (49-228)
core/src/main/java/com/predic8/membrane/core/transport/http/streampump/Statistics.java (1)
  • Resource (18-25)
core/src/main/java/com/predic8/membrane/core/util/TimerManager.java (1)
  • Resource (26-47)
annot/src/main/java/com/predic8/membrane/annot/generator/ResourceInfo.java (1)
annot/src/main/java/com/predic8/membrane/annot/model/doc/Doc.java (2)
  • Doc (34-146)
  • Entry (41-89)
core/src/main/java/com/predic8/membrane/core/util/DNSCache.java (2)
core/src/main/java/com/predic8/membrane/core/transport/http/streampump/Statistics.java (1)
  • Resource (18-25)
core/src/main/java/com/predic8/membrane/core/util/TimerManager.java (1)
  • Resource (26-47)
core/src/main/java/com/predic8/membrane/core/transport/http/HttpTransport.java (7)
core/src/main/java/com/predic8/membrane/core/exchangestore/LimitedMemoryExchangeStore.java (1)
  • Resource (38-374)
core/src/main/java/com/predic8/membrane/core/interceptor/FlowController.java (1)
  • Resource (56-162)
core/src/main/java/com/predic8/membrane/core/kubernetes/client/KubernetesClientFactory.java (1)
  • Resource (32-60)
core/src/main/java/com/predic8/membrane/core/resolver/ResolverMap.java (1)
  • Resource (50-327)
core/src/main/java/com/predic8/membrane/core/transport/http/client/HttpClientConfiguration.java (1)
  • Resource (49-228)
core/src/main/java/com/predic8/membrane/core/util/DNSCache.java (1)
  • Resource (29-81)
core/src/main/java/com/predic8/membrane/core/util/TimerManager.java (1)
  • Resource (26-47)
core/src/main/java/com/predic8/membrane/core/util/TimerManager.java (6)
core/src/main/java/com/predic8/membrane/core/exchangestore/LimitedMemoryExchangeStore.java (1)
  • Resource (38-374)
core/src/main/java/com/predic8/membrane/core/proxies/RuleManager.java (1)
  • Resource (36-341)
core/src/main/java/com/predic8/membrane/core/resolver/ResolverMap.java (1)
  • Resource (50-327)
core/src/main/java/com/predic8/membrane/core/transport/http/HttpTransport.java (1)
  • Resource (49-305)
core/src/main/java/com/predic8/membrane/core/transport/http/streampump/Statistics.java (1)
  • Resource (18-25)
core/src/main/java/com/predic8/membrane/core/util/DNSCache.java (1)
  • Resource (29-81)
core/src/main/java/com/predic8/membrane/core/resolver/ResolverMap.java (5)
core/src/main/java/com/predic8/membrane/core/kubernetes/client/KubernetesClientFactory.java (1)
  • Resource (32-60)
core/src/main/java/com/predic8/membrane/core/proxies/RuleManager.java (1)
  • Resource (36-341)
core/src/main/java/com/predic8/membrane/core/transport/http/HttpClientFactory.java (1)
  • Resource (34-78)
core/src/main/java/com/predic8/membrane/core/util/DNSCache.java (1)
  • Resource (29-81)
core/src/main/java/com/predic8/membrane/core/util/TimerManager.java (1)
  • Resource (26-47)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Automated tests
  • GitHub Check: Analyze (java)
🔇 Additional comments (25)
core/src/main/java/com/predic8/membrane/core/transport/http/streampump/Statistics.java (1)

16-19: LGTM! Clean resource annotation.

The @Resource annotation properly marks this class as a container-managed bean. The class has no external dependencies and uses simple initialization, making it straightforward for the DI container to manage.

core/src/main/java/com/predic8/membrane/core/exchangestore/LimitedMemoryExchangeStore.java (1)

23-23: LGTM! Proper service type specification.

The @Resource(type = ExchangeStore.class) annotation correctly specifies the service interface, allowing the container to register this implementation as an ExchangeStore bean. This follows the same pattern used in other components like HttpTransport.

Also applies to: 38-40

core/src/main/java/com/predic8/membrane/core/util/TimerManager.java (1)

17-17: LGTM! Straightforward resource annotation.

The @Resource annotation is properly applied. The class uses a default no-arg constructor and has no external dependencies, making it ideal for container-managed instantiation.

Also applies to: 26-27

annot/src/main/java/com/predic8/membrane/annot/model/Model.java (1)

21-21: LGTM! Consistent with existing pattern.

The new resources field and getResources() method follow the same design pattern as the existing mains field. Both expose mutable collections directly, which appears intentional for the annotation processor to populate these collections during compilation.

Also applies to: 30-30, 40-42

core/src/main/java/com/predic8/membrane/core/transport/http/HttpClientFactory.java (1)

34-42: Verify constructor injection support with nullable parameters.

The @Resource annotation (from Jakarta EE) does not support constructor parameter injection. Unlike @Autowired or @Inject, @Resource is designed for field and setter injection only. HttpClientFactory's parameterized constructor with @Nullable TimerManager will not have the dependency automatically injected by Spring—the parameter will remain null unless custom bean factory logic is implemented.

This pattern differs from other @Resource-annotated classes like Statistics and TimerManager, which use zero-argument constructors and rely on field/setter injection. Consider:

  • Adding an @Autowired constructor if constructor injection is intended
  • Adding a zero-argument constructor and using field/setter injection instead
  • Documenting how the DI container is configured to handle this non-standard pattern
core/src/main/java/com/predic8/membrane/core/transport/http/client/HttpClientConfiguration.java (1)

19-19: LGTM! Resource annotation added for DI support.

The @Resource annotation enables Jakarta-based dependency injection for HttpClientConfiguration, aligning with the broader PR objective to support resource-based wiring across core components.

Also applies to: 49-49

core/src/main/java/com/predic8/membrane/core/util/DNSCache.java (1)

17-18: LGTM! Resource annotation enables singleton DI behavior.

The @Resource annotation on DNSCache ensures this stateful cache utility is managed as a singleton by the DI container, consistent with similar utility classes like Statistics and TimerManager.

Also applies to: 29-29

annot/src/main/java/com/predic8/membrane/annot/beanregistry/SpringContextAdapter.java (1)

61-61: LGTM! API renamed to align with BeanRegistry interface.

The method rename from registerIfAbsent to registerFallbackIfAbsent and the void return type align with the updated BeanRegistry API. Based on learnings, since SpringContextAdapter is test-only code, the stub implementation remains acceptable.

core/src/main/java/com/predic8/membrane/core/router/DefaultRouter.java (1)

38-38: LGTM! Automatic initialization via @PostConstruct.

The @PostConstruct annotation enables automatic initialization after bean construction, aligning with the resource-based lifecycle management introduced in this PR. The existing guard at lines 128-130 prevents double initialization, ensuring compatibility with existing code that may call init() or start() manually.

Also applies to: 123-123

core/src/main/java/com/predic8/membrane/core/interceptor/FlowController.java (1)

20-20: LGTM! Resource annotation enables DI for constructor dependencies.

The @Resource annotation on FlowController enables dependency injection for its Router constructor parameter, consistent with similar resource-annotated classes like KubernetesClientFactory that have constructor dependencies.

Also applies to: 56-56

core/src/main/java/com/predic8/membrane/core/kubernetes/client/KubernetesClientFactory.java (1)

17-17: LGTM!

The @Resource annotation aligns with the broader DI pattern introduced across the codebase (e.g., HttpClientFactory, DNSCache, TimerManager). The annotation enables the bean registry to discover and manage this factory class.

Also applies to: 32-33

annot/src/main/java/com/predic8/membrane/annot/SpringConfigurationXSDGeneratingAnnotationProcessor.java (2)

293-296: LGTM!

The resource collection follows the same pattern as MCElement processing above. The cast to TypeElement is appropriate since @Resource is applied at the class level.


534-534: LGTM!

The ResourceInfo invocation is appropriately placed in the processing flow alongside other generators.

core/src/main/java/com/predic8/membrane/core/transport/http/HttpTransport.java (1)

24-24: LGTM!

The @Resource(type = Transport.class) annotation correctly specifies the parent interface type, enabling the bean registry to wire HttpTransport as the Transport implementation. This pattern is consistent with LimitedMemoryExchangeStore using @Resource(type = ExchangeStore.class).

Also applies to: 49-51

core/src/main/java/com/predic8/membrane/core/proxies/RuleManager.java (1)

47-54: LGTM with a note on initialization order.

The dual-constructor pattern with @Priority(1) is consistent with other DI-enabled components like ResolverMap. The no-arg constructor relies on setRouter() being called before methods that access the router field (e.g., addProxyAndOpenPortIfNew). This is acceptable given the existing design where setRouter is already used.

core/src/test/java/com/predic8/membrane/core/kubernetes/GenericYamlParserTest.java (1)

378-379: LGTM!

The method signature update from registerIfAbsent to registerFallbackIfAbsent with a void return type correctly aligns with the updated BeanRegistry interface contract. The empty stub implementation is appropriate for test purposes. Based on learnings, stub implementations in test adapters are acceptable.

annot/src/main/java/com/predic8/membrane/annot/generator/ResourceInfo.java (1)

41-60: LGTM!

The write() method correctly generates resources.txt files containing qualified names of @Resource-annotated classes. The FilerException handling for duplicate files follows the established pattern in this annotation processing codebase.

core/src/main/java/com/predic8/membrane/core/resolver/ResolverMap.java (2)

26-27: LGTM!

The @Resource annotation and import additions are consistent with the DI pattern introduced across the codebase.

Also applies to: 50-52


163-167: LGTM!

The new constructor follows the established pattern from RuleManager, using @Priority(1) to signal constructor preference when all dependencies are available. The delegation to the existing two-argument constructor maintains proper initialization of schema resolvers before adding the rule resolver.

annot/src/main/java/com/predic8/membrane/annot/beanregistry/BeanRegistry.java (1)

59-67: LGTM! Clear API design for lazy fallback registration.

The rename to registerFallbackIfAbsent with void return type accurately reflects the deferred evaluation semantics described in the JavaDoc.

annot/src/main/java/com/predic8/membrane/annot/beanregistry/BeanRegistryImplementation.java (2)

254-257: LGTM!

Good addition to propagate the registry to beans implementing BeanRegistryAware.


193-195: LGTM!

Clean helper method for checking existing bean definitions.

core/src/main/java/com/predic8/membrane/core/router/DefaultMainComponents.java (3)

136-141: LGTM!

Clean integration of defineResourceBeans() into the initialization flow.


150-227: LGTM!

Consistent refactoring of getters to use registry-based bean resolution aligns with the PR's objective of registry-driven wiring.


229-232: LGTM!

Eagerly defining resource beans when the registry is set ensures proper initialization order.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Fix all issues with AI agents
In
@annot/src/main/java/com/predic8/membrane/annot/beanregistry/BeanRegistryImplementation.java:
- Around line 179-181: The loop in BeanRegistryImplementation iterates unsafely
over the synchronizedList field fallbacks, which can cause
ConcurrentModificationException; fix by synchronizing on the fallbacks object
while iterating (e.g., wrap the for-loop body in synchronized(fallbacks) { ...
}) or iterate over a snapshot copy (e.g., new ArrayList<>(fallbacks)) before
calling FallbackBeanDefiner.defineIfNecessary() to ensure thread-safe iteration.

In
@core/src/main/java/com/predic8/membrane/core/router/DefaultMainComponents.java:
- Around line 120-128: The chooseConstructor method currently sorts
Constructor<?>[] by Priority.value() ascending but then returns the last
element, which picks the lowest-priority constructor; change it to return the
highest-priority one per Jakarta @Priority (lower numeric = higher priority) by
returning the first element after sorting (or invert the comparator to sort
descending and keep the last); update the logic in chooseConstructor to
consistently select the constructor annotated with @Priority having the smallest
numeric value.
- Line 60: The plain boolean field beansDefined in DefaultMainComponents is not
thread-safe and can allow concurrent defineResourceBeans() calls from init() and
setRegistry() to both run; change the field to a thread-safe guard (e.g.,
replace boolean beansDefined with an AtomicBoolean or make it volatile and use
synchronized/compare-and-set semantics) and update defineResourceBeans() to
perform an atomic check-and-set (e.g., AtomicBoolean.compareAndSet(false, true)
or synchronized check then set) so only one thread performs the registrations.
🧹 Nitpick comments (2)
annot/src/main/java/com/predic8/membrane/annot/SpringConfigurationXSDGeneratingAnnotationProcessor.java (1)

295-297: Consider filtering to TYPE elements only for defensive safety.

jakarta.annotation.Resource can legally target TYPE, FIELD, or METHOD. Casting directly to TypeElement will throw ClassCastException if someone applies @Resource to a field or method.

While current usages in the codebase are all class-level (as seen in Statistics, TimerManager, ResolverMap, etc.), adding a filter would be more robust:

♻️ Proposed fix
 for (Element element : getCachedElementsAnnotatedWith(roundEnv, Resource.class)) {
-    m.getResources().add((TypeElement) element);
+    if (element.getKind().isClass() || element.getKind().isInterface()) {
+        m.getResources().add((TypeElement) element);
+    }
 }

Alternatively, if only type-level usage is intended, consider creating a custom annotation (e.g., @MCResource) with @Target(ElementType.TYPE) to enforce this constraint at compile time.

core/src/main/java/com/predic8/membrane/core/router/DefaultMainComponents.java (1)

67-89: Consider validating uniqueness of fallback registrations per type.

The method registers one FallbackBeanDefiner per resource class. If defineResourceBeans() is inadvertently called multiple times despite the beansDefined guard (e.g., due to the thread-safety issue on line 60), duplicate fallback definers for the same type could be registered in the fallbacks list.

While the existing design expects only one fallback per type (per learnings), adding a defensive check in registerFallbackIfAbsent to detect or prevent duplicate registrations would help catch misuse early during development.

Based on learnings, only one FallbackBeanDefiner per type is expected by design.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cfa2637 and 3234720.

📒 Files selected for processing (4)
  • annot/src/main/java/com/predic8/membrane/annot/SpringConfigurationXSDGeneratingAnnotationProcessor.java
  • annot/src/main/java/com/predic8/membrane/annot/beanregistry/BeanRegistryImplementation.java
  • annot/src/main/java/com/predic8/membrane/annot/generator/ResourceInfo.java
  • core/src/main/java/com/predic8/membrane/core/router/DefaultMainComponents.java
🚧 Files skipped from review as they are similar to previous changes (1)
  • annot/src/main/java/com/predic8/membrane/annot/generator/ResourceInfo.java
🧰 Additional context used
🧠 Learnings (4)
📚 Learning: 2026-01-07T12:40:41.122Z
Learnt from: rrayst
Repo: membrane/api-gateway PR: 2556
File: annot/src/main/java/com/predic8/membrane/annot/beanregistry/BeanRegistryImplementation.java:79-82
Timestamp: 2026-01-07T12:40:41.122Z
Learning: In BeanRegistryImplementation.start(), component beans (beans where isComponent() returns true) are intentionally instantiated during startup. The filtering of component beans in methods like getBeans() serves a different purpose—those methods return user-facing beans, not internal components. Component beans can and should be created without issue.

Applied to files:

  • core/src/main/java/com/predic8/membrane/core/router/DefaultMainComponents.java
📚 Learning: 2026-01-07T15:43:16.568Z
Learnt from: rrayst
Repo: membrane/api-gateway PR: 2559
File: annot/src/main/java/com/predic8/membrane/annot/beanregistry/BeanRegistryImplementation.java:76-93
Timestamp: 2026-01-07T15:43:16.568Z
Learning: In BeanRegistryImplementation (annot/src/main/java/com/predic8/membrane/annot/beanregistry/BeanRegistryImplementation.java), only one FallbackBeanDefiner should be registered per type, so concurrent registration of multiple fallbacks for the same type is not expected to occur.

Applied to files:

  • core/src/main/java/com/predic8/membrane/core/router/DefaultMainComponents.java
  • annot/src/main/java/com/predic8/membrane/annot/beanregistry/BeanRegistryImplementation.java
📚 Learning: 2026-01-07T12:43:52.805Z
Learnt from: rrayst
Repo: membrane/api-gateway PR: 2529
File: annot/src/main/java/com/predic8/membrane/annot/beanregistry/SpringContextAdapter.java:50-53
Timestamp: 2026-01-07T12:43:52.805Z
Learning: SpringContextAdapter in annot/src/main/java/com/predic8/membrane/annot/beanregistry/SpringContextAdapter.java is only used during tests, not in production code, so stub implementations throwing UnsupportedOperationException are acceptable.

Applied to files:

  • annot/src/main/java/com/predic8/membrane/annot/beanregistry/BeanRegistryImplementation.java
  • annot/src/main/java/com/predic8/membrane/annot/SpringConfigurationXSDGeneratingAnnotationProcessor.java
📚 Learning: 2026-01-03T19:24:48.014Z
Learnt from: predic8
Repo: membrane/api-gateway PR: 2529
File: annot/src/main/java/com/predic8/membrane/annot/beanregistry/BeanRegistryImplementation.java:168-172
Timestamp: 2026-01-03T19:24:48.014Z
Learning: In BeanRegistryImplementation.java, the getOrCreate(BeanRegistry, Grammar) method on BeanContainer is guaranteed to return a non-null object or throw an exception. Do not perform or rely on null-filtering after calling getOrCreate; remove any post-call null checks and rely on the contract. If you need extra safety, consider an assertion or explicit exception path for unexpected nulls, but prefer not to branch on null after getOrCreate.

Applied to files:

  • annot/src/main/java/com/predic8/membrane/annot/beanregistry/BeanRegistryImplementation.java
🧬 Code graph analysis (2)
core/src/main/java/com/predic8/membrane/core/router/DefaultMainComponents.java (4)
core/src/main/java/com/predic8/membrane/core/exchangestore/LimitedMemoryExchangeStore.java (1)
  • Resource (38-374)
core/src/main/java/com/predic8/membrane/core/resolver/ResolverMap.java (1)
  • Resource (50-327)
core/src/main/java/com/predic8/membrane/core/transport/http/streampump/Statistics.java (1)
  • Resource (18-25)
core/src/main/java/com/predic8/membrane/core/util/TimerManager.java (1)
  • Resource (26-47)
annot/src/main/java/com/predic8/membrane/annot/SpringConfigurationXSDGeneratingAnnotationProcessor.java (8)
core/src/main/java/com/predic8/membrane/core/kubernetes/client/KubernetesClientFactory.java (1)
  • Resource (32-60)
core/src/main/java/com/predic8/membrane/core/interceptor/FlowController.java (1)
  • Resource (56-162)
core/src/main/java/com/predic8/membrane/core/exchangestore/LimitedMemoryExchangeStore.java (1)
  • Resource (38-374)
core/src/main/java/com/predic8/membrane/core/resolver/ResolverMap.java (1)
  • Resource (50-327)
core/src/main/java/com/predic8/membrane/core/transport/http/streampump/Statistics.java (1)
  • Resource (18-25)
core/src/main/java/com/predic8/membrane/core/transport/http/client/HttpClientConfiguration.java (1)
  • Resource (49-228)
core/src/main/java/com/predic8/membrane/core/util/TimerManager.java (1)
  • Resource (26-47)
core/src/main/java/com/predic8/membrane/core/transport/http/HttpTransport.java (1)
  • Resource (49-305)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Automated tests
  • GitHub Check: Analyze (java)
🔇 Additional comments (5)
annot/src/main/java/com/predic8/membrane/annot/SpringConfigurationXSDGeneratingAnnotationProcessor.java (4)

19-19: LGTM!

The import for jakarta.annotation.Resource is correctly added to support the new resource collection functionality.


176-180: LGTM!

Good refactor using String.formatted() for cleaner string interpolation.


189-192: LGTM!

Including the @Resource element count in the status log provides useful visibility during annotation processing.


536-536: LGTM!

The ResourceInfo.write(m) call is correctly placed in the generation sequence after validation and alongside other generators.

annot/src/main/java/com/predic8/membrane/annot/beanregistry/BeanRegistryImplementation.java (1)

82-86: Previous NPE concern resolved.

The change on line 85 to return registry.getBean(clazz).orElseThrow() (instead of null) resolves the NPE issues flagged in previous reviews for lines 212 and 229, where List.of(null) and Optional.of(null) would have thrown exceptions.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In
@core/src/main/java/com/predic8/membrane/core/router/DefaultMainComponents.java:
- Around line 76-91: defineResourceBeans() assumes every class returned by
loadResourceClasses() has a @Resource annotation and calls resource.type()
without a null check; add a defensive null check after obtaining Resource
resource = aClass.getAnnotation(Resource.class) and skip (or log and continue)
classes where resource is null instead of dereferencing it, so
registry.registerFallbackIfAbsent(...) is only invoked when resource != null;
use the existing createResource(aClass) path for valid resources and include a
clear log message referencing the class name when skipping a missing annotation.
🧹 Nitpick comments (1)
core/src/main/java/com/predic8/membrane/core/router/DefaultMainComponents.java (1)

114-120: Consider improving parameter resolution error messages.

When getBean(...).orElseThrow() fails (line 117), the default NoSuchElementException message doesn't indicate which parameter or constructor failed. This can make debugging constructor resolution issues difficult.

📝 Enhanced error message
 private Object @NotNull [] fillParameterList(Constructor<?> constructor) {
     Object[] parameters = new Object[constructor.getParameterCount()];
     for (int i = 0; i < parameters.length; i++) {
-        parameters[i] = registry.getBean(constructor.getParameterTypes()[i]).orElseThrow();
+        Class<?> paramType = constructor.getParameterTypes()[i];
+        parameters[i] = registry.getBean(paramType)
+            .orElseThrow(() -> new RuntimeException(
+                "Cannot resolve parameter " + i + " of type " + paramType.getName() +
+                " for constructor of " + constructor.getDeclaringClass().getName()));
     }
     return parameters;
 }
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3234720 and b58a59e.

📒 Files selected for processing (2)
  • annot/src/main/java/com/predic8/membrane/annot/beanregistry/BeanRegistryImplementation.java
  • core/src/main/java/com/predic8/membrane/core/router/DefaultMainComponents.java
🧰 Additional context used
🧠 Learnings (5)
📓 Common learnings
Learnt from: rrayst
Repo: membrane/api-gateway PR: 2529
File: annot/src/main/java/com/predic8/membrane/annot/beanregistry/SpringContextAdapter.java:50-53
Timestamp: 2026-01-07T12:43:52.805Z
Learning: SpringContextAdapter in annot/src/main/java/com/predic8/membrane/annot/beanregistry/SpringContextAdapter.java is only used during tests, not in production code, so stub implementations throwing UnsupportedOperationException are acceptable.
📚 Learning: 2026-01-07T15:43:16.568Z
Learnt from: rrayst
Repo: membrane/api-gateway PR: 2559
File: annot/src/main/java/com/predic8/membrane/annot/beanregistry/BeanRegistryImplementation.java:76-93
Timestamp: 2026-01-07T15:43:16.568Z
Learning: In BeanRegistryImplementation (annot/src/main/java/com/predic8/membrane/annot/beanregistry/BeanRegistryImplementation.java), only one FallbackBeanDefiner should be registered per type, so concurrent registration of multiple fallbacks for the same type is not expected to occur.

Applied to files:

  • annot/src/main/java/com/predic8/membrane/annot/beanregistry/BeanRegistryImplementation.java
  • core/src/main/java/com/predic8/membrane/core/router/DefaultMainComponents.java
📚 Learning: 2026-01-07T12:43:52.805Z
Learnt from: rrayst
Repo: membrane/api-gateway PR: 2529
File: annot/src/main/java/com/predic8/membrane/annot/beanregistry/SpringContextAdapter.java:50-53
Timestamp: 2026-01-07T12:43:52.805Z
Learning: SpringContextAdapter in annot/src/main/java/com/predic8/membrane/annot/beanregistry/SpringContextAdapter.java is only used during tests, not in production code, so stub implementations throwing UnsupportedOperationException are acceptable.

Applied to files:

  • annot/src/main/java/com/predic8/membrane/annot/beanregistry/BeanRegistryImplementation.java
📚 Learning: 2026-01-03T19:24:48.014Z
Learnt from: predic8
Repo: membrane/api-gateway PR: 2529
File: annot/src/main/java/com/predic8/membrane/annot/beanregistry/BeanRegistryImplementation.java:168-172
Timestamp: 2026-01-03T19:24:48.014Z
Learning: In BeanRegistryImplementation.java, the getOrCreate(BeanRegistry, Grammar) method on BeanContainer is guaranteed to return a non-null object or throw an exception. Do not perform or rely on null-filtering after calling getOrCreate; remove any post-call null checks and rely on the contract. If you need extra safety, consider an assertion or explicit exception path for unexpected nulls, but prefer not to branch on null after getOrCreate.

Applied to files:

  • annot/src/main/java/com/predic8/membrane/annot/beanregistry/BeanRegistryImplementation.java
📚 Learning: 2026-01-07T12:40:41.122Z
Learnt from: rrayst
Repo: membrane/api-gateway PR: 2556
File: annot/src/main/java/com/predic8/membrane/annot/beanregistry/BeanRegistryImplementation.java:79-82
Timestamp: 2026-01-07T12:40:41.122Z
Learning: In BeanRegistryImplementation.start(), component beans (beans where isComponent() returns true) are intentionally instantiated during startup. The filtering of component beans in methods like getBeans() serves a different purpose—those methods return user-facing beans, not internal components. Component beans can and should be created without issue.

Applied to files:

  • annot/src/main/java/com/predic8/membrane/annot/beanregistry/BeanRegistryImplementation.java
  • core/src/main/java/com/predic8/membrane/core/router/DefaultMainComponents.java
🧬 Code graph analysis (1)
core/src/main/java/com/predic8/membrane/core/router/DefaultMainComponents.java (9)
core/src/main/java/com/predic8/membrane/core/kubernetes/client/KubernetesClientFactory.java (1)
  • Resource (32-60)
core/src/main/java/com/predic8/membrane/core/interceptor/FlowController.java (1)
  • Resource (56-162)
core/src/main/java/com/predic8/membrane/core/proxies/RuleManager.java (1)
  • Resource (36-341)
core/src/main/java/com/predic8/membrane/core/exchangestore/LimitedMemoryExchangeStore.java (1)
  • Resource (38-374)
core/src/main/java/com/predic8/membrane/core/resolver/ResolverMap.java (1)
  • Resource (50-327)
core/src/main/java/com/predic8/membrane/core/transport/http/HttpTransport.java (1)
  • Resource (49-305)
core/src/main/java/com/predic8/membrane/core/transport/http/streampump/Statistics.java (1)
  • Resource (18-25)
core/src/main/java/com/predic8/membrane/core/util/TimerManager.java (1)
  • Resource (26-47)
core/src/main/java/com/predic8/membrane/core/util/DNSCache.java (1)
  • Resource (29-81)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Analyze (java)
🔇 Additional comments (3)
annot/src/main/java/com/predic8/membrane/annot/beanregistry/BeanRegistryImplementation.java (1)

76-93: LGTM! Previous issues resolved.

The fallback mechanism implementation is sound:

  • defineIfNecessary() now correctly returns an existing bean from the registry (line 85) instead of null, resolving the NPE issues flagged in previous reviews
  • All iterations over fallbacks are properly synchronized (lines 179-183, 211-216, 229-234)
  • The synchronized methods on FallbackBeanDefiner provide adequate thread safety given that only one definer per type is registered by design

The addition of BeanRegistryAware propagation (lines 261-262) is a useful enhancement for beans that need registry access.

Also applies to: 179-183, 208-217, 227-235, 261-267

core/src/main/java/com/predic8/membrane/core/router/DefaultMainComponents.java (2)

122-130: Constructor selection logic is now correct.

The fix properly implements Jakarta @Priority semantics: after sorting ascending by priority value (line 128), returning constructors[0] (line 129) correctly selects the highest-priority constructor (lowest numeric value).


61-62: Thread safety is adequate with synchronized method.

The synchronized modifier on defineResourceBeans() (line 76) combined with @GuardedBy("this") correctly ensures that concurrent calls from init() and setRegistry() are serialized, with only the first invocation proceeding past the flag check.

@membrane-ci-server
Copy link
Copy Markdown

This pull request needs "/ok-to-test" from an authorized committer.

// uid -> bean container
private final Map<String, BeanContainer> bcs = new ConcurrentHashMap<>(); // Order is not critical. Order is determined by uidsToActivate

private final List<FallbackBeanDefiner> fallbacks = Collections.synchronizedList(new ArrayList<>());
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is s fallback?

@rrayst rrayst closed this Jan 9, 2026
@rrayst rrayst deleted the define-default-bean-by-annotation branch January 9, 2026 09:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants