mirror of
https://codeberg.org/Mercury-IM/Smack
synced 2024-11-25 07:42:06 +01:00
Merge Smack 4.1.0-rc2
Conflicts: smack-core/src/main/java/org/jivesoftware/smack/filter/FromMatchesFilter.java smack-extensions/src/main/java/org/jivesoftware/smackx/iqregister/AccountManager.java version.gradle
This commit is contained in:
commit
fbf0ba13ce
48 changed files with 650 additions and 166 deletions
|
@ -95,6 +95,7 @@ allprojects {
|
||||||
options.addStringOption('Xdoclint:none', '-quiet')
|
options.addStringOption('Xdoclint:none', '-quiet')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
gradle.taskGraph.whenReady { taskGraph ->
|
gradle.taskGraph.whenReady { taskGraph ->
|
||||||
|
|
|
@ -46,7 +46,7 @@ own filters by coding to the `PacketFilter` interface. The default set of
|
||||||
filters includes:
|
filters includes:
|
||||||
|
|
||||||
* `PacketTypeFilter` -- filters for packets that are a particular Class type.
|
* `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.
|
* `ThreadFilter` -- filters for message packets with a particular thread ID.
|
||||||
* `ToContainsFilter` -- filters for packets that are sent to a particular address.
|
* `ToContainsFilter` -- filters for packets that are sent to a particular address.
|
||||||
* `FromContainsFilter` -- filters for packets that are sent to a particular address.
|
* `FromContainsFilter` -- filters for packets that are sent to a particular address.
|
||||||
|
|
|
@ -141,6 +141,17 @@ hr {
|
||||||
|
|
||||||
<div id="pageBody">
|
<div id="pageBody">
|
||||||
|
|
||||||
|
<h2>4.0.7 -- <span style="font-weight: normal;">2015-02-20</span></h2>
|
||||||
|
|
||||||
|
<h2> Bug
|
||||||
|
</h2>
|
||||||
|
<ul>
|
||||||
|
<li>[<a href='https://igniterealtime.org/issues/browse/SMACK-635'>SMACK-635</a>] - Typo DNSUtil.init() prevents DNS SRV lookups to fail in some cases
|
||||||
|
</li>
|
||||||
|
<li>[<a href='https://igniterealtime.org/issues/browse/SMACK-643'>SMACK-643</a>] - Smack should not set the service name to the vale of the 'from' attribute of the opening stream element received from the service
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
<h2>4.0.6 -- <span style="font-weight: normal;">2014-11-23</span></h2>
|
<h2>4.0.6 -- <span style="font-weight: normal;">2014-11-23</span></h2>
|
||||||
|
|
||||||
<h2> Bug
|
<h2> Bug
|
||||||
|
|
|
@ -7,9 +7,10 @@ smack-extensions and smack-experimental."""
|
||||||
// Note that the test dependencies (junit, …) are inferred from the
|
// Note that the test dependencies (junit, …) are inferred from the
|
||||||
// sourceSet.test of the core subproject
|
// sourceSet.test of the core subproject
|
||||||
dependencies {
|
dependencies {
|
||||||
|
// androidProjects lists all projects that are checked to compile against android.jar
|
||||||
// Filter out the optional Smack dependencies from androidProjects
|
// Filter out the optional Smack dependencies from androidProjects
|
||||||
androidProjects.findAll {
|
androidProjects.findAll {
|
||||||
![':smack-tcp', ':smack-extensions', ':smack-experimental'].contains(it.getPath())
|
![':smack-tcp', ':smack-extensions', ':smack-experimental', ':smack-bosh'].contains(it.getPath())
|
||||||
}.each { project ->
|
}.each { project ->
|
||||||
compile project
|
compile project
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,5 +4,5 @@ This API is considered beta quality."""
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
compile project(':smack-core')
|
compile project(':smack-core')
|
||||||
compile 'org.igniterealtime.jbosh:jbosh:(0.8,0.9]'
|
compile 'org.igniterealtime.jbosh:jbosh:[0.8,0.9)'
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,7 +39,6 @@ import org.jivesoftware.smack.packet.Message;
|
||||||
import org.jivesoftware.smack.packet.Stanza;
|
import org.jivesoftware.smack.packet.Stanza;
|
||||||
import org.jivesoftware.smack.packet.PlainStreamElement;
|
import org.jivesoftware.smack.packet.PlainStreamElement;
|
||||||
import org.jivesoftware.smack.packet.Presence;
|
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.SASLFailure;
|
||||||
import org.jivesoftware.smack.sasl.packet.SaslStreamElements.Success;
|
import org.jivesoftware.smack.sasl.packet.SaslStreamElements.Success;
|
||||||
import org.jivesoftware.smack.util.PacketParserUtils;
|
import org.jivesoftware.smack.util.PacketParserUtils;
|
||||||
|
@ -289,19 +288,6 @@ public class XMPPBOSHConnection extends AbstractXMPPConnection {
|
||||||
connected = false;
|
connected = false;
|
||||||
isFirstInitialization = 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.
|
// Close down the readers and writers.
|
||||||
if (readerPipe != null) {
|
if (readerPipe != null) {
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -4,5 +4,5 @@ Allow to compress the XMPP stream with help of jzlib."""
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
compile project(path: ':smack-core')
|
compile project(path: ':smack-core')
|
||||||
compile 'com.jcraft:jzlib:(1.1,1.2]'
|
compile 'com.jcraft:jzlib:[1.1,1.2)'
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,7 +57,7 @@ import org.jivesoftware.smack.compression.XMPPInputOutputStream;
|
||||||
import org.jivesoftware.smack.debugger.SmackDebugger;
|
import org.jivesoftware.smack.debugger.SmackDebugger;
|
||||||
import org.jivesoftware.smack.filter.IQReplyFilter;
|
import org.jivesoftware.smack.filter.IQReplyFilter;
|
||||||
import org.jivesoftware.smack.filter.PacketFilter;
|
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.iqrequest.IQRequestHandler;
|
||||||
import org.jivesoftware.smack.packet.Bind;
|
import org.jivesoftware.smack.packet.Bind;
|
||||||
import org.jivesoftware.smack.packet.ErrorIQ;
|
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
|
// 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.
|
// available. It will become available right after the resource has been successfully bound.
|
||||||
Bind bindResource = Bind.newSet(resource);
|
Bind bindResource = Bind.newSet(resource);
|
||||||
PacketCollector packetCollector = createPacketCollectorAndSend(new PacketIDFilter(bindResource), bindResource);
|
PacketCollector packetCollector = createPacketCollectorAndSend(new StanzaIdFilter(bindResource), bindResource);
|
||||||
Bind response = packetCollector.nextResultOrThrow();
|
Bind response = packetCollector.nextResultOrThrow();
|
||||||
// Set the connections user to the result of resource binding. It is important that we don't infer the user
|
// 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,
|
// 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
|
// For more information see http://tools.ietf.org/html/draft-cridland-xmpp-session-01
|
||||||
if (sessionFeature != null && !sessionFeature.isOptional() && !getConfiguration().isLegacySessionDisabled()) {
|
if (sessionFeature != null && !sessionFeature.isOptional() && !getConfiguration().isLegacySessionDisabled()) {
|
||||||
Session session = new Session();
|
Session session = new Session();
|
||||||
packetCollector = createPacketCollectorAndSend(new PacketIDFilter(session), session);
|
packetCollector = createPacketCollectorAndSend(new StanzaIdFilter(session), session);
|
||||||
packetCollector.nextResultOrThrow();
|
packetCollector.nextResultOrThrow();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1445,7 +1445,7 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void sendStanzaWithResponseCallback(Stanza stanza, PacketFilter replyFilter,
|
public void sendStanzaWithResponseCallback(Stanza stanza, final PacketFilter replyFilter,
|
||||||
final PacketListener callback, final ExceptionCallback exceptionCallback,
|
final PacketListener callback, final ExceptionCallback exceptionCallback,
|
||||||
long timeout) throws NotConnectedException, InterruptedException {
|
long timeout) throws NotConnectedException, InterruptedException {
|
||||||
Objects.requireNonNull(stanza, "stanza must not be null");
|
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
|
// If the packetListener got removed, then it was never run and
|
||||||
// we never received a response, inform the exception callback
|
// we never received a response, inform the exception callback
|
||||||
if (removed && exceptionCallback != null) {
|
if (removed && exceptionCallback != null) {
|
||||||
exceptionCallback.processException(new NoResponseException(AbstractXMPPConnection.this));
|
exceptionCallback.processException(NoResponseException.newWith(AbstractXMPPConnection.this, replyFilter));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, timeout, TimeUnit.MILLISECONDS);
|
}, timeout, TimeUnit.MILLISECONDS);
|
||||||
|
|
|
@ -205,7 +205,7 @@ public class PacketCollector {
|
||||||
P result = nextResult(timeout);
|
P result = nextResult(timeout);
|
||||||
cancel();
|
cancel();
|
||||||
if (result == null) {
|
if (result == null) {
|
||||||
throw new NoResponseException(connection);
|
throw NoResponseException.newWith(connection, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
XMPPErrorException.ifHasErrorThenThrow(result);
|
XMPPErrorException.ifHasErrorThenThrow(result);
|
||||||
|
|
|
@ -196,7 +196,7 @@ public class SASLAuthentication {
|
||||||
maybeThrowException();
|
maybeThrowException();
|
||||||
|
|
||||||
if (!authenticationSuccessful) {
|
if (!authenticationSuccessful) {
|
||||||
throw new NoResponseException(connection);
|
throw NoResponseException.newWith(connection);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -239,7 +239,7 @@ public class SASLAuthentication {
|
||||||
maybeThrowException();
|
maybeThrowException();
|
||||||
|
|
||||||
if (!authenticationSuccessful) {
|
if (!authenticationSuccessful) {
|
||||||
throw new NoResponseException(connection);
|
throw NoResponseException.newWith(connection);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -274,7 +274,7 @@ public class SASLAuthentication {
|
||||||
maybeThrowException();
|
maybeThrowException();
|
||||||
|
|
||||||
if (!authenticationSuccessful) {
|
if (!authenticationSuccessful) {
|
||||||
throw new NoResponseException(connection);
|
throw NoResponseException.newWith(connection);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,7 @@ package org.jivesoftware.smack;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.jivesoftware.smack.filter.PacketFilter;
|
||||||
import org.jivesoftware.smack.util.dns.HostAddress;
|
import org.jivesoftware.smack.util.dns.HostAddress;
|
||||||
import org.jxmpp.jid.Jid;
|
import org.jxmpp.jid.Jid;
|
||||||
|
|
||||||
|
@ -65,10 +66,47 @@ public class SmackException extends Exception {
|
||||||
*/
|
*/
|
||||||
private static final long serialVersionUID = -6523363748984543636L;
|
private static final long serialVersionUID = -6523363748984543636L;
|
||||||
|
|
||||||
public NoResponseException(XMPPConnection connection) {
|
private final PacketFilter filter;
|
||||||
super("No response received within packet reply timeout. Timeout was " + connection.getPacketReplyTimeout()
|
|
||||||
+ "ms (~" + connection.getPacketReplyTimeout() / 1000 + "s)");
|
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 <code>null</code>.
|
||||||
|
*/
|
||||||
|
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 {
|
public static class NotLoggedInException extends SmackException {
|
||||||
|
|
|
@ -190,7 +190,7 @@ public class SynchronizationPoint<E extends Exception> {
|
||||||
case Initial:
|
case Initial:
|
||||||
case NoResponse:
|
case NoResponse:
|
||||||
case RequestSent:
|
case RequestSent:
|
||||||
throw new NoResponseException(connection);
|
throw NoResponseException.newWith(connection);
|
||||||
default:
|
default:
|
||||||
// Do nothing
|
// Do nothing
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -129,11 +129,6 @@ public abstract class XMPPException extends Exception {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return getMessage();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void ifHasErrorThenThrow(Stanza packet) throws XMPPErrorException {
|
public static void ifHasErrorThenThrow(Stanza packet) throws XMPPErrorException {
|
||||||
XMPPError xmppError = packet.getError();
|
XMPPError xmppError = packet.getError();
|
||||||
if (xmppError != null) {
|
if (xmppError != null) {
|
||||||
|
@ -157,7 +152,7 @@ public abstract class XMPPException extends Exception {
|
||||||
* @param streamError the root cause of the exception.
|
* @param streamError the root cause of the exception.
|
||||||
*/
|
*/
|
||||||
public StreamErrorException(StreamError streamError) {
|
public StreamErrorException(StreamError streamError) {
|
||||||
super();
|
super(streamError.toString());
|
||||||
this.streamError = streamError;
|
this.streamError = streamError;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -171,14 +166,5 @@ public abstract class XMPPException extends Exception {
|
||||||
return streamError;
|
return streamError;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getMessage() {
|
|
||||||
return streamError.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return getMessage();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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<PacketFilter> filters;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an empty filter.
|
||||||
|
*/
|
||||||
|
protected AbstractListFilter() {
|
||||||
|
filters = new ArrayList<PacketFilter>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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<PacketFilter>(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();
|
||||||
|
}
|
||||||
|
}
|
|
@ -17,12 +17,7 @@
|
||||||
|
|
||||||
package org.jivesoftware.smack.filter;
|
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.packet.Stanza;
|
||||||
import org.jivesoftware.smack.util.Objects;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implements the logical AND operation over two or more packet filters.
|
* Implements the logical AND operation over two or more packet filters.
|
||||||
|
@ -30,19 +25,14 @@ import org.jivesoftware.smack.util.Objects;
|
||||||
*
|
*
|
||||||
* @author Matt Tucker
|
* @author Matt Tucker
|
||||||
*/
|
*/
|
||||||
public class AndFilter implements PacketFilter {
|
public class AndFilter extends AbstractListFilter implements PacketFilter {
|
||||||
|
|
||||||
/**
|
|
||||||
* The list of filters.
|
|
||||||
*/
|
|
||||||
private final List<PacketFilter> filters;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an empty AND filter. Filters should be added using the
|
* Creates an empty AND filter. Filters should be added using the
|
||||||
* {@link #addFilter(PacketFilter)} method.
|
* {@link #addFilter(PacketFilter)} method.
|
||||||
*/
|
*/
|
||||||
public AndFilter() {
|
public AndFilter() {
|
||||||
filters = new ArrayList<PacketFilter>();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -51,22 +41,7 @@ public class AndFilter implements PacketFilter {
|
||||||
* @param filters the filters to add.
|
* @param filters the filters to add.
|
||||||
*/
|
*/
|
||||||
public AndFilter(PacketFilter... filters) {
|
public AndFilter(PacketFilter... filters) {
|
||||||
Objects.requireNonNull(filters, "Parameter must not be null.");
|
super(filters);
|
||||||
for(PacketFilter filter : filters) {
|
|
||||||
Objects.requireNonNull(filter, "Parameter must not be null.");
|
|
||||||
}
|
|
||||||
this.filters = new ArrayList<PacketFilter>(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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean accept(Stanza packet) {
|
public boolean accept(Stanza packet) {
|
||||||
|
@ -78,7 +53,4 @@ public class AndFilter implements PacketFilter {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String toString() {
|
|
||||||
return filters.toString();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,7 @@ package org.jivesoftware.smack.filter;
|
||||||
import java.lang.reflect.ParameterizedType;
|
import java.lang.reflect.ParameterizedType;
|
||||||
|
|
||||||
import org.jivesoftware.smack.packet.Stanza;
|
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.
|
* 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<P extends Stanza> implements Pack
|
||||||
protected final Class<P> packetType;
|
protected final Class<P> packetType;
|
||||||
|
|
||||||
public FlexiblePacketTypeFilter(Class<P> packetType) {
|
public FlexiblePacketTypeFilter(Class<P> packetType) {
|
||||||
this.packetType = packetType;
|
this.packetType = Objects.requireNonNull(packetType, "Type must not be null");
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
|
@ -49,4 +50,12 @@ public abstract class FlexiblePacketTypeFilter<P extends Stanza> implements Pack
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract boolean acceptSpecific(P packet);
|
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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -106,6 +106,6 @@ public class FromMatchesFilter implements PacketFilter {
|
||||||
|
|
||||||
public String toString() {
|
public String toString() {
|
||||||
String matchMode = ignoreResourcepart ? "ignoreResourcepart" : "full";
|
String matchMode = ignoreResourcepart ? "ignoreResourcepart" : "full";
|
||||||
return "FromMatchesFilter (" +matchMode + "): " + address;
|
return getClass().getSimpleName() + " (" + matchMode + "): " + address;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -95,7 +95,7 @@ public class IQReplyFilter implements PacketFilter {
|
||||||
packetId = iqPacket.getStanzaId();
|
packetId = iqPacket.getStanzaId();
|
||||||
|
|
||||||
PacketFilter iqFilter = new OrFilter(IQTypeFilter.ERROR, IQTypeFilter.RESULT);
|
PacketFilter iqFilter = new OrFilter(IQTypeFilter.ERROR, IQTypeFilter.RESULT);
|
||||||
PacketFilter idFilter = new PacketIDFilter(iqPacket);
|
PacketFilter idFilter = new StanzaIdFilter(iqPacket);
|
||||||
iqAndIdFilter = new AndFilter(iqFilter, idFilter);
|
iqAndIdFilter = new AndFilter(iqFilter, idFilter);
|
||||||
fromFilter = new OrFilter();
|
fromFilter = new OrFilter();
|
||||||
fromFilter.addFilter(FromMatchesFilter.createFull(to));
|
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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,4 +38,11 @@ public class IQResultReplyFilter extends IQReplyFilter {
|
||||||
return IQTypeFilter.RESULT.accept(packet);
|
return IQTypeFilter.RESULT.accept(packet);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append(getClass().getSimpleName());
|
||||||
|
sb.append(" (" + super.toString() + ')');
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,7 @@ package org.jivesoftware.smack.filter;
|
||||||
|
|
||||||
import org.jivesoftware.smack.packet.IQ;
|
import org.jivesoftware.smack.packet.IQ;
|
||||||
import org.jivesoftware.smack.packet.IQ.Type;
|
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
|
* 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<IQ> {
|
||||||
|
|
||||||
private IQTypeFilter(IQ.Type type) {
|
private IQTypeFilter(IQ.Type type) {
|
||||||
super(IQ.class);
|
super(IQ.class);
|
||||||
this.type = type;
|
this.type = Objects.requireNonNull(type, "Type must not be null");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean acceptSpecific(IQ iq) {
|
protected boolean acceptSpecific(IQ iq) {
|
||||||
return iq.getType() == type;
|
return iq.getType() == type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return getClass().getSimpleName() + ": type=" + type;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,4 +55,8 @@ public class MessageTypeFilter extends FlexiblePacketTypeFilter<Message> {
|
||||||
return message.getType() == type;
|
return message.getType() == type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return getClass().getSimpleName() + ": type=" + type;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,4 +36,8 @@ public class MessageWithBodiesFilter extends FlexiblePacketTypeFilter<Message> {
|
||||||
return !message.getBodies().isEmpty();
|
return !message.getBodies().isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return getClass().getSimpleName();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,4 +36,8 @@ public class MessageWithSubjectFilter extends FlexiblePacketTypeFilter<Message>
|
||||||
return message.getSubject() != null;
|
return message.getSubject() != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return getClass().getSimpleName();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
package org.jivesoftware.smack.filter;
|
package org.jivesoftware.smack.filter;
|
||||||
|
|
||||||
import org.jivesoftware.smack.packet.Stanza;
|
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
|
* 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.
|
* @param filter the filter.
|
||||||
*/
|
*/
|
||||||
public NotFilter(PacketFilter filter) {
|
public NotFilter(PacketFilter filter) {
|
||||||
if (filter == null) {
|
this.filter = Objects.requireNonNull(filter, "Parameter must not be null.");
|
||||||
throw new IllegalArgumentException("Parameter must not be null.");
|
|
||||||
}
|
|
||||||
this.filter = filter;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean accept(Stanza packet) {
|
public boolean accept(Stanza packet) {
|
||||||
|
|
|
@ -17,12 +17,7 @@
|
||||||
|
|
||||||
package org.jivesoftware.smack.filter;
|
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.packet.Stanza;
|
||||||
import org.jivesoftware.smack.util.Objects;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implements the logical OR operation over two or more packet filters. In
|
* 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
|
* @author Matt Tucker
|
||||||
*/
|
*/
|
||||||
public class OrFilter implements PacketFilter {
|
public class OrFilter extends AbstractListFilter implements PacketFilter {
|
||||||
|
|
||||||
/**
|
|
||||||
* The list of filters.
|
|
||||||
*/
|
|
||||||
private final List<PacketFilter> filters;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an empty OR filter. Filters should be added using the
|
* Creates an empty OR filter. Filters should be added using the
|
||||||
* {@link #addFilter(PacketFilter)} method.
|
* {@link #addFilter(PacketFilter)} method.
|
||||||
*/
|
*/
|
||||||
public OrFilter() {
|
public OrFilter() {
|
||||||
filters = new ArrayList<PacketFilter>();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -51,24 +41,10 @@ public class OrFilter implements PacketFilter {
|
||||||
* @param filters the filters to add.
|
* @param filters the filters to add.
|
||||||
*/
|
*/
|
||||||
public OrFilter(PacketFilter... filters) {
|
public OrFilter(PacketFilter... filters) {
|
||||||
Objects.requireNonNull(filters, "Parameter must not be null.");
|
super(filters);
|
||||||
for(PacketFilter filter : filters) {
|
|
||||||
Objects.requireNonNull(filter, "Parameter must not be null.");
|
|
||||||
}
|
|
||||||
this.filters = new ArrayList<PacketFilter>(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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public boolean accept(Stanza packet) {
|
public boolean accept(Stanza packet) {
|
||||||
for (PacketFilter filter : filters) {
|
for (PacketFilter filter : filters) {
|
||||||
if (filter.accept(packet)) {
|
if (filter.accept(packet)) {
|
||||||
|
@ -78,7 +54,4 @@ public class OrFilter implements PacketFilter {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String toString() {
|
|
||||||
return filters.toString();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -68,4 +68,9 @@ public class PacketExtensionFilter implements PacketFilter {
|
||||||
public boolean accept(Stanza packet) {
|
public boolean accept(Stanza packet) {
|
||||||
return packet.hasExtension(elementName, namespace);
|
return packet.hasExtension(elementName, namespace);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return getClass().getSimpleName() + ": element=" + elementName + " namespace=" + namespace;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,7 +28,7 @@ import org.jivesoftware.smack.packet.Stanza;
|
||||||
* packet filtering by using the {@link org.jivesoftware.smack.filter.AndFilter AndFilter} and
|
* 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
|
* {@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
|
* 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).
|
||||||
*
|
*
|
||||||
* <pre>
|
* <pre>
|
||||||
* // Use an anonymous inner class to define a packet filter that returns
|
* // Use an anonymous inner class to define a packet filter that returns
|
||||||
|
|
|
@ -24,7 +24,9 @@ import org.jivesoftware.smack.util.StringUtils;
|
||||||
* Filters for packets with a particular packet ID.
|
* Filters for packets with a particular packet ID.
|
||||||
*
|
*
|
||||||
* @author Matt Tucker
|
* @author Matt Tucker
|
||||||
|
* @deprecated use {@link StanzaIdFilter} instead.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public class PacketIDFilter implements PacketFilter {
|
public class PacketIDFilter implements PacketFilter {
|
||||||
|
|
||||||
private final String packetID;
|
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.
|
* Creates a new packet ID filter using the specified packet's ID.
|
||||||
*
|
*
|
||||||
* @param packet the packet which the ID is taken from.
|
* @param packet the packet which the ID is taken from.
|
||||||
|
* @deprecated use {@link StanzaIdfilter(Stanza)} instead.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public PacketIDFilter(Stanza packet) {
|
public PacketIDFilter(Stanza packet) {
|
||||||
this(packet.getStanzaId());
|
this(packet.getStanzaId());
|
||||||
}
|
}
|
||||||
|
@ -42,7 +46,9 @@ public class PacketIDFilter implements PacketFilter {
|
||||||
* Creates a new packet ID filter using the specified packet ID.
|
* Creates a new packet ID filter using the specified packet ID.
|
||||||
*
|
*
|
||||||
* @param packetID the packet ID to filter for.
|
* @param packetID the packet ID to filter for.
|
||||||
|
* @deprecated use {@link StanzaIdFilter(String)} instead.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public PacketIDFilter(String packetID) {
|
public PacketIDFilter(String packetID) {
|
||||||
StringUtils.requireNotNullOrEmpty(packetID, "Packet ID must not be null or empty.");
|
StringUtils.requireNotNullOrEmpty(packetID, "Packet ID must not be null or empty.");
|
||||||
this.packetID = packetID;
|
this.packetID = packetID;
|
||||||
|
@ -53,6 +59,6 @@ public class PacketIDFilter implements PacketFilter {
|
||||||
}
|
}
|
||||||
|
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "PacketIDFilter by id: " + packetID;
|
return getClass().getSimpleName() + ": id=" + packetID;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,7 +53,8 @@ public class PacketTypeFilter implements PacketFilter {
|
||||||
return packetType.isInstance(packet);
|
return packetType.isInstance(packet);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "PacketTypeFilter: " + packetType.getName();
|
return getClass().getSimpleName() + ": " + packetType.getName();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,7 @@ package org.jivesoftware.smack.filter;
|
||||||
|
|
||||||
import org.jivesoftware.smack.packet.Presence;
|
import org.jivesoftware.smack.packet.Presence;
|
||||||
import org.jivesoftware.smack.packet.Presence.Type;
|
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
|
* 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<Presence> {
|
||||||
|
|
||||||
private PresenceTypeFilter(Presence.Type type) {
|
private PresenceTypeFilter(Presence.Type type) {
|
||||||
super(Presence.class);
|
super(Presence.class);
|
||||||
this.type = type;
|
this.type = Objects.requireNonNull(type, "type must not be null");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean acceptSpecific(Presence presence) {
|
protected boolean acceptSpecific(Presence presence) {
|
||||||
return presence.getType() == type;
|
return presence.getType() == type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return getClass().getSimpleName() + ": type=" + type;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -17,7 +17,6 @@
|
||||||
|
|
||||||
package org.jivesoftware.smack.filter;
|
package org.jivesoftware.smack.filter;
|
||||||
|
|
||||||
import org.jivesoftware.smack.packet.Stanza;
|
|
||||||
import org.jivesoftware.smack.packet.Message;
|
import org.jivesoftware.smack.packet.Message;
|
||||||
import org.jivesoftware.smack.util.StringUtils;
|
import org.jivesoftware.smack.util.StringUtils;
|
||||||
|
|
||||||
|
@ -26,7 +25,7 @@ import org.jivesoftware.smack.util.StringUtils;
|
||||||
*
|
*
|
||||||
* @author Matt Tucker
|
* @author Matt Tucker
|
||||||
*/
|
*/
|
||||||
public class ThreadFilter implements PacketFilter {
|
public class ThreadFilter extends FlexiblePacketTypeFilter<Message> implements PacketFilter {
|
||||||
|
|
||||||
private final String thread;
|
private final String thread;
|
||||||
|
|
||||||
|
@ -40,7 +39,13 @@ public class ThreadFilter implements PacketFilter {
|
||||||
this.thread = thread;
|
this.thread = thread;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean accept(Stanza packet) {
|
@Override
|
||||||
return packet instanceof Message && thread.equals(((Message) packet).getThread());
|
protected boolean acceptSpecific(Message message) {
|
||||||
|
return thread.equals(message.getThread());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return getClass().getSimpleName() + ": thread=" + thread;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,4 +36,8 @@ public class ToFilter implements PacketFilter {
|
||||||
return packetTo.equals(to);
|
return packetTo.equals(to);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return getClass().getSimpleName() + ": to=" + to;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,6 @@ Connect your favourite slf4j backend of choice to get output inside of it"""
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
compile project(':smack-core')
|
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
|
testCompile project(':smack-core').sourceSets.test.runtimeClasspath
|
||||||
}
|
}
|
||||||
|
|
|
@ -176,11 +176,8 @@ public class FaultTolerantNegotiator extends StreamNegotiator {
|
||||||
this.collector = collector;
|
this.collector = collector;
|
||||||
}
|
}
|
||||||
|
|
||||||
public InputStream call() throws XMPPErrorException, InterruptedException, SmackException {
|
public InputStream call() throws XMPPErrorException, InterruptedException, NoResponseException, SmackException {
|
||||||
Stanza streamInitiation = collector.nextResult();
|
Stanza streamInitiation = collector.nextResultOrThrow();
|
||||||
if (streamInitiation == null) {
|
|
||||||
throw new NoResponseException(connection);
|
|
||||||
}
|
|
||||||
StreamNegotiator negotiator = determineNegotiator(streamInitiation);
|
StreamNegotiator negotiator = determineNegotiator(streamInitiation);
|
||||||
return negotiator.negotiateIncomingStream(streamInitiation);
|
return negotiator.negotiateIncomingStream(streamInitiation);
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,6 +32,7 @@ import org.jivesoftware.smack.filter.PacketFilter;
|
||||||
import org.jivesoftware.smack.filter.PacketTypeFilter;
|
import org.jivesoftware.smack.filter.PacketTypeFilter;
|
||||||
import org.jivesoftware.smack.packet.IQ;
|
import org.jivesoftware.smack.packet.IQ;
|
||||||
import org.jivesoftware.smack.packet.Stanza;
|
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.Socks5BytestreamManager;
|
||||||
import org.jivesoftware.smackx.bytestreams.socks5.Socks5BytestreamRequest;
|
import org.jivesoftware.smackx.bytestreams.socks5.Socks5BytestreamRequest;
|
||||||
import org.jivesoftware.smackx.bytestreams.socks5.Socks5BytestreamSession;
|
import org.jivesoftware.smackx.bytestreams.socks5.Socks5BytestreamSession;
|
||||||
|
@ -128,14 +129,11 @@ public class Socks5TransferNegotiator extends StreamNegotiator {
|
||||||
*/
|
*/
|
||||||
private static class BytestreamSIDFilter extends PacketTypeFilter {
|
private static class BytestreamSIDFilter extends PacketTypeFilter {
|
||||||
|
|
||||||
private String sessionID;
|
private final String sessionID;
|
||||||
|
|
||||||
public BytestreamSIDFilter(String sessionID) {
|
public BytestreamSIDFilter(String sessionID) {
|
||||||
super(Bytestream.class);
|
super(Bytestream.class);
|
||||||
if (sessionID == null) {
|
this.sessionID = Objects.requireNonNull(sessionID, "SessionID cannot be null");
|
||||||
throw new IllegalArgumentException("StreamID cannot be null");
|
|
||||||
}
|
|
||||||
this.sessionID = sessionID;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -22,6 +22,7 @@ import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.WeakHashMap;
|
import java.util.WeakHashMap;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import org.jivesoftware.smack.Manager;
|
import org.jivesoftware.smack.Manager;
|
||||||
import org.jivesoftware.smack.PacketCollector;
|
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.NoResponseException;
|
||||||
import org.jivesoftware.smack.SmackException.NotConnectedException;
|
import org.jivesoftware.smack.SmackException.NotConnectedException;
|
||||||
import org.jivesoftware.smack.XMPPException.XMPPErrorException;
|
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.smack.packet.IQ;
|
||||||
import org.jivesoftware.smackx.iqregister.packet.Registration;
|
import org.jivesoftware.smackx.iqregister.packet.Registration;
|
||||||
|
|
||||||
|
@ -41,6 +42,9 @@ import org.jivesoftware.smackx.iqregister.packet.Registration;
|
||||||
* @author Matt Tucker
|
* @author Matt Tucker
|
||||||
*/
|
*/
|
||||||
public class AccountManager extends Manager {
|
public class AccountManager extends Manager {
|
||||||
|
|
||||||
|
private static final Logger LOGGER = Logger.getLogger(AccountManager.class.getName());
|
||||||
|
|
||||||
private static final Map<XMPPConnection, AccountManager> INSTANCES = new WeakHashMap<XMPPConnection, AccountManager>();
|
private static final Map<XMPPConnection, AccountManager> INSTANCES = new WeakHashMap<XMPPConnection, AccountManager>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -58,6 +62,35 @@ public class AccountManager extends Manager {
|
||||||
return accountManager;
|
return accountManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static boolean allowSensitiveOperationOverInsecureConnectionDefault = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The default value used by new account managers for <code>allowSensitiveOperationOverInsecureConnection</code>.
|
||||||
|
*
|
||||||
|
* @param allow
|
||||||
|
* @see #sensitiveOperationOverInsecureConnection(boolean)
|
||||||
|
* @since 4.1
|
||||||
|
*/
|
||||||
|
public static void sensitiveOperationOverInsecureConnectionDefault(boolean allow) {
|
||||||
|
AccountManager.allowSensitiveOperationOverInsecureConnectionDefault = allow;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean allowSensitiveOperationOverInsecureConnection = allowSensitiveOperationOverInsecureConnectionDefault;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set to <code>true</code> to allow sensitive operation over insecure connection.
|
||||||
|
* <p>
|
||||||
|
* Set to true to allow sensitive operations like account creation or password changes over an insecure (e.g.
|
||||||
|
* unencrypted) connections.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param allow
|
||||||
|
* @since 4.1
|
||||||
|
*/
|
||||||
|
public void sensitiveOperationOverInsecureConnection(boolean allow) {
|
||||||
|
this.allowSensitiveOperationOverInsecureConnection = allow;
|
||||||
|
}
|
||||||
|
|
||||||
private Registration info = null;
|
private Registration info = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -231,6 +264,11 @@ public class AccountManager extends Manager {
|
||||||
*/
|
*/
|
||||||
public void createAccount(String username, String password, Map<String, String> attributes)
|
public void createAccount(String username, String password, Map<String, String> attributes)
|
||||||
throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
|
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("username", username);
|
||||||
attributes.put("password", password);
|
attributes.put("password", password);
|
||||||
Registration reg = new Registration(attributes);
|
Registration reg = new Registration(attributes);
|
||||||
|
@ -251,6 +289,11 @@ public class AccountManager extends Manager {
|
||||||
* @throws InterruptedException
|
* @throws InterruptedException
|
||||||
*/
|
*/
|
||||||
public void changePassword(String newPassword) throws NoResponseException, XMPPErrorException, NotConnectedException, 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<String, String> map = new HashMap<String, String>();
|
Map<String, String> map = new HashMap<String, String>();
|
||||||
map.put("username", connection().getUser().getLocalpart().toString());
|
map.put("username", connection().getUser().getLocalpart().toString());
|
||||||
map.put("password",newPassword);
|
map.put("password",newPassword);
|
||||||
|
@ -298,7 +341,7 @@ public class AccountManager extends Manager {
|
||||||
}
|
}
|
||||||
|
|
||||||
private PacketCollector createPacketCollectorAndSend(IQ req) throws NotConnectedException, InterruptedException {
|
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;
|
return collector;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,26 +27,6 @@ import org.jxmpp.jid.Jid;
|
||||||
* A Version IQ packet, which is used by XMPP clients to discover version information
|
* A Version IQ packet, which is used by XMPP clients to discover version information
|
||||||
* about the software running at another entity's JID.<p>
|
* about the software running at another entity's JID.<p>
|
||||||
*
|
*
|
||||||
* An example to discover the version of the server:
|
|
||||||
* <pre>
|
|
||||||
* // 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...
|
|
||||||
* }</pre><p>
|
|
||||||
*
|
|
||||||
* @author Gaston Dombiak
|
* @author Gaston Dombiak
|
||||||
*/
|
*/
|
||||||
public class Version extends IQ {
|
public class Version extends IQ {
|
||||||
|
|
|
@ -57,14 +57,15 @@ public class ItemProvider extends PacketExtensionProvider<Item>
|
||||||
String payloadElemName = parser.getName();
|
String payloadElemName = parser.getName();
|
||||||
String payloadNS = parser.getNamespace();
|
String payloadNS = parser.getNamespace();
|
||||||
|
|
||||||
if (ProviderManager.getExtensionProvider(payloadElemName, payloadNS) == null)
|
final PacketExtensionProvider<PacketExtension> extensionProvider = ProviderManager.getExtensionProvider(payloadElemName, payloadNS);
|
||||||
|
if (extensionProvider == null)
|
||||||
{
|
{
|
||||||
CharSequence payloadText = PacketParserUtils.parseElement(parser, true);
|
CharSequence payloadText = PacketParserUtils.parseElement(parser, true);
|
||||||
return new PayloadItem<SimplePayload>(id, node, new SimplePayload(payloadElemName, payloadNS, payloadText));
|
return new PayloadItem<SimplePayload>(id, node, new SimplePayload(payloadElemName, payloadNS, payloadText));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return new PayloadItem<PacketExtension>(id, node, PacketParserUtils.parsePacketExtension(payloadElemName, payloadNS, parser));
|
return new PayloadItem<PacketExtension>(id, node, extensionProvider.parse(parser));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,7 +51,7 @@ import org.jivesoftware.smack.packet.Stanza;
|
||||||
* <code>
|
* <code>
|
||||||
* public void methodToTest() {
|
* public void methodToTest() {
|
||||||
* Packet packet = new Packet(); // create an XMPP packet
|
* Packet packet = new Packet(); // create an XMPP packet
|
||||||
* PacketCollector collector = connection.createPacketCollector(new PacketIDFilter());
|
* PacketCollector collector = connection.createPacketCollector(new StanzaIdFilter());
|
||||||
* connection.sendPacket(packet);
|
* connection.sendPacket(packet);
|
||||||
* Packet reply = collector.nextResult();
|
* Packet reply = collector.nextResult();
|
||||||
* }
|
* }
|
||||||
|
|
|
@ -142,6 +142,11 @@ public class Roster extends Manager {
|
||||||
*/
|
*/
|
||||||
private final Map<Jid, Map<Resourcepart, Presence>> presenceMap = new ConcurrentHashMap<>();
|
private final Map<Jid, Map<Resourcepart, Presence>> presenceMap = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Listeners called when the Roster was loaded.
|
||||||
|
*/
|
||||||
|
private final Set<RosterLoadedListener> rosterLoadedListeners = new LinkedHashSet<>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mutually exclude roster listener invocation and changing the {@link entries} map. Also used
|
* 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.
|
* 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.<p>
|
* Creates a new group.<p>
|
||||||
* <p/>
|
* <p/>
|
||||||
|
@ -1331,6 +1364,22 @@ public class Roster extends Manager {
|
||||||
}
|
}
|
||||||
// Fire event for roster listeners.
|
// Fire event for roster listeners.
|
||||||
fireRosterChangedEvent(addedEntries, updatedEntries, deletedEntries);
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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.
|
||||||
|
* <p>
|
||||||
|
* 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.
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
public interface RosterLoadedListener {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the Roster was loaded successfully.
|
||||||
|
*
|
||||||
|
* @param roster the Roster that was loaded successfully.
|
||||||
|
*/
|
||||||
|
public void onRosterLoaded(Roster roster);
|
||||||
|
|
||||||
|
}
|
|
@ -5,5 +5,5 @@ javax.naming API (e.g. Android)."""
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
compile project(path: ':smack-core')
|
compile project(path: ':smack-core')
|
||||||
compile 'dnsjava:dnsjava:(2.1,2.2]'
|
compile 'dnsjava:dnsjava:[2.1,2.2)'
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,6 @@ javax.naming API (e.g. Android)."""
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
compile project(path: ':smack-core')
|
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"
|
compile "org.jxmpp:jxmpp-util-cache:$jxmppVersion"
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,11 @@
|
||||||
*/
|
*/
|
||||||
package org.jivesoftware.smack.sm;
|
package org.jivesoftware.smack.sm;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import org.jivesoftware.smack.SmackException;
|
import org.jivesoftware.smack.SmackException;
|
||||||
|
import org.jivesoftware.smack.packet.Stanza;
|
||||||
|
|
||||||
public abstract class StreamManagementException extends SmackException {
|
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 + "'");
|
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<Stanza> ackedStanzas;
|
||||||
|
|
||||||
|
public StreamManagementCounterError(long handledCount, long previousServerHandlerCount,
|
||||||
|
long ackedStanzaCount,
|
||||||
|
List<Stanza> 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() ? "<no acked stanzas>"
|
||||||
|
: 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<Stanza> getAckedStanzas() {
|
||||||
|
return ackedStanzas;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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.
|
||||||
|
* <p>
|
||||||
|
* 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.
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* 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()}.
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
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);
|
||||||
|
|
||||||
|
}
|
|
@ -54,6 +54,7 @@ import org.jivesoftware.smack.sasl.packet.SaslStreamElements.Success;
|
||||||
import org.jivesoftware.smack.sm.SMUtils;
|
import org.jivesoftware.smack.sm.SMUtils;
|
||||||
import org.jivesoftware.smack.sm.StreamManagementException;
|
import org.jivesoftware.smack.sm.StreamManagementException;
|
||||||
import org.jivesoftware.smack.sm.StreamManagementException.StreamIdDoesNotMatchException;
|
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.StreamManagementException.StreamManagementNotEnabledException;
|
||||||
import org.jivesoftware.smack.sm.packet.StreamManagement;
|
import org.jivesoftware.smack.sm.packet.StreamManagement;
|
||||||
import org.jivesoftware.smack.sm.packet.StreamManagement.AckAnswer;
|
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.ConcurrentHashMap;
|
||||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
@ -185,6 +187,10 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
|
||||||
private final SynchronizationPoint<XMPPException> compressSyncPoint = new SynchronizationPoint<XMPPException>(
|
private final SynchronizationPoint<XMPPException> compressSyncPoint = new SynchronizationPoint<XMPPException>(
|
||||||
this);
|
this);
|
||||||
|
|
||||||
|
private static BundleAndDeferCallback defaultBundleAndDeferCallback;
|
||||||
|
|
||||||
|
private BundleAndDeferCallback bundleAndDeferCallback = defaultBundleAndDeferCallback;
|
||||||
|
|
||||||
private static boolean useSmDefault = false;
|
private static boolean useSmDefault = false;
|
||||||
|
|
||||||
private static boolean useSmResumptionDefault = true;
|
private static boolean useSmResumptionDefault = true;
|
||||||
|
@ -719,7 +725,7 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
|
||||||
Socket plain = socket;
|
Socket plain = socket;
|
||||||
// Secure the plain connection
|
// Secure the plain connection
|
||||||
socket = context.getSocketFactory().createSocket(plain,
|
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
|
// Initialize the reader and writer with the new secured version
|
||||||
initReaderAndWriter();
|
initReaderAndWriter();
|
||||||
|
|
||||||
|
@ -1272,6 +1278,30 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
|
||||||
if (element == null) {
|
if (element == null) {
|
||||||
continue;
|
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;
|
Stanza packet = null;
|
||||||
if (element instanceof Stanza) {
|
if (element instanceof Stanza) {
|
||||||
packet = (Stanza) element;
|
packet = (Stanza) element;
|
||||||
|
@ -1288,6 +1318,8 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
|
||||||
writer.flush();
|
writer.flush();
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
|
// It is important the we put the stanza in the unacknowledged stanza
|
||||||
|
// queue before we put it on the wire
|
||||||
unacknowledgedStanzas.put(packet);
|
unacknowledgedStanzas.put(packet);
|
||||||
}
|
}
|
||||||
catch (InterruptedException e) {
|
catch (InterruptedException e) {
|
||||||
|
@ -1646,7 +1678,7 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
|
||||||
return Math.min(clientResumptionTime, serverResumptionTime);
|
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);
|
long ackedStanzasCount = SMUtils.calculateDelta(handledCount, serverHandledStanzasCount);
|
||||||
final List<Stanza> ackedStanzas = new ArrayList<Stanza>(
|
final List<Stanza> ackedStanzas = new ArrayList<Stanza>(
|
||||||
handledCount <= Integer.MAX_VALUE ? (int) handledCount
|
handledCount <= Integer.MAX_VALUE ? (int) handledCount
|
||||||
|
@ -1655,7 +1687,10 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
|
||||||
Stanza ackedStanza = unacknowledgedStanzas.poll();
|
Stanza ackedStanza = unacknowledgedStanzas.poll();
|
||||||
// If the server ack'ed a stanza, then it must be in the
|
// If the server ack'ed a stanza, then it must be in the
|
||||||
// unacknowledged stanza queue. There can be no exception.
|
// unacknowledged stanza queue. There can be no exception.
|
||||||
assert(ackedStanza != null);
|
if (ackedStanza == null) {
|
||||||
|
throw new StreamManagementCounterError(handledCount, serverHandledStanzasCount,
|
||||||
|
ackedStanzasCount, ackedStanzas);
|
||||||
|
}
|
||||||
ackedStanzas.add(ackedStanza);
|
ackedStanzas.add(ackedStanza);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1691,7 +1726,7 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
|
||||||
}
|
}
|
||||||
String id = ackedStanza.getStanzaId();
|
String id = ackedStanza.getStanzaId();
|
||||||
if (StringUtils.isNullOrEmpty(id)) {
|
if (StringUtils.isNullOrEmpty(id)) {
|
||||||
return;
|
continue;
|
||||||
}
|
}
|
||||||
PacketListener listener = stanzaIdAcknowledgedListeners.remove(id);
|
PacketListener listener = stanzaIdAcknowledgedListeners.remove(id);
|
||||||
if (listener != null) {
|
if (listener != null) {
|
||||||
|
@ -1709,4 +1744,31 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
|
||||||
|
|
||||||
serverHandledStanzasCount = handledCount;
|
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.
|
||||||
|
* <p>
|
||||||
|
* You can use <code>null</code> as argument to reset the callback. Outgoing stanzas will then
|
||||||
|
* no longer get deferred.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param bundleAndDeferCallback the callback or <code>null</code>.
|
||||||
|
* @see BundleAndDeferCallback
|
||||||
|
* @since 4.1
|
||||||
|
*/
|
||||||
|
public void setBundleandDeferCallback(BundleAndDeferCallback bundleAndDeferCallback) {
|
||||||
|
this.bundleAndDeferCallback = bundleAndDeferCallback;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue