define default beans using @Resource annotation#2559
Conversation
|
/ok-to-test |
📝 WalkthroughWalkthroughCollects jakarta Changes
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
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
Estimated code review effort🎯 4 (Complex) | ⏱️ ~50 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
📜 Recent review detailsConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
⏰ 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)
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. Comment |
There was a problem hiding this comment.
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@SupportedAnnotationTypescoversjakarta.annotation.Resource.The processor uses
@SupportedAnnotationTypes(value = {"com.predic8.membrane.annot.*"})which only covers thecom.predic8.membrane.annotpackage. However, the new code processesjakarta.annotation.Resourcewhich 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 oversynchronizedListmay throwConcurrentModificationException.The
fallbackslist is asynchronizedList, but iterating with a for-each loop without external synchronization is not thread-safe per theCollections.synchronizedListcontract.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
MCElementimport 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, andDoc.Entryare 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.txtis missing,requireNonNullthrows 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
InvocationTargetExceptionto 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()throwsNoSuchElementExceptionwith 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
📒 Files selected for processing (20)
annot/src/main/java/com/predic8/membrane/annot/SpringConfigurationXSDGeneratingAnnotationProcessor.javaannot/src/main/java/com/predic8/membrane/annot/beanregistry/BeanRegistry.javaannot/src/main/java/com/predic8/membrane/annot/beanregistry/BeanRegistryImplementation.javaannot/src/main/java/com/predic8/membrane/annot/beanregistry/SpringContextAdapter.javaannot/src/main/java/com/predic8/membrane/annot/generator/ResourceInfo.javaannot/src/main/java/com/predic8/membrane/annot/model/Model.javacore/src/main/java/com/predic8/membrane/core/exchangestore/LimitedMemoryExchangeStore.javacore/src/main/java/com/predic8/membrane/core/interceptor/FlowController.javacore/src/main/java/com/predic8/membrane/core/kubernetes/client/KubernetesClientFactory.javacore/src/main/java/com/predic8/membrane/core/proxies/RuleManager.javacore/src/main/java/com/predic8/membrane/core/resolver/ResolverMap.javacore/src/main/java/com/predic8/membrane/core/router/DefaultMainComponents.javacore/src/main/java/com/predic8/membrane/core/router/DefaultRouter.javacore/src/main/java/com/predic8/membrane/core/transport/http/HttpClientFactory.javacore/src/main/java/com/predic8/membrane/core/transport/http/HttpTransport.javacore/src/main/java/com/predic8/membrane/core/transport/http/client/HttpClientConfiguration.javacore/src/main/java/com/predic8/membrane/core/transport/http/streampump/Statistics.javacore/src/main/java/com/predic8/membrane/core/util/DNSCache.javacore/src/main/java/com/predic8/membrane/core/util/TimerManager.javacore/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.javaannot/src/main/java/com/predic8/membrane/annot/SpringConfigurationXSDGeneratingAnnotationProcessor.javacore/src/test/java/com/predic8/membrane/core/kubernetes/GenericYamlParserTest.javacore/src/main/java/com/predic8/membrane/core/router/DefaultRouter.javaannot/src/main/java/com/predic8/membrane/annot/beanregistry/SpringContextAdapter.javaannot/src/main/java/com/predic8/membrane/annot/beanregistry/BeanRegistry.javacore/src/main/java/com/predic8/membrane/core/exchangestore/LimitedMemoryExchangeStore.javaannot/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
@Resourceannotation 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 anExchangeStorebean. This follows the same pattern used in other components likeHttpTransport.Also applies to: 38-40
core/src/main/java/com/predic8/membrane/core/util/TimerManager.java (1)
17-17: LGTM! Straightforward resource annotation.The
@Resourceannotation 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
resourcesfield andgetResources()method follow the same design pattern as the existingmainsfield. 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
@Resourceannotation (from Jakarta EE) does not support constructor parameter injection. Unlike@Autowiredor@Inject,@Resourceis designed for field and setter injection only. HttpClientFactory's parameterized constructor with@Nullable TimerManagerwill not have the dependency automatically injected by Spring—the parameter will remainnullunless custom bean factory logic is implemented.This pattern differs from other
@Resource-annotated classes likeStatisticsandTimerManager, which use zero-argument constructors and rely on field/setter injection. Consider:
- Adding an
@Autowiredconstructor 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
@Resourceannotation enables Jakarta-based dependency injection forHttpClientConfiguration, 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
@Resourceannotation onDNSCacheensures 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
registerIfAbsenttoregisterFallbackIfAbsentand the void return type align with the updatedBeanRegistryAPI. 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
@PostConstructannotation 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 callinit()orstart()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
@Resourceannotation onFlowControllerenables dependency injection for itsRouterconstructor 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
@Resourceannotation 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
MCElementprocessing above. The cast toTypeElementis appropriate since@Resourceis applied at the class level.
534-534: LGTM!The
ResourceInfoinvocation 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 wireHttpTransportas theTransportimplementation. This pattern is consistent withLimitedMemoryExchangeStoreusing@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 likeResolverMap. The no-arg constructor relies onsetRouter()being called before methods that access the router field (e.g.,addProxyAndOpenPortIfNew). This is acceptable given the existing design wheresetRouteris already used.core/src/test/java/com/predic8/membrane/core/kubernetes/GenericYamlParserTest.java (1)
378-379: LGTM!The method signature update from
registerIfAbsenttoregisterFallbackIfAbsentwith avoidreturn type correctly aligns with the updatedBeanRegistryinterface 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 generatesresources.txtfiles containing qualified names of@Resource-annotated classes. TheFilerExceptionhandling 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
@Resourceannotation 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
registerFallbackIfAbsentwith 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.
There was a problem hiding this comment.
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.Resourcecan legally targetTYPE,FIELD, orMETHOD. Casting directly toTypeElementwill throwClassCastExceptionif someone applies@Resourceto 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
FallbackBeanDefinerper resource class. IfdefineResourceBeans()is inadvertently called multiple times despite thebeansDefinedguard (e.g., due to the thread-safety issue on line 60), duplicate fallback definers for the same type could be registered in thefallbackslist.While the existing design expects only one fallback per type (per learnings), adding a defensive check in
registerFallbackIfAbsentto detect or prevent duplicate registrations would help catch misuse early during development.Based on learnings, only one
FallbackBeanDefinerper type is expected by design.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
annot/src/main/java/com/predic8/membrane/annot/SpringConfigurationXSDGeneratingAnnotationProcessor.javaannot/src/main/java/com/predic8/membrane/annot/beanregistry/BeanRegistryImplementation.javaannot/src/main/java/com/predic8/membrane/annot/generator/ResourceInfo.javacore/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.javaannot/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.javaannot/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.Resourceis 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
@Resourceelement 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 ofnull) resolves the NPE issues flagged in previous reviews for lines 212 and 229, whereList.of(null)andOptional.of(null)would have thrown exceptions.
There was a problem hiding this comment.
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 defaultNoSuchElementExceptionmessage 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
📒 Files selected for processing (2)
annot/src/main/java/com/predic8/membrane/annot/beanregistry/BeanRegistryImplementation.javacore/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.javacore/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.javacore/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
fallbacksare properly synchronized (lines 179-183, 211-216, 229-234)- The synchronized methods on
FallbackBeanDefinerprovide adequate thread safety given that only one definer per type is registered by designThe addition of
BeanRegistryAwarepropagation (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
@Prioritysemantics: after sorting ascending by priority value (line 128), returningconstructors[0](line 129) correctly selects the highest-priority constructor (lowest numeric value).
61-62: Thread safety is adequate with synchronized method.The
synchronizedmodifier ondefineResourceBeans()(line 76) combined with@GuardedBy("this")correctly ensures that concurrent calls frominit()andsetRegistry()are serialized, with only the first invocation proceeding past the flag check.
|
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<>()); |
Summary by CodeRabbit
New Features
Refactor
Chores
Tests
✏️ Tip: You can customize this high-level summary in your review settings.