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 c47deffff..7967bdc12 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/AbstractXMPPConnection.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/AbstractXMPPConnection.java @@ -1095,6 +1095,7 @@ public abstract class AbstractXMPPConnection implements XMPPConnection { if (packet instanceof IQ) { final IQ iq = (IQ) packet; if (iq.isRequestIQ()) { + final IQ iqRequest = iq; final String key = XmppStringUtils.generateKey(iq.getChildElementName(), iq.getChildElementNamespace()); IQRequestHandler iqRequestHandler; final IQ.Type type = iq.getType(); @@ -1160,6 +1161,11 @@ public abstract class AbstractXMPPConnection implements XMPPConnection { // e.g. to avoid presence leaks. return; } + + assert (response.getType() == IQ.Type.result || response.getType() == IQ.Type.error); + + response.setTo(iqRequest.getFrom()); + response.setStanzaId(iqRequest.getStanzaId()); try { sendStanza(response); } diff --git a/smack-core/src/main/java/org/jivesoftware/smack/ConnectionConfiguration.java b/smack-core/src/main/java/org/jivesoftware/smack/ConnectionConfiguration.java index 6a0d93d20..28c7ea782 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/ConnectionConfiguration.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/ConnectionConfiguration.java @@ -18,6 +18,7 @@ package org.jivesoftware.smack; import java.net.InetAddress; +import java.net.UnknownHostException; import java.security.KeyStore; import java.util.Arrays; import java.util.Collection; @@ -45,6 +46,7 @@ import org.jxmpp.jid.impl.JidCreate; import org.jxmpp.jid.parts.Resourcepart; import org.jxmpp.stringprep.XmppStringprepException; import org.minidns.dnsname.DnsName; +import org.minidns.util.InetAddressUtil; /** * Configuration to use while establishing the connection to the server. @@ -175,6 +177,14 @@ public abstract class ConnectionConfiguration { } + DnsName getHost() { + return host; + } + + InetAddress getHostAddress() { + return hostAddress; + } + /** * Returns the server name of the target server. * @@ -646,6 +656,33 @@ public abstract class ConnectionConfiguration { return getThis(); } + /** + * Set the host to connect to by either its fully qualified domain name (FQDN) or its IP. + * + * @param fqdnOrIp a CharSequence either representing the FQDN or the IP of the host. + * @return a reference to this builder. + * @see #setHost(DnsName) + * @see #setHostAddress(InetAddress) + * @since 4.3.2 + */ + public B setHostAddressByNameOrIp(CharSequence fqdnOrIp) { + String fqdnOrIpString = fqdnOrIp.toString(); + if (InetAddressUtil.isIpAddress(fqdnOrIp)) { + InetAddress hostInetAddress; + try { + hostInetAddress = InetAddress.getByName(fqdnOrIpString); + } + catch (UnknownHostException e) { + // Should never happen. + throw new AssertionError(e); + } + setHostAddress(hostInetAddress); + } else { + setHost(fqdnOrIpString); + } + return getThis(); + } + public B setPort(int port) { if (port < 0 || port > 65535) { throw new IllegalArgumentException( diff --git a/smack-core/src/test/java/org/jivesoftware/smack/ConnectionConfigurationTest.java b/smack-core/src/test/java/org/jivesoftware/smack/ConnectionConfigurationTest.java new file mode 100644 index 000000000..6637eae56 --- /dev/null +++ b/smack-core/src/test/java/org/jivesoftware/smack/ConnectionConfigurationTest.java @@ -0,0 +1,79 @@ +/** + * + * Copyright 2018 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 static org.junit.Assert.assertEquals; + +import org.junit.Test; +import org.jxmpp.jid.JidTestUtil; + +public class ConnectionConfigurationTest { + + @Test + public void setIp() { + DummyConnectionConfiguration.Builder builder = newUnitTestBuilder(); + + final String ip = "192.168.0.1"; + builder.setHostAddressByNameOrIp(ip); + + DummyConnectionConfiguration connectionConfiguration = builder.build(); + assertEquals('/' + ip, connectionConfiguration.getHostAddress().toString()); + } + + @Test + public void setFqdn() { + DummyConnectionConfiguration.Builder builder = newUnitTestBuilder(); + + final String fqdn = "foo.example.org"; + builder.setHostAddressByNameOrIp(fqdn); + + DummyConnectionConfiguration connectionConfiguration = builder.build(); + assertEquals(fqdn, connectionConfiguration.getHost().toString()); + } + + private static DummyConnectionConfiguration.Builder newUnitTestBuilder() { + DummyConnectionConfiguration.Builder builder = DummyConnectionConfiguration.builder(); + builder.setXmppDomain(JidTestUtil.DOMAIN_BARE_JID_1); + return builder; + } + + private static final class DummyConnectionConfiguration extends ConnectionConfiguration { + + protected DummyConnectionConfiguration(Builder builder) { + super(builder); + } + + public static Builder builder() { + return new Builder(); + } + + private static final class Builder + extends ConnectionConfiguration.Builder { + + @Override + public DummyConnectionConfiguration build() { + return new DummyConnectionConfiguration(this); + } + + @Override + protected Builder getThis() { + return this; + } + + } + } +} diff --git a/smack-tcp/src/main/java/org/jivesoftware/smack/sm/StreamManagementException.java b/smack-tcp/src/main/java/org/jivesoftware/smack/sm/StreamManagementException.java index f5e30784d..553d9d702 100644 --- a/smack-tcp/src/main/java/org/jivesoftware/smack/sm/StreamManagementException.java +++ b/smack-tcp/src/main/java/org/jivesoftware/smack/sm/StreamManagementException.java @@ -16,10 +16,13 @@ */ package org.jivesoftware.smack.sm; +import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.concurrent.BlockingQueue; import org.jivesoftware.smack.SmackException; +import org.jivesoftware.smack.packet.Element; import org.jivesoftware.smack.packet.Stanza; public abstract class StreamManagementException extends SmackException { @@ -110,5 +113,56 @@ public abstract class StreamManagementException extends SmackException { return ackedStanzas; } } + + public static final class UnacknowledgedQueueFullException extends StreamManagementException { + + /** + * + */ + private static final long serialVersionUID = 1L; + + private final int overflowElementNum; + private final int droppedElements; + private final List elements; + private final List unacknowledgesStanzas; + + private UnacknowledgedQueueFullException(String message, int overflowElementNum, int droppedElements, List elements, + List unacknowledgesStanzas) { + super(message); + this.overflowElementNum = overflowElementNum; + this.droppedElements = droppedElements; + this.elements = elements; + this.unacknowledgesStanzas = unacknowledgesStanzas; + } + + public int getOverflowElementNum() { + return overflowElementNum; + } + + public int getDroppedElements() { + return droppedElements; + } + + public List getElements() { + return elements; + } + + public List getUnacknowledgesStanzas() { + return unacknowledgesStanzas; + } + + public static UnacknowledgedQueueFullException newWith(int overflowElementNum, List elements, + BlockingQueue unacknowledgedStanzas) { + final int unacknowledgesStanzasQueueSize = unacknowledgedStanzas.size(); + List localUnacknowledgesStanzas = new ArrayList<>(unacknowledgesStanzasQueueSize); + localUnacknowledgesStanzas.addAll(unacknowledgedStanzas); + int droppedElements = elements.size() - overflowElementNum - 1; + + String message = "The queue size " + unacknowledgesStanzasQueueSize + " is not able to fit another " + + droppedElements + " potential stanzas type top-level stream-elements."; + return new UnacknowledgedQueueFullException(message, overflowElementNum, droppedElements, elements, + localUnacknowledgesStanzas); + } + } } 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 e8fdd9317..d34896fc2 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 @@ -1511,7 +1511,16 @@ public class XMPPTCPConnection extends AbstractXMPPConnection { private void drainWriterQueueToUnacknowledgedStanzas() { List elements = new ArrayList<>(queue.size()); queue.drainTo(elements); - for (Element element : elements) { + for (int i = 0; i < elements.size(); i++) { + Element element = elements.get(i); + // If the unacknowledgedStanza queue is full, then bail out with a warning message. See SMACK-844. + if (unacknowledgedStanzas.remainingCapacity() == 0) { + StreamManagementException.UnacknowledgedQueueFullException exception = StreamManagementException.UnacknowledgedQueueFullException + .newWith(i, elements, unacknowledgedStanzas); + LOGGER.log(Level.WARNING, + "Some stanzas may be lost as not all could be drained to the unacknowledged stanzas queue", exception); + return; + } if (element instanceof Stanza) { unacknowledgedStanzas.add((Stanza) element); }