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 52fe2138e..7a9bf2ee1 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/AbstractXMPPConnection.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/AbstractXMPPConnection.java @@ -235,7 +235,7 @@ public abstract class AbstractXMPPConnection implements XMPPConnection { * 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( + protected final SynchronizationPoint lastFeaturesReceived = new SynchronizationPoint<>( AbstractXMPPConnection.this, "last stream features received from server"); /** @@ -550,7 +550,7 @@ public abstract class AbstractXMPPConnection implements XMPPConnection { // - 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.checkIfSuccessOrWait(); + lastFeaturesReceived.checkIfSuccessOrWaitOrThrow(); if (!hasFeature(Bind.ELEMENT, Bind.NAMESPACE)) { 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 74d391539..a84b75211 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/SmackException.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/SmackException.java @@ -334,4 +334,16 @@ public class SmackException extends Exception { super("Resource binding was not offered by server"); } } + + public static class SmackWrappedException extends SmackException { + + /** + * + */ + private static final long serialVersionUID = 1L; + + public SmackWrappedException(Exception exception) { + super(exception); + } + } } diff --git a/smack-core/src/main/java/org/jivesoftware/smack/SynchronizationPoint.java b/smack-core/src/main/java/org/jivesoftware/smack/SynchronizationPoint.java index 54be30a36..a7113b284 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/SynchronizationPoint.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/SynchronizationPoint.java @@ -1,6 +1,6 @@ /** * - * Copyright © 2014-2015 Florian Schmaus + * 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. @@ -22,6 +22,7 @@ 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; @@ -37,6 +38,7 @@ public class SynchronizationPoint { // same memory synchronization effects as synchronization block enter and leave. private State state; private E failureException; + private SmackWrappedException smackWrappedExcpetion; /** * Construct a new synchronization point for the given connection. @@ -59,6 +61,7 @@ public class SynchronizationPoint { connectionLock.lock(); state = State.Initial; failureException = null; + smackWrappedExcpetion = null; connectionLock.unlock(); } @@ -71,7 +74,7 @@ public class SynchronizationPoint { * @throws InterruptedException if the connection is interrupted. * @return null if synchronization point was successful, or the failure Exception. */ - public E sendAndWaitForResponse(TopLevelStreamElement request) throws NoResponseException, + public Exception sendAndWaitForResponse(TopLevelStreamElement request) throws NoResponseException, NotConnectedException, InterruptedException { assert (state == State.Initial); connectionLock.lock(); @@ -103,15 +106,14 @@ public class SynchronizationPoint { * @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 { + NotConnectedException, InterruptedException, SmackWrappedException { sendAndWaitForResponse(request); switch (state) { case Failure: - if (failureException != null) { - throw failureException; - } + throwException(); break; default: // Success, do nothing @@ -123,11 +125,12 @@ public class SynchronizationPoint { * @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 { + public void checkIfSuccessOrWaitOrThrow() throws NoResponseException, E, InterruptedException, SmackWrappedException { checkIfSuccessOrWait(); if (state == State.Failure) { - throw failureException; + throwException(); } } @@ -137,7 +140,7 @@ public class SynchronizationPoint { * @throws InterruptedException * @return null if synchronization point was successful, or the failure Exception. */ - public E checkIfSuccessOrWait() throws NoResponseException, InterruptedException { + public Exception checkIfSuccessOrWait() throws NoResponseException, InterruptedException { connectionLock.lock(); try { switch (state) { @@ -145,7 +148,7 @@ public class SynchronizationPoint { case Success: return null; case Failure: - return failureException; + return getException(); default: // Do nothing break; @@ -198,6 +201,24 @@ public class SynchronizationPoint { } } + /** + * 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. * @@ -256,6 +277,20 @@ public class SynchronizationPoint { } } + 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. *

@@ -264,7 +299,7 @@ public class SynchronizationPoint { * @return true if synchronization point was successful, false on failure. * @throws NoResponseException */ - private E checkForResponse() throws NoResponseException { + private Exception checkForResponse() throws NoResponseException { switch (state) { case Initial: case NoResponse: @@ -273,7 +308,7 @@ public class SynchronizationPoint { case Success: return null; case Failure: - return failureException; + return getException(); default: throw new AssertionError("Unknown state " + state); } 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 4b44e61dd..0774e4375 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 @@ -84,6 +84,7 @@ import org.jivesoftware.smack.SmackException.NoResponseException; import org.jivesoftware.smack.SmackException.NotConnectedException; import org.jivesoftware.smack.SmackException.NotLoggedInException; import org.jivesoftware.smack.SmackException.SecurityRequiredByServerException; +import org.jivesoftware.smack.SmackException.SmackWrappedException; import org.jivesoftware.smack.SmackFuture; import org.jivesoftware.smack.StanzaListener; import org.jivesoftware.smack.SynchronizationPoint; @@ -937,6 +938,12 @@ public class XMPPTCPConnection extends AbstractXMPPConnection { if ((packetReader == null || packetReader.done) && (packetWriter == null || packetWriter.done())) return; + SmackWrappedException smackWrappedException = new SmackWrappedException(e); + tlsHandled.reportGenericFailure(smackWrappedException); + saslFeatureReceived.reportGenericFailure(smackWrappedException); + maybeCompressFeaturesReceived.reportGenericFailure(smackWrappedException); + lastFeaturesReceived.reportGenericFailure(smackWrappedException); + // Closes the connection temporary. A reconnection is possible // Note that a connection listener of XMPPTCPConnection will drop the SM state in // case the Exception is a StreamErrorException.