Skip to content

Conversation

@predic8
Copy link
Member

@predic8 predic8 commented Dec 25, 2025

Summary by CodeRabbit

  • New Features

    • Lazy bean registration for on-demand component creation
    • Automatic rule reinitializer to retry and recover inactive routes
    • XML-based router bootstrap for loading router configuration
  • Bug Fixes

    • Improved null-safety and startup robustness to avoid NPEs
    • Hardened error handling for malformed exchange/store responses
    • More diagnostic debug logging during startup and timer tasks
  • Documentation

    • Roadmap updated with JMX exporter and lifecycle notes
  • Breaking

    • Gatekeeper SSL interceptor has been removed

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

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 25, 2025

Important

Review skipped

Too many files!

122 files out of 272 files are above the max files limit of 150.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

📝 Walkthrough

Walkthrough

Introduces a registry-backed Router/IRouter abstraction, extensive API signature migrations from Router→IRouter, adds RouterBootstrap and RuleReinitializer, adds BeanRegistry.registerIfAbsent, replaces global singleton handling with per-container synchronization, removes GateKeeperClientInterceptor, and migrates many tests to per-test router lifecycle and router.add()/start() usage.

Changes

Cohort / File(s) Summary
Bean registry & container
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/BeanContainer.java
Added <T> T registerIfAbsent(Class<T>, Supplier<T>); replaced global singleton map with per-UID BeanContainer synchronization; BeanContainer singleton becomes AtomicReference; computeBeanName helper and safer activation/resolve flows.
Router core & new interfaces
core/src/main/java/com/predic8/membrane/core/Router.java, core/src/main/java/com/predic8/membrane/core/IRouter.java, core/src/main/java/com/predic8/membrane/core/RouterBootstrap.java, core/src/main/java/com/predic8/membrane/core/RuleReinitializer.java
Added IRouter interface; Router now implements IRouter and lazily uses registry for components; introduced RouterBootstrap.initByXML/initFromXMLString; added RuleReinitializer to retry inactive proxies.
Proxy lifecycle & init/start transition
core/src/main/java/com/predic8/membrane/core/proxies/Proxy.java, core/src/main/java/com/predic8/membrane/core/proxies/SSLProxy.java, core/src/main/java/com/predic8/membrane/core/HttpRouter.java, core/src/main/java/com/predic8/membrane/core/transport/Transport.java, core/src/main/java/com/predic8/membrane/core/transport/http/HttpTransport.java, core/src/main/java/com/predic8/membrane/core/sslinterceptor/SSLInterceptor.java, core/src/main/java/com/predic8/membrane/core/sslinterceptor/RouterIpResolverInterceptor.java
Removed checked exceptions from many init(Router) signatures (now no-throws) and adjusted implementations; Transport.getInterceptor now wraps reflection failures in RuntimeException; HttpRouter.createTransport made static.
Hot deploy API changes
core/src/main/java/com/predic8/membrane/core/router/hotdeploy/HotDeployer.java, .../DefaultHotDeployer.java, .../NullHotDeployer.java
HotDeployer.start now requires Router parameter; DefaultHotDeployer refactored to use a lock object and start/startInternal pattern; stop uses router.getReinitializer().stop().
Exchange store & transport adjustments
core/src/main/java/com/predic8/membrane/core/exchangestore/ElasticSearchExchangeStore.java, core/src/main/java/com/predic8/membrane/core/exchangestore/AbstractPersistentExchangeStore.java, core/src/main/java/com/predic8/membrane/core/exchangestore/MongoDBExchangeStore.java
init signatures updated to accept IRouter; ElasticSearch index error handling hardened with defensive JSON checks.
Many init() → init(IRouter) migrations
core/** (multiple interceptor, auth, session, balancer, cache, websocket, xslt, scripting, openapi files)
Widespread API migrations: methods and constructors that previously accepted concrete Router now accept IRouter; Interceptor.getRouter return types and many init() signatures updated to IRouter.
Removed component
core/src/main/java/com/predic8/membrane/core/sslinterceptor/GateKeeperClientInterceptor.java
Fully removed GateKeeperClientInterceptor (HTTP gatekeeper check + cache).
Test lifecycle & setup migration
core/src/test/java/... (many files, including AbstractTestWithRouter.java, SimpleTest.java, ReadRulesConfigurationTest.java, Proxy*, transport/*, openapi/*, etc.)
Converted many tests from class-level shared Router to per-test Router; changed Router.init() usage to RouterBootstrap.initByXML() in some spots; replaced getRuleManager().addProxyAndOpenPortIfNew(...); router.init() with router.add(...); router.start(); removed/ narrowed throws Exception on many test methods.
New/removed tests
core/src/test/java/com/predic8/membrane/core/proxies/TargetURLExpressionTest.java, removed AbstractServiceProxyExpressionTest.java
Added TargetURLExpressionTest verifying expression-evaluated target URL; removed AbstractServiceProxyExpressionTest.
Utilities, config & docs
core/src/main/java/com/predic8/membrane/core/Configuration.java, core/src/main/java/com/predic8/membrane/core/util/TimerManager.java, core/src/main/java/com/predic8/membrane/core/jmx/JmxRouter.java, docs/ROADMAP.md, test resource files
Added default jmxRouterName, added logging in TimerManager, changed JMX prefix formatting, updated ROADMAP (JMXExporter, breaking-change notes), and minor test resource edits.

Sequence Diagram(s)

sequenceDiagram
    participant CLI as CLI/Bootstrap
    participant Router as Router (IRouter)
    participant Registry as BeanRegistry
    participant RM as RuleManager
    participant Reinit as RuleReinitializer

    Note over CLI,Router: startup
    CLI->>Router: initByXML / initFromXMLString
    Router->>Registry: get/provision core beans (ExchangeStore, Transport, DNSCache, RuleManager)
    Registry-->>Router: beans (lazy-created via registerIfAbsent)
    Router->>RM: register proxies / add(proxy)
    Router->>RM: start() -> open ports
    Router->>Reinit: getReinitializer()
    Reinit->>Reinit: start() (schedule retry timer)
    Note over Registry: registerIfAbsent ensures at-most-once bean creation per type
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related PRs

Suggested labels

7.x

Suggested reviewers

  • rrayst

Poem

🐰 Hop, I built a tiny spring,

Beans wake up when callers bring,
Routers start and tests reset,
No old singletons to sweat,
I nibble code and then I sing!

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 4.38% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ 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 'Router refactor to beanfactory' clearly describes the main architectural change in the PR—refactoring router initialization to use a bean factory/registry approach.

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
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: 4

Caution

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

⚠️ Outside diff range comments (1)
core/src/main/java/com/predic8/membrane/core/Router.java (1)

85-101: Critical: Duplicate field declaration will cause compilation error.

The field openPorts is declared twice (lines 92 and 101) with identical initialization. This will fail to compile.

🔎 Proposed fix - remove duplicate declaration
     /**
      * Indicates whether the router should automatically open TCP ports when adding proxies.
      * This flag determines if the ports associated with the proxies are opened immediately
      * when they are added to the router. Setting this to {@code false} allows for proxies
      * to be defined without opening the associated ports, providing more control over when
      * the ports are made accessible.
      */
     private boolean openPorts = true;
-
-    /**
-     * Indicates whether the router should automatically open TCP ports when adding proxies.
-     * This flag determines if the ports associated with the proxies are opened immediately
-     * when they are added to the router. Setting this to {@code false} allows for proxies
-     * to be defined without opening the associated ports, providing more control over when
-     * the ports are made accessible.
-     */
-    private boolean openPorts = true;
🧹 Nitpick comments (1)
core/src/test/java/com/predic8/membrane/core/kubernetes/GenericYamlParserTest.java (1)

371-375: Test stub returns null, violating interface contract.

The registerIfAbsent stub unconditionally returns null, which violates the interface contract (should return either an existing or newly created bean). If any test exercises this method, it will receive null unexpectedly.

Consider implementing minimal behavior to match the contract:

🔎 Proposed fix
 @Override
 public <T> T registerIfAbsent(String name, Class<T> type, Supplier<T> supplier) {
-    return null;
+    Object existing = refs.get(name);
+    if (existing != null && type.isInstance(existing)) {
+        return type.cast(existing);
+    }
+    T created = supplier.get();
+    refs.put(name, created);
+    return created;
 }
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fff2a95 and 2df4c3b.

📒 Files selected for processing (5)
  • annot/src/main/java/com/predic8/membrane/annot/beanregistry/BeanRegistry.java
  • annot/src/main/java/com/predic8/membrane/annot/beanregistry/BeanRegistryImplementation.java
  • core/src/main/java/com/predic8/membrane/core/Router.java
  • core/src/main/java/com/predic8/membrane/core/cli/RouterCLI.java
  • core/src/test/java/com/predic8/membrane/core/kubernetes/GenericYamlParserTest.java
🧰 Additional context used
🧬 Code graph analysis (2)
core/src/main/java/com/predic8/membrane/core/cli/RouterCLI.java (1)
core/src/main/java/com/predic8/membrane/core/config/spring/CheckableBeanFactory.java (1)
  • InvalidConfigurationException (26-47)
core/src/main/java/com/predic8/membrane/core/Router.java (1)
core/src/main/java/com/predic8/membrane/core/kubernetes/KubernetesWatcher.java (1)
  • KubernetesWatcher (37-132)
⏰ 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 (6)
annot/src/main/java/com/predic8/membrane/annot/beanregistry/BeanRegistry.java (1)

48-60: Well-documented interface extension for lazy registration.

The new registerIfAbsent method provides a clean API for thread-safe lazy bean initialization. The Javadoc clearly specifies the contract, including return behavior for both existing and newly created beans.

core/src/main/java/com/predic8/membrane/core/cli/RouterCLI.java (1)

46-46: Import path update aligns with exception relocation.

The static import now references CheckableBeanFactory.InvalidConfigurationException, consistent with the exception's new location as shown in the relevant code snippet.

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

16-24: Import organization change looks good.

Wildcards consolidate related imports appropriately, and the addition of java.util.concurrent.* and java.util.function.* supports the new concurrency-aware registerIfAbsent implementation.

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

199-200: Registry-based KubernetesWatcher start looks good.

The migration from direct field access to registry-based resolution with ifPresent handles the optional nature of KubernetesWatcher cleanly.


439-441: Registry-based KubernetesWatcher stop is consistent with start.

Mirrors the start() pattern appropriately.


495-497: GlobalInterceptor registration via registry.

Clean migration to registry-based registration for the global interceptor.

@membrane-ci-server
Copy link

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

Copy link
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

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2df4c3b and 5369b43.

📒 Files selected for processing (4)
  • annot/src/main/java/com/predic8/membrane/annot/beanregistry/BeanRegistry.java
  • annot/src/main/java/com/predic8/membrane/annot/beanregistry/BeanRegistryImplementation.java
  • core/src/main/java/com/predic8/membrane/core/Router.java
  • core/src/test/java/com/predic8/membrane/core/kubernetes/GenericYamlParserTest.java
🚧 Files skipped from review as they are similar to previous changes (2)
  • core/src/test/java/com/predic8/membrane/core/kubernetes/GenericYamlParserTest.java
  • annot/src/main/java/com/predic8/membrane/annot/beanregistry/BeanRegistry.java
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-11-15T18:38:23.728Z
Learnt from: predic8
Repo: membrane/api-gateway PR: 2322
File: core/src/main/java/com/predic8/membrane/core/lang/CommonBuiltInFunctions.java:18-26
Timestamp: 2025-11-15T18:38:23.728Z
Learning: In the membrane/api-gateway repository, the maintainer predic8 prefers fail-fast behavior where NullPointerExceptions are allowed to crash rather than being caught or having defensive null checks that silently return null, as it makes debugging easier by showing where problems originate.

Applied to files:

  • core/src/main/java/com/predic8/membrane/core/Router.java
🧬 Code graph analysis (1)
core/src/main/java/com/predic8/membrane/core/Router.java (2)
core/src/main/java/com/predic8/membrane/core/kubernetes/KubernetesWatcher.java (1)
  • KubernetesWatcher (37-132)
core/src/main/java/com/predic8/membrane/core/util/DNSCache.java (1)
  • DNSCache (27-78)
⏰ 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 (6)
core/src/main/java/com/predic8/membrane/core/Router.java (6)

92-92: Well-documented feature addition.

The openPorts field with its comprehensive Javadoc provides clear control over automatic port opening behavior while maintaining backward compatibility with the default true value.


190-191: Clean registry-based dependency lookup.

The optional lookup pattern via getRegistry().getBean(KubernetesWatcher.class).ifPresent(...) is a good approach that avoids NPE if the watcher isn't registered while allowing for flexible configuration.


319-319: Registry-based singleton resolved the past concern.

The use of registerIfAbsent addresses the previous review comment about creating multiple DNSCache instances. The method now ensures a singleton is created and reused. However, note that registerIfAbsent itself has potential issues with null name registration (see comment on BeanRegistryImplementation.java line 193-203).


517-517: Registry-based singleton resolved the past concern.

The use of registerIfAbsent addresses the previous review comment about creating multiple FlowController instances. However, note that registerIfAbsent itself has potential issues with null name registration (see comment on BeanRegistryImplementation.java line 193-203).


610-613: Excellent documentation of timing concerns.

The updated Javadoc clearly documents the race condition risk and recommends calling setOpenPorts() before start(), which helps prevent misuse.


579-580: The lazy initialization with null observer and grammar is safe for Router's actual usage.

Router only calls getBean(), registerIfAbsent(), and register() on the registry—none of which access observer or grammar. The handle() method (which uses observer) is not part of the BeanRegistry interface and is never invoked from Router. Similarly, resolve() (which would use grammar) is never called. All actual usage paths operate safely with null values.

rrayst
rrayst previously approved these changes Dec 25, 2025
@rrayst
Copy link
Member

rrayst commented Dec 25, 2025

/ok-to-test

@predic8 predic8 marked this pull request as draft December 25, 2025 17:27
…`, improve logging and null checks, and streamline `activationRun` logic
…ad-safety in `BeanContainer`, and streamline `BeanRegistry` handling
…wn` and `setUp` methods, and clean up `BeanContainer` by removing unused `setIfAbsent` method
@predic8
Copy link
Member Author

predic8 commented Dec 26, 2025

@coderabbitai review

Copy link
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: 6

Caution

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

⚠️ Outside diff range comments (2)
core/src/main/java/com/predic8/membrane/core/router/hotdeploy/DefaultHotDeployer.java (1)

59-68: Inconsistent synchronization: stop() uses synchronized(this) but hdt is @GuardedBy("lock").

The hdt field is annotated with @GuardedBy("lock") (line 27), and startInternal() correctly synchronizes on lock. However, stop() synchronizes on this, breaking the synchronization contract. This can cause data races between concurrent calls to startInternal() and stop().

🔎 Proposed fix
     @Override
     public void stop() {
-        synchronized (this) {
+        synchronized (lock) {
             if (hdt == null)
                 return;

             router.getReinitializer().stop();
             hdt.stopASAP();
             hdt = null;
         }
     }
core/src/test/java/com/predic8/membrane/integration/withoutinternet/interceptor/oauth2/OAuth2AuthorizationServerInterceptorBase.java (1)

204-211: Add router cleanup in @AfterEach to prevent resource leaks.

Each test creates and starts a new router instance, but there's no visible cleanup (@AfterEach with router.stop()). Since routers typically bind to ports, start threads, and hold other resources, failing to stop them will cause resource leaks across test runs, potentially leading to port binding failures or thread exhaustion.

🔎 Proposed fix: Add cleanup method

Add an @AfterEach method to properly stop the router:

@AfterEach
public void tearDown() throws Exception {
    if (router != null) {
        router.stop();
    }
}
♻️ Duplicate comments (5)
core/src/main/java/com/predic8/membrane/core/router/hotdeploy/DefaultHotDeployer.java (1)

71-77: NPE fix applied; consider logging when router is null.

The null check for router addresses the previously raised NPE concern. However, calling setEnabled(true) before start(Router) will silently do nothing, which might be unexpected. Consider logging a warning to aid debugging.

🔎 Proposed enhancement
     @Override
     public void setEnabled(boolean enabled) {
-        if (enabled && router != null)
+        if (enabled && router != null) {
             startInternal();
-        else
+        } else if (enabled) {
+            log.warn("setEnabled(true) called before start(Router). Ignoring.");
+        } else {
             stop();
+        }
     }
core/src/main/java/com/predic8/membrane/core/Router.java (2)

348-348: Consider using registerIfAbsent for consistency.

While orElseThrow() ensures fail-fast behavior when DNSCache is missing, it requires init() to have been called first. For consistency with getFlowController() (line 491) and to handle edge cases where this getter might be called before init(), consider using registerIfAbsent:

🔎 Suggested pattern for consistency
 public DNSCache getDnsCache() {
-    return getRegistry().getBean(DNSCache.class).orElseThrow();
+    return getRegistry().registerIfAbsent(DNSCache.class, DNSCache::new);
 }

556-557: Verify null safety in BeanRegistryImplementation.

The lazy initialization creates BeanRegistryImplementation with null observer and grammar parameters. This was previously flagged as potentially causing NPEs during bean lifecycle operations. While fail-fast behavior is preferred (based on learnings), verify that BeanRegistryImplementation properly handles these null parameters or ensure they're never null in production scenarios.

Run the following to check null handling in BeanRegistryImplementation:

#!/bin/bash
# Check how BeanRegistryImplementation handles null observer and grammar
ast-grep --pattern $'class BeanRegistryImplementation {
  $$$
}' | head -200
core/src/test/java/com/predic8/membrane/core/config/ReadRulesConfigurationTest.java (1)

96-112: Consider using assertInstanceOf for cleaner assertions.

The current early-return pattern with fail() correctly addresses the redundant assertTrue(true) issue from the previous review. However, using assertInstanceOf() as originally suggested would be more idiomatic and produce clearer test failure messages if the proxy is not an SSLableProxy.

🔎 Cleaner approach using assertInstanceOf
 @Test
 void testLocalServiceProxyInboundSSL() {
-    if (proxies.get(2) instanceof SSLableProxy sp) {
-        assertFalse(sp.isInboundSSL());
-        return;
-    }
-    fail();
+    assertInstanceOf(SSLableProxy.class, proxies.get(2));
+    SSLableProxy sp = (SSLableProxy) proxies.get(2);
+    assertFalse(sp.isInboundSSL());
 }

 @Test
 void testLocalServiceProxyOutboundSSL() {
-    if (proxies.get(2) instanceof SSLableProxy sp) {
-        assertNull(sp.getSslOutboundContext());
-        return;
-    }
-    fail();
+    assertInstanceOf(SSLableProxy.class, proxies.get(2));
+    SSLableProxy sp = (SSLableProxy) proxies.get(2);
+    assertNull(sp.getSslOutboundContext());
 }
core/src/test/java/com/predic8/membrane/core/openapi/validators/security/JWTInterceptorAndSecurityValidatorTest.java (1)

71-72: Inconsistent request builder pattern - already flagged.

This test still uses the old new Request.Builder().get("/foo") pattern while the first test (line 63) was updated to use the static import pattern get("/foo"). This inconsistency was already noted in a previous review.

🔎 Proposed fix for consistency
-        Exchange exc = new Request.Builder().get("/foo").header("Authorization", "bearer " + getSignedJwt(privateKey, getJwtClaimsArrayScopesList())).buildExchange();
+        Exchange exc = get("/foo").header("Authorization", "bearer " + getSignedJwt(privateKey, getJwtClaimsArrayScopesList())).buildExchange();
🧹 Nitpick comments (4)
core/src/main/java/com/predic8/membrane/core/router/hotdeploy/DefaultHotDeployer.java (1)

30-32: Consider making router field volatile for thread-safety.

The router field is written in start() and read in startInternal(), stop(), and setEnabled() without synchronization. If these methods are called from different threads, there's a visibility concern where other threads might not see the updated value.

🔎 Proposed fix
-    private Router router;
+    private volatile Router router;
core/src/main/java/com/predic8/membrane/core/RuleReinitializer.java (1)

53-78: Consider synchronizing retry() or documenting thread-safety assumptions.

The retry() method is not synchronized and can be invoked concurrently (e.g., by the Timer thread and potentially external callers). While it calls the synchronized stop() method, the rest of the logic—including cloning proxies and replacing rules—runs without synchronization. This could lead to race conditions if RuleManager is not fully thread-safe or if multiple timers are inadvertently started.

Either synchronize the method or document the thread-safety guarantees you rely on:

