Skip to content

Router refactor to beanfactory#2514

Merged
predic8 merged 49 commits into
masterfrom
router-refactor-to-beanfactory
Jan 1, 2026
Merged

Router refactor to beanfactory#2514
predic8 merged 49 commits into
masterfrom
router-refactor-to-beanfactory

Conversation

@predic8
Copy link
Copy Markdown
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
Copy Markdown
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
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 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.

Comment thread core/src/main/java/com/predic8/membrane/core/Router.java Outdated
Comment thread core/src/main/java/com/predic8/membrane/core/Router.java Outdated
Comment thread core/src/main/java/com/predic8/membrane/core/Router.java Outdated
@membrane-ci-server
Copy link
Copy Markdown

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

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

📜 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
Copy Markdown
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
Copy Markdown
Member Author

predic8 commented Dec 26, 2025

@coderabbitai review

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

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.

Comment thread core/src/main/java/com/predic8/membrane/core/Router.java Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

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.

Comment thread core/src/main/java/com/predic8/membrane/core/IRouter.java Outdated
…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
Copy Markdown
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.

3 participants