From 10c6e5d121bec0e0cabef5c12e0585019228529f Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Tue, 26 May 2020 09:53:43 +0200 Subject: [PATCH 01/28] [openpgp] Use default constructor of DummyConnection in unit tests --- .../smackx/ox_im/OXInstantMessagingManagerTest.java | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/smack-openpgp/src/test/java/org/jivesoftware/smackx/ox_im/OXInstantMessagingManagerTest.java b/smack-openpgp/src/test/java/org/jivesoftware/smackx/ox_im/OXInstantMessagingManagerTest.java index fbf33e6a3..e7106820a 100644 --- a/smack-openpgp/src/test/java/org/jivesoftware/smackx/ox_im/OXInstantMessagingManagerTest.java +++ b/smack-openpgp/src/test/java/org/jivesoftware/smackx/ox_im/OXInstantMessagingManagerTest.java @@ -56,7 +56,6 @@ import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.jupiter.api.Test; import org.jxmpp.jid.EntityBareJid; -import org.jxmpp.jid.JidTestUtil; import org.pgpainless.decryption_verification.OpenPgpMetadata; public class OXInstantMessagingManagerTest extends SmackTestSuite { @@ -71,16 +70,10 @@ public class OXInstantMessagingManagerTest extends SmackTestSuite { public void test() throws IOException, PGPException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, NoSuchProviderException, SmackException, MissingUserIdOnKeyException, InterruptedException, XMPPException, XmlPullParserException { - DummyConnection aliceCon = new DummyConnection( - DummyConnection.DummyConnectionConfiguration.builder() - .setXmppDomain(JidTestUtil.EXAMPLE_ORG) - .setUsernameAndPassword("alice", "dummypass").build()); + DummyConnection aliceCon = new DummyConnection(); aliceCon.connect().login(); - DummyConnection bobCon = new DummyConnection( - DummyConnection.DummyConnectionConfiguration.builder() - .setXmppDomain(JidTestUtil.EXAMPLE_ORG) - .setUsernameAndPassword("bob", "dummypass").build()); + DummyConnection bobCon = new DummyConnection(); bobCon.connect().login(); FileBasedOpenPgpStore aliceStore = new FileBasedOpenPgpStore(new File(basePath, "alice")); From eae8acb856075a3d43a73a42c76591e9489c0d17 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Tue, 26 May 2020 09:54:06 +0200 Subject: [PATCH 02/28] [gradle] Add 'sinttestAll' task --- build.gradle | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/build.gradle b/build.gradle index 96dcab393..0bdce4c74 100644 --- a/build.gradle +++ b/build.gradle @@ -662,6 +662,14 @@ task omemoSignalIntTest { dependsOn project(':smack-omemo-signal-integration-test').tasks.run } +task sinttestAll { + description 'Run all of Smack\'s integration tests.' + dependsOn {[ + integrationTest, + omemoSignalIntTest, + ]} +} + def getGitCommit() { def dotGit = new File("$projectDir/.git") if (!dotGit.isDirectory()) return 'non-git build' From 223d58527b053d606b8980766c0aedd749c58663 Mon Sep 17 00:00:00 2001 From: Aditya Borikar Date: Thu, 28 May 2020 03:29:37 +0530 Subject: [PATCH 03/28] Use correct class name in docs XMPPConnectionConfiguration/XMPPTCPConnectionConfiguration --- .../jivesoftware/smack/tcp/XMPPTCPConnectionConfiguration.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XMPPTCPConnectionConfiguration.java b/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XMPPTCPConnectionConfiguration.java index aaccfacb6..e4bb64217 100644 --- a/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XMPPTCPConnectionConfiguration.java +++ b/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XMPPTCPConnectionConfiguration.java @@ -26,7 +26,7 @@ import org.jivesoftware.smack.ConnectionConfiguration; *

*
  * {@code
- * XMPPTCPConnectionConfiguration conf = XMPPConnectionConfiguration.builder()
+ * XMPPTCPConnectionConfiguration conf = XMPPTCPConnectionConfiguration.builder()
  *     .setXmppDomain("example.org").setUsernameAndPassword("user", "password")
  *     .setCompressionEnabled(false).build();
  * XMPPTCPConnection connection = new XMPPTCPConnection(conf);

From b1a4ccfae8999b900947029ad72f819fa2918328 Mon Sep 17 00:00:00 2001
From: Florian Schmaus 
Date: Sat, 30 May 2020 19:37:48 +0200
Subject: [PATCH 04/28] [core] Do not weakly reference "channel selected"
 callback

Since d65f2c932 ("Bump Error Prone version to 2.3.4 and fix new bug
patterns") the channel selected callback is no longer a final field of
the connection instance, hence it may be come null even if the
connection instance is still strongly referenced. Also the
ConnectionAttemptState class uses simply a lambda as callback, which
is also not strongly referenced otherwise.

The "channel selected" callback was wrapped in weak reference, so that
connection instances could get gc'ed if they are still connected but
the user lost all references to them. In this case, the weak reference
to the connection instance would become 'null' and
selectionKey.cancel() would be called.

This change means that a socket and its selection key of a "leaked"
connected connection instance continues to be part of the reactor. But
this may not be that bad: first, users are expected to manager their
connection instances, and disconnect them before they are
discarded. And secondly, at some point the connection likely will get
disconnected, and in this case, the socket and its selection key will
be removed from the reactor.
---
 .../java/org/jivesoftware/smack/SmackReactor.java  | 14 ++++----------
 1 file changed, 4 insertions(+), 10 deletions(-)

diff --git a/smack-core/src/main/java/org/jivesoftware/smack/SmackReactor.java b/smack-core/src/main/java/org/jivesoftware/smack/SmackReactor.java
index eaff9ca3d..75e1af8e9 100644
--- a/smack-core/src/main/java/org/jivesoftware/smack/SmackReactor.java
+++ b/smack-core/src/main/java/org/jivesoftware/smack/SmackReactor.java
@@ -17,7 +17,6 @@
 package org.jivesoftware.smack;
 
 import java.io.IOException;
-import java.lang.ref.WeakReference;
 import java.nio.channels.CancelledKeyException;
 import java.nio.channels.ClosedChannelException;
 import java.nio.channels.SelectableChannel;
@@ -368,13 +367,8 @@ public class SmackReactor {
         for (SelectionKey selectionKey : selectedKeys) {
             SelectableChannel channel = selectionKey.channel();
             SelectionKeyAttachment selectionKeyAttachment = (SelectionKeyAttachment) selectionKey.attachment();
-            ChannelSelectedCallback channelSelectedCallback = selectionKeyAttachment.weaeklyReferencedChannelSelectedCallback.get();
-            if (channelSelectedCallback != null) {
-                channelSelectedCallback.onChannelSelected(channel, selectionKey);
-            }
-            else {
-                selectionKey.cancel();
-            }
+            ChannelSelectedCallback channelSelectedCallback = selectionKeyAttachment.channelSelectedCallback;
+            channelSelectedCallback.onChannelSelected(channel, selectionKey);
         }
     }
 
@@ -422,11 +416,11 @@ public class SmackReactor {
     }
 
     public static final class SelectionKeyAttachment {
-        private final WeakReference weaeklyReferencedChannelSelectedCallback;
+        private final ChannelSelectedCallback channelSelectedCallback;
         private final AtomicBoolean reactorThreadRacing = new AtomicBoolean();
 
         private SelectionKeyAttachment(ChannelSelectedCallback channelSelectedCallback) {
-            this.weaeklyReferencedChannelSelectedCallback = new WeakReference<>(channelSelectedCallback);
+            this.channelSelectedCallback = channelSelectedCallback;
         }
 
         private void setRacing() {

From d639e0bc4c345a59c8356c47df84d95d24b7aaec Mon Sep 17 00:00:00 2001
From: Aditya Borikar 
Date: Sun, 31 May 2020 01:10:29 +0530
Subject: [PATCH 05/28] Some more docFix es

---
 .../smack/c2s/ModularXmppClientToServerConnection.java          | 2 +-
 .../main/java/org/jivesoftware/smack/fsm/StateDescriptor.java   | 2 +-
 .../java/org/jivesoftware/smack/tcp/XmppTcpTransportModule.java | 2 +-
 3 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/smack-core/src/main/java/org/jivesoftware/smack/c2s/ModularXmppClientToServerConnection.java b/smack-core/src/main/java/org/jivesoftware/smack/c2s/ModularXmppClientToServerConnection.java
index 061d25e7a..f272a366b 100644
--- a/smack-core/src/main/java/org/jivesoftware/smack/c2s/ModularXmppClientToServerConnection.java
+++ b/smack-core/src/main/java/org/jivesoftware/smack/c2s/ModularXmppClientToServerConnection.java
@@ -353,7 +353,7 @@ public final class ModularXmppClientToServerConnection extends AbstractXMPPConne
     }
 
     /**
-     * Attempt to enter a state. Note that this method may return null if this state can be safely ignored ignored.
+     * Attempt to enter a state. Note that this method may return null if this state can be safely ignored.
      *
      * @param successorStateVertex the successor state vertex.
      * @param walkStateGraphContext the "walk state graph" context.
diff --git a/smack-core/src/main/java/org/jivesoftware/smack/fsm/StateDescriptor.java b/smack-core/src/main/java/org/jivesoftware/smack/fsm/StateDescriptor.java
index 0ef98885c..aec14b18e 100644
--- a/smack-core/src/main/java/org/jivesoftware/smack/fsm/StateDescriptor.java
+++ b/smack-core/src/main/java/org/jivesoftware/smack/fsm/StateDescriptor.java
@@ -212,7 +212,7 @@ public abstract class StateDescriptor {
     protected State constructState(ModularXmppClientToServerConnectionInternal connectionInternal) {
         ModularXmppClientToServerConnection connection = connectionInternal.connection;
         try {
-            // If stateClassConstructor is null here, then you probably forgot to override the the
+            // If stateClassConstructor is null here, then you probably forgot to override the
             // StateDescriptor.constructState() method?
             return stateClassConstructor.newInstance(connection, this, connectionInternal);
         } catch (InstantiationException | IllegalAccessException | IllegalArgumentException
diff --git a/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XmppTcpTransportModule.java b/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XmppTcpTransportModule.java
index 74286fdd9..fd630ae6c 100644
--- a/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XmppTcpTransportModule.java
+++ b/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XmppTcpTransportModule.java
@@ -591,7 +591,7 @@ public class XmppTcpTransportModule extends ModularXmppClientToServerConnectionM
 
         @Override
         protected List> lookupConnectionEndpoints() {
-            // Assert that there are no stale discovred endpoints prior performing the lookup.
+            // Assert that there are no stale discovered endpoints prior performing the lookup.
             assert discoveredTcpEndpoints == null;
 
             List> futures = new ArrayList<>(2);

From 57961a8cc1f2df6ecc1afa8c4f8460794d8d2dce Mon Sep 17 00:00:00 2001
From: Florian Schmaus 
Date: Tue, 26 May 2020 21:39:08 +0200
Subject: [PATCH 06/28] Remove SynchronizationPoint

This continues the design started with e98d42790 ("SmackReactor/NIO,
Java8/Android19, Pretty print XML, FSM connections"), where the
exceptions that caused an operation to fail, are not recorded within
SynchronizationPoint but within the connection instance itself.
---
 .../smack/AbstractXMPPConnection.java         | 163 +++++---
 .../jivesoftware/smack/SmackException.java    |  25 +-
 .../smack/SynchronizationPoint.java           | 352 ------------------
 .../smack/XmppInputOutputFilter.java          |   3 +-
 .../ModularXmppClientToServerConnection.java  |  84 ++---
 ...rXmppClientToServerConnectionInternal.java |  10 +-
 .../smack/compression/CompressionModule.java  |   9 +-
 .../org/jivesoftware/smack/fsm/State.java     |   7 +-
 .../smack/fsm/StateTransitionResult.java      |   8 +
 .../org/jivesoftware/smack/util/Supplier.java |  24 ++
 .../WaitForClosingStreamElementTest.java      |   9 +-
 .../smack/tcp/ConnectionAttemptState.java     |  58 ++-
 .../smack/tcp/XMPPTCPConnection.java          | 217 +++++------
 .../smack/tcp/XmppTcpTransportModule.java     |  63 +---
 .../smack/tcp/PacketWriterTest.java           |   4 +-
 15 files changed, 352 insertions(+), 684 deletions(-)
 delete mode 100644 smack-core/src/main/java/org/jivesoftware/smack/SynchronizationPoint.java
 create mode 100644 smack-core/src/main/java/org/jivesoftware/smack/util/Supplier.java

diff --git a/smack-core/src/main/java/org/jivesoftware/smack/AbstractXMPPConnection.java b/smack-core/src/main/java/org/jivesoftware/smack/AbstractXMPPConnection.java
index 136976a09..2d0f7c693 100644
--- a/smack-core/src/main/java/org/jivesoftware/smack/AbstractXMPPConnection.java
+++ b/smack-core/src/main/java/org/jivesoftware/smack/AbstractXMPPConnection.java
@@ -111,6 +111,7 @@ import org.jivesoftware.smack.util.PacketParserUtils;
 import org.jivesoftware.smack.util.ParserUtils;
 import org.jivesoftware.smack.util.Predicate;
 import org.jivesoftware.smack.util.StringUtils;
+import org.jivesoftware.smack.util.Supplier;
 import org.jivesoftware.smack.xml.XmlPullParser;
 import org.jivesoftware.smack.xml.XmlPullParserException;
 
@@ -174,6 +175,12 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
         SmackConfiguration.getVersion();
     }
 
+    protected enum SyncPointState {
+        initial,
+        request_sent,
+        successful,
+    }
+
     /**
      * A collection of ConnectionListeners which listen for connection closing
      * and reconnection events.
@@ -271,30 +278,29 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
      */
     protected Writer writer;
 
-    protected final SynchronizationPoint tlsHandled = new SynchronizationPoint<>(this, "establishing TLS");
+    protected SmackException currentSmackException;
+    protected XMPPException currentXmppException;
+
+    protected boolean tlsHandled;
 
     /**
-     * Set to success if the last features stanza from the server has been parsed. A XMPP connection
+     * Set to true if the last features stanza from the server has been parsed. A XMPP connection
      * handshake can invoke multiple features stanzas, e.g. when TLS is activated a second feature
      * stanza is send by the server. This is set to true once the last feature stanza has been
      * parsed.
      */
-    protected final SynchronizationPoint lastFeaturesReceived = new SynchronizationPoint<>(
-                    AbstractXMPPConnection.this, "last stream features received from server");
+    protected boolean lastFeaturesReceived;
 
     /**
-     * Set to success if the SASL feature has been received.
+     * Set to true if the SASL feature has been received.
      */
-    protected final SynchronizationPoint saslFeatureReceived = new SynchronizationPoint<>(
-                    AbstractXMPPConnection.this, "SASL mechanisms stream feature from server");
-
+    protected boolean saslFeatureReceived;
 
     /**
      * A synchronization point which is successful if this connection has received the closing
      * stream element from the remote end-point, i.e. the server.
      */
-    protected final SynchronizationPoint closingStreamReceived = new SynchronizationPoint<>(
-                    this, "stream closing element received");
+    protected boolean closingStreamReceived;
 
     /**
      * The SASLAuthentication manager that is responsible for authenticating with the server.
@@ -369,8 +375,6 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
      */
     protected boolean wasAuthenticated = false;
 
-    protected Exception currentConnectionException;
-
     private final Map setIqRequestHandler = new HashMap<>();
     private final Map getIqRequestHandler = new HashMap<>();
 
@@ -486,10 +490,10 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
     public abstract boolean isUsingCompression();
 
     protected void initState() {
-        saslFeatureReceived.init();
-        lastFeaturesReceived.init();
-        tlsHandled.init();
-        // TODO: We do not init() closingStreamReceived here, as the integration tests use it to check if we waited for
+        currentSmackException = null;
+        currentXmppException = null;
+        saslFeatureReceived = lastFeaturesReceived = tlsHandled = false;
+        // TODO: We do not init closingStreamReceived here, as the integration tests use it to check if we waited for
         // it.
     }
 
@@ -512,7 +516,7 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
 
         // Reset the connection state
         initState();
-        closingStreamReceived.init();
+        closingStreamReceived = false;
         streamId = null;
 
         try {
@@ -657,15 +661,82 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
         return streamId;
     }
 
-    protected Resourcepart bindResourceAndEstablishSession(Resourcepart resource) throws XMPPErrorException,
-                    SmackException, InterruptedException {
+    protected final void throwCurrentConnectionException() throws SmackException, XMPPException {
+        if (currentSmackException != null) {
+            throw currentSmackException;
+        } else if (currentXmppException != null) {
+            throw currentXmppException;
+        }
 
+        throw new AssertionError("No current connection exception set, although throwCurrentException() was called");
+    }
+
+    protected final boolean hasCurrentConnectionException() {
+        return currentSmackException != null || currentXmppException != null;
+    }
+
+    protected final void setCurrentConnectionExceptionAndNotify(Exception exception) {
+        if (exception instanceof SmackException) {
+            currentSmackException = (SmackException) exception;
+        } else if (exception instanceof XMPPException) {
+            currentXmppException = (XMPPException) exception;
+        } else {
+            currentSmackException = new SmackException.SmackWrappedException(exception);
+        }
+
+        notifyWaitingThreads();
+    }
+
+    /**
+     * We use an extra object for {@link #notifyWaitingThreads()} and {@link #waitForCondition(Supplier)}, because all state
+     * changing methods of the connection are synchronized using the connection instance as monitor. If we now would
+     * also use the connection instance for the internal process to wait for a condition, the {@link Object#wait()}
+     * would leave the monitor when it waites, which would allow for another potential call to a state changing function
+     * to proceed.
+     */
+    private final Object internalMonitor = new Object();
+
+    protected final void notifyWaitingThreads() {
+        synchronized (internalMonitor) {
+            internalMonitor.notifyAll();
+        }
+    }
+
+    protected final boolean waitForCondition(Supplier condition) throws InterruptedException {
+        final long deadline = System.currentTimeMillis() + getReplyTimeout();
+        synchronized (internalMonitor) {
+            while (!condition.get().booleanValue() && !hasCurrentConnectionException()) {
+                final long now = System.currentTimeMillis();
+                if (now >= deadline) {
+                    return false;
+                }
+                internalMonitor.wait(deadline - now);
+            }
+        }
+        return true;
+    }
+
+    protected final void waitForCondition(Supplier condition, String waitFor) throws InterruptedException, NoResponseException {
+        boolean success = waitForCondition(condition);
+        if (!success) {
+            throw NoResponseException.newWith(this, waitFor);
+        }
+    }
+
+    protected final void waitForConditionOrThrowConnectionException(Supplier condition, String waitFor) throws InterruptedException, SmackException, XMPPException {
+        waitForCondition(condition, waitFor);
+        if (hasCurrentConnectionException()) {
+            throwCurrentConnectionException();
+        }
+    }
+
+    protected Resourcepart bindResourceAndEstablishSession(Resourcepart resource)
+                    throws SmackException, InterruptedException, XMPPException {
         // Wait until either:
         // - the servers last features stanza has been parsed
         // - the timeout occurs
         LOGGER.finer("Waiting for last features to be received before continuing with resource binding");
-        lastFeaturesReceived.checkIfSuccessOrWaitOrThrow();
-
+        waitForConditionOrThrowConnectionException(() -> lastFeaturesReceived, "last stream features received from server");
 
         if (!hasFeature(Bind.ELEMENT, Bind.NAMESPACE)) {
             // Server never offered resource binding, which is REQUIRED in XMPP client and
@@ -905,29 +976,20 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
             return;
         }
 
+        // TODO: Remove this async but ordered?
         ASYNC_BUT_ORDERED.performAsyncButOrdered(this, () -> {
-            currentConnectionException = exception;
+            // Note that we first have to set the current connection exception and notify waiting threads, as one of them
+            // could hold the instance lock, which we also need later when calling instantShutdown().
+            setCurrentConnectionExceptionAndNotify(exception);
+
+            // Closes the connection temporary. A if the connection supports stream management, then a reconnection is
+            // possible. Note that a connection listener of e.g. XMPPTCPConnection will drop the SM state in
+            // case the Exception is a StreamErrorException.
+            instantShutdown();
 
             for (StanzaCollector collector : collectors) {
                 collector.notifyConnectionError(exception);
             }
-            SmackWrappedException smackWrappedException = new SmackWrappedException(exception);
-            tlsHandled.reportGenericFailure(smackWrappedException);
-            saslFeatureReceived.reportGenericFailure(smackWrappedException);
-            lastFeaturesReceived.reportGenericFailure(smackWrappedException);
-            closingStreamReceived.reportFailure(smackWrappedException);
-            // TODO From XMPPTCPConnection. Was called in Smack 4.3 where notifyConnectionError() was part of
-            // XMPPTCPConnection. Create delegation method?
-            // maybeCompressFeaturesReceived.reportGenericFailure(smackWrappedException);
-
-            synchronized (AbstractXMPPConnection.this) {
-                notifyAll();
-
-                // Closes the connection temporary. A if the connection supports stream management, then a reconnection is
-                // possible. Note that a connection listener of e.g. XMPPTCPConnection will drop the SM state in
-                // case the Exception is a StreamErrorException.
-                instantShutdown();
-            }
 
             Async.go(() -> {
                 // Notify connection listeners of the error.
@@ -947,19 +1009,13 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
     protected abstract void shutdown();
 
     protected final boolean waitForClosingStreamTagFromServer() {
-        Exception exception;
         try {
-            // After we send the closing stream element, check if there was already a
-            // closing stream element sent by the server or wait with a timeout for a
-            // closing stream element to be received from the server.
-            exception = closingStreamReceived.checkIfSuccessOrWait();
-        } catch (InterruptedException | NoResponseException e) {
-            exception = e;
+            waitForConditionOrThrowConnectionException(() -> closingStreamReceived, "closing stream tag from the server");
+        } catch (InterruptedException | SmackException | XMPPException e) {
+            LOGGER.log(Level.INFO, "Exception while waiting for closing stream element from the server " + this, e);
+            return false;
         }
-        if (exception != null) {
-            LOGGER.log(Level.INFO, "Exception while waiting for closing stream element from the server " + this, exception);
-        }
-        return exception == null;
+        return true;
     }
 
     @Override
@@ -1817,8 +1873,8 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
             // Only proceed with SASL auth if TLS is disabled or if the server doesn't announce it
             if (!hasFeature(StartTls.ELEMENT, StartTls.NAMESPACE)
                             || config.getSecurityMode() == SecurityMode.disabled) {
-                tlsHandled.reportSuccess();
-                saslFeatureReceived.reportSuccess();
+                tlsHandled = saslFeatureReceived = true;
+                notifyWaitingThreads();
             }
         }
 
@@ -1829,7 +1885,8 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
                             || !config.isCompressionEnabled()) {
                 // This was was last features from the server is either it did not contain
                 // compression or if we disabled it
-                lastFeaturesReceived.reportSuccess();
+                lastFeaturesReceived = true;
+                notifyWaitingThreads();
             }
         }
         afterFeaturesReceived();
diff --git a/smack-core/src/main/java/org/jivesoftware/smack/SmackException.java b/smack-core/src/main/java/org/jivesoftware/smack/SmackException.java
index f58ed10a4..3d2c0b851 100644
--- a/smack-core/src/main/java/org/jivesoftware/smack/SmackException.java
+++ b/smack-core/src/main/java/org/jivesoftware/smack/SmackException.java
@@ -16,6 +16,7 @@
  */
 package org.jivesoftware.smack;
 
+import java.security.cert.CertificateException;
 import java.util.Collections;
 import java.util.List;
 
@@ -371,15 +372,6 @@ public abstract class SmackException extends Exception {
         }
     }
 
-    public static class ConnectionUnexpectedTerminatedException extends SmackException {
-
-        private static final long serialVersionUID = 1L;
-
-        public ConnectionUnexpectedTerminatedException(Throwable wrappedThrowable) {
-            super(wrappedThrowable);
-        }
-    }
-
     public static class FeatureNotSupportedException extends SmackException {
 
         /**
@@ -487,4 +479,19 @@ public abstract class SmackException extends Exception {
             super(message, exception);
         }
     }
+
+    public static class SmackCertificateException extends SmackException {
+
+        private static final long serialVersionUID = 1L;
+
+        private final CertificateException certificateException;
+
+        public SmackCertificateException(CertificateException certificateException) {
+            this.certificateException = certificateException;
+        }
+
+        public CertificateException getCertificateException() {
+            return certificateException;
+        }
+    }
 }
diff --git a/smack-core/src/main/java/org/jivesoftware/smack/SynchronizationPoint.java b/smack-core/src/main/java/org/jivesoftware/smack/SynchronizationPoint.java
deleted file mode 100644
index 7652a48e0..000000000
--- a/smack-core/src/main/java/org/jivesoftware/smack/SynchronizationPoint.java
+++ /dev/null
@@ -1,352 +0,0 @@
-/**
- *
- * Copyright © 2014-2019 Florian Schmaus
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.jivesoftware.smack;
-
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.locks.Condition;
-import java.util.concurrent.locks.Lock;
-
-import org.jivesoftware.smack.SmackException.NoResponseException;
-import org.jivesoftware.smack.SmackException.NotConnectedException;
-import org.jivesoftware.smack.SmackException.SmackWrappedException;
-import org.jivesoftware.smack.packet.Nonza;
-import org.jivesoftware.smack.packet.Stanza;
-import org.jivesoftware.smack.packet.TopLevelStreamElement;
-
-public class SynchronizationPoint {
-
-    private final AbstractXMPPConnection connection;
-    private final Lock connectionLock;
-    private final Condition condition;
-    private final String waitFor;
-
-    // Note that there is no need to make 'state' and 'failureException' volatile. Since 'lock' and 'unlock' have the
-    // same memory synchronization effects as synchronization block enter and leave.
-    private State state;
-    private E failureException;
-    private SmackWrappedException smackWrappedExcpetion;
-
-    private volatile long waitStart;
-
-    /**
-     * Construct a new synchronization point for the given connection.
-     *
-     * @param connection the connection of this synchronization point.
-     * @param waitFor a description of the event this synchronization point handles.
-     */
-    public SynchronizationPoint(AbstractXMPPConnection connection, String waitFor) {
-        this.connection = connection;
-        this.connectionLock = connection.getConnectionLock();
-        this.condition = connection.getConnectionLock().newCondition();
-        this.waitFor = waitFor;
-        init();
-    }
-
-    /**
-     * Initialize (or reset) this synchronization point.
-     */
-    @SuppressWarnings("LockNotBeforeTry")
-    public void init() {
-        connectionLock.lock();
-        state = State.Initial;
-        failureException = null;
-        smackWrappedExcpetion = null;
-        connectionLock.unlock();
-    }
-
-    /**
-     * Send the given top level stream element and wait for a response.
-     *
-     * @param request the plain stream element to send.
-     * @throws NoResponseException if no response was received.
-     * @throws NotConnectedException if the connection is not connected.
-     * @throws InterruptedException if the connection is interrupted.
-     * @return null if synchronization point was successful, or the failure Exception.
-     */
-    public Exception sendAndWaitForResponse(TopLevelStreamElement request) throws NoResponseException,
-                    NotConnectedException, InterruptedException {
-        assert state == State.Initial;
-        connectionLock.lock();
-        try {
-            if (request != null) {
-                if (request instanceof Stanza) {
-                    connection.sendStanza((Stanza) request);
-                }
-                else if (request instanceof Nonza) {
-                    connection.sendNonza((Nonza) request);
-                } else {
-                    throw new IllegalStateException("Unsupported element type");
-                }
-                state = State.RequestSent;
-            }
-            waitForConditionOrTimeout();
-        }
-        finally {
-            connectionLock.unlock();
-        }
-        return checkForResponse();
-    }
-
-    /**
-     * Send the given plain stream element and wait for a response.
-     *
-     * @param request the plain stream element to send.
-     * @throws E if an failure was reported.
-     * @throws NoResponseException if no response was received.
-     * @throws NotConnectedException if the connection is not connected.
-     * @throws InterruptedException if the connection is interrupted.
-     * @throws SmackWrappedException in case of a wrapped exception;
-     */
-    public void sendAndWaitForResponseOrThrow(Nonza request) throws E, NoResponseException,
-                    NotConnectedException, InterruptedException, SmackWrappedException {
-        sendAndWaitForResponse(request);
-        switch (state) {
-        case Failure:
-            throwException();
-            break;
-        default:
-            // Success, do nothing
-        }
-    }
-
-    /**
-     * Check if this synchronization point is successful or wait the connections reply timeout.
-     * @throws NoResponseException if there was no response marking the synchronization point as success or failed.
-     * @throws E if there was a failure
-     * @throws InterruptedException if the connection is interrupted.
-     * @throws SmackWrappedException in case of a wrapped exception;
-     */
-    public void checkIfSuccessOrWaitOrThrow() throws NoResponseException, E, InterruptedException, SmackWrappedException {
-        checkIfSuccessOrWait();
-        if (state == State.Failure) {
-            throwException();
-        }
-    }
-
-    /**
-     * Check if this synchronization point is successful or wait the connections reply timeout.
-     * @throws NoResponseException if there was no response marking the synchronization point as success or failed.
-     * @throws InterruptedException if the calling thread was interrupted.
-     * @return null if synchronization point was successful, or the failure Exception.
-     */
-    public Exception checkIfSuccessOrWait() throws NoResponseException, InterruptedException {
-        connectionLock.lock();
-        try {
-            switch (state) {
-            // Return immediately on success or failure
-            case Success:
-                return null;
-            case Failure:
-                return getException();
-            default:
-                // Do nothing
-                break;
-            }
-            waitForConditionOrTimeout();
-        } finally {
-            connectionLock.unlock();
-        }
-        return checkForResponse();
-    }
-
-    /**
-     * Report this synchronization point as successful.
-     */
-    public void reportSuccess() {
-        connectionLock.lock();
-        try {
-            state = State.Success;
-            condition.signalAll();
-        }
-        finally {
-            connectionLock.unlock();
-        }
-    }
-
-    /**
-     * Deprecated.
-     * @deprecated use {@link #reportFailure(Exception)} instead.
-     */
-    @Deprecated
-    public void reportFailure() {
-        reportFailure(null);
-    }
-
-    /**
-     * Report this synchronization point as failed because of the given exception. The {@code failureException} must be set.
-     *
-     * @param failureException the exception causing this synchronization point to fail.
-     */
-    public void reportFailure(E failureException) {
-        assert failureException != null;
-        connectionLock.lock();
-        try {
-            state = State.Failure;
-            this.failureException = failureException;
-            condition.signalAll();
-        }
-        finally {
-            connectionLock.unlock();
-        }
-    }
-
-    /**
-     * Report this synchronization point as failed because of the given exception. The {@code failureException} must be set.
-     *
-     * @param exception the exception causing this synchronization point to fail.
-     */
-    public void reportGenericFailure(SmackWrappedException exception) {
-        assert exception != null;
-        connectionLock.lock();
-        try {
-            state = State.Failure;
-            this.smackWrappedExcpetion = exception;
-            condition.signalAll();
-        }
-        finally {
-            connectionLock.unlock();
-        }
-    }
-
-    /**
-     * Check if this synchronization point was successful.
-     *
-     * @return true if the synchronization point was successful, false otherwise.
-     */
-    public boolean wasSuccessful() {
-        connectionLock.lock();
-        try {
-            return state == State.Success;
-        }
-        finally {
-            connectionLock.unlock();
-        }
-    }
-
-    public boolean isNotInInitialState() {
-        connectionLock.lock();
-        try {
-            return state != State.Initial;
-        }
-        finally {
-            connectionLock.unlock();
-        }
-    }
-
-    /**
-     * Check if this synchronization point has its request already sent.
-     *
-     * @return true if the request was already sent, false otherwise.
-     */
-    public boolean requestSent() {
-        connectionLock.lock();
-        try {
-            return state == State.RequestSent;
-        }
-        finally {
-            connectionLock.unlock();
-        }
-    }
-
-    public E getFailureException() {
-        connectionLock.lock();
-        try {
-            return failureException;
-        }
-        finally {
-            connectionLock.unlock();
-        }
-    }
-
-    public void resetTimeout() {
-        waitStart = System.currentTimeMillis();
-    }
-
-    /**
-     * Wait for the condition to become something else as {@link State#RequestSent} or {@link State#Initial}.
-     * {@link #reportSuccess()}, {@link #reportFailure()} and {@link #reportFailure(Exception)} will either set this
-     * synchronization point to {@link State#Success} or {@link State#Failure}. If none of them is set after the
-     * connections reply timeout, this method will set the state of {@link State#NoResponse}.
-     * @throws InterruptedException if the calling thread was interrupted.
-     */
-    private void waitForConditionOrTimeout() throws InterruptedException {
-        waitStart = System.currentTimeMillis();
-        while (state == State.RequestSent || state == State.Initial) {
-            long timeout = connection.getReplyTimeout();
-            long remainingWaitMillis = timeout - (System.currentTimeMillis() - waitStart);
-            long remainingWait = TimeUnit.MILLISECONDS.toNanos(remainingWaitMillis);
-
-            if (remainingWait <= 0) {
-                state = State.NoResponse;
-                break;
-            }
-
-            try {
-                condition.awaitNanos(remainingWait);
-            } catch (InterruptedException e) {
-                state = State.Interrupted;
-                throw e;
-            }
-        }
-    }
-
-    private Exception getException() {
-        if (failureException != null) {
-            return failureException;
-        }
-        return smackWrappedExcpetion;
-    }
-
-    private void throwException() throws E, SmackWrappedException {
-        if (failureException != null) {
-            throw failureException;
-        }
-        throw smackWrappedExcpetion;
-    }
-
-    /**
-     * Check for a response and throw a {@link NoResponseException} if there was none.
-     * 

- * The exception is thrown, if state is one of 'Initial', 'NoResponse' or 'RequestSent' - *

- * @return true if synchronization point was successful, false on failure. - * @throws NoResponseException if there was no response from the remote entity. - */ - private Exception checkForResponse() throws NoResponseException { - switch (state) { - case Initial: - case NoResponse: - case RequestSent: - throw NoResponseException.newWith(connection, waitFor); - case Success: - return null; - case Failure: - return getException(); - default: - throw new AssertionError("Unknown state " + state); - } - } - - private enum State { - Initial, - RequestSent, - NoResponse, - Success, - Failure, - Interrupted, - } -} diff --git a/smack-core/src/main/java/org/jivesoftware/smack/XmppInputOutputFilter.java b/smack-core/src/main/java/org/jivesoftware/smack/XmppInputOutputFilter.java index 18432f564..97fdff246 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/XmppInputOutputFilter.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/XmppInputOutputFilter.java @@ -67,7 +67,8 @@ public interface XmppInputOutputFilter { default void closeInputOutput() { } - default void waitUntilInputOutputClosed() throws IOException, NoResponseException, CertificateException, InterruptedException, SmackException { + default void waitUntilInputOutputClosed() throws IOException, NoResponseException, CertificateException, + InterruptedException, SmackException, XMPPException { } Object getStats(); diff --git a/smack-core/src/main/java/org/jivesoftware/smack/c2s/ModularXmppClientToServerConnection.java b/smack-core/src/main/java/org/jivesoftware/smack/c2s/ModularXmppClientToServerConnection.java index 061d25e7a..ac8087540 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/c2s/ModularXmppClientToServerConnection.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/c2s/ModularXmppClientToServerConnection.java @@ -35,7 +35,6 @@ import javax.net.ssl.SSLSession; import org.jivesoftware.smack.AbstractXMPPConnection; import org.jivesoftware.smack.SmackException; -import org.jivesoftware.smack.SmackException.ConnectionUnexpectedTerminatedException; import org.jivesoftware.smack.SmackException.NoResponseException; import org.jivesoftware.smack.SmackException.NotConnectedException; import org.jivesoftware.smack.SmackFuture; @@ -78,6 +77,7 @@ import org.jivesoftware.smack.util.ArrayBlockingQueueWithShutdown; import org.jivesoftware.smack.util.ExtendedAppendable; import org.jivesoftware.smack.util.PacketParserUtils; import org.jivesoftware.smack.util.StringUtils; +import org.jivesoftware.smack.util.Supplier; import org.jivesoftware.smack.xml.XmlPullParser; import org.jivesoftware.smack.xml.XmlPullParserException; @@ -142,7 +142,8 @@ public final class ModularXmppClientToServerConnection extends AbstractXMPPConne @Override public void onStreamClosed() { - ModularXmppClientToServerConnection.this.closingStreamReceived.reportSuccess(); + ModularXmppClientToServerConnection.this.closingStreamReceived = true; + notifyWaitingThreads(); } @Override @@ -177,7 +178,7 @@ public final class ModularXmppClientToServerConnection extends AbstractXMPPConne @Override public void newStreamOpenWaitForFeaturesSequence(String waitFor) throws InterruptedException, - ConnectionUnexpectedTerminatedException, NoResponseException, NotConnectedException { + SmackException, XMPPException { ModularXmppClientToServerConnection.this.newStreamOpenWaitForFeaturesSequence(waitFor); } @@ -198,8 +199,14 @@ public final class ModularXmppClientToServerConnection extends AbstractXMPPConne } @Override - public Exception getCurrentConnectionException() { - return ModularXmppClientToServerConnection.this.currentConnectionException; + public void waitForCondition(Supplier condition, String waitFor) + throws InterruptedException, SmackException, XMPPException { + ModularXmppClientToServerConnection.this.waitForConditionOrThrowConnectionException(condition, waitFor); + } + + @Override + public void notifyWaitingThreads() { + ModularXmppClientToServerConnection.this.notifyWaitingThreads(); } @Override @@ -263,14 +270,13 @@ public final class ModularXmppClientToServerConnection extends AbstractXMPPConne revertedState.resetState(); } - protected void walkStateGraph(WalkStateGraphContext walkStateGraphContext) throws XMPPErrorException, - SASLErrorException, FailedNonzaException, IOException, SmackException, InterruptedException { + protected void walkStateGraph(WalkStateGraphContext walkStateGraphContext) + throws XMPPException, IOException, SmackException, InterruptedException { // Save a copy of the current state GraphVertex previousStateVertex = currentStateVertex; try { walkStateGraphInternal(walkStateGraphContext); - } catch (XMPPErrorException | SASLErrorException | FailedNonzaException | IOException | SmackException - | InterruptedException e) { + } catch (IOException | SmackException | InterruptedException | XMPPException e) { currentStateVertex = previousStateVertex; // Unwind the state. State revertedState = currentStateVertex.getElement(); @@ -279,8 +285,8 @@ public final class ModularXmppClientToServerConnection extends AbstractXMPPConne } } - private void walkStateGraphInternal(WalkStateGraphContext walkStateGraphContext) throws XMPPErrorException, - SASLErrorException, IOException, SmackException, InterruptedException, FailedNonzaException { + private void walkStateGraphInternal(WalkStateGraphContext walkStateGraphContext) + throws IOException, SmackException, InterruptedException, XMPPException { // Save a copy of the current state final GraphVertex initialStateVertex = currentStateVertex; final State initialState = initialStateVertex.getElement(); @@ -359,15 +365,13 @@ public final class ModularXmppClientToServerConnection extends AbstractXMPPConne * @param walkStateGraphContext the "walk state graph" context. * @return A state transition result or null if this state can be ignored. * @throws SmackException if Smack detected an exceptional situation. - * @throws XMPPErrorException if an XMPP protocol error was received. - * @throws SASLErrorException if a SASL protocol error was returned. + * @throws XMPPException if an XMPP protocol error was received. * @throws IOException if an I/O error occurred. * @throws InterruptedException if the calling thread was interrupted. - * @throws FailedNonzaException if an XMPP protocol failure was received. */ private StateTransitionResult attemptEnterState(GraphVertex successorStateVertex, - WalkStateGraphContext walkStateGraphContext) throws SmackException, XMPPErrorException, - SASLErrorException, IOException, InterruptedException, FailedNonzaException { + WalkStateGraphContext walkStateGraphContext) throws SmackException, XMPPException, + IOException, InterruptedException { final GraphVertex initialStateVertex = currentStateVertex; final State initialState = initialStateVertex.getElement(); final State successorState = successorStateVertex.getElement(); @@ -400,8 +404,7 @@ public final class ModularXmppClientToServerConnection extends AbstractXMPPConne invokeConnectionStateMachineListener(new ConnectionStateEvent.AboutToTransitionInto(initialState, successorState)); transitionAttemptResult = successorState.transitionInto(walkStateGraphContext); - } catch (SmackException | XMPPErrorException | SASLErrorException | IOException | InterruptedException - | FailedNonzaException e) { + } catch (SmackException | IOException | InterruptedException | XMPPException e) { // Unwind the state here too, since this state will not be unwound by walkStateGraph(), as it will not // become a predecessor state in the walk. unwindState(successorState); @@ -474,8 +477,7 @@ public final class ModularXmppClientToServerConnection extends AbstractXMPPConne try { walkStateGraph(context); - } catch (XMPPErrorException | SASLErrorException | IOException | SmackException | InterruptedException - | FailedNonzaException e) { + } catch (IOException | SmackException | InterruptedException | XMPPException e) { throw new IllegalStateException("A walk to disconnected state should never throw", e); } } @@ -491,9 +493,7 @@ public final class ModularXmppClientToServerConnection extends AbstractXMPPConne @Override protected void afterFeaturesReceived() { featuresReceived = true; - synchronized (this) { - notifyAll(); - } + notifyWaitingThreads(); } protected void parseAndProcessElement(String element) { @@ -522,8 +522,10 @@ public final class ModularXmppClientToServerConnection extends AbstractXMPPConne break; case "error": StreamError streamError = PacketParserUtils.parseStreamError(parser, null); - saslFeatureReceived.reportFailure(new StreamErrorException(streamError)); - throw new StreamErrorException(streamError); + StreamErrorException streamErrorException = new StreamErrorException(streamError); + currentXmppException = streamErrorException; + notifyWaitingThreads(); + throw streamErrorException; case "features": parseFeatures(parser); afterFeaturesReceived(); @@ -550,25 +552,12 @@ public final class ModularXmppClientToServerConnection extends AbstractXMPPConne } protected void waitForFeaturesReceived(String waitFor) - throws InterruptedException, ConnectionUnexpectedTerminatedException, NoResponseException { - long waitStartMs = System.currentTimeMillis(); - long timeoutMs = getReplyTimeout(); - synchronized (this) { - while (!featuresReceived && currentConnectionException == null) { - long remainingWaitMs = timeoutMs - (System.currentTimeMillis() - waitStartMs); - if (remainingWaitMs <= 0) { - throw NoResponseException.newWith(this, waitFor); - } - wait(remainingWaitMs); - } - if (currentConnectionException != null) { - throw new SmackException.ConnectionUnexpectedTerminatedException(currentConnectionException); - } - } + throws InterruptedException, SmackException, XMPPException { + waitForConditionOrThrowConnectionException(() -> featuresReceived, waitFor); } protected void newStreamOpenWaitForFeaturesSequence(String waitFor) throws InterruptedException, - ConnectionUnexpectedTerminatedException, NoResponseException, NotConnectedException { + SmackException, XMPPException { prepareToWaitForFeaturesReceived(); sendStreamOpen(); waitForFeaturesReceived(waitFor); @@ -763,8 +752,7 @@ public final class ModularXmppClientToServerConnection extends AbstractXMPPConne @Override public StateTransitionResult.AttemptResult transitionInto(WalkStateGraphContext walkStateGraphContext) - throws XMPPErrorException, SASLErrorException, IOException, SmackException, - InterruptedException { + throws IOException, SmackException, InterruptedException, XMPPException { prepareToWaitForFeaturesReceived(); LoginContext loginContext = walkStateGraphContext.getLoginContext(); @@ -813,12 +801,12 @@ public final class ModularXmppClientToServerConnection extends AbstractXMPPConne @Override public StateTransitionResult.AttemptResult transitionInto(WalkStateGraphContext walkStateGraphContext) - throws XMPPErrorException, SASLErrorException, IOException, SmackException, - InterruptedException { + throws IOException, SmackException, InterruptedException, XMPPException { // Calling bindResourceAndEstablishSession() below requires the lastFeaturesReceived sync point to be signaled. // Since we entered this state, the FSM has decided that the last features have been received, hence signal // the sync point. - lastFeaturesReceived.reportSuccess(); + lastFeaturesReceived = true; + notifyWaitingThreads(); LoginContext loginContext = walkStateGraphContext.getLoginContext(); Resourcepart resource = bindResourceAndEstablishSession(loginContext.resource); @@ -914,7 +902,7 @@ public final class ModularXmppClientToServerConnection extends AbstractXMPPConne @Override public StateTransitionResult.AttemptResult transitionInto(WalkStateGraphContext walkStateGraphContext) { - closingStreamReceived.init(); + closingStreamReceived = false; boolean streamCloseIssued = outgoingElementsQueue.offerAndShutdown(StreamClose.INSTANCE); @@ -936,7 +924,7 @@ public final class ModularXmppClientToServerConnection extends AbstractXMPPConne XmppInputOutputFilter filter = it.next(); try { filter.waitUntilInputOutputClosed(); - } catch (IOException | CertificateException | InterruptedException | SmackException e) { + } catch (IOException | CertificateException | InterruptedException | SmackException | XMPPException e) { LOGGER.log(Level.WARNING, "waitUntilInputOutputClosed() threw", e); } } diff --git a/smack-core/src/main/java/org/jivesoftware/smack/c2s/internal/ModularXmppClientToServerConnectionInternal.java b/smack-core/src/main/java/org/jivesoftware/smack/c2s/internal/ModularXmppClientToServerConnectionInternal.java index b08676914..81a485771 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/c2s/internal/ModularXmppClientToServerConnectionInternal.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/c2s/internal/ModularXmppClientToServerConnectionInternal.java @@ -22,11 +22,12 @@ import java.nio.channels.SelectionKey; import java.util.ListIterator; import java.util.Queue; -import org.jivesoftware.smack.SmackException.ConnectionUnexpectedTerminatedException; +import org.jivesoftware.smack.SmackException; import org.jivesoftware.smack.SmackException.NoResponseException; import org.jivesoftware.smack.SmackException.NotConnectedException; import org.jivesoftware.smack.SmackReactor; import org.jivesoftware.smack.SmackReactor.ChannelSelectedCallback; +import org.jivesoftware.smack.XMPPException; import org.jivesoftware.smack.XMPPException.FailedNonzaException; import org.jivesoftware.smack.XmppInputOutputFilter; import org.jivesoftware.smack.c2s.ModularXmppClientToServerConnection; @@ -38,6 +39,7 @@ import org.jivesoftware.smack.packet.Nonza; import org.jivesoftware.smack.packet.TopLevelStreamElement; import org.jivesoftware.smack.packet.XmlEnvironment; import org.jivesoftware.smack.util.Consumer; +import org.jivesoftware.smack.util.Supplier; import org.jivesoftware.smack.xml.XmlPullParser; public abstract class ModularXmppClientToServerConnectionInternal { @@ -98,7 +100,7 @@ public abstract class ModularXmppClientToServerConnectionInternal { public abstract ListIterator getXmppInputOutputFilterEndIterator(); public abstract void newStreamOpenWaitForFeaturesSequence(String waitFor) throws InterruptedException, - ConnectionUnexpectedTerminatedException, NoResponseException, NotConnectedException; + NoResponseException, NotConnectedException, SmackException, XMPPException; public abstract SmackTlsContext getSmackTlsContext(); @@ -108,7 +110,9 @@ public abstract class ModularXmppClientToServerConnectionInternal { public abstract void asyncGo(Runnable runnable); - public abstract Exception getCurrentConnectionException(); + public abstract void waitForCondition(Supplier condition, String waitFor) throws InterruptedException, SmackException, XMPPException; + + public abstract void notifyWaitingThreads(); public abstract void setCompressionEnabled(boolean compressionEnabled); diff --git a/smack-core/src/main/java/org/jivesoftware/smack/compression/CompressionModule.java b/smack-core/src/main/java/org/jivesoftware/smack/compression/CompressionModule.java index 2a018899b..ce0dddeea 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/compression/CompressionModule.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/compression/CompressionModule.java @@ -17,10 +17,8 @@ package org.jivesoftware.smack.compression; import org.jivesoftware.smack.ConnectionConfiguration; -import org.jivesoftware.smack.SmackException.ConnectionUnexpectedTerminatedException; -import org.jivesoftware.smack.SmackException.NoResponseException; -import org.jivesoftware.smack.SmackException.NotConnectedException; -import org.jivesoftware.smack.XMPPException.FailedNonzaException; +import org.jivesoftware.smack.SmackException; +import org.jivesoftware.smack.XMPPException; import org.jivesoftware.smack.XmppInputOutputFilter; import org.jivesoftware.smack.c2s.ModularXmppClientToServerConnection.AuthenticatedButUnboundStateDescriptor; import org.jivesoftware.smack.c2s.ModularXmppClientToServerConnection.ResourceBindingStateDescriptor; @@ -90,8 +88,7 @@ public class CompressionModule extends ModularXmppClientToServerConnectionModule @Override public StateTransitionResult.AttemptResult transitionInto(WalkStateGraphContext walkStateGraphContext) - throws NoResponseException, NotConnectedException, FailedNonzaException, InterruptedException, - ConnectionUnexpectedTerminatedException { + throws InterruptedException, SmackException, XMPPException { final String compressionMethod = selectedCompressionFactory.getCompressionMethod(); connectionInternal.sendAndWaitForResponse(new Compress(compressionMethod), Compressed.class, Failure.class); diff --git a/smack-core/src/main/java/org/jivesoftware/smack/fsm/State.java b/smack-core/src/main/java/org/jivesoftware/smack/fsm/State.java index f8749161d..a4a7b76ea 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/fsm/State.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/fsm/State.java @@ -19,11 +19,9 @@ package org.jivesoftware.smack.fsm; import java.io.IOException; import org.jivesoftware.smack.SmackException; -import org.jivesoftware.smack.XMPPException.FailedNonzaException; -import org.jivesoftware.smack.XMPPException.XMPPErrorException; +import org.jivesoftware.smack.XMPPException; import org.jivesoftware.smack.c2s.internal.ModularXmppClientToServerConnectionInternal; import org.jivesoftware.smack.c2s.internal.WalkStateGraphContext; -import org.jivesoftware.smack.sasl.SASLErrorException; /** * Note that this is an non-static inner class of XmppClientToServerConnection so that states can inspect and modify @@ -53,8 +51,7 @@ public abstract class State { } public abstract StateTransitionResult.AttemptResult transitionInto(WalkStateGraphContext walkStateGraphContext) - throws XMPPErrorException, SASLErrorException, IOException, SmackException, - InterruptedException, FailedNonzaException; + throws IOException, SmackException, InterruptedException, XMPPException; public StateDescriptor getStateDescriptor() { return stateDescriptor; diff --git a/smack-core/src/main/java/org/jivesoftware/smack/fsm/StateTransitionResult.java b/smack-core/src/main/java/org/jivesoftware/smack/fsm/StateTransitionResult.java index 053d35e77..a9acb4f32 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/fsm/StateTransitionResult.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/fsm/StateTransitionResult.java @@ -67,6 +67,14 @@ public abstract class StateTransitionResult { } } + public static final class FailureCausedByTimeout extends Failure { + + public FailureCausedByTimeout(String failureMessage) { + super(failureMessage); + } + + } + public abstract static class TransitionImpossible extends StateTransitionResult { protected TransitionImpossible(String message) { super(message); diff --git a/smack-core/src/main/java/org/jivesoftware/smack/util/Supplier.java b/smack-core/src/main/java/org/jivesoftware/smack/util/Supplier.java new file mode 100644 index 000000000..f0910adb7 --- /dev/null +++ b/smack-core/src/main/java/org/jivesoftware/smack/util/Supplier.java @@ -0,0 +1,24 @@ +/** + * + * Copyright 2020 Florian Schmaus + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jivesoftware.smack.util; + +// TODO: Replace with java.util.function.Supplier once Smack's minimum Android SDK level is 24 or higher. +public interface Supplier { + + T get(); + +} diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smack/WaitForClosingStreamElementTest.java b/smack-integration-test/src/main/java/org/jivesoftware/smack/WaitForClosingStreamElementTest.java index 023979d64..2326866c7 100644 --- a/smack-integration-test/src/main/java/org/jivesoftware/smack/WaitForClosingStreamElementTest.java +++ b/smack-integration-test/src/main/java/org/jivesoftware/smack/WaitForClosingStreamElementTest.java @@ -38,12 +38,7 @@ public class WaitForClosingStreamElementTest extends AbstractSmackLowLevelIntegr Field closingStreamReceivedField = AbstractXMPPConnection.class.getDeclaredField("closingStreamReceived"); closingStreamReceivedField.setAccessible(true); - SynchronizationPoint closingStreamReceived = (SynchronizationPoint) closingStreamReceivedField.get(connection); - Exception failureException = closingStreamReceived.getFailureException(); - if (failureException != null) { - throw new AssertionError("Sync poing yielded failure exception", failureException); - } - boolean closingStreamReceivedSuccessful = closingStreamReceived.wasSuccessful(); - assertTrue(closingStreamReceivedSuccessful); + boolean closingStreamReceived = (boolean) closingStreamReceivedField.get(connection); + assertTrue(closingStreamReceived); } } diff --git a/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/ConnectionAttemptState.java b/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/ConnectionAttemptState.java index 415af19f9..d8a3890e0 100644 --- a/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/ConnectionAttemptState.java +++ b/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/ConnectionAttemptState.java @@ -26,10 +26,9 @@ import java.util.ArrayList; import java.util.Iterator; import java.util.List; -import org.jivesoftware.smack.SmackException.ConnectionException; import org.jivesoftware.smack.SmackException.EndpointConnectionException; -import org.jivesoftware.smack.SynchronizationPoint; import org.jivesoftware.smack.c2s.internal.ModularXmppClientToServerConnectionInternal; +import org.jivesoftware.smack.fsm.StateTransitionResult; import org.jivesoftware.smack.tcp.XmppTcpTransportModule.EstablishingTcpConnectionState; import org.jivesoftware.smack.tcp.rce.Rfc6120TcpRemoteConnectionEndpoint; import org.jivesoftware.smack.util.Async; @@ -48,7 +47,10 @@ public final class ConnectionAttemptState { final SocketChannel socketChannel; final List> connectionExceptions; - final SynchronizationPoint tcpConnectionEstablishedSyncPoint; + + EndpointConnectionException connectionException; + boolean connected; + long deadline; final Iterator connectionEndpointIterator; /** The current connection endpoint we are trying */ @@ -65,17 +67,32 @@ public final class ConnectionAttemptState { socketChannel = SocketChannel.open(); socketChannel.configureBlocking(false); - connectionEndpointIterator = discoveredEndpoints.result.discoveredRemoteConnectionEndpoints.iterator(); + List endpoints = discoveredEndpoints.result.discoveredRemoteConnectionEndpoints; + connectionEndpointIterator = endpoints.iterator(); connectionEndpoint = connectionEndpointIterator.next(); - connectionExceptions = new ArrayList<>(discoveredEndpoints.result.discoveredRemoteConnectionEndpoints.size()); - - tcpConnectionEstablishedSyncPoint = new SynchronizationPoint<>(connectionInternal.connection, - "TCP connection establishment"); + connectionExceptions = new ArrayList<>(endpoints.size()); } - void establishTcpConnection() { + StateTransitionResult.Failure establishTcpConnection() throws InterruptedException { RemoteConnectionEndpoint.InetSocketAddressCoupling address = nextAddress(); establishTcpConnection(address); + + synchronized (this) { + while (!connected && connectionException == null) { + final long now = System.currentTimeMillis(); + if (now >= deadline) { + return new StateTransitionResult.FailureCausedByTimeout("Timeout waiting to establish connection"); + } + wait (deadline - now); + } + } + if (connected) { + assert connectionException == null; + // Success case: we have been able to establish a connection to one remote endpoint. + return null; + } + + return new StateTransitionResult.FailureCausedByException(connectionException); } private void establishTcpConnection( @@ -84,8 +101,10 @@ public final class ConnectionAttemptState { establishingTcpConnectionState, address); connectionInternal.invokeConnectionStateMachineListener(connectingToHostEvent); - final boolean connected; final InetSocketAddress inetSocketAddress = address.getInetSocketAddress(); + // TODO: Should use "connect timeout" instead of reply timeout. But first connect timeout needs to be moved from + // XMPPTCPConnectionConfiguration. into XMPPConnectionConfiguration. + deadline = System.currentTimeMillis() + connectionInternal.connection.getReplyTimeout(); try { connected = socketChannel.connect(inetSocketAddress); } catch (IOException e) { @@ -98,7 +117,9 @@ public final class ConnectionAttemptState { establishingTcpConnectionState, address, true); connectionInternal.invokeConnectionStateMachineListener(connectedToHostEvent); - tcpConnectionEstablishedSyncPoint.reportSuccess(); + synchronized (this) { + notifyAll(); + } return; } @@ -124,9 +145,10 @@ public final class ConnectionAttemptState { establishingTcpConnectionState, address, false); connectionInternal.invokeConnectionStateMachineListener(connectedToHostEvent); - // Do not set 'state' here, since this is processed by a reactor thread, which doesn't hold - // the objects lock. - tcpConnectionEstablishedSyncPoint.reportSuccess(); + connected = true; + synchronized (ConnectionAttemptState.this) { + notifyAll(); + } }); } catch (ClosedChannelException e) { onIOExceptionWhenEstablishingTcpConnection(e, address); @@ -137,14 +159,14 @@ public final class ConnectionAttemptState { RemoteConnectionEndpoint.InetSocketAddressCoupling failedAddress) { RemoteConnectionEndpoint.InetSocketAddressCoupling nextInetSocketAddress = nextAddress(); if (nextInetSocketAddress == null) { - EndpointConnectionException connectionException = EndpointConnectionException.from( + connectionException = EndpointConnectionException.from( discoveredEndpoints.result.lookupFailures, connectionExceptions); - tcpConnectionEstablishedSyncPoint.reportFailure(connectionException); + synchronized (this) { + notifyAll(); + } return; } - tcpConnectionEstablishedSyncPoint.resetTimeout(); - RemoteConnectionException rce = new RemoteConnectionException<>( failedAddress, exception); connectionExceptions.add(rce); diff --git a/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XMPPTCPConnection.java b/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XMPPTCPConnection.java index ea34b9518..dce23f149 100644 --- a/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XMPPTCPConnection.java +++ b/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XMPPTCPConnection.java @@ -26,11 +26,6 @@ import java.io.Writer; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.Socket; -import java.security.KeyManagementException; -import java.security.KeyStoreException; -import java.security.NoSuchAlgorithmException; -import java.security.NoSuchProviderException; -import java.security.UnrecoverableKeyException; import java.security.cert.CertificateException; import java.util.ArrayList; import java.util.Collection; @@ -44,7 +39,6 @@ import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentLinkedQueue; -import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.logging.Level; @@ -65,13 +59,12 @@ import org.jivesoftware.smack.SmackException.AlreadyConnectedException; import org.jivesoftware.smack.SmackException.AlreadyLoggedInException; import org.jivesoftware.smack.SmackException.ConnectionException; import org.jivesoftware.smack.SmackException.EndpointConnectionException; -import org.jivesoftware.smack.SmackException.NoResponseException; import org.jivesoftware.smack.SmackException.NotConnectedException; import org.jivesoftware.smack.SmackException.NotLoggedInException; +import org.jivesoftware.smack.SmackException.SecurityNotPossibleException; import org.jivesoftware.smack.SmackException.SecurityRequiredByServerException; import org.jivesoftware.smack.SmackFuture; import org.jivesoftware.smack.StanzaListener; -import org.jivesoftware.smack.SynchronizationPoint; import org.jivesoftware.smack.XMPPConnection; import org.jivesoftware.smack.XMPPException; import org.jivesoftware.smack.XMPPException.FailedNonzaException; @@ -151,8 +144,6 @@ public class XMPPTCPConnection extends AbstractXMPPConnection { private SSLSocket secureSocket; - private final Semaphore readerWriterSemaphore = new Semaphore(2); - /** * Protected access level because of unit test purposes */ @@ -166,14 +157,12 @@ public class XMPPTCPConnection extends AbstractXMPPConnection { /** * */ - private final SynchronizationPoint maybeCompressFeaturesReceived = new SynchronizationPoint( - this, "stream compression feature"); + private boolean streamFeaturesAfterAuthenticationReceived; /** * */ - private final SynchronizationPoint compressSyncPoint = new SynchronizationPoint<>( - this, "stream compression"); + private boolean compressSyncPoint; /** * The default bundle and defer callback, used for new connections. @@ -197,15 +186,14 @@ public class XMPPTCPConnection extends AbstractXMPPConnection { /** * The stream ID of the stream that is currently resumable, ie. the stream we hold the state * for in {@link #clientHandledStanzasCount}, {@link #serverHandledStanzasCount} and - * {@link #unacknowledgedStanzas}. + * {@link #unFailedNonzaExceptionacknowledgedStanzas}. */ private String smSessionId; - private final SynchronizationPoint smResumedSyncPoint = new SynchronizationPoint<>( - this, "stream resumed element"); + private SyncPointState smResumedSyncPoint; + private Failed smResumptionFailed; - private final SynchronizationPoint smEnabledSyncPoint = new SynchronizationPoint<>( - this, "stream enabled element"); + private boolean smEnabledSyncPoint; /** * The client's preferred maximum resumption time in seconds. @@ -381,15 +369,17 @@ public class XMPPTCPConnection extends AbstractXMPPConnection { // Wait for stream features after the authentication. // TODO: The name of this synchronization point "maybeCompressFeaturesReceived" is not perfect. It should be // renamed to "streamFeaturesAfterAuthenticationReceived". - maybeCompressFeaturesReceived.checkIfSuccessOrWait(); + waitForConditionOrThrowConnectionException(() -> streamFeaturesAfterAuthenticationReceived, "compress features from server"); // If compression is enabled then request the server to use stream compression. XEP-170 // recommends to perform stream compression before resource binding. maybeEnableCompression(); if (isSmResumptionPossible()) { - smResumedSyncPoint.sendAndWaitForResponse(new Resume(clientHandledStanzasCount, smSessionId)); - if (smResumedSyncPoint.wasSuccessful()) { + smResumedSyncPoint = SyncPointState.request_sent; + sendNonza(new Resume(clientHandledStanzasCount, smSessionId)); + waitForCondition(() -> smResumedSyncPoint == SyncPointState.successful || smResumptionFailed != null, "resume previous stream"); + if (smResumedSyncPoint == SyncPointState.successful) { // We successfully resumed the stream, be done here afterSuccessfulLogin(true); return; @@ -397,7 +387,8 @@ public class XMPPTCPConnection extends AbstractXMPPConnection { // SM resumption failed, what Smack does here is to report success of // lastFeaturesReceived in case of sm resumption was answered with 'failed' so that // normal resource binding can be tried. - LOGGER.fine("Stream resumption failed, continuing with normal stream establishment process"); + assert smResumptionFailed != null; + LOGGER.fine("Stream resumption failed, continuing with normal stream establishment process: " + smResumptionFailed); } List previouslyUnackedStanzas = new LinkedList(); @@ -421,9 +412,10 @@ public class XMPPTCPConnection extends AbstractXMPPConnection { if (isSmAvailable() && useSm) { // Remove what is maybe left from previously stream managed sessions serverHandledStanzasCount = 0; + sendNonza(new Enable(useSmResumption, smClientMaxResumptionTime)); // XEP-198 3. Enabling Stream Management. If the server response to 'Enable' is 'Failed' // then this is a non recoverable error and we therefore throw an exception. - smEnabledSyncPoint.sendAndWaitForResponseOrThrow(new Enable(useSmResumption, smClientMaxResumptionTime)); + waitForConditionOrThrowConnectionException(() -> smEnabledSyncPoint, "enabling stream mangement"); synchronized (requestAckPredicates) { if (requestAckPredicates.isEmpty()) { // Assure that we have at lest one predicate set up that so that we request acks @@ -503,9 +495,14 @@ public class XMPPTCPConnection extends AbstractXMPPConnection { setWasAuthenticated(); - // Wait for reader and writer threads to be terminated. - readerWriterSemaphore.acquireUninterruptibly(2); - readerWriterSemaphore.release(2); + try { + boolean readerAndWriterThreadsTermianted = waitForCondition(() -> !packetWriter.running && !packetReader.running); + if (!readerAndWriterThreadsTermianted) { + LOGGER.severe("Reader and writer threads did not terminate"); + } + } catch (InterruptedException e) { + LOGGER.log(Level.FINE, "Interrupted while waiting for reader and writer threads to terminate", e); + } if (disconnectedButResumeable) { return; @@ -536,10 +533,10 @@ public class XMPPTCPConnection extends AbstractXMPPConnection { @Override protected void initState() { super.initState(); - maybeCompressFeaturesReceived.init(); - compressSyncPoint.init(); - smResumedSyncPoint.init(); - smEnabledSyncPoint.init(); + streamFeaturesAfterAuthenticationReceived = compressSyncPoint = false; + smResumedSyncPoint = SyncPointState.initial; + smResumptionFailed = null; + smEnabledSyncPoint = false; } @Override @@ -652,15 +649,6 @@ public class XMPPTCPConnection extends AbstractXMPPConnection { // Set the reader and writer instance variables initReaderAndWriter(); - int availableReaderWriterSemaphorePermits = readerWriterSemaphore.availablePermits(); - if (availableReaderWriterSemaphorePermits < 2) { - Object[] logObjects = new Object[] { - this, - availableReaderWriterSemaphorePermits, - }; - LOGGER.log(Level.FINE, "Not every reader/writer threads where terminated on connection re-initializtion of {0}. Available permits {1}", logObjects); - } - readerWriterSemaphore.acquire(2); // Start the writer thread. This will open an XMPP stream to the server packetWriter.init(); // Start the reader thread. The startup() method will block until we @@ -688,17 +676,11 @@ public class XMPPTCPConnection extends AbstractXMPPConnection { * existing plain connection and perform a handshake. This method won't return until the * connection has finished the handshake or an error occurred while securing the connection. * @throws IOException if an I/O error occurred. - * @throws CertificateException - * @throws NoSuchAlgorithmException if no such algorithm is available. - * @throws NoSuchProviderException - * @throws KeyStoreException - * @throws UnrecoverableKeyException - * @throws KeyManagementException if there was a key mangement error. - * @throws SmackException if Smack detected an exceptional situation. - * @throws Exception if an exception occurs. + * @throws SecurityNotPossibleException if TLS is not possible. + * @throws CertificateException if there is an issue with the certificate. */ @SuppressWarnings("LiteralClassName") - private void proceedTLSReceived() throws NoSuchAlgorithmException, CertificateException, IOException, KeyStoreException, NoSuchProviderException, UnrecoverableKeyException, KeyManagementException, SmackException { + private void proceedTLSReceived() throws IOException, SecurityNotPossibleException, CertificateException { SmackTlsContext smackTlsContext = getSmackTlsContext(); Socket plain = socket; @@ -773,7 +755,7 @@ public class XMPPTCPConnection extends AbstractXMPPConnection { @Override public boolean isUsingCompression() { - return compressionHandler != null && compressSyncPoint.wasSuccessful(); + return compressionHandler != null && compressSyncPoint; } /** @@ -791,10 +773,10 @@ public class XMPPTCPConnection extends AbstractXMPPConnection { * * @throws NotConnectedException if the XMPP connection is not connected. * @throws SmackException if Smack detected an exceptional situation. - * @throws NoResponseException if there was no response from the remote entity. * @throws InterruptedException if the calling thread was interrupted. + * @throws XMPPException if an XMPP protocol error was received. */ - private void maybeEnableCompression() throws SmackException, InterruptedException { + private void maybeEnableCompression() throws SmackException, InterruptedException, XMPPException { if (!config.isCompressionEnabled()) { return; } @@ -807,7 +789,8 @@ public class XMPPTCPConnection extends AbstractXMPPConnection { // If stream compression was offered by the server and we want to use // compression then send compression request to the server if ((compressionHandler = maybeGetCompressionHandler(compression)) != null) { - compressSyncPoint.sendAndWaitForResponseOrThrow(new Compress(compressionHandler.getCompressionMethod())); + sendNonza(new Compress(compressionHandler.getCompressionMethod())); + waitForConditionOrThrowConnectionException(() -> compressSyncPoint, "establishing stream compression"); } else { LOGGER.warning("Could not enable compression because no matching handler/method pair was found"); } @@ -835,11 +818,11 @@ public class XMPPTCPConnection extends AbstractXMPPConnection { // We connected successfully to the servers TCP port initConnection(); - // TLS handled will be successful either if TLS was established, or if it was not mandatory. - tlsHandled.checkIfSuccessOrWaitOrThrow(); + // TLS handled will be true either if TLS was established, or if it was not mandatory. + waitForConditionOrThrowConnectionException(() -> tlsHandled, "establishing TLS"); // Wait with SASL auth until the SASL mechanisms have been received - saslFeatureReceived.checkIfSuccessOrWaitOrThrow(); + waitForConditionOrThrowConnectionException(() -> saslFeatureReceived, "SASL mechanisms stream feature from server"); } /** @@ -857,24 +840,28 @@ public class XMPPTCPConnection extends AbstractXMPPConnection { if (startTlsFeature != null) { if (startTlsFeature.required() && config.getSecurityMode() == SecurityMode.disabled) { SecurityRequiredByServerException smackException = new SecurityRequiredByServerException(); - tlsHandled.reportFailure(smackException); + currentSmackException = smackException; + notifyWaitingThreads(); throw smackException; } if (config.getSecurityMode() != ConnectionConfiguration.SecurityMode.disabled) { sendNonza(new StartTls()); } else { - tlsHandled.reportSuccess(); + tlsHandled = true; + notifyWaitingThreads(); } } else { - tlsHandled.reportSuccess(); + tlsHandled = true; + notifyWaitingThreads(); } if (isSaslAuthenticated()) { // If we have received features after the SASL has been successfully completed, then we // have also *maybe* received, as it is an optional feature, the compression feature // from the server. - maybeCompressFeaturesReceived.reportSuccess(); + streamFeaturesAfterAuthenticationReceived = true; + notifyWaitingThreads(); } } @@ -899,6 +886,8 @@ public class XMPPTCPConnection extends AbstractXMPPConnection { private volatile boolean done; + private boolean running; + /** * Initializes the reader in order to be used. The reader is initialized during the * first connection and when reconnecting due to an abruptly disconnection. @@ -910,11 +899,13 @@ public class XMPPTCPConnection extends AbstractXMPPConnection { @Override public void run() { LOGGER.finer(threadName + " start"); + running = true; try { parsePackets(); } finally { LOGGER.finer(threadName + " exit"); - XMPPTCPConnection.this.readerWriterSemaphore.release(); + running = false; + notifyWaitingThreads(); } } }, threadName); @@ -931,10 +922,8 @@ public class XMPPTCPConnection extends AbstractXMPPConnection { * Parse top-level packets in order to process them further. */ private void parsePackets() { - boolean initialStreamOpenSend = false; try { openStreamAndResetParser(); - initialStreamOpenSend = true; XmlPullParser.Event eventType = parser.getEventType(); while (!done) { switch (eventType) { @@ -955,27 +944,21 @@ public class XMPPTCPConnection extends AbstractXMPPConnection { break; case "error": StreamError streamError = PacketParserUtils.parseStreamError(parser); - saslFeatureReceived.reportFailure(new StreamErrorException(streamError)); + currentXmppException = new StreamErrorException(streamError); // Mark the tlsHandled sync point as success, we will use the saslFeatureReceived sync // point to report the error, which is checked immediately after tlsHandled in // connectInternal(). - tlsHandled.reportSuccess(); + tlsHandled = true; + notifyWaiters(); throw new StreamErrorException(streamError); case "features": parseFeaturesAndNotify(parser); break; case "proceed": - try { - // Secure the connection by negotiating TLS - proceedTLSReceived(); - // Send a new opening stream to the server - openStreamAndResetParser(); - } - catch (Exception e) { - SmackException.SmackWrappedException smackException = new SmackException.SmackWrappedException(e); - tlsHandled.reportFailure(smackException); - throw e; - } + // Secure the connection by negotiating TLS + proceedTLSReceived(); + // Send a new opening stream to the server + openStreamAndResetParser(); break; case "failure": String namespace = parser.getNamespace(null); @@ -989,8 +972,8 @@ public class XMPPTCPConnection extends AbstractXMPPConnection { // situation. It is still possible to authenticate and // use the connection but using an uncompressed connection // TODO Parse failure stanza - compressSyncPoint.reportFailure(new SmackException.SmackMessageException( - "Could not establish compression")); + currentSmackException = new SmackException.SmackMessageException("Could not establish compression"); + notifyWaitingThreads(); break; default: parseAndProcessNonza(parser); @@ -1004,7 +987,8 @@ public class XMPPTCPConnection extends AbstractXMPPConnection { // Send a new opening stream to the server openStreamAndResetParser(); // Notify that compression is being used - compressSyncPoint.reportSuccess(); + compressSyncPoint = true; + notifyWaitingThreads(); break; case Enabled.ELEMENT: Enabled enabled = ParseStreamManagement.enabled(parser); @@ -1012,7 +996,7 @@ public class XMPPTCPConnection extends AbstractXMPPConnection { smSessionId = enabled.getId(); if (StringUtils.isNullOrEmpty(smSessionId)) { SmackException xmppException = new SmackException.SmackMessageException("Stream Management 'enabled' element with resume attribute but without session id received"); - smEnabledSyncPoint.reportFailure(xmppException); + setCurrentConnectionExceptionAndNotify(xmppException); throw xmppException; } smServerMaxResumptionTime = enabled.getMaxResumptionTime(); @@ -1022,28 +1006,18 @@ public class XMPPTCPConnection extends AbstractXMPPConnection { } clientHandledStanzasCount = 0; smWasEnabledAtLeastOnce = true; - smEnabledSyncPoint.reportSuccess(); - LOGGER.fine("Stream Management (XEP-198): successfully enabled"); + smEnabledSyncPoint = true; + notifyWaitingThreads(); break; case Failed.ELEMENT: Failed failed = ParseStreamManagement.failed(parser); - FailedNonzaException xmppException = new FailedNonzaException(failed, failed.getStanzaErrorCondition()); - // If only XEP-198 would specify different failure elements for the SM - // enable and SM resume failure case. But this is not the case, so we - // need to determine if this is a 'Failed' response for either 'Enable' - // or 'Resume'. - if (smResumedSyncPoint.requestSent()) { - smResumedSyncPoint.reportFailure(xmppException); - } - else { - if (!smEnabledSyncPoint.requestSent()) { - throw new IllegalStateException("Failed element received but SM was not previously enabled"); - } - smEnabledSyncPoint.reportFailure(new SmackException.SmackWrappedException(xmppException)); - // Report success for last lastFeaturesReceived so that in case a - // failed resumption, we can continue with normal resource binding. - // See text of XEP-198 5. below Example 11. - lastFeaturesReceived.reportSuccess(); + if (smResumedSyncPoint == SyncPointState.request_sent) { + // This is a nonza in a response to resuming a previous stream, failure to do + // so is non-fatal as we can simply continue with resource binding in this case. + smResumptionFailed = failed; + } else { + FailedNonzaException xmppException = new FailedNonzaException(failed, failed.getStanzaErrorCondition()); + setCurrentConnectionExceptionAndNotify(xmppException); } break; case Resumed.ELEMENT: @@ -1052,7 +1026,7 @@ public class XMPPTCPConnection extends AbstractXMPPConnection { throw new StreamIdDoesNotMatchException(smSessionId, resumed.getPrevId()); } // Mark SM as enabled - smEnabledSyncPoint.reportSuccess(); + smEnabledSyncPoint = true; // First, drop the stanzas already handled by the server processHandledCount(resumed.getHandledCount()); // Then re-send what is left in the unacknowledged queue @@ -1068,8 +1042,8 @@ public class XMPPTCPConnection extends AbstractXMPPConnection { requestSmAcknowledgementInternal(); } // Mark SM resumption as successful - smResumedSyncPoint.reportSuccess(); - LOGGER.fine("Stream Management (XEP-198): Stream resumed"); + smResumedSyncPoint = SyncPointState.successful; + notifyWaitingThreads(); break; case AckAnswer.ELEMENT: AckAnswer ackAnswer = ParseStreamManagement.ackAnswer(parser); @@ -1077,7 +1051,7 @@ public class XMPPTCPConnection extends AbstractXMPPConnection { break; case AckRequest.ELEMENT: ParseStreamManagement.ackRequest(parser); - if (smEnabledSyncPoint.wasSuccessful()) { + if (smEnabledSyncPoint) { sendSmAcknowledgementInternal(); } else { LOGGER.warning("SM Ack Request received while SM is not enabled"); @@ -1101,7 +1075,8 @@ public class XMPPTCPConnection extends AbstractXMPPConnection { // did re-start the queue again, causing this writer to assume that the queue is not // shutdown, which results in a call to disconnect(). final boolean queueWasShutdown = packetWriter.queue.isShutdown(); - closingStreamReceived.reportSuccess(); + closingStreamReceived = true; + notifyWaitingThreads(); if (queueWasShutdown) { // We received a closing stream element *after* we initiated the @@ -1137,12 +1112,9 @@ public class XMPPTCPConnection extends AbstractXMPPConnection { } } catch (Exception e) { - // TODO: Move the call closingStreamReceived.reportFailure(e) into notifyConnectionError? - closingStreamReceived.reportFailure(e); // The exception can be ignored if the the connection is 'done' - // or if the it was caused because the socket got closed. It can not be ignored if it - // happened before (or while) the initial stream opened was send. - if (!(done || packetWriter.queue.isShutdown()) || !initialStreamOpenSend) { + // or if the it was caused because the socket got closed. + if (!done) { // Close the connection and notify connection listeners of the // error. notifyConnectionError(e); @@ -1161,12 +1133,6 @@ public class XMPPTCPConnection extends AbstractXMPPConnection { private final ArrayBlockingQueueWithShutdown queue = new ArrayBlockingQueueWithShutdown<>( QUEUE_SIZE, true); - /** - * Needs to be protected for unit testing purposes. - */ - protected SynchronizationPoint shutdownDone = new SynchronizationPoint<>( - XMPPTCPConnection.this, "shutdown completed"); - /** * If set, the stanza writer is shut down */ @@ -1184,12 +1150,13 @@ public class XMPPTCPConnection extends AbstractXMPPConnection { */ private boolean shouldBundleAndDefer; + private boolean running; + /** * Initializes the writer in order to be used. It is called at the first connection and also * is invoked if the connection is disconnected by an error. */ void init() { - shutdownDone.init(); shutdownTimestamp = null; if (unacknowledgedStanzas != null) { @@ -1204,11 +1171,13 @@ public class XMPPTCPConnection extends AbstractXMPPConnection { @Override public void run() { LOGGER.finer(threadName + " start"); + running = true; try { writePackets(); } finally { LOGGER.finer(threadName + " exit"); - XMPPTCPConnection.this.readerWriterSemaphore.release(); + running = false; + notifyWaitingThreads(); } } }, threadName); @@ -1262,13 +1231,6 @@ public class XMPPTCPConnection extends AbstractXMPPConnection { instantShutdown = instant; queue.shutdown(); shutdownTimestamp = System.currentTimeMillis(); - if (shutdownDone.isNotInInitialState()) { - try { - shutdownDone.checkIfSuccessOrWait(); - } catch (NoResponseException | InterruptedException e) { - LOGGER.log(Level.WARNING, "shutdownDone was not marked as successful by the writer thread", e); - } - } } /** @@ -1407,9 +1369,6 @@ public class XMPPTCPConnection extends AbstractXMPPConnection { } else { LOGGER.log(Level.FINE, "Ignoring Exception in writePackets()", e); } - } finally { - LOGGER.fine("Reporting shutdownDone success in writer thread"); - shutdownDone.reportSuccess(); } // Delay notifyConnectionError after shutdownDone has been reported in the finally block. if (writerException != null) { @@ -1721,7 +1680,7 @@ public class XMPPTCPConnection extends AbstractXMPPConnection { * @return true if Stream Management was negotiated. */ public boolean isSmEnabled() { - return smEnabledSyncPoint.wasSuccessful(); + return smEnabledSyncPoint; } /** @@ -1730,7 +1689,7 @@ public class XMPPTCPConnection extends AbstractXMPPConnection { * @return true if the stream was resumed. */ public boolean streamWasResumed() { - return smResumedSyncPoint.wasSuccessful(); + return smResumedSyncPoint == SyncPointState.successful; } /** diff --git a/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XmppTcpTransportModule.java b/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XmppTcpTransportModule.java index 74286fdd9..26c74b663 100644 --- a/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XmppTcpTransportModule.java +++ b/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XmppTcpTransportModule.java @@ -47,17 +47,13 @@ import javax.net.ssl.SSLSession; import org.jivesoftware.smack.ConnectionConfiguration.SecurityMode; import org.jivesoftware.smack.SmackException; -import org.jivesoftware.smack.SmackException.ConnectionException; -import org.jivesoftware.smack.SmackException.ConnectionUnexpectedTerminatedException; -import org.jivesoftware.smack.SmackException.NoResponseException; -import org.jivesoftware.smack.SmackException.NotConnectedException; import org.jivesoftware.smack.SmackException.SecurityRequiredByClientException; import org.jivesoftware.smack.SmackException.SecurityRequiredByServerException; -import org.jivesoftware.smack.SmackException.SmackWrappedException; +import org.jivesoftware.smack.SmackException.SmackCertificateException; import org.jivesoftware.smack.SmackFuture; import org.jivesoftware.smack.SmackFuture.InternalSmackFuture; import org.jivesoftware.smack.SmackReactor.SelectionKeyAttachment; -import org.jivesoftware.smack.XMPPException.FailedNonzaException; +import org.jivesoftware.smack.XMPPException; import org.jivesoftware.smack.XmppInputOutputFilter; import org.jivesoftware.smack.c2s.ModularXmppClientToServerConnection.ConnectedButUnauthenticatedStateDescriptor; import org.jivesoftware.smack.c2s.ModularXmppClientToServerConnection.LookupRemoteConnectionEndpointsStateDescriptor; @@ -744,23 +740,14 @@ public class XmppTcpTransportModule extends ModularXmppClientToServerConnectionM @Override public StateTransitionResult.AttemptResult transitionInto(WalkStateGraphContext walkStateGraphContext) - throws InterruptedException, ConnectionUnexpectedTerminatedException, NotConnectedException, - NoResponseException, IOException { + throws InterruptedException, IOException, SmackException, XMPPException { // The fields inetSocketAddress and failedAddresses are handed over from LookupHostAddresses to // ConnectingToHost. ConnectionAttemptState connectionAttemptState = new ConnectionAttemptState(connectionInternal, discoveredTcpEndpoints, this); - connectionAttemptState.establishTcpConnection(); - - try { - connectionAttemptState.tcpConnectionEstablishedSyncPoint.checkIfSuccessOrWaitOrThrow(); - } catch (ConnectionException | NoResponseException e) { - // TODO: It is not really elegant that we catch the exception here. Ideally ConnectionAttemptState would - // simply return a StateTranstionResult.FailureCausedByException. - return new StateTransitionResult.FailureCausedByException<>(e); - } catch (SmackWrappedException e) { - // Should never throw SmackWrappedException. - throw new AssertionError(e); + StateTransitionResult.Failure failure = connectionAttemptState.establishTcpConnection(); + if (failure != null) { + return failure; } socketChannel = connectionAttemptState.socketChannel; @@ -858,8 +845,7 @@ public class XmppTcpTransportModule extends ModularXmppClientToServerConnectionM @Override public StateTransitionResult.AttemptResult transitionInto(WalkStateGraphContext walkStateGraphContext) - throws SmackWrappedException, FailedNonzaException, IOException, InterruptedException, - ConnectionUnexpectedTerminatedException, NoResponseException, NotConnectedException { + throws IOException, InterruptedException, SmackException, XMPPException { connectionInternal.sendAndWaitForResponse(StartTls.INSTANCE, TlsProceed.class, TlsFailure.class); SmackTlsContext smackTlsContext = connectionInternal.getSmackTlsContext(); @@ -881,7 +867,7 @@ public class XmppTcpTransportModule extends ModularXmppClientToServerConnectionM try { tlsState.waitForHandshakeFinished(); } catch (CertificateException e) { - throw new SmackWrappedException(e); + throw new SmackCertificateException(e); } connectionInternal.newStreamOpenWaitForFeaturesSequence("stream features after TLS established"); @@ -1166,43 +1152,20 @@ public class XmppTcpTransportModule extends ModularXmppClientToServerConnectionM private void handleSslException(SSLException e) { handshakeException = e; handshakeStatus = TlsHandshakeStatus.failed; - synchronized (this) { - notifyAll(); - } + connectionInternal.notifyWaitingThreads(); } private void onHandshakeFinished() { handshakeStatus = TlsHandshakeStatus.successful; - synchronized (this) { - notifyAll(); - } + connectionInternal.notifyWaitingThreads(); } private boolean isHandshakeFinished() { return handshakeStatus == TlsHandshakeStatus.successful || handshakeStatus == TlsHandshakeStatus.failed; } - private void waitForHandshakeFinished() throws InterruptedException, CertificateException, SSLException, ConnectionUnexpectedTerminatedException, NoResponseException { - final long deadline = System.currentTimeMillis() + connectionInternal.connection.getReplyTimeout(); - - Exception currentConnectionException = null; - synchronized (this) { - while (!isHandshakeFinished() - && (currentConnectionException = connectionInternal.getCurrentConnectionException()) == null) { - final long now = System.currentTimeMillis(); - if (now >= deadline) - break; - wait(deadline - now); - } - } - - if (currentConnectionException != null) { - throw new SmackException.ConnectionUnexpectedTerminatedException(currentConnectionException); - } - - if (!isHandshakeFinished()) { - throw NoResponseException.newWith(connectionInternal.connection, "TLS handshake to finish"); - } + private void waitForHandshakeFinished() throws InterruptedException, CertificateException, SSLException, SmackException, XMPPException { + connectionInternal.waitForCondition(() -> isHandshakeFinished(), "TLS handshake to finish"); if (handshakeStatus == TlsHandshakeStatus.failed) { throw handshakeException; @@ -1235,7 +1198,7 @@ public class XmppTcpTransportModule extends ModularXmppClientToServerConnectionM @Override public void waitUntilInputOutputClosed() throws IOException, CertificateException, InterruptedException, - ConnectionUnexpectedTerminatedException, NoResponseException { + SmackException, XMPPException { waitForHandshakeFinished(); } diff --git a/smack-tcp/src/test/java/org/jivesoftware/smack/tcp/PacketWriterTest.java b/smack-tcp/src/test/java/org/jivesoftware/smack/tcp/PacketWriterTest.java index 95f74bc60..b6a2ab6a9 100644 --- a/smack-tcp/src/test/java/org/jivesoftware/smack/tcp/PacketWriterTest.java +++ b/smack-tcp/src/test/java/org/jivesoftware/smack/tcp/PacketWriterTest.java @@ -1,6 +1,6 @@ /** * - * Copyright 2014-2019 Florian Schmaus + * Copyright 2014-2020 Florian Schmaus * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -124,8 +124,6 @@ public class PacketWriterTest { // Not really cool, but may increases the chances for 't' to block in sendStanza. Thread.sleep(250); - // Set to true for testing purposes, so that shutdown() won't wait packet writer - pw.shutdownDone.reportSuccess(); // Shutdown the packetwriter, this will also interrupt the writer thread, which is what we hope to happen in the // thread created above. pw.shutdown(false); From 81f10b0c5ba421a74fd228d7c48fc685e058c457 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Sun, 31 May 2020 12:34:02 +0200 Subject: [PATCH 07/28] [core] Synchronize notifyConnectionError() Synchronize notifyConnectionError() so that only one exception is handled and remove the ASYNC_BUT_ORDERED usage here. The ASYNC_BUT_ORDERED was added with 7d2c3ac9f ("Do not call synchronized methods in reader/writer thread"), but is no longer necessary, since the Semaphores where replaced with conditions in the previous commit. --- .../smack/AbstractXMPPConnection.java | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/smack-core/src/main/java/org/jivesoftware/smack/AbstractXMPPConnection.java b/smack-core/src/main/java/org/jivesoftware/smack/AbstractXMPPConnection.java index 2d0f7c693..0da542111 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/AbstractXMPPConnection.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/AbstractXMPPConnection.java @@ -963,6 +963,8 @@ public abstract class AbstractXMPPConnection implements XMPPConnection { callConnectionClosedListener(); } + private final Object notifyConnectionErrorMonitor = new Object(); + /** * Sends out a notification that there was an error with the connection * and closes the connection. @@ -970,14 +972,13 @@ public abstract class AbstractXMPPConnection implements XMPPConnection { * @param exception the exception that causes the connection close event. */ protected final void notifyConnectionError(final Exception exception) { - if (!isConnected()) { - LOGGER.log(Level.INFO, "Connection was already disconnected when attempting to handle " + exception, - exception); - return; - } + synchronized (notifyConnectionErrorMonitor) { + if (!isConnected()) { + LOGGER.log(Level.INFO, "Connection was already disconnected when attempting to handle " + exception, + exception); + return; + } - // TODO: Remove this async but ordered? - ASYNC_BUT_ORDERED.performAsyncButOrdered(this, () -> { // Note that we first have to set the current connection exception and notify waiting threads, as one of them // could hold the instance lock, which we also need later when calling instantShutdown(). setCurrentConnectionExceptionAndNotify(exception); @@ -995,7 +996,7 @@ public abstract class AbstractXMPPConnection implements XMPPConnection { // Notify connection listeners of the error. callConnectionClosedOnErrorListener(exception); }, AbstractXMPPConnection.this + " callConnectionClosedOnErrorListener()"); - }); + } } /** From 22baa742983f1f3e7f6a941b7c26de7a1463c291 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Sun, 31 May 2020 13:37:58 +0200 Subject: [PATCH 08/28] [tcp] Remove flush() in writer thread We will flush the stream after the closing stream tag has been written anyway. No need to do it here. --- .../main/java/org/jivesoftware/smack/tcp/XMPPTCPConnection.java | 1 - 1 file changed, 1 deletion(-) diff --git a/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XMPPTCPConnection.java b/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XMPPTCPConnection.java index dce23f149..215c8dabb 100644 --- a/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XMPPTCPConnection.java +++ b/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XMPPTCPConnection.java @@ -1332,7 +1332,6 @@ public class XMPPTCPConnection extends AbstractXMPPConnection { } writer.write(packet.toXML().toString()); } - writer.flush(); } catch (Exception e) { LOGGER.log(Level.WARNING, From 84b7adb7646f6437b905f756943bbd846a782862 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Sun, 31 May 2020 13:47:22 +0200 Subject: [PATCH 09/28] [tcp] Remove javadoc throws annotation This method does no longer throw. --- .../main/java/org/jivesoftware/smack/tcp/XMPPTCPConnection.java | 1 - 1 file changed, 1 deletion(-) diff --git a/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XMPPTCPConnection.java b/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XMPPTCPConnection.java index 215c8dabb..0980d00b2 100644 --- a/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XMPPTCPConnection.java +++ b/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XMPPTCPConnection.java @@ -1225,7 +1225,6 @@ public class XMPPTCPConnection extends AbstractXMPPConnection { /** * Shuts down the stanza writer. Once this method has been called, no further * packets will be written to the server. - * @throws InterruptedException if the calling thread was interrupted. */ void shutdown(boolean instant) { instantShutdown = instant; From b7465e820002aee48ce2ba37b54ceb45bc48b199 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Sun, 31 May 2020 14:15:13 +0200 Subject: [PATCH 10/28] [tcp] Do not needlessly wait for closing stream tag --- .../smack/tcp/XMPPTCPConnection.java | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XMPPTCPConnection.java b/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XMPPTCPConnection.java index 0980d00b2..361eb2255 100644 --- a/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XMPPTCPConnection.java +++ b/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XMPPTCPConnection.java @@ -477,14 +477,19 @@ public class XMPPTCPConnection extends AbstractXMPPConnection { } private void shutdown(boolean instant) { - // First shutdown the writer, this will result in a closing stream element getting send to - // the server - LOGGER.finer("PacketWriter shutdown()"); - packetWriter.shutdown(instant); - LOGGER.finer("PacketWriter has been shut down"); + // The writer thread may already been finished at this point, for example when the connection is in the + // disconnected-but-resumable state. There is no need to wait for the closing stream tag from the server in this + // case. + if (!packetWriter.done()) { + // First shutdown the writer, this will result in a closing stream element getting send to + // the server + LOGGER.finer("PacketWriter shutdown()"); + packetWriter.shutdown(instant); + LOGGER.finer("PacketWriter has been shut down"); - if (!instant) { - waitForClosingStreamTagFromServer(); + if (!instant) { + waitForClosingStreamTagFromServer(); + } } LOGGER.finer("PacketReader shutdown()"); From 7d129d6f6ce40c6f9a7b6e2c066428e929066836 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Sun, 31 May 2020 19:44:53 +0200 Subject: [PATCH 11/28] [tcp] Cleanup handling of stream errors in XMPPTCPConnection There is no need to notify waiting threads, throwing the stream error will also notify them. Also settings tlsHandled to true is no longer necessary. --- .../org/jivesoftware/smack/tcp/XMPPTCPConnection.java | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XMPPTCPConnection.java b/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XMPPTCPConnection.java index 361eb2255..3e30f83b5 100644 --- a/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XMPPTCPConnection.java +++ b/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XMPPTCPConnection.java @@ -949,12 +949,8 @@ public class XMPPTCPConnection extends AbstractXMPPConnection { break; case "error": StreamError streamError = PacketParserUtils.parseStreamError(parser); - currentXmppException = new StreamErrorException(streamError); - // Mark the tlsHandled sync point as success, we will use the saslFeatureReceived sync - // point to report the error, which is checked immediately after tlsHandled in - // connectInternal(). - tlsHandled = true; - notifyWaiters(); + // Stream errors are non recoverable, throw this exceptions. Also note that this will set + // this exception as current connection exceptions and notify any waiting threads. throw new StreamErrorException(streamError); case "features": parseFeaturesAndNotify(parser); From 2900cc22740d20a12699a93c207eb67a316bec2e Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Sun, 31 May 2020 21:00:42 +0200 Subject: [PATCH 12/28] [sinttest] Disconnect connection in LoginIntegrationTest So that it will cause an connectionClosedOnError() callback, caused by an connection-timeout stream error. --- .../org/jivesoftware/smack/LoginIntegrationTest.java | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smack/LoginIntegrationTest.java b/smack-integration-test/src/main/java/org/jivesoftware/smack/LoginIntegrationTest.java index 47c299671..7534c4449 100644 --- a/smack-integration-test/src/main/java/org/jivesoftware/smack/LoginIntegrationTest.java +++ b/smack-integration-test/src/main/java/org/jivesoftware/smack/LoginIntegrationTest.java @@ -58,11 +58,15 @@ public class LoginIntegrationTest extends AbstractSmackLowLevelIntegrationTest { AbstractXMPPConnection connection = getUnconnectedConnection(); connection.connect(); - SASLErrorException saslErrorException = assertThrows(SASLErrorException.class, - () -> connection.login(nonExistentUserString, invalidPassword)); + try { + SASLErrorException saslErrorException = assertThrows(SASLErrorException.class, + () -> connection.login(nonExistentUserString, invalidPassword)); - SaslNonza.SASLFailure saslFailure = saslErrorException.getSASLFailure(); - assertEquals(SASLError.not_authorized, saslFailure.getSASLError()); + SaslNonza.SASLFailure saslFailure = saslErrorException.getSASLFailure(); + assertEquals(SASLError.not_authorized, saslFailure.getSASLError()); + } finally { + connection.disconnect(); + } } } From dd4cd8cede84d8d0bbb9ce6ee8091b8885ea273c Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Sun, 31 May 2020 21:01:32 +0200 Subject: [PATCH 13/28] [sinttest] Disconnect unrecycleable connections --- .../org/igniterealtime/smack/inttest/XmppConnectionManager.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/XmppConnectionManager.java b/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/XmppConnectionManager.java index 207ab1c02..204562fd4 100644 --- a/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/XmppConnectionManager.java +++ b/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/XmppConnectionManager.java @@ -480,6 +480,8 @@ public class XmppConnectionManager { synchronized (connectionPool) { connectionPool.put(connectionClass, connection); } + } else { + connection.disconnect(); } // Note that we do not delete the account of the unauthenticated connection here, as it is done at the end of // the test run together with all other dynamically created accounts. From 6c84356278bb55c067ae0f336cd18cc6d914faa0 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Sun, 31 May 2020 21:01:57 +0200 Subject: [PATCH 14/28] [sinttest] Recycle low-level test connections --- .../smack/inttest/SmackIntegrationTestFramework.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/SmackIntegrationTestFramework.java b/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/SmackIntegrationTestFramework.java index b89402df8..14fedd388 100644 --- a/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/SmackIntegrationTestFramework.java +++ b/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/SmackIntegrationTestFramework.java @@ -863,6 +863,8 @@ public class SmackIntegrationTestFramework { } testMethod.invoke(test, connectionsArray); } + + connectionManager.recycle(connections); } @Override From e2a196fa52222a6816dd850ee8380335a90f7850 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Sun, 31 May 2020 21:08:14 +0200 Subject: [PATCH 15/28] [sinttest] Improve status output --- .../smack/inttest/SmackIntegrationTestFramework.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/SmackIntegrationTestFramework.java b/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/SmackIntegrationTestFramework.java index 14fedd388..e8d7758f8 100644 --- a/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/SmackIntegrationTestFramework.java +++ b/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/SmackIntegrationTestFramework.java @@ -454,10 +454,12 @@ public class SmackIntegrationTestFramework { } sb.append('\n'); } - sb.append("Available tests: ").append(numberOfAvailableTests) - .append("(#-classes: ").append(testRunResult.disabledTestClasses.size()) - .append(", #-tests: ").append(testRunResult.disabledTests.size()) - .append(")\n"); + sb.append("Available tests: ").append(numberOfAvailableTests); + if (!testRunResult.disabledTestClasses.isEmpty() || !testRunResult.disabledTests.isEmpty()) { + sb.append(" (Disabled ").append(testRunResult.disabledTestClasses.size()).append(" classes") + .append(" and ").append(testRunResult.disabledTests.size()).append(" tests"); + } + sb.append('\n'); LOGGER.info(sb.toString()); for (PreparedTest test : tests) { From ccbc0922ad7c70bca609e6df81fc5dbaa9e9ccc2 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Tue, 2 Jun 2020 10:05:56 +0200 Subject: [PATCH 16/28] [core] Fix comment --- .../java/org/jivesoftware/smack/AbstractXMPPConnection.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/smack-core/src/main/java/org/jivesoftware/smack/AbstractXMPPConnection.java b/smack-core/src/main/java/org/jivesoftware/smack/AbstractXMPPConnection.java index 0da542111..6e90e35b2 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/AbstractXMPPConnection.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/AbstractXMPPConnection.java @@ -1884,8 +1884,8 @@ public abstract class AbstractXMPPConnection implements XMPPConnection { if (hasFeature(Bind.ELEMENT, Bind.NAMESPACE)) { if (!hasFeature(Compress.Feature.ELEMENT, Compress.NAMESPACE) || !config.isCompressionEnabled()) { - // This was was last features from the server is either it did not contain - // compression or if we disabled it + // This where the last stream features from the server, either it did not contain + // compression or we disabled it. lastFeaturesReceived = true; notifyWaitingThreads(); } From 1d49de6b60052f641505c20e1c72855ccb727516 Mon Sep 17 00:00:00 2001 From: adiaholic Date: Thu, 14 May 2020 16:29:20 +0530 Subject: [PATCH 17/28] Add integration test for MultiUserChat SMACK-888 has a mention of unreachable code and is now solved under commit 0f7b7df. This integration test tests those class entities which were to be set by previously stated unreachable code. --- .../muc/MultiUserChatIntegrationTest.java | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smackx/muc/MultiUserChatIntegrationTest.java b/smack-integration-test/src/main/java/org/jivesoftware/smackx/muc/MultiUserChatIntegrationTest.java index 7ba0c1d1e..8943e9023 100644 --- a/smack-integration-test/src/main/java/org/jivesoftware/smackx/muc/MultiUserChatIntegrationTest.java +++ b/smack-integration-test/src/main/java/org/jivesoftware/smackx/muc/MultiUserChatIntegrationTest.java @@ -20,6 +20,7 @@ import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.List; +import java.util.concurrent.TimeoutException; import org.jivesoftware.smack.MessageListener; import org.jivesoftware.smack.SmackException.NoResponseException; @@ -39,6 +40,7 @@ import org.igniterealtime.smack.inttest.SmackIntegrationTestEnvironment; import org.igniterealtime.smack.inttest.TestNotPossibleException; import org.igniterealtime.smack.inttest.annotations.SmackIntegrationTest; import org.igniterealtime.smack.inttest.util.ResultSyncPoint; +import org.igniterealtime.smack.inttest.util.SimpleResultSyncPoint; import org.jxmpp.jid.DomainBareJid; import org.jxmpp.jid.EntityBareJid; import org.jxmpp.jid.impl.JidCreate; @@ -120,4 +122,41 @@ public class MultiUserChatIntegrationTest extends AbstractSmackIntegrationTest { mucAsSeenByOne.leave(); mucAsSeenByTwo.leave(); } + + @SmackIntegrationTest + public void mucDestroyTest() throws TimeoutException, Exception { + + EntityBareJid mucAddress = JidCreate.entityBareFrom(Localpart.from("smack-inttest-join-leave-" + randomString), + mucService.getDomain()); + + MultiUserChat muc = mucManagerOne.getMultiUserChat(mucAddress); + muc.join(Resourcepart.from("nick-one")); + + final SimpleResultSyncPoint mucDestroyed = new SimpleResultSyncPoint(); + + @SuppressWarnings("deprecation") + DefaultUserStatusListener userStatusListener = new DefaultUserStatusListener() { + @Override + public void roomDestroyed(MultiUserChat alternateMUC, String reason) { + mucDestroyed.signal(); + } + }; + + muc.addUserStatusListener(userStatusListener); + + assertTrue(mucManagerOne.getJoinedRooms().size() == 1); + assertTrue(muc.getOccupantsCount() == 1); + assertTrue(muc.getNickname() != null); + + try { + muc.destroy("Dummy reason", null); + mucDestroyed.waitForResult(timeout); + } finally { + muc.removeUserStatusListener(userStatusListener); + } + + assertTrue(mucManagerOne.getJoinedRooms().size() == 0); + assertTrue(muc.getOccupantsCount() == 0); + assertTrue(muc.getNickname() == null); + } } From f1319d1c8bf69a37dd8f6d3cf7f1a41bced74d44 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Thu, 11 Jun 2020 13:59:57 +0200 Subject: [PATCH 18/28] Move getters from Message to MessageView --- .../jivesoftware/smack/packet/Message.java | 179 +----------------- .../smack/packet/MessageView.java | 165 +++++++++++++++- .../org/jivesoftware/smack/packet/Stanza.java | 14 ++ 3 files changed, 182 insertions(+), 176 deletions(-) diff --git a/smack-core/src/main/java/org/jivesoftware/smack/packet/Message.java b/smack-core/src/main/java/org/jivesoftware/smack/packet/Message.java index 9b2d392a0..c424e339e 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/packet/Message.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/packet/Message.java @@ -17,12 +17,8 @@ package org.jivesoftware.smack.packet; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashSet; import java.util.List; import java.util.Locale; -import java.util.Set; import javax.xml.namespace.QName; @@ -186,59 +182,6 @@ public final class Message extends MessageOrPresence this.type = type; } - /** - * Returns the default subject of the message, or null if the subject has not been set. - * The subject is a short description of message contents. - *

- * The default subject of a message is the subject that corresponds to the message's language. - * (see {@link #getLanguage()}) or if no language is set to the applications default - * language (see {@link Stanza#getDefaultLanguage()}). - * - * @return the subject of the message. - */ - public String getSubject() { - return getSubject(null); - } - - /** - * Returns the subject corresponding to the language. If the language is null, the method result - * will be the same as {@link #getSubject()}. Null will be returned if the language does not have - * a corresponding subject. - * - * @param language the language of the subject to return. - * @return the subject related to the passed in language. - */ - public String getSubject(String language) { - Subject subject = getMessageSubject(language); - return subject == null ? null : subject.subject; - } - - private Subject getMessageSubject(String language) { - language = determineLanguage(language); - for (Subject subject : getSubjects()) { - if (Objects.equals(language, subject.language) - || (subject.language == null && Objects.equals(this.language, language))) { - return subject; - } - } - return null; - } - - /** - * Returns a set of all subjects in this Message, including the default message subject accessible - * from {@link #getSubject()}. - * - * @return a collection of all subjects in this message. - */ - public Set getSubjects() { - List subjectList = getExtensions(Subject.class); - - Set subjects = new HashSet<>(subjectList.size()); - subjects.addAll(subjectList); - - return subjects; - } - /** * Sets the subject of the message. The subject is a short description of * message contents. @@ -267,7 +210,7 @@ public final class Message extends MessageOrPresence @Deprecated // TODO: Remove when stanza builder is ready. public Subject addSubject(String language, String subject) { - language = determineLanguage(language); + language = Stanza.determineLanguage(this, language); List currentSubjects = getExtensions(Subject.class); for (Subject currentSubject : currentSubjects) { @@ -290,7 +233,7 @@ public final class Message extends MessageOrPresence @Deprecated // TODO: Remove when stanza builder is ready. public boolean removeSubject(String language) { - language = determineLanguage(language); + language = Stanza.determineLanguage(this, language); for (Subject subject : getExtensions(Subject.class)) { if (language.equals(subject.language)) { return removeSubject(subject); @@ -311,77 +254,6 @@ public final class Message extends MessageOrPresence return removeExtension(subject) != null; } - /** - * Returns all the languages being used for the subjects, not including the default subject. - * - * @return the languages being used for the subjects. - */ - public List getSubjectLanguages() { - Subject defaultSubject = getMessageSubject(null); - List languages = new ArrayList(); - for (Subject subject : getExtensions(Subject.class)) { - if (!subject.equals(defaultSubject)) { - languages.add(subject.language); - } - } - return Collections.unmodifiableList(languages); - } - - /** - * Returns the default body of the message, or null if the body has not been set. The body - * is the main message contents. - *

- * The default body of a message is the body that corresponds to the message's language. - * (see {@link #getLanguage()}) or if no language is set to the applications default - * language (see {@link Stanza#getDefaultLanguage()}). - * - * @return the body of the message. - */ - public String getBody() { - return getBody(language); - } - - /** - * Returns the body corresponding to the language. If the language is null, the method result - * will be the same as {@link #getBody()}. Null will be returned if the language does not have - * a corresponding body. - * - * @param language the language of the body to return. - * @return the body related to the passed in language. - * @since 3.0.2 - */ - public String getBody(String language) { - Body body = getMessageBody(language); - return body == null ? null : body.message; - } - - private Body getMessageBody(String language) { - language = determineLanguage(language); - for (Body body : getBodies()) { - if (Objects.equals(language, body.language) || (language != null && language.equals(this.language) && body.language == null)) { - return body; - } - } - return null; - } - - /** - * Returns a set of all bodies in this Message, including the default message body accessible - * from {@link #getBody()}. - * - * @return a collection of all bodies in this Message. - * @since 3.0.2 - */ - public Set getBodies() { - List bodiesList = getExtensions(Body.ELEMENT, Body.NAMESPACE); - Set resultSet = new HashSet<>(bodiesList.size()); - for (ExtensionElement extensionElement : bodiesList) { - Body body = (Body) extensionElement; - resultSet.add(body); - } - return resultSet; - } - /** * Sets the body of the message. * @@ -431,7 +303,7 @@ public final class Message extends MessageOrPresence @Deprecated // TODO: Remove when stanza builder is ready. public Body addBody(String language, String body) { - language = determineLanguage(language); + language = Stanza.determineLanguage(this, language); removeBody(language); @@ -450,7 +322,7 @@ public final class Message extends MessageOrPresence @Deprecated // TODO: Remove when stanza builder is ready. public boolean removeBody(String language) { - language = determineLanguage(language); + language = Stanza.determineLanguage(this, language); for (Body body : getBodies()) { String bodyLanguage = body.getLanguage(); if (Objects.equals(bodyLanguage, language)) { @@ -476,37 +348,6 @@ public final class Message extends MessageOrPresence return removedElement != null; } - /** - * Returns all the languages being used for the bodies, not including the default body. - * - * @return the languages being used for the bodies. - * @since 3.0.2 - */ - public List getBodyLanguages() { - Body defaultBody = getMessageBody(null); - List languages = new ArrayList(); - for (Body body : getBodies()) { - if (!body.equals(defaultBody)) { - languages.add(body.language); - } - } - return Collections.unmodifiableList(languages); - } - - /** - * Returns the thread id of the message, which is a unique identifier for a sequence - * of "chat" messages. If no thread id is set, null will be returned. - * - * @return the thread id of the message, or null if it doesn't exist. - */ - public String getThread() { - Message.Thread thread = getExtension(Message.Thread.class); - if (thread == null) { - return null; - } - return thread.getThread(); - } - /** * Sets the thread id of the message, which is a unique identifier for a sequence * of "chat" messages. @@ -520,18 +361,6 @@ public final class Message extends MessageOrPresence addExtension(new Message.Thread(thread)); } - private String determineLanguage(String language) { - - // empty string is passed by #setSubject() and #setBody() and is the same as null - language = "".equals(language) ? null : language; - - // if given language is null check if message language is set - if (language == null && this.language != null) { - return this.language; - } - return language; - } - @Override public String getElementName() { return ELEMENT; diff --git a/smack-core/src/main/java/org/jivesoftware/smack/packet/MessageView.java b/smack-core/src/main/java/org/jivesoftware/smack/packet/MessageView.java index 046b9274f..62d78b829 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/packet/MessageView.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/packet/MessageView.java @@ -1,6 +1,6 @@ /** * - * Copyright 2019 Florian Schmaus + * Copyright 2003-2007 Jive Software, 2019-2020 Florian Schmaus * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,6 +16,15 @@ */ package org.jivesoftware.smack.packet; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.jivesoftware.smack.packet.Message.Subject; +import org.jivesoftware.smack.util.Objects; + public interface MessageView extends StanzaView { /** @@ -26,4 +35,158 @@ public interface MessageView extends StanzaView { */ Message.Type getType(); + /** + * Returns the default subject of the message, or null if the subject has not been set. + * The subject is a short description of message contents. + *

+ * The default subject of a message is the subject that corresponds to the message's language. + * (see {@link #getLanguage()}) or if no language is set to the applications default + * language (see {@link Stanza#getDefaultLanguage()}). + * + * @return the subject of the message. + */ + default String getSubject() { + return getSubject(null); + } + + /** + * Returns the subject corresponding to the language. If the language is null, the method result + * will be the same as {@link #getSubject()}. Null will be returned if the language does not have + * a corresponding subject. + * + * @param language the language of the subject to return. + * @return the subject related to the passed in language. + */ + default String getSubject(String language) { + Subject subject = getMessageSubject(language); + return subject == null ? null : subject.getSubject(); + } + + default Message.Subject getMessageSubject(String language) { + language = Stanza.determineLanguage(this, language); + for (Message.Subject subject : getSubjects()) { + if (Objects.equals(language, subject.getLanguage()) + || (subject.getLanguage() == null && Objects.equals(getLanguage(), language))) { + return subject; + } + } + return null; + } + + /** + * Returns a set of all subjects in this Message, including the default message subject accessible + * from {@link #getSubject()}. + * + * @return a collection of all subjects in this message. + */ + default Set getSubjects() { + List subjectList = getExtensions(Subject.class); + + Set subjects = new HashSet<>(subjectList.size()); + subjects.addAll(subjectList); + + return subjects; + } + + /** + * Returns all the languages being used for the subjects, not including the default subject. + * + * @return the languages being used for the subjects. + */ + default List getSubjectLanguages() { + Message.Subject defaultSubject = getMessageSubject(null); + List languages = new ArrayList(); + for (Message.Subject subject : getExtensions(Message.Subject.class)) { + if (!subject.equals(defaultSubject)) { + languages.add(subject.getLanguage()); + } + } + return Collections.unmodifiableList(languages); + } + + /** + * Returns the default body of the message, or null if the body has not been set. The body + * is the main message contents. + *

+ * The default body of a message is the body that corresponds to the message's language. + * (see {@link #getLanguage()}) or if no language is set to the applications default + * language (see {@link Stanza#getDefaultLanguage()}). + * + * @return the body of the message. + */ + default String getBody() { + return getBody(getLanguage()); + } + + /** + * Returns the body corresponding to the language. If the language is null, the method result + * will be the same as {@link #getBody()}. Null will be returned if the language does not have + * a corresponding body. + * + * @param language the language of the body to return. + * @return the body related to the passed in language. + * @since 3.0.2 + */ + default String getBody(String language) { + Message.Body body = getMessageBody(language); + return body == null ? null : body.getMessage(); + } + + default Message.Body getMessageBody(String language) { + language = Stanza.determineLanguage(this, language); + for (Message.Body body : getBodies()) { + if (Objects.equals(language, body.getLanguage()) || (language != null && language.equals(getLanguage()) && body.getLanguage() == null)) { + return body; + } + } + return null; + } + + /** + * Returns a set of all bodies in this Message, including the default message body accessible + * from {@link #getBody()}. + * + * @return a collection of all bodies in this Message. + * @since 3.0.2 + */ + default Set getBodies() { + List bodiesList = getExtensions(Message.Body.QNAME); + Set resultSet = new HashSet<>(bodiesList.size()); + for (ExtensionElement extensionElement : bodiesList) { + Message.Body body = (Message.Body) extensionElement; + resultSet.add(body); + } + return resultSet; + } + + /** + * Returns all the languages being used for the bodies, not including the default body. + * + * @return the languages being used for the bodies. + * @since 3.0.2 + */ + default List getBodyLanguages() { + Message.Body defaultBody = getMessageBody(null); + List languages = new ArrayList(); + for (Message.Body body : getBodies()) { + if (!body.equals(defaultBody)) { + languages.add(body.getLanguage()); + } + } + return Collections.unmodifiableList(languages); + } + + /** + * Returns the thread id of the message, which is a unique identifier for a sequence + * of "chat" messages. If no thread id is set, null will be returned. + * + * @return the thread id of the message, or null if it doesn't exist. + */ + default String getThread() { + Message.Thread thread = getExtension(Message.Thread.class); + if (thread == null) { + return null; + } + return thread.getThread(); + } } diff --git a/smack-core/src/main/java/org/jivesoftware/smack/packet/Stanza.java b/smack-core/src/main/java/org/jivesoftware/smack/packet/Stanza.java index ee9377d1a..bce5ba0f5 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/packet/Stanza.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/packet/Stanza.java @@ -575,4 +575,18 @@ public abstract class Stanza implements StanzaView, TopLevelStreamElement { xml.append(error); } } + + /** + * Return the provided non-empty language, or use this {@link XmlLangElement} language (if set). + * + * @param language the provided language, may be the empty string or null. + * @return the provided language or this element's language (if set). + */ + static String determineLanguage(XmlLangElement xmlLangElement, String language) { + if (language != null && !language.isEmpty()) { + return language; + } + + return xmlLangElement.getLanguage(); + } } From 8e337d7810bbfb5d4a4bd1b5a68e471f865a4ea1 Mon Sep 17 00:00:00 2001 From: damencho Date: Thu, 11 Jun 2020 13:12:13 -0500 Subject: [PATCH 19/28] [muc] Make providesMucService() use the KNOWN_MUC_SERVICES cache This reduces the number of disco#info queries on MUC join in some situations. --- .../jivesoftware/smackx/muc/MultiUserChat.java | 12 ++---------- .../smackx/muc/MultiUserChatManager.java | 16 ++++++++++++++-- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/muc/MultiUserChat.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/muc/MultiUserChat.java index 40c3a85a2..f5ee3ec53 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/muc/MultiUserChat.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/muc/MultiUserChat.java @@ -88,7 +88,6 @@ import org.jxmpp.jid.EntityJid; import org.jxmpp.jid.Jid; import org.jxmpp.jid.impl.JidCreate; import org.jxmpp.jid.parts.Resourcepart; -import org.jxmpp.util.cache.ExpirationCache; /** * A MultiUserChat room (XEP-45), created with {@link MultiUserChatManager#getMultiUserChat(EntityBareJid)}. @@ -112,9 +111,6 @@ import org.jxmpp.util.cache.ExpirationCache; public class MultiUserChat { private static final Logger LOGGER = Logger.getLogger(MultiUserChat.class.getName()); - private static final ExpirationCache KNOWN_MUC_SERVICES = new ExpirationCache<>( - 100, 1000 * 60 * 60 * 24); - private final XMPPConnection connection; private final EntityBareJid room; private final MultiUserChatManager multiUserChatManager; @@ -340,12 +336,8 @@ public class MultiUserChat { private Presence enter(MucEnterConfiguration conf) throws NotConnectedException, NoResponseException, XMPPErrorException, InterruptedException, NotAMucServiceException { final DomainBareJid mucService = room.asDomainBareJid(); - if (!KNOWN_MUC_SERVICES.containsKey(mucService)) { - if (multiUserChatManager.providesMucService(mucService)) { - KNOWN_MUC_SERVICES.put(mucService, null); - } else { - throw new NotAMucServiceException(this); - } + if (!multiUserChatManager.providesMucService(mucService)) { + throw new NotAMucServiceException(this); } // We enter a room by sending a presence packet where the "to" // field is in the form "roomName@service/nickname" diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/muc/MultiUserChatManager.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/muc/MultiUserChatManager.java index e2654de89..7d92e9817 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/muc/MultiUserChatManager.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/muc/MultiUserChatManager.java @@ -64,6 +64,7 @@ import org.jxmpp.jid.EntityFullJid; import org.jxmpp.jid.EntityJid; import org.jxmpp.jid.Jid; import org.jxmpp.jid.parts.Resourcepart; +import org.jxmpp.util.cache.ExpirationCache; /** * A manager for Multi-User Chat rooms. @@ -136,6 +137,9 @@ public final class MultiUserChatManager extends Manager { private static final StanzaFilter INVITATION_FILTER = new AndFilter(StanzaTypeFilter.MESSAGE, new StanzaExtensionFilter(new MUCUser()), new NotFilter(MessageTypeFilter.ERROR)); + private static final ExpirationCache KNOWN_MUC_SERVICES = new ExpirationCache<>( + 100, 1000 * 60 * 60 * 24); + private final Set invitationsListeners = new CopyOnWriteArraySet(); /** @@ -392,8 +396,16 @@ public final class MultiUserChatManager extends Manager { */ public boolean providesMucService(DomainBareJid domainBareJid) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { - return serviceDiscoveryManager.supportsFeature(domainBareJid, - MUCInitialPresence.NAMESPACE); + boolean contains = KNOWN_MUC_SERVICES.containsKey(domainBareJid); + if (!contains) { + if (serviceDiscoveryManager.supportsFeature(domainBareJid, + MUCInitialPresence.NAMESPACE)) { + KNOWN_MUC_SERVICES.put(domainBareJid, null); + return true; + } + } + + return contains; } /** From 843c8dd7fe59a778a5d19b529f0af808ed31258a Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Fri, 12 Jun 2020 16:35:32 +0200 Subject: [PATCH 20/28] [tcp] Add missing notifyWaitingThreads() after receiving SM --- .../main/java/org/jivesoftware/smack/tcp/XMPPTCPConnection.java | 1 + 1 file changed, 1 insertion(+) diff --git a/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XMPPTCPConnection.java b/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XMPPTCPConnection.java index 3e30f83b5..b641fd183 100644 --- a/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XMPPTCPConnection.java +++ b/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XMPPTCPConnection.java @@ -1016,6 +1016,7 @@ public class XMPPTCPConnection extends AbstractXMPPConnection { // This is a nonza in a response to resuming a previous stream, failure to do // so is non-fatal as we can simply continue with resource binding in this case. smResumptionFailed = failed; + notifyWaitingThreads(); } else { FailedNonzaException xmppException = new FailedNonzaException(failed, failed.getStanzaErrorCondition()); setCurrentConnectionExceptionAndNotify(xmppException); From fd1b49ca8f5d3b8918101f66549c5688b674ca10 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Fri, 12 Jun 2020 18:15:24 +0200 Subject: [PATCH 21/28] [tcp] Set sync points before they are used, and not in init() --- .../jivesoftware/smack/tcp/XMPPTCPConnection.java | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XMPPTCPConnection.java b/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XMPPTCPConnection.java index b641fd183..8faaf30d9 100644 --- a/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XMPPTCPConnection.java +++ b/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XMPPTCPConnection.java @@ -364,6 +364,8 @@ public class XMPPTCPConnection extends AbstractXMPPConnection { SmackException, IOException, InterruptedException { // Authenticate using SASL SSLSession sslSession = secureSocket != null ? secureSocket.getSession() : null; + + streamFeaturesAfterAuthenticationReceived = false; authenticate(username, password, config.getAuthzid(), sslSession); // Wait for stream features after the authentication. @@ -375,6 +377,8 @@ public class XMPPTCPConnection extends AbstractXMPPConnection { // recommends to perform stream compression before resource binding. maybeEnableCompression(); + smResumedSyncPoint = SyncPointState.initial; + smResumptionFailed = null; if (isSmResumptionPossible()) { smResumedSyncPoint = SyncPointState.request_sent; sendNonza(new Resume(clientHandledStanzasCount, smSessionId)); @@ -409,6 +413,7 @@ public class XMPPTCPConnection extends AbstractXMPPConnection { // unacknowledgedStanzas and become duplicated on reconnect. See SMACK-706. bindResourceAndEstablishSession(resource); + smEnabledSyncPoint = false; if (isSmAvailable() && useSm) { // Remove what is maybe left from previously stream managed sessions serverHandledStanzasCount = 0; @@ -535,15 +540,6 @@ public class XMPPTCPConnection extends AbstractXMPPConnection { initState(); } - @Override - protected void initState() { - super.initState(); - streamFeaturesAfterAuthenticationReceived = compressSyncPoint = false; - smResumedSyncPoint = SyncPointState.initial; - smResumptionFailed = null; - smEnabledSyncPoint = false; - } - @Override public void sendNonza(Nonza element) throws NotConnectedException, InterruptedException { packetWriter.sendStreamElement(element); @@ -794,6 +790,7 @@ public class XMPPTCPConnection extends AbstractXMPPConnection { // If stream compression was offered by the server and we want to use // compression then send compression request to the server if ((compressionHandler = maybeGetCompressionHandler(compression)) != null) { + compressSyncPoint = false; sendNonza(new Compress(compressionHandler.getCompressionMethod())); waitForConditionOrThrowConnectionException(() -> compressSyncPoint, "establishing stream compression"); } else { From cf1f533fff954f4be8f640838ff8d22c6e7f7202 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Fri, 12 Jun 2020 21:16:05 +0200 Subject: [PATCH 22/28] [tcp] Mark SM sync points as volatile --- .../smack/tcp/XMPPTCPConnection.java | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XMPPTCPConnection.java b/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XMPPTCPConnection.java index 8faaf30d9..ded929681 100644 --- a/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XMPPTCPConnection.java +++ b/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XMPPTCPConnection.java @@ -190,10 +190,22 @@ public class XMPPTCPConnection extends AbstractXMPPConnection { */ private String smSessionId; - private SyncPointState smResumedSyncPoint; + /** + * Represents the state of stream management resumption. + *

+ * Unlike other sync points, this sync point is marked volatile because it is also read by the reader thread. + *

+ */ + private volatile SyncPointState smResumedSyncPoint; private Failed smResumptionFailed; - private boolean smEnabledSyncPoint; + /** + * Represents the state of stream magement. + *

+ * This boolean is marked volatile as it is read by various threads, including the reader thread via {@link #isSmEnabled()}. + *

+ */ + private volatile boolean smEnabledSyncPoint; /** * The client's preferred maximum resumption time in seconds. From c689bef7ec7a90c7ba92df5d78ec15a79f432496 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Sun, 14 Jun 2020 16:51:28 +0200 Subject: [PATCH 23/28] Add static QNAME field to Compressed, Failure and Tls(Failure|Proceed) --- .../org/jivesoftware/smack/compress/packet/Compressed.java | 5 ++++- .../java/org/jivesoftware/smack/compress/packet/Failure.java | 5 ++++- .../main/java/org/jivesoftware/smack/packet/TlsFailure.java | 5 ++++- .../main/java/org/jivesoftware/smack/packet/TlsProceed.java | 5 ++++- 4 files changed, 16 insertions(+), 4 deletions(-) diff --git a/smack-core/src/main/java/org/jivesoftware/smack/compress/packet/Compressed.java b/smack-core/src/main/java/org/jivesoftware/smack/compress/packet/Compressed.java index 22026a161..e0278cff4 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/compress/packet/Compressed.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/compress/packet/Compressed.java @@ -1,6 +1,6 @@ /** * - * Copyright © 2014-2015 Florian Schmaus + * Copyright © 2014-2020 Florian Schmaus * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,12 +16,15 @@ */ package org.jivesoftware.smack.compress.packet; +import javax.xml.namespace.QName; + import org.jivesoftware.smack.packet.Nonza; public final class Compressed implements Nonza { public static final String ELEMENT = "compressed"; public static final String NAMESPACE = Compress.NAMESPACE; + public static final QName QNAME = new QName(NAMESPACE, ELEMENT); public static final Compressed INSTANCE = new Compressed(); diff --git a/smack-core/src/main/java/org/jivesoftware/smack/compress/packet/Failure.java b/smack-core/src/main/java/org/jivesoftware/smack/compress/packet/Failure.java index 473abaccf..2d409db65 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/compress/packet/Failure.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/compress/packet/Failure.java @@ -1,6 +1,6 @@ /** * - * Copyright 2018 Florian Schmaus + * Copyright 2018-2020 Florian Schmaus * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,6 +18,8 @@ package org.jivesoftware.smack.compress.packet; import java.util.Objects; +import javax.xml.namespace.QName; + import org.jivesoftware.smack.packet.Nonza; import org.jivesoftware.smack.packet.StanzaError; import org.jivesoftware.smack.util.XmlStringBuilder; @@ -26,6 +28,7 @@ public class Failure implements Nonza { public static final String ELEMENT = "failure"; public static final String NAMESPACE = Compress.NAMESPACE; + public static final QName QNAME = new QName(NAMESPACE, ELEMENT); public enum CompressFailureError { setup_failed, diff --git a/smack-core/src/main/java/org/jivesoftware/smack/packet/TlsFailure.java b/smack-core/src/main/java/org/jivesoftware/smack/packet/TlsFailure.java index 05aa4b6fa..bce64aa05 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/packet/TlsFailure.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/packet/TlsFailure.java @@ -1,6 +1,6 @@ /** * - * Copyright 2018 Florian Schmaus + * Copyright 2018-2020 Florian Schmaus * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,12 +16,15 @@ */ package org.jivesoftware.smack.packet; +import javax.xml.namespace.QName; + public final class TlsFailure implements Nonza { public static final TlsFailure INSTANCE = new TlsFailure(); public static final String ELEMENT = "failure"; public static final String NAMESPACE = TlsProceed.NAMESPACE; + public static final QName QNAME = new QName(NAMESPACE, ELEMENT); private TlsFailure() { } diff --git a/smack-core/src/main/java/org/jivesoftware/smack/packet/TlsProceed.java b/smack-core/src/main/java/org/jivesoftware/smack/packet/TlsProceed.java index 7967a89b7..cf26f3da1 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/packet/TlsProceed.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/packet/TlsProceed.java @@ -1,6 +1,6 @@ /** * - * Copyright 2018 Florian Schmaus + * Copyright 2018-2020 Florian Schmaus * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,12 +16,15 @@ */ package org.jivesoftware.smack.packet; +import javax.xml.namespace.QName; + public final class TlsProceed implements Nonza { public static final TlsProceed INSTANCE = new TlsProceed(); public static final String ELEMENT = "proceed"; public static final String NAMESPACE = "urn:ietf:params:xml:ns:xmpp-tls"; + public static final QName QNAME = new QName(NAMESPACE, ELEMENT); private TlsProceed() { } From 9d6665735f236d1aba77c9bef8cc0de0b703c615 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Sun, 14 Jun 2020 16:52:13 +0200 Subject: [PATCH 24/28] [pubsub] Rework NodeExtension.toXML() --- .../smackx/pubsub/AffiliationsExtension.java | 23 ++++-------- .../jivesoftware/smackx/pubsub/FormNode.java | 24 ++++-------- .../smackx/pubsub/GetItemsRequest.java | 6 +-- .../org/jivesoftware/smackx/pubsub/Item.java | 16 +------- .../smackx/pubsub/ItemsExtension.java | 37 ++++++------------- .../smackx/pubsub/NodeExtension.java | 15 +++++++- .../smackx/pubsub/OptionsExtension.java | 8 ++-- .../smackx/pubsub/PayloadItem.java | 9 ++--- .../smackx/pubsub/PublishItem.java | 19 +++------- .../smackx/pubsub/SubscribeExtension.java | 6 +-- .../smackx/pubsub/Subscription.java | 15 +++----- .../smackx/pubsub/SubscriptionsExtension.java | 30 ++++----------- .../smackx/pubsub/UnsubscribeExtension.java | 6 +-- .../pubsub/AffiliationsExtensionTest.java | 7 ++-- 14 files changed, 76 insertions(+), 145 deletions(-) diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/AffiliationsExtension.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/AffiliationsExtension.java index 2e30dd04c..c18a68243 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/AffiliationsExtension.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/AffiliationsExtension.java @@ -32,7 +32,6 @@ import org.jivesoftware.smackx.pubsub.Affiliation.AffiliationNamespace; */ public class AffiliationsExtension extends NodeExtension { protected List items = Collections.emptyList(); - private final String node; public AffiliationsExtension() { this(null); @@ -51,9 +50,8 @@ public class AffiliationsExtension extends NodeExtension { } public AffiliationsExtension(AffiliationNamespace affiliationsNamespace, List subList, String node) { - super(affiliationsNamespace.type); + super(affiliationsNamespace.type, node); items = subList; - this.node = node; } public List getAffiliations() { @@ -61,19 +59,14 @@ public class AffiliationsExtension extends NodeExtension { } @Override - public CharSequence toXML(org.jivesoftware.smack.packet.XmlEnvironment enclosingNamespace) { + protected void addXml(XmlStringBuilder xml) { if ((items == null) || (items.size() == 0)) { - return super.toXML(enclosingNamespace); - } - else { - // Can't use XmlStringBuilder(this), because we don't want the namespace to be included - XmlStringBuilder xml = new XmlStringBuilder(); - xml.halfOpenElement(getElementName()); - xml.optAttribute("node", node); - xml.rightAngleBracket(); - xml.append(items); - xml.closeElement(this); - return xml; + xml.closeEmptyElement(); + return; } + + xml.rightAngleBracket(); + xml.append(items); + xml.closeElement(this); } } diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/FormNode.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/FormNode.java index 6c3c69171..4d1a46cdd 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/FormNode.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/FormNode.java @@ -16,6 +16,8 @@ */ package org.jivesoftware.smackx.pubsub; +import org.jivesoftware.smack.util.XmlStringBuilder; + import org.jivesoftware.smackx.xdata.packet.DataForm; /** @@ -69,26 +71,14 @@ public class FormNode extends NodeExtension { } @Override - public CharSequence toXML(org.jivesoftware.smack.packet.XmlEnvironment enclosingNamespace) { + protected void addXml(XmlStringBuilder xml) { if (configForm == null) { - return super.toXML(enclosingNamespace); + xml.closeEmptyElement(); + return; } - else { - StringBuilder builder = new StringBuilder("<"); - builder.append(getElementName()); - if (getNode() != null) { - builder.append(" node='"); - builder.append(getNode()); - builder.append("'>"); - } - else - builder.append('>'); - builder.append(configForm.toXML()); - builder.append("'); - return builder.toString(); - } + xml.append(configForm); + xml.closeElement(this); } } diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/GetItemsRequest.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/GetItemsRequest.java index d55637a7c..f929dd355 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/GetItemsRequest.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/GetItemsRequest.java @@ -54,13 +54,9 @@ public class GetItemsRequest extends NodeExtension { } @Override - public XmlStringBuilder toXML(org.jivesoftware.smack.packet.XmlEnvironment enclosingNamespace) { - XmlStringBuilder xml = new XmlStringBuilder(); - xml.halfOpenElement(getElementName()); - xml.attribute("node", getNode()); + protected void addXml(XmlStringBuilder xml) { xml.optAttribute("subid", getSubscriptionId()); xml.optIntAttribute("max_items", getMaxItems()); xml.closeEmptyElement(); - return xml; } } diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/Item.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/Item.java index 727de6c48..0cb4db2e3 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/Item.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/Item.java @@ -150,21 +150,9 @@ public class Item extends NodeExtension { } @Override - public XmlStringBuilder toXML(org.jivesoftware.smack.packet.XmlEnvironment enclosingNamespace) { - XmlStringBuilder xml = getCommonXml(); - - xml.closeEmptyElement(); - - return xml; - } - - protected final XmlStringBuilder getCommonXml() { - XmlStringBuilder xml = new XmlStringBuilder(this); - + protected void addXml(XmlStringBuilder xml) { xml.optAttribute("id", getId()); - xml.optAttribute("node", getNode()); - - return xml; + xml.closeEmptyElement(); } @Override diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/ItemsExtension.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/ItemsExtension.java index 000138897..02ae67901 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/ItemsExtension.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/ItemsExtension.java @@ -20,6 +20,7 @@ import java.util.List; import org.jivesoftware.smack.packet.ExtensionElement; import org.jivesoftware.smack.packet.NamedElement; +import org.jivesoftware.smack.util.XmlStringBuilder; /** * This class is used for multiple purposes. @@ -150,35 +151,21 @@ public class ItemsExtension extends NodeExtension implements EmbeddedPacketExten } @Override - public CharSequence toXML(org.jivesoftware.smack.packet.XmlEnvironment enclosingNamespace) { + protected void addXml(XmlStringBuilder xml) { if ((items == null) || (items.size() == 0)) { - return super.toXML(enclosingNamespace); + xml.closeEmptyElement(); + return; } - else { - StringBuilder builder = new StringBuilder("<"); - builder.append(getElementName()); - builder.append(" node='"); - builder.append(getNode()); - if (notify != null) { - builder.append("' "); - builder.append(type.getElementAttribute()); - builder.append("='"); - builder.append(notify.equals(Boolean.TRUE) ? 1 : 0); - builder.append("'>"); - } - else { - builder.append("'>"); - for (NamedElement item : items) { - builder.append(item.toXML()); - } - } - - builder.append("'); - return builder.toString(); + if (notify != null) { + xml.attribute(type.getElementAttribute(), notify); + xml.rightAngleBracket(); + } else { + xml.rightAngleBracket(); + xml.append(items); } + + xml.closeElement(this); } @Override diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/NodeExtension.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/NodeExtension.java index cc8fac480..d89e438ca 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/NodeExtension.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/NodeExtension.java @@ -17,6 +17,8 @@ package org.jivesoftware.smackx.pubsub; import org.jivesoftware.smack.packet.ExtensionElement; +import org.jivesoftware.smack.packet.XmlEnvironment; +import org.jivesoftware.smack.util.XmlStringBuilder; import org.jivesoftware.smackx.pubsub.packet.PubSubNamespace; @@ -78,8 +80,17 @@ public class NodeExtension implements ExtensionElement { } @Override - public CharSequence toXML(org.jivesoftware.smack.packet.XmlEnvironment enclosingNamespace) { - return '<' + getElementName() + (node == null ? "" : " node='" + node + '\'') + "/>"; + public final XmlStringBuilder toXML(XmlEnvironment enclosingNamespace) { + XmlStringBuilder xml = new XmlStringBuilder(this, enclosingNamespace); + xml.optAttribute("node", node); + + addXml(xml); + + return xml; + } + + protected void addXml(XmlStringBuilder xml) { + xml.closeEmptyElement(); } @Override diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/OptionsExtension.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/OptionsExtension.java index c9bbf6ab2..7249f9eea 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/OptionsExtension.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/OptionsExtension.java @@ -50,13 +50,15 @@ public class OptionsExtension extends NodeExtension { } @Override - public XmlStringBuilder toXML(org.jivesoftware.smack.packet.XmlEnvironment enclosingNamespace) { - XmlStringBuilder xml = new XmlStringBuilder(); + protected void addXml(XmlStringBuilder xml) { + xml.rightAngleBracket(); + xml.halfOpenElement(getElementName()); xml.attribute("jid", jid); xml.optAttribute("node", getNode()); xml.optAttribute("subid", id); + xml.closeEmptyElement(); - return xml; + xml.closeElement(this); } } diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/PayloadItem.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/PayloadItem.java index f22a8e876..6cb4210d8 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/PayloadItem.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/PayloadItem.java @@ -132,14 +132,11 @@ public class PayloadItem extends Item { } @Override - public XmlStringBuilder toXML(org.jivesoftware.smack.packet.XmlEnvironment enclosingNamespace) { - XmlStringBuilder xml = getCommonXml(); - + protected void addXml(XmlStringBuilder xml) { + xml.optAttribute("id", getId()); xml.rightAngleBracket(); - xml.append(payload.toXML()); + xml.append(payload); xml.closeElement(this); - - return xml; } @Override diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/PublishItem.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/PublishItem.java index ec38e7aa5..7bdead52f 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/PublishItem.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/PublishItem.java @@ -19,6 +19,8 @@ package org.jivesoftware.smackx.pubsub; import java.util.ArrayList; import java.util.Collection; +import org.jivesoftware.smack.util.XmlStringBuilder; + /** * Represents a request to publish an item(s) to a specific node. * @@ -51,18 +53,9 @@ public class PublishItem extends NodeExtension { } @Override - public String toXML(org.jivesoftware.smack.packet.XmlEnvironment enclosingNamespace) { - StringBuilder builder = new StringBuilder("<"); - builder.append(getElementName()); - builder.append(" node='"); - builder.append(getNode()); - builder.append("'>"); - - for (Item item : items) { - builder.append(item.toXML()); - } - builder.append(""); - - return builder.toString(); + protected void addXml(XmlStringBuilder xml) { + xml.rightAngleBracket(); + xml.append(items); + xml.closeElement(this); } } diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/SubscribeExtension.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/SubscribeExtension.java index 9c4a55b10..64ef67d62 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/SubscribeExtension.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/SubscribeExtension.java @@ -16,7 +16,6 @@ */ package org.jivesoftware.smackx.pubsub; -import org.jivesoftware.smack.packet.XmlEnvironment; import org.jivesoftware.smack.util.XmlStringBuilder; import org.jxmpp.jid.Jid; @@ -44,11 +43,8 @@ public class SubscribeExtension extends NodeExtension { } @Override - public XmlStringBuilder toXML(XmlEnvironment xmlEnvironment) { - XmlStringBuilder xml = new XmlStringBuilder(this, xmlEnvironment); - xml.optAttribute("node", getNode()); + protected void addXml(XmlStringBuilder xml) { xml.attribute("jid", getJid()); xml.closeEmptyElement(); - return xml; } } diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/Subscription.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/Subscription.java index 3b81ca016..c9b59896d 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/Subscription.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/Subscription.java @@ -138,16 +138,11 @@ public class Subscription extends NodeExtension { } @Override - public XmlStringBuilder toXML(org.jivesoftware.smack.packet.XmlEnvironment enclosingNamespace) { - XmlStringBuilder builder = new XmlStringBuilder(this); - builder.attribute("jid", jid); - - builder.optAttribute("node", getNode()); - builder.optAttribute("subid", id); - builder.optAttribute("subscription", state.toString()); - - builder.closeEmptyElement(); - return builder; + protected void addXml(XmlStringBuilder xml) { + xml.attribute("jid", jid); + xml.optAttribute("subid", id); + xml.optAttribute("subscription", state); + xml.closeEmptyElement(); } } diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/SubscriptionsExtension.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/SubscriptionsExtension.java index 8abde5836..b9f3ee7df 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/SubscriptionsExtension.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/SubscriptionsExtension.java @@ -19,6 +19,8 @@ package org.jivesoftware.smackx.pubsub; import java.util.Collections; import java.util.List; +import org.jivesoftware.smack.util.XmlStringBuilder; + /** * Represents the element holding the list of subscription elements. * @@ -91,29 +93,13 @@ public class SubscriptionsExtension extends NodeExtension { } @Override - public CharSequence toXML(org.jivesoftware.smack.packet.XmlEnvironment enclosingNamespace) { + protected void addXml(XmlStringBuilder xml) { if ((items == null) || (items.size() == 0)) { - return super.toXML(enclosingNamespace); - } - else { - StringBuilder builder = new StringBuilder("<"); - builder.append(getElementName()); - - if (getNode() != null) { - builder.append(" node='"); - builder.append(getNode()); - builder.append('\''); - } - builder.append('>'); - - for (Subscription item : items) { - builder.append(item.toXML()); - } - - builder.append("'); - return builder.toString(); + xml.closeEmptyElement(); + return; } + xml.rightAngleBracket(); + xml.append(items); + xml.closeElement(this); } } diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/UnsubscribeExtension.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/UnsubscribeExtension.java index 107271786..3c801ff21 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/UnsubscribeExtension.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/UnsubscribeExtension.java @@ -51,13 +51,9 @@ public class UnsubscribeExtension extends NodeExtension { } @Override - public XmlStringBuilder toXML(org.jivesoftware.smack.packet.XmlEnvironment enclosingNamespace) { - XmlStringBuilder xml = new XmlStringBuilder(); - xml.halfOpenElement(getElementName()); + protected void addXml(XmlStringBuilder xml) { xml.attribute("jid", jid); - xml.optAttribute("node", getNode()); xml.optAttribute("subid", id); xml.closeEmptyElement(); - return xml; } } diff --git a/smack-extensions/src/test/java/org/jivesoftware/smackx/pubsub/AffiliationsExtensionTest.java b/smack-extensions/src/test/java/org/jivesoftware/smackx/pubsub/AffiliationsExtensionTest.java index 5a03d5ed3..c584ca73c 100644 --- a/smack-extensions/src/test/java/org/jivesoftware/smackx/pubsub/AffiliationsExtensionTest.java +++ b/smack-extensions/src/test/java/org/jivesoftware/smackx/pubsub/AffiliationsExtensionTest.java @@ -1,6 +1,6 @@ /** * - * Copyright 2017 Florian Schmaus + * Copyright 2017-2020 Florian Schmaus * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,6 +23,7 @@ import java.util.ArrayList; import java.util.List; import org.jivesoftware.smackx.pubsub.Affiliation.Type; +import org.jivesoftware.smackx.pubsub.packet.PubSub; import org.junit.jupiter.api.Test; import org.jxmpp.jid.BareJid; @@ -40,10 +41,10 @@ public class AffiliationsExtensionTest { AffiliationsExtension affiliationsExtension = new AffiliationsExtension(affiliationsList, "testNode"); - CharSequence xml = affiliationsExtension.toXML(); + CharSequence xml = affiliationsExtension.toXML(PubSub.NAMESPACE); assertXmlSimilar("", - xml.toString()); + xml); } } From 18c2c37ad064e51671583885e9c3126d7a5c29ca Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Sun, 14 Jun 2020 16:53:21 +0200 Subject: [PATCH 25/28] Rename XmlUnitUtils to XmlAssertUtil --- .../org/jivesoftware/smack/compress/packet/FailureTest.java | 2 +- .../java/org/jivesoftware/smack/packet/MessageTest.java | 2 +- .../java/org/jivesoftware/smack/packet/PresenceTest.java | 2 +- .../org/jivesoftware/smack/provider/SaslProviderTest.java | 2 +- .../org/jivesoftware/smack/util/PacketParserUtilsTest.java | 4 ++-- .../org/jivesoftware/smack/util/XmlStringBuilderTest.java | 6 +++--- .../test/util/{XmlUnitUtils.java => XmlAssertUtil.java} | 5 ++--- .../jivesoftware/smackx/httpfileupload/SlotCreateTest.java | 2 +- .../smackx/httpfileupload/SlotRequestCreateTest.java | 2 +- .../smackx/httpfileupload/provider/SlotProviderTest.java | 2 +- .../org/jivesoftware/smackx/mam/RetrieveFormFieldsTest.java | 2 +- .../message_fastening/MessageFasteningElementsTest.java | 2 +- .../smackx/message_markup/MessageMarkupTest.java | 2 +- .../message_retraction/element/RetractElementTest.java | 2 +- .../message_retraction/element/RetractedElementTest.java | 2 +- .../org/jivesoftware/smackx/reference/ReferenceTest.java | 2 +- .../jivesoftware/smackx/sid/StableUniqueStanzaIdTest.java | 2 +- .../java/org/jivesoftware/smackx/spoiler/SpoilerTest.java | 2 +- .../jivesoftware/smackx/attention/AttentionElementTest.java | 2 +- .../smackx/bytestreams/ibb/packet/CloseTest.java | 2 +- .../bytestreams/ibb/packet/DataPacketExtensionTest.java | 2 +- .../smackx/bytestreams/ibb/packet/DataTest.java | 2 +- .../smackx/bytestreams/ibb/packet/OpenTest.java | 2 +- .../org/jivesoftware/smackx/last_interaction/IdleTest.java | 2 +- .../jivesoftware/smackx/mood/MoodConcretisationTest.java | 2 +- .../java/org/jivesoftware/smackx/mood/MoodElementTest.java | 2 +- .../test/java/org/jivesoftware/smackx/nick/NickTest.java | 2 +- .../smackx/pubsub/AffiliationsExtensionTest.java | 2 +- .../org/jivesoftware/smackx/pubsub/ItemValidationTest.java | 2 +- .../smackx/softwareinfo/SoftwareInfoFormTest.java | 2 +- .../jivesoftware/smackx/usertune/UserTuneElementTest.java | 2 +- .../java/org/jivesoftware/smackx/xdata/FormFieldTest.java | 2 +- .../java/org/jivesoftware/smackx/ox/OpenPgpElementTest.java | 2 +- .../java/org/jivesoftware/smackx/ox/PubkeyElementTest.java | 2 +- .../jivesoftware/smackx/ox/PublicKeysListElementTest.java | 2 +- .../org/jivesoftware/smackx/ox/SecretkeyElementTest.java | 2 +- 36 files changed, 40 insertions(+), 41 deletions(-) rename smack-core/src/testFixtures/java/org/jivesoftware/smack/test/util/{XmlUnitUtils.java => XmlAssertUtil.java} (94%) diff --git a/smack-core/src/test/java/org/jivesoftware/smack/compress/packet/FailureTest.java b/smack-core/src/test/java/org/jivesoftware/smack/compress/packet/FailureTest.java index cbc5ba88f..9e7ab3b1f 100644 --- a/smack-core/src/test/java/org/jivesoftware/smack/compress/packet/FailureTest.java +++ b/smack-core/src/test/java/org/jivesoftware/smack/compress/packet/FailureTest.java @@ -16,7 +16,7 @@ */ package org.jivesoftware.smack.compress.packet; -import static org.jivesoftware.smack.test.util.XmlUnitUtils.assertXmlSimilar; +import static org.jivesoftware.smack.test.util.XmlAssertUtil.assertXmlSimilar; import java.io.IOException; diff --git a/smack-core/src/test/java/org/jivesoftware/smack/packet/MessageTest.java b/smack-core/src/test/java/org/jivesoftware/smack/packet/MessageTest.java index 68d8535fa..4c45be4fe 100644 --- a/smack-core/src/test/java/org/jivesoftware/smack/packet/MessageTest.java +++ b/smack-core/src/test/java/org/jivesoftware/smack/packet/MessageTest.java @@ -16,7 +16,7 @@ */ package org.jivesoftware.smack.packet; -import static org.jivesoftware.smack.test.util.XmlUnitUtils.assertXmlSimilar; +import static org.jivesoftware.smack.test.util.XmlAssertUtil.assertXmlSimilar; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; diff --git a/smack-core/src/test/java/org/jivesoftware/smack/packet/PresenceTest.java b/smack-core/src/test/java/org/jivesoftware/smack/packet/PresenceTest.java index 863e4b0ff..19bee63ec 100644 --- a/smack-core/src/test/java/org/jivesoftware/smack/packet/PresenceTest.java +++ b/smack-core/src/test/java/org/jivesoftware/smack/packet/PresenceTest.java @@ -16,7 +16,7 @@ */ package org.jivesoftware.smack.packet; -import static org.jivesoftware.smack.test.util.XmlUnitUtils.assertXmlSimilar; +import static org.jivesoftware.smack.test.util.XmlAssertUtil.assertXmlSimilar; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; diff --git a/smack-core/src/test/java/org/jivesoftware/smack/provider/SaslProviderTest.java b/smack-core/src/test/java/org/jivesoftware/smack/provider/SaslProviderTest.java index 9252625ed..944a0503a 100644 --- a/smack-core/src/test/java/org/jivesoftware/smack/provider/SaslProviderTest.java +++ b/smack-core/src/test/java/org/jivesoftware/smack/provider/SaslProviderTest.java @@ -16,7 +16,7 @@ */ package org.jivesoftware.smack.provider; -import static org.jivesoftware.smack.test.util.XmlUnitUtils.assertXmlSimilar; +import static org.jivesoftware.smack.test.util.XmlAssertUtil.assertXmlSimilar; import java.io.IOException; diff --git a/smack-core/src/test/java/org/jivesoftware/smack/util/PacketParserUtilsTest.java b/smack-core/src/test/java/org/jivesoftware/smack/util/PacketParserUtilsTest.java index f775a4fcc..3ac5215d5 100644 --- a/smack-core/src/test/java/org/jivesoftware/smack/util/PacketParserUtilsTest.java +++ b/smack-core/src/test/java/org/jivesoftware/smack/util/PacketParserUtilsTest.java @@ -16,8 +16,8 @@ */ package org.jivesoftware.smack.util; -import static org.jivesoftware.smack.test.util.XmlUnitUtils.assertXmlNotSimilar; -import static org.jivesoftware.smack.test.util.XmlUnitUtils.assertXmlSimilar; +import static org.jivesoftware.smack.test.util.XmlAssertUtil.assertXmlNotSimilar; +import static org.jivesoftware.smack.test.util.XmlAssertUtil.assertXmlSimilar; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; diff --git a/smack-core/src/test/java/org/jivesoftware/smack/util/XmlStringBuilderTest.java b/smack-core/src/test/java/org/jivesoftware/smack/util/XmlStringBuilderTest.java index 8436a4dd2..425bdf9b3 100644 --- a/smack-core/src/test/java/org/jivesoftware/smack/util/XmlStringBuilderTest.java +++ b/smack-core/src/test/java/org/jivesoftware/smack/util/XmlStringBuilderTest.java @@ -18,7 +18,7 @@ package org.jivesoftware.smack.util; import org.jivesoftware.smack.packet.StandardExtensionElement; import org.jivesoftware.smack.packet.XmlEnvironment; -import org.jivesoftware.smack.test.util.XmlUnitUtils; +import org.jivesoftware.smack.test.util.XmlAssertUtil; import org.junit.jupiter.api.Test; @@ -38,10 +38,10 @@ public class XmlStringBuilderTest { String expectedXml = ""; XmlStringBuilder actualXml = outer.toXML(XmlEnvironment.EMPTY); - XmlUnitUtils.assertXmlSimilar(expectedXml, actualXml); + XmlAssertUtil.assertXmlSimilar(expectedXml, actualXml); StringBuilder actualXmlTwo = actualXml.toXML(XmlEnvironment.EMPTY); - XmlUnitUtils.assertXmlSimilar(expectedXml, actualXmlTwo); + XmlAssertUtil.assertXmlSimilar(expectedXml, actualXmlTwo); } } diff --git a/smack-core/src/testFixtures/java/org/jivesoftware/smack/test/util/XmlUnitUtils.java b/smack-core/src/testFixtures/java/org/jivesoftware/smack/test/util/XmlAssertUtil.java similarity index 94% rename from smack-core/src/testFixtures/java/org/jivesoftware/smack/test/util/XmlUnitUtils.java rename to smack-core/src/testFixtures/java/org/jivesoftware/smack/test/util/XmlAssertUtil.java index 3d9b5ba0f..185bf4eea 100644 --- a/smack-core/src/testFixtures/java/org/jivesoftware/smack/test/util/XmlUnitUtils.java +++ b/smack-core/src/testFixtures/java/org/jivesoftware/smack/test/util/XmlAssertUtil.java @@ -1,6 +1,6 @@ /** * - * Copyright 2014-2019 Florian Schmaus + * Copyright 2014-2020 Florian Schmaus * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,8 +26,7 @@ import org.xmlunit.diff.DefaultNodeMatcher; import org.xmlunit.diff.ElementSelectors; import org.xmlunit.input.NormalizedSource; -// TODO: Rename this class to XmlAssertUtil -public class XmlUnitUtils { +public class XmlAssertUtil { public static void assertXmlNotSimilar(CharSequence xmlOne, CharSequence xmlTwo) { normalizedCompare(xmlOne, xmlTwo).areNotSimilar(); diff --git a/smack-experimental/src/test/java/org/jivesoftware/smackx/httpfileupload/SlotCreateTest.java b/smack-experimental/src/test/java/org/jivesoftware/smackx/httpfileupload/SlotCreateTest.java index 3dc287098..26d940e39 100644 --- a/smack-experimental/src/test/java/org/jivesoftware/smackx/httpfileupload/SlotCreateTest.java +++ b/smack-experimental/src/test/java/org/jivesoftware/smackx/httpfileupload/SlotCreateTest.java @@ -16,7 +16,7 @@ */ package org.jivesoftware.smackx.httpfileupload; -import static org.jivesoftware.smack.test.util.XmlUnitUtils.assertXmlSimilar; +import static org.jivesoftware.smack.test.util.XmlAssertUtil.assertXmlSimilar; import static org.junit.jupiter.api.Assertions.assertEquals; import java.io.IOException; diff --git a/smack-experimental/src/test/java/org/jivesoftware/smackx/httpfileupload/SlotRequestCreateTest.java b/smack-experimental/src/test/java/org/jivesoftware/smackx/httpfileupload/SlotRequestCreateTest.java index e30a60c99..3bf92210e 100644 --- a/smack-experimental/src/test/java/org/jivesoftware/smackx/httpfileupload/SlotRequestCreateTest.java +++ b/smack-experimental/src/test/java/org/jivesoftware/smackx/httpfileupload/SlotRequestCreateTest.java @@ -16,7 +16,7 @@ */ package org.jivesoftware.smackx.httpfileupload; -import static org.jivesoftware.smack.test.util.XmlUnitUtils.assertXmlSimilar; +import static org.jivesoftware.smack.test.util.XmlAssertUtil.assertXmlSimilar; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; diff --git a/smack-experimental/src/test/java/org/jivesoftware/smackx/httpfileupload/provider/SlotProviderTest.java b/smack-experimental/src/test/java/org/jivesoftware/smackx/httpfileupload/provider/SlotProviderTest.java index 17c85f32d..9aa9699d9 100644 --- a/smack-experimental/src/test/java/org/jivesoftware/smackx/httpfileupload/provider/SlotProviderTest.java +++ b/smack-experimental/src/test/java/org/jivesoftware/smackx/httpfileupload/provider/SlotProviderTest.java @@ -16,7 +16,7 @@ */ package org.jivesoftware.smackx.httpfileupload.provider; -import static org.jivesoftware.smack.test.util.XmlUnitUtils.assertXmlSimilar; +import static org.jivesoftware.smack.test.util.XmlAssertUtil.assertXmlSimilar; import static org.junit.jupiter.api.Assertions.assertEquals; import java.net.MalformedURLException; diff --git a/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/RetrieveFormFieldsTest.java b/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/RetrieveFormFieldsTest.java index 87000a6e8..f973fa719 100644 --- a/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/RetrieveFormFieldsTest.java +++ b/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/RetrieveFormFieldsTest.java @@ -16,7 +16,7 @@ */ package org.jivesoftware.smackx.mam; -import static org.jivesoftware.smack.test.util.XmlUnitUtils.assertXmlSimilar; +import static org.jivesoftware.smack.test.util.XmlAssertUtil.assertXmlSimilar; import static org.junit.jupiter.api.Assertions.assertEquals; import org.jivesoftware.smack.packet.StreamOpen; diff --git a/smack-experimental/src/test/java/org/jivesoftware/smackx/message_fastening/MessageFasteningElementsTest.java b/smack-experimental/src/test/java/org/jivesoftware/smackx/message_fastening/MessageFasteningElementsTest.java index 7a36752da..8230f974a 100644 --- a/smack-experimental/src/test/java/org/jivesoftware/smackx/message_fastening/MessageFasteningElementsTest.java +++ b/smack-experimental/src/test/java/org/jivesoftware/smackx/message_fastening/MessageFasteningElementsTest.java @@ -16,7 +16,7 @@ */ package org.jivesoftware.smackx.message_fastening; -import static org.jivesoftware.smack.test.util.XmlUnitUtils.assertXmlSimilar; +import static org.jivesoftware.smack.test.util.XmlAssertUtil.assertXmlSimilar; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; diff --git a/smack-experimental/src/test/java/org/jivesoftware/smackx/message_markup/MessageMarkupTest.java b/smack-experimental/src/test/java/org/jivesoftware/smackx/message_markup/MessageMarkupTest.java index 42275642c..63937453e 100644 --- a/smack-experimental/src/test/java/org/jivesoftware/smackx/message_markup/MessageMarkupTest.java +++ b/smack-experimental/src/test/java/org/jivesoftware/smackx/message_markup/MessageMarkupTest.java @@ -16,7 +16,7 @@ */ package org.jivesoftware.smackx.message_markup; -import static org.jivesoftware.smack.test.util.XmlUnitUtils.assertXmlSimilar; +import static org.jivesoftware.smack.test.util.XmlAssertUtil.assertXmlSimilar; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; diff --git a/smack-experimental/src/test/java/org/jivesoftware/smackx/message_retraction/element/RetractElementTest.java b/smack-experimental/src/test/java/org/jivesoftware/smackx/message_retraction/element/RetractElementTest.java index 3cb8491de..1278caed4 100644 --- a/smack-experimental/src/test/java/org/jivesoftware/smackx/message_retraction/element/RetractElementTest.java +++ b/smack-experimental/src/test/java/org/jivesoftware/smackx/message_retraction/element/RetractElementTest.java @@ -16,7 +16,7 @@ */ package org.jivesoftware.smackx.message_retraction.element; -import static org.jivesoftware.smack.test.util.XmlUnitUtils.assertXmlSimilar; +import static org.jivesoftware.smack.test.util.XmlAssertUtil.assertXmlSimilar; import static org.junit.jupiter.api.Assertions.assertNotNull; import java.io.IOException; diff --git a/smack-experimental/src/test/java/org/jivesoftware/smackx/message_retraction/element/RetractedElementTest.java b/smack-experimental/src/test/java/org/jivesoftware/smackx/message_retraction/element/RetractedElementTest.java index 20dcbd650..ba5acea49 100644 --- a/smack-experimental/src/test/java/org/jivesoftware/smackx/message_retraction/element/RetractedElementTest.java +++ b/smack-experimental/src/test/java/org/jivesoftware/smackx/message_retraction/element/RetractedElementTest.java @@ -16,7 +16,7 @@ */ package org.jivesoftware.smackx.message_retraction.element; -import static org.jivesoftware.smack.test.util.XmlUnitUtils.assertXmlSimilar; +import static org.jivesoftware.smack.test.util.XmlAssertUtil.assertXmlSimilar; import static org.junit.jupiter.api.Assertions.assertNotNull; import java.io.IOException; diff --git a/smack-experimental/src/test/java/org/jivesoftware/smackx/reference/ReferenceTest.java b/smack-experimental/src/test/java/org/jivesoftware/smackx/reference/ReferenceTest.java index 84106e0a6..462c80acc 100644 --- a/smack-experimental/src/test/java/org/jivesoftware/smackx/reference/ReferenceTest.java +++ b/smack-experimental/src/test/java/org/jivesoftware/smackx/reference/ReferenceTest.java @@ -16,7 +16,7 @@ */ package org.jivesoftware.smackx.reference; -import static org.jivesoftware.smack.test.util.XmlUnitUtils.assertXmlSimilar; +import static org.jivesoftware.smack.test.util.XmlAssertUtil.assertXmlSimilar; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; diff --git a/smack-experimental/src/test/java/org/jivesoftware/smackx/sid/StableUniqueStanzaIdTest.java b/smack-experimental/src/test/java/org/jivesoftware/smackx/sid/StableUniqueStanzaIdTest.java index a95e5caac..a4ccdc6ce 100644 --- a/smack-experimental/src/test/java/org/jivesoftware/smackx/sid/StableUniqueStanzaIdTest.java +++ b/smack-experimental/src/test/java/org/jivesoftware/smackx/sid/StableUniqueStanzaIdTest.java @@ -16,7 +16,7 @@ */ package org.jivesoftware.smackx.sid; -import static org.jivesoftware.smack.test.util.XmlUnitUtils.assertXmlSimilar; +import static org.jivesoftware.smack.test.util.XmlAssertUtil.assertXmlSimilar; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; diff --git a/smack-experimental/src/test/java/org/jivesoftware/smackx/spoiler/SpoilerTest.java b/smack-experimental/src/test/java/org/jivesoftware/smackx/spoiler/SpoilerTest.java index 165c4be40..5a96059c7 100644 --- a/smack-experimental/src/test/java/org/jivesoftware/smackx/spoiler/SpoilerTest.java +++ b/smack-experimental/src/test/java/org/jivesoftware/smackx/spoiler/SpoilerTest.java @@ -16,7 +16,7 @@ */ package org.jivesoftware.smackx.spoiler; -import static org.jivesoftware.smack.test.util.XmlUnitUtils.assertXmlSimilar; +import static org.jivesoftware.smack.test.util.XmlAssertUtil.assertXmlSimilar; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; diff --git a/smack-extensions/src/test/java/org/jivesoftware/smackx/attention/AttentionElementTest.java b/smack-extensions/src/test/java/org/jivesoftware/smackx/attention/AttentionElementTest.java index dd01b7331..755ca3ba0 100644 --- a/smack-extensions/src/test/java/org/jivesoftware/smackx/attention/AttentionElementTest.java +++ b/smack-extensions/src/test/java/org/jivesoftware/smackx/attention/AttentionElementTest.java @@ -16,7 +16,7 @@ */ package org.jivesoftware.smackx.attention; -import static org.jivesoftware.smack.test.util.XmlUnitUtils.assertXmlSimilar; +import static org.jivesoftware.smack.test.util.XmlAssertUtil.assertXmlSimilar; import org.jivesoftware.smackx.attention.packet.AttentionExtension; diff --git a/smack-extensions/src/test/java/org/jivesoftware/smackx/bytestreams/ibb/packet/CloseTest.java b/smack-extensions/src/test/java/org/jivesoftware/smackx/bytestreams/ibb/packet/CloseTest.java index 8fbb488fc..07dd9920e 100644 --- a/smack-extensions/src/test/java/org/jivesoftware/smackx/bytestreams/ibb/packet/CloseTest.java +++ b/smack-extensions/src/test/java/org/jivesoftware/smackx/bytestreams/ibb/packet/CloseTest.java @@ -16,7 +16,7 @@ */ package org.jivesoftware.smackx.bytestreams.ibb.packet; -import static org.jivesoftware.smack.test.util.XmlUnitUtils.assertXmlSimilar; +import static org.jivesoftware.smack.test.util.XmlAssertUtil.assertXmlSimilar; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; diff --git a/smack-extensions/src/test/java/org/jivesoftware/smackx/bytestreams/ibb/packet/DataPacketExtensionTest.java b/smack-extensions/src/test/java/org/jivesoftware/smackx/bytestreams/ibb/packet/DataPacketExtensionTest.java index 3042ae07a..beab45d02 100644 --- a/smack-extensions/src/test/java/org/jivesoftware/smackx/bytestreams/ibb/packet/DataPacketExtensionTest.java +++ b/smack-extensions/src/test/java/org/jivesoftware/smackx/bytestreams/ibb/packet/DataPacketExtensionTest.java @@ -16,7 +16,7 @@ */ package org.jivesoftware.smackx.bytestreams.ibb.packet; -import static org.jivesoftware.smack.test.util.XmlUnitUtils.assertXmlSimilar; +import static org.jivesoftware.smack.test.util.XmlAssertUtil.assertXmlSimilar; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; diff --git a/smack-extensions/src/test/java/org/jivesoftware/smackx/bytestreams/ibb/packet/DataTest.java b/smack-extensions/src/test/java/org/jivesoftware/smackx/bytestreams/ibb/packet/DataTest.java index aa24c043d..2e0a0cacf 100644 --- a/smack-extensions/src/test/java/org/jivesoftware/smackx/bytestreams/ibb/packet/DataTest.java +++ b/smack-extensions/src/test/java/org/jivesoftware/smackx/bytestreams/ibb/packet/DataTest.java @@ -16,7 +16,7 @@ */ package org.jivesoftware.smackx.bytestreams.ibb.packet; -import static org.jivesoftware.smack.test.util.XmlUnitUtils.assertXmlSimilar; +import static org.jivesoftware.smack.test.util.XmlAssertUtil.assertXmlSimilar; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.Mockito.mock; diff --git a/smack-extensions/src/test/java/org/jivesoftware/smackx/bytestreams/ibb/packet/OpenTest.java b/smack-extensions/src/test/java/org/jivesoftware/smackx/bytestreams/ibb/packet/OpenTest.java index 8206efbf2..d28cd617f 100644 --- a/smack-extensions/src/test/java/org/jivesoftware/smackx/bytestreams/ibb/packet/OpenTest.java +++ b/smack-extensions/src/test/java/org/jivesoftware/smackx/bytestreams/ibb/packet/OpenTest.java @@ -16,7 +16,7 @@ */ package org.jivesoftware.smackx.bytestreams.ibb.packet; -import static org.jivesoftware.smack.test.util.XmlUnitUtils.assertXmlSimilar; +import static org.jivesoftware.smack.test.util.XmlAssertUtil.assertXmlSimilar; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; diff --git a/smack-extensions/src/test/java/org/jivesoftware/smackx/last_interaction/IdleTest.java b/smack-extensions/src/test/java/org/jivesoftware/smackx/last_interaction/IdleTest.java index 3a6da6b49..bf8c6ae27 100644 --- a/smack-extensions/src/test/java/org/jivesoftware/smackx/last_interaction/IdleTest.java +++ b/smack-extensions/src/test/java/org/jivesoftware/smackx/last_interaction/IdleTest.java @@ -16,7 +16,7 @@ */ package org.jivesoftware.smackx.last_interaction; -import static org.jivesoftware.smack.test.util.XmlUnitUtils.assertXmlSimilar; +import static org.jivesoftware.smack.test.util.XmlAssertUtil.assertXmlSimilar; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; diff --git a/smack-extensions/src/test/java/org/jivesoftware/smackx/mood/MoodConcretisationTest.java b/smack-extensions/src/test/java/org/jivesoftware/smackx/mood/MoodConcretisationTest.java index 4ad647e29..f05bec4a2 100644 --- a/smack-extensions/src/test/java/org/jivesoftware/smackx/mood/MoodConcretisationTest.java +++ b/smack-extensions/src/test/java/org/jivesoftware/smackx/mood/MoodConcretisationTest.java @@ -16,7 +16,7 @@ */ package org.jivesoftware.smackx.mood; -import static org.jivesoftware.smack.test.util.XmlUnitUtils.assertXmlSimilar; +import static org.jivesoftware.smack.test.util.XmlAssertUtil.assertXmlSimilar; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; diff --git a/smack-extensions/src/test/java/org/jivesoftware/smackx/mood/MoodElementTest.java b/smack-extensions/src/test/java/org/jivesoftware/smackx/mood/MoodElementTest.java index 31d937be8..c1cb72fd5 100644 --- a/smack-extensions/src/test/java/org/jivesoftware/smackx/mood/MoodElementTest.java +++ b/smack-extensions/src/test/java/org/jivesoftware/smackx/mood/MoodElementTest.java @@ -16,7 +16,7 @@ */ package org.jivesoftware.smackx.mood; -import static org.jivesoftware.smack.test.util.XmlUnitUtils.assertXmlSimilar; +import static org.jivesoftware.smack.test.util.XmlAssertUtil.assertXmlSimilar; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNull; diff --git a/smack-extensions/src/test/java/org/jivesoftware/smackx/nick/NickTest.java b/smack-extensions/src/test/java/org/jivesoftware/smackx/nick/NickTest.java index 61c7b70eb..f4379e034 100644 --- a/smack-extensions/src/test/java/org/jivesoftware/smackx/nick/NickTest.java +++ b/smack-extensions/src/test/java/org/jivesoftware/smackx/nick/NickTest.java @@ -16,7 +16,7 @@ */ package org.jivesoftware.smackx.nick; -import static org.jivesoftware.smack.test.util.XmlUnitUtils.assertXmlSimilar; +import static org.jivesoftware.smack.test.util.XmlAssertUtil.assertXmlSimilar; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; diff --git a/smack-extensions/src/test/java/org/jivesoftware/smackx/pubsub/AffiliationsExtensionTest.java b/smack-extensions/src/test/java/org/jivesoftware/smackx/pubsub/AffiliationsExtensionTest.java index c584ca73c..8c1706091 100644 --- a/smack-extensions/src/test/java/org/jivesoftware/smackx/pubsub/AffiliationsExtensionTest.java +++ b/smack-extensions/src/test/java/org/jivesoftware/smackx/pubsub/AffiliationsExtensionTest.java @@ -16,7 +16,7 @@ */ package org.jivesoftware.smackx.pubsub; -import static org.jivesoftware.smack.test.util.XmlUnitUtils.assertXmlSimilar; +import static org.jivesoftware.smack.test.util.XmlAssertUtil.assertXmlSimilar; import java.io.IOException; import java.util.ArrayList; diff --git a/smack-extensions/src/test/java/org/jivesoftware/smackx/pubsub/ItemValidationTest.java b/smack-extensions/src/test/java/org/jivesoftware/smackx/pubsub/ItemValidationTest.java index 9dafb5334..8ed23eed8 100644 --- a/smack-extensions/src/test/java/org/jivesoftware/smackx/pubsub/ItemValidationTest.java +++ b/smack-extensions/src/test/java/org/jivesoftware/smackx/pubsub/ItemValidationTest.java @@ -16,7 +16,7 @@ */ package org.jivesoftware.smackx.pubsub; -import static org.jivesoftware.smack.test.util.XmlUnitUtils.assertXmlSimilar; +import static org.jivesoftware.smack.test.util.XmlAssertUtil.assertXmlSimilar; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; diff --git a/smack-extensions/src/test/java/org/jivesoftware/smackx/softwareinfo/SoftwareInfoFormTest.java b/smack-extensions/src/test/java/org/jivesoftware/smackx/softwareinfo/SoftwareInfoFormTest.java index 48613cd02..ffeed72aa 100644 --- a/smack-extensions/src/test/java/org/jivesoftware/smackx/softwareinfo/SoftwareInfoFormTest.java +++ b/smack-extensions/src/test/java/org/jivesoftware/smackx/softwareinfo/SoftwareInfoFormTest.java @@ -16,7 +16,7 @@ */ package org.jivesoftware.smackx.softwareinfo; -import static org.jivesoftware.smack.test.util.XmlUnitUtils.assertXmlSimilar; +import static org.jivesoftware.smack.test.util.XmlAssertUtil.assertXmlSimilar; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; diff --git a/smack-extensions/src/test/java/org/jivesoftware/smackx/usertune/UserTuneElementTest.java b/smack-extensions/src/test/java/org/jivesoftware/smackx/usertune/UserTuneElementTest.java index b9bdb1189..59c912eaf 100644 --- a/smack-extensions/src/test/java/org/jivesoftware/smackx/usertune/UserTuneElementTest.java +++ b/smack-extensions/src/test/java/org/jivesoftware/smackx/usertune/UserTuneElementTest.java @@ -16,7 +16,7 @@ */ package org.jivesoftware.smackx.usertune; -import static org.jivesoftware.smack.test.util.XmlUnitUtils.assertXmlSimilar; +import static org.jivesoftware.smack.test.util.XmlAssertUtil.assertXmlSimilar; import java.io.IOException; import java.net.URI; diff --git a/smack-extensions/src/test/java/org/jivesoftware/smackx/xdata/FormFieldTest.java b/smack-extensions/src/test/java/org/jivesoftware/smackx/xdata/FormFieldTest.java index 214c67e29..67cd5dd89 100644 --- a/smack-extensions/src/test/java/org/jivesoftware/smackx/xdata/FormFieldTest.java +++ b/smack-extensions/src/test/java/org/jivesoftware/smackx/xdata/FormFieldTest.java @@ -16,7 +16,7 @@ */ package org.jivesoftware.smackx.xdata; -import static org.jivesoftware.smack.test.util.XmlUnitUtils.assertXmlSimilar; +import static org.jivesoftware.smack.test.util.XmlAssertUtil.assertXmlSimilar; import org.junit.jupiter.api.Test; import org.jxmpp.jid.JidTestUtil; diff --git a/smack-openpgp/src/test/java/org/jivesoftware/smackx/ox/OpenPgpElementTest.java b/smack-openpgp/src/test/java/org/jivesoftware/smackx/ox/OpenPgpElementTest.java index 2d8d55419..143580531 100644 --- a/smack-openpgp/src/test/java/org/jivesoftware/smackx/ox/OpenPgpElementTest.java +++ b/smack-openpgp/src/test/java/org/jivesoftware/smackx/ox/OpenPgpElementTest.java @@ -18,7 +18,7 @@ package org.jivesoftware.smackx.ox; import static junit.framework.TestCase.assertEquals; import static junit.framework.TestCase.assertNotNull; -import static org.jivesoftware.smack.test.util.XmlUnitUtils.assertXmlSimilar; +import static org.jivesoftware.smack.test.util.XmlAssertUtil.assertXmlSimilar; import static org.junit.jupiter.api.Assertions.assertThrows; import java.io.IOException; diff --git a/smack-openpgp/src/test/java/org/jivesoftware/smackx/ox/PubkeyElementTest.java b/smack-openpgp/src/test/java/org/jivesoftware/smackx/ox/PubkeyElementTest.java index a67d4870d..46e053891 100644 --- a/smack-openpgp/src/test/java/org/jivesoftware/smackx/ox/PubkeyElementTest.java +++ b/smack-openpgp/src/test/java/org/jivesoftware/smackx/ox/PubkeyElementTest.java @@ -17,7 +17,7 @@ package org.jivesoftware.smackx.ox; import static junit.framework.TestCase.assertEquals; -import static org.jivesoftware.smack.test.util.XmlUnitUtils.assertXmlSimilar; +import static org.jivesoftware.smack.test.util.XmlAssertUtil.assertXmlSimilar; import java.io.IOException; import java.text.ParseException; diff --git a/smack-openpgp/src/test/java/org/jivesoftware/smackx/ox/PublicKeysListElementTest.java b/smack-openpgp/src/test/java/org/jivesoftware/smackx/ox/PublicKeysListElementTest.java index 54f2f98cc..f90015b9f 100644 --- a/smack-openpgp/src/test/java/org/jivesoftware/smackx/ox/PublicKeysListElementTest.java +++ b/smack-openpgp/src/test/java/org/jivesoftware/smackx/ox/PublicKeysListElementTest.java @@ -17,7 +17,7 @@ package org.jivesoftware.smackx.ox; import static junit.framework.TestCase.assertEquals; -import static org.jivesoftware.smack.test.util.XmlUnitUtils.assertXmlSimilar; +import static org.jivesoftware.smack.test.util.XmlAssertUtil.assertXmlSimilar; import java.util.Date; diff --git a/smack-openpgp/src/test/java/org/jivesoftware/smackx/ox/SecretkeyElementTest.java b/smack-openpgp/src/test/java/org/jivesoftware/smackx/ox/SecretkeyElementTest.java index 56902d75f..b9745bb20 100644 --- a/smack-openpgp/src/test/java/org/jivesoftware/smackx/ox/SecretkeyElementTest.java +++ b/smack-openpgp/src/test/java/org/jivesoftware/smackx/ox/SecretkeyElementTest.java @@ -17,7 +17,7 @@ package org.jivesoftware.smackx.ox; import static junit.framework.TestCase.assertTrue; -import static org.jivesoftware.smack.test.util.XmlUnitUtils.assertXmlSimilar; +import static org.jivesoftware.smack.test.util.XmlAssertUtil.assertXmlSimilar; import java.nio.charset.Charset; import java.util.Arrays; From 3f9ca68134961841103c07ffd0bef638ad74718b Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Sun, 14 Jun 2020 17:38:51 +0200 Subject: [PATCH 26/28] Delete TypedCloneable --- .../jivesoftware/smack/packet/Message.java | 17 ++++++++-- .../smack/packet/MessageOrPresence.java | 8 ++++- .../jivesoftware/smack/packet/Presence.java | 20 +++++++++-- .../org/jivesoftware/smack/util/MultiMap.java | 4 +-- .../smack/util/TypedCloneable.java | 34 ------------------- .../smackx/disco/ServiceDiscoveryManager.java | 3 +- .../smackx/disco/packet/DiscoverInfo.java | 18 +++++----- .../smackx/muc/MucEnterConfiguration.java | 12 ++++--- .../org/jivesoftware/smack/roster/Roster.java | 12 +++---- 9 files changed, 67 insertions(+), 61 deletions(-) delete mode 100644 smack-core/src/main/java/org/jivesoftware/smack/util/TypedCloneable.java diff --git a/smack-core/src/main/java/org/jivesoftware/smack/packet/Message.java b/smack-core/src/main/java/org/jivesoftware/smack/packet/Message.java index c424e339e..a85f7c5b6 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/packet/Message.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/packet/Message.java @@ -22,11 +22,11 @@ import java.util.Locale; import javax.xml.namespace.QName; +import org.jivesoftware.smack.XMPPConnection; import org.jivesoftware.smack.util.EqualsUtil; import org.jivesoftware.smack.util.HashCode; import org.jivesoftware.smack.util.Objects; import org.jivesoftware.smack.util.StringUtils; -import org.jivesoftware.smack.util.TypedCloneable; import org.jivesoftware.smack.util.XmlStringBuilder; import org.jxmpp.jid.Jid; @@ -58,7 +58,7 @@ import org.jxmpp.stringprep.XmppStringprepException; * @author Matt Tucker */ public final class Message extends MessageOrPresence - implements MessageView, TypedCloneable { + implements MessageView { public static final String ELEMENT = "message"; public static final String BODY = "body"; @@ -371,6 +371,16 @@ public final class Message extends MessageOrPresence return StanzaBuilder.buildMessageFrom(this, getStanzaId()); } + @Override + public MessageBuilder asBuilder(String id) { + return StanzaBuilder.buildMessageFrom(this, id); + } + + @Override + public MessageBuilder asBuilder(XMPPConnection connection) { + return connection.getStanzaFactory().buildMessageStanzaFrom(this); + } + @Override public String toString() { StringBuilder sb = new StringBuilder(); @@ -409,7 +419,10 @@ public final class Message extends MessageOrPresence * instance. *

* @return a clone of this message. + * @deprecated use {@link #asBuilder()} instead. */ + // TODO: Remove in Smack 4.5. + @Deprecated @Override public Message clone() { return new Message(this); diff --git a/smack-core/src/main/java/org/jivesoftware/smack/packet/MessageOrPresence.java b/smack-core/src/main/java/org/jivesoftware/smack/packet/MessageOrPresence.java index c885e941c..f48b794a0 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/packet/MessageOrPresence.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/packet/MessageOrPresence.java @@ -1,6 +1,6 @@ /** * - * Copyright 2019 Florian Schmaus + * Copyright 2019-2020 Florian Schmaus * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,6 +16,8 @@ */ package org.jivesoftware.smack.packet; +import org.jivesoftware.smack.XMPPConnection; + public abstract class MessageOrPresence> extends Stanza { @Deprecated @@ -33,4 +35,8 @@ public abstract class MessageOrPresence - implements PresenceView, TypedCloneable { + implements PresenceView { public static final String ELEMENT = "presence"; @@ -282,6 +282,16 @@ public final class Presence extends MessageOrPresence return StanzaBuilder.buildPresenceFrom(this, getStanzaId()); } + @Override + public PresenceBuilder asBuilder(String id) { + return StanzaBuilder.buildPresenceFrom(this, id); + } + + @Override + public PresenceBuilder asBuilder(XMPPConnection connection) { + return connection.getStanzaFactory().buildPresenceStanzaFrom(this); + } + @Override public String toString() { StringBuilder sb = new StringBuilder(); @@ -343,7 +353,10 @@ public final class Presence extends MessageOrPresence * instance. *

* @return a clone of this presence. + * @deprecated use {@link #asBuilder()} instead. */ + // TODO: Remove in Smack 4.5. + @Deprecated @Override public Presence clone() { return new Presence(this); @@ -354,7 +367,10 @@ public final class Presence extends MessageOrPresence * * @return a "clone" of this presence with a different stanza ID. * @since 4.1.2 + * @deprecated use {@link #asBuilder(XMPPConnection)} or {@link #asBuilder(String)}instead. */ + // TODO: Remove in Smack 4.5. + @Deprecated public Presence cloneWithNewId() { Presence clone = clone(); clone.setNewStanzaId(); diff --git a/smack-core/src/main/java/org/jivesoftware/smack/util/MultiMap.java b/smack-core/src/main/java/org/jivesoftware/smack/util/MultiMap.java index daa3241a8..f7dcad596 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/util/MultiMap.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/util/MultiMap.java @@ -1,6 +1,6 @@ /** * - * Copyright © 2015-2019 Florian Schmaus + * Copyright © 2015-2020 Florian Schmaus * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -34,7 +34,7 @@ import java.util.Set; * @param the type of the keys the map uses. * @param the type of the values the map uses. */ -public class MultiMap implements TypedCloneable> { +public class MultiMap { /** * The constant value {@value}. diff --git a/smack-core/src/main/java/org/jivesoftware/smack/util/TypedCloneable.java b/smack-core/src/main/java/org/jivesoftware/smack/util/TypedCloneable.java deleted file mode 100644 index 0a2121845..000000000 --- a/smack-core/src/main/java/org/jivesoftware/smack/util/TypedCloneable.java +++ /dev/null @@ -1,34 +0,0 @@ -/** - * - * Copyright 2015 Florian Schmaus - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jivesoftware.smack.util; - -/** - * An extended version of {@link java.lang.Cloneable}, which defines a generic {@link #clone()} - * method. - * - * @param the type returned by {@link #clone()}. - */ -public interface TypedCloneable extends Cloneable { - - /** - * Clone this instance. - * - * @return a cloned version of this instance. - */ - T clone(); - -} diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/disco/ServiceDiscoveryManager.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/disco/ServiceDiscoveryManager.java index 61103d2ee..312b559b5 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/disco/ServiceDiscoveryManager.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/disco/ServiceDiscoveryManager.java @@ -962,8 +962,9 @@ public final class ServiceDiscoveryManager extends Manager { // to respect ConnectionConfiguration.isSendPresence() final Presence presenceSend = this.presenceSend; if (connection.isAuthenticated() && presenceSend != null) { + Presence presence = presenceSend.asBuilder(connection).build(); try { - connection.sendStanza(presenceSend.cloneWithNewId()); + connection.sendStanza(presence); } catch (InterruptedException | NotConnectedException e) { LOGGER.log(Level.WARNING, "Could could not update presence with caps info", e); diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/disco/packet/DiscoverInfo.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/disco/packet/DiscoverInfo.java index 7de48f1be..b4d3f7554 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/disco/packet/DiscoverInfo.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/disco/packet/DiscoverInfo.java @@ -30,7 +30,6 @@ import org.jivesoftware.smack.packet.IqData; import org.jivesoftware.smack.util.EqualsUtil; import org.jivesoftware.smack.util.HashCode; import org.jivesoftware.smack.util.StringUtils; -import org.jivesoftware.smack.util.TypedCloneable; import org.jivesoftware.smack.util.XmlStringBuilder; import org.jxmpp.util.XmppStringUtils; @@ -44,7 +43,7 @@ import org.jxmpp.util.XmppStringUtils; * * @author Gaston Dombiak */ -public class DiscoverInfo extends IQ implements DiscoverInfoView, TypedCloneable { +public class DiscoverInfo extends IQ implements DiscoverInfoView { public static final String ELEMENT = QUERY_ELEMENT; public static final String NAMESPACE = "http://jabber.org/protocol/disco#info"; @@ -303,7 +302,13 @@ public class DiscoverInfo extends IQ implements DiscoverInfoView, TypedCloneable return new DiscoverInfoBuilder(this, stanzaId); } - // TODO: Deprecate in favor of asBuilder(). + /** + * Deprecated, do not use. + * + * @deprecated use {@link #asBuilder(String)} instead. + */ + // TODO: Remove in Smack 4.5. + @Deprecated @Override public DiscoverInfo clone() { return new DiscoverInfo(this); @@ -516,7 +521,7 @@ public class DiscoverInfo extends IQ implements DiscoverInfoView, TypedCloneable * as well as specific feature types of interest, if any (e.g., for the purpose of feature * negotiation). */ - public static final class Feature implements TypedCloneable { + public static final class Feature { private final String variable; @@ -566,11 +571,6 @@ public class DiscoverInfo extends IQ implements DiscoverInfoView, TypedCloneable return variable.hashCode(); } - @Override - public Feature clone() { - return new Feature(this); - } - @Override public String toString() { return toXML().toString(); diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/muc/MucEnterConfiguration.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/muc/MucEnterConfiguration.java index de2b6606e..8a6b4b2fd 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/muc/MucEnterConfiguration.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/muc/MucEnterConfiguration.java @@ -1,6 +1,6 @@ /** * - * Copyright 2015-2016 Florian Schmaus + * Copyright 2015-2020 Florian Schmaus * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -60,15 +60,17 @@ public final class MucEnterConfiguration { since = builder.since; timeout = builder.timeout; + final PresenceBuilder joinPresenceBuilder; if (builder.joinPresence == null) { - joinPresence = builder.joinPresenceBuilder.ofType(Presence.Type.available).build(); + joinPresenceBuilder = builder.joinPresenceBuilder.ofType(Presence.Type.available); } else { - joinPresence = builder.joinPresence.clone(); + joinPresenceBuilder = builder.joinPresence.asBuilder(); } // Indicate the the client supports MUC - joinPresence.addExtension(new MUCInitialPresence(password, maxChars, maxStanzas, seconds, + joinPresenceBuilder.addExtension(new MUCInitialPresence(password, maxChars, maxStanzas, seconds, since)); + joinPresence = joinPresenceBuilder.build(); } Presence getJoinPresence(MultiUserChat multiUserChat) { @@ -92,6 +94,8 @@ public final class MucEnterConfiguration { private long timeout; private final PresenceBuilder joinPresenceBuilder; + + // TODO: Remove in Smack 4.5. private Presence joinPresence; Builder(Resourcepart nickname, XMPPConnection connection) { diff --git a/smack-im/src/main/java/org/jivesoftware/smack/roster/Roster.java b/smack-im/src/main/java/org/jivesoftware/smack/roster/Roster.java index dd05ac97b..02ee0d7e1 100644 --- a/smack-im/src/main/java/org/jivesoftware/smack/roster/Roster.java +++ b/smack-im/src/main/java/org/jivesoftware/smack/roster/Roster.java @@ -1047,7 +1047,7 @@ public final class Roster extends Manager { } if (presence == null) { if (unavailable != null) { - return unavailable.clone(); + return unavailable; } else { presence = synthesizeUnvailablePresence(jid); @@ -1055,7 +1055,7 @@ public final class Roster extends Manager { } } else { - return presence.clone(); + return presence; } } } @@ -1084,7 +1084,7 @@ public final class Roster extends Manager { return presence; } else { - return presence.clone(); + return presence; } } } @@ -1107,7 +1107,7 @@ public final class Roster extends Manager { } else { res = new ArrayList<>(userPresences.values().size()); for (Presence presence : userPresences.values()) { - res.add(presence.clone()); + res.add(presence); } } return res; @@ -1156,7 +1156,7 @@ public final class Roster extends Manager { Presence unavailable = null; for (Presence presence : userPresences.values()) { if (presence.isAvailable()) { - answer.add(presence.clone()); + answer.add(presence); } else { unavailable = presence; @@ -1166,7 +1166,7 @@ public final class Roster extends Manager { res = answer; } else if (unavailable != null) { - res = Arrays.asList(unavailable.clone()); + res = Arrays.asList(unavailable); } else { Presence presence = synthesizeUnvailablePresence(jid); From b6be7a9694f2dffeff9b55f247672d733c329642 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Sun, 14 Jun 2020 22:23:42 +0200 Subject: [PATCH 27/28] Smack 4.4.0-alpha4 --- version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version b/version index 92560b19f..c2fb0abc6 100644 --- a/version +++ b/version @@ -1 +1 @@ -4.4.0-alpha4-SNAPSHOT +4.4.0-alpha4 From d74f19e26a03a445292af9f67077422cdc959566 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Sun, 14 Jun 2020 23:00:21 +0200 Subject: [PATCH 28/28] Smack 4.4.0-alpha5-SNAPSHOT --- version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version b/version index c2fb0abc6..c4b97782d 100644 --- a/version +++ b/version @@ -1 +1 @@ -4.4.0-alpha4 +4.4.0-alpha5-SNAPSHOT