1
0
Fork 0
mirror of https://github.com/vanitasvitae/Smack.git synced 2024-10-31 17:25:58 +01:00

Smack 4.3.3

-----BEGIN PGP SIGNATURE-----
 
 iQGTBAABCgB9FiEEl3UFnzoh3OFr5PuuIjmn6PWFIFIFAlyKV9tfFIAAAAAALgAo
 aXNzdWVyLWZwckBub3RhdGlvbnMub3BlbnBncC5maWZ0aGhvcnNlbWFuLm5ldDk3
 NzUwNTlGM0EyMURDRTE2QkU0RkJBRTIyMzlBN0U4RjU4NTIwNTIACgkQIjmn6PWF
 IFL4vQf/Qfg3VzNEnmk0+KjOtuvfAbhMfzE92gfo15vE0PPEIe9VA0Pzkvqhva4k
 Efw7BhD2zx8hWvo0d5FfkdII89hSYnOCiSmhiX1Ln9q/gUqFW0TDAKpsMfAl7jAK
 Fap8M7uUStP9T6fF/gq01djYCYoWA/4v1lGKv4J4b9gWCqzGIF2sK0M7of7VERnr
 pEXJSUM228rk6EYjVmX/9Ujo1Y+xaNMFFZZSbKYrirAqjP540v0OPBCRQyB8qFaI
 NTcNJ+qESe6Q80mw5V+y/kD6kX0LERSDB+pigzOOOlmfMtD1uADR84tAynuTKNAU
 7/5K/YSQxceRY6RTgD17Al5lHIH5nw==
 =ToR+
 -----END PGP SIGNATURE-----

Merge tag '4.3.3'

Smack 4.3.3
This commit is contained in:
Florian Schmaus 2019-03-14 18:23:33 +01:00
commit daab6039a1
15 changed files with 250 additions and 63 deletions

View file

