Add BundleAndDeferCallback to smack-tcp

This commit is contained in:
Florian Schmaus 2015-02-18 14:38:56 +01:00
parent aa8daba1cf
commit d415661e35
3 changed files with 144 additions and 0 deletions

View File

@ -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();
}
}
}

View File

@ -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);
}

View File

@ -125,6 +125,7 @@ import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Level;
import java.util.logging.Logger;
@ -184,6 +185,10 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
private final SynchronizationPoint<XMPPException> compressSyncPoint = new SynchronizationPoint<XMPPException>(
this);
private static BundleAndDeferCallback defaultBundleAndDeferCallback;
private BundleAndDeferCallback bundleAndDeferCallback = defaultBundleAndDeferCallback;
private static boolean useSmDefault = false;
private static boolean useSmResumptionDefault = true;
@ -1269,6 +1274,30 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
if (element == null) {
continue;
}
// Get a local version of the bundle and defer callback, in case it's unset
// between the null check and the method invocation
final BundleAndDeferCallback localBundleAndDeferCallback = bundleAndDeferCallback;
// If the preconditions are given (e.g. bundleAndDefer callback is set, queue is
// empty), then we could wait a bit for further stanzas attempting to decrease
// our energy consumption
if (localBundleAndDeferCallback != null && isAuthenticated() && queue.isEmpty()) {
final AtomicBoolean bundlingAndDeferringStopped = new AtomicBoolean();
final int bundleAndDeferMillis = localBundleAndDeferCallback.getBundleAndDeferMillis(new BundleAndDefer(
bundlingAndDeferringStopped));
if (bundleAndDeferMillis > 0) {
long remainingWait = bundleAndDeferMillis;
final long waitStart = System.currentTimeMillis();
synchronized (bundlingAndDeferringStopped) {
while (!bundlingAndDeferringStopped.get() && remainingWait > 0) {
bundlingAndDeferringStopped.wait(remainingWait);
remainingWait = bundleAndDeferMillis
- (System.currentTimeMillis() - waitStart);
}
}
}
}
Stanza packet = null;
if (element instanceof Stanza) {
packet = (Stanza) element;
@ -1709,4 +1738,31 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
serverHandledStanzasCount = handledCount;
}
/**
* Set the default bundle and defer callback used for new connections.
*
* @param defaultBundleAndDeferCallback
* @see BundleAndDeferCallback
* @since 4.1
*/
public static void setDefaultBundleAndDeferCallback(BundleAndDeferCallback defaultBundleAndDeferCallback) {
XMPPTCPConnection.defaultBundleAndDeferCallback = defaultBundleAndDeferCallback;
}
/**
* Set the bundle and defer callback used for this connection.
* <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;
}
}