-public void retry() {
+public synchronized void retry() {
     boolean stillFailing = false;
     ...
core/src/main/java/com/predic8/membrane/core/Router.java (1)

377-395: Consider reducing lock scope to avoid holding during potentially expensive operations.

The method now correctly synchronizes access to the running field (fixing the previous review concern). However, holding the lock during proxy.init(this) at line 392 could block other threads if initialization performs expensive operations (I/O, network calls, etc.).

🔎 Suggested pattern to minimize lock contention
 public void add(Proxy proxy) throws IOException {
     log.debug("Adding proxy {}.", proxy.getName());
     RuleManager ruleManager = getRuleManager();
+    boolean isRunning;
+    boolean shouldInit;
 
     synchronized (lock) {
+        isRunning = running;
+        shouldInit = running;
         if (proxy instanceof SSLableProxy sp) {
-            if (running) {
+            if (isRunning) {
                 ruleManager.addProxyAndOpenPortIfNew(sp, MANUAL);
             } else
                 ruleManager.addProxy(sp, MANUAL);
         } else {
             ruleManager.addProxy(proxy, MANUAL);
         }
-        if (running) {
-            // init() has already been called
-            proxy.init(this);
-        }
     }
+    if (shouldInit) {
+        // init() has already been called
+        proxy.init(this);
+    }
 }
core/src/test/java/com/predic8/membrane/core/config/ReadRulesConfigurationTest.java (1)

36-39: Consider adding a null check in tearDown.

If setUp() fails and throws an exception, the router field will remain null. When JUnit calls tearDown(), the router.shutdown() call will throw a NullPointerException, which may mask the original setup failure in test output.

🔎 Suggested improvement
 @AfterAll
 static void tearDown() {
+    if (router != null) {
         router.shutdown();
+    }
 }
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e221d3a and 2a6a7cc.

📒 Files selected for processing (9)
  • core/src/main/java/com/predic8/membrane/core/HttpRouter.java
  • core/src/main/java/com/predic8/membrane/core/Router.java
  • core/src/main/java/com/predic8/membrane/core/RuleReinitializer.java
  • core/src/main/java/com/predic8/membrane/core/router/hotdeploy/DefaultHotDeployer.java
  • core/src/test/java/com/predic8/membrane/core/config/ReadRulesConfigurationTest.java
  • core/src/test/java/com/predic8/membrane/core/interceptor/oauth2/client/b2c/B2CMembrane.java
  • core/src/test/java/com/predic8/membrane/core/openapi/validators/security/JWTInterceptorAndSecurityValidatorTest.java
  • core/src/test/java/com/predic8/membrane/core/proxies/UnavailableSoapProxyTest.java
  • core/src/test/java/com/predic8/membrane/integration/withoutinternet/interceptor/oauth2/OAuth2AuthorizationServerInterceptorBase.java
✅ Files skipped from review due to trivial changes (1)
  • core/src/test/java/com/predic8/membrane/core/proxies/UnavailableSoapProxyTest.java
🚧 Files skipped from review as they are similar to previous changes (2)
  • core/src/main/java/com/predic8/membrane/core/HttpRouter.java
  • core/src/test/java/com/predic8/membrane/core/interceptor/oauth2/client/b2c/B2CMembrane.java
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-11-15T18:38:23.728Z
Learnt from: predic8
Repo: membrane/api-gateway PR: 2322
File: core/src/main/java/com/predic8/membrane/core/lang/CommonBuiltInFunctions.java:18-26
Timestamp: 2025-11-15T18:38:23.728Z
Learning: In the membrane/api-gateway repository, the maintainer predic8 prefers fail-fast behavior where NullPointerExceptions are allowed to crash rather than being caught or having defensive null checks that silently return null, as it makes debugging easier by showing where problems originate.

Applied to files:

  • core/src/main/java/com/predic8/membrane/core/Router.java
🧬 Code graph analysis (2)
core/src/test/java/com/predic8/membrane/core/openapi/validators/security/JWTInterceptorAndSecurityValidatorTest.java (2)
core/src/main/java/com/predic8/membrane/core/openapi/model/Request.java (1)
  • Request (27-157)
core/src/test/java/com/predic8/membrane/test/TestUtil.java (1)
  • TestUtil (29-61)
core/src/test/java/com/predic8/membrane/core/config/ReadRulesConfigurationTest.java (1)
core/src/main/java/com/predic8/membrane/core/RouterBootstrap.java (1)
  • RouterBootstrap (28-65)
⏰ 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 (16)
core/src/main/java/com/predic8/membrane/core/router/hotdeploy/DefaultHotDeployer.java (3)

34-38: LGTM!

Clean delegation pattern for the public API entry point.


40-57: LGTM!

Proper synchronization on the lock object, defensive guard against double-start, and clean use of pattern matching for the instanceof check.


79-82: LGTM!

Simple implementation appropriate for a default deployer that's always enabled.

core/src/test/java/com/predic8/membrane/integration/withoutinternet/interceptor/oauth2/OAuth2AuthorizationServerInterceptorBase.java (1)

285-285: LGTM. The removal of throws Exception is correct—the oasi.init(router) call at line 297 invokes AbstractInterceptor.init(Router router), which does not declare checked exceptions.

core/src/main/java/com/predic8/membrane/core/Router.java (9)

107-107: LGTM: Logger initialization follows best practices.

The logger is now properly initialized using LoggerFactory.getLogger(Router.class), which is the standard SLF4J pattern.


174-209: Well-structured initialization with proper lifecycle separation.

The new init() method correctly separates initialization from start(), uses registerIfAbsent for thread-safe singleton registration, and includes a guard against double initialization. The initialization order (registry components first, transport last) is appropriate.


289-295: Excellent fix: Race condition eliminated.

The previous non-atomic check-then-act pattern has been properly replaced with registerIfAbsent, which atomically ensures a single RuleManager instance. This correctly addresses the previous review concern.


490-492: Excellent fix: Singleton pattern correctly implemented.

The previous issue of creating multiple FlowController instances has been properly fixed using registerIfAbsent. This ensures thread-safe singleton registration. This correctly addresses the previous review concern.


303-305: Good fix: Uses accessor instead of direct field access.

The method now correctly calls getRegistry() instead of accessing the registry field directly, ensuring proper initialization and null-checking. This correctly addresses the previous review concern.


228-262: Well-structured start sequence with proper lifecycle management.

The start() method correctly:

  • Separates initialization from startup (calls init() if needed)
  • Uses proper synchronization for state checks
  • Starts components in the right order (ports, JMX, hot deployer, reinitializer)
  • Handles exceptions comprehensively with appropriate error messages

502-537: Improved observability and clearer action handling.

The added debug logging enhances troubleshooting capabilities, and the refactored action handling using explicit isAdded(), isDeleted(), and isModified() methods improves code readability.


589-617: Clear and user-friendly error handling.

The extracted error handling methods provide well-formatted, actionable error messages for OpenAPI-related configuration issues. The use of text blocks for multi-line messages enhances readability.


155-155: Excellent addition of debug logging throughout lifecycle.

The strategic placement of debug logs at key lifecycle points (creation, initialization, starting, proxy addition) significantly improves observability and troubleshooting capabilities without adding overhead in production (when debug logging is disabled).

Also applies to: 175-175, 212-214, 229-229, 270-270, 298-298, 378-378, 495-495

core/src/test/java/com/predic8/membrane/core/config/ReadRulesConfigurationTest.java (1)

48-48: No changes needed. The project targets Java 21 (confirmed in pom.xml: <javac.source>21</javac.source> and <javac.target>21</javac.target>), so List.getFirst() is a valid and appropriate method to use. Multiple other files in the core module already use this API.

core/src/test/java/com/predic8/membrane/core/openapi/validators/security/JWTInterceptorAndSecurityValidatorTest.java (2)

37-37: LGTM! Good visibility and API improvements.

The static import for Request.get enables cleaner test code, and tightening the visibility of SPEC_LOCATION and proxy to private follows the principle of least privilege.

Also applies to: 44-45


62-63: LGTM! Cleaner request construction.

The switch to package-private visibility is appropriate for JUnit 5, and the fluent get("/foo") pattern is more readable than the builder pattern.

Copy link
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

♻️ Duplicate comments (3)
core/src/main/java/com/predic8/membrane/core/RuleReinitializer.java (3)

30-32: Add null validation for the router parameter.

The constructor does not validate that router is non-null. If a null router is passed, subsequent calls to start() (line 48) or getInactiveRules() (line 91) will throw a NullPointerException.

🔎 Proposed fix
 public RuleReinitializer(Router router) {
+    if (router == null) {
+        throw new IllegalArgumentException("router must not be null");
+    }
     this.router = router;
 }

66-78: Do not replace the proxy when the new proxy is still inactive.

The code unconditionally calls replaceRule() at line 74, even after detecting that newProxy.isActive() is false. This replaces the old inactive proxy with a new inactive clone, which is not the intended behavior. The replacement should only occur if the new proxy successfully activates.

🔎 Proposed fix
 for (Proxy proxy : inactive) {
     try {
         log.info("Trying to start API {}.", proxy.getName());
         Proxy newProxy = proxy.clone();
         if (!newProxy.isActive()) {
             log.warn("New rule for API {} is still not active.", proxy.getName());
             stillFailing = true;
+        } else {
+            router.getRuleManager().replaceRule(proxy, newProxy);
         }
-        router.getRuleManager().replaceRule(proxy, newProxy);
     } catch (CloneNotSupportedException e) {
         log.error("", e);
     }
 }

89-95: Avoid potential ConcurrentModificationException in getInactiveRules().

The method iterates over router.getRuleManager().getRules(), which returns the live Vector directly rather than a defensive copy. While Vector is thread-safe for individual operations, iteration can throw ConcurrentModificationException if another thread adds or removes proxies concurrently.

Consider taking a snapshot before iteration to avoid concurrent modification issues.

🔎 Proposed fix
 List<Proxy> getInactiveRules() {
+    List<Proxy> rules = new ArrayList<>(router.getRuleManager().getRules());
     ArrayList<Proxy> inactive = new ArrayList<>();
-    for (Proxy proxy : router.getRuleManager().getRules())
+    for (Proxy proxy : rules)
         if (!proxy.isActive())
             inactive.add(proxy);
     return inactive;
 }
🧹 Nitpick comments (4)
annot/src/main/java/com/predic8/membrane/annot/beanregistry/BeanContainer.java (1)

34-36: Consider renaming parameter to avoid shadowing.

The parameter name singleton shadows the field name, making this.singleton.set(singleton) harder to read. Consider renaming the parameter to value or bean for clarity.

🔎 Proposed refactor
-    public void setSingleton(Object singleton) {
-        this.singleton.set(singleton);
+    public void setSingleton(Object value) {
+        this.singleton.set(value);
     }
core/src/test/java/com/predic8/membrane/integration/withoutinternet/interceptor/oauth2/OAuth2AuthorizationServerInterceptorBase.java (1)

213-216: Good addition of test cleanup.

Proper teardown ensures the router is shut down after each test, preventing resource leaks and test interference.

Consider adding defensive exception handling to prevent teardown failures from masking test issues:

🔎 Optional: Add error handling
 @AfterEach
 void tearDown() {
-    router.shutdown();
+    try {
+        router.shutdown();
+    } catch (Exception e) {
+        // Log but don't fail the test due to cleanup issues
+        System.err.println("Error during router shutdown: " + e.getMessage());
+    }
 }
core/src/main/java/com/predic8/membrane/core/router/hotdeploy/DefaultHotDeployer.java (2)

30-30: Consider making the router field volatile for thread-safety.

The router field is assigned in start(Router) (line 36) without synchronization and read in multiple methods with inconsistent locking. If start(Router) can be called concurrently with other methods, visibility issues may arise.

Making router volatile ensures all threads see the updated value immediately.

🔎 Proposed fix
-    private Router router;
+    private volatile Router router;

40-57: Consider making startInternal() idempotent to avoid exceptions when re-enabling.

Currently, calling setEnabled(true) when hot deployment is already running will trigger startInternal(), which throws IllegalStateException because hdt != null. This makes setEnabled(true) non-idempotent.

For better usability, consider returning early instead of throwing when hdt is already initialized.

🔎 Proposed fix
 private void startInternal() {
     // Prevent multiple threads from starting hot deployment at the same time.
     synchronized (lock) {
         if (hdt != null)
-            throw new IllegalStateException("Hot deployment already started.");
+            return; // Already started, no-op
 
         if (!(router.getBeanFactory() instanceof TrackingApplicationContext tac)) {
             log.warn("""
                     ApplicationContext is not a TrackingApplicationContext. Please set <router hotDeploy="false">.
                     """);
             return;
         }
 
         hdt = new HotDeploymentThread(router.getRef());
         hdt.setFiles(tac.getFiles());
         hdt.start();
     }
 }
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2a6a7cc and be5387d.

📒 Files selected for processing (7)
  • annot/src/main/java/com/predic8/membrane/annot/beanregistry/BeanContainer.java
  • core/src/main/java/com/predic8/membrane/core/Router.java
  • core/src/main/java/com/predic8/membrane/core/RuleReinitializer.java
  • core/src/main/java/com/predic8/membrane/core/router/hotdeploy/DefaultHotDeployer.java
  • core/src/test/java/com/predic8/membrane/core/openapi/validators/security/JWTInterceptorAndSecurityValidatorTest.java
  • core/src/test/java/com/predic8/membrane/core/proxies/SOAPProxyTest.java
  • core/src/test/java/com/predic8/membrane/integration/withoutinternet/interceptor/oauth2/OAuth2AuthorizationServerInterceptorBase.java
✅ Files skipped from review due to trivial changes (1)
  • core/src/main/java/com/predic8/membrane/core/Router.java
🚧 Files skipped from review as they are similar to previous changes (2)
  • core/src/test/java/com/predic8/membrane/core/proxies/SOAPProxyTest.java
  • core/src/test/java/com/predic8/membrane/core/openapi/validators/security/JWTInterceptorAndSecurityValidatorTest.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)
🔇 Additional comments (4)
core/src/main/java/com/predic8/membrane/core/RuleReinitializer.java (1)

34-56: LGTM: Lifecycle methods properly address thread-safety concerns.

The start() method correctly prevents multiple concurrent timers with the idempotency check at line 36-37, and stop() properly nullifies the timer reference at line 55. Both methods are synchronized, ensuring thread-safe access to the timer field.

core/src/test/java/com/predic8/membrane/integration/withoutinternet/interceptor/oauth2/OAuth2AuthorizationServerInterceptorBase.java (2)

290-303: Exception handling change is consistent with refactored init() API.

The removal of throws Exception from initOasi() is correct: OAuth2AuthorizationServerInterceptor.init() and Router.init() no longer declare checked exceptions in their signatures, making the removal of the throws clause in the test method appropriate.


204-211: Router lifecycle management is correct.

The setup follows an explicit create → start → initialize pattern. The router.start() call before initOasi() and initMas() is intentional and consistent with other OAuth2 integration tests in the codebase. The addition of tearDown() with router.shutdown() provides proper cleanup after each test.

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

72-77: Good fix for the NPE concern, but note the idempotency issue.

The addition of router != null check at line 73 properly addresses the past review comment about potential NPE when setEnabled(true) is called before start(Router).

However, as noted in the startInternal() review, calling setEnabled(true) when hot deployment is already running will throw IllegalStateException due to the non-idempotent behavior of startInternal().

Copy link
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

Caution

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

⚠️ Outside diff range comments (4)
core/src/test/java/com/predic8/membrane/core/openapi/serviceproxy/APIProxyOpenAPITest.java (1)

41-46: Add @AfterEach cleanup method to prevent resource leaks.

The HttpRouter instantiated in setUp() requires explicit cleanup. Router's shutdown() method closes transport resources and shuts down timer managers that are allocated during instantiation. Add an @AfterEach method calling router.shutdown() (as shown in AbstractTestWithRouter).

core/src/test/java/com/predic8/membrane/core/openapi/serviceproxy/APIProxySpringConfigurationTest.java (1)

59-79: Fix inaccurate comment on line 74 — OpenAPIInterceptor is added during Spring configuration parsing, not during init().

The comment "// Got added" on line 74 is misleading. Test 1 (interceptorSequenceFromSpringConfiguration) expects OpenAPIInterceptor at position 0 without calling init(), indicating the interceptor is already added by Spring configuration parsing. Test 2 has identical assertions because init() only adds missing interceptors via initOpenAPI() (lines 126–131 of APIProxy.java), and in this case, the interceptor is already present from Spring config.

Test 3 (noPublisherNoOpenAPIInterceptor) correctly demonstrates init() behavior — it omits <openapiPublisher/> from the config, so init() adds OpenAPIPublisherInterceptor and the order changes. The "// Was added" comment on line 87 accurately reflects this.

Update the comment on line 74 to clarify that the interceptor came from Spring configuration, not init(). Alternatively, if the comment is meant to highlight a specific aspect of the test, consider a more precise message or remove it entirely.

core/src/test/java/com/predic8/membrane/core/openapi/serviceproxy/ApiDocsInterceptorTest.java (1)

94-98: Inconsistent Router usage detected.

Line 96 still instantiates a plain Router() while the rest of the test has migrated to HttpRouter. This inconsistency could cause issues if the refactoring requires all instances to use HttpRouter.

🔎 Proposed fix
     void initializeEmptyRuleApiSpecsTest() {
         ApiDocsInterceptor adi = new ApiDocsInterceptor();
-        adi.init(new Router());
+        adi.init(new HttpRouter());
         assertEquals(new HashMap<>(), adi.initializeRuleApiSpecs());
     }
core/src/test/java/com/predic8/membrane/core/interceptor/apikey/ApiKeysInterceptorTest.java (1)

168-173: Inconsistent Router usage detected.

Line 172 still instantiates a plain Router() while the rest of the test has migrated to HttpRouter (lines 60, 64, 70). This inconsistency should be corrected to maintain uniformity across the refactoring.

🔎 Proposed fix
     void handleDuplicateApiKeys() {
         var dupeStore = new ApiKeyFileStore();
         dupeStore.setLocation(getKeyfilePath("apikeys/duplicate-api-keys.txt"));
-        assertThrows(RuntimeException.class, () -> dupeStore.init(new Router()));
+        assertThrows(RuntimeException.class, () -> dupeStore.init(new HttpRouter()));
     }
♻️ Duplicate comments (5)
annot/src/main/java/com/predic8/membrane/annot/beanregistry/BeanContainer.java (1)

42-45: Previous review feedback addressed.

The toString() method now correctly uses singleton.get() to display the actual bean value instead of the AtomicReference wrapper.

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

63-72: Add null checks before accessing router and reinitializer.

Line 68 calls router.getReinitializer().stop() without verifying that router is not null. Since stop() can be invoked before start() is called, router may be null, resulting in an NPE. Additionally, getReinitializer() should be checked for null as a defensive measure.

🔎 Proposed fix
 @Override
 public void stop() {
     synchronized (lock) {
         if (hdt == null)
             return;
 
+        if (router == null)
+            return;
+
+        var reinitializer = router.getReinitializer();
+        if (reinitializer != null)
+            reinitializer.stop();
-        router.getReinitializer().stop();
         hdt.stopASAP();
         hdt = null;
     }
 }
core/src/test/java/com/predic8/membrane/core/openapi/validators/security/JWTInterceptorAndSecurityValidatorTest.java (1)

76-82: Inconsistent request builder pattern (duplicate issue).

Line 77 still uses the old new Request.Builder().get("/foo") pattern instead of the static get("/foo") import introduced at line 37 and used consistently at line 68. Both test methods should use the same pattern.

🔎 Apply the previously suggested fix
-        Exchange exc = new Request.Builder().get("/foo").header("Authorization", "bearer " + getSignedJwt(privateKey, getJwtClaimsArrayScopesList())).buildExchange();
+        Exchange exc = get("/foo").header("Authorization", "bearer " + getSignedJwt(privateKey, getJwtClaimsArrayScopesList())).buildExchange();
core/src/main/java/com/predic8/membrane/core/Router.java (2)

375-386: Unsynchronized read of running field persists.

The running field is @GuardedBy("lock") (line 135-136), but this method reads it at line 379 without synchronization. This was flagged in a previous review and remains unaddressed.

🔎 Proposed fix
 public void add(Proxy proxy) throws IOException {
     log.debug("Adding proxy {}.", proxy.getName());
     RuleManager ruleManager = getRuleManager();
+    boolean isRunning;
+    synchronized (lock) {
+        isRunning = running;
+    }

-    if (running && proxy instanceof SSLableProxy sp) {
+    if (isRunning && proxy instanceof SSLableProxy sp) {
         sp.init(this);
         ruleManager.addProxyAndOpenPortIfNew(sp, MANUAL);
     } else {
         ruleManager.addProxy(proxy, MANUAL);
     }
 }

546-549: BeanRegistryImplementation created with null parameters.

This was flagged in a previous review. Creating BeanRegistryImplementation with null for both observer and grammar parameters may cause NullPointerException when the registry invokes methods on these parameters during bean lifecycle operations.

🔎 Proposed fix

Consider providing a no-op observer or ensuring BeanRegistryImplementation handles null gracefully:

 public BeanRegistry getRegistry() {
     if (registry == null)
-        registry = new BeanRegistryImplementation(null, this, null);
+        registry = new BeanRegistryImplementation(BeanCacheObserver.NO_OP, this, null);
     return registry;
 }

Or add null-safety in BeanRegistryImplementation for observer calls.

🧹 Nitpick comments (3)
annot/src/main/java/com/predic8/membrane/annot/beanregistry/BeanContainer.java (1)

17-18: Consider using an explicit import.

Wildcard imports can be replaced with explicit imports for clarity. Only AtomicReference is used from this package.

🔎 Suggested change
-import java.util.concurrent.atomic.*;
+import java.util.concurrent.atomic.AtomicReference;
core/src/test/java/com/predic8/membrane/core/openapi/serviceproxy/APIProxySpringConfigurationTest.java (1)

22-22: Consider using specific static import instead of wildcard.

Wildcard static imports (import static ... *) can reduce code clarity and increase the risk of naming conflicts. Since only getPathFromResource is used in this file, prefer the specific import.

🔎 Proposed fix
-import static com.predic8.membrane.test.TestUtil.*;
+import static com.predic8.membrane.test.TestUtil.getPathFromResource;
core/src/main/java/com/predic8/membrane/core/HttpRouter.java (1)

42-49: Consider addressing the TODO or creating a tracking issue.

The TODO comment indicates this method is brittle and test-only. If this is technical debt, consider either documenting a plan to refactor it or creating an issue to track the improvement.

Would you like me to open an issue to track this technical debt?

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 86f1857 and 27317a6.

📒 Files selected for processing (23)
  • annot/src/main/java/com/predic8/membrane/annot/beanregistry/BeanContainer.java
  • core/src/main/java/com/predic8/membrane/core/HttpRouter.java
  • core/src/main/java/com/predic8/membrane/core/Router.java
  • core/src/main/java/com/predic8/membrane/core/interceptor/Interceptor.java
  • core/src/main/java/com/predic8/membrane/core/router/hotdeploy/DefaultHotDeployer.java
  • core/src/test/java/com/predic8/membrane/core/interceptor/apikey/ApiKeysInterceptorTest.java
  • core/src/test/java/com/predic8/membrane/core/interceptor/apikey/stores/ApiKeyFileStoreTest.java
  • core/src/test/java/com/predic8/membrane/core/interceptor/schemavalidation/SOAPFaultTest.java
  • core/src/test/java/com/predic8/membrane/core/interceptor/schemavalidation/SOAPMessageValidatorInterceptorTest.java
  • core/src/test/java/com/predic8/membrane/core/interceptor/schemavalidation/ValidatorInterceptorTest.java
  • core/src/test/java/com/predic8/membrane/core/interceptor/server/WebServerInterceptorTest.java
  • core/src/test/java/com/predic8/membrane/core/interceptor/templating/StaticInterceptorTest.java
  • core/src/test/java/com/predic8/membrane/core/openapi/serviceproxy/APIProxyOpenAPITest.java
  • core/src/test/java/com/predic8/membrane/core/openapi/serviceproxy/APIProxySpringConfigurationTest.java
  • core/src/test/java/com/predic8/membrane/core/openapi/serviceproxy/AbstractProxySpringConfigurationTest.java
  • core/src/test/java/com/predic8/membrane/core/openapi/serviceproxy/ApiDocsInterceptorTest.java
  • core/src/test/java/com/predic8/membrane/core/openapi/serviceproxy/OpenAPI31Test.java
  • core/src/test/java/com/predic8/membrane/core/openapi/serviceproxy/OpenAPIInterceptorTest.java
  • core/src/test/java/com/predic8/membrane/core/openapi/serviceproxy/OpenAPIRecordFactoryTest.java
  • core/src/test/java/com/predic8/membrane/core/openapi/serviceproxy/XMembraneExtensionSecurityTest.java
  • core/src/test/java/com/predic8/membrane/core/openapi/validators/security/AbstractSecurityValidatorTest.java
  • core/src/test/java/com/predic8/membrane/core/openapi/validators/security/BasicAuthSecurityValidationTest.java
  • core/src/test/java/com/predic8/membrane/core/openapi/validators/security/JWTInterceptorAndSecurityValidatorTest.java
🚧 Files skipped from review as they are similar to previous changes (1)
  • core/src/test/java/com/predic8/membrane/core/openapi/serviceproxy/OpenAPIInterceptorTest.java
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-09-19T11:12:26.787Z
Learnt from: predic8
Repo: membrane/api-gateway PR: 2156
File: core/src/main/java/com/predic8/membrane/core/openapi/validators/QueryParameterValidator.java:129-131
Timestamp: 2025-09-19T11:12:26.787Z
Learning: Request.get().path() in com.predic8.membrane.core.openapi.model.Request preserves the query string portion of the path, so URIFactory.createWithoutException(request.getPath()).getQuery() correctly extracts query parameters.

Applied to files:

  • core/src/test/java/com/predic8/membrane/core/openapi/validators/security/BasicAuthSecurityValidationTest.java
  • core/src/test/java/com/predic8/membrane/core/interceptor/schemavalidation/ValidatorInterceptorTest.java
  • core/src/test/java/com/predic8/membrane/core/openapi/serviceproxy/OpenAPI31Test.java
📚 Learning: 2025-11-15T18:38:23.728Z
Learnt from: predic8
Repo: membrane/api-gateway PR: 2322
File: core/src/main/java/com/predic8/membrane/core/lang/CommonBuiltInFunctions.java:18-26
Timestamp: 2025-11-15T18:38:23.728Z
Learning: In the membrane/api-gateway repository, the maintainer predic8 prefers fail-fast behavior where NullPointerExceptions are allowed to crash rather than being caught or having defensive null checks that silently return null, as it makes debugging easier by showing where problems originate.

Applied to files:

  • core/src/main/java/com/predic8/membrane/core/Router.java
🧬 Code graph analysis (4)
core/src/test/java/com/predic8/membrane/core/openapi/serviceproxy/APIProxySpringConfigurationTest.java (1)
core/src/test/java/com/predic8/membrane/test/TestUtil.java (1)
  • TestUtil (29-61)
core/src/test/java/com/predic8/membrane/core/openapi/validators/security/JWTInterceptorAndSecurityValidatorTest.java (3)
core/src/main/java/com/predic8/membrane/core/openapi/model/Request.java (1)
  • Request (27-157)
core/src/test/java/com/predic8/membrane/core/openapi/util/OpenAPITestUtils.java (1)
  • OpenAPITestUtils (34-98)
core/src/test/java/com/predic8/membrane/test/TestUtil.java (1)
  • TestUtil (29-61)
core/src/test/java/com/predic8/membrane/core/openapi/validators/security/BasicAuthSecurityValidationTest.java (1)
core/src/main/java/com/predic8/membrane/core/openapi/model/Request.java (1)
  • Request (27-157)
core/src/test/java/com/predic8/membrane/core/interceptor/schemavalidation/ValidatorInterceptorTest.java (1)
core/src/main/java/com/predic8/membrane/core/http/Request.java (1)
  • Request (32-369)
⏰ 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 (35)
annot/src/main/java/com/predic8/membrane/annot/beanregistry/BeanContainer.java (1)

24-36: LGTM! Thread-safety improvement is correctly implemented.

The migration from volatile Object to AtomicReference<Object> is sound. The final modifier ensures the reference itself cannot be reassigned, and the atomic operations (get()/set()) provide thread-safe access. Compound operations requiring atomicity are handled externally via synchronization in BeanRegistryImplementation.

core/src/main/java/com/predic8/membrane/core/router/hotdeploy/DefaultHotDeployer.java (4)

19-19: LGTM! Good thread-safety improvement.

The addition of a dedicated lock object and updated @GuardedBy annotation improve thread-safety by preventing external synchronization interference.

Also applies to: 28-28, 33-33


36-39: LGTM! Clear lifecycle API.

The method rename to start() and @NotNull annotation improve API clarity and compile-time safety. Delegation to startInternal() properly separates public API from internal implementation.


41-60: LGTM! Well-structured initialization.

The lock-based synchronization and defensive check for repeated starts (lines 44-47) ensure thread-safe initialization. The logging approach is appropriate for this lifecycle method.


75-80: LGTM! NPE prevention addressed.

The null check for router on line 76 properly addresses the previous concern about potential NPE when setEnabled(true) is called before start(Router).

core/src/test/java/com/predic8/membrane/core/interceptor/server/WebServerInterceptorTest.java (1)

35-35: LGTM! Clean refactor aligning with the HttpRouter migration.

The instantiation now uses HttpRouter while keeping the field type as Router, which follows good practice by programming to the interface. This change is consistent with the broader architectural refactoring described in the PR objectives.

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

56-59: Add Javadoc for the new init method signatures in the interface.

The two new init method overloads at lines 56-58 lack documentation explaining their purpose, when each is invoked, and the relationship between them. While AbstractInterceptor provides implementations, the interface contract should be explicitly documented (e.g., which overload is called during what lifecycle phase, and why the Proxy parameter variant exists).

core/src/test/java/com/predic8/membrane/core/openapi/validators/security/JWTInterceptorAndSecurityValidatorTest.java (5)

37-37: Static import added for fluent request building.

The static import for get enables cleaner test code. Ensure it's used consistently across all test methods.


44-46: Improved encapsulation and lifecycle management.

The visibility changes to private and the introduction of instance fields for router and proxy properly support per-test lifecycle management and improve encapsulation.


50-59: HttpRouter usage aligns with refactoring objectives.

The switch to HttpRouter (line 52) and per-test router lifecycle management properly support the bean-factory/registry refactoring described in the PR objectives. The initialization logic is clear and correct.


61-64: Resource cleanup added, resolving past review concern.

The @AfterEach tearDown method properly shuts down the router, preventing resource leaks. This directly addresses the major issue flagged in previous reviews.


67-73: Improved request building with fluent API.

The test method correctly uses the new fluent request-building pattern get("/foo").header(...), which is cleaner and more readable than the previous Request.Builder() approach.

core/src/test/java/com/predic8/membrane/core/openapi/serviceproxy/ApiDocsInterceptorTest.java (1)

48-48: LGTM: HttpRouter migration in test setup.

The test setup correctly instantiates HttpRouter instead of Router, aligning with the PR's refactoring goals.

core/src/test/java/com/predic8/membrane/core/openapi/serviceproxy/OpenAPIRecordFactoryTest.java (1)

33-33: LGTM: HttpRouter migration.

The test correctly instantiates HttpRouter in the setup, consistent with the refactoring objectives.

core/src/test/java/com/predic8/membrane/core/interceptor/schemavalidation/SOAPMessageValidatorInterceptorTest.java (1)

42-42: LGTM: HttpRouter migration.

The test correctly instantiates HttpRouter in the setup.

core/src/test/java/com/predic8/membrane/core/openapi/serviceproxy/XMembraneExtensionSecurityTest.java (1)

35-35: LGTM: HttpRouter migration.

The test correctly instantiates HttpRouter in the setup.

core/src/test/java/com/predic8/membrane/core/interceptor/apikey/stores/ApiKeyFileStoreTest.java (1)

103-103: LGTM: HttpRouter migration in test helper.

The helper method correctly instantiates HttpRouter for store initialization.

core/src/test/java/com/predic8/membrane/core/interceptor/apikey/ApiKeysInterceptorTest.java (2)

16-16: LGTM: Import adjustment for HttpRouter.

The wildcard import now includes HttpRouter used throughout the test setup.


60-70: LGTM: HttpRouter migration in test setup.

The test setup correctly instantiates HttpRouter instances for store and extractor initialization.

core/src/test/java/com/predic8/membrane/core/openapi/validators/security/BasicAuthSecurityValidationTest.java (3)

31-31: LGTM: Static import for cleaner test code.

Adding the static import for Request.get improves readability in the test methods.


46-46: LGTM: HttpRouter migration.

The test correctly instantiates HttpRouter in the setup.


68-68: LGTM: Simplified request construction.

Using the static imported get() method makes the test code more concise and readable.

Also applies to: 76-76

core/src/test/java/com/predic8/membrane/core/openapi/serviceproxy/AbstractProxySpringConfigurationTest.java (1)

39-41: Explicit init() call is necessary—Spring context is not started in this test.

The ctx.refresh() call prepares the Spring beans but does not start the ApplicationContext lifecycle. In production code (RouterBootstrap), bf.start() is called after getBean(), which triggers router.start(), which automatically calls init() if not already initialized. Since this test uses only refresh() without start(), the explicit router.init() call is intentional and necessary. This pattern is consistent with the codebase: direct initialization paths (direct instantiation or refresh-only contexts) require explicit init() calls, while managed contexts that call start() rely on the router's lifecycle callbacks.

core/src/test/java/com/predic8/membrane/core/openapi/validators/security/AbstractSecurityValidatorTest.java (1)

44-48: LGTM!

The change from Router to HttpRouter aligns with the PR's migration pattern. The return type remains Router, preserving API compatibility while using the concrete HttpRouter implementation.

core/src/test/java/com/predic8/membrane/core/interceptor/templating/StaticInterceptorTest.java (1)

44-44: LGTM!

Consistent migration to HttpRouter across all test methods. The initialization pattern is appropriately applied.

Also applies to: 53-53, 84-84

core/src/test/java/com/predic8/membrane/core/interceptor/schemavalidation/ValidatorInterceptorTest.java (1)

58-64: LGTM!

Good use of static import for post() which improves test readability. The migration to HttpRouter is consistent with the broader refactoring effort while maintaining the Router type for the field.

core/src/test/java/com/predic8/membrane/core/openapi/serviceproxy/OpenAPI31Test.java (1)

41-53: LGTM!

Clean refactoring that:

  • Uses the Request.get() factory method for improved readability
  • Properly declares URISyntaxException in the method signature
  • Migrates to HttpRouter consistently with other test files
core/src/test/java/com/predic8/membrane/core/interceptor/schemavalidation/SOAPFaultTest.java (1)

35-35: LGTM!

Consistent migration to HttpRouter while preserving the Router type declaration for the field.

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

27-40: LGTM!

Clean constructor refactoring:

  • Proper delegation pattern with this(null)
  • Null-safe proxy configuration handling
  • Overridden getTransport() provides stronger typing by returning HttpTransport

The static createTransport() method is appropriate since it doesn't require instance state.

core/src/main/java/com/predic8/membrane/core/Router.java (6)

52-80: Useful architectural documentation.

The ADR-style comments clearly document design decisions and open questions. Consider extracting these to a separate ADR document if the project maintains architecture decision records.


290-296: Addresses previous review feedback.

The getRuleManager() method now uses registerIfAbsent, making the get-or-create pattern atomic and thread-safe. This properly addresses the race condition flagged in a previous review.


304-306: Uses getRegistry() accessor correctly.

This addresses a previous review comment about using registry directly. The code now properly calls getRegistry() which handles lazy initialization.


481-483: Addresses previous review feedback.

The getFlowController() method now uses registerIfAbsent, ensuring only one instance is created. This properly addresses the concern about multiple instances being created on each call.


580-608: Static error handlers improve code organization.

These methods centralize error handling for OpenAPI-related startup failures with clear, user-friendly error messages.


348-350: DNSCache registration is properly handled in init(), supporting the orElseThrow() change.

The verification confirms that DNSCache is registered in the init() method at line 196 using registerIfAbsent(DNSCache.class, DNSCache::new). All call sites of getDnsCache() are in transport handlers, proxies, and interceptors that execute during request processing, after the Router is initialized. This initialization order is implicit but follows the standard pattern.

The change from orElse(new DNSCache()) to orElseThrow() is appropriate and correctly prevents multiple cache instances without requiring explicit guarantees.

Copy link
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

Caution

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

⚠️ Outside diff range comments (2)
core/src/test/java/com/predic8/membrane/core/openapi/serviceproxy/APIProxySpringConfigurationTest.java (1)

68-79: Clarify the misleading test name.

The test name noPublisherNoOpenAPIInterceptor refers to the XML configuration (absence of the <openapiPublisher/> tag), but the test validates that both OpenAPIPublisherInterceptor and OpenAPIInterceptor are automatically added at runtime (lines 74-75). This creates confusion about what the test is actually validating.

Rename to better reflect the behavior being tested, such as bothInterceptorsAddedDuringExplicitInitWhenNoExplicitPublisher or automaticInterceptorsAddedOnInit, to clarify that the test is checking automatic addition during the explicit ap.init(router) call.

core/src/main/java/com/predic8/membrane/core/interceptor/apikey/stores/ApiKeyStore.java (1)

22-29: Javadoc references Router but parameter is IRouter.

The Javadoc on line 23 mentions {@link Router} while the actual parameter type is now IRouter. Update the documentation to reflect the new type.

🔎 Proposed fix
     /**
-     * Lifecycle hook invoked once to provide the {@link Router} context.
+     * Lifecycle hook invoked once to provide the {@link IRouter} context.
      * Default is a no-op to preserve backward compatibility; implementations may override.
      *
-     * @param router non-null router instance
+     * @param router non-null IRouter instance
      */
     default void init(IRouter router) {
     }
♻️ Duplicate comments (2)
core/src/main/java/com/predic8/membrane/core/Router.java (2)

370-381: Unsynchronized read of running field persists.

The running field is marked @GuardedBy("lock") at line 134, but line 374 reads it without synchronization. This could lead to visibility issues in multi-threaded scenarios.

🔎 Proposed fix
 public void add(Proxy proxy) throws IOException {
     log.debug("Adding proxy {}.", proxy.getName());
     RuleManager ruleManager = getRuleManager();
+    boolean isRunning;
+    synchronized (lock) {
+        isRunning = running;
+    }

-    if (running && proxy instanceof SSLableProxy sp) {
+    if (isRunning && proxy instanceof SSLableProxy sp) {
         sp.init(this);
         ruleManager.addProxyAndOpenPortIfNew(sp, MANUAL);
     } else {
         ruleManager.addProxy(proxy, MANUAL);
     }
 }

541-545: BeanRegistryImplementation created with null observer and grammar parameters.

The getRegistry() method creates a BeanRegistryImplementation with null for both observer and grammar parameters. According to the past review, this can cause NullPointerException during bean lifecycle operations when observer methods are invoked.

🔎 Proposed fix

Consider providing a no-op observer or ensuring BeanRegistryImplementation handles null gracefully:

 public BeanRegistry getRegistry() {
     if (registry == null)
-        registry = new BeanRegistryImplementation(null, this, null);
+        registry = new BeanRegistryImplementation(BeanCacheObserver.NOOP, this, new GrammarAutoGenerated());
     return registry;
 }

Alternatively, add null checks in BeanRegistryImplementation before invoking observer methods.

#!/bin/bash
# Check BeanRegistryImplementation for null handling of observer and grammar
cat annot/src/main/java/com/predic8/membrane/annot/beanregistry/BeanRegistryImplementation.java | head -100
🧹 Nitpick comments (11)
core/src/main/java/com/predic8/membrane/core/interceptor/balancer/RoundRobinStrategy.java (1)

19-19: Consider using explicit imports instead of wildcard imports.

Wildcard imports can reduce code clarity and potentially cause naming conflicts. Since this change was likely made just to import IRouter, an explicit import would be cleaner.

🔎 Suggested change
-import com.predic8.membrane.core.*;
+import com.predic8.membrane.core.IRouter;
core/src/main/java/com/predic8/membrane/core/interceptor/balancer/SessionIdExtractor.java (1)

17-17: Consider using specific import instead of wildcard.

Wildcard imports reduce code clarity and can introduce ambiguity. Replace with a specific import for IRouter.

🔎 Proposed refactor
-import com.predic8.membrane.core.*;
+import com.predic8.membrane.core.IRouter;
core/src/test/java/com/predic8/membrane/core/openapi/serviceproxy/ApiDocsInterceptorTest.java (1)

48-48: LGTM! Router instantiation updated to HttpRouter.

The change from new Router() to new HttpRouter() aligns with the PR's refactoring objectives. The field type at line 41 remains Router, which works correctly through polymorphism.

Optional consideration: If the broader refactoring moves toward the IRouter abstraction (as mentioned in the PR summary), you might consider updating the field type from Router to IRouter for consistency:

-    Router router;
+    IRouter router;

However, this is purely optional and depends on whether test code is migrating to the interface-based type as well.

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

20-20: Consider using explicit imports instead of wildcard.

The wildcard import com.predic8.membrane.core.* reduces code clarity and can lead to naming conflicts. Explicit imports make it clearer which classes are being used.

core/src/test/java/com/predic8/membrane/core/openapi/serviceproxy/APIProxySpringConfigurationTest.java (1)

22-22: Consider using a specific import instead of a wildcard.

The wildcard import reduces clarity since only getPathFromResource is used in this file. Specific imports make dependencies more explicit and prevent potential naming conflicts.

🔎 Proposed fix
-import static com.predic8.membrane.test.TestUtil.*;
+import static com.predic8.membrane.test.TestUtil.getPathFromResource;
core/src/main/java/com/predic8/membrane/core/exchangestore/ElasticSearchExchangeStore.java (1)

510-515: Improved error handling with defensive null check.

The addition of indexRes.has("error") check before accessing the error object is a good defensive improvement. However, the code still assumes that if "error" exists, it contains a "root_cause" array with at least one element. While the outer try-catch at line 509 will handle unexpected structures, consider further hardening:

🔎 Optional: More defensive error structure checking
-                if(indexRes.has("error") && indexRes.getJSONObject("error").getJSONArray("root_cause").getJSONObject(0).getString("type").equals("resource_already_exists_exception")){
+                if(indexRes.has("error")) {
+                    JSONObject error = indexRes.getJSONObject("error");
+                    if(error.has("root_cause") && error.getJSONArray("root_cause").length() > 0 
+                        && error.getJSONArray("root_cause").getJSONObject(0).has("type")
+                        && error.getJSONArray("root_cause").getJSONObject(0).getString("type").equals("resource_already_exists_exception")){
                     log.info("Index already exists skipping index creation");
+                        return;
+                    }
+                    log.error("Error happened. Reply from elastic search is below");
+                    log.error(indexRes.toString());
                 } else {
-                    log.error("Error happened. Reply from elastic search is below");
-                    log.error(indexRes.toString());
+                    // Unexpected response structure
+                    log.warn("Unexpected response structure from Elasticsearch: {}", indexRes);
                 }
core/src/main/java/com/predic8/membrane/core/interceptor/apikey/extractors/ApiKeyExtractor.java (1)

16-16: Consider using explicit imports instead of wildcard.

The wildcard import (import com.predic8.membrane.core.*;) reduces code readability and can lead to namespace pollution. Explicit imports make dependencies clearer.

🔎 Suggested refactor
-import com.predic8.membrane.core.*;
+import com.predic8.membrane.core.IRouter;
core/src/main/java/com/predic8/membrane/core/interceptor/session/JwtSessionManager.java (1)

19-19: Consider using explicit imports instead of wildcard.

The wildcard import (import com.predic8.membrane.core.*;) can obscure which specific classes are being used and may cause future compilation issues if new classes are added to the package.

🔎 Suggested refactor
-import com.predic8.membrane.core.*;
+import com.predic8.membrane.core.IRouter;
core/src/main/java/com/predic8/membrane/core/interceptor/oauth2/tokengenerators/TokenGenerator.java (1)

16-16: Consider using explicit imports instead of wildcard.

Using explicit imports improves code clarity and prevents potential naming conflicts.

🔎 Suggested refactor
-import com.predic8.membrane.core.*;
+import com.predic8.membrane.core.IRouter;
core/src/main/java/com/predic8/membrane/core/interceptor/session/MemcachedSessionManager.java (1)

20-20: Consider using explicit imports instead of wildcard.

Wildcard imports reduce code readability and can lead to maintenance issues. Use explicit imports to make dependencies clear.

🔎 Suggested refactor
-import com.predic8.membrane.core.*;
+import com.predic8.membrane.core.IRouter;
core/src/main/java/com/predic8/membrane/core/interceptor/oauth2/authorizationservice/AuthorizationService.java (1)

18-18: Consider using explicit imports instead of wildcard.

Replace the wildcard import with explicit imports to improve code clarity and maintainability.

🔎 Suggested refactor
-import com.predic8.membrane.core.*;
+import com.predic8.membrane.core.IRouter;
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 27317a6 and 7a8e80c.

📒 Files selected for processing (87)
  • core/src/main/java/com/predic8/membrane/core/IRouter.java
  • core/src/main/java/com/predic8/membrane/core/Router.java
  • core/src/main/java/com/predic8/membrane/core/exchangestore/AbstractPersistentExchangeStore.java
  • core/src/main/java/com/predic8/membrane/core/exchangestore/ElasticSearchExchangeStore.java
  • core/src/main/java/com/predic8/membrane/core/exchangestore/ExchangeStore.java
  • core/src/main/java/com/predic8/membrane/core/exchangestore/MongoDBExchangeStore.java
  • core/src/main/java/com/predic8/membrane/core/graphql/GraphQLoverHttpValidator.java
  • core/src/main/java/com/predic8/membrane/core/interceptor/AbstractInterceptor.java
  • core/src/main/java/com/predic8/membrane/core/interceptor/ApisJsonInterceptor.java
  • core/src/main/java/com/predic8/membrane/core/interceptor/Interceptor.java
  • core/src/main/java/com/predic8/membrane/core/interceptor/acl/AbstractClientAddress.java
  • core/src/main/java/com/predic8/membrane/core/interceptor/acl/AccessControl.java
  • core/src/main/java/com/predic8/membrane/core/interceptor/acl/AccessControlInterceptor.java
  • core/src/main/java/com/predic8/membrane/core/interceptor/acl/Any.java
  • core/src/main/java/com/predic8/membrane/core/interceptor/acl/Hostname.java
  • core/src/main/java/com/predic8/membrane/core/interceptor/acl/Ip.java
  • core/src/main/java/com/predic8/membrane/core/interceptor/acl/Resource.java
  • core/src/main/java/com/predic8/membrane/core/interceptor/administration/AdminPageBuilder.java
  • core/src/main/java/com/predic8/membrane/core/interceptor/administration/RuleUtil.java
  • core/src/main/java/com/predic8/membrane/core/interceptor/apikey/extractors/ApiKeyExpressionExtractor.java
  • core/src/main/java/com/predic8/membrane/core/interceptor/apikey/extractors/ApiKeyExtractor.java
  • core/src/main/java/com/predic8/membrane/core/interceptor/apikey/stores/ApiKeyFileStore.java
  • core/src/main/java/com/predic8/membrane/core/interceptor/apikey/stores/ApiKeyStore.java
  • core/src/main/java/com/predic8/membrane/core/interceptor/apikey/stores/MongoDBApiKeyStore.java
  • core/src/main/java/com/predic8/membrane/core/interceptor/authentication/session/CachingUserDataProvider.java
  • core/src/main/java/com/predic8/membrane/core/interceptor/authentication/session/CustomStatementJdbcUserDataProvider.java
  • core/src/main/java/com/predic8/membrane/core/interceptor/authentication/session/EmailTokenProvider.java
  • core/src/main/java/com/predic8/membrane/core/interceptor/authentication/session/EmptyTokenProvider.java
  • core/src/main/java/com/predic8/membrane/core/interceptor/authentication/session/FileUserDataProvider.java
  • core/src/main/java/com/predic8/membrane/core/interceptor/authentication/session/JdbcUserDataProvider.java
  • core/src/main/java/com/predic8/membrane/core/interceptor/authentication/session/JwtSessionManager.java
  • core/src/main/java/com/predic8/membrane/core/interceptor/authentication/session/LDAPUserDataProvider.java
  • core/src/main/java/com/predic8/membrane/core/interceptor/authentication/session/LoginDialog.java
  • core/src/main/java/com/predic8/membrane/core/interceptor/authentication/session/SessionManager.java
  • core/src/main/java/com/predic8/membrane/core/interceptor/authentication/session/StaticUserDataProvider.java
  • core/src/main/java/com/predic8/membrane/core/interceptor/authentication/session/TOTPTokenProvider.java
  • core/src/main/java/com/predic8/membrane/core/interceptor/authentication/session/TelekomSMSTokenProvider.java
  • core/src/main/java/com/predic8/membrane/core/interceptor/authentication/session/TokenProvider.java
  • core/src/main/java/com/predic8/membrane/core/interceptor/authentication/session/UnifyingUserDataProvider.java
  • core/src/main/java/com/predic8/membrane/core/interceptor/authentication/session/UserDataProvider.java
  • core/src/main/java/com/predic8/membrane/core/interceptor/authentication/session/WhateverMobileSMSTokenProvider.java
  • core/src/main/java/com/predic8/membrane/core/interceptor/authentication/xen/XenAuthenticationInterceptor.java
  • core/src/main/java/com/predic8/membrane/core/interceptor/balancer/BalancerUtil.java
  • core/src/main/java/com/predic8/membrane/core/interceptor/balancer/ByThreadStrategy.java
  • core/src/main/java/com/predic8/membrane/core/interceptor/balancer/DispatchingStrategy.java
  • core/src/main/java/com/predic8/membrane/core/interceptor/balancer/PolyglotSessionIdExtractor.java
  • core/src/main/java/com/predic8/membrane/core/interceptor/balancer/PriorityStrategy.java
  • core/src/main/java/com/predic8/membrane/core/interceptor/balancer/RoundRobinStrategy.java
  • core/src/main/java/com/predic8/membrane/core/interceptor/balancer/SessionIdExtractor.java
  • core/src/main/java/com/predic8/membrane/core/interceptor/balancer/faultmonitoring/FaultMonitoringStrategy.java
  • core/src/main/java/com/predic8/membrane/core/interceptor/cache/CacheInterceptor.java
  • core/src/main/java/com/predic8/membrane/core/interceptor/flow/AbstractFlowInterceptor.java
  • core/src/main/java/com/predic8/membrane/core/interceptor/flow/choice/Case.java
  • core/src/main/java/com/predic8/membrane/core/interceptor/flow/choice/InterceptorContainer.java
  • core/src/main/java/com/predic8/membrane/core/interceptor/javascript/GraalVMJavascriptLanguageAdapter.java
  • core/src/main/java/com/predic8/membrane/core/interceptor/javascript/LanguageAdapter.java
  • core/src/main/java/com/predic8/membrane/core/interceptor/javascript/RhinoJavascriptLanguageAdapter.java
  • core/src/main/java/com/predic8/membrane/core/interceptor/oauth2/ClaimList.java
  • core/src/main/java/com/predic8/membrane/core/interceptor/oauth2/ClientList.java
  • core/src/main/java/com/predic8/membrane/core/interceptor/oauth2/ConsentPageFile.java
  • core/src/main/java/com/predic8/membrane/core/interceptor/oauth2/StaticClientList.java
  • core/src/main/java/com/predic8/membrane/core/interceptor/oauth2/authorizationservice/AuthorizationService.java
  • core/src/main/java/com/predic8/membrane/core/interceptor/oauth2/authorizationservice/DynamicRegistration.java
  • core/src/main/java/com/predic8/membrane/core/interceptor/oauth2/tokengenerators/BearerJwtTokenGenerator.java
  • core/src/main/java/com/predic8/membrane/core/interceptor/oauth2/tokengenerators/BearerTokenGenerator.java
  • core/src/main/java/com/predic8/membrane/core/interceptor/oauth2/tokengenerators/TokenGenerator.java
  • core/src/main/java/com/predic8/membrane/core/interceptor/oauth2client/rf/SessionAuthorizer.java
  • core/src/main/java/com/predic8/membrane/core/interceptor/schemavalidation/SchematronValidator.java
  • core/src/main/java/com/predic8/membrane/core/interceptor/session/InMemorySessionManager.java
  • core/src/main/java/com/predic8/membrane/core/interceptor/session/JwtSessionManager.java
  • core/src/main/java/com/predic8/membrane/core/interceptor/session/MemcachedSessionManager.java
  • core/src/main/java/com/predic8/membrane/core/interceptor/session/RedisSessionManager.java
  • core/src/main/java/com/predic8/membrane/core/interceptor/session/SessionManager.java
  • core/src/main/java/com/predic8/membrane/core/interceptor/xslt/XSLTTransformer.java
  • core/src/main/java/com/predic8/membrane/core/lang/ExchangeExpression.java
  • core/src/main/java/com/predic8/membrane/core/lang/ScriptingUtils.java
  • core/src/main/java/com/predic8/membrane/core/lang/groovy/GroovyExchangeExpression.java
  • core/src/main/java/com/predic8/membrane/core/openapi/serviceproxy/OpenAPIPublisher.java
  • core/src/main/java/com/predic8/membrane/core/transport/ws/WebSocketInterceptorInterface.java
  • core/src/main/java/com/predic8/membrane/core/transport/ws/interceptors/WebSocketLogInterceptor.java
  • core/src/main/java/com/predic8/membrane/core/transport/ws/interceptors/WebSocketSpringInterceptor.java
  • core/src/main/java/com/predic8/membrane/core/transport/ws/interceptors/WebSocketStompReassembler.java
  • core/src/test/java/com/predic8/membrane/core/interceptor/apikey/ApiKeysInterceptorTest.java
  • core/src/test/java/com/predic8/membrane/core/interceptor/session/FakeSyncSessionStoreManager.java
  • core/src/test/java/com/predic8/membrane/core/openapi/serviceproxy/APIProxySpringConfigurationTest.java
  • core/src/test/java/com/predic8/membrane/core/openapi/serviceproxy/ApiDocsInterceptorTest.java
  • distribution/src/test/java/com/predic8/membrane/examples/withinternet/test/Loadbalancing4XmlSessionExampleTest.java
🚧 Files skipped from review as they are similar to previous changes (2)
  • core/src/test/java/com/predic8/membrane/core/interceptor/apikey/ApiKeysInterceptorTest.java
  • core/src/main/java/com/predic8/membrane/core/interceptor/Interceptor.java
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-11-15T18:38:23.728Z
Learnt from: predic8
Repo: membrane/api-gateway PR: 2322
File: core/src/main/java/com/predic8/membrane/core/lang/CommonBuiltInFunctions.java:18-26
Timestamp: 2025-11-15T18:38:23.728Z
Learning: In the membrane/api-gateway repository, the maintainer predic8 prefers fail-fast behavior where NullPointerExceptions are allowed to crash rather than being caught or having defensive null checks that silently return null, as it makes debugging easier by showing where problems originate.

Applied to files:

  • core/src/main/java/com/predic8/membrane/core/Router.java
🧬 Code graph analysis (2)
core/src/test/java/com/predic8/membrane/core/openapi/serviceproxy/APIProxySpringConfigurationTest.java (1)
core/src/test/java/com/predic8/membrane/test/TestUtil.java (1)
  • TestUtil (29-61)
core/src/main/java/com/predic8/membrane/core/Router.java (5)
core/src/main/java/com/predic8/membrane/core/RuleManager.java (1)
  • RuleManager (35-344)
core/src/main/java/com/predic8/membrane/core/util/DLPUtil.java (1)
  • DLPUtil (26-78)
core/src/main/java/com/predic8/membrane/core/util/DNSCache.java (1)
  • DNSCache (27-78)
core/src/main/java/com/predic8/membrane/core/kubernetes/KubernetesWatcher.java (1)
  • KubernetesWatcher (37-132)
core/src/main/java/com/predic8/membrane/core/proxies/ApiInfo.java (1)
  • ApiInfo (27-86)
⏰ 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 (102)
core/src/main/java/com/predic8/membrane/core/interceptor/balancer/RoundRobinStrategy.java (1)

74-76: LGTM!

The signature change from Router to IRouter correctly aligns with the PR's migration to the interface abstraction, improving testability and adhering to the dependency inversion principle. The empty implementation is appropriate since this strategy doesn't require router access.

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

23-24: LGTM! Interface correctly migrated to IRouter.

The method signature change from Router to IRouter aligns with the PR's refactoring objectives. Since Router implements IRouter, this maintains backward compatibility while moving to the abstraction layer.

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

73-73: LGTM! Clean migration to IRouter abstraction.

The parameter type change from Router to IRouter aligns perfectly with the PR's repository-wide refactor objectives. The method body requires no changes since it only uses methods available on the IRouter interface (getRuleManager(), getRules(), etc.).

distribution/src/test/java/com/predic8/membrane/examples/withinternet/test/Loadbalancing4XmlSessionExampleTest.java (3)

27-30: LGTM! Good refactoring to extract constants.

Extracting the JSON body strings into named constants eliminates duplication and improves readability. The constant names clearly convey their purpose in the session-based load balancing test.


38-38: LGTM! Appropriate visibility for JUnit 5.

The change from public to package-private visibility is correct for JUnit 5 tests, which do not require public access modifiers.


41-102: LGTM! Consistent usage of constants throughout the test.

All inline JSON bodies have been correctly replaced with the appropriate constants (BODY_SESSION_1 and BODY_SESSION_2). The test logic remains functionally equivalent while being more maintainable.

core/src/test/java/com/predic8/membrane/core/openapi/serviceproxy/ApiDocsInterceptorTest.java (1)

96-96: LGTM! Consistent instantiation change.

The change to new HttpRouter() is consistent with the setUp method change at line 48 and properly supports the isolated test scenario for empty rule specs.

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

44-51: Signature change correctly migrates parameter to IRouter interface.

The parameter type change from Router to IRouter is consistent with the PR's Router→IRouter migration. The implementation correctly uses IRouter methods (getResolverMap(), getBaseLocation()), the logic remains unchanged, and call sites are already compatible since AbstractInterceptor inherited the IRouter-typed router field.

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

70-70: [Rewritten review comment]
[Classification tag]

core/src/test/java/com/predic8/membrane/core/openapi/serviceproxy/APIProxySpringConfigurationTest.java (1)

55-66: LGTM!

The test correctly validates the new bootstrap-driven initialization flow. The comment on line 60 helpfully clarifies that OpenAPIInterceptor is added automatically during bootstrap initialization.

core/src/main/java/com/predic8/membrane/core/lang/ScriptingUtils.java (1)

44-44: LGTM!

Clean parameter type migration to IRouter. The method correctly delegates to interface methods (getBeanFactory(), getUriFactory()), maintaining the same functionality.

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

125-127: LGTM!

The init(IRouter router) signature aligns with the DispatchingStrategy interface migration. No behavioral change since the method body is a no-op.

core/src/main/java/com/predic8/membrane/core/interceptor/authentication/session/TelekomSMSTokenProvider.java (1)

98-100: LGTM!

The parameter type migration to IRouter is consistent with the broader refactor. The method continues to correctly obtain the HTTP client via router.getHttpClientFactory().

core/src/main/java/com/predic8/membrane/core/interceptor/apikey/stores/ApiKeyFileStore.java (1)

62-68: LGTM!

The init(IRouter router) implementation correctly aligns with the updated ApiKeyStore interface. File reading logic using router.getResolverMap() and router.getBaseLocation() remains intact.

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

22-22: LGTM!

The interface method signature update to init(IRouter router) establishes the new contract for all DispatchingStrategy implementations. This is the foundation for the consistent migration across implementing classes.

core/src/main/java/com/predic8/membrane/core/transport/ws/interceptors/WebSocketSpringInterceptor.java (1)

58-60: LGTM!

The init(IRouter router) signature aligns with the WebSocketInterceptorInterface migration. The delegation to i.init(router) correctly propagates the router context to the referenced interceptor.

core/src/main/java/com/predic8/membrane/core/interceptor/apikey/stores/MongoDBApiKeyStore.java (1)

61-70: LGTM!

The init(IRouter router) implementation aligns with the updated ApiKeyStore interface. The router parameter is unused here since MongoDB initialization only requires the connection string, but this is acceptable for interface compliance.

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

63-70: LGTM! Clean migration to IRouter.

The signature update from Router to IRouter is consistent with the broader refactor. The wiring logic for ProxyAware instances and delegation to init(router) remains correct.

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

28-30: LGTM! Constructor signature correctly updated.

The migration from Router to IRouter is straightforward and the delegation to the parent constructor remains correct.

core/src/main/java/com/predic8/membrane/core/interceptor/oauth2client/rf/SessionAuthorizer.java (1)

49-64: LGTM! Field and method signature correctly migrated.

The change from Router to IRouter is consistent throughout—field declaration, parameter, assignment, and usage (line 107) all align properly.

core/src/main/java/com/predic8/membrane/core/interceptor/authentication/session/JdbcUserDataProvider.java (1)

37-53: LGTM! Field and method signature correctly updated.

The migration from Router to IRouter is straightforward and correct. The usage of router.getBeanFactory() on line 82 is consistent with the IRouter interface.

core/src/main/java/com/predic8/membrane/core/interceptor/acl/AbstractClientAddress.java (2)

23-29: LGTM! Field and constructor correctly migrated.

The change from Router to IRouter for the final field is correct, and the constructor properly initializes it.


47-47: LGTM! Hook method signature correctly updated.

The empty init method serves as an optional hook for subclasses. The signature update to IRouter is consistent with the broader migration.

core/src/main/java/com/predic8/membrane/core/interceptor/oauth2/authorizationservice/DynamicRegistration.java (1)

44-51: IRouter interface correctly exposes all required methods.

Verification confirms that IRouter provides getResolverMap(), getBaseLocation(), getHttpClientFactory(), and getFlowController() at the locations used in DynamicRegistration (lines 47, 50, 76, 82). The migration is correct.

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

36-43: LGTM! Constructor parameter correctly migrated.

Both GraalVMJavascriptLanguageAdapter (line 28) and RhinoJavascriptLanguageAdapter (line 27) have constructors accepting IRouter and properly call super(router), confirming the migration is complete and compatible.

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

25-25: LGTM! Clean migration to IRouter abstraction.

All public method signatures consistently updated from Router to IRouter parameter type. This refactoring improves modularity and aligns with interface-based programming practices.

Also applies to: 37-37, 49-49, 61-61, 73-73, 85-85, 89-89, 93-93, 97-97, 101-101, 105-105, 109-109, 113-113, 117-117, 121-121

core/src/main/java/com/predic8/membrane/core/interceptor/authentication/session/CustomStatementJdbcUserDataProvider.java (1)

18-18: LGTM! Consistent migration to IRouter.

The import, field type, and init method signature cleanly migrated from Router to IRouter. The usage pattern (accessing router.getBeanFactory()) remains unchanged.

Also applies to: 33-33, 43-43

core/src/main/java/com/predic8/membrane/core/lang/groovy/GroovyExchangeExpression.java (1)

36-36: LGTM! Field type updated to IRouter.

The router field type cleanly migrated to IRouter, maintaining immutability with the final modifier. Constructor initialization and usage remain consistent.

core/src/main/java/com/predic8/membrane/core/interceptor/authentication/session/UnifyingUserDataProvider.java (1)

26-26: LGTM! Init signature updated to IRouter.

The import and init method signature cleanly migrated to use IRouter, properly delegating to nested user data providers.

Also applies to: 66-66

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

41-41: LGTM! Init method signature updated to IRouter.

Clean parameter type migration from Router to IRouter with no behavioral changes.

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

17-17: LGTM! Constructor updated to accept IRouter.

The import and constructor signature cleanly migrated to IRouter, delegating properly to the parent class.

Also applies to: 23-23

core/src/main/java/com/predic8/membrane/core/interceptor/flow/choice/Case.java (1)

39-39: LGTM! Init method updated to IRouter.

The method signature cleanly migrated to accept IRouter, properly passing it to the InterceptorAdapter constructor.

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

25-29: LGTM! Method signature updated to IRouter with explicit null return.

The parameter type cleanly migrated to IRouter. The explicit return null; at line 29 improves code clarity for the not-found case.

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

34-38: LGTM! Clean migration to IRouter abstraction.

The method signature correctly adopts the IRouter interface while preserving all existing logic. This aligns with the broader architectural refactoring described in the PR objectives.

core/src/main/java/com/predic8/membrane/core/interceptor/authentication/session/UserDataProvider.java (1)

22-22: LGTM! Interface contract updated to IRouter.

The interface method signature correctly migrates to the IRouter abstraction, maintaining consistency with the project-wide refactoring pattern.

core/src/test/java/com/predic8/membrane/core/interceptor/session/FakeSyncSessionStoreManager.java (2)

16-16: Wildcard import adopted.

The import statement changed from a specific Router import to a wildcard import. This is a minor style change that accommodates the IRouter interface.


30-30: LGTM! Test utility aligned with IRouter migration.

The method signature correctly adopts the IRouter interface, maintaining consistency with the broader refactoring.

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

45-45: LGTM! Signature updated to IRouter.

The method signature correctly migrates to the IRouter abstraction. The no-op implementation suggests this strategy requires no router-specific initialization.

core/src/main/java/com/predic8/membrane/core/interceptor/authentication/session/TokenProvider.java (2)

18-18: Wildcard import adopted.

The import statement changed to accommodate the IRouter interface, consistent with the broader refactoring pattern.


22-24: LGTM! Interface contract migrated to IRouter.

The init method signature correctly adopts the IRouter abstraction. Other interface methods remain unchanged, preserving the existing contract for token operations.

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

50-52: LGTM! Init method signature updated to IRouter.

The method signature correctly migrates to the IRouter abstraction while preserving the no-op implementation. This aligns with the project-wide refactoring pattern.

core/src/main/java/com/predic8/membrane/core/interceptor/authentication/session/EmailTokenProvider.java (1)

74-75: LGTM! Signature migrated to IRouter.

The method signature correctly adopts the IRouter abstraction. The empty implementation indicates this provider requires no router-specific initialization.

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

27-30: LGTM! Constructor parameter migrated to IRouter.

The constructor signature correctly adopts the IRouter abstraction and properly passes it to the parent class. The router field usage on Line 34 (router.isProduction()) confirms that IRouter provides the necessary interface methods.

core/src/main/java/com/predic8/membrane/core/interceptor/oauth2/tokengenerators/BearerTokenGenerator.java (1)

17-17: LGTM!

The import and init method signature updates correctly align with the IRouter abstraction migration. Since the init body is a no-op, this change is purely mechanical with no functional impact.

Also applies to: 68-70

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

18-18: LGTM!

The init signature update to IRouter is consistent with the ClientList interface migration. The router parameter remains unused in this implementation, so no behavioral change.

Also applies to: 32-34

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

16-16: LGTM!

The interface method signature update to IRouter establishes the contract that all ClientList implementations must follow. This is consistent with the broader migration pattern.

Also applies to: 21-24

core/src/main/java/com/predic8/membrane/core/interceptor/authentication/session/TOTPTokenProvider.java (1)

20-20: LGTM!

The init signature update to IRouter is consistent with the migration. The no-op implementation means no behavioral change.

Also applies to: 53-55

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

45-75: LGTM!

The init signature update to IRouter is consistent with the migration, and super.init(router) correctly propagates the interface type to the parent class.

core/src/main/java/com/predic8/membrane/core/interceptor/authentication/session/CachingUserDataProvider.java (1)

22-22: LGTM!

The init signature update to IRouter correctly propagates the interface type to the wrapped userDataProvider. Cache initialization remains unchanged.

Also applies to: 80-86

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

19-19: LGTM!

The init signature update to IRouter aligns with the migration. The router parameter is unused here (init only triggers scope re-initialization), which is acceptable.

Also applies to: 82-84

core/src/main/java/com/predic8/membrane/core/interceptor/authentication/session/LoginDialog.java (1)

87-102: The signature change to IRouter is correct and properly abstracted.

All required methods are exposed by the IRouter interface: getUriFactory() (line 54), getResolverMap() (line 48), and getBaseLocation() (line 46). The method body's usage is consistent with existing patterns throughout the codebase.

core/src/main/java/com/predic8/membrane/core/interceptor/oauth2/tokengenerators/BearerJwtTokenGenerator.java (1)

18-18: LGTM: Signature migrated to IRouter.

The migration from Router to IRouter aligns with the project-wide refactoring objective and maintains existing functionality.

Also applies to: 53-53

core/src/main/java/com/predic8/membrane/core/interceptor/authentication/session/JwtSessionManager.java (1)

18-18: LGTM: Signature migrated to IRouter.

The change is consistent with the IRouter abstraction migration and correctly passes the router to the parent class.

Also applies to: 59-59

core/src/main/java/com/predic8/membrane/core/interceptor/authentication/session/LDAPUserDataProvider.java (1)

39-39: LGTM: Signature migrated to IRouter.

The migration is straightforward and maintains compatibility with the existing getResolverMap() and getBaseLocation() usage.

Also applies to: 432-432

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

25-25: LGTM: Signature migrated to IRouter.

The change aligns with the broader migration to IRouter and correctly delegates to the parent class.

Also applies to: 54-54

core/src/main/java/com/predic8/membrane/core/interceptor/balancer/faultmonitoring/FaultMonitoringStrategy.java (1)

19-19: LGTM: Signature migrated to IRouter with @OverRide annotation.

Good addition of the @Override annotation, which improves compile-time safety and makes the contract explicit.

Also applies to: 125-126

core/src/main/java/com/predic8/membrane/core/transport/ws/WebSocketInterceptorInterface.java (1)

17-17: LGTM: Interface signature migrated to IRouter.

The interface change is consistent with the project-wide IRouter migration. All implementations should be updated accordingly.

Also applies to: 24-24

core/src/main/java/com/predic8/membrane/core/interceptor/apikey/extractors/ApiKeyExpressionExtractor.java (1)

17-17: LGTM: Signature migrated to IRouter.

The change is straightforward and maintains compatibility with InterceptorAdapter usage.

Also applies to: 59-59

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

79-79: LGTM: Signature migrated to IRouter.

The init signature change aligns with the project-wide IRouter migration.

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

60-60: LGTM! Clean interface migration to IRouter.

The default method signature update from Router to IRouter aligns well with the PR's objective to introduce a router abstraction layer. This change maintains backward compatibility through the default implementation while enabling future implementations to work with the IRouter interface.

core/src/main/java/com/predic8/membrane/core/interceptor/authentication/session/FileUserDataProvider.java (1)

18-19: LGTM! Signature updated to IRouter abstraction.

The import and method signature changes correctly align with the IRouter migration. The method implementation doesn't actually use the router parameter, so this is a safe type-level change.

Also applies to: 112-126

core/src/main/java/com/predic8/membrane/core/interceptor/authentication/session/WhateverMobileSMSTokenProvider.java (1)

18-18: LGTM! IRouter migration with actual router usage.

The signature change to IRouter is correct. The method uses router.getHttpClientFactory(), which should be part of the IRouter interface contract. This change maintains the existing behavior while working with the new abstraction.

Also applies to: 72-74

core/src/main/java/com/predic8/membrane/core/interceptor/authentication/session/StaticUserDataProvider.java (1)

20-20: LGTM! Clean signature migration to IRouter.

The import and signature updates align with the IRouter abstraction. The router parameter is not used in the method body, making this a straightforward type-level change.

Also applies to: 194-197

core/src/main/java/com/predic8/membrane/core/interceptor/authentication/session/SessionManager.java (1)

64-67: LGTM! IRouter parameter type updated.

The signature change from Router to IRouter is consistent with the broader refactoring effort. The router parameter is not used in the method body, so this is a safe change.

core/src/main/java/com/predic8/membrane/core/interceptor/authentication/session/EmptyTokenProvider.java (1)

20-20: LGTM! No-op implementation correctly updated.

The signature change to IRouter is appropriate. Since the method body is a no-op, this change is entirely safe.

Also applies to: 26-28

core/src/main/java/com/predic8/membrane/core/transport/ws/interceptors/WebSocketLogInterceptor.java (1)

19-19: LGTM! Interface update with empty implementation.

The signature change to IRouter aligns with the refactoring objectives. The empty method body makes this change risk-free.

Also applies to: 40-42

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

35-50: LGTM! Constructor updated to use IRouter abstraction.

The constructor parameter change from Router to IRouter is correct. The implementation uses router.getResolverMap() and router.getBaseLocation(), which should be part of the IRouter interface contract. The logic remains unchanged—these methods are called both in the main thread (line 41) and in the virtual thread (line 45) to create transformers.

core/src/main/java/com/predic8/membrane/core/interceptor/apikey/extractors/ApiKeyExtractor.java (1)

22-22: LGTM: Clean migration to IRouter abstraction.

The change from Router to IRouter aligns with the dependency inversion principle and improves testability and decoupling across the codebase.

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

62-83: LGTM: IRouter migration properly implemented.

The method signature update to use IRouter is well-integrated, and all router method calls (getResolverMap(), getBaseLocation()) are compatible with the IRouter abstraction.

core/src/main/java/com/predic8/membrane/core/interceptor/oauth2/tokengenerators/TokenGenerator.java (1)

22-22: LGTM: Interface updated to use IRouter.

The migration to IRouter in the interface definition is consistent with the broader refactoring and improves the abstraction layer.

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

132-162: LGTM: IRouter parameter properly utilized.

The parse method signature update to use IRouter is well-integrated. All router method invocations (getResolverMap(), getBaseLocation()) are compatible with the IRouter abstraction.

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

46-48: LGTM: Clean IRouter migration.

The method signature update is straightforward and consistent with the broader refactoring effort.

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

57-88: LGTM: Constructor updated to use IRouter.

The constructor parameter change from Router to IRouter is well-integrated. The router.getResolverMap() call at line 73 is compatible with the IRouter interface, and the refactoring maintains the existing functionality.

core/src/main/java/com/predic8/membrane/core/openapi/serviceproxy/OpenAPIPublisher.java (4)

90-106: LGTM: Consistent IRouter migration across methods.

All method signatures have been consistently updated to use IRouter instead of Router. The router.getUriFactory() calls are compatible with the IRouter interface.


119-122: LGTM: returnHtmlOverview updated correctly.

The method signature change to IRouter is consistent with the class-wide refactoring.


136-141: LGTM: returnOpenApiAsYaml properly refactored.

The IRouter parameter is correctly used to access getUriFactory() at line 138.


147-154: LGTM: renderOverviewTemplate updated correctly.

The IRouter parameter is properly utilized to access router.getUriFactory() at line 152.

core/src/main/java/com/predic8/membrane/core/interceptor/oauth2/authorizationservice/AuthorizationService.java (2)

72-72: LGTM: Field type updated to IRouter.

The field type change from Router to IRouter follows the dependency inversion principle and improves testability.


94-109: LGTM: init method properly migrated to IRouter.

The method signature update and all router method invocations (getResolverMap(), getBaseLocation(), getHttpClientFactory()) are compatible with the IRouter interface. The refactoring maintains existing functionality while improving abstraction.

core/src/main/java/com/predic8/membrane/core/transport/ws/interceptors/WebSocketStompReassembler.java (1)

42-45: LGTM!

The init method signature correctly updated to accept IRouter, aligning with the project-wide migration to the IRouter abstraction. The delegation to contained interceptors remains unchanged.

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

28-31: LGTM!

Constructor signature correctly updated to accept IRouter, consistent with the parent class LanguageAdapter and the broader IRouter migration.

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

34-34: LGTM!

The AbstractInterceptor base class correctly migrated to use IRouter:

  • Field type updated to IRouter
  • Both init method overloads accept IRouter
  • getRouter() returns IRouter

This foundational change properly enables the interface-based routing abstraction throughout the interceptor hierarchy.

Also applies to: 102-114

core/src/main/java/com/predic8/membrane/core/interceptor/flow/choice/InterceptorContainer.java (1)

31-49: LGTM!

Method signatures correctly updated to use IRouter. The handleInvocationProblemDetails method now properly calls buildAndSetResponse(exc) (line 48), ensuring that error responses are constructed and set on the exchange when plugin invocation fails. This is a good improvement over potentially leaving the exchange in an incomplete state.

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

39-46: LGTM!

The Resource class correctly migrated to use IRouter for the field, constructor, and init method. The child ACL address components (Ip, Hostname, Any) are properly wired with the IRouter instance.

Also applies to: 100-103

core/src/main/java/com/predic8/membrane/core/lang/ExchangeExpression.java (1)

77-84: LGTM!

Both InterceptorAdapter constructors correctly updated to accept IRouter, consistent with the parent AbstractInterceptor class changes.

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

59-61: LGTM!

Constructor and init method correctly updated to use IRouter. The added null-safety check on line 107 (router.getTransport() != null &&) is a good defensive improvement that prevents potential NPE when the transport is not configured.

Also applies to: 105-108

core/src/main/java/com/predic8/membrane/core/interceptor/authentication/xen/XenAuthenticationInterceptor.java (2)

102-107: LGTM!

The XenSessionManager interface correctly updated to accept IRouter in the init method signature, aligning with the project-wide abstraction migration.


114-115: LGTM!

Both InMemorySessionManager and JwtSessionManager implementations correctly updated to accept IRouter. The JwtSessionManager.init properly accesses IRouter methods (getResolverMap(), getBaseLocation()) for JWK resolution.

Also applies to: 143-149

core/src/main/java/com/predic8/membrane/core/graphql/GraphQLoverHttpValidator.java (1)

61-72: LGTM! Clean migration to IRouter interface.

The field and constructor parameter types are correctly updated from Router to IRouter. The only usage at line 245 (router.getUriFactory()) is properly supported by the IRouter interface.

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

56-74: LGTM! Consistent migration to IRouter interface.

The field and constructor parameter are correctly updated to use IRouter. All router method calls throughout the file (getTransport(), getRuleManager(), getStatistics()) are properly defined in the IRouter interface.

core/src/main/java/com/predic8/membrane/core/interceptor/acl/AccessControl.java (2)

21-34: LGTM! Consistent migration to IRouter interface.

The import, field, and constructor are correctly updated to use IRouter. The Resource class (referenced at line 43) is also part of this migration as noted in the PR summary.


64-67: LGTM! Init method signature updated consistently.

The init method parameter type is correctly changed to IRouter, maintaining consistency with the field and constructor changes.

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

29-69: Well-designed interface for router abstraction.

The IRouter interface provides a clean abstraction over the router functionality, enabling better testability and decoupling. The API surface covers all essential router operations.

A few observations:

  • The TODO at lines 34-35 about shutdown() vs stop() distinction is worth documenting in an ADR
  • The TODO at line 66 about moving getRules() to RuleManager is a reasonable consideration for future refactoring
core/src/main/java/com/predic8/membrane/core/Router.java (9)

103-105: LGTM! Router now implements IRouter interface.

The class declaration correctly implements IRouter and the logger is updated to use the class reference.


136-137: Good addition of initialized guard with proper synchronization annotation.

The new @GuardedBy("lock") annotation on initialized is consistent with the existing running field pattern.


168-205: Well-structured initialization with registry-based component provisioning.

The init() method properly uses registerIfAbsent for thread-safe singleton registration of components. The guard against double initialization is appropriate.


285-291: Good fix: getRuleManager() now uses atomic registerIfAbsent pattern.

This addresses the previously identified race condition with the check-then-act pattern.


299-301: Uses getRegistry() accessor instead of registry field directly.

This is correct - the code calls getRegistry() which handles lazy initialization, addressing the concern from the past review.


476-478: Good fix: getFlowController() now uses atomic registerIfAbsent pattern.

This addresses the previously identified issue where a new FlowController was created on every call when absent from registry.


575-603: Good error handling helpers for OpenAPI configuration errors.

The helper methods handleOpenAPIParsingException and handleDuplicateOpenAPIPaths provide clear, user-friendly error messages with formatting. The use of System.err is appropriate for configuration errors, and throwing ExitException ensures the application terminates cleanly.


571-573: New public accessor for RuleReinitializer.

The getReinitializer() method exposes the reinitializer instance. Note that reinitializer is only set at the end of init() (line 204), so callers must ensure init() has been called first, or handle a potential null return.


343-345: The implementation is correct and safe—no defensive approach is needed.

All calls to getDnsCache() occur in request/message handling code that executes only after Router.start() completes, which guarantees init() has run and DNSCache is registered. The orElseThrow() call is appropriate.

…e `Router` with `IRouter` or specialized implementations, and align tests accordingly
… specialized implementations, and align related components and tests
…)` for improved naming consistency across core components and tests
…uce `DefaultMainComponents` to streamline router initialization and component management
…mplementation`, enhance initialization/logging in routers, and remove unused/deprecated methods
Copy link
Member

@rrayst rrayst left a comment

Choose a reason for hiding this comment

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

Approved. Before merging, we should check the test results.

@predic8 predic8 merged commit 9463751 into master Jan 1, 2026
5 checks passed
@predic8 predic8 deleted the router-refactor-to-beanfactory branch January 1, 2026 13:05
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.

4 participants