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'
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..6e90e35b2 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
@@ -892,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.
@@ -899,41 +972,31 @@ 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;
+ }
- 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.
callConnectionClosedOnErrorListener(exception);
}, AbstractXMPPConnection.this + " callConnectionClosedOnErrorListener()");
- });
+ }
}
/**
@@ -947,19 +1010,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 +1874,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();
}
}
@@ -1827,9 +1884,10 @@ 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
- lastFeaturesReceived.reportSuccess();
+ // This where the last stream features from the server, either it did not contain
+ // compression or we disabled it.
+ 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/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() {
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..f62caf8ee 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();
@@ -353,21 +359,19 @@ 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.
* @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/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/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/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-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/packet/Message.java b/smack-core/src/main/java/org/jivesoftware/smack/packet/Message.java
index 9b2d392a0..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
@@ -17,20 +17,16 @@
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;
+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;
@@ -62,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";
@@ -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;
@@ -542,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();
@@ -580,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
+ * 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/Presence.java b/smack-core/src/main/java/org/jivesoftware/smack/packet/Presence.java
index 0f819b429..0c7014110 100644
--- a/smack-core/src/main/java/org/jivesoftware/smack/packet/Presence.java
+++ b/smack-core/src/main/java/org/jivesoftware/smack/packet/Presence.java
@@ -20,9 +20,9 @@ package org.jivesoftware.smack.packet;
import java.util.List;
import java.util.Locale;
+import org.jivesoftware.smack.XMPPConnection;
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;
@@ -61,7 +61,7 @@ import org.jxmpp.jid.Jid;
* @author Matt Tucker
*/
public final class Presence extends 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/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();
+ }
}
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() {
}
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/Supplier.java
similarity index 61%
rename from smack-core/src/main/java/org/jivesoftware/smack/util/TypedCloneable.java
rename to smack-core/src/main/java/org/jivesoftware/smack/util/Supplier.java
index 0a2121845..f0910adb7 100644
--- a/smack-core/src/main/java/org/jivesoftware/smack/util/TypedCloneable.java
+++ b/smack-core/src/main/java/org/jivesoftware/smack/util/Supplier.java
@@ -1,6 +1,6 @@
/**
*
- * Copyright 2015 Florian Schmaus
+ * 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.
@@ -16,19 +16,9 @@
*/
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 {
+// TODO: Replace with java.util.function.Supplier once Smack's minimum Android SDK level is 24 or higher.
+public interface Supplier {
- /**
- * Clone this instance.
- *
- * @return a cloned version of this instance.
- */
- T clone();
+ T get();
}
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/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-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;
}
/**
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("");
- builder.append(getElementName() + '>');
- 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("");
- builder.append(getElementName());
- 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("");
- builder.append(getElementName());
- 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/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 5a03d5ed3..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
@@ -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.
@@ -16,13 +16,14 @@
*/
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;
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);
}
}
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-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);
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..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) {
@@ -863,6 +865,8 @@ public class SmackIntegrationTestFramework {
}
testMethod.invoke(test, connectionsArray);
}
+
+ connectionManager.recycle(connections);
}
@Override
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.
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();
+ }
}
}
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-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);
+ }
}
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;
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"));
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..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
@@ -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,26 @@ 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");
+ /**
+ * 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 final SynchronizationPoint smEnabledSyncPoint = new SynchronizationPoint<>(
- this, "stream enabled element");
+ /**
+ * 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.
@@ -376,20 +376,26 @@ 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.
// 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();
+ smResumedSyncPoint = SyncPointState.initial;
+ smResumptionFailed = null;
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 +403,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();
@@ -418,12 +425,14 @@ 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;
+ 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
@@ -485,14 +494,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()");
@@ -503,9 +517,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;
@@ -533,15 +552,6 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
initState();
}
- @Override
- protected void initState() {
- super.initState();
- maybeCompressFeaturesReceived.init();
- compressSyncPoint.init();
- smResumedSyncPoint.init();
- smEnabledSyncPoint.init();
- }
-
@Override
public void sendNonza(Nonza element) throws NotConnectedException, InterruptedException {
packetWriter.sendStreamElement(element);
@@ -652,15 +662,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 +689,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 +768,7 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
@Override
public boolean isUsingCompression() {
- return compressionHandler != null && compressSyncPoint.wasSuccessful();
+ return compressionHandler != null && compressSyncPoint;
}
/**
@@ -791,10 +786,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 +802,9 @@ 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()));
+ compressSyncPoint = false;
+ 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 +832,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 +854,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 +900,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 +913,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 +936,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 +958,17 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
break;
case "error":
StreamError streamError = PacketParserUtils.parseStreamError(parser);
- saslFeatureReceived.reportFailure(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();
+ // 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);
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 +982,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 +997,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 +1006,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 +1016,19 @@ 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;
+ notifyWaitingThreads();
+ } else {
+ FailedNonzaException xmppException = new FailedNonzaException(failed, failed.getStanzaErrorCondition());
+ setCurrentConnectionExceptionAndNotify(xmppException);
}
break;
case Resumed.ELEMENT:
@@ -1052,7 +1037,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 +1053,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 +1062,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 +1086,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 +1123,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 +1144,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 +1161,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 +1182,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);
@@ -1256,19 +1236,11 @@ 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;
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);
- }
- }
}
/**
@@ -1370,7 +1342,6 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
}
writer.write(packet.toXML().toString());
}
- writer.flush();
}
catch (Exception e) {
LOGGER.log(Level.WARNING,
@@ -1407,9 +1378,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 +1689,7 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
* @return true if Stream Management was negotiated.
*/
public boolean isSmEnabled() {
- return smEnabledSyncPoint.wasSuccessful();
+ return smEnabledSyncPoint;
}
/**
@@ -1730,7 +1698,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/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);
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..e48c35b25 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;
@@ -591,7 +587,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);
@@ -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);
diff --git a/version b/version
index 92560b19f..c4b97782d 100644
--- a/version
+++ b/version
@@ -1 +1 @@
-4.4.0-alpha4-SNAPSHOT
+4.4.0-alpha5-SNAPSHOT