mirror of
https://github.com/vanitasvitae/Smack.git
synced 2025-09-09 17:19:39 +02:00
Merge branch '4.2'
This commit is contained in:
commit
384c285fbc
20 changed files with 303 additions and 79 deletions
|
@ -43,7 +43,10 @@ import org.jivesoftware.smack.util.Async;
|
|||
* </ol>
|
||||
*
|
||||
* {@link ReconnectionPolicy#FIXED_DELAY} - The reconnection mechanism will try to reconnect after a fixed delay
|
||||
* independently from the number of reconnection attempts already performed
|
||||
* independently from the number of reconnection attempts already performed.
|
||||
* <p>
|
||||
* Interrupting the reconnection thread will abort the reconnection mechanism.
|
||||
* </p>
|
||||
*
|
||||
* @author Francisco Vives
|
||||
* @author Luca Stucchi
|
||||
|
@ -163,7 +166,7 @@ public final class ReconnectionManager {
|
|||
private ReconnectionManager(AbstractXMPPConnection connection) {
|
||||
weakRefConnection = new WeakReference<AbstractXMPPConnection>(connection);
|
||||
|
||||
reconnectionRunnable = new Thread() {
|
||||
reconnectionRunnable = new Runnable() {
|
||||
|
||||
/**
|
||||
* Holds the current number of reconnection attempts
|
||||
|
@ -211,6 +214,10 @@ public final class ReconnectionManager {
|
|||
if (connection == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Reset attempts to zero since a new reconnection cycle is started once this runs.
|
||||
attempts = 0;
|
||||
|
||||
// The process will try to reconnect until the connection is established or
|
||||
// the user cancel the reconnection process AbstractXMPPConnection.disconnect().
|
||||
while (isReconnectionPossible(connection)) {
|
||||
|
@ -219,7 +226,10 @@ public final class ReconnectionManager {
|
|||
// Sleep until we're ready for the next reconnection attempt. Notify
|
||||
// listeners once per second about how much time remains before the next
|
||||
// reconnection attempt.
|
||||
while (isReconnectionPossible(connection) && remainingSeconds > 0) {
|
||||
while (remainingSeconds > 0) {
|
||||
if (!isReconnectionPossible(connection)) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
Thread.sleep(1000);
|
||||
remainingSeconds--;
|
||||
|
@ -228,8 +238,9 @@ public final class ReconnectionManager {
|
|||
}
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
LOGGER.log(Level.FINE, "waiting for reconnection interrupted", e);
|
||||
break;
|
||||
LOGGER.log(Level.FINE, "Reconnection Thread was interrupted, aborting reconnection mechanism", e);
|
||||
// Exit the reconnection thread in case it was interrupted.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -237,24 +248,18 @@ public final class ReconnectionManager {
|
|||
listener.reconnectingIn(0);
|
||||
}
|
||||
|
||||
if (!isReconnectionPossible(connection)) {
|
||||
return;
|
||||
}
|
||||
// Makes a reconnection attempt
|
||||
try {
|
||||
if (isReconnectionPossible(connection)) {
|
||||
try {
|
||||
connection.connect();
|
||||
} catch (SmackException.AlreadyConnectedException e) {
|
||||
LOGGER.log(Level.FINER, "Connection was already connected on reconnection attempt", e);
|
||||
}
|
||||
try {
|
||||
connection.connect();
|
||||
}
|
||||
// TODO Starting with Smack 4.2, connect() will no
|
||||
// longer login automatically. So change this and the
|
||||
// previous lines to connection.connect().login() in the
|
||||
// 4.2, or any later, branch.
|
||||
if (!connection.isAuthenticated()) {
|
||||
connection.login();
|
||||
catch (SmackException.AlreadyConnectedException e) {
|
||||
LOGGER.log(Level.FINER, "Connection was already connected on reconnection attempt", e);
|
||||
}
|
||||
// Successfully reconnected.
|
||||
attempts = 0;
|
||||
connection.login();
|
||||
}
|
||||
catch (SmackException.AlreadyLoggedInException e) {
|
||||
// This can happen if another thread concurrently triggers a reconnection
|
||||
|
@ -262,12 +267,21 @@ public final class ReconnectionManager {
|
|||
// failure. See also SMACK-725.
|
||||
LOGGER.log(Level.FINER, "Reconnection not required, was already logged in", e);
|
||||
}
|
||||
catch (SmackException | IOException | XMPPException | InterruptedException e) {
|
||||
catch (SmackException | IOException | XMPPException e) {
|
||||
// Fires the failed reconnection notification
|
||||
for (ConnectionListener listener : connection.connectionListeners) {
|
||||
listener.reconnectionFailed(e);
|
||||
}
|
||||
// Failed to reconnect, try again.
|
||||
continue;
|
||||
} catch (InterruptedException e) {
|
||||
LOGGER.log(Level.FINE, "Reconnection Thread was interrupted, aborting reconnection mechanism", e);
|
||||
// Exit the reconnection thread in case it was interrupted.
|
||||
return;
|
||||
}
|
||||
|
||||
// Successfully reconnected .
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -314,7 +328,7 @@ public final class ReconnectionManager {
|
|||
*
|
||||
* @return true, if the reconnection mechanism is enabled.
|
||||
*/
|
||||
public boolean isAutomaticReconnectEnabled() {
|
||||
public synchronized boolean isAutomaticReconnectEnabled() {
|
||||
return automaticReconnectEnabled;
|
||||
}
|
||||
|
||||
|
@ -348,6 +362,20 @@ public final class ReconnectionManager {
|
|||
"Smack Reconnection Manager (" + connection.getConnectionCounter() + ')');
|
||||
}
|
||||
|
||||
/**
|
||||
* Abort a possibly running reconnection mechanism.
|
||||
*
|
||||
* @since 4.2.2
|
||||
*/
|
||||
public synchronized void abortPossiblyRunningReconnection() {
|
||||
if (reconnectionThread == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
reconnectionThread.interrupt();
|
||||
reconnectionThread = null;
|
||||
}
|
||||
|
||||
private final ConnectionListener connectionListener = new AbstractConnectionListener() {
|
||||
|
||||
@Override
|
||||
|
|
|
@ -184,7 +184,7 @@ public final class SmackConfiguration {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* Add a Collection of SASL mechanisms to the list to be used.
|
||||
*
|
||||
* @param mechs the Collection of SASL mechanisms to be added
|
||||
|
@ -204,7 +204,7 @@ public final class SmackConfiguration {
|
|||
defaultMechs.remove(mech);
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* Remove a Collection of SASL mechanisms to the list to be used.
|
||||
*
|
||||
* @param mechs the Collection of SASL mechanisms to be removed
|
||||
|
@ -339,7 +339,7 @@ public final class SmackConfiguration {
|
|||
return defaultHostnameVerififer;
|
||||
}
|
||||
|
||||
enum UnknownIqRequestReplyMode {
|
||||
public enum UnknownIqRequestReplyMode {
|
||||
doNotReply,
|
||||
replyFeatureNotImplemented,
|
||||
replyServiceUnavailable,
|
||||
|
|
|
@ -23,6 +23,7 @@ import java.util.List;
|
|||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
import org.jivesoftware.smack.util.Objects;
|
||||
import org.jivesoftware.smack.util.PacketUtil;
|
||||
import org.jivesoftware.smack.util.XmlStringBuilder;
|
||||
|
||||
|
@ -85,6 +86,7 @@ public class AbstractError {
|
|||
* @return the descriptive text or null.
|
||||
*/
|
||||
public String getDescriptiveText(String xmllang) {
|
||||
Objects.requireNonNull(xmllang, "xmllang must not be null");
|
||||
return descriptiveTexts.get(xmllang);
|
||||
}
|
||||
|
||||
|
@ -105,7 +107,8 @@ public class AbstractError {
|
|||
String xmllang = entry.getKey();
|
||||
String text = entry.getValue();
|
||||
xml.halfOpenElement("text").xmlnsAttribute(textNamespace)
|
||||
.xmllangAttribute(xmllang).rightAngleBracket();
|
||||
.optXmlLangAttribute(xmllang)
|
||||
.rightAngleBracket();
|
||||
xml.escape(text);
|
||||
xml.closeElement("text");
|
||||
}
|
||||
|
@ -120,6 +123,15 @@ public class AbstractError {
|
|||
protected List<ExtensionElement> extensions;
|
||||
|
||||
public B setDescriptiveTexts(Map<String, String> descriptiveTexts) {
|
||||
if (descriptiveTexts == null) {
|
||||
this.descriptiveTexts = null;
|
||||
return getThis();
|
||||
}
|
||||
for (String key : descriptiveTexts.keySet()) {
|
||||
if (key == null) {
|
||||
throw new IllegalArgumentException("descriptiveTexts cannot contain null key");
|
||||
}
|
||||
}
|
||||
if (this.descriptiveTexts == null) {
|
||||
this.descriptiveTexts = descriptiveTexts;
|
||||
}
|
||||
|
|
|
@ -38,14 +38,14 @@ import org.jivesoftware.smack.util.XmlStringBuilder;
|
|||
* <tr><td>conflict</td><td>CANCEL</td><td>8.3.3.2</td></tr>
|
||||
* <tr><td>feature-not-implemented</td><td>CANCEL</td><td>8.3.3.3</td></tr>
|
||||
* <tr><td>forbidden</td><td>AUTH</td><td>8.3.3.4</td></tr>
|
||||
* <tr><td>gone</td><td>MODIFY</td><td>8.3.3.5</td></tr>
|
||||
* <tr><td>gone</td><td>CANCEL</td><td>8.3.3.5</td></tr>
|
||||
* <tr><td>internal-server-error</td><td>WAIT</td><td>8.3.3.6</td></tr>
|
||||
* <tr><td>item-not-found</td><td>CANCEL</td><td>8.3.3.7</td></tr>
|
||||
* <tr><td>jid-malformed</td><td>MODIFY</td><td>8.3.3.8</td></tr>
|
||||
* <tr><td>not-acceptable</td><td> MODIFY</td><td>8.3.3.9</td></tr>
|
||||
* <tr><td>not-acceptable</td><td>MODIFY</td><td>8.3.3.9</td></tr>
|
||||
* <tr><td>not-allowed</td><td>CANCEL</td><td>8.3.3.10</td></tr>
|
||||
* <tr><td>not-authorized</td><td>AUTH</td><td>8.3.3.11</td></tr>
|
||||
* <tr><td>policy-violation</td><td>AUTH</td><td>8.3.3.12</td></tr>
|
||||
* <tr><td>policy-violation</td><td>MODIFY</td><td>8.3.3.12</td></tr>
|
||||
* <tr><td>recipient-unavailable</td><td>WAIT</td><td>8.3.3.13</td></tr>
|
||||
* <tr><td>redirect</td><td>MODIFY</td><td>8.3.3.14</td></tr>
|
||||
* <tr><td>registration-required</td><td>AUTH</td><td>8.3.3.15</td></tr>
|
||||
|
@ -54,7 +54,7 @@ import org.jivesoftware.smack.util.XmlStringBuilder;
|
|||
* <tr><td>resource-constraint</td><td>WAIT</td><td>8.3.3.18</td></tr>
|
||||
* <tr><td>service-unavailable</td><td>CANCEL</td><td>8.3.3.19</td></tr>
|
||||
* <tr><td>subscription-required</td><td>AUTH</td><td>8.3.3.20</td></tr>
|
||||
* <tr><td>undefined-condition</td><td>WAIT</td><td>8.3.3.21</td></tr>
|
||||
* <tr><td>undefined-condition</td><td>MODIFY</td><td>8.3.3.21</td></tr>
|
||||
* <tr><td>unexpected-request</td><td>WAIT</td><td>8.3.3.22</td></tr>
|
||||
* </table>
|
||||
*
|
||||
|
@ -69,7 +69,7 @@ public class XMPPError extends AbstractError {
|
|||
public static final String ERROR = "error";
|
||||
|
||||
private static final Logger LOGGER = Logger.getLogger(XMPPError.class.getName());
|
||||
private static final Map<Condition, Type> CONDITION_TO_TYPE = new HashMap<Condition, Type>();
|
||||
static final Map<Condition, Type> CONDITION_TO_TYPE = new HashMap<Condition, Type>();
|
||||
|
||||
static {
|
||||
CONDITION_TO_TYPE.put(Condition.bad_request, Type.MODIFY);
|
||||
|
@ -90,9 +90,10 @@ public class XMPPError extends AbstractError {
|
|||
CONDITION_TO_TYPE.put(Condition.remote_server_not_found, Type.CANCEL);
|
||||
CONDITION_TO_TYPE.put(Condition.remote_server_timeout, Type.WAIT);
|
||||
CONDITION_TO_TYPE.put(Condition.resource_constraint, Type.WAIT);
|
||||
CONDITION_TO_TYPE.put(Condition.service_unavailable, Type.WAIT);
|
||||
CONDITION_TO_TYPE.put(Condition.subscription_required, Type.WAIT);
|
||||
CONDITION_TO_TYPE.put(Condition.unexpected_request, Type.MODIFY);
|
||||
CONDITION_TO_TYPE.put(Condition.service_unavailable, Type.CANCEL);
|
||||
CONDITION_TO_TYPE.put(Condition.subscription_required, Type.AUTH);
|
||||
CONDITION_TO_TYPE.put(Condition.undefined_condition, Type.MODIFY);
|
||||
CONDITION_TO_TYPE.put(Condition.unexpected_request, Type.WAIT);
|
||||
}
|
||||
|
||||
private final Condition condition;
|
||||
|
|
|
@ -749,6 +749,12 @@ public class PacketParserUtils {
|
|||
descriptiveTexts = new HashMap<String, String>();
|
||||
}
|
||||
String xmllang = getLanguageAttribute(parser);
|
||||
if (xmllang == null) {
|
||||
// XMPPError assumes the default locale, 'en', or the empty string.
|
||||
// Establish the invariant that there is never null as a key.
|
||||
xmllang = "";
|
||||
}
|
||||
|
||||
String text = parser.nextText();
|
||||
String previousValue = descriptiveTexts.put(xmllang, text);
|
||||
assert (previousValue == null);
|
||||
|
|
|
@ -361,7 +361,7 @@ public class XmlStringBuilder implements Appendable, CharSequence {
|
|||
}
|
||||
|
||||
public XmlStringBuilder optXmlLangAttribute(String lang) {
|
||||
if (lang != null) {
|
||||
if (!StringUtils.isNullOrEmpty(lang)) {
|
||||
xmllangAttribute(lang);
|
||||
}
|
||||
return this;
|
||||
|
|
|
@ -59,6 +59,20 @@ public abstract class DNSResolver {
|
|||
return new HostAddress(name, port, inetAddresses);
|
||||
}
|
||||
|
||||
/**
|
||||
* Lookup the IP addresses of a given host name. Returns <code>null</code> if there was an error, in which the error
|
||||
* reason will be added in form of a <code>HostAddress</code> to <code>failedAddresses</code>. Returns a empty list
|
||||
* in case the DNS name exists but has no associated A or AAAA resource records. Otherwise, if the resolution was
|
||||
* successful <em>and</em> there is at least one A or AAAA resource record, then a non-empty list will be returned.
|
||||
* <p>
|
||||
* Concrete DNS resolver implementations are free to overwrite this, but have to stick to the interface contract.
|
||||
* </p>
|
||||
*
|
||||
* @param name the DNS name to lookup
|
||||
* @param failedAddresses a list with the failed addresses
|
||||
* @param dnssecMode the selected DNSSEC mode
|
||||
* @return A list, either empty or non-empty, or <code>null</code>
|
||||
*/
|
||||
protected List<InetAddress> lookupHostAddress0(String name, List<HostAddress> failedAddresses, DnssecMode dnssecMode) {
|
||||
// Default implementation of a DNS name lookup for A/AAAA records. It is assumed that this method does never
|
||||
// support DNSSEC. Subclasses are free to override this method.
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
/**
|
||||
*
|
||||
* Copyright © 2017 Ingo Bauersachs
|
||||
*
|
||||
* 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.packet;
|
||||
|
||||
import static org.jivesoftware.smack.packet.XMPPError.Condition;
|
||||
import static org.jivesoftware.smack.packet.XMPPError.Type;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
public class XMPPErrorTest {
|
||||
@Test
|
||||
public void testConditionHasDefaultTypeMapping() throws NoSuchFieldException, IllegalAccessException {
|
||||
Map<Condition, Type> conditionToTypeMap = XMPPError.CONDITION_TO_TYPE;
|
||||
assertEquals("CONDITION_TO_TYPE map is likely out of sync with Condition enum",
|
||||
Condition.values().length,
|
||||
conditionToTypeMap.size());
|
||||
}
|
||||
}
|
|
@ -25,7 +25,9 @@ import static org.junit.Assert.assertTrue;
|
|||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
import javax.xml.parsers.FactoryConfigurationError;
|
||||
|
@ -35,6 +37,7 @@ import javax.xml.transform.TransformerException;
|
|||
import org.jivesoftware.smack.packet.Message;
|
||||
import org.jivesoftware.smack.packet.Presence;
|
||||
import org.jivesoftware.smack.packet.Stanza;
|
||||
import org.jivesoftware.smack.packet.XMPPError;
|
||||
import org.jivesoftware.smack.sasl.SASLError;
|
||||
import org.jivesoftware.smack.sasl.packet.SaslStreamElements;
|
||||
import org.jivesoftware.smack.sasl.packet.SaslStreamElements.SASLFailure;
|
||||
|
@ -867,4 +870,44 @@ public class PacketParserUtilsTest {
|
|||
return otherLanguage;
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void descriptiveTextNullLangPassedMap() throws Exception {
|
||||
final String text = "Dummy descriptive text";
|
||||
Map<String, String> texts = new HashMap<>();
|
||||
texts.put(null, text);
|
||||
XMPPError
|
||||
.getBuilder(XMPPError.Condition.internal_server_error)
|
||||
.setDescriptiveTexts(texts)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ensureNoEmptyLangInDescriptiveText() throws Exception {
|
||||
final String text = "Dummy descriptive text";
|
||||
Map<String, String> texts = new HashMap<>();
|
||||
texts.put("", text);
|
||||
XMPPError error = XMPPError
|
||||
.getBuilder(XMPPError.Condition.internal_server_error)
|
||||
.setDescriptiveTexts(texts)
|
||||
.build();
|
||||
final String errorXml = XMLBuilder
|
||||
.create(XMPPError.ERROR).a("type", "cancel").up()
|
||||
.element("internal-server-error", XMPPError.NAMESPACE).up()
|
||||
.element("text", XMPPError.NAMESPACE).t(text).up()
|
||||
.asString();
|
||||
XmlUnitUtils.assertSimilar(errorXml, error.toXML());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ensureNoNullLangInParsedDescriptiveTexts() throws Exception {
|
||||
final String text = "Dummy descriptive text";
|
||||
final String errorXml = XMLBuilder
|
||||
.create(XMPPError.ERROR).a("type", "cancel").up()
|
||||
.element("internal-server-error", XMPPError.NAMESPACE).up()
|
||||
.element("text", XMPPError.NAMESPACE).t(text).up()
|
||||
.asString();
|
||||
XmlPullParser parser = TestUtils.getParser(errorXml);
|
||||
XMPPError error = PacketParserUtils.parseError(parser).build();
|
||||
assertEquals(text, error.getDescriptiveText());
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue