From a5693609b20fe8ca1bcfb517083d26f901e10135 Mon Sep 17 00:00:00 2001 From: Henning Staib Date: Sun, 15 Aug 2010 16:32:09 +0000 Subject: [PATCH] add the ability to register for roster events before logging in (SMACK-156) git-svn-id: http://svn.igniterealtime.org/svn/repos/smack/trunk@11826 b35dd754-fafc-0310-a699-88a17e54d16e --- documentation/roster.html | 6 +- source/org/jivesoftware/smack/Connection.java | 12 +- source/org/jivesoftware/smack/Roster.java | 73 +++++-- .../jivesoftware/smack/XMPPConnection.java | 36 ++-- .../jivesoftware/smack/RosterOfflineTest.java | 92 +++++++++ .../RosterInitializedBeforeConnectTest.java | 34 +++ .../smack/RosterListenerTest.java | 193 ++++++++++++++++++ .../jivesoftware/smack/RosterSmackTest.java | 14 +- .../smack/test/SmackTestCase.java | 97 +++++---- 9 files changed, 469 insertions(+), 88 deletions(-) create mode 100644 test-unit/org/jivesoftware/smack/RosterOfflineTest.java create mode 100644 test/org/jivesoftware/smack/RosterInitializedBeforeConnectTest.java create mode 100644 test/org/jivesoftware/smack/RosterListenerTest.java diff --git a/documentation/roster.html b/documentation/roster.html index 287b50968..632ad2605 100644 --- a/documentation/roster.html +++ b/documentation/roster.html @@ -22,7 +22,7 @@ A roster also allows you to organize users into groups such as "Friends" and etc.

A Roster instance is obtained using the Connection.getRoster() -method, but only after successfully logging into a server. +method.

Roster Entries

@@ -70,7 +70,9 @@ a Roster in the Exodus XMPP client to the right.

The presence information will likely change often, and it's also possible for the roster entries to change or be deleted. -To listen for changing roster and presence data, a RosterListener should be used. +To listen for changing roster and presence data, a RosterListener should be used. +To be informed about all changes to the roster the RosterListener should be registered +before logging into the XMPP server. The following code snippet registers a RosterListener with the Roster that prints any presence changes in the roster to standard out. A normal client would use similar code to update the roster UI with the changing information. diff --git a/source/org/jivesoftware/smack/Connection.java b/source/org/jivesoftware/smack/Connection.java index b1930f5ff..e857254de 100644 --- a/source/org/jivesoftware/smack/Connection.java +++ b/source/org/jivesoftware/smack/Connection.java @@ -419,11 +419,15 @@ public abstract class Connection { } /** - * Returns the roster for the user logged into the server. If the user has not yet - * logged into the server (or if the user is logged in anonymously), this method will return - * null. + * Returns the roster for the user. + *

+ * This method will never return null, instead if the user has not yet logged into + * the server or is logged in anonymously all modifying methods of the returned roster object + * like {@link Roster#createEntry(String, String, String[])}, + * {@link Roster#removeEntry(RosterEntry)} , etc. except adding or removing + * {@link RosterListener}s will throw an IllegalStateException. * - * @return the user's roster, or null if the user has not logged in yet. + * @return the user's roster. */ public abstract Roster getRoster(); diff --git a/source/org/jivesoftware/smack/Roster.java b/source/org/jivesoftware/smack/Roster.java index 8acc67bc4..7539c32c8 100644 --- a/source/org/jivesoftware/smack/Roster.java +++ b/source/org/jivesoftware/smack/Roster.java @@ -62,7 +62,7 @@ public class Roster { private final List rosterListeners; private Map> presenceMap; // The roster is marked as initialized when at least a single roster packet - // has been recieved and processed. + // has been received and processed. boolean rosterInitialized = false; private PresencePacketListener presencePacketListener; @@ -111,8 +111,10 @@ public class Roster { PacketFilter presenceFilter = new PacketTypeFilter(Presence.class); presencePacketListener = new PresencePacketListener(); connection.addPacketListener(presencePacketListener, presenceFilter); + // Listen for connection events - connection.addConnectionListener(new ConnectionListener() { + final ConnectionListener connectionListener = new AbstractConnectionListener() { + public void connectionClosed() { // Changes the presence available contacts to unavailable setOfflinePresences(); @@ -123,18 +125,22 @@ public class Roster { setOfflinePresences(); } - public void reconnectingIn(int seconds) { - // Ignore - } - - public void reconnectionFailed(Exception e) { - // Ignore - } - - public void reconnectionSuccessful() { - // Ignore - } - }); + }; + + // if not connected add listener after successful login + if(!this.connection.isConnected()) { + Connection.addConnectionCreationListener(new ConnectionCreationListener() { + + public void connectionCreated(Connection connection) { + if(connection.equals(Roster.this.connection)) { + Roster.this.connection.addConnectionListener(connectionListener); + } + + } + }); + } else { + connection.addConnectionListener(connectionListener); + } } /** @@ -171,8 +177,17 @@ public class Roster { * Reloads the entire roster from the server. This is an asynchronous operation, * which means the method will return immediately, and the roster will be * reloaded at a later point when the server responds to the reload request. + * + * @throws IllegalStateException if connection is not logged in or logged in anonymously */ public void reload() { + if (!connection.isAuthenticated()) { + throw new IllegalStateException("Not logged in to server."); + } + if (connection.isAnonymous()) { + throw new IllegalStateException("Anonymous users can't have a roster."); + } + connection.sendPacket(new RosterPacket()); } @@ -206,11 +221,19 @@ public class Roster { * * @param name the name of the group. * @return a new group. + * @throws IllegalStateException if connection is not logged in or logged in anonymously */ public RosterGroup createGroup(String name) { + if (!connection.isAuthenticated()) { + throw new IllegalStateException("Not logged in to server."); + } + if (connection.isAnonymous()) { + throw new IllegalStateException("Anonymous users can't have a roster."); + } if (groups.containsKey(name)) { throw new IllegalArgumentException("Group with name " + name + " alread exists."); } + RosterGroup group = new RosterGroup(name, connection); groups.put(name, group); return group; @@ -225,8 +248,16 @@ public class Roster { * @param groups the list of group names the entry will belong to, or null if the * the roster entry won't belong to a group. * @throws XMPPException if an XMPP exception occurs. + * @throws IllegalStateException if connection is not logged in or logged in anonymously */ public void createEntry(String user, String name, String[] groups) throws XMPPException { + if (!connection.isAuthenticated()) { + throw new IllegalStateException("Not logged in to server."); + } + if (connection.isAnonymous()) { + throw new IllegalStateException("Anonymous users can't have a roster."); + } + // Create and send roster entry creation packet. RosterPacket rosterPacket = new RosterPacket(); rosterPacket.setType(IQ.Type.SET); @@ -267,8 +298,16 @@ public class Roster { * * @param entry a roster entry. * @throws XMPPException if an XMPP error occurs. + * @throws IllegalStateException if connection is not logged in or logged in anonymously */ public void removeEntry(RosterEntry entry) throws XMPPException { + if (!connection.isAuthenticated()) { + throw new IllegalStateException("Not logged in to server."); + } + if (connection.isAnonymous()) { + throw new IllegalStateException("Anonymous users can't have a roster."); + } + // Only remove the entry if it's in the entry list. // The actual removal logic takes place in RosterPacketListenerprocess>>Packet(Packet) if (!entries.containsKey(entry.getUser())) { @@ -389,7 +428,7 @@ public class Roster { } /** - * Returns an unmodiable collections of all the roster groups. + * Returns an unmodifiable collections of all the roster groups. * * @return an iterator for all roster groups. */ @@ -859,14 +898,14 @@ public class Roster { // We have the list of old and new group names. We now need to // remove the entry from the all the groups it may no longer belong - // to. We do this by subracting the new group set from the old. + // to. We do this by subtracting the new group set from the old. for (String newGroupName : newGroupNames) { currentGroupNames.remove(newGroupName); } } // Loop through any groups that remain and remove the entries. - // This is neccessary for the case of remote entry removals. + // This is necessary for the case of remote entry removals. for (String groupName : currentGroupNames) { RosterGroup group = getGroup(groupName); group.removeEntryLocal(entry); diff --git a/source/org/jivesoftware/smack/XMPPConnection.java b/source/org/jivesoftware/smack/XMPPConnection.java index 5b7990388..8ccaef2b8 100644 --- a/source/org/jivesoftware/smack/XMPPConnection.java +++ b/source/org/jivesoftware/smack/XMPPConnection.java @@ -119,7 +119,7 @@ public class XMPPConnection extends Connection { } /** - * Creates a new XMPP conection in the same way {@link #XMPPConnection(String,CallbackHandler)} does, but + * Creates a new XMPP connection in the same way {@link #XMPPConnection(String,CallbackHandler)} does, but * with no callback handler for password prompting of the keystore. This will work * in most cases, provided the client is not required to provide a certificate to * the server. @@ -135,7 +135,7 @@ public class XMPPConnection extends Connection { } /** - * Creates a new XMPP conection in the same way {@link #XMPPConnection(ConnectionConfiguration,CallbackHandler)} does, but + * Creates a new XMPP connection in the same way {@link #XMPPConnection(ConnectionConfiguration,CallbackHandler)} does, but * with no callback handler for password prompting of the keystore. This will work * in most cases, provided the client is not required to provide a certificate to * the server. @@ -210,7 +210,7 @@ public class XMPPConnection extends Connection { * @param resource the resource. * @throws XMPPException if an error occurs. * @throws IllegalStateException if not connected to the server, or already logged in - * to the serrver. + * to the server. */ public synchronized void login(String username, String password, String resource) throws XMPPException { if (!isConnected()) { @@ -257,7 +257,11 @@ public class XMPPConnection extends Connection { useCompression(); } - // Create the roster if it is not a reconnection. + // Indicate that we're now authenticated. + authenticated = true; + anonymous = false; + + // Create the roster if it is not a reconnection or roster already created by getRoster() if (this.roster == null) { this.roster = new Roster(this); } @@ -270,11 +274,7 @@ public class XMPPConnection extends Connection { packetWriter.sendPacket(new Presence(Presence.Type.available)); } - // Indicate that we're now authenticated. - authenticated = true; - anonymous = false; - - // Stores the autentication for future reconnection + // Stores the authentication for future reconnection config.setLoginInfo(username, password, resource); // If debugging is enabled, change the the debug window title to include the @@ -294,7 +294,7 @@ public class XMPPConnection extends Connection { * * @throws XMPPException if an error occurs or anonymous logins are not supported by the server. * @throws IllegalStateException if not connected to the server, or already logged in - * to the serrver. + * to the server. */ public synchronized void loginAnonymously() throws XMPPException { if (!isConnected()) { @@ -324,9 +324,6 @@ public class XMPPConnection extends Connection { useCompression(); } - // Anonymous users can't have a roster. - roster = null; - // Set presence to online. packetWriter.sendPacket(new Presence(Presence.Type.available)); @@ -344,9 +341,18 @@ public class XMPPConnection extends Connection { } public Roster getRoster() { - if (roster == null) { - return null; + // synchronize against login() + synchronized(this) { + // if connection is authenticated the roster is already set by login() + // or a previous call to getRoster() + if (!isAuthenticated() || isAnonymous()) { + if (roster == null) { + roster = new Roster(this); + } + return roster; + } } + if (!config.isRosterLoadedAtLogin()) { roster.reload(); } diff --git a/test-unit/org/jivesoftware/smack/RosterOfflineTest.java b/test-unit/org/jivesoftware/smack/RosterOfflineTest.java new file mode 100644 index 000000000..777ee7bed --- /dev/null +++ b/test-unit/org/jivesoftware/smack/RosterOfflineTest.java @@ -0,0 +1,92 @@ +package org.jivesoftware.smack; + +import static org.junit.Assert.*; + +import java.util.Collection; +import java.util.Iterator; + +import org.jivesoftware.smack.Roster.SubscriptionMode; +import org.jivesoftware.smack.packet.Presence; +import org.junit.Before; +import org.junit.Test; + +/** + * Tests the behavior of the roster if the connection is not authenticated yet. + * + * @author Henning Staib + */ +public class RosterOfflineTest { + + Connection connection; + + Roster roster; + + @Before + public void setup() { + this.connection = new XMPPConnection("localhost"); + assertFalse(connection.isConnected()); + + roster = connection.getRoster(); + assertNotNull(roster); + } + + @Test + public void shouldThrowNoExceptionOnGetterMethods() { + // all getter methods should work + assertFalse(roster.contains("test")); + + Collection entries = roster.getEntries(); + assertTrue(entries.size() == 0); + + assertNull(roster.getEntry("test")); + + assertEquals(0, roster.getEntryCount()); + + assertNull(roster.getGroup("test")); + + assertEquals(0, roster.getGroupCount()); + + Collection groups = roster.getGroups(); + assertEquals(0, groups.size()); + + Presence presence = roster.getPresence("test"); + assertEquals(Presence.Type.unavailable, presence.getType()); + + Presence presenceResource = roster.getPresenceResource("test"); + assertEquals(Presence.Type.unavailable, presenceResource.getType()); + + Iterator iterator = roster.getPresences("test"); + assertTrue(iterator.hasNext()); + assertEquals(Presence.Type.unavailable, iterator.next().getType()); + assertFalse(iterator.hasNext()); + + assertEquals(0, roster.getUnfiledEntries().size()); + + assertEquals(0, roster.getUnfiledEntryCount()); + + roster.setSubscriptionMode(SubscriptionMode.accept_all); + assertEquals(SubscriptionMode.accept_all, roster.getSubscriptionMode()); + + } + + @Test(expected = IllegalStateException.class) + public void shouldThrowExceptionOnCreateEntry() throws Exception { + roster.createEntry("test", "test", null); + } + + @Test(expected = IllegalStateException.class) + public void shouldThrowExceptionOnCreateGroup() throws Exception { + roster.createGroup("test"); + } + + @Test(expected = IllegalStateException.class) + public void shouldThrowExceptionOnReload() throws Exception { + roster.reload(); + } + + @Test(expected = IllegalStateException.class) + public void shouldThrowExceptionRemoveEntry() throws Exception { + roster.removeEntry(null); + } + +} diff --git a/test/org/jivesoftware/smack/RosterInitializedBeforeConnectTest.java b/test/org/jivesoftware/smack/RosterInitializedBeforeConnectTest.java new file mode 100644 index 000000000..377e0e225 --- /dev/null +++ b/test/org/jivesoftware/smack/RosterInitializedBeforeConnectTest.java @@ -0,0 +1,34 @@ +package org.jivesoftware.smack; + +/** + * Run all tests defined in RosterTest but initialize the roster before connection is logged in and + * authenticated. + * + * @author Henning Staib + */ +public class RosterInitializedBeforeConnectTest extends RosterSmackTest { + + public RosterInitializedBeforeConnectTest(String name) { + super(name); + } + + protected boolean createOfflineConnections() { + return true; + } + + protected void setUp() throws Exception { + super.setUp(); + + // initialize all rosters before login + for (int i = 0; i < getMaxConnections(); i++) { + XMPPConnection connection = getConnection(i); + assertFalse(connection.isConnected()); + + Roster roster = connection.getRoster(); + assertNotNull(roster); + + connectAndLogin(i); + } + } + +} diff --git a/test/org/jivesoftware/smack/RosterListenerTest.java b/test/org/jivesoftware/smack/RosterListenerTest.java new file mode 100644 index 000000000..254d66d76 --- /dev/null +++ b/test/org/jivesoftware/smack/RosterListenerTest.java @@ -0,0 +1,193 @@ +package org.jivesoftware.smack; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.jivesoftware.smack.packet.Presence; +import org.jivesoftware.smack.test.SmackTestCase; + +/** + * Test cases for adding the {@link RosterListener} in different connection states. + * + * @author Henning Staib + */ +public class RosterListenerTest extends SmackTestCase { + + public RosterListenerTest(String arg0) { + super(arg0); + } + + public void testAddingRosterListenerBeforeConnect() throws Exception { + int inviterIndex = 0; + int inviteeIndex = 1; + XMPPConnection inviterConnection = getConnection(inviterIndex); + connectAndLogin(inviterIndex); + + assertTrue("Inviter is not online", inviterConnection.isConnected()); + + Roster inviterRoster = inviterConnection.getRoster(); + + // add user1 to roster to create roster events stored at XMPP server + inviterRoster.createEntry(getBareJID(inviteeIndex), getUsername(inviteeIndex), null); + + Thread.sleep(500); // wait for XMPP server + + XMPPConnection inviteeConnection = getConnection(inviteeIndex); + assertFalse("Invitee is already online", inviteeConnection.isConnected()); + + // collector for added entries + final List addedEntries = new ArrayList(); + + // register roster listener before login + Roster inviteeRoster = inviteeConnection.getRoster(); + inviteeRoster.addRosterListener(new RosterListener() { + + public void presenceChanged(Presence presence) { + // ignore + } + + public void entriesUpdated(Collection addresses) { + // ignore + } + + public void entriesDeleted(Collection addresses) { + // ignore + } + + public void entriesAdded(Collection addresses) { + addedEntries.addAll(addresses); + } + }); + + // connect after adding the listener + connectAndLogin(inviteeIndex); + + Thread.sleep(500); // wait for packets to be processed + + assertNotNull("inviter is not in roster", inviteeRoster.getEntry(getBareJID(inviterIndex))); + + assertTrue("got no event for adding inviter", + addedEntries.contains(getBareJID(inviterIndex))); + + } + + public void testAddingRosterListenerAfterConnect() throws Exception { + int inviterIndex = 0; + int inviteeIndex = 1; + XMPPConnection inviterConnection = getConnection(inviterIndex); + connectAndLogin(inviterIndex); + assertTrue("Inviter is not online", inviterConnection.isConnected()); + + Roster inviterRoster = inviterConnection.getRoster(); + + // add user1 to roster to create roster events stored at XMPP server + inviterRoster.createEntry(getBareJID(inviteeIndex), getUsername(inviteeIndex), null); + + Thread.sleep(500); // wait for XMPP server + + XMPPConnection inviteeConnection = getConnection(inviteeIndex); + connectAndLogin(inviteeIndex); + assertTrue("Invitee is not online", inviteeConnection.isConnected()); + + // collector for added entries + final List addedEntries = new ArrayList(); + + // wait to simulate concurrency before adding listener + Thread.sleep(200); + + // register roster listener after login + Roster inviteeRoster = inviteeConnection.getRoster(); + inviteeRoster.addRosterListener(new RosterListener() { + + public void presenceChanged(Presence presence) { + // ignore + } + + public void entriesUpdated(Collection addresses) { + // ignore + } + + public void entriesDeleted(Collection addresses) { + // ignore + } + + public void entriesAdded(Collection addresses) { + addedEntries.addAll(addresses); + } + }); + + Thread.sleep(500); // wait for packets to be processed + + assertNotNull("Inviter is not in roster", inviteeRoster.getEntry(getBareJID(inviterIndex))); + + assertFalse("got event for adding inviter", addedEntries.contains(getBareJID(inviterIndex))); + + } + + @Override + protected void tearDown() throws Exception { + cleanUpRoster(); + super.tearDown(); + } + + protected int getMaxConnections() { + return 2; + } + + protected boolean createOfflineConnections() { + return true; + } + + /** + * Clean up all the entries in the roster + */ + private void cleanUpRoster() { + for (int i = 0; i < getMaxConnections(); i++) { + // Delete all the entries from the roster + Roster roster = getConnection(i).getRoster(); + for (RosterEntry entry : roster.getEntries()) { + try { + roster.removeEntry(entry); + Thread.sleep(100); + } + catch (XMPPException e) { + e.printStackTrace(); + fail(e.getMessage()); + } + catch (InterruptedException e) { + // ignore + } + } + + try { + Thread.sleep(700); + } + catch (InterruptedException e) { + fail(e.getMessage()); + } + } + // Wait up to 6 seconds to receive roster removal notifications + long initial = System.currentTimeMillis(); + while (System.currentTimeMillis() - initial < 6000 + && (getConnection(0).getRoster().getEntryCount() != 0 || getConnection(1).getRoster().getEntryCount() != 0)) { + try { + Thread.sleep(100); + } + catch (InterruptedException e) { + } + } + + assertEquals("Wrong number of entries in connection 0", 0, + getConnection(0).getRoster().getEntryCount()); + assertEquals("Wrong number of groups in connection 0", 0, + getConnection(0).getRoster().getGroupCount()); + + assertEquals("Wrong number of entries in connection 1", 0, + getConnection(1).getRoster().getEntryCount()); + assertEquals("Wrong number of groups in connection 1", 0, + getConnection(1).getRoster().getGroupCount()); + + } + +} diff --git a/test/org/jivesoftware/smack/RosterSmackTest.java b/test/org/jivesoftware/smack/RosterSmackTest.java index 3ee5efcaa..17f867257 100644 --- a/test/org/jivesoftware/smack/RosterSmackTest.java +++ b/test/org/jivesoftware/smack/RosterSmackTest.java @@ -446,18 +446,12 @@ public class RosterSmackTest extends SmackTestCase { } assertNull("The group Amigos still exists", roster.getGroup("Amigos")); - assertNotNull("The group with no name does not exist", roster.getGroup("")); + assertNull("The group with no name does exist", roster.getGroup("")); + assertEquals("There are still groups in the roster", 0, roster.getGroupCount()); assertEquals( - "Wrong number of entries in the group with no name", + "Wrong number of unfiled entries", 2, - roster.getGroup("").getEntryCount()); - - /*assertEquals("There are still groups in the roster", 0, roster.getGroupCount()); - assertEquals( - "Wrong number of entries in the group \"\" ", - 2, - roster.getUnfiledEntryCount());*/ - + roster.getUnfiledEntryCount()); Thread.sleep(200); } diff --git a/test/org/jivesoftware/smack/test/SmackTestCase.java b/test/org/jivesoftware/smack/test/SmackTestCase.java index 5caef5f7c..35994e915 100644 --- a/test/org/jivesoftware/smack/test/SmackTestCase.java +++ b/test/org/jivesoftware/smack/test/SmackTestCase.java @@ -23,8 +23,6 @@ import junit.framework.TestCase; import org.jivesoftware.smack.ConnectionConfiguration; import org.jivesoftware.smack.XMPPConnection; import org.jivesoftware.smack.XMPPException; -import org.jivesoftware.smack.packet.XMPPError; -import org.jivesoftware.smack.packet.XMPPError.Type; import org.xmlpull.mxp1.MXParser; import org.xmlpull.v1.XmlPullParser; @@ -96,6 +94,23 @@ public abstract class SmackTestCase extends TestCase { return null; } + /** + * Returns false if the connections initialized by the test case will be + * automatically connected to the XMPP server. + * Returns true if the connections initialized by the test case will + * NOT be connected to the XMPP server. To connect the connections invoke + * {@link #connectAndLogin(int)}. + *

+ * Connections are connected by default. + * Overwrite this method if the test case needs unconnected connections. + * + * @return true if connections should NOT be connected automatically, + * false if connections should be connected automatically. + */ + protected boolean createOfflineConnections() { + return false; + } + /** * Returns the XMPPConnection located at the requested position. Each test case holds a * pool of connections which is initialized while setting up the test case. The maximum @@ -209,7 +224,8 @@ public abstract class SmackTestCase extends TestCase { // Connect to the server for (int i = 0; i < getMaxConnections(); i++) { connections[i] = createConnection(); - connections[i].connect(); + if (!createOfflineConnections()) + connections[i].connect(); } // Use the host name that the server reports. This is a good idea in most // cases, but could fail if the user set a hostname in their XMPP server @@ -217,41 +233,37 @@ public abstract class SmackTestCase extends TestCase { host = connections[0].getHost(); serviceName = connections[0].getServiceName(); - for (int i = 0; i < getMaxConnections(); i++) { - String password = usernamePrefix + (i+1); - String currentUser = password; - - if (passwordPrefix != null) - password = (samePassword ? passwordPrefix : passwordPrefix + (i+1)); - - try - { - getConnection(i).login(currentUser, password, "Smack"); - } - catch (XMPPException e) - { - e.printStackTrace(); - - // Create the test accounts - if (!getConnection(0).getAccountManager().supportsAccountCreation()) - fail("Server does not support account creation"); - - // Create the account and try logging in again as the - // same user. - try - { - createAccount(i, currentUser, password); - } - catch (Exception e1) - { - e1.printStackTrace(); - fail("Could not create user: " + currentUser); - } - i--; - } + if (!createOfflineConnections()) { + for (int i = 0; i < getMaxConnections(); i++) { + String password = usernamePrefix + (i+1); + String currentUser = password; + + if (passwordPrefix != null) + password = (samePassword ? passwordPrefix : passwordPrefix + (i+1)); + + try { + getConnection(i).login(currentUser, password, "Smack"); + } catch (XMPPException e) { + e.printStackTrace(); + + // Create the test accounts + if (!getConnection(0).getAccountManager().supportsAccountCreation()) + fail("Server does not support account creation"); + + // Create the account and try logging in again as the + // same user. + try { + createAccount(i, currentUser, password); + } catch (Exception e1) { + e1.printStackTrace(); + fail("Could not create user: " + currentUser); + } + i--; + } + } + // Let the server process the available presences + Thread.sleep(150); } - // Let the server process the available presences - Thread.sleep(150); } catch (Exception e) { e.printStackTrace(); @@ -261,16 +273,21 @@ public abstract class SmackTestCase extends TestCase { protected void connectAndLogin(int connectionIndex) throws XMPPException { - String password = usernamePrefix + connectionIndex; + String password = usernamePrefix + (connectionIndex + 1); if (passwordPrefix != null) - password = (samePassword ? passwordPrefix : passwordPrefix + connectionIndex); + password = (samePassword ? passwordPrefix : passwordPrefix + (connectionIndex + 1)); XMPPConnection con = getConnection(connectionIndex); if (!con.isConnected()) con.connect(); - con.login(usernamePrefix + connectionIndex, password, "Smack"); + try { + con.login(usernamePrefix + (connectionIndex + 1), password, "Smack"); + } catch (XMPPException e) { + createAccount(connectionIndex, usernamePrefix + (connectionIndex + 1), password); + con.login(usernamePrefix + (connectionIndex + 1), password, "Smack"); + } } protected void disconnect(int connectionIndex) throws XMPPException