diff --git a/build.gradle b/build.gradle index bcffddd94..0fd69f2e0 100644 --- a/build.gradle +++ b/build.gradle @@ -95,6 +95,7 @@ allprojects { options.addStringOption('Xdoclint:none', '-quiet') } } + } gradle.taskGraph.whenReady { taskGraph -> diff --git a/documentation/processing.md b/documentation/processing.md index 4e4fe1b9b..6d3b41c7e 100644 --- a/documentation/processing.md +++ b/documentation/processing.md @@ -46,7 +46,7 @@ own filters by coding to the `PacketFilter` interface. The default set of filters includes: * `PacketTypeFilter` -- filters for packets that are a particular Class type. - * `PacketIDFilter` -- filters for packets with a particular packet ID. + * `StanzaIdFilter` -- filters for packets with a particular packet ID. * `ThreadFilter` -- filters for message packets with a particular thread ID. * `ToContainsFilter` -- filters for packets that are sent to a particular address. * `FromContainsFilter` -- filters for packets that are sent to a particular address. diff --git a/resources/releasedocs/changelog.html b/resources/releasedocs/changelog.html index 93b258e2d..eb5830715 100644 --- a/resources/releasedocs/changelog.html +++ b/resources/releasedocs/changelog.html @@ -141,6 +141,17 @@ hr {
+

4.0.7 -- 2015-02-20

+ +

Bug +

+ +

4.0.6 -- 2014-11-23

Bug diff --git a/smack-android/build.gradle b/smack-android/build.gradle index 84326fb2e..48e9a4785 100644 --- a/smack-android/build.gradle +++ b/smack-android/build.gradle @@ -7,9 +7,10 @@ smack-extensions and smack-experimental.""" // Note that the test dependencies (junit, …) are inferred from the // sourceSet.test of the core subproject dependencies { + // androidProjects lists all projects that are checked to compile against android.jar // Filter out the optional Smack dependencies from androidProjects androidProjects.findAll { - ![':smack-tcp', ':smack-extensions', ':smack-experimental'].contains(it.getPath()) + ![':smack-tcp', ':smack-extensions', ':smack-experimental', ':smack-bosh'].contains(it.getPath()) }.each { project -> compile project } diff --git a/smack-bosh/build.gradle b/smack-bosh/build.gradle index 8955ed6e4..4ec1ce03a 100644 --- a/smack-bosh/build.gradle +++ b/smack-bosh/build.gradle @@ -4,5 +4,5 @@ This API is considered beta quality.""" dependencies { compile project(':smack-core') - compile 'org.igniterealtime.jbosh:jbosh:(0.8,0.9]' + compile 'org.igniterealtime.jbosh:jbosh:[0.8,0.9)' } diff --git a/smack-bosh/src/main/java/org/jivesoftware/smack/bosh/XMPPBOSHConnection.java b/smack-bosh/src/main/java/org/jivesoftware/smack/bosh/XMPPBOSHConnection.java index 39ec4b976..5b8807c9d 100644 --- a/smack-bosh/src/main/java/org/jivesoftware/smack/bosh/XMPPBOSHConnection.java +++ b/smack-bosh/src/main/java/org/jivesoftware/smack/bosh/XMPPBOSHConnection.java @@ -39,7 +39,6 @@ import org.jivesoftware.smack.packet.Message; import org.jivesoftware.smack.packet.Stanza; import org.jivesoftware.smack.packet.PlainStreamElement; import org.jivesoftware.smack.packet.Presence; -import org.jivesoftware.smack.packet.Presence.Type; import org.jivesoftware.smack.sasl.packet.SaslStreamElements.SASLFailure; import org.jivesoftware.smack.sasl.packet.SaslStreamElements.Success; import org.jivesoftware.smack.util.PacketParserUtils; @@ -289,19 +288,6 @@ public class XMPPBOSHConnection extends AbstractXMPPConnection { connected = false; isFirstInitialization = false; - Presence unavailablePresence = new Presence(Type.unavailable); - try { - client.disconnect(ComposableBody.builder() - .setNamespaceDefinition("xmpp", XMPP_BOSH_NS) - .setPayloadXML(unavailablePresence.toXML().toString()) - .build()); - // Wait 150 ms for processes to clean-up, then shutdown. - Thread.sleep(150); - } - catch (Exception e) { - // Ignore. - } - // Close down the readers and writers. if (readerPipe != null) { try { diff --git a/smack-compression-jzlib/build.gradle b/smack-compression-jzlib/build.gradle index f90968ddf..9f449f88e 100644 --- a/smack-compression-jzlib/build.gradle +++ b/smack-compression-jzlib/build.gradle @@ -4,5 +4,5 @@ Allow to compress the XMPP stream with help of jzlib.""" dependencies { compile project(path: ':smack-core') - compile 'com.jcraft:jzlib:(1.1,1.2]' + compile 'com.jcraft:jzlib:[1.1,1.2)' } 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 7ab1e558f..68aec5dfd 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/AbstractXMPPConnection.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/AbstractXMPPConnection.java @@ -57,7 +57,7 @@ import org.jivesoftware.smack.compression.XMPPInputOutputStream; import org.jivesoftware.smack.debugger.SmackDebugger; import org.jivesoftware.smack.filter.IQReplyFilter; import org.jivesoftware.smack.filter.PacketFilter; -import org.jivesoftware.smack.filter.PacketIDFilter; +import org.jivesoftware.smack.filter.StanzaIdFilter; import org.jivesoftware.smack.iqrequest.IQRequestHandler; import org.jivesoftware.smack.packet.Bind; import org.jivesoftware.smack.packet.ErrorIQ; @@ -547,7 +547,7 @@ public abstract class AbstractXMPPConnection implements XMPPConnection { // Note that we can not use IQReplyFilter here, since the users full JID is not yet // available. It will become available right after the resource has been successfully bound. Bind bindResource = Bind.newSet(resource); - PacketCollector packetCollector = createPacketCollectorAndSend(new PacketIDFilter(bindResource), bindResource); + PacketCollector packetCollector = createPacketCollectorAndSend(new StanzaIdFilter(bindResource), bindResource); Bind response = packetCollector.nextResultOrThrow(); // Set the connections user to the result of resource binding. It is important that we don't infer the user // from the login() arguments and the configurations service name, as, for example, when SASL External is used, @@ -560,7 +560,7 @@ public abstract class AbstractXMPPConnection implements XMPPConnection { // For more information see http://tools.ietf.org/html/draft-cridland-xmpp-session-01 if (sessionFeature != null && !sessionFeature.isOptional() && !getConfiguration().isLegacySessionDisabled()) { Session session = new Session(); - packetCollector = createPacketCollectorAndSend(new PacketIDFilter(session), session); + packetCollector = createPacketCollectorAndSend(new StanzaIdFilter(session), session); packetCollector.nextResultOrThrow(); } } @@ -1445,7 +1445,7 @@ public abstract class AbstractXMPPConnection implements XMPPConnection { } @Override - public void sendStanzaWithResponseCallback(Stanza stanza, PacketFilter replyFilter, + public void sendStanzaWithResponseCallback(Stanza stanza, final PacketFilter replyFilter, final PacketListener callback, final ExceptionCallback exceptionCallback, long timeout) throws NotConnectedException, InterruptedException { Objects.requireNonNull(stanza, "stanza must not be null"); @@ -1478,7 +1478,7 @@ public abstract class AbstractXMPPConnection implements XMPPConnection { // If the packetListener got removed, then it was never run and // we never received a response, inform the exception callback if (removed && exceptionCallback != null) { - exceptionCallback.processException(new NoResponseException(AbstractXMPPConnection.this)); + exceptionCallback.processException(NoResponseException.newWith(AbstractXMPPConnection.this, replyFilter)); } } }, timeout, TimeUnit.MILLISECONDS); diff --git a/smack-core/src/main/java/org/jivesoftware/smack/PacketCollector.java b/smack-core/src/main/java/org/jivesoftware/smack/PacketCollector.java index 9143782b0..4b5c4c63b 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/PacketCollector.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/PacketCollector.java @@ -205,7 +205,7 @@ public class PacketCollector { P result = nextResult(timeout); cancel(); if (result == null) { - throw new NoResponseException(connection); + throw NoResponseException.newWith(connection, this); } XMPPErrorException.ifHasErrorThenThrow(result); diff --git a/smack-core/src/main/java/org/jivesoftware/smack/SASLAuthentication.java b/smack-core/src/main/java/org/jivesoftware/smack/SASLAuthentication.java index a632b9f96..4da349e8e 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/SASLAuthentication.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/SASLAuthentication.java @@ -196,7 +196,7 @@ public class SASLAuthentication { maybeThrowException(); if (!authenticationSuccessful) { - throw new NoResponseException(connection); + throw NoResponseException.newWith(connection); } } else { @@ -239,7 +239,7 @@ public class SASLAuthentication { maybeThrowException(); if (!authenticationSuccessful) { - throw new NoResponseException(connection); + throw NoResponseException.newWith(connection); } } else { @@ -274,7 +274,7 @@ public class SASLAuthentication { maybeThrowException(); if (!authenticationSuccessful) { - throw new NoResponseException(connection); + throw NoResponseException.newWith(connection); } } 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 d0c747800..508c0595a 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/SmackException.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/SmackException.java @@ -19,6 +19,7 @@ package org.jivesoftware.smack; import java.util.ArrayList; import java.util.List; +import org.jivesoftware.smack.filter.PacketFilter; import org.jivesoftware.smack.util.dns.HostAddress; import org.jxmpp.jid.Jid; @@ -65,10 +66,47 @@ public class SmackException extends Exception { */ private static final long serialVersionUID = -6523363748984543636L; - public NoResponseException(XMPPConnection connection) { - super("No response received within packet reply timeout. Timeout was " + connection.getPacketReplyTimeout() - + "ms (~" + connection.getPacketReplyTimeout() / 1000 + "s)"); + private final PacketFilter filter; + + private NoResponseException(String message, PacketFilter filter) { + super(message); + this.filter = filter; } + + /** + * Get the filter that was used to collect the response. + * + * @return the used filter or null. + */ + public PacketFilter getFilter() { + return filter; + } + + public static NoResponseException newWith(XMPPConnection connection) { + return newWith(connection, (PacketFilter) null); + } + + public static NoResponseException newWith(XMPPConnection connection, + PacketCollector collector) { + return newWith(connection, collector.getPacketFilter()); + } + + public static NoResponseException newWith(XMPPConnection connection, PacketFilter filter) { + final long replyTimeout = connection.getPacketReplyTimeout(); + final StringBuilder sb = new StringBuilder(256); + sb.append("No response received within reply timeout. Timeout was " + + replyTimeout + "ms (~" + + replyTimeout / 1000 + "s). Used filter: "); + if (filter != null) { + sb.append(filter.toString()); + } + else { + sb.append("No filter used or filter was 'null'"); + } + sb.append('.'); + return new NoResponseException(sb.toString(), filter); + } + } public static class NotLoggedInException extends SmackException { 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 4b6bd909e..e5e985201 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/SynchronizationPoint.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/SynchronizationPoint.java @@ -190,7 +190,7 @@ public class SynchronizationPoint { case Initial: case NoResponse: case RequestSent: - throw new NoResponseException(connection); + throw NoResponseException.newWith(connection); default: // Do nothing break; diff --git a/smack-core/src/main/java/org/jivesoftware/smack/XMPPException.java b/smack-core/src/main/java/org/jivesoftware/smack/XMPPException.java index 2656c6890..774ca76ad 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/XMPPException.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/XMPPException.java @@ -129,11 +129,6 @@ public abstract class XMPPException extends Exception { } } - @Override - public String toString() { - return getMessage(); - } - public static void ifHasErrorThenThrow(Stanza packet) throws XMPPErrorException { XMPPError xmppError = packet.getError(); if (xmppError != null) { @@ -157,7 +152,7 @@ public abstract class XMPPException extends Exception { * @param streamError the root cause of the exception. */ public StreamErrorException(StreamError streamError) { - super(); + super(streamError.toString()); this.streamError = streamError; } @@ -171,14 +166,5 @@ public abstract class XMPPException extends Exception { return streamError; } - @Override - public String getMessage() { - return streamError.toString(); - } - - @Override - public String toString() { - return getMessage(); - } } } diff --git a/smack-core/src/main/java/org/jivesoftware/smack/filter/AbstractListFilter.java b/smack-core/src/main/java/org/jivesoftware/smack/filter/AbstractListFilter.java new file mode 100644 index 000000000..d32fb6eef --- /dev/null +++ b/smack-core/src/main/java/org/jivesoftware/smack/filter/AbstractListFilter.java @@ -0,0 +1,78 @@ +/** + * + * Copyright 2015 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.filter; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.jivesoftware.smack.util.Objects; + +/** + * + */ +public abstract class AbstractListFilter implements PacketFilter { + + /** + * The list of filters. + */ + protected final List filters; + + /** + * Creates an empty filter. + */ + protected AbstractListFilter() { + filters = new ArrayList(); + } + + /** + * Creates an filter using the specified filters. + * + * @param filters the filters to add. + */ + protected AbstractListFilter(PacketFilter... filters) { + Objects.requireNonNull(filters, "Parameter must not be null."); + for(PacketFilter filter : filters) { + Objects.requireNonNull(filter, "Parameter must not be null."); + } + this.filters = new ArrayList(Arrays.asList(filters)); + } + + /** + * Adds a filter to the filter list. A stanza will pass the filter if all of the filters in the + * list accept it. + * + * @param filter a filter to add to the filter list. + */ + public void addFilter(PacketFilter filter) { + Objects.requireNonNull(filter, "Parameter must not be null."); + filters.add(filter); + } + + @Override + public final String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(getClass().getSimpleName()); + sb.append(" ("); + for (PacketFilter filter : filters) { + sb.append(' ' + filter.toString() + ','); + } + sb.append(")"); + return sb.toString(); + } +} diff --git a/smack-core/src/main/java/org/jivesoftware/smack/filter/AndFilter.java b/smack-core/src/main/java/org/jivesoftware/smack/filter/AndFilter.java index 0f7ccb4e0..7006852fe 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/filter/AndFilter.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/filter/AndFilter.java @@ -17,12 +17,7 @@ package org.jivesoftware.smack.filter; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - import org.jivesoftware.smack.packet.Stanza; -import org.jivesoftware.smack.util.Objects; /** * Implements the logical AND operation over two or more packet filters. @@ -30,19 +25,14 @@ import org.jivesoftware.smack.util.Objects; * * @author Matt Tucker */ -public class AndFilter implements PacketFilter { - - /** - * The list of filters. - */ - private final List filters; +public class AndFilter extends AbstractListFilter implements PacketFilter { /** * Creates an empty AND filter. Filters should be added using the * {@link #addFilter(PacketFilter)} method. */ public AndFilter() { - filters = new ArrayList(); + super(); } /** @@ -51,22 +41,7 @@ public class AndFilter implements PacketFilter { * @param filters the filters to add. */ public AndFilter(PacketFilter... filters) { - Objects.requireNonNull(filters, "Parameter must not be null."); - for(PacketFilter filter : filters) { - Objects.requireNonNull(filter, "Parameter must not be null."); - } - this.filters = new ArrayList(Arrays.asList(filters)); - } - - /** - * Adds a filter to the filter list for the AND operation. A packet - * will pass the filter if all of the filters in the list accept it. - * - * @param filter a filter to add to the filter list. - */ - public void addFilter(PacketFilter filter) { - Objects.requireNonNull(filter, "Parameter must not be null."); - filters.add(filter); + super(filters); } public boolean accept(Stanza packet) { @@ -78,7 +53,4 @@ public class AndFilter implements PacketFilter { return true; } - public String toString() { - return filters.toString(); - } } diff --git a/smack-core/src/main/java/org/jivesoftware/smack/filter/FlexiblePacketTypeFilter.java b/smack-core/src/main/java/org/jivesoftware/smack/filter/FlexiblePacketTypeFilter.java index 54e49c2fe..454d8caa3 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/filter/FlexiblePacketTypeFilter.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/filter/FlexiblePacketTypeFilter.java @@ -20,6 +20,7 @@ package org.jivesoftware.smack.filter; import java.lang.reflect.ParameterizedType; import org.jivesoftware.smack.packet.Stanza; +import org.jivesoftware.smack.util.Objects; /** * Filters for packets of a particular type and allows a custom method to further filter the packets. @@ -31,7 +32,7 @@ public abstract class FlexiblePacketTypeFilter

implements Pack protected final Class

packetType; public FlexiblePacketTypeFilter(Class

packetType) { - this.packetType = packetType; + this.packetType = Objects.requireNonNull(packetType, "Type must not be null"); } @SuppressWarnings("unchecked") @@ -49,4 +50,12 @@ public abstract class FlexiblePacketTypeFilter

implements Pack } protected abstract boolean acceptSpecific(P packet); + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(getClass().getSimpleName()); + sb.append(" (" + packetType.toString() + ')'); + return sb.toString(); + } } diff --git a/smack-core/src/main/java/org/jivesoftware/smack/filter/FromMatchesFilter.java b/smack-core/src/main/java/org/jivesoftware/smack/filter/FromMatchesFilter.java index 78af6cdca..654fa051f 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/filter/FromMatchesFilter.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/filter/FromMatchesFilter.java @@ -106,6 +106,6 @@ public class FromMatchesFilter implements PacketFilter { public String toString() { String matchMode = ignoreResourcepart ? "ignoreResourcepart" : "full"; - return "FromMatchesFilter (" +matchMode + "): " + address; + return getClass().getSimpleName() + " (" + matchMode + "): " + address; } } diff --git a/smack-core/src/main/java/org/jivesoftware/smack/filter/IQReplyFilter.java b/smack-core/src/main/java/org/jivesoftware/smack/filter/IQReplyFilter.java index 499029563..b8f491863 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/filter/IQReplyFilter.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/filter/IQReplyFilter.java @@ -95,7 +95,7 @@ public class IQReplyFilter implements PacketFilter { packetId = iqPacket.getStanzaId(); PacketFilter iqFilter = new OrFilter(IQTypeFilter.ERROR, IQTypeFilter.RESULT); - PacketFilter idFilter = new PacketIDFilter(iqPacket); + PacketFilter idFilter = new StanzaIdFilter(iqPacket); iqAndIdFilter = new AndFilter(iqFilter, idFilter); fromFilter = new OrFilter(); fromFilter.addFilter(FromMatchesFilter.createFull(to)); @@ -126,4 +126,12 @@ public class IQReplyFilter implements PacketFilter { } } + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(getClass().getSimpleName()); + sb.append(": iqAndIdFilter (").append(iqAndIdFilter.toString()).append("), "); + sb.append(": fromFilter (").append(fromFilter.toString()).append(')'); + return sb.toString(); + } } diff --git a/smack-core/src/main/java/org/jivesoftware/smack/filter/IQResultReplyFilter.java b/smack-core/src/main/java/org/jivesoftware/smack/filter/IQResultReplyFilter.java index c77539f56..a8225dcf4 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/filter/IQResultReplyFilter.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/filter/IQResultReplyFilter.java @@ -38,4 +38,11 @@ public class IQResultReplyFilter extends IQReplyFilter { return IQTypeFilter.RESULT.accept(packet); } + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(getClass().getSimpleName()); + sb.append(" (" + super.toString() + ')'); + return sb.toString(); + } } diff --git a/smack-core/src/main/java/org/jivesoftware/smack/filter/IQTypeFilter.java b/smack-core/src/main/java/org/jivesoftware/smack/filter/IQTypeFilter.java index f0cccb245..dc2c38b10 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/filter/IQTypeFilter.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/filter/IQTypeFilter.java @@ -18,6 +18,7 @@ package org.jivesoftware.smack.filter; import org.jivesoftware.smack.packet.IQ; import org.jivesoftware.smack.packet.IQ.Type; +import org.jivesoftware.smack.util.Objects; /** * A filter for IQ packet types. Returns true only if the packet is an IQ packet @@ -38,11 +39,16 @@ public class IQTypeFilter extends FlexiblePacketTypeFilter { private IQTypeFilter(IQ.Type type) { super(IQ.class); - this.type = type; + this.type = Objects.requireNonNull(type, "Type must not be null"); } @Override protected boolean acceptSpecific(IQ iq) { return iq.getType() == type; } + + @Override + public String toString() { + return getClass().getSimpleName() + ": type=" + type; + } } diff --git a/smack-core/src/main/java/org/jivesoftware/smack/filter/MessageTypeFilter.java b/smack-core/src/main/java/org/jivesoftware/smack/filter/MessageTypeFilter.java index c7eaa5275..74df7bfed 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/filter/MessageTypeFilter.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/filter/MessageTypeFilter.java @@ -55,4 +55,8 @@ public class MessageTypeFilter extends FlexiblePacketTypeFilter { return message.getType() == type; } + @Override + public String toString() { + return getClass().getSimpleName() + ": type=" + type; + } } diff --git a/smack-core/src/main/java/org/jivesoftware/smack/filter/MessageWithBodiesFilter.java b/smack-core/src/main/java/org/jivesoftware/smack/filter/MessageWithBodiesFilter.java index a8f796622..201e5546d 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/filter/MessageWithBodiesFilter.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/filter/MessageWithBodiesFilter.java @@ -36,4 +36,8 @@ public class MessageWithBodiesFilter extends FlexiblePacketTypeFilter { return !message.getBodies().isEmpty(); } + @Override + public String toString() { + return getClass().getSimpleName(); + } } diff --git a/smack-core/src/main/java/org/jivesoftware/smack/filter/MessageWithSubjectFilter.java b/smack-core/src/main/java/org/jivesoftware/smack/filter/MessageWithSubjectFilter.java index fa29ec497..c484aa2e8 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/filter/MessageWithSubjectFilter.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/filter/MessageWithSubjectFilter.java @@ -36,4 +36,8 @@ public class MessageWithSubjectFilter extends FlexiblePacketTypeFilter return message.getSubject() != null; } + @Override + public String toString() { + return getClass().getSimpleName(); + } } diff --git a/smack-core/src/main/java/org/jivesoftware/smack/filter/NotFilter.java b/smack-core/src/main/java/org/jivesoftware/smack/filter/NotFilter.java index 3dd76a7f0..76d63ce9c 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/filter/NotFilter.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/filter/NotFilter.java @@ -18,6 +18,7 @@ package org.jivesoftware.smack.filter; import org.jivesoftware.smack.packet.Stanza; +import org.jivesoftware.smack.util.Objects; /** * Implements the logical NOT operation on a packet filter. In other words, packets @@ -35,10 +36,7 @@ public class NotFilter implements PacketFilter { * @param filter the filter. */ public NotFilter(PacketFilter filter) { - if (filter == null) { - throw new IllegalArgumentException("Parameter must not be null."); - } - this.filter = filter; + this.filter = Objects.requireNonNull(filter, "Parameter must not be null."); } public boolean accept(Stanza packet) { diff --git a/smack-core/src/main/java/org/jivesoftware/smack/filter/OrFilter.java b/smack-core/src/main/java/org/jivesoftware/smack/filter/OrFilter.java index db583c076..b1d96eeef 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/filter/OrFilter.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/filter/OrFilter.java @@ -17,12 +17,7 @@ package org.jivesoftware.smack.filter; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - import org.jivesoftware.smack.packet.Stanza; -import org.jivesoftware.smack.util.Objects; /** * Implements the logical OR operation over two or more packet filters. In @@ -30,19 +25,14 @@ import org.jivesoftware.smack.util.Objects; * * @author Matt Tucker */ -public class OrFilter implements PacketFilter { - - /** - * The list of filters. - */ - private final List filters; +public class OrFilter extends AbstractListFilter implements PacketFilter { /** * Creates an empty OR filter. Filters should be added using the * {@link #addFilter(PacketFilter)} method. */ public OrFilter() { - filters = new ArrayList(); + super(); } /** @@ -51,24 +41,10 @@ public class OrFilter implements PacketFilter { * @param filters the filters to add. */ public OrFilter(PacketFilter... filters) { - Objects.requireNonNull(filters, "Parameter must not be null."); - for(PacketFilter filter : filters) { - Objects.requireNonNull(filter, "Parameter must not be null."); - } - this.filters = new ArrayList(Arrays.asList(filters)); - } - - /** - * Adds a filter to the filter list for the OR operation. A packet - * will pass the filter if any filter in the list accepts it. - * - * @param filter a filter to add to the filter list. - */ - public void addFilter(PacketFilter filter) { - Objects.requireNonNull(filter, "Parameter must not be null."); - filters.add(filter); + super(filters); } + @Override public boolean accept(Stanza packet) { for (PacketFilter filter : filters) { if (filter.accept(packet)) { @@ -78,7 +54,4 @@ public class OrFilter implements PacketFilter { return false; } - public String toString() { - return filters.toString(); - } } diff --git a/smack-core/src/main/java/org/jivesoftware/smack/filter/PacketExtensionFilter.java b/smack-core/src/main/java/org/jivesoftware/smack/filter/PacketExtensionFilter.java index 0ba17386f..c0deb6183 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/filter/PacketExtensionFilter.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/filter/PacketExtensionFilter.java @@ -68,4 +68,9 @@ public class PacketExtensionFilter implements PacketFilter { public boolean accept(Stanza packet) { return packet.hasExtension(elementName, namespace); } + + @Override + public String toString() { + return getClass().getSimpleName() + ": element=" + elementName + " namespace=" + namespace; + } } diff --git a/smack-core/src/main/java/org/jivesoftware/smack/filter/PacketFilter.java b/smack-core/src/main/java/org/jivesoftware/smack/filter/PacketFilter.java index c71cbe3fe..5aecfa2a2 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/filter/PacketFilter.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/filter/PacketFilter.java @@ -28,7 +28,7 @@ import org.jivesoftware.smack.packet.Stanza; * packet filtering by using the {@link org.jivesoftware.smack.filter.AndFilter AndFilter} and * {@link org.jivesoftware.smack.filter.OrFilter OrFilter} filters. It's also possible to define * your own filters by implementing this interface. The code example below creates a trivial filter - * for packets with a specific ID (real code should use {@link PacketIDFilter} instead). + * for packets with a specific ID (real code should use {@link StanzaIdFilter} instead). * *

  * // Use an anonymous inner class to define a packet filter that returns
diff --git a/smack-core/src/main/java/org/jivesoftware/smack/filter/PacketIDFilter.java b/smack-core/src/main/java/org/jivesoftware/smack/filter/PacketIDFilter.java
index 82a2ea904..11796a0ab 100644
--- a/smack-core/src/main/java/org/jivesoftware/smack/filter/PacketIDFilter.java
+++ b/smack-core/src/main/java/org/jivesoftware/smack/filter/PacketIDFilter.java
@@ -24,7 +24,9 @@ import org.jivesoftware.smack.util.StringUtils;
  * Filters for packets with a particular packet ID.
  *
  * @author Matt Tucker
+ * @deprecated use {@link StanzaIdFilter} instead.
  */
+@Deprecated
 public class PacketIDFilter implements PacketFilter {
 
     private final String packetID;
@@ -33,7 +35,9 @@ public class PacketIDFilter implements PacketFilter {
      * Creates a new packet ID filter using the specified packet's ID.
      *
      * @param packet the packet which the ID is taken from.
+     * @deprecated use {@link StanzaIdfilter(Stanza)} instead.
      */
+    @Deprecated
     public PacketIDFilter(Stanza packet) {
         this(packet.getStanzaId());
     }
@@ -42,7 +46,9 @@ public class PacketIDFilter implements PacketFilter {
      * Creates a new packet ID filter using the specified packet ID.
      *
      * @param packetID the packet ID to filter for.
+     * @deprecated use {@link StanzaIdFilter(String)} instead.
      */
+    @Deprecated
     public PacketIDFilter(String packetID) {
         StringUtils.requireNotNullOrEmpty(packetID, "Packet ID must not be null or empty.");
         this.packetID = packetID;
@@ -53,6 +59,6 @@ public class PacketIDFilter implements PacketFilter {
     }
 
     public String toString() {
-        return "PacketIDFilter by id: " + packetID;
+        return getClass().getSimpleName() + ": id=" + packetID;
     }
 }
diff --git a/smack-core/src/main/java/org/jivesoftware/smack/filter/PacketTypeFilter.java b/smack-core/src/main/java/org/jivesoftware/smack/filter/PacketTypeFilter.java
index 6c57a4071..5a32e784c 100644
--- a/smack-core/src/main/java/org/jivesoftware/smack/filter/PacketTypeFilter.java
+++ b/smack-core/src/main/java/org/jivesoftware/smack/filter/PacketTypeFilter.java
@@ -53,7 +53,8 @@ public class PacketTypeFilter implements PacketFilter {
         return packetType.isInstance(packet);
     }
 
+    @Override
     public String toString() {
-        return "PacketTypeFilter: " + packetType.getName();
+        return getClass().getSimpleName() + ": " + packetType.getName();
     }
 }
diff --git a/smack-core/src/main/java/org/jivesoftware/smack/filter/PresenceTypeFilter.java b/smack-core/src/main/java/org/jivesoftware/smack/filter/PresenceTypeFilter.java
index 045cd2bf8..d67635609 100644
--- a/smack-core/src/main/java/org/jivesoftware/smack/filter/PresenceTypeFilter.java
+++ b/smack-core/src/main/java/org/jivesoftware/smack/filter/PresenceTypeFilter.java
@@ -18,6 +18,7 @@ package org.jivesoftware.smack.filter;
 
 import org.jivesoftware.smack.packet.Presence;
 import org.jivesoftware.smack.packet.Presence.Type;
+import org.jivesoftware.smack.util.Objects;
 
 /**
  * A filter for Presence types. Returns true only if the stanza is an Presence packet and it matches the type provided in the
@@ -38,11 +39,16 @@ public class PresenceTypeFilter extends FlexiblePacketTypeFilter {
 
     private PresenceTypeFilter(Presence.Type type) {
         super(Presence.class);
-        this.type = type;
+        this.type = Objects.requireNonNull(type, "type must not be null");
     }
 
     @Override
     protected boolean acceptSpecific(Presence presence) {
         return presence.getType() == type;
     }
+
+    @Override
+    public String toString() {
+        return getClass().getSimpleName() + ": type=" + type;
+    }
 }
diff --git a/smack-core/src/main/java/org/jivesoftware/smack/filter/StanzaIdFilter.java b/smack-core/src/main/java/org/jivesoftware/smack/filter/StanzaIdFilter.java
new file mode 100644
index 000000000..bf123726b
--- /dev/null
+++ b/smack-core/src/main/java/org/jivesoftware/smack/filter/StanzaIdFilter.java
@@ -0,0 +1,57 @@
+/**
+ *
+ * Copyright 2003-2007 Jive Software.
+ *
+ * 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.filter;
+
+import org.jivesoftware.smack.packet.Stanza;
+import org.jivesoftware.smack.util.StringUtils;
+
+/**
+ * Filters for Stanzas with a particular stanza ID.
+ *
+ * @author Matt Tucker
+ */
+public class StanzaIdFilter implements PacketFilter {
+
+    private final String stanzaId;
+
+    /**
+     * Creates a new stanza ID filter using the specified stanza's ID.
+     *
+     * @param stanza the stanza which the ID is taken from.
+     */
+    public StanzaIdFilter(Stanza stanza) {
+        this(stanza.getStanzaId());
+    }
+
+    /**
+     * Creates a new stanza ID filter using the specified stanza ID.
+     *
+     * @param stanzaID the stanza ID to filter for.
+     */
+    public StanzaIdFilter(String stanzaID) {
+        this.stanzaId = StringUtils.requireNotNullOrEmpty(stanzaID, "Stanza ID must not be null or empty.");
+    }
+
+    public boolean accept(Stanza stanza) {
+        return stanzaId.equals(stanza.getStanzaId());
+    }
+
+    public String toString() {
+        return getClass().getSimpleName() + ": id=" + stanzaId;
+    }
+}
diff --git a/smack-core/src/main/java/org/jivesoftware/smack/filter/ThreadFilter.java b/smack-core/src/main/java/org/jivesoftware/smack/filter/ThreadFilter.java
index e053d364b..da1c3d7dd 100644
--- a/smack-core/src/main/java/org/jivesoftware/smack/filter/ThreadFilter.java
+++ b/smack-core/src/main/java/org/jivesoftware/smack/filter/ThreadFilter.java
@@ -17,7 +17,6 @@
 
 package org.jivesoftware.smack.filter;
 
-import org.jivesoftware.smack.packet.Stanza;
 import org.jivesoftware.smack.packet.Message;
 import org.jivesoftware.smack.util.StringUtils;
 
@@ -26,7 +25,7 @@ import org.jivesoftware.smack.util.StringUtils;
  *
  * @author Matt Tucker
  */
-public class ThreadFilter implements PacketFilter {
+public class ThreadFilter extends FlexiblePacketTypeFilter implements PacketFilter {
 
     private final String thread;
 
@@ -40,7 +39,13 @@ public class ThreadFilter implements PacketFilter {
         this.thread = thread;
     }
 
-    public boolean accept(Stanza packet) {
-        return packet instanceof Message && thread.equals(((Message) packet).getThread());
+    @Override
+    protected boolean acceptSpecific(Message message) {
+        return thread.equals(message.getThread());
+    }
+
+    @Override
+    public String toString() {
+        return getClass().getSimpleName() + ": thread=" + thread;
     }
 }
diff --git a/smack-core/src/main/java/org/jivesoftware/smack/filter/ToFilter.java b/smack-core/src/main/java/org/jivesoftware/smack/filter/ToFilter.java
index e0fd2f900..cdbfccaf4 100644
--- a/smack-core/src/main/java/org/jivesoftware/smack/filter/ToFilter.java
+++ b/smack-core/src/main/java/org/jivesoftware/smack/filter/ToFilter.java
@@ -36,4 +36,8 @@ public class ToFilter implements PacketFilter {
         return packetTo.equals(to);
     }
 
+    @Override
+    public String toString() {
+        return getClass().getSimpleName() + ": to=" + to;
+    }
 }
diff --git a/smack-debug-slf4j/build.gradle b/smack-debug-slf4j/build.gradle
index 4e5a2a65f..43f430dbb 100644
--- a/smack-debug-slf4j/build.gradle
+++ b/smack-debug-slf4j/build.gradle
@@ -5,6 +5,6 @@ Connect your favourite slf4j backend of choice to get output inside of it"""
 
 dependencies {
     compile project(':smack-core')
-    compile 'org.slf4j:slf4j-api:(1.7,1.8]'
+    compile 'org.slf4j:slf4j-api:[1.7,1.8)'
     testCompile project(':smack-core').sourceSets.test.runtimeClasspath
 }
diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/filetransfer/FaultTolerantNegotiator.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/filetransfer/FaultTolerantNegotiator.java
index adae5be86..031092dd9 100644
--- a/smack-extensions/src/main/java/org/jivesoftware/smackx/filetransfer/FaultTolerantNegotiator.java
+++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/filetransfer/FaultTolerantNegotiator.java
@@ -176,11 +176,8 @@ public class FaultTolerantNegotiator extends StreamNegotiator {
             this.collector = collector;
         }
 
-        public InputStream call() throws XMPPErrorException, InterruptedException, SmackException {
-            Stanza streamInitiation = collector.nextResult();
-            if (streamInitiation == null) {
-                throw new NoResponseException(connection);
-            }
+        public InputStream call() throws XMPPErrorException, InterruptedException, NoResponseException, SmackException {
+            Stanza streamInitiation = collector.nextResultOrThrow();
             StreamNegotiator negotiator = determineNegotiator(streamInitiation);
             return negotiator.negotiateIncomingStream(streamInitiation);
         }
diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/filetransfer/Socks5TransferNegotiator.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/filetransfer/Socks5TransferNegotiator.java
index 1ebf2d614..d574060cd 100644
--- a/smack-extensions/src/main/java/org/jivesoftware/smackx/filetransfer/Socks5TransferNegotiator.java
+++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/filetransfer/Socks5TransferNegotiator.java
@@ -32,6 +32,7 @@ import org.jivesoftware.smack.filter.PacketFilter;
 import org.jivesoftware.smack.filter.PacketTypeFilter;
 import org.jivesoftware.smack.packet.IQ;
 import org.jivesoftware.smack.packet.Stanza;
+import org.jivesoftware.smack.util.Objects;
 import org.jivesoftware.smackx.bytestreams.socks5.Socks5BytestreamManager;
 import org.jivesoftware.smackx.bytestreams.socks5.Socks5BytestreamRequest;
 import org.jivesoftware.smackx.bytestreams.socks5.Socks5BytestreamSession;
@@ -128,14 +129,11 @@ public class Socks5TransferNegotiator extends StreamNegotiator {
      */
     private static class BytestreamSIDFilter extends PacketTypeFilter {
 
-        private String sessionID;
+        private final String sessionID;
 
         public BytestreamSIDFilter(String sessionID) {
             super(Bytestream.class);
-            if (sessionID == null) {
-                throw new IllegalArgumentException("StreamID cannot be null");
-            }
-            this.sessionID = sessionID;
+            this.sessionID = Objects.requireNonNull(sessionID, "SessionID cannot be null");
         }
 
         @Override
diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/iqregister/AccountManager.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/iqregister/AccountManager.java
index d673f9753..c7424949d 100644
--- a/smack-extensions/src/main/java/org/jivesoftware/smackx/iqregister/AccountManager.java
+++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/iqregister/AccountManager.java
@@ -22,6 +22,7 @@ import java.util.HashMap;
 import java.util.Map;
 import java.util.Set;
 import java.util.WeakHashMap;
+import java.util.logging.Logger;
 
 import org.jivesoftware.smack.Manager;
 import org.jivesoftware.smack.PacketCollector;
@@ -31,7 +32,7 @@ import org.jivesoftware.smack.XMPPException;
 import org.jivesoftware.smack.SmackException.NoResponseException;
 import org.jivesoftware.smack.SmackException.NotConnectedException;
 import org.jivesoftware.smack.XMPPException.XMPPErrorException;
-import org.jivesoftware.smack.filter.PacketIDFilter;
+import org.jivesoftware.smack.filter.StanzaIdFilter;
 import org.jivesoftware.smack.packet.IQ;
 import org.jivesoftware.smackx.iqregister.packet.Registration;
 
@@ -41,6 +42,9 @@ import org.jivesoftware.smackx.iqregister.packet.Registration;
  * @author Matt Tucker
  */
 public class AccountManager extends Manager {
+
+    private static final Logger LOGGER = Logger.getLogger(AccountManager.class.getName());
+
     private static final Map INSTANCES = new WeakHashMap();
 
     /**
@@ -58,6 +62,35 @@ public class AccountManager extends Manager {
         return accountManager;
     }
 
+    private static boolean allowSensitiveOperationOverInsecureConnectionDefault = false;
+
+    /**
+     * The default value used by new account managers for allowSensitiveOperationOverInsecureConnection.
+     *
+     * @param allow
+     * @see #sensitiveOperationOverInsecureConnection(boolean)
+     * @since 4.1
+     */
+    public static void sensitiveOperationOverInsecureConnectionDefault(boolean allow) {
+        AccountManager.allowSensitiveOperationOverInsecureConnectionDefault = allow;
+    }
+
+    private boolean allowSensitiveOperationOverInsecureConnection = allowSensitiveOperationOverInsecureConnectionDefault;
+
+    /**
+     * Set to true to allow sensitive operation over insecure connection.
+     * 

+ * Set to true to allow sensitive operations like account creation or password changes over an insecure (e.g. + * unencrypted) connections. + *

+ * + * @param allow + * @since 4.1 + */ + public void sensitiveOperationOverInsecureConnection(boolean allow) { + this.allowSensitiveOperationOverInsecureConnection = allow; + } + private Registration info = null; /** @@ -231,6 +264,11 @@ public class AccountManager extends Manager { */ public void createAccount(String username, String password, Map attributes) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { + if (!connection().isSecureConnection() && !allowSensitiveOperationOverInsecureConnection) { + // TODO throw exception in newer Smack versions + LOGGER.warning("Creating account over insecure connection. " + + "This will throw an exception in future versions of Smack if AccountManager.sensitiveOperationOverInsecureConnection(true) is not set"); + } attributes.put("username", username); attributes.put("password", password); Registration reg = new Registration(attributes); @@ -251,6 +289,11 @@ public class AccountManager extends Manager { * @throws InterruptedException */ public void changePassword(String newPassword) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { + if (!connection().isSecureConnection() && !allowSensitiveOperationOverInsecureConnection) { + // TODO throw exception in newer Smack versions + LOGGER.warning("Changing password over insecure connection. " + + "This will throw an exception in future versions of Smack if AccountManager.sensitiveOperationOverInsecureConnection(true) is not set"); + } Map map = new HashMap(); map.put("username", connection().getUser().getLocalpart().toString()); map.put("password",newPassword); @@ -298,7 +341,7 @@ public class AccountManager extends Manager { } private PacketCollector createPacketCollectorAndSend(IQ req) throws NotConnectedException, InterruptedException { - PacketCollector collector = connection().createPacketCollectorAndSend(new PacketIDFilter(req.getStanzaId()), req); + PacketCollector collector = connection().createPacketCollectorAndSend(new StanzaIdFilter(req.getStanzaId()), req); return collector; } } diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/iqversion/packet/Version.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/iqversion/packet/Version.java index 6a72a919f..1debbb29a 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/iqversion/packet/Version.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/iqversion/packet/Version.java @@ -27,26 +27,6 @@ import org.jxmpp.jid.Jid; * A Version IQ packet, which is used by XMPP clients to discover version information * about the software running at another entity's JID.

* - * An example to discover the version of the server: - *

- * // Request the version from the server.
- * Version versionRequest = new Version();
- * timeRequest.setType(IQ.Type.get);
- * timeRequest.setTo("example.com");
- *
- * // Create a packet collector to listen for a response.
- * PacketCollector collector = con.createPacketCollector(
- *                new PacketIDFilter(versionRequest.getStanzaId()));
- *
- * con.sendPacket(versionRequest);
- *
- * // Wait up to 5 seconds for a result.
- * IQ result = (IQ)collector.nextResult(5000);
- * if (result != null && result.getType() == IQ.Type.result) {
- *     Version versionResult = (Version)result;
- *     // Do something with result...
- * }

- * * @author Gaston Dombiak */ public class Version extends IQ { diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/provider/ItemProvider.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/provider/ItemProvider.java index 42d961faa..17a170cb1 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/provider/ItemProvider.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/provider/ItemProvider.java @@ -57,14 +57,15 @@ public class ItemProvider extends PacketExtensionProvider String payloadElemName = parser.getName(); String payloadNS = parser.getNamespace(); - if (ProviderManager.getExtensionProvider(payloadElemName, payloadNS) == null) + final PacketExtensionProvider extensionProvider = ProviderManager.getExtensionProvider(payloadElemName, payloadNS); + if (extensionProvider == null) { CharSequence payloadText = PacketParserUtils.parseElement(parser, true); return new PayloadItem(id, node, new SimplePayload(payloadElemName, payloadNS, payloadText)); } else { - return new PayloadItem(id, node, PacketParserUtils.parsePacketExtension(payloadElemName, payloadNS, parser)); + return new PayloadItem(id, node, extensionProvider.parse(parser)); } } } diff --git a/smack-extensions/src/test/java/org/jivesoftware/util/Protocol.java b/smack-extensions/src/test/java/org/jivesoftware/util/Protocol.java index 71fdbc33a..75acff249 100644 --- a/smack-extensions/src/test/java/org/jivesoftware/util/Protocol.java +++ b/smack-extensions/src/test/java/org/jivesoftware/util/Protocol.java @@ -51,7 +51,7 @@ import org.jivesoftware.smack.packet.Stanza; * * public void methodToTest() { * Packet packet = new Packet(); // create an XMPP packet - * PacketCollector collector = connection.createPacketCollector(new PacketIDFilter()); + * PacketCollector collector = connection.createPacketCollector(new StanzaIdFilter()); * connection.sendPacket(packet); * Packet reply = collector.nextResult(); * } 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 13e767f05..64accea3f 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 @@ -142,6 +142,11 @@ public class Roster extends Manager { */ private final Map> presenceMap = new ConcurrentHashMap<>(); + /** + * Listeners called when the Roster was loaded. + */ + private final Set rosterLoadedListeners = new LinkedHashSet<>(); + /** * Mutually exclude roster listener invocation and changing the {@link entries} map. Also used * to synchronize access to either the roster listeners or the entries map. @@ -391,6 +396,34 @@ public class Roster extends Manager { } } + /** + * Add a roster loaded listener. + * + * @param rosterLoadedListener the listener to add. + * @return true if the listener was not already added. + * @see RosterLoadedListener + * @since 4.1 + */ + public boolean addRosterLoadedListener(RosterLoadedListener rosterLoadedListener) { + synchronized (rosterLoadedListener) { + return rosterLoadedListeners.add(rosterLoadedListener); + } + } + + /** + * Remove a roster loaded listener. + * + * @param rosterLoadedListener the listener to remove. + * @return true if the listener was active and got removed. + * @see RosterLoadedListener + * @since 4.1 + */ + public boolean removeRosterLoadedListener(RosterLoadedListener rosterLoadedListener) { + synchronized (rosterLoadedListener) { + return rosterLoadedListeners.remove(rosterLoadedListener); + } + } + /** * Creates a new group.

*

@@ -1331,6 +1364,22 @@ public class Roster extends Manager { } // Fire event for roster listeners. fireRosterChangedEvent(addedEntries, updatedEntries, deletedEntries); + + // Call the roster loaded listeners after the roster events have been fired. This is + // imporant because the user may call getEntriesAndAddListener() in onRosterLoaded(), + // and if the order would be the other way around, the roster listener added by + // getEntriesAndAddListener() would be invoked with information that was already + // available at the time getEntriesAndAddListenr() was called. + try { + synchronized (rosterLoadedListeners) { + for (RosterLoadedListener rosterLoadedListener : rosterLoadedListeners) { + rosterLoadedListener.onRosterLoaded(Roster.this); + } + } + } + catch (Exception e) { + LOGGER.log(Level.WARNING, "RosterLoadedListener threw exception", e); + } } } diff --git a/smack-im/src/main/java/org/jivesoftware/smack/roster/RosterLoadedListener.java b/smack-im/src/main/java/org/jivesoftware/smack/roster/RosterLoadedListener.java new file mode 100644 index 000000000..94f08f094 --- /dev/null +++ b/smack-im/src/main/java/org/jivesoftware/smack/roster/RosterLoadedListener.java @@ -0,0 +1,38 @@ +/** + * + * Copyright 2015 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.roster; + +/** + * Roster loaded listeners are invoked once the {@link Roster} was successfully loaded. + *

+ * A common approach is to call + * {@link Roster#getEntriesAndAddListener(RosterListener, RosterEntries)} within + * {@link #onRosterLoaded(Roster)}, to initialize or update your UI components with the current + * roster state. + *

+ */ +public interface RosterLoadedListener { + + /** + * Called when the Roster was loaded successfully. + * + * @param roster the Roster that was loaded successfully. + */ + public void onRosterLoaded(Roster roster); + +} diff --git a/smack-resolver-dnsjava/build.gradle b/smack-resolver-dnsjava/build.gradle index 2e46948bc..9de83e1f5 100644 --- a/smack-resolver-dnsjava/build.gradle +++ b/smack-resolver-dnsjava/build.gradle @@ -5,5 +5,5 @@ javax.naming API (e.g. Android).""" dependencies { compile project(path: ':smack-core') - compile 'dnsjava:dnsjava:(2.1,2.2]' + compile 'dnsjava:dnsjava:[2.1,2.2)' } diff --git a/smack-resolver-minidns/build.gradle b/smack-resolver-minidns/build.gradle index 49438ee95..812fe8c4b 100644 --- a/smack-resolver-minidns/build.gradle +++ b/smack-resolver-minidns/build.gradle @@ -5,6 +5,6 @@ javax.naming API (e.g. Android).""" dependencies { compile project(path: ':smack-core') - compile 'de.measite.minidns:minidns:(0.1,0.2]' + compile 'de.measite.minidns:minidns:[0.1,0.2)' compile "org.jxmpp:jxmpp-util-cache:$jxmppVersion" } 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 95278f3af..43684e299 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,7 +16,11 @@ */ package org.jivesoftware.smack.sm; +import java.util.Collections; +import java.util.List; + import org.jivesoftware.smack.SmackException; +import org.jivesoftware.smack.packet.Stanza; public abstract class StreamManagementException extends SmackException { @@ -52,5 +56,59 @@ public abstract class StreamManagementException extends SmackException { super("Stream IDs do not match. Expected '" + expected + "', but got '" + got + "'"); } } + + public static class StreamManagementCounterError extends StreamManagementException { + + /** + * + */ + private static final long serialVersionUID = 1L; + + private final long handledCount; + private final long previousServerHandledCount; + private final long ackedStanzaCount; + private final int outstandingStanzasCount; + private final List ackedStanzas; + + public StreamManagementCounterError(long handledCount, long previousServerHandlerCount, + long ackedStanzaCount, + List ackedStanzas) { + super( + "There was an error regarding the Stream Mangement counters. Server reported " + + handledCount + + " handled stanzas, which means that the " + + ackedStanzaCount + + " recently send stanzas by client are now acked by the server. But Smack had only " + + ackedStanzas.size() + + " to acknowledge. The stanza id of the last acked outstanding stanza is " + + (ackedStanzas.isEmpty() ? "" + : ackedStanzas.get(ackedStanzas.size() - 1).getStanzaId())); + this.handledCount = handledCount; + this.previousServerHandledCount = previousServerHandlerCount; + this.ackedStanzaCount = ackedStanzaCount; + this.outstandingStanzasCount = ackedStanzas.size(); + this.ackedStanzas = Collections.unmodifiableList(ackedStanzas); + } + + public long getHandledCount() { + return handledCount; + } + + public long getPreviousServerHandledCount() { + return previousServerHandledCount; + } + + public long getAckedStanzaCount() { + return ackedStanzaCount; + } + + public int getOutstandingStanzasCount() { + return outstandingStanzasCount; + } + + public List getAckedStanzas() { + return ackedStanzas; + } + } } diff --git a/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/BundleAndDefer.java b/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/BundleAndDefer.java new file mode 100644 index 000000000..ad899f9e5 --- /dev/null +++ b/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/BundleAndDefer.java @@ -0,0 +1,39 @@ +/** + * + * Copyright 2015 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.tcp; + +import java.util.concurrent.atomic.AtomicBoolean; + + +public class BundleAndDefer { + + private final AtomicBoolean isStopped; + + BundleAndDefer(AtomicBoolean isStopped) { + this.isStopped = isStopped; + } + + public void stopCurrentBundleAndDefer() { + synchronized (isStopped) { + if (isStopped.get()) { + return; + } + isStopped.set(true); + isStopped.notify(); + } + } +} diff --git a/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/BundleAndDeferCallback.java b/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/BundleAndDeferCallback.java new file mode 100644 index 000000000..5102b0019 --- /dev/null +++ b/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/BundleAndDeferCallback.java @@ -0,0 +1,49 @@ +/** + * + * Copyright 2015 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.tcp; + +/** + * This callback is used to get the current value of the period in which Smack does bundle and defer + * outgoing stanzas. + *

+ * Smack will bundle and defer stanzas if the connection is authenticated, the send queue is empty + * and if a bundle and defer callback is set, either via + * {@link XMPPTCPConnection#setDefaultBundleAndDeferCallback(BundleAndDeferCallback)} or + * {@link XMPPTCPConnection#setBundleandDeferCallback(BundleAndDeferCallback)}, and + * {@link #getBundleAndDeferMillis(BundleAndDefer)} returns a positive value. In a mobile environment, bundling + * and deferring outgoing stanzas may reduce battery consumption. It heavily depends on the + * environment, but recommend values for the bundle and defer period range from 20-60 seconds. But + * keep in mind that longer periods decrease the realtime aspect of Smack. + *

+ *

+ * Smack will invoke the callback when it needs to know the length of the bundle and defer period. + * If {@link #getBundleAndDeferMillis(BundleAndDefer)} returns 0 or a negative value, then the + * stanzas will send immediately. You can also prematurely abort the bundling of stanzas by calling + * {@link BundleAndDefer#stopCurrentBundleAndDefer()}. + *

+ */ +public interface BundleAndDeferCallback { + + /** + * Return the bundle and defer period used by Smack in milliseconds. + * + * @param bundleAndDefer used to premature abort bundle and defer. + * @return the bundle and defer period in milliseconds. + */ + public int getBundleAndDeferMillis(BundleAndDefer bundleAndDefer); + +} 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 7add86c4b..03e580dcd 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 @@ -54,6 +54,7 @@ import org.jivesoftware.smack.sasl.packet.SaslStreamElements.Success; import org.jivesoftware.smack.sm.SMUtils; import org.jivesoftware.smack.sm.StreamManagementException; import org.jivesoftware.smack.sm.StreamManagementException.StreamIdDoesNotMatchException; +import org.jivesoftware.smack.sm.StreamManagementException.StreamManagementCounterError; import org.jivesoftware.smack.sm.StreamManagementException.StreamManagementNotEnabledException; import org.jivesoftware.smack.sm.packet.StreamManagement; import org.jivesoftware.smack.sm.packet.StreamManagement.AckAnswer; @@ -126,6 +127,7 @@ import java.util.concurrent.BlockingQueue; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.logging.Level; import java.util.logging.Logger; @@ -185,6 +187,10 @@ public class XMPPTCPConnection extends AbstractXMPPConnection { private final SynchronizationPoint compressSyncPoint = new SynchronizationPoint( this); + private static BundleAndDeferCallback defaultBundleAndDeferCallback; + + private BundleAndDeferCallback bundleAndDeferCallback = defaultBundleAndDeferCallback; + private static boolean useSmDefault = false; private static boolean useSmResumptionDefault = true; @@ -719,7 +725,7 @@ public class XMPPTCPConnection extends AbstractXMPPConnection { Socket plain = socket; // Secure the plain connection socket = context.getSocketFactory().createSocket(plain, - plain.getInetAddress().getHostAddress(), plain.getPort(), true); + host, plain.getPort(), true); // Initialize the reader and writer with the new secured version initReaderAndWriter(); @@ -1272,6 +1278,30 @@ public class XMPPTCPConnection extends AbstractXMPPConnection { if (element == null) { continue; } + + // Get a local version of the bundle and defer callback, in case it's unset + // between the null check and the method invocation + final BundleAndDeferCallback localBundleAndDeferCallback = bundleAndDeferCallback; + // If the preconditions are given (e.g. bundleAndDefer callback is set, queue is + // empty), then we could wait a bit for further stanzas attempting to decrease + // our energy consumption + if (localBundleAndDeferCallback != null && isAuthenticated() && queue.isEmpty()) { + final AtomicBoolean bundlingAndDeferringStopped = new AtomicBoolean(); + final int bundleAndDeferMillis = localBundleAndDeferCallback.getBundleAndDeferMillis(new BundleAndDefer( + bundlingAndDeferringStopped)); + if (bundleAndDeferMillis > 0) { + long remainingWait = bundleAndDeferMillis; + final long waitStart = System.currentTimeMillis(); + synchronized (bundlingAndDeferringStopped) { + while (!bundlingAndDeferringStopped.get() && remainingWait > 0) { + bundlingAndDeferringStopped.wait(remainingWait); + remainingWait = bundleAndDeferMillis + - (System.currentTimeMillis() - waitStart); + } + } + } + } + Stanza packet = null; if (element instanceof Stanza) { packet = (Stanza) element; @@ -1288,6 +1318,8 @@ public class XMPPTCPConnection extends AbstractXMPPConnection { writer.flush(); } try { + // It is important the we put the stanza in the unacknowledged stanza + // queue before we put it on the wire unacknowledgedStanzas.put(packet); } catch (InterruptedException e) { @@ -1646,7 +1678,7 @@ public class XMPPTCPConnection extends AbstractXMPPConnection { return Math.min(clientResumptionTime, serverResumptionTime); } - private void processHandledCount(long handledCount) throws NotConnectedException { + private void processHandledCount(long handledCount) throws NotConnectedException, StreamManagementCounterError { long ackedStanzasCount = SMUtils.calculateDelta(handledCount, serverHandledStanzasCount); final List ackedStanzas = new ArrayList( handledCount <= Integer.MAX_VALUE ? (int) handledCount @@ -1655,7 +1687,10 @@ public class XMPPTCPConnection extends AbstractXMPPConnection { Stanza ackedStanza = unacknowledgedStanzas.poll(); // If the server ack'ed a stanza, then it must be in the // unacknowledged stanza queue. There can be no exception. - assert(ackedStanza != null); + if (ackedStanza == null) { + throw new StreamManagementCounterError(handledCount, serverHandledStanzasCount, + ackedStanzasCount, ackedStanzas); + } ackedStanzas.add(ackedStanza); } @@ -1691,7 +1726,7 @@ public class XMPPTCPConnection extends AbstractXMPPConnection { } String id = ackedStanza.getStanzaId(); if (StringUtils.isNullOrEmpty(id)) { - return; + continue; } PacketListener listener = stanzaIdAcknowledgedListeners.remove(id); if (listener != null) { @@ -1709,4 +1744,31 @@ public class XMPPTCPConnection extends AbstractXMPPConnection { serverHandledStanzasCount = handledCount; } + + /** + * Set the default bundle and defer callback used for new connections. + * + * @param defaultBundleAndDeferCallback + * @see BundleAndDeferCallback + * @since 4.1 + */ + public static void setDefaultBundleAndDeferCallback(BundleAndDeferCallback defaultBundleAndDeferCallback) { + XMPPTCPConnection.defaultBundleAndDeferCallback = defaultBundleAndDeferCallback; + } + + /** + * Set the bundle and defer callback used for this connection. + *

+ * You can use null as argument to reset the callback. Outgoing stanzas will then + * no longer get deferred. + *

+ * + * @param bundleAndDeferCallback the callback or null. + * @see BundleAndDeferCallback + * @since 4.1 + */ + public void setBundleandDeferCallback(BundleAndDeferCallback bundleAndDeferCallback) { + this.bundleAndDeferCallback = bundleAndDeferCallback; + } + }