@ -141,6 +141,24 @@ hr {
<div id="pageBody"> <div id="pageBody">
<h2>4.3.3 -- <span style="font-weight: normal;">2019-03-14</span></h2>
<h2> Bug
</h2>
<ul>
<li>[<a href='https://issues.igniterealtime.org/browse/SMACK-856'>SMACK-856</a>] - Smack fails under JDK 11 because com.sun.jndi.dns.DnsContextFactory is not inaccessible
</li>
</ul>
<h2> Improvement
</h2>
<ul>
<li>[<a href='https://issues.igniterealtime.org/browse/SMACK-858'>SMACK-858</a>] - Dependency version specifier of jxmpp and MiniDNS include alpha/beta/... versions of the follow up version when Maven is used
</li>
<li>[<a href='https://issues.igniterealtime.org/browse/SMACK-859'>SMACK-859</a>] - MultiUserChat enter() should reset the timeout of the collector waiting for the final self presence to prevent timeouts for large MUCs
</li>
</ul>
<h2>4.3.2 -- <span style="font-weight: normal;">2019-02-22</span></h2> <h2>4.3.2 -- <span style="font-weight: normal;">2019-02-22</span></h2>
<h2> Bug <h2> Bug

View file

@ -266,6 +266,11 @@ public class XMPPBOSHConnection extends AbstractXMPPConnection {
client = null; client = null;
} }
instantShutdown();
}
@Override
public void instantShutdown() {
setWasAuthenticated(); setWasAuthenticated();
sessionID = null; sessionID = null;
done = true; done = true;

View file

@ -111,6 +111,7 @@ import org.jivesoftware.smack.provider.ExtensionElementProvider;
import org.jivesoftware.smack.provider.NonzaProvider; import org.jivesoftware.smack.provider.NonzaProvider;
import org.jivesoftware.smack.provider.ProviderManager; import org.jivesoftware.smack.provider.ProviderManager;
import org.jivesoftware.smack.sasl.core.SASLAnonymous; import org.jivesoftware.smack.sasl.core.SASLAnonymous;
import org.jivesoftware.smack.util.Async;
import org.jivesoftware.smack.util.DNSUtil; import org.jivesoftware.smack.util.DNSUtil;
import org.jivesoftware.smack.util.Objects; import org.jivesoftware.smack.util.Objects;
import org.jivesoftware.smack.util.PacketParserUtils; import org.jivesoftware.smack.util.PacketParserUtils;
@ -357,6 +358,9 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
*/ */
protected boolean authenticated = false; protected boolean authenticated = false;
// TODO: Migrate to ZonedDateTime once Smack's minimum required Android SDK level is 26 (8.0, Oreo) or higher.
protected long authenticatedConnectionInitiallyEstablishedTimestamp;
/** /**
* Flag that indicates if the user was authenticated with the server when the connection * Flag that indicates if the user was authenticated with the server when the connection
* to the server was closed (abruptly or not). * to the server was closed (abruptly or not).
@ -449,6 +453,12 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
@Override @Override
public abstract boolean isUsingCompression(); public abstract boolean isUsingCompression();
protected void initState() {
saslFeatureReceived.init();
lastFeaturesReceived.init();
tlsHandled.init();
}
/** /**
* Establishes a connection to the XMPP server. It basically * Establishes a connection to the XMPP server. It basically
* creates and maintains a connection to the server. * creates and maintains a connection to the server.
@ -467,12 +477,11 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
throwAlreadyConnectedExceptionIfAppropriate(); throwAlreadyConnectedExceptionIfAppropriate();
// Reset the connection state // Reset the connection state
initState();
saslAuthentication.init(); saslAuthentication.init();
saslFeatureReceived.init();
lastFeaturesReceived.init();
tlsHandled.init();
streamId = null; streamId = null;
try {
// Perform the actual connection to the XMPP service // Perform the actual connection to the XMPP service
connectInternal(); connectInternal();
@ -480,9 +489,12 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
// from the server and throw an error. First check if we've already negotiated TLS // from the server and throw an error. First check if we've already negotiated TLS
// and are secure, however (features get parsed a second time after TLS is established). // and are secure, however (features get parsed a second time after TLS is established).
if (!isSecureConnection() && getConfiguration().getSecurityMode() == SecurityMode.required) { if (!isSecureConnection() && getConfiguration().getSecurityMode() == SecurityMode.required) {
shutdown();
throw new SecurityRequiredByClientException(); throw new SecurityRequiredByClientException();
} }
} catch (SmackException | IOException | XMPPException | InterruptedException e) {
instantShutdown();
throw e;
}
// Make note of the fact that we're now connected. // Make note of the fact that we're now connected.
connected = true; connected = true;
@ -652,6 +664,9 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
} }
protected void afterSuccessfulLogin(final boolean resumed) throws NotConnectedException, InterruptedException { protected void afterSuccessfulLogin(final boolean resumed) throws NotConnectedException, InterruptedException {
if (!resumed) {
authenticatedConnectionInitiallyEstablishedTimestamp = System.currentTimeMillis();
}
// Indicate that we're now authenticated. // Indicate that we're now authenticated.
this.authenticated = true; this.authenticated = true;
@ -834,15 +849,18 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
* *
* @param exception the exception that causes the connection close event. * @param exception the exception that causes the connection close event.
*/ */
protected final synchronized void notifyConnectionError(Exception exception) { protected final void notifyConnectionError(final Exception exception) {
if (!isConnected()) { if (!isConnected()) {
LOGGER.log(Level.INFO, "Connection was already disconnected when attempting to handle " + exception, LOGGER.log(Level.INFO, "Connection was already disconnected when attempting to handle " + exception,
exception); exception);
return; return;
} }
ASYNC_BUT_ORDERED.performAsyncButOrdered(this, () -> {
currentConnectionException = exception; currentConnectionException = exception;
synchronized (AbstractXMPPConnection.this) {
notifyAll(); notifyAll();
}
for (StanzaCollector collector : collectors) { for (StanzaCollector collector : collectors) {
collector.notifyConnectionError(exception); collector.notifyConnectionError(exception);
@ -851,19 +869,26 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
tlsHandled.reportGenericFailure(smackWrappedException); tlsHandled.reportGenericFailure(smackWrappedException);
saslFeatureReceived.reportGenericFailure(smackWrappedException); saslFeatureReceived.reportGenericFailure(smackWrappedException);
lastFeaturesReceived.reportGenericFailure(smackWrappedException); lastFeaturesReceived.reportGenericFailure(smackWrappedException);
// TODO From XMPPTCPConnection. Was called in Smack 4.3 where notifyConnectionError() was part of
// XMPPTCPConnection. Create delegation method?
// maybeCompressFeaturesReceived.reportGenericFailure(smackWrappedException);
// Closes the connection temporary. A if the connection supports stream management, then a reconnection is // 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 // possible. Note that a connection listener of e.g. XMPPTCPConnection will drop the SM state in
// case the Exception is a StreamErrorException. // case the Exception is a StreamErrorException.
instantShutdown(); instantShutdown();
Async.go(() -> {
// Notify connection listeners of the error.
callConnectionClosedOnErrorListener(exception); callConnectionClosedOnErrorListener(exception);
}, AbstractXMPPConnection.this + " callConnectionClosedOnErrorListener()");
});
} }
protected void instantShutdown() { /**
// Default implementation simply calls shutdown(), subclasses may override this. * Performs an unclean disconnect and shutdown of the connection. Does not send a closing stream stanza.
shutdown(); */
} public abstract void instantShutdown();
/** /**
* Shuts the current connection down. * Shuts the current connection down.
@ -1811,6 +1836,18 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
return lastStanzaReceived; return lastStanzaReceived;
} }
/**
* Get the timestamp when the connection was the first time authenticated, i.e., when the first successful login was
* performed. Note that this value is not reset on disconnect, so it represents the timestamp from the last
* authenticated connection. The value is also not reset on stream resumption.
*
* @return the timestamp or {@code null}.
* @since 4.3.3
*/
public final long getAuthenticatedConnectionInitiallyEstablishedTimestamp() {
return authenticatedConnectionInitiallyEstablishedTimestamp;
}
/** /**
* Install a parsing exception callback, which will be invoked once an exception is encountered while parsing a * Install a parsing exception callback, which will be invoked once an exception is encountered while parsing a
* stanza. * stanza.

View file

@ -93,17 +93,24 @@ public abstract class SmackException extends Exception {
return new NoResponseException(sb.toString()); return new NoResponseException(sb.toString());
} }
public static NoResponseException newWith(XMPPConnection connection, @Deprecated
// TODO: Remove in Smack 4.4.
public static NoResponseException newWith(long timeout,
StanzaCollector collector) {
return newWith(timeout, collector.getStanzaFilter(), false);
}
public static NoResponseException newWith(long timeout,
StanzaCollector collector, boolean stanzaCollectorCancelled) { StanzaCollector collector, boolean stanzaCollectorCancelled) {
return newWith(connection, collector.getStanzaFilter(), stanzaCollectorCancelled); return newWith(timeout, collector.getStanzaFilter(), stanzaCollectorCancelled);
} }
public static NoResponseException newWith(XMPPConnection connection, StanzaFilter filter) { public static NoResponseException newWith(XMPPConnection connection, StanzaFilter filter) {
return newWith(connection, filter, false); return newWith(connection.getReplyTimeout(), filter, false);
} }
public static NoResponseException newWith(XMPPConnection connection, StanzaFilter filter, boolean stanzaCollectorCancelled) { public static NoResponseException newWith(long timeout, StanzaFilter filter, boolean stanzaCollectorCancelled) {
final StringBuilder sb = getWaitingFor(connection); final StringBuilder sb = getWaitingFor(timeout);
if (stanzaCollectorCancelled) { if (stanzaCollectorCancelled) {
sb.append(" StanzaCollector has been cancelled."); sb.append(" StanzaCollector has been cancelled.");
} }
@ -119,7 +126,10 @@ public abstract class SmackException extends Exception {
} }
private static StringBuilder getWaitingFor(XMPPConnection connection) { private static StringBuilder getWaitingFor(XMPPConnection connection) {
final long replyTimeout = connection.getReplyTimeout(); return getWaitingFor(connection.getReplyTimeout());
}
private static StringBuilder getWaitingFor(final long replyTimeout) {
final StringBuilder sb = new StringBuilder(256); final StringBuilder sb = new StringBuilder(256);
sb.append("No response received within reply timeout. Timeout was " sb.append("No response received within reply timeout. Timeout was "
+ replyTimeout + "ms (~" + replyTimeout + "ms (~"

View file

@ -290,7 +290,7 @@ public class StanzaCollector implements AutoCloseable {
if (!connection.isConnected()) { if (!connection.isConnected()) {
throw new NotConnectedException(connection, packetFilter); throw new NotConnectedException(connection, packetFilter);
} }
throw NoResponseException.newWith(connection, this, cancelled); throw NoResponseException.newWith(timeout, this, cancelled);
} }
XMPPErrorException.ifHasErrorThenThrow(result); XMPPErrorException.ifHasErrorThenThrow(result);

View file

@ -198,6 +198,12 @@ public class StanzaError extends AbstractError implements ExtensionElement {
public String toString() { public String toString() {
StringBuilder sb = new StringBuilder("XMPPError: "); StringBuilder sb = new StringBuilder("XMPPError: ");
sb.append(condition.toString()).append(" - ").append(type.toString()); sb.append(condition.toString()).append(" - ").append(type.toString());
String descriptiveText = getDescriptiveText();
if (descriptiveText != null) {
sb.append(" [").append(descriptiveText).append(']');
}
if (errorGenerator != null) { if (errorGenerator != null) {
sb.append(". Generated by ").append(errorGenerator); sb.append(". Generated by ").append(errorGenerator);
} }

View file

@ -100,6 +100,11 @@ public class DummyConnection extends AbstractXMPPConnection {
callConnectionClosedListener(); callConnectionClosedListener();
} }
@Override
public void instantShutdown() {
shutdown();
}
@Override @Override
public boolean isSecureConnection() { public boolean isSecureConnection() {
return false; return false;
@ -226,4 +231,5 @@ public class DummyConnection extends AbstractXMPPConnection {
} }
} }
} }
} }

View file

@ -27,7 +27,6 @@ import org.jivesoftware.smack.XMPPConnectionRegistry;
import org.jivesoftware.smack.filter.AndFilter; import org.jivesoftware.smack.filter.AndFilter;
import org.jivesoftware.smack.filter.MessageTypeFilter; import org.jivesoftware.smack.filter.MessageTypeFilter;
import org.jivesoftware.smack.filter.NotFilter; import org.jivesoftware.smack.filter.NotFilter;
import org.jivesoftware.smack.filter.StanzaExtensionFilter;
import org.jivesoftware.smack.filter.StanzaFilter; import org.jivesoftware.smack.filter.StanzaFilter;
import org.jivesoftware.smack.filter.ToTypeFilter; import org.jivesoftware.smack.filter.ToTypeFilter;
import org.jivesoftware.smack.packet.Message; import org.jivesoftware.smack.packet.Message;
@ -46,13 +45,12 @@ public final class StableUniqueStanzaIdManager extends Manager {
MessageTypeFilter.NORMAL_OR_CHAT_OR_HEADLINE, MessageTypeFilter.NORMAL_OR_CHAT_OR_HEADLINE,
ToTypeFilter.ENTITY_FULL_OR_BARE_JID); ToTypeFilter.ENTITY_FULL_OR_BARE_JID);
private static final StanzaFilter ORIGIN_ID_FILTER = new StanzaExtensionFilter(OriginIdElement.ELEMENT, NAMESPACE);
// Listener for outgoing stanzas that adds origin-ids to outgoing stanzas. // Listener for outgoing stanzas that adds origin-ids to outgoing stanzas.
private final StanzaListener stanzaListener = new StanzaListener() { private static final StanzaListener ADD_ORIGIN_ID_INTERCEPTOR = new StanzaListener() {
@Override @Override
public void processStanza(Stanza stanza) { public void processStanza(Stanza stanza) {
OriginIdElement.addOriginId((Message) stanza); Message message = (Message) stanza;
OriginIdElement.addOriginId(message);
} }
}; };
@ -95,7 +93,7 @@ public final class StableUniqueStanzaIdManager extends Manager {
public synchronized void enable() { public synchronized void enable() {
ServiceDiscoveryManager.getInstanceFor(connection()).addFeature(NAMESPACE); ServiceDiscoveryManager.getInstanceFor(connection()).addFeature(NAMESPACE);
StanzaFilter filter = new AndFilter(OUTGOING_FILTER, new NotFilter(OUTGOING_FILTER)); StanzaFilter filter = new AndFilter(OUTGOING_FILTER, new NotFilter(OUTGOING_FILTER));
connection().addStanzaInterceptor(stanzaListener, filter); connection().addStanzaInterceptor(ADD_ORIGIN_ID_INTERCEPTOR, filter);
} }
/** /**
@ -103,7 +101,7 @@ public final class StableUniqueStanzaIdManager extends Manager {
*/ */
public synchronized void disable() { public synchronized void disable() {
ServiceDiscoveryManager.getInstanceFor(connection()).removeFeature(NAMESPACE); ServiceDiscoveryManager.getInstanceFor(connection()).removeFeature(NAMESPACE);
connection().removeStanzaInterceptor(stanzaListener); connection().removeStanzaInterceptor(ADD_ORIGIN_ID_INTERCEPTOR);
} }
/** /**

View file

@ -24,7 +24,11 @@ import org.xmlpull.v1.XmlPullParser;
public class OriginIdProvider extends ExtensionElementProvider<OriginIdElement> { public class OriginIdProvider extends ExtensionElementProvider<OriginIdElement> {
public static final OriginIdProvider TEST_INSTANCE = new OriginIdProvider(); public static final OriginIdProvider INSTANCE = new OriginIdProvider();
// TODO: Remove in Smack 4.4.
@Deprecated
public static final OriginIdProvider TEST_INSTANCE = INSTANCE;
@Override @Override
public OriginIdElement parse(XmlPullParser parser, int initialDepth, XmlEnvironment xmlEnvironment) { public OriginIdElement parse(XmlPullParser parser, int initialDepth, XmlEnvironment xmlEnvironment) {

View file

@ -24,7 +24,11 @@ import org.xmlpull.v1.XmlPullParser;
public class StanzaIdProvider extends ExtensionElementProvider<StanzaIdElement> { public class StanzaIdProvider extends ExtensionElementProvider<StanzaIdElement> {
public static StanzaIdProvider TEST_INSTANCE = new StanzaIdProvider(); public static final StanzaIdProvider INSTANCE = new StanzaIdProvider();
// TODO: Remove in Smack 4.4.
@Deprecated
public static final StanzaIdProvider TEST_INSTANCE = INSTANCE;
@Override @Override
public StanzaIdElement parse(XmlPullParser parser, int initialDepth, XmlEnvironment xmlEnvironment) { public StanzaIdElement parse(XmlPullParser parser, int initialDepth, XmlEnvironment xmlEnvironment) {

View file

@ -25,6 +25,7 @@ import static org.custommonkey.xmlunit.XMLAssert.assertXMLEqual;
import org.jivesoftware.smack.packet.Message; import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.test.util.SmackTestSuite; import org.jivesoftware.smack.test.util.SmackTestSuite;
import org.jivesoftware.smack.test.util.TestUtils; import org.jivesoftware.smack.test.util.TestUtils;
import org.jivesoftware.smack.util.PacketParserUtils;
import org.jivesoftware.smackx.sid.element.OriginIdElement; import org.jivesoftware.smackx.sid.element.OriginIdElement;
import org.jivesoftware.smackx.sid.element.StanzaIdElement; import org.jivesoftware.smackx.sid.element.StanzaIdElement;
import org.jivesoftware.smackx.sid.provider.OriginIdProvider; import org.jivesoftware.smackx.sid.provider.OriginIdProvider;
@ -42,7 +43,7 @@ public class StableUniqueStanzaIdTest extends SmackTestSuite {
assertEquals("alice@wonderland.lit", element.getBy()); assertEquals("alice@wonderland.lit", element.getBy());
assertXMLEqual(xml, element.toXML().toString()); assertXMLEqual(xml, element.toXML().toString());
StanzaIdElement parsed = StanzaIdProvider.TEST_INSTANCE.parse(TestUtils.getParser(xml)); StanzaIdElement parsed = StanzaIdProvider.INSTANCE.parse(TestUtils.getParser(xml));
assertEquals(element.getId(), parsed.getId()); assertEquals(element.getId(), parsed.getId());
assertEquals(element.getBy(), parsed.getBy()); assertEquals(element.getBy(), parsed.getBy());
} }
@ -54,7 +55,7 @@ public class StableUniqueStanzaIdTest extends SmackTestSuite {
assertEquals("de305d54-75b4-431b-adb2-eb6b9e546013", element.getId()); assertEquals("de305d54-75b4-431b-adb2-eb6b9e546013", element.getId());
assertXMLEqual(xml, element.toXML().toString()); assertXMLEqual(xml, element.toXML().toString());
OriginIdElement parsed = OriginIdProvider.TEST_INSTANCE.parse(TestUtils.getParser(xml)); OriginIdElement parsed = OriginIdProvider.INSTANCE.parse(TestUtils.getParser(xml));
assertEquals(element.getId(), parsed.getId()); assertEquals(element.getId(), parsed.getId());
} }
@ -81,4 +82,17 @@ public class StableUniqueStanzaIdTest extends SmackTestSuite {
assertTrue(StanzaIdElement.hasStanzaId(message)); assertTrue(StanzaIdElement.hasStanzaId(message));
assertEquals(stanzaId, StanzaIdElement.getStanzaId(message)); assertEquals(stanzaId, StanzaIdElement.getStanzaId(message));
} }
@Test
public void testMultipleUssidExtensions() throws Exception {
String message = "<message xmlns='jabber:client' from='e4aec989-3e20-4846-83bf-f50df89b5d07@muclight.example.com/user1@example.com' to='user1@example.com' id='6b71fe3a-3cb2-489c-9c8e-b6879761d15e' type='groupchat'>" +
"<body>Test message</body>" +
"<markable xmlns='urn:xmpp:chat-markers:0'/>" +
"<stanza-id by='e4aec989-3e20-4846-83bf-f50df89b5d07@muclight.example.com' id='B0KK24ETVC81' xmlns='urn:xmpp:sid:0'/>" +
"<stanza-id by='user1@example.com' id='B0KK24EV89G1' xmlns='urn:xmpp:sid:0'/>" +
"</message>";
Message messageStanza = PacketParserUtils.parseStanza(message);
assertTrue(StanzaIdElement.hasStanzaId(messageStanza));
}
} }

View file

@ -341,9 +341,10 @@ public class MultiUserChat {
// Setup the messageListeners and presenceListeners *before* the join presence is send. // Setup the messageListeners and presenceListeners *before* the join presence is send.
connection.addSyncStanzaListener(messageListener, fromRoomGroupchatFilter); connection.addSyncStanzaListener(messageListener, fromRoomGroupchatFilter);
connection.addSyncStanzaListener(presenceListener, new AndFilter(fromRoomFilter, StanzaFilter presenceFromRoomFilter = new AndFilter(fromRoomFilter,
StanzaTypeFilter.PRESENCE, StanzaTypeFilter.PRESENCE,
PossibleFromTypeFilter.ENTITY_FULL_JID)); PossibleFromTypeFilter.ENTITY_FULL_JID);
connection.addSyncStanzaListener(presenceListener, presenceFromRoomFilter);
// @formatter:off // @formatter:off
connection.addSyncStanzaListener(subjectListener, connection.addSyncStanzaListener(subjectListener,
new AndFilter(fromRoomFilter, new AndFilter(fromRoomFilter,
@ -372,15 +373,27 @@ public class MultiUserChat {
) )
); );
// @formatter:on // @formatter:on
StanzaCollector presenceStanzaCollector = null;
Presence presence; Presence presence;
try { try {
presence = connection.createStanzaCollectorAndSend(responseFilter, joinPresence).nextResultOrThrow(conf.getTimeout()); // This stanza collector will collect the final self presence from the MUC, which also signals that we have successful entered the MUC.
StanzaCollector selfPresenceCollector = connection.createStanzaCollectorAndSend(responseFilter, joinPresence);
StanzaCollector.Configuration presenceStanzaCollectorConfguration = StanzaCollector.newConfiguration().setCollectorToReset(
selfPresenceCollector).setStanzaFilter(presenceFromRoomFilter);
// This stanza collector is used to reset the timeout of the selfPresenceCollector.
presenceStanzaCollector = connection.createStanzaCollector(presenceStanzaCollectorConfguration);
presence = selfPresenceCollector.nextResultOrThrow(conf.getTimeout());
} }
catch (NotConnectedException | InterruptedException | NoResponseException | XMPPErrorException e) { catch (NotConnectedException | InterruptedException | NoResponseException | XMPPErrorException e) {
// Ensure that all callbacks are removed if there is an exception // Ensure that all callbacks are removed if there is an exception
removeConnectionCallbacks(); removeConnectionCallbacks();
throw e; throw e;
} }
finally {
if (presenceStanzaCollector != null) {
presenceStanzaCollector.cancel();
}
}
// This presence must be send from a full JID. We use the resourcepart of this JID as nick, since the room may // This presence must be send from a full JID. We use the resourcepart of this JID as nick, since the room may
// performed roomnick rewriting // performed roomnick rewriting

View file

@ -18,6 +18,7 @@ package org.jivesoftware.smack.util.dns.javax;
import java.net.InetAddress; import java.net.InetAddress;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List; import java.util.List;
import java.util.logging.Level; import java.util.logging.Level;
@ -40,6 +41,7 @@ import org.minidns.dnsname.DnsName;
/** /**
* A DNS resolver (mostly for SRV records), which makes use of the API provided in the javax.* namespace. * A DNS resolver (mostly for SRV records), which makes use of the API provided in the javax.* namespace.
* Note that using JavaxResovler requires applications using newer Java versions (at least 11) to declare a dependency on the "sun.jdk" module.
* *
* @author Florian Schmaus * @author Florian Schmaus
* *
@ -51,7 +53,9 @@ public class JavaxResolver extends DNSResolver implements SmackInitializer {
static { static {
try { try {
dirContext = new InitialDirContext(); Hashtable<String, String> env = new Hashtable<>();
env.put("java.naming.factory.initial", "com.sun.jndi.dns.DnsContextFactory");
dirContext = new InitialDirContext(env);
} catch (NamingException e) { } catch (NamingException e) {
LOGGER.log(Level.SEVERE, "Could not construct InitialDirContext", e); LOGGER.log(Level.SEVERE, "Could not construct InitialDirContext", e);
} }

View file

@ -257,6 +257,15 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
*/ */
private final Collection<StanzaListener> stanzaAcknowledgedListeners = new ConcurrentLinkedQueue<>(); private final Collection<StanzaListener> stanzaAcknowledgedListeners = new ConcurrentLinkedQueue<>();
/**
* These listeners are invoked for every stanza that got dropped.
* <p>
* We use a {@link ConcurrentLinkedQueue} here in order to allow the listeners to remove
* themselves after they have been invoked.
* </p>
*/
private final Collection<StanzaListener> stanzaDroppedListeners = new ConcurrentLinkedQueue<>();
/** /**
* This listeners are invoked for a acknowledged stanza that has the given stanza ID. They will * This listeners are invoked for a acknowledged stanza that has the given stanza ID. They will
* only be invoked once and automatically removed after that. * only be invoked once and automatically removed after that.
@ -421,10 +430,25 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
} }
} }
} }
// (Re-)send the stanzas *after* we tried to enable SM // Inform client about failed resumption if possible, resend stanzas otherwise
// Process the stanzas synchronously so a client can re-queue them for transmission
// before it is informed about connection success
if (!stanzaDroppedListeners.isEmpty()) {
for (Stanza stanza : previouslyUnackedStanzas) {
for (StanzaListener listener : stanzaDroppedListeners) {
try {
listener.processStanza(stanza);
}
catch (InterruptedException | NotConnectedException | NotLoggedInException e) {
LOGGER.log(Level.FINER, "StanzaDroppedListener received exception", e);
}
}
}
} else {
for (Stanza stanza : previouslyUnackedStanzas) { for (Stanza stanza : previouslyUnackedStanzas) {
sendStanzaInternal(stanza); sendStanzaInternal(stanza);
} }
}
afterSuccessfulLogin(false); afterSuccessfulLogin(false);
} }
@ -452,9 +476,6 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
shutdown(false); shutdown(false);
} }
/**
* Performs an unclean disconnect and shutdown of the connection. Does not send a closing stream stanza.
*/
@Override @Override
public synchronized void instantShutdown() { public synchronized void instantShutdown() {
shutdown(true); shutdown(true);
@ -492,6 +513,8 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
// Reset the stream management session id to null, since if the stream is cleanly closed, i.e. sending a closing // Reset the stream management session id to null, since if the stream is cleanly closed, i.e. sending a closing
// stream tag, there is no longer a stream to resume. // stream tag, there is no longer a stream to resume.
smSessionId = null; smSessionId = null;
// Note that we deliberately do not reset authenticatedConnectionInitiallyEstablishedTimestamp here, so that the
// information is available in the connectionClosedOnError() listeners.
} }
authenticated = false; authenticated = false;
connected = false; connected = false;
@ -499,6 +522,16 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
reader = null; reader = null;
writer = null; writer = null;
initState();
// Wait for reader and writer threads to be terminated.
readerWriterSemaphore.acquireUninterruptibly(2);
readerWriterSemaphore.release(2);
}
@Override
protected void initState() {
super.initState();
maybeCompressFeaturesReceived.init(); maybeCompressFeaturesReceived.init();
compressSyncPoint.init(); compressSyncPoint.init();
smResumedSyncPoint.init(); smResumedSyncPoint.init();
@ -784,14 +817,13 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
} }
@Override @Override
protected void afterFeaturesReceived() throws NotConnectedException, InterruptedException { protected void afterFeaturesReceived() throws NotConnectedException, InterruptedException, SecurityRequiredByServerException {
StartTls startTlsFeature = getFeature(StartTls.ELEMENT, StartTls.NAMESPACE); StartTls startTlsFeature = getFeature(StartTls.ELEMENT, StartTls.NAMESPACE);
if (startTlsFeature != null) { if (startTlsFeature != null) {
if (startTlsFeature.required() && config.getSecurityMode() == SecurityMode.disabled) { if (startTlsFeature.required() && config.getSecurityMode() == SecurityMode.disabled) {
SmackException smackException = new SecurityRequiredByServerException(); SecurityRequiredByServerException smackException = new SecurityRequiredByServerException();
tlsHandled.reportFailure(smackException); tlsHandled.reportFailure(smackException);
notifyConnectionError(smackException); throw smackException;
return;
} }
if (config.getSecurityMode() != ConnectionConfiguration.SecurityMode.disabled) { if (config.getSecurityMode() != ConnectionConfiguration.SecurityMode.disabled) {
@ -1062,7 +1094,11 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
LOGGER.info(XMPPTCPConnection.this LOGGER.info(XMPPTCPConnection.this
+ " received closing </stream> element." + " received closing </stream> element."
+ " Server wants to terminate the connection, calling disconnect()"); + " Server wants to terminate the connection, calling disconnect()");
ASYNC_BUT_ORDERED.performAsyncButOrdered(XMPPTCPConnection.this, new Runnable() {
@Override
public void run() {
disconnect(); disconnect();
}});
} }
} }
break; break;
@ -1567,6 +1603,32 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
stanzaAcknowledgedListeners.clear(); stanzaAcknowledgedListeners.clear();
} }
/**
* Add a Stanza dropped listener.
* <p>
* Those listeners will be invoked every time a Stanza has been dropped due to a failed SM resume. They will not get
* automatically removed. If at least one StanzaDroppedListener is configured, no attempt will be made to retransmit
* the Stanzas.
* </p>
*
* @param listener the listener to add.
* @since 4.3.3
*/
public void addStanzaDroppedListener(StanzaListener listener) {
stanzaDroppedListeners.add(listener);
}
/**
* Remove the given Stanza dropped listener.
*
* @param listener the listener.
* @return true if the listener was removed.
* @since 4.3.3
*/
public boolean removeStanzaDroppedListener(StanzaListener listener) {
return stanzaDroppedListeners.remove(listener);
}
/** /**
* Add a new Stanza ID acknowledged listener for the given ID. * Add a new Stanza ID acknowledged listener for the given ID.
* <p> * <p>

View file

@ -2,6 +2,12 @@ allprojects {
ext { ext {
shortVersion = '4.4.0-alpha2' shortVersion = '4.4.0-alpha2'
isSnapshot = true isSnapshot = true
// When using dynamic versions for those, do *not* use [1.0,
// 2.0), since this will also pull in 2.0-alpha1. Instead use
// [1.0, 1.0.99].
// See also:
// - https://issues.apache.org/jira/browse/MNG-6232
// - https://issues.igniterealtime.org/browse/SMACK-858
jxmppVersion = '0.7.0-alpha5' jxmppVersion = '0.7.0-alpha5'
miniDnsVersion = '0.4.0-alpha3' miniDnsVersion = '0.4.0-alpha3'
smackMinAndroidSdk = 19 smackMinAndroidSdk = 19