Skip to content

Add MUC room config extension SPI and disco query passthrough#3420

Open
romanbsd wants to merge 1 commit into
igniterealtime:mainfrom
romanbsd:muc-room-config-extension-spi
Open

Add MUC room config extension SPI and disco query passthrough#3420
romanbsd wants to merge 1 commit into
igniterealtime:mainfrom
romanbsd:muc-room-config-extension-spi

Conversation

@romanbsd

@romanbsd romanbsd commented Jul 4, 2026

Copy link
Copy Markdown

Summary

Introduces a plugin-facing MUCRoomConfigExtension SPI so plugins can contribute room-owner configuration fields, handle their submission, and augment room disco#info — without patching MUC internals. It also threads the full disco#items query element through to DiscoItemsProvider implementations, enabling query-aware providers (e.g. XEP-0462 PubSub Type Filtering, XEP-0499 Pubsub Extended Discovery).

This is the core-server groundwork for an out-of-tree XEP-0503 (Server-side Spaces) plugin, but the SPI is deliberately generic and space-agnostic.

Changes

  • MUCRoomConfigExtension — new interface: contributeConfigForm / populateConfigForm / processConfigSubmit, plus default no-op contributeRoomDiscoFeatures / contributeRoomDiscoForms hooks.
  • MUCRoomConfigExtensionManager — thread-safe singleton registry (CopyOnWriteArrayList, atomic addIfAbsent) that fans out to registered extensions.
  • IQOwnerHandler — invokes the extension hooks when building, populating, and processing the room owner configuration form.
  • MultiUserChatServiceImpl — invokes the disco feature/form hooks when answering a room's disco#info.
  • DiscoItemsProvider — adds a default 4-arg getItems(name, node, senderJID, query) that receives the full disco#items query element and delegates to the existing 3-arg method, so current providers are unaffected.
  • IQDiscoItemsHandler — passes the query element to providers.
  • Adds MUCRoomConfigExtensionManagerTest.

Compatibility

Fully backward compatible: the new DiscoItemsProvider and disco hook methods are default, so existing providers and any current callers compile and behave unchanged. No behavior change unless a plugin registers an extension.

Testing

./mvnw verify — full suite green (1973 core tests, 0 failures).

@coderabbitai

coderabbitai Bot commented Jul 4, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: ef917067-b7d3-4a18-8d6c-33dba2f6b692

📥 Commits

Reviewing files that changed from the base of the PR and between aace8b4 and 8dd6fd9.

📒 Files selected for processing (7)
  • xmppserver/src/main/java/org/jivesoftware/openfire/disco/DiscoItemsProvider.java
  • xmppserver/src/main/java/org/jivesoftware/openfire/disco/IQDiscoItemsHandler.java
  • xmppserver/src/main/java/org/jivesoftware/openfire/muc/MUCRoomConfigExtension.java
  • xmppserver/src/main/java/org/jivesoftware/openfire/muc/MUCRoomConfigExtensionManager.java
  • xmppserver/src/main/java/org/jivesoftware/openfire/muc/spi/IQOwnerHandler.java
  • xmppserver/src/main/java/org/jivesoftware/openfire/muc/spi/MultiUserChatServiceImpl.java
  • xmppserver/src/test/java/org/jivesoftware/openfire/muc/MUCRoomConfigExtensionManagerTest.java
🚧 Files skipped from review as they are similar to previous changes (6)
  • xmppserver/src/main/java/org/jivesoftware/openfire/disco/IQDiscoItemsHandler.java
  • xmppserver/src/main/java/org/jivesoftware/openfire/disco/DiscoItemsProvider.java
  • xmppserver/src/main/java/org/jivesoftware/openfire/muc/spi/IQOwnerHandler.java
  • xmppserver/src/main/java/org/jivesoftware/openfire/muc/spi/MultiUserChatServiceImpl.java
  • xmppserver/src/main/java/org/jivesoftware/openfire/muc/MUCRoomConfigExtensionManager.java
  • xmppserver/src/test/java/org/jivesoftware/openfire/muc/MUCRoomConfigExtensionManagerTest.java

📝 Walkthrough

Walkthrough

This change adds a default DiscoItemsProvider.getItems overload that accepts the disco query element, and updates IQDiscoItemsHandler to pass it. It also introduces MUCRoomConfigExtension and MUCRoomConfigExtensionManager, wires the manager into MUC owner configuration and disco response generation, and adds tests covering registration, delegation, and exception isolation.

Changes

  • Disco items handling now passes the full IQ query element through a new overload.
  • A new MUC extension interface defines config-form and disco contribution hooks.
  • A singleton MUC extension manager registers extensions and delegates calls with failure isolation.
  • IQOwnerHandler and MultiUserChatServiceImpl invoke the manager during config and disco flows.
  • New tests cover manager behavior.

Sequence Diagram(s)

sequenceDiagram
  participant IQDiscoItemsHandler
  participant DiscoItemsProvider
  participant IQOwnerHandler
  participant MultiUserChatServiceImpl
  participant MUCRoomConfigExtensionManager

  IQDiscoItemsHandler->>DiscoItemsProvider: getItems(name, node, senderJID, iq.createCopy())
  IQOwnerHandler->>MUCRoomConfigExtensionManager: processConfigSubmit(completedForm, room)
  IQOwnerHandler->>MUCRoomConfigExtensionManager: contributeConfigForm(configurationForm, room)
  IQOwnerHandler->>MUCRoomConfigExtensionManager: populateConfigForm(configurationForm, room)
  MultiUserChatServiceImpl->>MUCRoomConfigExtensionManager: contributeRoomDiscoFeatures(features, room)
  MultiUserChatServiceImpl->>MUCRoomConfigExtensionManager: contributeRoomDiscoForms(dataForms, room)
Loading

Estimated code review effort: 3/5

Related issues: None specified.

Related PRs: None specified.

Suggested labels: enhancement, muc, disco

Suggested reviewers: guusdk, akrherz

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description check ✅ Passed The description accurately matches the changeset, covering the new MUC extension SPI, disco query threading, compatibility, and tests.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (3)
xmppserver/src/main/java/org/jivesoftware/openfire/disco/IQDiscoItemsHandler.java (1)

159-166: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Consider passing a defensive copy of the query element to providers.

iq is a live, mutable reference to packet.getChildElement(). It's now handed to third-party DiscoItemsProvider#getItems(..., Element query) implementations (including future plugin extensions per this PR's objective) before iq.createCopy() is used to build the reply. If a provider implementation mutates query, that mutation leaks into the reply (and potentially into any other code still holding packet.getChildElement()). Passing iq.createCopy() instead removes this class of risk with negligible cost.

♻️ Proposed fix
-            Iterator<DiscoItem> itemsItr = itemsProvider.getItems(name, node, packet.getFrom(), iq);
+            Iterator<DiscoItem> itemsItr = itemsProvider.getItems(name, node, packet.getFrom(), iq.createCopy());
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@xmppserver/src/main/java/org/jivesoftware/openfire/disco/IQDiscoItemsHandler.java`
around lines 159 - 166, The `IQDiscoItemsHandler` flow is passing the live `iq`
element from `packet.getChildElement()` into `DiscoItemsProvider#getItems(...)`,
which lets provider code mutate the original query object. Update this call site
to pass a defensive copy of the query element instead, using `iq.createCopy()`
before invoking `itemsProvider.getItems(...)`, so any provider-side changes
cannot leak back into the reply or shared packet state.
xmppserver/src/main/java/org/jivesoftware/openfire/muc/MUCRoomConfigExtension.java (1)

34-34: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider making all hook methods default for easier partial adoption.

Plugins that only want to contribute disco#info features/forms (via the already-default methods) are still forced to implement contributeConfigForm, populateConfigForm, and processConfigSubmit with no-op bodies. Defaulting these to no-ops lowers the barrier for narrowly-scoped extensions without any behavior change for existing implementers.

♻️ Proposed refactor
-    void contributeConfigForm(DataForm form, MUCRoom room);
+    default void contributeConfigForm(DataForm form, MUCRoom room) {
+    }
...
-    void populateConfigForm(DataForm form, MUCRoom room);
+    default void populateConfigForm(DataForm form, MUCRoom room) {
+    }
...
-    void processConfigSubmit(DataForm completedForm, MUCRoom room);
+    default void processConfigSubmit(DataForm completedForm, MUCRoom room) {
+    }

Also applies to: 42-42, 50-50

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@xmppserver/src/main/java/org/jivesoftware/openfire/muc/MUCRoomConfigExtension.java`
at line 34, Make the MUCRoomConfigExtension hook methods easier to partially
adopt by defaulting the config-form related callbacks to no-ops. Update the
interface methods contributeConfigForm, populateConfigForm, and
processConfigSubmit so plugins that only implement disco#info hooks are no
longer forced to provide empty bodies, while keeping existing behavior unchanged
for current implementers.
xmppserver/src/test/java/org/jivesoftware/openfire/muc/MUCRoomConfigExtensionManagerTest.java (1)

64-71: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add coverage for populateConfigForm and processConfigSubmit delegation.

Only contributeConfigForm delegation is asserted here; populateConfigForm and processConfigSubmit (both implemented by testExtension) aren't exercised by any test.

✅ Proposed addition
     `@Test`
     void registeredExtensionContributesConfigForm() {
         MUCRoomConfigExtensionManager.getInstance().register(testExtension);
         final DataForm form = new DataForm(DataForm.Type.form);
         final MUCRoom room = mock(MUCRoom.class);
         MUCRoomConfigExtensionManager.getInstance().contributeConfigForm(form, room);
         assertNotNull(form.getField("test#field"));
+        MUCRoomConfigExtensionManager.getInstance().populateConfigForm(form, room);
+        assertEquals("value", form.getField("test#field").getFirstValue());
+        MUCRoomConfigExtensionManager.getInstance().processConfigSubmit(form, room); // no-op, but exercises delegation path
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@xmppserver/src/test/java/org/jivesoftware/openfire/muc/MUCRoomConfigExtensionManagerTest.java`
around lines 64 - 71, The test currently only verifies
MUCRoomConfigExtensionManager.contributeConfigForm delegation, so add coverage
for the other two extension callbacks implemented by testExtension as well.
Extend MUCRoomConfigExtensionManagerTest with assertions that populateConfigForm
is invoked when the manager populates a DataForm, and that processConfigSubmit
is invoked when a submit form is processed for a MUCRoom. Use the existing
MUCRoomConfigExtensionManager, DataForm, MUCRoom, and testExtension setup to
locate the delegation points and verify the expected field/state changes.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@xmppserver/src/main/java/org/jivesoftware/openfire/muc/MUCRoomConfigExtensionManager.java`:
- Around line 50-78: The MUCRoomConfigExtensionManager delegate methods
currently let plugin exceptions escape, so one bad MUCRoomConfigExtension can
break all room config and disco handling. Update contributeConfigForm,
populateConfigForm, processConfigSubmit, contributeRoomDiscoFeatures, and
contributeRoomDiscoForms to wrap each extension call in a try/catch and isolate
failures per extension, using the same pattern consistently across all five
methods. Refer to MUCRoomConfigExtensionManager and its extension iteration
loops when making the change.

---

Nitpick comments:
In
`@xmppserver/src/main/java/org/jivesoftware/openfire/disco/IQDiscoItemsHandler.java`:
- Around line 159-166: The `IQDiscoItemsHandler` flow is passing the live `iq`
element from `packet.getChildElement()` into `DiscoItemsProvider#getItems(...)`,
which lets provider code mutate the original query object. Update this call site
to pass a defensive copy of the query element instead, using `iq.createCopy()`
before invoking `itemsProvider.getItems(...)`, so any provider-side changes
cannot leak back into the reply or shared packet state.

In
`@xmppserver/src/main/java/org/jivesoftware/openfire/muc/MUCRoomConfigExtension.java`:
- Line 34: Make the MUCRoomConfigExtension hook methods easier to partially
adopt by defaulting the config-form related callbacks to no-ops. Update the
interface methods contributeConfigForm, populateConfigForm, and
processConfigSubmit so plugins that only implement disco#info hooks are no
longer forced to provide empty bodies, while keeping existing behavior unchanged
for current implementers.

In
`@xmppserver/src/test/java/org/jivesoftware/openfire/muc/MUCRoomConfigExtensionManagerTest.java`:
- Around line 64-71: The test currently only verifies
MUCRoomConfigExtensionManager.contributeConfigForm delegation, so add coverage
for the other two extension callbacks implemented by testExtension as well.
Extend MUCRoomConfigExtensionManagerTest with assertions that populateConfigForm
is invoked when the manager populates a DataForm, and that processConfigSubmit
is invoked when a submit form is processed for a MUCRoom. Use the existing
MUCRoomConfigExtensionManager, DataForm, MUCRoom, and testExtension setup to
locate the delegation points and verify the expected field/state changes.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: a3a6288c-e252-454a-a14a-99217f979c8e

📥 Commits

Reviewing files that changed from the base of the PR and between 6e9d1d3 and bad25c4.

📒 Files selected for processing (7)
  • xmppserver/src/main/java/org/jivesoftware/openfire/disco/DiscoItemsProvider.java
  • xmppserver/src/main/java/org/jivesoftware/openfire/disco/IQDiscoItemsHandler.java
  • xmppserver/src/main/java/org/jivesoftware/openfire/muc/MUCRoomConfigExtension.java
  • xmppserver/src/main/java/org/jivesoftware/openfire/muc/MUCRoomConfigExtensionManager.java
  • xmppserver/src/main/java/org/jivesoftware/openfire/muc/spi/IQOwnerHandler.java
  • xmppserver/src/main/java/org/jivesoftware/openfire/muc/spi/MultiUserChatServiceImpl.java
  • xmppserver/src/test/java/org/jivesoftware/openfire/muc/MUCRoomConfigExtensionManagerTest.java

@romanbsd romanbsd force-pushed the muc-room-config-extension-spi branch from bad25c4 to aace8b4 Compare July 4, 2026 22:11

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
xmppserver/src/test/java/org/jivesoftware/openfire/muc/MUCRoomConfigExtensionManagerTest.java (2)

51-78: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Missing coverage for populateConfigForm and processConfigSubmit.

testExtension overrides populateConfigForm (adds a value to test#field) and processConfigSubmit, but no test exercises MUCRoomConfigExtensionManager.populateConfigForm(...) or processConfigSubmit(...) delegation/isolation, unlike the coverage given to contributeConfigForm and the disco methods.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@xmppserver/src/test/java/org/jivesoftware/openfire/muc/MUCRoomConfigExtensionManagerTest.java`
around lines 51 - 78, Add test coverage in MUCRoomConfigExtensionManagerTest for
the missing delegation paths on testExtension: verify
MUCRoomConfigExtensionManager.populateConfigForm(...) calls
MUCRoomConfigExtension.populateConfigForm(...) and updates the existing
test#field value, and verify processConfigSubmit(...) is invoked with the
submitted DataForm without affecting unrelated fields. Reuse the existing
MUCRoomConfigExtension anonymous instance and the manager methods to keep the
new assertions aligned with the current contributeConfigForm and disco
feature/form coverage.

28-111: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Test isolation relies on a live global singleton.

MUCRoomConfigExtensionManager.getInstance() is a shared, process-wide singleton. These tests register/unregister extensions on it directly rather than resetting/injecting a fresh instance. As long as @AfterEach runs, this class is self-consistent, but any other test elsewhere in the suite that registers an extension on this same singleton and fails to unregister (e.g., due to an exception before its own cleanup, or forgetting unregister) will leak state into these assertions (e.g., assertFalse(forms.isEmpty()), exact field presence checks).

Consider exposing a package-private clear()/reset method on the manager for test use, or asserting on the delta contributed by the registered extension rather than on absolute collection state.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@xmppserver/src/test/java/org/jivesoftware/openfire/muc/MUCRoomConfigExtensionManagerTest.java`
around lines 28 - 111, The test class relies on the shared
MUCRoomConfigExtensionManager singleton, so leaked registrations from other
tests can make these assertions flaky. Update the tests to avoid depending on
global singleton state by either adding a test-only reset/clear hook on
MUCRoomConfigExtensionManager and using it in setup/cleanup, or by asserting
only the incremental contribution from testExtension in
registeredExtensionContributesDisco and
registeredExtensionContributesConfigForm. Keep the focus on
MUCRoomConfigExtensionManager.getInstance(), register/unregister, and the
testExtension/throwingExtension fixtures.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In
`@xmppserver/src/test/java/org/jivesoftware/openfire/muc/MUCRoomConfigExtensionManagerTest.java`:
- Around line 51-78: Add test coverage in MUCRoomConfigExtensionManagerTest for
the missing delegation paths on testExtension: verify
MUCRoomConfigExtensionManager.populateConfigForm(...) calls
MUCRoomConfigExtension.populateConfigForm(...) and updates the existing
test#field value, and verify processConfigSubmit(...) is invoked with the
submitted DataForm without affecting unrelated fields. Reuse the existing
MUCRoomConfigExtension anonymous instance and the manager methods to keep the
new assertions aligned with the current contributeConfigForm and disco
feature/form coverage.
- Around line 28-111: The test class relies on the shared
MUCRoomConfigExtensionManager singleton, so leaked registrations from other
tests can make these assertions flaky. Update the tests to avoid depending on
global singleton state by either adding a test-only reset/clear hook on
MUCRoomConfigExtensionManager and using it in setup/cleanup, or by asserting
only the incremental contribution from testExtension in
registeredExtensionContributesDisco and
registeredExtensionContributesConfigForm. Keep the focus on
MUCRoomConfigExtensionManager.getInstance(), register/unregister, and the
testExtension/throwingExtension fixtures.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 6e826187-823c-4d04-88cf-e40db817bff2

📥 Commits

Reviewing files that changed from the base of the PR and between bad25c4 and aace8b4.

📒 Files selected for processing (7)
  • xmppserver/src/main/java/org/jivesoftware/openfire/disco/DiscoItemsProvider.java
  • xmppserver/src/main/java/org/jivesoftware/openfire/disco/IQDiscoItemsHandler.java
  • xmppserver/src/main/java/org/jivesoftware/openfire/muc/MUCRoomConfigExtension.java
  • xmppserver/src/main/java/org/jivesoftware/openfire/muc/MUCRoomConfigExtensionManager.java
  • xmppserver/src/main/java/org/jivesoftware/openfire/muc/spi/IQOwnerHandler.java
  • xmppserver/src/main/java/org/jivesoftware/openfire/muc/spi/MultiUserChatServiceImpl.java
  • xmppserver/src/test/java/org/jivesoftware/openfire/muc/MUCRoomConfigExtensionManagerTest.java
🚧 Files skipped from review as they are similar to previous changes (6)
  • xmppserver/src/main/java/org/jivesoftware/openfire/disco/DiscoItemsProvider.java
  • xmppserver/src/main/java/org/jivesoftware/openfire/muc/MUCRoomConfigExtension.java
  • xmppserver/src/main/java/org/jivesoftware/openfire/muc/MUCRoomConfigExtensionManager.java
  • xmppserver/src/main/java/org/jivesoftware/openfire/disco/IQDiscoItemsHandler.java
  • xmppserver/src/main/java/org/jivesoftware/openfire/muc/spi/IQOwnerHandler.java
  • xmppserver/src/main/java/org/jivesoftware/openfire/muc/spi/MultiUserChatServiceImpl.java

Introduce a plugin-facing MUCRoomConfigExtension registry so plugins can
contribute room owner config fields, handle config submission, and
augment room disco#info without modifying MUC internals.

- Add MUCRoomConfigExtension interface and MUCRoomConfigExtensionManager
- Wire extension hooks into IQOwnerHandler (form build/populate/submit)
- Wire extension disco contributions into MultiUserChatServiceImpl
- Extend DiscoItemsProvider with a 4-arg getItems() default that receives
  the full disco#items query element (for XEP-0462/XEP-0499 consumers)
- Pass the query element from IQDiscoItemsHandler to providers
- Add MUCRoomConfigExtensionManagerTest
@romanbsd romanbsd force-pushed the muc-room-config-extension-spi branch from aace8b4 to 8dd6fd9 Compare July 4, 2026 22:25
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.

1 participant