diff --git a/smack-core/src/main/java/org/jivesoftware/smack/SynchronizationPoint.java b/smack-core/src/main/java/org/jivesoftware/smack/SynchronizationPoint.java index 678097d3d..3402299ce 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/SynchronizationPoint.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/SynchronizationPoint.java @@ -1,6 +1,6 @@ /** * - * Copyright © 2014 Florian Schmaus + * Copyright © 2014-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. @@ -41,6 +41,11 @@ public class SynchronizationPoint { private State state; private E failureException; + /** + * Construct a new synchronization point for the given connection. + * + * @param connection the connection of this synchronization point. + */ public SynchronizationPoint(AbstractXMPPConnection connection) { this.connection = connection; this.connectionLock = connection.getConnectionLock(); @@ -48,6 +53,9 @@ public class SynchronizationPoint { init(); } + /** + * Initialize (or reset) this synchronization point. + */ public void init() { connectionLock.lock(); state = State.Initial; @@ -55,6 +63,13 @@ public class SynchronizationPoint { connectionLock.unlock(); } + /** + * Send the given top level stream element and wait for a response. + * + * @param request the plain stream element to send. + * @throws NoResponseException if no response was received. + * @throws NotConnectedException if the connection is not connected. + */ public void sendAndWaitForResponse(TopLevelStreamElement request) throws NoResponseException, NotConnectedException, InterruptedException { assert (state == State.Initial); @@ -79,6 +94,14 @@ public class SynchronizationPoint { checkForResponse(); } + /** + * Send the given plain stream element and wait for a response. + * + * @param request the plain stream element to send. + * @throws E if an failure was reported. + * @throws NoResponseException if no response was received. + * @throws NotConnectedException if the connection is not connected. + */ public void sendAndWaitForResponseOrThrow(PlainStreamElement request) throws E, NoResponseException, NotConnectedException, InterruptedException { sendAndWaitForResponse(request); @@ -93,6 +116,11 @@ public class SynchronizationPoint { } } + /** + * Check if this synchronization point is successful or wait the connections reply timeout. + * @throws NoResponseException if there was no response marking the synchronization point as success or failed. + * @throws E if there was a failure + */ public void checkIfSuccessOrWaitOrThrow() throws NoResponseException, E { checkIfSuccessOrWait(); if (state == State.Failure) { @@ -100,6 +128,10 @@ public class SynchronizationPoint { } } + /** + * Check if this synchronization point is successful or wait the connections reply timeout. + * @throws NoResponseException if there was no response marking the synchronization point as success or failed. + */ public void checkIfSuccessOrWait() throws NoResponseException { connectionLock.lock(); try { @@ -114,33 +146,52 @@ public class SynchronizationPoint { checkForResponse(); } + /** + * Report this synchronization point as successful. + */ public void reportSuccess() { connectionLock.lock(); try { state = State.Success; - condition.signal(); + condition.signalAll(); } finally { connectionLock.unlock(); } } + /** + * Deprecated. + * @deprecated use {@link #reportFailure(Exception)} instead. + */ + @Deprecated public void reportFailure() { reportFailure(null); } + /** + * Report this synchronization point as failed because of the given exception. The {@code failureException} must be set. + * + * @param failureException the exception causing this synchronization point to fail. + */ public void reportFailure(E failureException) { + assert failureException != null; connectionLock.lock(); try { state = State.Failure; this.failureException = failureException; - condition.signal(); + condition.signalAll(); } finally { connectionLock.unlock(); } } + /** + * Check if this synchronization point was successful. + * + * @return true if the synchronization point was successful, false otherwise. + */ public boolean wasSuccessful() { connectionLock.lock(); try { @@ -151,6 +202,11 @@ public class SynchronizationPoint { } } + /** + * Check if this synchronization point has its request already sent. + * + * @return true if the request was already sent, false otherwise. + */ public boolean requestSent() { connectionLock.lock(); try { @@ -161,16 +217,21 @@ public class SynchronizationPoint { } } + /** + * Wait for the condition to become something else as {@link State#RequestSent} or {@link State#Initial}. + * {@link #reportSuccess()}, {@link #reportFailure()} and {@link #reportFailure(Exception)} will either set this + * synchronization point to {@link State#Success} or {@link State#Failure}. If none of them is set after the + * connections reply timeout, this method will set the state of {@link State#NoResponse}. + */ private void waitForConditionOrTimeout() { long remainingWait = TimeUnit.MILLISECONDS.toNanos(connection.getPacketReplyTimeout()); while (state == State.RequestSent || state == State.Initial) { try { - remainingWait = condition.awaitNanos( - remainingWait); if (remainingWait <= 0) { state = State.NoResponse; break; } + remainingWait = condition.awaitNanos(remainingWait); } catch (InterruptedException e) { // This InterruptedException could be "spurious wakeups", see javadoc of awaitNanos() LOGGER.log(Level.WARNING, "Thread interrupt while waiting for condition or timeout ignored", e); diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/Item.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/Item.java index 456d0acd4..ae52eccbf 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/Item.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/Item.java @@ -24,21 +24,27 @@ import org.jivesoftware.smackx.pubsub.provider.ItemProvider; * pubsub node. An Item has several properties that are dependent * on the configuration of the node to which it has been or will be published. * - *

An Item received from a node (via {@link LeafNode#getItems()} or {@link LeafNode#addItemEventListener(org.jivesoftware.smackx.pubsub.listener.ItemEventListener)} + *

An Item received from a node (via {@link LeafNode#getItems()} or {@link LeafNode#addItemEventListener(org.jivesoftware.smackx.pubsub.listener.ItemEventListener)}

+ *
    *
  • Will always have an id (either user or server generated) unless node configuration has both * {@link ConfigureForm#isPersistItems()} and {@link ConfigureForm#isDeliverPayloads()}set to false. *
  • Will have a payload if the node configuration has {@link ConfigureForm#isDeliverPayloads()} set * to true, otherwise it will be null. + *
* - *

An Item created to send to a node (via {@link LeafNode#send()} or {@link LeafNode#publish()} + *

An Item created to send to a node (via {@link LeafNode#send()} or {@link LeafNode#publish()}

+ *
    *
  • The id is optional, since the server will generate one if necessary, but should be used if it is * meaningful in the context of the node. This value must be unique within the node that it is sent to, since * resending an item with the same id will overwrite the one that already exists if the items are persisted. *
  • Will require payload if the node configuration has {@link ConfigureForm#isDeliverPayloads()} set - * to true. + * to true. + *
* - *

To customise the payload object being returned from the {@link PayloadItem#getPayload()} method, you can + *

+ * To customise the payload object being returned from the {@link PayloadItem#getPayload()} method, you can * add a custom parser as explained in {@link ItemProvider}. + *

* * @author Robin Collier */ diff --git a/smack-sasl-provided/src/main/java/org/jivesoftware/smack/sasl/provided/SASLDigestMD5Mechanism.java b/smack-sasl-provided/src/main/java/org/jivesoftware/smack/sasl/provided/SASLDigestMD5Mechanism.java index 309c25527..1c0a8370a 100644 --- a/smack-sasl-provided/src/main/java/org/jivesoftware/smack/sasl/provided/SASLDigestMD5Mechanism.java +++ b/smack-sasl-provided/src/main/java/org/jivesoftware/smack/sasl/provided/SASLDigestMD5Mechanism.java @@ -104,6 +104,13 @@ public class SASLDigestMD5Mechanism extends SASLMechanism { String[] keyValue = part.split("="); assert (keyValue.length == 2); String key = keyValue[0]; + // RFC 2831 § 7.1 about the formating of the digest-challenge: + // "The full form is "#element" indicating at least and + // at most elements, each separated by one or more commas + // (",") and OPTIONAL linear white space (LWS)." + // Which means the key value may be preceded by whitespace, + // which is what we remove: *Only the preceding whitespace*. + key = key.replaceFirst("^\\s+", ""); String value = keyValue[1]; if ("nonce".equals(key)) { if (nonce != null) { diff --git a/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XMPPTCPConnection.java b/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XMPPTCPConnection.java index ac512a3b1..575edbb7f 100644 --- a/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XMPPTCPConnection.java +++ b/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XMPPTCPConnection.java @@ -1597,7 +1597,7 @@ public class XMPPTCPConnection extends AbstractXMPPConnection { throw new StreamManagementException.StreamManagementNotEnabledException(); } // Remove the listener after max. 12 hours - final int removeAfterSeconds = Math.min(getMaxSmResumptionTime() + 60, 12 * 60 * 60); + final int removeAfterSeconds = Math.min(getMaxSmResumptionTime(), 12 * 60 * 60); schedule(new Runnable() { @Override public void run() { @@ -1678,8 +1678,10 @@ public class XMPPTCPConnection extends AbstractXMPPConnection { // See if resumption time is over long current = System.currentTimeMillis(); - long maxResumptionMillies = getMaxSmResumptionTime() * 1000; - if (shutdownTimestamp + maxResumptionMillies > current) { + long maxResumptionMillies = ((long) getMaxSmResumptionTime()) * 1000; + if (current > shutdownTimestamp + maxResumptionMillies) { + // Stream resumption is *not* possible if the current timestamp is greater then the greatest timestamp where + // resumption is possible return false; } else { return true; @@ -1688,8 +1690,13 @@ public class XMPPTCPConnection extends AbstractXMPPConnection { /** * Get the maximum resumption time in seconds after which a managed stream can be resumed. + *

+ * This method will return {@link Integer#MAX_VALUE} if neither the client nor the server specify a maximum + * resumption time. Be aware of integer overflows when using this value, e.g. do not add arbitrary values to it + * without checking for overflows before. + *

* - * @return the maximum resumption time in seconds. + * @return the maximum resumption time in seconds or {@link Integer#MAX_VALUE} if none set. */ public int getMaxSmResumptionTime() { int clientResumptionTime = smClientMaxResumptionTime > 0 ? smClientMaxResumptionTime : Integer.MAX_VALUE;