From a203d3198c35fffc931f0ca3f061cd83ead853fb Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Sat, 12 Aug 2017 12:37:09 +0200 Subject: [PATCH 1/3] Update Bitcoin Donation address --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5ab05ca8a..46bf0f16d 100644 --- a/README.md +++ b/README.md @@ -67,8 +67,8 @@ Donate If you find Smack useful and feel like donating, then you can use one of the following systems: +- Donate via Bitcoin: 1LiU78z3498wu3jwRPKbvovKAHjTcpVbuK - [Donate via Flattr](https://flattr.com/thing/3480125) -- Donate via Bitcoin: 1D4k11bsPxRB6rS51fxDty8QYfRVUt5JMB Ignite Realtime =============== From 0602ae064ad854c090edbb409b08db3d25058504 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Fri, 11 Aug 2017 23:50:32 +0200 Subject: [PATCH 2/3] Remove deprecated methods from XMPPConnection --- .../smack/AbstractXMPPConnection.java | 36 --------- .../jivesoftware/smack/XMPPConnection.java | 75 ------------------- .../admin/ServiceAdministrationManager.java | 4 +- .../jivesoftware/smackx/ping/PingManager.java | 2 +- .../smack/inttest/IntTestUtil.java | 4 +- 5 files changed, 5 insertions(+), 116 deletions(-) diff --git a/smack-core/src/main/java/org/jivesoftware/smack/AbstractXMPPConnection.java b/smack-core/src/main/java/org/jivesoftware/smack/AbstractXMPPConnection.java index a4f5ae88a..4b6b73e5c 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/AbstractXMPPConnection.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/AbstractXMPPConnection.java @@ -323,12 +323,6 @@ public abstract class AbstractXMPPConnection implements XMPPConnection { return config; } - @SuppressWarnings("deprecation") - @Override - public DomainBareJid getServiceName() { - return getXMPPServiceDomain(); - } - @Override public DomainBareJid getXMPPServiceDomain() { if (xmppServiceDomain != null) { @@ -665,12 +659,6 @@ public abstract class AbstractXMPPConnection implements XMPPConnection { } } - @Deprecated - @Override - public void sendPacket(Stanza packet) throws NotConnectedException, InterruptedException { - sendStanza(packet); - } - @Override public void sendStanza(Stanza stanza) throws NotConnectedException, InterruptedException { Objects.requireNonNull(stanza, "Stanza must not be null"); @@ -803,18 +791,6 @@ public abstract class AbstractXMPPConnection implements XMPPConnection { collectors.remove(collector); } - @Override - @Deprecated - public void addPacketListener(StanzaListener packetListener, StanzaFilter packetFilter) { - addAsyncStanzaListener(packetListener, packetFilter); - } - - @Override - @Deprecated - public boolean removePacketListener(StanzaListener packetListener) { - return removeAsyncStanzaListener(packetListener); - } - @Override public void addSyncStanzaListener(StanzaListener packetListener, StanzaFilter packetFilter) { if (packetListener == null) { @@ -976,18 +952,6 @@ public abstract class AbstractXMPPConnection implements XMPPConnection { } } - @SuppressWarnings("deprecation") - @Override - public long getPacketReplyTimeout() { - return getReplyTimeout(); - } - - @SuppressWarnings("deprecation") - @Override - public void setPacketReplyTimeout(long timeout) { - setReplyTimeout(timeout); - } - @Override public long getReplyTimeout() { return replyTimeout; diff --git a/smack-core/src/main/java/org/jivesoftware/smack/XMPPConnection.java b/smack-core/src/main/java/org/jivesoftware/smack/XMPPConnection.java index f6e48b8d4..10894237f 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/XMPPConnection.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/XMPPConnection.java @@ -72,18 +72,6 @@ import org.jxmpp.jid.EntityFullJid; */ public interface XMPPConnection { - /** - * Returns the name of the service provided by the XMPP server for this connection. - * This is also called XMPP domain of the connected server. After - * authenticating with the server the returned value may be different. - * - * @return the name of the service provided by the XMPP server. - // TODO remove this once the java bugs are fixed, causing a warning -// * @deprecated use {@link #getXMPPServiceDomain()} instead. - */ -// @Deprecated - public DomainBareJid getServiceName(); - /** * Returns the XMPP Domain of the service provided by the XMPP server and used for this connection. After * authenticating with the server the returned value may be different. @@ -165,18 +153,6 @@ public interface XMPPConnection { */ public boolean isUsingCompression(); - /** - * Sends the specified stanza(/packet) to the server. - * - * @param packet the stanza(/packet) to send. - * @throws NotConnectedException - * @throws InterruptedException - * @deprecated use {@link #sendStanza(Stanza)} instead. - */ - // TODO Remove in 4.3. - @Deprecated - public void sendPacket(Stanza packet) throws NotConnectedException, InterruptedException; - /** * Sends the specified stanza to the server. * @@ -281,35 +257,6 @@ public interface XMPPConnection { */ public void removeStanzaCollector(StanzaCollector collector); - /** - * Registers a stanza(/packet) listener with this connection. - *

- * This method has been deprecated. It is important to differentiate between using an asynchronous stanza(/packet) listener - * (preferred where possible) and a synchronous stanza(/packet) lister. Refer - * {@link #addAsyncStanzaListener(StanzaListener, StanzaFilter)} and - * {@link #addSyncStanzaListener(StanzaListener, StanzaFilter)} for more information. - *

- * - * @param packetListener the stanza(/packet) listener to notify of new received packets. - * @param packetFilter the stanza(/packet) filter to use. - * @deprecated use {@link #addAsyncStanzaListener(StanzaListener, StanzaFilter)} or - * {@link #addSyncStanzaListener(StanzaListener, StanzaFilter)}. - */ - // TODO Remove in 4.3. - @Deprecated - public void addPacketListener(StanzaListener packetListener, StanzaFilter packetFilter); - - /** - * Removes a stanza(/packet) listener for received packets from this connection. - * - * @param packetListener the stanza(/packet) listener to remove. - * @return true if the stanza(/packet) listener was removed - * @deprecated use {@link #removeAsyncStanzaListener(StanzaListener)} or {@link #removeSyncStanzaListener(StanzaListener)}. - */ - // TODO Remove in 4.3. - @Deprecated - public boolean removePacketListener(StanzaListener packetListener); - /** * Registers a synchronous stanza(/packet) listener with this connection. A stanza(/packet) listener will be invoked only when * an incoming stanza(/packet) is received. A stanza(/packet) filter determines which packets will be delivered to the listener. If @@ -405,28 +352,6 @@ public interface XMPPConnection { */ public void removePacketInterceptor(StanzaListener packetInterceptor); - /** - * Returns the current value of the reply timeout in milliseconds for request for this - * XMPPConnection instance. - * - * @return the stanza(/packet) reply timeout in milliseconds - * @deprecated use {@link #getReplyTimeout()} instead. - */ - @Deprecated - // TODO Remove in Smack 4.3 - public long getPacketReplyTimeout(); - - /** - * Set the stanza(/packet) reply timeout in milliseconds. In most cases, Smack will throw a - * {@link NoResponseException} if no reply to a request was received within the timeout period. - * - * @param timeout the stanza(/packet) reply timeout in milliseconds - * @deprecated use {@link #setReplyTimeout(long)} instead. - */ - @Deprecated - // TODO Remove in Smack 4.3 - public void setPacketReplyTimeout(long timeout); - /** * Returns the current value of the reply timeout in milliseconds for request for this * XMPPConnection instance. diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/admin/ServiceAdministrationManager.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/admin/ServiceAdministrationManager.java index 369ea7ce9..e133bc805 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/admin/ServiceAdministrationManager.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/admin/ServiceAdministrationManager.java @@ -62,7 +62,7 @@ public class ServiceAdministrationManager extends Manager { } public RemoteCommand addUser() { - return addUser(connection().getServiceName()); + return addUser(connection().getXMPPServiceDomain()); } public RemoteCommand addUser(Jid service) { @@ -90,7 +90,7 @@ public class ServiceAdministrationManager extends Manager { } public RemoteCommand deleteUser() { - return deleteUser(connection().getServiceName()); + return deleteUser(connection().getXMPPServiceDomain()); } public RemoteCommand deleteUser(Jid service) { diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/ping/PingManager.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/ping/PingManager.java index 79dca32bf..2eb2452bb 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/ping/PingManager.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/ping/PingManager.java @@ -151,7 +151,7 @@ public final class PingManager extends Manager { private boolean isValidErrorPong(Jid destinationJid, XMPPErrorException xmppErrorException) { // If it is an error error response and the destination was our own service, then this must mean that the // service responded, i.e. is up and pingable. - if (destinationJid.equals(connection().getServiceName())) { + if (destinationJid.equals(connection().getXMPPServiceDomain())) { return true; } diff --git a/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/IntTestUtil.java b/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/IntTestUtil.java index f38e18a36..ff91480de 100644 --- a/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/IntTestUtil.java +++ b/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/IntTestUtil.java @@ -1,6 +1,6 @@ /** * - * Copyright 2015-2016 Florian Schmaus + * Copyright 2015-2017 Florian Schmaus * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -74,7 +74,7 @@ public class IntTestUtil { ServiceAdministrationManager adminManager = ServiceAdministrationManager.getInstanceFor(connection); - EntityBareJid userJid = JidCreate.entityBareFrom(Localpart.from(username), connection.getServiceName()); + EntityBareJid userJid = JidCreate.entityBareFrom(Localpart.from(username), connection.getXMPPServiceDomain()); adminManager.addUser(userJid, password); connection.disconnect(); From f4391c07d7e05d3932e73fb3172e6f346d2b8087 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Sat, 12 Aug 2017 17:35:20 +0200 Subject: [PATCH 3/3] Rework SmackFuture and add async API based on it --- .../smack/AbstractXMPPConnection.java | 82 ++++++++ .../org/jivesoftware/smack/SmackFuture.java | 192 ++++++++++++++---- .../jivesoftware/smack/XMPPConnection.java | 43 ++++ .../smack/util/CallbackRecipient.java | 25 +++ .../smack/util/ExceptionCallback.java | 23 +++ .../smack/{ => util}/SuccessCallback.java | 2 +- .../jivesoftware/smack/SmackFutureTest.java | 15 +- .../jivesoftware/smackx/ping/PingManager.java | 33 +-- 8 files changed, 349 insertions(+), 66 deletions(-) create mode 100644 smack-core/src/main/java/org/jivesoftware/smack/util/CallbackRecipient.java create mode 100644 smack-core/src/main/java/org/jivesoftware/smack/util/ExceptionCallback.java rename smack-core/src/main/java/org/jivesoftware/smack/{ => util}/SuccessCallback.java (94%) diff --git a/smack-core/src/main/java/org/jivesoftware/smack/AbstractXMPPConnection.java b/smack-core/src/main/java/org/jivesoftware/smack/AbstractXMPPConnection.java index 4b6b73e5c..484e07873 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/AbstractXMPPConnection.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/AbstractXMPPConnection.java @@ -49,6 +49,7 @@ import org.jivesoftware.smack.SmackException.NotConnectedException; import org.jivesoftware.smack.SmackException.ResourceBindingNotOfferedException; import org.jivesoftware.smack.SmackException.SecurityRequiredByClientException; import org.jivesoftware.smack.SmackException.SecurityRequiredException; +import org.jivesoftware.smack.SmackFuture.InternalSmackFuture; import org.jivesoftware.smack.XMPPException.StreamErrorException; import org.jivesoftware.smack.XMPPException.XMPPErrorException; import org.jivesoftware.smack.compress.packet.Compress; @@ -1489,6 +1490,87 @@ public abstract class AbstractXMPPConnection implements XMPPConnection { getReplyTimeout()); } + @Override + public SmackFuture sendIqRequestAsync(IQ request) { + return sendIqRequestAsync(request, getReplyTimeout()); + } + + @Override + public SmackFuture sendIqRequestAsync(IQ request, long timeout) { + StanzaFilter replyFilter = new IQReplyFilter(request, this); + return sendAsync(request, replyFilter, timeout); + } + + @Override + public SmackFuture sendAsync(S stanza, final StanzaFilter replyFilter) { + return sendAsync(stanza, replyFilter, getReplyTimeout()); + } + + @SuppressWarnings("FutureReturnValueIgnored") + @Override + public SmackFuture sendAsync(S stanza, final StanzaFilter replyFilter, long timeout) { + Objects.requireNonNull(stanza, "stanza must not be null"); + // While Smack allows to add PacketListeners with a PacketFilter value of 'null', we + // disallow it here in the async API as it makes no sense + Objects.requireNonNull(replyFilter, "replyFilter must not be null"); + + final InternalSmackFuture future = new InternalSmackFuture<>(); + + final StanzaListener stanzaListener = new StanzaListener() { + @Override + public void processStanza(Stanza stanza) throws NotConnectedException, InterruptedException { + boolean removed = removeAsyncStanzaListener(this); + if (!removed) { + // We lost a race against the "no response" handling runnable. Avoid calling the callback, as the + // exception callback will be invoked (if any). + return; + } + try { + XMPPErrorException.ifHasErrorThenThrow(stanza); + @SuppressWarnings("unchecked") + S s = (S) stanza; + future.setResult(s); + } + catch (XMPPErrorException exception) { + future.setException(exception); + } + } + }; + removeCallbacksService.schedule(new Runnable() { + @Override + public void run() { + boolean removed = removeAsyncStanzaListener(stanzaListener); + if (!removed) { + // We lost a race against the stanza listener, he already removed itself because he received a + // reply. There is nothing more to do here. + return; + } + + // If the packetListener got removed, then it was never run and + // we never received a response, inform the exception callback + Exception exception; + if (!isConnected()) { + // If the connection is no longer connected, throw a not connected exception. + exception = new NotConnectedException(AbstractXMPPConnection.this, replyFilter); + } + else { + exception = NoResponseException.newWith(AbstractXMPPConnection.this, replyFilter); + } + future.setException(exception); + } + }, timeout, TimeUnit.MILLISECONDS); + + addAsyncStanzaListener(stanzaListener, replyFilter); + try { + sendStanza(stanza); + } + catch (NotConnectedException | InterruptedException exception) { + future.setException(exception); + } + + return future; + } + @SuppressWarnings("FutureReturnValueIgnored") @Override public void sendStanzaWithResponseCallback(Stanza stanza, final StanzaFilter replyFilter, diff --git a/smack-core/src/main/java/org/jivesoftware/smack/SmackFuture.java b/smack-core/src/main/java/org/jivesoftware/smack/SmackFuture.java index 36dbda2cb..076a16d4c 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/SmackFuture.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/SmackFuture.java @@ -16,25 +16,34 @@ */ package org.jivesoftware.smack; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.CancellationException; import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; import java.util.concurrent.Future; +import java.util.concurrent.RejectedExecutionHandler; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; -import org.jivesoftware.smack.SmackException.NotConnectedException; import org.jivesoftware.smack.packet.Stanza; +import org.jivesoftware.smack.util.CallbackRecipient; +import org.jivesoftware.smack.util.ExceptionCallback; +import org.jivesoftware.smack.util.SuccessCallback; -public abstract class SmackFuture implements Future { +public abstract class SmackFuture implements Future, CallbackRecipient { private boolean cancelled; - private V result; + protected V result; - protected Exception exception; + protected E exception; private SuccessCallback successCallback; - private ExceptionCallback exceptionCallback; + private ExceptionCallback exceptionCallback; @Override public synchronized final boolean cancel(boolean mayInterruptIfRunning) { @@ -43,6 +52,11 @@ public abstract class SmackFuture implements Future { } cancelled = true; + + if (mayInterruptIfRunning) { + notifyAll(); + } + return true; } @@ -56,37 +70,62 @@ public abstract class SmackFuture implements Future { return result != null; } - public void onSuccessOrError(SuccessCallback successCallback, ExceptionCallback exceptionCallback) { + @Override + public CallbackRecipient onSuccess(SuccessCallback successCallback) { this.successCallback = successCallback; - this.exceptionCallback = exceptionCallback; - maybeInvokeCallbacks(); + return this; } - public void onSuccess(SuccessCallback successCallback) { - onSuccessOrError(successCallback, null); + @Override + public CallbackRecipient onError(ExceptionCallback exceptionCallback) { + this.exceptionCallback = exceptionCallback; + maybeInvokeCallbacks(); + return this; } - public void onError(ExceptionCallback exceptionCallback) { - onSuccessOrError(null, exceptionCallback); - } - - private final V getResultOrThrow() throws ExecutionException { - assert (result != null || exception != null); + private final V getOrThrowExecutionException() throws ExecutionException { + assert (result != null || exception != null || cancelled); if (result != null) { return result; } + if (exception != null) { + throw new ExecutionException(exception); + } - throw new ExecutionException(exception); + assert (cancelled); + throw new CancellationException(); } @Override public synchronized final V get() throws InterruptedException, ExecutionException { - while (result == null && exception == null) { + while (result == null && exception == null && !cancelled) { wait(); } - return getResultOrThrow(); + return getOrThrowExecutionException(); + } + + public synchronized final V getOrThrow() throws E { + while (result == null && exception == null && !cancelled) { + try { + wait(); + } + catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + if (exception != null) { + throw exception; + } + + if (cancelled) { + throw new CancellationException(); + } + + assert result != null; + return result; } @Override @@ -100,45 +139,101 @@ public abstract class SmackFuture implements Future { } } + if (cancelled) { + throw new CancellationException(); + } + if (result == null || exception == null) { throw new TimeoutException(); } - return getResultOrThrow(); + return getOrThrowExecutionException(); } + private static final ExecutorService EXECUTOR_SERVICE; + + static { + ThreadFactory threadFactory = new ThreadFactory() { + @Override + public Thread newThread(Runnable r) { + Thread thread = new Thread(r); + thread.setDaemon(true); + thread.setName("SmackFuture Thread"); + return thread; + } + }; + BlockingQueue blockingQueue = new ArrayBlockingQueue<>(128); + RejectedExecutionHandler rejectedExecutionHandler = new RejectedExecutionHandler() { + @Override + public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) { + r.run(); + } + }; + int cores = Runtime.getRuntime().availableProcessors(); + int maximumPoolSize = cores <= 4 ? 2 : cores; + ExecutorService executorService = new ThreadPoolExecutor(0, maximumPoolSize, 60L, TimeUnit.SECONDS, + blockingQueue, threadFactory, rejectedExecutionHandler); + + EXECUTOR_SERVICE = executorService; + } + + @SuppressWarnings("FutureReturnValueIgnored") protected final synchronized void maybeInvokeCallbacks() { + if (cancelled) { + return; + } + if (result != null && successCallback != null) { - successCallback.onSuccess(result); - } else if (exception != null && exceptionCallback != null) { - exceptionCallback.processException(exception); + EXECUTOR_SERVICE.submit(new Runnable() { + @Override + public void run() { + successCallback.onSuccess(result); + } + }); + } + else if (exception != null && exceptionCallback != null) { + EXECUTOR_SERVICE.submit(new Runnable() { + @Override + public void run() { + exceptionCallback.processException(exception); + } + }); } } - /** - * This method checks if the given exception is not fatal. If this method returns false, then - * the future will automatically set the given exception as failure reason and notify potential waiting threads. - * - * @param exception the exception to check. - * @return true if the exception is not fatal, false otherwise. - */ - protected abstract boolean isNonFatalException(Exception exception); + public static class InternalSmackFuture extends SmackFuture { + public final synchronized void setResult(V result) { + this.result = result; + this.notifyAll(); - protected abstract void handleStanza(Stanza stanza) throws NotConnectedException, InterruptedException; + maybeInvokeCallbacks(); + } - protected final void setResult(V result) { - assert (Thread.holdsLock(this)); + public final synchronized void setException(E exception) { + this.exception = exception; + this.notifyAll(); - this.result = result; - this.notifyAll(); - - maybeInvokeCallbacks(); + maybeInvokeCallbacks(); + } } - public static abstract class InternalSmackFuture extends SmackFuture implements StanzaListener, ExceptionCallback { + public static abstract class InternalProcessStanzaSmackFuture extends InternalSmackFuture + implements StanzaListener, ExceptionCallback { + + /** + * This method checks if the given exception is not fatal. If this method returns false, + * then the future will automatically set the given exception as failure reason and notify potential waiting + * threads. + * + * @param exception the exception to check. + * @return true if the exception is not fatal, false otherwise. + */ + protected abstract boolean isNonFatalException(E exception); + + protected abstract void handleStanza(Stanza stanza); @Override - public synchronized final void processException(Exception exception) { + public synchronized final void processException(E exception) { if (!isNonFatalException(exception)) { this.exception = exception; this.notifyAll(); @@ -151,20 +246,29 @@ public abstract class SmackFuture implements Future { * Wrapper method for {@link #handleStanza(Stanza)}. Note that this method is synchronized. */ @Override - public synchronized final void processStanza(Stanza stanza) throws NotConnectedException, InterruptedException { + public synchronized final void processStanza(Stanza stanza) { handleStanza(stanza); } } /** - * A simple version of InternalSmackFuture which implements {@link #isNonFatalException(Exception)} as always returning false method. + * A simple version of InternalSmackFuture which implements isNonFatalException(E) as always returning + * false method. * * @param */ - public static abstract class SimpleInternalSmackFuture extends InternalSmackFuture { + public static abstract class SimpleInternalProcessStanzaSmackFuture + extends InternalProcessStanzaSmackFuture { @Override - protected boolean isNonFatalException(Exception exception) { + protected boolean isNonFatalException(E exception) { return false; } } + + public static SmackFuture from(V result) { + InternalSmackFuture future = new InternalSmackFuture<>(); + future.setResult(result); + return future; + } + } diff --git a/smack-core/src/main/java/org/jivesoftware/smack/XMPPConnection.java b/smack-core/src/main/java/org/jivesoftware/smack/XMPPConnection.java index 10894237f..ce4c2f138 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/XMPPConnection.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/XMPPConnection.java @@ -428,6 +428,43 @@ public interface XMPPConnection { */ public boolean hasFeature(String element, String namespace); + + /** + * Send an IQ request asynchronously. The connection's default reply timeout will be used. + * + * @param request the IQ request to send. + * @return a SmackFuture for the response. + */ + public SmackFuture sendIqRequestAsync(IQ request); + + /** + * Send an IQ request asynchronously. + * + * @param request the IQ request to send. + * @param timeout the reply timeout in milliseconds. + * @return a SmackFuture for the response. + */ + public SmackFuture sendIqRequestAsync(IQ request, long timeout); + + /** + * Send a stanza asynchronously, waiting for exactly one response stanza using the given reply filter. The connection's default reply timeout will be used. + * + * @param stanza the stanza to send. + * @param replyFilter the filter used for the response stanza. + * @return a SmackFuture for the response. + */ + public SmackFuture sendAsync(S stanza, StanzaFilter replyFilter); + + /** + * Send a stanza asynchronously, waiting for exactly one response stanza using the given reply filter. + * + * @param stanza the stanza to send. + * @param replyFilter the filter used for the response stanza. + * @param timeout the reply timeout in milliseconds. + * @return a SmackFuture for the response. + */ + public SmackFuture sendAsync(S stanza, StanzaFilter replyFilter, long timeout); + /** * Send a stanza and wait asynchronously for a response by using replyFilter. *

@@ -442,6 +479,7 @@ public interface XMPPConnection { * @throws NotConnectedException * @throws InterruptedException */ + // TODO: Mark deprecated in favor of the new SmackFuture based async API. public void sendStanzaWithResponseCallback(Stanza stanza, StanzaFilter replyFilter, StanzaListener callback) throws NotConnectedException, InterruptedException; @@ -460,6 +498,7 @@ public interface XMPPConnection { * @throws NotConnectedException * @throws InterruptedException */ + // TODO: Mark deprecated in favor of the new SmackFuture based async API. And do not forget to mark smack.ExceptionCallback deprecated too. public void sendStanzaWithResponseCallback(Stanza stanza, StanzaFilter replyFilter, StanzaListener callback, ExceptionCallback exceptionCallback) throws NotConnectedException, InterruptedException; @@ -479,6 +518,7 @@ public interface XMPPConnection { * @throws NotConnectedException * @throws InterruptedException */ + // TODO: Mark deprecated in favor of the new SmackFuture based async API. public void sendStanzaWithResponseCallback(Stanza stanza, StanzaFilter replyFilter, final StanzaListener callback, final ExceptionCallback exceptionCallback, long timeout) throws NotConnectedException, InterruptedException; @@ -493,6 +533,7 @@ public interface XMPPConnection { * @throws NotConnectedException * @throws InterruptedException */ + // TODO: Mark deprecated in favor of the new SmackFuture based async API. public void sendIqWithResponseCallback(IQ iqRequest, StanzaListener callback) throws NotConnectedException, InterruptedException; /** @@ -509,6 +550,7 @@ public interface XMPPConnection { * @throws NotConnectedException * @throws InterruptedException */ + // TODO: Mark deprecated in favor of the new SmackFuture based async API. public void sendIqWithResponseCallback(IQ iqRequest, StanzaListener callback, ExceptionCallback exceptionCallback) throws NotConnectedException, InterruptedException; @@ -527,6 +569,7 @@ public interface XMPPConnection { * @throws NotConnectedException * @throws InterruptedException */ + // TODO: Mark deprecated in favor of the new SmackFuture based async API. public void sendIqWithResponseCallback(IQ iqRequest, final StanzaListener callback, final ExceptionCallback exceptionCallback, long timeout) throws NotConnectedException, InterruptedException; diff --git a/smack-core/src/main/java/org/jivesoftware/smack/util/CallbackRecipient.java b/smack-core/src/main/java/org/jivesoftware/smack/util/CallbackRecipient.java new file mode 100644 index 000000000..18d631361 --- /dev/null +++ b/smack-core/src/main/java/org/jivesoftware/smack/util/CallbackRecipient.java @@ -0,0 +1,25 @@ +/** + * + * Copyright 2017 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.util; + +public interface CallbackRecipient { + + CallbackRecipient onSuccess(SuccessCallback successCallback); + + CallbackRecipient onError(ExceptionCallback exceptionCallback); + +} diff --git a/smack-core/src/main/java/org/jivesoftware/smack/util/ExceptionCallback.java b/smack-core/src/main/java/org/jivesoftware/smack/util/ExceptionCallback.java new file mode 100644 index 000000000..52ffb4454 --- /dev/null +++ b/smack-core/src/main/java/org/jivesoftware/smack/util/ExceptionCallback.java @@ -0,0 +1,23 @@ +/** + * + * Copyright 2017 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.util; + +public interface ExceptionCallback { + + public void processException(E exception); + +} diff --git a/smack-core/src/main/java/org/jivesoftware/smack/SuccessCallback.java b/smack-core/src/main/java/org/jivesoftware/smack/util/SuccessCallback.java similarity index 94% rename from smack-core/src/main/java/org/jivesoftware/smack/SuccessCallback.java rename to smack-core/src/main/java/org/jivesoftware/smack/util/SuccessCallback.java index 49106a9f1..ba4d24162 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/SuccessCallback.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/util/SuccessCallback.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.jivesoftware.smack; +package org.jivesoftware.smack.util; public interface SuccessCallback { diff --git a/smack-core/src/test/java/org/jivesoftware/smack/SmackFutureTest.java b/smack-core/src/test/java/org/jivesoftware/smack/SmackFutureTest.java index 90bb7cb20..a8f4a8442 100644 --- a/smack-core/src/test/java/org/jivesoftware/smack/SmackFutureTest.java +++ b/smack-core/src/test/java/org/jivesoftware/smack/SmackFutureTest.java @@ -22,9 +22,8 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; -import org.jivesoftware.smack.SmackException.NotConnectedException; -import org.jivesoftware.smack.SmackFuture.InternalSmackFuture; -import org.jivesoftware.smack.SmackFuture.SimpleInternalSmackFuture; +import org.jivesoftware.smack.SmackFuture.InternalProcessStanzaSmackFuture; +import org.jivesoftware.smack.SmackFuture.SimpleInternalProcessStanzaSmackFuture; import org.jivesoftware.smack.packet.Stanza; import org.junit.Test; @@ -32,10 +31,10 @@ import org.junit.Test; public class SmackFutureTest { @Test - public void simpleSmackFutureSuccessTest() throws NotConnectedException, InterruptedException, ExecutionException { - InternalSmackFuture future = new SimpleInternalSmackFuture() { + public void simpleSmackFutureSuccessTest() throws InterruptedException, ExecutionException { + InternalProcessStanzaSmackFuture future = new SimpleInternalProcessStanzaSmackFuture() { @Override - protected void handleStanza(Stanza stanza) throws NotConnectedException, InterruptedException { + protected void handleStanza(Stanza stanza) { setResult(true); } }; @@ -47,9 +46,9 @@ public class SmackFutureTest { @Test(expected = TimeoutException.class) public void simpleSmackFutureTimeoutTest() throws InterruptedException, ExecutionException, TimeoutException { - InternalSmackFuture future = new SimpleInternalSmackFuture() { + InternalProcessStanzaSmackFuture future = new SimpleInternalProcessStanzaSmackFuture() { @Override - protected void handleStanza(Stanza stanza) throws NotConnectedException, InterruptedException { + protected void handleStanza(Stanza stanza) { } }; diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/ping/PingManager.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/ping/PingManager.java index 2eb2452bb..958ef7cc5 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/ping/PingManager.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/ping/PingManager.java @@ -33,9 +33,8 @@ import org.jivesoftware.smack.Manager; import org.jivesoftware.smack.SmackException; import org.jivesoftware.smack.SmackException.NoResponseException; import org.jivesoftware.smack.SmackException.NotConnectedException; -import org.jivesoftware.smack.SmackException.NotLoggedInException; import org.jivesoftware.smack.SmackFuture; -import org.jivesoftware.smack.SmackFuture.InternalSmackFuture; +import org.jivesoftware.smack.SmackFuture.InternalProcessStanzaSmackFuture; import org.jivesoftware.smack.XMPPConnection; import org.jivesoftware.smack.XMPPConnectionRegistry; import org.jivesoftware.smack.XMPPException.XMPPErrorException; @@ -45,7 +44,9 @@ import org.jivesoftware.smack.packet.IQ; import org.jivesoftware.smack.packet.IQ.Type; import org.jivesoftware.smack.packet.Stanza; import org.jivesoftware.smack.packet.XMPPError; +import org.jivesoftware.smack.util.ExceptionCallback; import org.jivesoftware.smack.util.SmackExecutorThreadFactory; +import org.jivesoftware.smack.util.SuccessCallback; import org.jivesoftware.smackx.disco.ServiceDiscoveryManager; import org.jivesoftware.smackx.ping.packet.Ping; @@ -178,14 +179,14 @@ public final class PingManager extends Manager { return type == XMPPError.Type.CANCEL && condition == XMPPError.Condition.feature_not_implemented; } - public SmackFuture pingAsync(Jid jid) { + public SmackFuture pingAsync(Jid jid) { return pingAsync(jid, connection().getReplyTimeout()); } - public SmackFuture pingAsync(final Jid jid, long pongTimeout) { - final InternalSmackFuture future = new InternalSmackFuture() { + public SmackFuture pingAsync(final Jid jid, long pongTimeout) { + final InternalProcessStanzaSmackFuture future = new InternalProcessStanzaSmackFuture() { @Override - public void handleStanza(Stanza packet) throws NotConnectedException, InterruptedException { + public void handleStanza(Stanza packet) { setResult(true); } @Override @@ -202,13 +203,19 @@ public final class PingManager extends Manager { }; Ping ping = new Ping(jid); - try { - XMPPConnection connection = getAuthenticatedConnectionOrThrow(); - connection.sendIqWithResponseCallback(ping, future, future, pongTimeout); - } - catch (NotLoggedInException | NotConnectedException | InterruptedException e) { - future.processException(e); - } + connection().sendIqRequestAsync(ping, pongTimeout) + .onSuccess(new SuccessCallback() { + @Override + public void onSuccess(IQ result) { + future.processStanza(result); + } + }) + .onError(new ExceptionCallback() { + @Override + public void processException(Exception exception) { + future.processException(exception); + } + }); return future; }