allthingssecurity commented on code in PR #28262:
URL: https://github.com/apache/camel/pull/28262#discussion_r4345214505


##########
components/camel-xmpp/src/main/java/org/apache/camel/component/xmpp/XmppComponent.java:
##########
@@ -92,14 +93,111 @@ protected void doStart() throws Exception {
 
     @Override
     protected void doStop() throws Exception {
-        ServiceHelper.stopService(endpointCache.values());
-        endpointCache.clear();
+        List<SharedConnection> remaining;
+        synchronized (connections) {
+            remaining = List.copyOf(connections.values());
+            connections.clear();
+        }
+        for (SharedConnection shared : remaining) {
+            shared.disconnect();
+        }
 
         super.doStop();
     }
 
-    private String extractCacheKeyFromUri(String uri) throws 
URISyntaxException {
-        URI u = new URI(uri);
-        return u.getScheme() + "://" + u.getHost() + u.getPort() + 
u.getQuery();
+    /**
+     * Gets the connection of the endpoint's account for the given consumer or 
producer, and creates it when the account
+     * has no connection yet or its connection is no longer connected. The 
connection is shared with the other consumers
+     * and producers of the account, also of other endpoints; release it with
+     * {@link #releaseConnection(XmppEndpoint, Object)}.
+     */
+    XMPPTCPConnection acquireConnection(XmppEndpoint endpoint, Object user)
+            throws InterruptedException, IOException, SmackException, 
XMPPException {
+        List<Object> key = endpoint.getConnectionKey();
+        SharedConnection shared;
+        synchronized (connections) {
+            shared = connections.computeIfAbsent(key, k -> new 
SharedConnection());
+            shared.users.add(user);
+        }
+        boolean acquired = false;
+        shared.lock.lock();
+        try {
+            if (shared.connection == null || !shared.connection.isConnected()) 
{
+                shared.connection = endpoint.createConnection();

Review Comment:
   Done in 81f6fe21a538. `acquireConnection` creates a connection only when the 
account has none. Otherwise it connects and logs in the existing object under 
the account's lock (`connectAndLogin`). `reconnect` uses the same helper now. 
`AlreadyConnectedException` and `AlreadyLoggedInException` are tolerated. The 
login uses Smack's `login()`, which logs in again with the user, password and 
resource the connection used before (checked in the 4.4.8 jar: `login()` takes 
`usedUsername`/`usedPassword`/`usedResource` before the configuration's 
values). So it does not need the `login` helper of #28263, and the two PRs 
still merge cleanly. New test 
`connectionAcquiredDuringAnOutageIsTheConnectionOfTheAccount`: the server drops 
the session, something of the account acquires during the outage, it gets the 
same object, logged in again, and the producer that held it delivers again with 
no `conflict`. With the old code it fails on `assertSame`. Since there is never 
a second object now, `releaseCon
 nection` disconnects the only one. It now also calls `disconnect()` on a 
connection that is not connected, so Smack's `ReconnectionManager` stops 
(`connectionClosed` sets its `done` flag); before, a dropped connection 
released by its last user was reconnected later with nobody using it.
   
   _Claude Code on behalf of allthingssecurity_
   



##########
components/camel-xmpp/src/main/java/org/apache/camel/component/xmpp/XmppConsumer.java:
##########
@@ -187,8 +187,16 @@ protected void doStop() throws Exception {
             muc.leave();
             muc = null;
         }
-        if (connection != null && connection.isConnected()) {
-            connection.disconnect();
+        if (connection != null) {
+            // the connection is shared with the other consumers and producers 
of the account: stop listening on it,
+            // and release it (it is closed when nobody uses it any more)
+            if (chatManager != null) {
+                chatManager.removeIncomingListener(this);
+                chatManager = null;
+            }
+            connection.removeSyncStanzaListener(this);
+            endpoint.releaseConnection(this);

Review Comment:
   Done in 81f6fe21a538. In `XmppConsumer.doStop`, `muc.leave()` is in a `try` 
whose `finally` removes the listeners (chat manager and stanza listener) and 
releases the connection. The exception from `leave()` still propagates, and the 
route stop logs it as before. `XmppGroupChatProducer.doStop` does the same 
around `chat.leave()`. New tests 
`consumerThatCannotLeaveItsRoomReleasesTheConnection` and 
`groupChatProducerThatCannotLeaveItsRoomReleasesTheConnection` drop the server 
connection, stop the route (so `leave()` throws `NotConnectedException`) and 
check that the account has no users left (a package-private 
`XmppComponent.connectionUsers`, for tests).
   
   _Claude Code on behalf of allthingssecurity_
   



##########
components/camel-xmpp/src/main/java/org/apache/camel/component/xmpp/XmppEndpoint.java:
##########
@@ -177,18 +177,39 @@ protected String createEndpointUri() {
         return "xmpp://" + host + ":" + port + "/" + getParticipant() + 
"?serviceName=" + serviceName;
     }
 
+    /**
+     * Gets the connection of this endpoint's account for the given consumer 
or producer. The endpoints of one account
+     * share its connection (a server keeps one session per account and 
resource); each endpoint keeps its own
+     * participant, room and other settings. Release it with {@link 
#releaseConnection(Object)} when the consumer or
+     * producer stops: the connection is closed when no consumer or producer 
of the account uses it any more.
+     */
+    public XMPPTCPConnection acquireConnection(Object user)
+            throws InterruptedException, IOException, SmackException, 
XMPPException {
+        return getComponent().acquireConnection(this, user);
+    }
+
+    /**
+     * Releases the connection that the given consumer or producer acquired 
with {@link #acquireConnection(Object)}.
+     */
+    public void releaseConnection(Object user) {
+        getComponent().releaseConnection(this, user);
+    }
+
+    /**
+     * The settings that make up the connection of an account: the endpoints 
with the same settings share a connection.
+     */
+    List<Object> getConnectionKey() {
+        return Arrays.asList(host, port == 0 ? 5222 : port, serviceName, user, 
password, resource, login, connectionConfig);

Review Comment:
   Covered by the javadoc of `getConnectionKey` and the "Connections" section 
of the component page (endpoints share a connection only when they refer to the 
same `ConnectionConfiguration` instance). The new upgrade-guide entry says it 
too. I am leaving this thread for you to resolve.
   
   _Claude Code on behalf of allthingssecurity_
   



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to