Skip to content

CAMEL-25842: camel-xmpp - the consumer logs in and enters its room again after its connection was lost - #28263

Merged
davsclaus merged 3 commits into
apache:mainfrom
allthingssecurity:camel-xmpp-consumer-login-after-reconnect
Oct 22, 2026
Merged

davsclaus merged 3 commits into
apache:mainfrom
allthingssecurity:camel-xmpp-consumer-login-after-reconnect

Conversation

@allthingssecurity

@allthingssecurity allthingssecurity commented Oct 22, 2026 •

Copy link
Copy Markdown
Contributor

Description

CAMEL-25842

When the connection of XmppConsumer is lost, two things try to connect it again: the consumer's connection monitor (every connectionPollDelay seconds) and Smack's ReconnectionManager, which XmppComponent.doStart enables for every connection (its first attempt comes after a random delay of 2 to 14 seconds). The ReconnectionManager logs in after it connected, but it gives up when the monitor connected first, and the monitor only called connection.connect(), which does not log in with Smack 4.4. An XMPP server delivers nothing to a stream that did not log in and bind a resource, so when the monitor won the race the consumer reported a working connection and consumed nothing again (private chats and rooms) until the route was restarted. And whoever logged in again, the consumer did not enter its room again, although the server removes the occupant from a room when its connection ends: a room consumer never received a room message again after a lost connection.

Changes:

  • XmppConsumer.checkConnection: when the connection is connected but not authenticated, log it in again (not when login=false).
  • XmppConsumer: a ConnectionListener whose authenticated(connection, resumed) reacts to every fresh login of the connection (not to a resumed stream), by the monitor or by the ReconnectionManager: it logs "Logged in again" at INFO and enters the room again (same nickname and room password) in the monitor thread, not in the thread that logged in. If entering fails, the monitor tries again at its next check. Nothing is logged or done when no login happened.
  • XmppConsumer.checkConnection: the monitor and the ReconnectionManager can connect and log in the same connection at the same time. Smack 4.4.8 makes connect() and login(...) synchronized and throws AlreadyConnectedException / AlreadyLoggedInException to the second caller (the ReconnectionManager itself catches both). The monitor now takes either as done by the other one (DEBUG), and goes on with the login after AlreadyConnectedException; before, it logged a WARN with a stack trace and, after AlreadyConnectedException, skipped the login.
  • XmppEndpoint.login: the login part of createConnection, as the configured user or anonymously. createAccount still only applies to the first connection.
  • pom.xml: jxmpp-util-cache is declared next to jxmpp-jid (same ${jxmpp-version}). Smack needs it, and it comes in only through jxmpp-core, which Smack 4.4.8 asks for in the range [1.0.0,1.0.999]; the CI build left it off the test classpath (NoClassDefFoundError: org/jxmpp/util/cache/Cache, review).

The producers' reconnect is not changed (a send on such a connection fails loudly there). No option changed, so no upgrade guide entry.

Tests:

  • New XmppConsumerReconnectTest (2 tests) with FakeXmppServer, a small in-process XMPP server (plain TCP, SASL PLAIN and ANONYMOUS, resource binding, room presence) that, like a real server, delivers stanzas only to sessions that logged in and to the room's occupants. The real Smack client connects to it; the tests drop every connection on the server. With connectionPollDelay=1 the consumer's monitor connects first. Without the change both fail (The consumers did not log in again ==> expected: <2> but was: <0>, The consumer did not enter the room again ==> expected: <1> but was: <0>); with the change both pass.
  • New XmppConsumerSmackReconnectTest: the ReconnectionManager wins the race (fixed delay of 1 s, connectionPollDelay=3600), connects and logs in. Without the change, and with only the monitor re-entering the room, it fails (The consumer did not enter the room again ==> expected: <1> but was: <0>); with the change it passes.
  • New XmppConsumerConnectionRaceTest (2 tests, no server): a connection whose connect() throws AlreadyConnectedException is still logged in by the monitor, and a login that throws AlreadyLoggedInException is no failure of checkConnection. Without that part of the change both fail (The monitor did not log the connection in ==> expected: <[[not-authenticated] (1)]> but was: <[]>, Unexpected exception thrown: ...AlreadyLoggedInException).
  • camel-xmpp unit tests with the change: 14 run, 0 failures, 5 skipped (also with -Daether.dependencyCollector.impl=bf).
  • The test cleanup restores both Smack reconnection defaults (setDefaultFixedDelay first, since it also sets the policy to FIXED_DELAY; review).
  • This branch and the one for CAMEL-25841 (CAMEL-25841: camel-xmpp - do not hand out the endpoint of another participant or user #28262, one connection per account) merge without conflicts, also after the review commits of CAMEL-25841: camel-xmpp - do not hand out the endpoint of another participant or user #28262 (git merge-tree).

Note: when Smack still thinks it is in the room, MultiUserChat.join first leaves it and waits for the server's answer; a server that does not answer the leave of a session that is gone delays the new entry by Smack's reply timeout (5 s by default).

How it was found: variant analysis of CAMEL-25815 (server-side state, here the logged-in session and the room occupancy, lost on a reconnect and not created again), then the JUnit test. XmppRobustConnectionTest has been disabled since Smack 4.2 with the note that the reconnect does not seem to work; this is a likely cause, but I did not re-enable it (it needs the container).

What was not run: a real XMPP server (the container tests); the full root build.

Target

  • I checked that the commit is targeting the correct branch (Camel 4 uses the main branch)

Tracking

  • If this is a large change, bug fix, or code improvement, I checked there is a JIRA issue filed for the change (usually before you start working on it): CAMEL-25842.

Apache Camel coding standards and style

  • I checked that each commit in the pull request has a meaningful subject line and body.
  • I have run mvn clean install -DskipTests locally from root folder and I have committed all auto-generated changes.
    (I built and tested camel-xmpp. No option and no component page changed. I did not run the full root build.)

AI-assisted contributions

  • If this PR includes AI-generated code, commits have proper co-authorship attribution (e.g., Co-authored-by trailers) and the PR description identifies the AI tool used.
    This PR was prepared with Claude Code (Claude Opus 5.5). The commit carries a Co-Authored-By trailer.

Claude Code on behalf of allthingssecurity

🤖 Generated with Claude Code

…ain after its connection was lost

When the connection of XmppConsumer is lost, its connection monitor and
Smack's ReconnectionManager (enabled by XmppComponent) both try to
connect it again. The ReconnectionManager logs in after it connected, but
gives up when the monitor connected first, and the monitor only called
connection.connect(), which does not log in. An XMPP server delivers
nothing to a stream that did not log in and bind a resource, so the
consumer then reported a working connection and consumed nothing again
until the route was restarted. And whoever logged in again, the consumer
did not enter its room again, although the server removed it from the
room when the connection was lost.

The monitor now logs the connection in again when it is not authenticated
(XmppEndpoint.login, split out of createConnection; the account is only
created with the first connection; nothing is done when login=false). The
consumer listens for every fresh login of its connection, by the monitor
or by the ReconnectionManager, logs it and enters its room again in the
monitor thread; the monitor tries again if entering fails.

The monitor and the ReconnectionManager can connect and log in the same
connection at the same time: Smack throws AlreadyConnectedException or
AlreadyLoggedInException to the second one. The monitor now takes either
as done by the other one (before, it logged a WARN with a stack trace for
either, and skipped the login after AlreadyConnectedException).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

🌟 Thank you for your contribution to the Apache Camel project! 🌟
🤖 CI automation will test this PR automatically.

🐫 Apache Camel Committers, please review the following items:

  • First-time contributors require MANUAL approval for the GitHub Actions to run
  • You can use the command /component-test (camel-)component-name1 (camel-)component-name2.. to request a test from the test bot although they are normally detected and executed by CI.
  • You can label PRs using skip-tests and test-dependents to fine-tune the checks executed by this PR.
  • Build and test logs are available in the summary page. Only Apache Camel committers have access to the summary.

⚠️ Be careful when sharing logs. Review their contents before sharing them publicly.

@davsclaus davsclaus left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks Shashank,

I checked the Smack 4.4.8 side against the jar: ReconnectionManager returns without logging in when it finds the connection already connected, connect() and login() are synchronized and throw AlreadyConnectedException / AlreadyLoggedInException to the second caller, and MultiUserChat.join leaves first while Smack still thinks it is in the room. So the description holds, and entering the room again from the monitor thread is the right place. Smack's own MultiUserChatManager.setAutoJoinOnReconnect would not be enough here, as it enters again with the nickname only and drops the room password.

  1. Not blocking: the cleanup in XmppConsumerSmackReconnectTest does not restore Smack's defaults. setDefaultFixedDelay also sets the policy to FIXED_DELAY, so calling it after setDefaultReconnectionPolicy(RANDOM_INCREASING_DELAY) leaves later tests in the same JVM with a fixed 15 s delay. See inline.
  2. Not blocking: the producers still only call connect() after a lost connection (no login), and the group chat producer does not enter its room again either. You mention this follow-up on #28262; please file a JIRA for it so it is tracked.
  3. FYI: this PR and #28262 merge without conflicts (both add the same FakeXmppServer). Once both are in, the follow-up that moves checkConnection onto endpoint.reconnect(connection) makes sense, so consumers and producers of an account reconnect under the same lock.

LGTM once the build jobs are green.

Claude Code on behalf of davsclaus. This review was generated by an AI agent and may contain inaccuracies. Please verify all suggestions before applying.

@github-actions

github-actions Bot commented Oct 22, 2026 •

Copy link
Copy Markdown
Contributor

🧪 CI tested the following changed modules:

  • components/camel-xmpp

🔁 1 test passed only after a retry on JDK 25 (1 retried attempt)

Recovered flaky tests on JDK 25 (1)
Module Test Failed attempts First failure
dsl/camel-jbang/camel-jbang-plugin-tui AiPanelAcpTest.usageUpdateFeedsTheTokenCounter 1 1234 + the 66 this turn added ==> expected: <1300> but was: <1234>

ℹ️ These tests did not fail the build. Surefire retried them and they passed.
Retries are enabled project-wide by surefire.rerunFailingTestsCount in parent/pom.xml.


🔬 Scalpel shadow comparison — Scalpel: 8 of 707 tested, 3 compile-only — current: 8 all tested

Maveniverse Scalpel detected 8 affected modules (current approach: 8).

Skip-tests mode would test 8 modules (1 direct + 0 downstream), skip tests for 3 (generated code, meta-modules)

Modules Scalpel would test (8)
  • camel-jbang-mcp ← depends on affected reactor module org.apache.camel:camel-yaml-dsl-validator
  • camel-jbang-plugin-mcp ← depends on affected reactor module org.apache.camel:camel-jbang-core
  • camel-jbang-plugin-tui ← depends on affected reactor module org.apache.camel:camel-yaml-dsl-validator
  • camel-jbang-plugin-validate ← depends on affected reactor module org.apache.camel:camel-yaml-dsl-validator
  • camel-launcher-container ← depends on affected reactor module org.apache.camel:camel-launcher
  • camel-xmpp ← components/camel-xmpp/src/main/java/org/apache/camel/component/xmpp/XmppConsumer.java, components/camel-xmpp/src/main/java/org/apache/camel/component/xmpp/XmppEndpoint.java, components/camel-xmpp/src/test/java/org/apache/camel/component/xmpp/FakeXmppServer.java, components/camel-xmpp/src/test/java/org/apache/camel/component/xmpp/XmppConsumerConnectionRaceTest.java, components/camel-xmpp/src/test/java/org/apache/camel/component/xmpp/XmppConsumerReconnectTest.java, components/camel-xmpp/src/test/java/org/apache/camel/component/xmpp/XmppConsumerSmackReconnectTest.java, own pom components/camel-xmpp/pom.xml changed
  • camel-yaml-dsl-validator ← depends on affected reactor module org.apache.camel:camel-catalog
  • camel-yaml-dsl-validator-maven-plugin ← depends on affected reactor module org.apache.camel:camel-yaml-dsl-validator
Modules with tests skipped (3)
  • camel-itest
  • camel-yaml-dsl
  • camel-yaml-dsl-deserializers

ℹ️ Shadow mode — Scalpel observes but does not affect test execution. Learn more

⚠️ Some tests are disabled on GitHub Actions (@DisabledIfSystemProperty(named = "ci.env.name")) and require manual verification:

  • components/camel-xmpp: 6 test(s) disabled on GitHub Actions
All tested modules (35 modules, 5m 22s total)

Total reactor time: 5m 22s

Module Duration Status
Camel :: Launcher 48.5s SUCCESS
Camel :: JBang :: MCP 39.8s SUCCESS
Camel :: JBang :: Plugin :: Validate 28.4s SUCCESS
Camel :: Component DSL 27.4s SUCCESS
Camel :: YAML DSL :: Validator 23.4s SUCCESS
Camel :: YAML DSL 23.0s SUCCESS
Camel :: Catalog :: Camel Catalog 22.7s SUCCESS
Camel :: Kamelet Main 17.2s SUCCESS
Camel :: Docs 14.7s SUCCESS
Camel :: JBang :: Plugin :: Testing 13.4s SUCCESS
Camel :: JBang :: Plugin :: Kubernetes 11.4s SUCCESS
Camel :: Catalog :: Camel Route Parser 8.3s SUCCESS
Camel :: YAML DSL :: Deserializers 8.0s SUCCESS
Camel :: Catalog :: Camel Report Maven Plugin 7.0s SUCCESS
Camel :: All Components Sync point 5.2s SUCCESS
Camel :: YAML DSL :: Validator Maven Plugin 4.2s SUCCESS
Camel :: YAML DSL :: Maven Plugins 3.6s SUCCESS
Camel :: Catalog :: Maven 2.6s SUCCESS
Camel :: Catalog :: Suggest (deprecated) 1.8s SUCCESS
Camel :: Coverage 1.6s SUCCESS
Camel :: JBang :: Plugin :: Edit 1.5s SUCCESS
Camel :: Catalog :: Dummy Component 1.4s SUCCESS
Camel :: Assembly 1.3s SUCCESS
Camel :: JBang :: Plugin :: Generate 1.2s SUCCESS
Camel :: Catalog :: Console 1.1s SUCCESS
Camel :: JBang :: Integration tests 0.8s SUCCESS
Camel :: Endpoint DSL :: Support 0.8s SUCCESS
Camel :: JBang :: Main 0.7s SUCCESS
Camel :: Launcher :: Container 0.7s SUCCESS
Camel :: JBang :: Plugin :: MCP 0.5s SUCCESS
Camel :: Endpoint DSL n/a
Camel :: Integration Tests n/a
Camel :: JBang :: Core n/a
Camel :: JBang :: Plugin :: TUI n/a
Camel :: XMPP n/a

Top 20 slowest modules:

  • Camel :: Launcher (48.5s)
  • Camel :: JBang :: MCP (39.8s)
  • Camel :: JBang :: Plugin :: Validate (28.4s)
  • Camel :: Component DSL (27.4s)
  • Camel :: YAML DSL :: Validator (23.4s)
  • Camel :: YAML DSL (23.0s)
  • Camel :: Catalog :: Camel Catalog (22.7s)
  • Camel :: Kamelet Main (17.2s)
  • Camel :: Docs (14.7s)
  • Camel :: JBang :: Plugin :: Testing (13.4s)
  • Camel :: JBang :: Plugin :: Kubernetes (11.4s)
  • Camel :: Catalog :: Camel Route Parser (8.3s)
  • Camel :: YAML DSL :: Deserializers (8.0s)
  • Camel :: Catalog :: Camel Report Maven Plugin (7.0s)
  • Camel :: All Components Sync point (5.2s)
  • Camel :: YAML DSL :: Validator Maven Plugin (4.2s)
  • Camel :: YAML DSL :: Maven Plugins (3.6s)
  • Camel :: Catalog :: Maven (2.6s)
  • Camel :: Catalog :: Suggest (deprecated) (1.8s)
  • Camel :: Coverage (1.6s)

⚙️ View full build and test results

… the test cleanup (setDefaultFixedDelay first, as it also sets the policy)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@allthingssecurity

Copy link
Copy Markdown
Contributor Author

Thanks Claus. Pushed 53d929c:

  1. The two ReconnectionManager calls in the test cleanup are swapped, so Smack's defaults are restored (see inline).
  2. The producers after a reconnect: CAMEL-25841: camel-xmpp - do not hand out the endpoint of another participant or user #28262 now makes XmppEndpoint.reconnect log the connection in again (Smack's login() with the credentials it used before). The rest is a follow-up JIRA draft for Shashank to file: a producer that sends between the connect and the login, the group chat producer entering its room again, and the consumer's monitor on the account's lock.
  3. Checked again after the CAMEL-25841: camel-xmpp - do not hand out the endpoint of another participant or user #28262 changes: the two branches still merge without conflicts (git merge-tree).

camel-xmpp: 14 tests, 0 failures, 5 skipped (the tests that need an XMPP server container).

Claude Code on behalf of allthingssecurity

@davsclaus davsclaus left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks for the quick update. The swapped ReconnectionManager calls in the cleanup look right, and the thread is addressed.

One thing still blocks the merge: CI. The run on 83d1734 (https://github.com/apache/camel/actions/runs/39697995006, build (25)) failed in camel-xmpp, and the failure comes from the new tests. They are the first camel-xmpp tests that load Smack on GitHub Actions; the other Smack tests need a container and are disabled there. All five failed on every rerun:

XmppConsumerConnectionRaceTest.connectionLoggedInMeanwhileByTheReconnectionManagerIsNotAFailure:77
  Caused by: java.lang.ExceptionInInitializerError: Exception java.lang.NoClassDefFoundError: org/jxmpp/util/cache/Cache
    at org.jivesoftware.smack.SmackInitialization.loadSmackClass(SmackInitialization.java:210)
    ...
    at org.jivesoftware.smack.AbstractXMPPConnection.<clinit>(AbstractXMPPConnection.java:175)

After that, XmppConsumerReconnectTest, XmppConsumerSmackReconnectTest and the second race test fail with Could not initialize class SmackInitialization / XMPPTCPConnectionConfiguration.

So on CI, jxmpp-util-cache does not end up on the test classpath, even though it does locally. My guess is version ranges plus the CI resolver: smack-core 4.4.8 asks for jxmpp-core/jxmpp-jid in the range [1.0.0,1.0.999], camel-xmpp pins jxmpp-jid to ${jxmpp-version} (1.1.0), and CI builds with mvnd and -Daether.dependencyCollector.impl=bf. Could you compare mvn dependency:tree -Daether.dependencyCollector.impl=bf in components/camel-xmpp with the default resolver? If jxmpp-util-cache drops out, declaring it explicitly next to jxmpp-jid (same ${jxmpp-version}) should fix the tests. It would also guard users whose builds resolve the same way.

The run for 53d929c is still queued. If it comes back green, please ignore this. #28262 adds the same FakeXmppServer and will probably hit the same problem.

Claude Code on behalf of @davsclaus.

This review was generated by an AI agent and may contain inaccuracies. Please verify all suggestions before applying.

… on the test classpath

smack-core 4.4.8 asks for jxmpp-core and jxmpp-jid in the range [1.0.0,1.0.999],
while camel-xmpp pins jxmpp-jid to ${jxmpp-version} (1.1.0). The default resolver
then uses jxmpp 1.0.3 everywhere, but the CI build (mvnd) left jxmpp-util-cache off
the test classpath, so Smack failed to initialize (NoClassDefFoundError:
org/jxmpp/util/cache/Cache). Declaring it explicitly keeps it there for any resolver.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
allthingssecurity added a commit to allthingssecurity/camel that referenced this pull request Oct 22, 2026
… on the test classpath

smack-core 4.4.8 asks for jxmpp-core and jxmpp-jid in the range [1.0.0,1.0.999],
while camel-xmpp pins jxmpp-jid to ${jxmpp-version} (1.1.0). The default resolver
then uses jxmpp 1.0.3 everywhere, but the CI build (mvnd) left jxmpp-util-cache off
the test classpath of apache#28263, so Smack failed to initialize (NoClassDefFoundError:
org/jxmpp/util/cache/Cache). This PR adds Smack tests too; declaring it explicitly
keeps it there for any resolver.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@allthingssecurity

Copy link
Copy Markdown
Contributor Author

Thanks Claus, good catch. Fixed in d59f575 (and the same change in #28262, 252c279).

I compared the trees in components/camel-xmpp: locally the default resolver and -Daether.dependencyCollector.impl=bf give the same result, and both have jxmpp-util-cache. The verbose tree shows why the versions are odd: smack-core 4.4.8 asks for jxmpp-core and jxmpp-jid in the range [1.0.0,1.0.999], so our direct jxmpp-jid:1.1.0 is "omitted for conflict with 1.0.3" and the build really uses jxmpp 1.0.3 (the SBOM agrees). So I could not reproduce the CI classpath here, but org/jxmpp/util/cache/Cache only comes in through that range-resolved jxmpp-core, which fits your guess about mvnd's resolver.

The commit declares jxmpp-util-cache next to jxmpp-jid with the same ${jxmpp-version}, so it is on the classpath whatever the resolver does. With it the tree has jxmpp-jid/jxmpp-core 1.0.3 and jxmpp-util-cache 1.1.0 (the Cache classes are the same in both). camel-xmpp unit tests with -Daether.dependencyCollector.impl=bf: 14 run, 0 failures, 5 skipped here (20 run on #28262).

Separately, the 1.1.0 in jxmpp-version has never been used because of Smack's range; aligning the property with what Smack 4.4.x accepts would be a follow-up, not this PR.

Claude Code on behalf of allthingssecurity

@davsclaus davsclaus left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks Shashank, declaring jxmpp-util-cache explicitly is the right answer to the CI failure. It is on the classpath whatever the resolver does. I compared the 1.0.3 and 1.1.0 jars, and the org.jxmpp.util.cache API is the same, so running it next to jxmpp-core 1.0.3 is fine. It merges cleanly with #28262, which adds the same lines.

Optional: as you noted, jxmpp-version (1.1.0) is never used for jxmpp-core and jxmpp-jid because of Smack's range. Please add that to the follow-up JIRA so it gets aligned.

Approving, to merge once CI is green.

Claude Code on behalf of @davsclaus. This review was generated by an AI agent and may contain inaccuracies. Please verify all suggestions before applying.

@allthingssecurity

Copy link
Copy Markdown
Contributor Author

Follow-up JIRA, with the jxmpp-version alignment added: CAMEL-25961.

Claude Code on behalf of allthingssecurity

@davsclaus davsclaus added this to the 4.24.0 milestone Oct 22, 2026
@davsclaus davsclaus added the bug Something isn't working label Oct 22, 2026
@davsclaus
davsclaus merged commit 3856bca into apache:main Oct 22, 2026
6 checks passed
@allthingssecurity
allthingssecurity deleted the camel-xmpp-consumer-login-after-reconnect branch October 23, 2026 01:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working components

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants