mirror of
https://github.com/vanitasvitae/Smack.git
synced 2025-09-09 00:59:39 +02:00
Add non-blocking send
This commit is contained in:
parent
711d7d92bd
commit
c77948bb91
9 changed files with 237 additions and 101 deletions
|
@ -51,6 +51,7 @@ import org.jivesoftware.smack.SmackException.AlreadyLoggedInException;
|
|||
import org.jivesoftware.smack.SmackException.NoResponseException;
|
||||
import org.jivesoftware.smack.SmackException.NotConnectedException;
|
||||
import org.jivesoftware.smack.SmackException.NotLoggedInException;
|
||||
import org.jivesoftware.smack.SmackException.OutgoingQueueFullException;
|
||||
import org.jivesoftware.smack.SmackException.ResourceBindingNotOfferedException;
|
||||
import org.jivesoftware.smack.SmackException.SecurityRequiredByClientException;
|
||||
import org.jivesoftware.smack.SmackException.SecurityRequiredException;
|
||||
|
@ -460,8 +461,17 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
|
|||
@Override
|
||||
public abstract boolean isSecureConnection();
|
||||
|
||||
protected abstract void sendStanzaInternal(Stanza packet) throws NotConnectedException, InterruptedException;
|
||||
// Usually batching is a good idea. So the two
|
||||
// send(Internal|NonBlockingInternal) methods below could be using
|
||||
// Collection<? extends TopLevelStreamElement> as parameter type instead.
|
||||
// TODO: Add "batched send" support. Note that for the non-blocking variant, this probably requires a change in
|
||||
// return type, so that it is possible to signal which messages could be "send" and which not.
|
||||
|
||||
protected abstract void sendInternal(TopLevelStreamElement element) throws NotConnectedException, InterruptedException;
|
||||
|
||||
protected abstract void sendNonBlockingInternal(TopLevelStreamElement element) throws NotConnectedException, OutgoingQueueFullException;
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Override
|
||||
public boolean trySendStanza(Stanza stanza) throws NotConnectedException {
|
||||
// Default implementation which falls back to sendStanza() as mentioned in the methods javadoc. May be
|
||||
|
@ -476,6 +486,7 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
|
|||
return true;
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Override
|
||||
public boolean trySendStanza(Stanza stanza, long timeout, TimeUnit unit)
|
||||
throws NotConnectedException, InterruptedException {
|
||||
|
@ -486,7 +497,14 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
|
|||
}
|
||||
|
||||
@Override
|
||||
public abstract void sendNonza(Nonza element) throws NotConnectedException, InterruptedException;
|
||||
public final void sendNonza(Nonza nonza) throws NotConnectedException, InterruptedException {
|
||||
sendInternal(nonza);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void sendNonzaNonBlocking(Nonza nonza) throws NotConnectedException, OutgoingQueueFullException {
|
||||
sendNonBlockingInternal(nonza);
|
||||
}
|
||||
|
||||
@Override
|
||||
public abstract boolean isUsingCompression();
|
||||
|
@ -853,8 +871,7 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
|
|||
return stanzaFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void sendStanza(Stanza stanza) throws NotConnectedException, InterruptedException {
|
||||
private Stanza preSendStanza(Stanza stanza) throws NotConnectedException {
|
||||
Objects.requireNonNull(stanza, "Stanza must not be null");
|
||||
assert stanza instanceof Message || stanza instanceof Presence || stanza instanceof IQ;
|
||||
|
||||
|
@ -873,7 +890,19 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
|
|||
// Invoke interceptors for the new stanza that is about to be sent. Interceptors may modify
|
||||
// the content of the stanza.
|
||||
Stanza stanzaAfterInterceptors = firePacketInterceptors(stanza);
|
||||
sendStanzaInternal(stanzaAfterInterceptors);
|
||||
return stanzaAfterInterceptors;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void sendStanza(Stanza stanza) throws NotConnectedException, InterruptedException {
|
||||
stanza = preSendStanza(stanza);
|
||||
sendInternal(stanza);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void sendStanzaNonBlocking(Stanza stanza) throws NotConnectedException, OutgoingQueueFullException {
|
||||
stanza = preSendStanza(stanza);
|
||||
sendNonBlockingInternal(stanza);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -2006,18 +2035,11 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
|
|||
}, timeout, TimeUnit.MILLISECONDS);
|
||||
|
||||
addAsyncStanzaListener(stanzaListener, replyFilter);
|
||||
Runnable sendOperation = () -> {
|
||||
try {
|
||||
sendStanza(stanza);
|
||||
}
|
||||
catch (NotConnectedException | InterruptedException exception) {
|
||||
future.setException(exception);
|
||||
}
|
||||
};
|
||||
if (SmackConfiguration.TRUELY_ASYNC_SENDS) {
|
||||
Async.go(sendOperation);
|
||||
} else {
|
||||
sendOperation.run();
|
||||
try {
|
||||
sendStanzaNonBlocking(stanza);
|
||||
}
|
||||
catch (NotConnectedException | OutgoingQueueFullException exception) {
|
||||
future.setException(exception);
|
||||
}
|
||||
|
||||
return future;
|
||||
|
|
|
@ -387,13 +387,4 @@ public final class SmackConfiguration {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If enabled, causes {@link AbstractXMPPConnection} to create a thread for every asynchronous send operation. This
|
||||
* is meant to work-around a shortcoming of Smack 4.4, where certain send operations are not asynchronous even if
|
||||
* they should be. This is an expert setting, do not toggle if you do not understand the consequences or have been
|
||||
* told to do so. Note that it is expected that this will not be needed in future Smack versions.
|
||||
*
|
||||
* @since 4.4.6
|
||||
*/
|
||||
public static boolean TRUELY_ASYNC_SENDS = false;
|
||||
}
|
||||
|
|
|
@ -206,6 +206,12 @@ public abstract class SmackException extends Exception {
|
|||
}
|
||||
}
|
||||
|
||||
public static class OutgoingQueueFullException extends SmackException {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
}
|
||||
|
||||
public static class IllegalStateChangeException extends SmackException {
|
||||
|
||||
/**
|
||||
|
|
|
@ -22,6 +22,7 @@ import javax.xml.namespace.QName;
|
|||
|
||||
import org.jivesoftware.smack.SmackException.NoResponseException;
|
||||
import org.jivesoftware.smack.SmackException.NotConnectedException;
|
||||
import org.jivesoftware.smack.SmackException.OutgoingQueueFullException;
|
||||
import org.jivesoftware.smack.XMPPException.XMPPErrorException;
|
||||
import org.jivesoftware.smack.filter.IQReplyFilter;
|
||||
import org.jivesoftware.smack.filter.StanzaFilter;
|
||||
|
@ -199,6 +200,8 @@ public interface XMPPConnection {
|
|||
* */
|
||||
void sendStanza(Stanza stanza) throws NotConnectedException, InterruptedException;
|
||||
|
||||
void sendStanzaNonBlocking(Stanza stanza) throws NotConnectedException, OutgoingQueueFullException;
|
||||
|
||||
/**
|
||||
* Try to send the given stanza. Returns {@code true} if the stanza was successfully put into the outgoing stanza
|
||||
* queue, otherwise, if {@code false} is returned, the stanza could not be scheduled for sending (for example
|
||||
|
@ -213,7 +216,10 @@ public interface XMPPConnection {
|
|||
* @return {@code true} if the stanza was successfully scheduled to be send, {@code false} otherwise.
|
||||
* @throws NotConnectedException if the connection is not connected.
|
||||
* @since 4.4.0
|
||||
* @deprecated use {@link #sendStanzaNonBlocking(Stanza)} instead.
|
||||
*/
|
||||
// TODO: Remove in Smack 4.7.
|
||||
@Deprecated
|
||||
boolean trySendStanza(Stanza stanza) throws NotConnectedException;
|
||||
|
||||
/**
|
||||
|
@ -234,7 +240,10 @@ public interface XMPPConnection {
|
|||
* @throws NotConnectedException if the connection is not connected.
|
||||
* @throws InterruptedException if the calling thread was interrupted.
|
||||
* @since 4.4.0
|
||||
* @deprecated use {@link #sendStanzaNonBlocking(Stanza)} instead.
|
||||
*/
|
||||
// TODO: Remove in Smack 4.7.
|
||||
@Deprecated
|
||||
boolean trySendStanza(Stanza stanza, long timeout, TimeUnit unit) throws NotConnectedException, InterruptedException;
|
||||
|
||||
/**
|
||||
|
@ -251,6 +260,8 @@ public interface XMPPConnection {
|
|||
*/
|
||||
void sendNonza(Nonza nonza) throws NotConnectedException, InterruptedException;
|
||||
|
||||
void sendNonzaNonBlocking(Nonza stanza) throws NotConnectedException, OutgoingQueueFullException;
|
||||
|
||||
/**
|
||||
* Adds a connection listener to this connection that will be notified when
|
||||
* the connection closes or fails.
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2018-2021 Florian Schmaus
|
||||
* Copyright 2018-2022 Florian Schmaus
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -37,6 +37,7 @@ import org.jivesoftware.smack.AbstractXMPPConnection;
|
|||
import org.jivesoftware.smack.SmackException;
|
||||
import org.jivesoftware.smack.SmackException.NoResponseException;
|
||||
import org.jivesoftware.smack.SmackException.NotConnectedException;
|
||||
import org.jivesoftware.smack.SmackException.OutgoingQueueFullException;
|
||||
import org.jivesoftware.smack.SmackFuture;
|
||||
import org.jivesoftware.smack.XMPPException;
|
||||
import org.jivesoftware.smack.XMPPException.FailedNonzaException;
|
||||
|
@ -67,7 +68,6 @@ import org.jivesoftware.smack.packet.IQ;
|
|||
import org.jivesoftware.smack.packet.Message;
|
||||
import org.jivesoftware.smack.packet.Nonza;
|
||||
import org.jivesoftware.smack.packet.Presence;
|
||||
import org.jivesoftware.smack.packet.Stanza;
|
||||
import org.jivesoftware.smack.packet.StreamError;
|
||||
import org.jivesoftware.smack.packet.TopLevelStreamElement;
|
||||
import org.jivesoftware.smack.packet.XmlEnvironment;
|
||||
|
@ -438,16 +438,7 @@ public final class ModularXmppClientToServerConnection extends AbstractXMPPConne
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void sendStanzaInternal(Stanza stanza) throws NotConnectedException, InterruptedException {
|
||||
sendTopLevelStreamElement(stanza);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendNonza(Nonza nonza) throws NotConnectedException, InterruptedException {
|
||||
sendTopLevelStreamElement(nonza);
|
||||
}
|
||||
|
||||
private void sendTopLevelStreamElement(TopLevelStreamElement element) throws NotConnectedException, InterruptedException {
|
||||
protected void sendInternal(TopLevelStreamElement element) throws NotConnectedException, InterruptedException {
|
||||
final XmppClientToServerTransport transport = activeTransport;
|
||||
if (transport == null) {
|
||||
throw new NotConnectedException();
|
||||
|
@ -457,6 +448,21 @@ public final class ModularXmppClientToServerConnection extends AbstractXMPPConne
|
|||
transport.notifyAboutNewOutgoingElements();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void sendNonBlockingInternal(TopLevelStreamElement element) throws NotConnectedException, OutgoingQueueFullException {
|
||||
final XmppClientToServerTransport transport = activeTransport;
|
||||
if (transport == null) {
|
||||
throw new NotConnectedException();
|
||||
}
|
||||
|
||||
boolean enqueued = outgoingElementsQueue.offer(element);
|
||||
if (!enqueued) {
|
||||
throw new OutgoingQueueFullException();
|
||||
}
|
||||
|
||||
transport.notifyAboutNewOutgoingElements();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void shutdown() {
|
||||
shutdown(false);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2010 Jive Software.
|
||||
* Copyright 2010 Jive Software, 2022 Florian Schmaus.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -23,8 +23,8 @@ import java.util.concurrent.BlockingQueue;
|
|||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.jivesoftware.smack.SmackException.OutgoingQueueFullException;
|
||||
import org.jivesoftware.smack.packet.ExtensionElement;
|
||||
import org.jivesoftware.smack.packet.Nonza;
|
||||
import org.jivesoftware.smack.packet.Stanza;
|
||||
import org.jivesoftware.smack.packet.TopLevelStreamElement;
|
||||
|
||||
|
@ -127,13 +127,16 @@ public class DummyConnection extends AbstractXMPPConnection {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void sendNonza(Nonza element) {
|
||||
protected void sendInternal(TopLevelStreamElement element) {
|
||||
queue.add(element);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void sendStanzaInternal(Stanza packet) {
|
||||
queue.add(packet);
|
||||
protected void sendNonBlockingInternal(TopLevelStreamElement element) throws OutgoingQueueFullException {
|
||||
boolean enqueued = queue.add(element);
|
||||
if (!enqueued) {
|
||||
throw new OutgoingQueueFullException();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -26,6 +26,7 @@ import java.util.logging.Logger;
|
|||
import org.jivesoftware.smack.packet.IQ;
|
||||
import org.jivesoftware.smack.packet.Message;
|
||||
import org.jivesoftware.smack.packet.Stanza;
|
||||
import org.jivesoftware.smack.packet.TopLevelStreamElement;
|
||||
|
||||
/**
|
||||
* A threaded dummy connection.
|
||||
|
@ -40,10 +41,11 @@ public class ThreadedDummyConnection extends DummyConnection {
|
|||
private volatile boolean timeout = false;
|
||||
|
||||
@Override
|
||||
protected void sendStanzaInternal(Stanza packet) {
|
||||
super.sendStanzaInternal(packet);
|
||||
protected void sendInternal(TopLevelStreamElement element) {
|
||||
super.sendInternal(element);
|
||||
|
||||
if (packet instanceof IQ && !timeout) {
|
||||
if (element instanceof IQ && !timeout) {
|
||||
IQ iq = (IQ) element;
|
||||
timeout = false;
|
||||
// Set reply packet to match one being sent. We haven't started the
|
||||
// other thread yet so this is still safe.
|
||||
|
@ -51,11 +53,11 @@ public class ThreadedDummyConnection extends DummyConnection {
|
|||
|
||||
// If no reply has been set via addIQReply, then we create a simple reply
|
||||
if (replyPacket == null) {
|
||||
replyPacket = IQ.createResultIQ((IQ) packet);
|
||||
replyPacket = IQ.createResultIQ(iq);
|
||||
replyQ.add(replyPacket);
|
||||
}
|
||||
replyPacket.setStanzaId(packet.getStanzaId());
|
||||
replyPacket.setTo(packet.getFrom());
|
||||
replyPacket.setStanzaId(iq.getStanzaId());
|
||||
replyPacket.setTo(iq.getFrom());
|
||||
if (replyPacket.getType() == null) {
|
||||
replyPacket.setType(IQ.Type.result);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue