Smack 4.1.3

-----BEGIN PGP SIGNATURE-----
 Version: GnuPG v2
 
 iQF8BAABCgBmBQJVpgWiXxSAAAAAAC4AKGlzc3Vlci1mcHJAbm90YXRpb25zLm9w
 ZW5wZ3AuZmlmdGhob3JzZW1hbi5uZXQxMzU3QjAxODY1QjI1MDNDMTg0NTNEMjA4
 Q0FDMkE5Njc4NTQ4RTM1AAoJEIysKpZ4VI41G1gH+gIw/seXSSY6vYlVkYEFtR+e
 LV/LArN/eN1ZGc+WjN0EysRyqOBqF8HVHuyO7fF67huDRn62s7hufVY//NTctJ5L
 m4TXwaEUvgjdul7vm/dZcNRYr0jcSpDTFWx2egkOXt3qE9AhnpbnaIJ5c3q9VVVD
 aba88c3NS7quxp0hQm1SNEAmt1CCMPom7YkxdIPKWlLj8N5AF1UuSKwckLLYSUlS
 wloBbITb6EjI1IwszhN6e6o3W+7Pz/1zbFjk0CkKUS+TmhHhKil8TonH8Se/9DYD
 1SVHxvZa8LHWsU9G/R1Nhl69K2+GHEUbGmXalFmyPIf5ifhYyNimpx9krXQUuLs=
 =3/cn
 -----END PGP SIGNATURE-----

Merge tag '4.1.3'

Smack 4.1.3
This commit is contained in:
Florian Schmaus 2015-07-15 09:37:46 +02:00
commit 1c716bc1e0
5 changed files with 61 additions and 19 deletions

View File

@ -17,7 +17,7 @@ and then send them a text message:
``` ```
// Assume we've created an XMPPConnection name "connection"._ // Assume we've created an XMPPConnection name "connection"._
ChatManager chatmanager = connection.getChatManager(); ChatManager chatmanager = ChatManager.getInstanceFor(connection);
Chat newChat = chatmanager.createChat("jsmith@jivesoftware.com", new MessageListener() { Chat newChat = chatmanager.createChat("jsmith@jivesoftware.com", new MessageListener() {
public void processMessage(Chat chat, Message message) { public void processMessage(Chat chat, Message message) {
System.out.println("Received message: " + message); System.out.println("Received message: " + message);
@ -72,8 +72,9 @@ when it happens. You can register a message listener to receive all future
messages as part of this handler. messages as part of this handler.
``` ```
_// Assume we've created an XMPPConnection name "connection"._ // Assume we've created an XMPPConnection name "connection"._
ChatManager chatmanager = connection.getChatManager().addChatListener( ChatManager chatManager = ChatManager.getInstanceFor(connection);
chatManager.addChatListener(
new ChatManagerListener() { new ChatManagerListener() {
@Override @Override
public void chatCreated(Chat chat, boolean createdLocally) public void chatCreated(Chat chat, boolean createdLocally)

View File

@ -141,6 +141,19 @@ hr {
<div id="pageBody"> <div id="pageBody">
<h2>4.1.3 -- <span style="font-weight: normal;">2015-07-15</span></h2>
<h2> Bug
</h2>
<ul>
<li>[<a href='https://igniterealtime.org/issues/browse/SMACK-679'>SMACK-679</a>] - Memory leak in Socks5BytestreamManager. Should use weak map for &#39;managers&#39;
</li>
<li>[<a href='https://igniterealtime.org/issues/browse/SMACK-680'>SMACK-680</a>] - XHTML bodies are un-escaped after parsing
</li>
<li>[<a href='https://igniterealtime.org/issues/browse/SMACK-681'>SMACK-681</a>] - Roster presence callbacks may not be invoked right after login
</li>
</ul>
<h2>4.1.2 -- <span style="font-weight: normal;">2015-06-27</span></h2> <h2>4.1.2 -- <span style="font-weight: normal;">2015-06-27</span></h2>
<h2> Bug <h2> Bug

View File

@ -477,7 +477,7 @@ public class PacketParserUtils {
} }
break; break;
case XmlPullParser.TEXT: case XmlPullParser.TEXT:
xml.append(parser.getText()); xml.escape(parser.getText());
break; break;
} }
event = parser.next(); event = parser.next();
@ -493,7 +493,12 @@ public class PacketParserUtils {
// Only append the text if the parser is not on on an empty element' start tag. Empty elements are reported // Only append the text if the parser is not on on an empty element' start tag. Empty elements are reported
// twice, so in order to prevent duplication we only add their text when we are on their end tag. // twice, so in order to prevent duplication we only add their text when we are on their end tag.
if (!(event == XmlPullParser.START_TAG && parser.isEmptyElementTag())) { if (!(event == XmlPullParser.START_TAG && parser.isEmptyElementTag())) {
sb.append(parser.getText()); CharSequence text = parser.getText();
if (event == XmlPullParser.TEXT) {
// TODO the toString() can be removed in Smack 4.2.
text = StringUtils.escapeForXML(text.toString());
}
sb.append(text);
} }
if (event == XmlPullParser.END_TAG && parser.getDepth() <= depth) { if (event == XmlPullParser.END_TAG && parser.getDepth() <= depth) {
break outerloop; break outerloop;

View File

@ -20,13 +20,13 @@ import java.io.IOException;
import java.net.Socket; import java.net.Socket;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Random; import java.util.Random;
import java.util.Set; import java.util.Set;
import java.util.WeakHashMap;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeoutException; import java.util.concurrent.TimeoutException;
@ -113,7 +113,7 @@ public final class Socks5BytestreamManager extends Manager implements Bytestream
private final static Random randomGenerator = new Random(); private final static Random randomGenerator = new Random();
/* stores one Socks5BytestreamManager for each XMPP connection */ /* stores one Socks5BytestreamManager for each XMPP connection */
private final static Map<XMPPConnection, Socks5BytestreamManager> managers = new HashMap<XMPPConnection, Socks5BytestreamManager>(); private final static Map<XMPPConnection, Socks5BytestreamManager> managers = new WeakHashMap<>();
/* /*
* assigns a user to a listener that is informed if a bytestream request for this user is * assigns a user to a listener that is informed if a bytestream request for this user is

View File

@ -159,9 +159,16 @@ public final class Roster extends Manager {
*/ */
private final Object rosterListenersAndEntriesLock = new Object(); private final Object rosterListenersAndEntriesLock = new Object();
// The roster is marked as initialized when at least a single roster packet private enum RosterState {
// has been received and processed. uninitialized,
private boolean loaded = false; loading,
loaded,
}
/**
* The current state of the roster.
*/
private RosterState rosterState = RosterState.uninitialized;
private final PresencePacketListener presencePacketListener = new PresencePacketListener(); private final PresencePacketListener presencePacketListener = new PresencePacketListener();
@ -338,9 +345,11 @@ public final class Roster extends Manager {
if (rosterStore != null && isRosterVersioningSupported()) { if (rosterStore != null && isRosterVersioningSupported()) {
packet.setVersion(rosterStore.getRosterVersion()); packet.setVersion(rosterStore.getRosterVersion());
} }
rosterState = RosterState.loading;
connection.sendIqWithResponseCallback(packet, new RosterResultListener(), new ExceptionCallback() { connection.sendIqWithResponseCallback(packet, new RosterResultListener(), new ExceptionCallback() {
@Override @Override
public void processException(Exception exception) { public void processException(Exception exception) {
rosterState = RosterState.uninitialized;
Level logLevel; Level logLevel;
if (exception instanceof NotConnectedException) { if (exception instanceof NotConnectedException) {
logLevel = Level.FINE; logLevel = Level.FINE;
@ -384,16 +393,15 @@ public final class Roster extends Manager {
return true; return true;
} }
boolean waitUntilLoaded() throws InterruptedException { protected boolean waitUntilLoaded() throws InterruptedException {
final XMPPConnection connection = connection(); long waitTime = connection().getPacketReplyTimeout();
while (!loaded) { long start = System.currentTimeMillis();
long waitTime = connection.getPacketReplyTimeout(); while (!isLoaded()) {
long start = System.currentTimeMillis();
if (waitTime <= 0) { if (waitTime <= 0) {
break; break;
} }
synchronized (this) { synchronized (this) {
if (!loaded) { if (!isLoaded()) {
wait(waitTime); wait(waitTime);
} }
} }
@ -411,7 +419,7 @@ public final class Roster extends Manager {
* @since 4.1 * @since 4.1
*/ */
public boolean isLoaded() { public boolean isLoaded() {
return loaded; return rosterState == RosterState.loaded;
} }
/** /**
@ -1059,7 +1067,7 @@ public final class Roster extends Manager {
} }
} }
} }
loaded = false; rosterState = RosterState.uninitialized;
} }
/** /**
@ -1270,6 +1278,21 @@ public final class Roster extends Manager {
@Override @Override
public void processPacket(Stanza packet) throws NotConnectedException, InterruptedException { public void processPacket(Stanza packet) throws NotConnectedException, InterruptedException {
// Try to ensure that the roster is loaded when processing presence stanzas. While the
// presence listener is synchronous, the roster result listener is not, which means that
// the presence listener may be invoked with a not yet loaded roster.
if (rosterState == RosterState.loading) {
try {
waitUntilLoaded();
}
catch (InterruptedException e) {
LOGGER.log(Level.INFO, "Presence listener was interrupted", e);
}
}
if (!isLoaded()) {
LOGGER.warning("Roster not loaded while processing presence stanza");
}
Presence presence = (Presence) packet; Presence presence = (Presence) packet;
Jid from = presence.getFrom(); Jid from = presence.getFrom();
Resourcepart fromResource = Resourcepart.EMPTY; Resourcepart fromResource = Resourcepart.EMPTY;
@ -1409,7 +1432,7 @@ public final class Roster extends Manager {
} }
} }
loaded = true; rosterState = RosterState.loaded;
synchronized (Roster.this) { synchronized (Roster.this) {
Roster.this.notifyAll(); Roster.this.notifyAll();
} }