From 3af86fd462c47b31978fd91881d4ffc85f08dac5 Mon Sep 17 00:00:00 2001 From: Gaston Dombiak Date: Thu, 14 Sep 2006 19:16:40 +0000 Subject: [PATCH] Added reconnection support. SMACK-172 git-svn-id: http://svn.igniterealtime.org/svn/repos/smack/trunk@5367 b35dd754-fafc-0310-a699-88a17e54d16e --- build/resources/META-INF/smack-config.xml | 1 + .../smack/ConnectionConfiguration.java | 68 +++++++ .../smack/ConnectionListener.java | 28 ++- .../org/jivesoftware/smack/PacketReader.java | 63 +++++-- .../org/jivesoftware/smack/PacketWriter.java | 42 +++-- .../smack/PrivacyListManager.java | 11 +- .../smack/SASLAuthentication.java | 21 ++- .../smack/debugger/ConsoleDebugger.java | 20 ++ .../jivesoftware/smack/packet/XMPPError.java | 173 +++++++++--------- .../smackx/ServiceDiscoveryManager.java | 15 +- .../smackx/debugger/EnhancedDebugger.java | 46 +++-- .../debugger/EnhancedDebuggerWindow.java | 21 ++- .../filetransfer/FileTransferNegotiator.java | 37 ++-- .../smackx/muc/MultiUserChat.java | 12 ++ .../smackx/muc/RoomListenerMultiplexor.java | 12 ++ test/org/jivesoftware/smack/LoginTest.java | 19 +- .../smack/MessengerLoginTest.java | 1 + test/org/jivesoftware/smack/PresenceTest.java | 12 +- test/org/jivesoftware/smack/RosterTest.java | 49 ++++- .../smack/test/SmackTestCase.java | 26 +-- .../jivesoftware/smackx/CompressionTest.java | 12 +- .../smackx/muc/MultiUserChatTest.java | 6 +- 22 files changed, 501 insertions(+), 194 deletions(-) diff --git a/build/resources/META-INF/smack-config.xml b/build/resources/META-INF/smack-config.xml index 42874aa98..9d6654de9 100644 --- a/build/resources/META-INF/smack-config.xml +++ b/build/resources/META-INF/smack-config.xml @@ -10,6 +10,7 @@ org.jivesoftware.smackx.muc.MultiUserChat org.jivesoftware.smackx.filetransfer.FileTransferManager org.jivesoftware.smackx.LastActivityManager + org.jivesoftware.smack.ReconnectionManager diff --git a/source/org/jivesoftware/smack/ConnectionConfiguration.java b/source/org/jivesoftware/smack/ConnectionConfiguration.java index bdb3e617a..a613604e8 100644 --- a/source/org/jivesoftware/smack/ConnectionConfiguration.java +++ b/source/org/jivesoftware/smack/ConnectionConfiguration.java @@ -20,6 +20,7 @@ package org.jivesoftware.smack; +import javax.net.SocketFactory; import java.io.File; /** @@ -54,6 +55,16 @@ public class ConnectionConfiguration implements Cloneable { private boolean debuggerEnabled = XMPPConnection.DEBUG_ENABLED; + // Flag that indicates if a reconnection should be attempted when abruptly disconnected + private boolean reconnectionAllowed = true; + + // Holds the socket factory that is used to generate the socket in the connection + private SocketFactory socketFactory; + + // Holds the authentication information for future reconnections + private String username; + private String password; + public ConnectionConfiguration(String host, int port, String serviceName) { this.host = host; this.port = port; @@ -354,4 +365,61 @@ public class ConnectionConfiguration implements Cloneable { protected Object clone() throws CloneNotSupportedException { return super.clone(); } + + /** + * Sets if the reconnection mechanism is allowed to be used. By default + * reconnection is allowed. + * + * @param isAllowed if the reconnection mechanism is allowed to use. + */ + public void setReconnectionAllowed(boolean isAllowed) { + this.reconnectionAllowed = isAllowed; + } + /** + * Returns if the reconnection mechanism is allowed to be used. By default + * reconnection is allowed. + * + * @return if the reconnection mechanism is allowed to be used. + */ + public boolean isReconnectionAllowed() { + return this.reconnectionAllowed; + } + + /** + * Sets the socket factory used to create new xmppConnection sockets. + * This is mainly used when reconnection is necessary. + * + * @param socketFactory used to create new sockets. + */ + public void setSocketFactory(SocketFactory socketFactory) { + this.socketFactory = socketFactory; + } + + + /** + * Returns the socket factory used to create new xmppConnection sockets. + * This is mainly used when reconnection is necessary. + * + * @return socketFactory used to create new sockets. + */ + public SocketFactory getSocketFactory() { + return this.socketFactory; + } + + protected void setUsernameAndPassword(String username, String password) { + this.username = username; + this.password = password; + } + /** + * Returns the username used to login + */ + protected String getUsername() { + return this.username; + } + /** + * Returns the password used to login + */ + protected String getPassword() { + return this.password; + } } diff --git a/source/org/jivesoftware/smack/ConnectionListener.java b/source/org/jivesoftware/smack/ConnectionListener.java index 69bc854f3..4f62df183 100644 --- a/source/org/jivesoftware/smack/ConnectionListener.java +++ b/source/org/jivesoftware/smack/ConnectionListener.java @@ -22,7 +22,7 @@ package org.jivesoftware.smack; /** * Interface that allows for implementing classes to listen for connection closing - * events. Listeners are registered with XMPPConnection objects. + * and reconnection events. Listeners are registered with XMPPConnection objects. * * @see XMPPConnection#addConnectionListener * @see XMPPConnection#removeConnectionListener @@ -32,14 +32,36 @@ package org.jivesoftware.smack; public interface ConnectionListener { /** - * Notification that the connection was closed normally. + * Notification that the connection was closed normally or that the reconnection + * process has been aborted. */ public void connectionClosed(); /** - * Notification that the connection was closed due to an exception. + * Notification that the connection was closed due to an exception. When + * abruptly disconnected it is possible for the connection to try reconnecting + * to the server. * * @param e the exception. */ public void connectionClosedOnError(Exception e); + + /** + * The connection will retry to reconnect in the specified number of seconds. + * + * @param seconds remaining seconds before attempting a reconnection. + */ + public void reconnectingIn(int seconds); + + /** + * The connection has reconnected successfully to the server. Connections will + * reconnect to the server when the previous socket connection was abruptly closed. + */ + public void reconectionSuccessful(); + + /** + * An attempt to connect to the server has failed. The connection will keep trying + * reconnecting to the server in a moment. + */ + public void reconnectionFailed(Exception e); } \ No newline at end of file diff --git a/source/org/jivesoftware/smack/PacketReader.java b/source/org/jivesoftware/smack/PacketReader.java index 91075495d..26e5c4db2 100644 --- a/source/org/jivesoftware/smack/PacketReader.java +++ b/source/org/jivesoftware/smack/PacketReader.java @@ -44,12 +44,12 @@ import java.util.concurrent.TimeUnit; */ class PacketReader { - private final Thread readerThread; - private final Thread listenerThread; + private Thread readerThread; + private Thread listenerThread; private XMPPConnection connection; private XmlPullParser parser; - private boolean done = false; + private boolean done; private final VolatileMemberCollection collectors = new VolatileMemberCollection(50); protected final VolatileMemberCollection listeners = new VolatileMemberCollection(50); @@ -61,10 +61,20 @@ class PacketReader { protected PacketReader(XMPPConnection connection) { this.connection = connection; + this.init(); + } + + /** + * Initializes the reader in order to be used. The reader is initialized during the + * first connection and when reconnecting due to an abruptly disconnection. + */ + protected void init() { + done = false; + connectionID = null; readerThread = new Thread() { public void run() { - parsePackets(); + parsePackets(this); } }; readerThread.setName("Smack Packet Reader"); @@ -73,7 +83,7 @@ class PacketReader { listenerThread = new Thread() { public void run() { try { - processListeners(); + processListeners(this); } catch (Exception e) { e.printStackTrace(); @@ -195,7 +205,8 @@ class PacketReader { */ void notifyConnectionError(Exception e) { done = true; - connection.close(); + // Closes the connection temporary. A reconnection is possible + connection.shutdown(); // Print the stack trace to help catch the problem e.printStackTrace(); // Notify connection listeners of the error. @@ -214,6 +225,26 @@ class PacketReader { } } + /** + * Sends a notification indicating that the connection was reconnected successfully. + */ + protected void notifyReconnection() { + // Notify connection listeners of the reconnection. + List listenersCopy; + synchronized (connectionListeners) { + // Make a copy since it's possible that a listener will be removed from the list + listenersCopy = new ArrayList(connectionListeners); + for (ConnectionListener listener : listenersCopy) { + listener.reconectionSuccessful(); + } + } + + // Make sure that the listenerThread is awake to shutdown properly + synchronized (listenerThread) { + listenerThread.notify(); + } + } + /** * Resets the parser using the latest connection's reader. Reseting the parser is necessary * when the plain connection has been secured or when a new opening stream element is going @@ -232,9 +263,11 @@ class PacketReader { /** * Process listeners. + * + * @param thread the thread that is being used by the reader to process incoming packets. */ - private void processListeners() { - while (!done) { + private void processListeners(Thread thread) { + while (!done && thread == listenerThread) { boolean processedPacket = false; Iterator it = listeners.getIterator(); while (it.hasNext()) { @@ -257,8 +290,10 @@ class PacketReader { /** * Parse top-level packets in order to process them further. + * + * @param thread the thread that is being used by the reader to parse incoming packets. */ - private void parsePackets() { + private void parsePackets(Thread thread) { try { int eventType = parser.getEventType(); do { @@ -356,12 +391,12 @@ class PacketReader { } else if (eventType == XmlPullParser.END_TAG) { if (parser.getName().equals("stream")) { - // Close the connection. - connection.close(); + // Disconnect the connection + connection.disconnect(); } } eventType = parser.next(); - } while (!done && eventType != XmlPullParser.END_DOCUMENT); + } while (!done && eventType != XmlPullParser.END_DOCUMENT && thread == readerThread); } catch (Exception e) { if (!done) { @@ -470,7 +505,7 @@ class PacketReader { } } } - // Release the lock after TLS has been negotiated or we are not insterested in TLS + // Release the lock after TLS has been negotiated or we are not insterested in TLS if (!startTLSReceived || !connection.getConfiguration().isTLSEnabled()) { releaseConnectionIDLock(); } @@ -873,7 +908,7 @@ class PacketReader { /** * A wrapper class to associate a packet collector with a listener. */ - private static class ListenerWrapper { + protected static class ListenerWrapper { private PacketListener packetListener; private PacketCollector packetCollector; diff --git a/source/org/jivesoftware/smack/PacketWriter.java b/source/org/jivesoftware/smack/PacketWriter.java index 164c551c8..b516cfaf9 100644 --- a/source/org/jivesoftware/smack/PacketWriter.java +++ b/source/org/jivesoftware/smack/PacketWriter.java @@ -39,13 +39,14 @@ import java.util.List; class PacketWriter { private Thread writerThread; + private Thread keepAliveThread; private Writer writer; private XMPPConnection connection; final private LinkedList queue; - private boolean done = false; + private boolean done; - final private List listeners = new ArrayList(); - private boolean listenersDeleted = false; + final protected List listeners = new ArrayList(); + private boolean listenersDeleted; /** * Timestamp when the last stanza was sent to the server. This information is used @@ -70,13 +71,24 @@ class PacketWriter { * @param connection the connection. */ protected PacketWriter(XMPPConnection connection) { - this.connection = connection; - this.writer = connection.writer; this.queue = new LinkedList(); + this.connection = connection; + init(); + } + + /** + * Initializes the writer in order to be used. It is called at the first connection and also + * is invoked if the connection is disconnected by an error. + */ + protected void init() { + this.writer = connection.writer; + listenersDeleted = false; + interceptorDeleted = false; + done = false; writerThread = new Thread() { public void run() { - writePackets(); + writePackets(this); } }; writerThread.setName("Smack Packet Writer"); @@ -205,8 +217,11 @@ class PacketWriter { // out a space character each time it runs to keep the TCP/IP connection open. int keepAliveInterval = SmackConfiguration.getKeepAliveInterval(); if (keepAliveInterval > 0) { - Thread keepAliveThread = new Thread(new KeepAliveTask(keepAliveInterval)); + KeepAliveTask target = new KeepAliveTask(keepAliveInterval); + keepAliveThread = new Thread(target); + target.setThread(keepAliveThread); keepAliveThread.setDaemon(true); + keepAliveThread.setName("Smack Keep Alive"); keepAliveThread.start(); } } @@ -247,12 +262,12 @@ class PacketWriter { } } - private void writePackets() { + private void writePackets(Thread thisThread) { try { // Open the stream. openStream(); // Write out packets from the queue. - while (!done) { + while (!done && (writerThread == thisThread)) { Packet packet = nextPacket(); if (packet != null) { synchronized (writer) { @@ -370,7 +385,7 @@ class PacketWriter { /** * A wrapper class to associate a packet filter with a listener. */ - private static class ListenerWrapper { + protected static class ListenerWrapper { private PacketListener packetListener; private PacketFilter packetFilter; @@ -444,11 +459,16 @@ class PacketWriter { private class KeepAliveTask implements Runnable { private int delay; + private Thread thread; public KeepAliveTask(int delay) { this.delay = delay; } + protected void setThread(Thread thread) { + this.thread = thread; + } + public void run() { try { // Sleep 15 seconds before sending first heartbeat. This will give time to @@ -458,7 +478,7 @@ class PacketWriter { catch (InterruptedException ie) { // Do nothing } - while (!done) { + while (!done && keepAliveThread == thread) { synchronized (writer) { // Send heartbeat if no packet has been sent to the server for a given time if (System.currentTimeMillis() - lastActive >= delay) { diff --git a/source/org/jivesoftware/smack/PrivacyListManager.java b/source/org/jivesoftware/smack/PrivacyListManager.java index ce79d6e4e..93febcbd5 100644 --- a/source/org/jivesoftware/smack/PrivacyListManager.java +++ b/source/org/jivesoftware/smack/PrivacyListManager.java @@ -71,24 +71,27 @@ public class PrivacyListManager { // the connection is closed connection.addConnectionListener(new ConnectionListener() { public void connectionClosed() { - // Unregister this instance since the connection has been closed + // Unregister this instance since the connection has been closed instances.remove(connection); } public void connectionClosedOnError(Exception e) { // ignore } + public void reconnectionFailed(Exception e) { // ignore } - public void attemptToReconnectIn(int seconds) { + + public void reconnectingIn(int seconds) { // ignore } - public void conectionReestablished() { + + public void reconectionSuccessful() { // ignore } }); - + connection.addPacketListener(new PacketListener() { public void processPacket(Packet packet) { diff --git a/source/org/jivesoftware/smack/SASLAuthentication.java b/source/org/jivesoftware/smack/SASLAuthentication.java index f8f531418..9904f04fe 100644 --- a/source/org/jivesoftware/smack/SASLAuthentication.java +++ b/source/org/jivesoftware/smack/SASLAuthentication.java @@ -68,14 +68,14 @@ public class SASLAuthentication implements UserAuthentication { /** * Boolean indicating if SASL negotiation has finished and was successful. */ - private boolean saslNegotiated = false; + private boolean saslNegotiated; /** * Boolean indication if SASL authentication has failed. When failed the server may end * the connection. */ - private boolean saslFailed = false; - private boolean resourceBinded = false; - private boolean sessionSupported = false; + private boolean saslFailed; + private boolean resourceBinded; + private boolean sessionSupported; static { // Register SASL mechanisms supported by Smack @@ -126,6 +126,7 @@ public class SASLAuthentication implements UserAuthentication { SASLAuthentication(XMPPConnection connection) { super(); this.connection = connection; + this.init(); } /** @@ -413,4 +414,16 @@ public class SASLAuthentication implements UserAuthentication { void sessionsSupported() { sessionSupported = true; } + + /** + * Initializes the internal state in order to be able to be reused. The authentication + * is used by the connection at the first login and then reused after the connection + * is disconnected and then reconnected. + */ + protected void init() { + saslNegotiated = false; + saslFailed = false; + resourceBinded = false; + sessionSupported = false; + } } diff --git a/source/org/jivesoftware/smack/debugger/ConsoleDebugger.java b/source/org/jivesoftware/smack/debugger/ConsoleDebugger.java index a3757cb2a..e4db9fe78 100644 --- a/source/org/jivesoftware/smack/debugger/ConsoleDebugger.java +++ b/source/org/jivesoftware/smack/debugger/ConsoleDebugger.java @@ -108,6 +108,26 @@ public class ConsoleDebugger implements SmackDebugger { ")"); e.printStackTrace(); } + public void reconnectionFailed(Exception e) { + System.out.println( + dateFormatter.format(new Date()) + + " Reconnection failed due to an exception (" + + connection.hashCode() + + ")"); + e.printStackTrace(); + } + public void reconectionSuccessful() { + System.out.println( + dateFormatter.format(new Date()) + " Connection reconnected (" + + connection.hashCode() + + ")"); + } + public void reconnectingIn(int seconds) { + System.out.println( + dateFormatter.format(new Date()) + " Connection (" + + connection.hashCode() + + ") will reconnect in " + seconds); + } }; } diff --git a/source/org/jivesoftware/smack/packet/XMPPError.java b/source/org/jivesoftware/smack/packet/XMPPError.java index 89aef8723..8b1b0566a 100644 --- a/source/org/jivesoftware/smack/packet/XMPPError.java +++ b/source/org/jivesoftware/smack/packet/XMPPError.java @@ -20,10 +20,7 @@ package org.jivesoftware.smack.packet; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; +import java.util.*; /** * Represents a XMPP error sub-packet. Typically, a server responds to a request that has @@ -79,8 +76,8 @@ public class XMPPError { * @param condition the error condition. */ public XMPPError(Condition condition) { - this.init(condition); - this.message = null; + this.init(condition); + this.message = null; } /** @@ -97,7 +94,7 @@ public class XMPPError { this.init(condition); this.message = messageText; } - + /** * Creates a new error with the specified code and no message. * @@ -108,7 +105,7 @@ public class XMPPError { this.code = code; this.message = null; } - + /** * Creates a new error with the specified code and message. * deprecated @@ -121,8 +118,7 @@ public class XMPPError { this.code = code; this.message = message; } - - + /** * Creates a new error with the specified code, type, condition and message. * This constructor is used when the condition is not recognized automatically by XMPPError @@ -156,7 +152,7 @@ public class XMPPError { // If there is a default error specification for the received condition, // it get configured with the infered type and code. this.type = defaultErrorSpecification.getType(); - this.code = defaultErrorSpecification.getCode(); + this.code = defaultErrorSpecification.getCode(); } } /** @@ -167,7 +163,7 @@ public class XMPPError { public String getCondition() { return condition; } - + /** * Returns the error type. * @@ -176,7 +172,7 @@ public class XMPPError { public Type getType() { return type; } - + /** * Returns the error code. * @@ -210,17 +206,17 @@ public class XMPPError { } buf.append(">"); if (condition != null) { - buf.append("<").append(condition); - buf.append(" xmlns=\"urn:ietf:params:xml:ns:xmpp-stanzas\"/>"); + buf.append("<").append(condition); + buf.append(" xmlns=\"urn:ietf:params:xml:ns:xmpp-stanzas\"/>"); } if (message != null) { - buf.append(""); - buf.append(message); - buf.append(""); + buf.append(""); + buf.append(message); + buf.append(""); } for (PacketExtension element : this.getExtensions()) { - buf.append(element.toXML()); - } + buf.append(element.toXML()); + } buf.append(""); return buf.toString(); } @@ -270,7 +266,7 @@ public class XMPPError { } return null; } - + /** * Adds a packet extension to the error. * @@ -278,7 +274,7 @@ public class XMPPError { */ public synchronized void addExtension(PacketExtension extension) { if (applicationExtensions == null) { - applicationExtensions = new ArrayList(); + applicationExtensions = new ArrayList(); } applicationExtensions.add(extension); } @@ -289,10 +285,9 @@ public class XMPPError { * @param extension a packet extension. */ public synchronized void setExtension(List extension) { - applicationExtensions = extension; + applicationExtensions = extension; } - - + /** * A class to represent the type of the Error. The types are: * @@ -311,7 +306,7 @@ public class XMPPError { AUTH, CONTINUE } - + /** * A class to represent predefined error conditions. */ @@ -341,7 +336,7 @@ public class XMPPError { public static final Condition undefined_condition = new Condition("undefined-condition"); public static final Condition unexpected_condition = new Condition("unexpected-condition"); public static final Condition request_timeout = new Condition("request-timeout"); - + private String value; public Condition(String value) { @@ -353,80 +348,80 @@ public class XMPPError { } } - + /** * A class to represent the error specification used to infer common usage. */ private static class ErrorSpecification { - private int code; + private int code; private Type type; private Condition condition; - private static HashMap instances = errorSpecifications(); + private static Map instances = errorSpecifications(); private ErrorSpecification(Condition condition, Type type, int code) { - this.code = code; - this.type = type; - this.condition = condition; + this.code = code; + this.type = type; + this.condition = condition; } - - private static HashMap errorSpecifications() { - HashMap instances = new HashMap(22); - instances.put(Condition.interna_server_error, new ErrorSpecification( - Condition.interna_server_error, Type.WAIT, 500)); - instances.put(Condition.forbidden, new ErrorSpecification(Condition.forbidden, - Type.AUTH, 403)); - instances.put(Condition.bad_request, new XMPPError.ErrorSpecification( - Condition.bad_request, Type.MODIFY, 400)); - instances.put(Condition.item_not_found, new XMPPError.ErrorSpecification( - Condition.item_not_found, Type.CANCEL, 404)); - instances.put(Condition.conflict, new XMPPError.ErrorSpecification( - Condition.conflict, Type.CANCEL, 409)); - instances.put(Condition.feature_not_implemented, new XMPPError.ErrorSpecification( - Condition.feature_not_implemented, Type.CANCEL, 501)); - instances.put(Condition.gone, new XMPPError.ErrorSpecification( - Condition.gone, Type.MODIFY, 302)); - instances.put(Condition.jid_malformed, new XMPPError.ErrorSpecification( - Condition.jid_malformed, Type.MODIFY, 400)); - instances.put(Condition.no_acceptable, new XMPPError.ErrorSpecification( - Condition.no_acceptable, Type.MODIFY, 406)); - instances.put(Condition.not_allowed, new XMPPError.ErrorSpecification( - Condition.not_allowed, Type.CANCEL, 405)); - instances.put(Condition.not_authorized, new XMPPError.ErrorSpecification( - Condition.not_authorized, Type.AUTH, 401)); - instances.put(Condition.payment_required, new XMPPError.ErrorSpecification( - Condition.payment_required, Type.AUTH, 402)); - instances.put(Condition.recipient_unavailable, new XMPPError.ErrorSpecification( - Condition.recipient_unavailable, Type.WAIT, 404)); - instances.put(Condition.redirect, new XMPPError.ErrorSpecification( - Condition.redirect, Type.MODIFY, 302)); - instances.put(Condition.registration_required, new XMPPError.ErrorSpecification( - Condition.registration_required, Type.AUTH, 407)); - instances.put(Condition.remote_server_not_found, new XMPPError.ErrorSpecification( - Condition.remote_server_not_found, Type.CANCEL, 404)); - instances.put(Condition.remote_server_timeout, new XMPPError.ErrorSpecification( - Condition.remote_server_timeout, Type.WAIT, 504)); - instances.put(Condition.remote_server_error, new XMPPError.ErrorSpecification( - Condition.remote_server_error, Type.CANCEL, 502)); - instances.put(Condition.resource_constraint, new XMPPError.ErrorSpecification( - Condition.resource_constraint, Type.WAIT, 500)); - instances.put(Condition.service_unavailable, new XMPPError.ErrorSpecification( - Condition.service_unavailable, Type.CANCEL, 503)); - instances.put(Condition.subscription_required, new XMPPError.ErrorSpecification( - Condition.subscription_required, Type.AUTH, 407)); - instances.put(Condition.undefined_condition, new XMPPError.ErrorSpecification( - Condition.undefined_condition, Type.WAIT, 500)); - instances.put(Condition.unexpected_condition, new XMPPError.ErrorSpecification( - Condition.unexpected_condition, Type.WAIT, 400)); + + private static Map errorSpecifications() { + Map instances = new HashMap(22); + instances.put(Condition.interna_server_error, new ErrorSpecification( + Condition.interna_server_error, Type.WAIT, 500)); + instances.put(Condition.forbidden, new ErrorSpecification(Condition.forbidden, + Type.AUTH, 403)); + instances.put(Condition.bad_request, new XMPPError.ErrorSpecification( + Condition.bad_request, Type.MODIFY, 400)); + instances.put(Condition.item_not_found, new XMPPError.ErrorSpecification( + Condition.item_not_found, Type.CANCEL, 404)); + instances.put(Condition.conflict, new XMPPError.ErrorSpecification( + Condition.conflict, Type.CANCEL, 409)); + instances.put(Condition.feature_not_implemented, new XMPPError.ErrorSpecification( + Condition.feature_not_implemented, Type.CANCEL, 501)); + instances.put(Condition.gone, new XMPPError.ErrorSpecification( + Condition.gone, Type.MODIFY, 302)); + instances.put(Condition.jid_malformed, new XMPPError.ErrorSpecification( + Condition.jid_malformed, Type.MODIFY, 400)); + instances.put(Condition.no_acceptable, new XMPPError.ErrorSpecification( + Condition.no_acceptable, Type.MODIFY, 406)); + instances.put(Condition.not_allowed, new XMPPError.ErrorSpecification( + Condition.not_allowed, Type.CANCEL, 405)); + instances.put(Condition.not_authorized, new XMPPError.ErrorSpecification( + Condition.not_authorized, Type.AUTH, 401)); + instances.put(Condition.payment_required, new XMPPError.ErrorSpecification( + Condition.payment_required, Type.AUTH, 402)); + instances.put(Condition.recipient_unavailable, new XMPPError.ErrorSpecification( + Condition.recipient_unavailable, Type.WAIT, 404)); + instances.put(Condition.redirect, new XMPPError.ErrorSpecification( + Condition.redirect, Type.MODIFY, 302)); + instances.put(Condition.registration_required, new XMPPError.ErrorSpecification( + Condition.registration_required, Type.AUTH, 407)); + instances.put(Condition.remote_server_not_found, new XMPPError.ErrorSpecification( + Condition.remote_server_not_found, Type.CANCEL, 404)); + instances.put(Condition.remote_server_timeout, new XMPPError.ErrorSpecification( + Condition.remote_server_timeout, Type.WAIT, 504)); + instances.put(Condition.remote_server_error, new XMPPError.ErrorSpecification( + Condition.remote_server_error, Type.CANCEL, 502)); + instances.put(Condition.resource_constraint, new XMPPError.ErrorSpecification( + Condition.resource_constraint, Type.WAIT, 500)); + instances.put(Condition.service_unavailable, new XMPPError.ErrorSpecification( + Condition.service_unavailable, Type.CANCEL, 503)); + instances.put(Condition.subscription_required, new XMPPError.ErrorSpecification( + Condition.subscription_required, Type.AUTH, 407)); + instances.put(Condition.undefined_condition, new XMPPError.ErrorSpecification( + Condition.undefined_condition, Type.WAIT, 500)); + instances.put(Condition.unexpected_condition, new XMPPError.ErrorSpecification( + Condition.unexpected_condition, Type.WAIT, 400)); instances.put(Condition.request_timeout, new XMPPError.ErrorSpecification( Condition.request_timeout, Type.CANCEL, 408)); - - return instances; - } + + return instances; + } protected static ErrorSpecification specFor(Condition condition) { - return instances.get(condition); + return instances.get(condition); } - + /** * Returns the error condition. * @@ -435,7 +430,7 @@ public class XMPPError { protected Condition getCondition() { return condition; } - + /** * Returns the error type. * @@ -444,7 +439,7 @@ public class XMPPError { protected Type getType() { return type; } - + /** * Returns the error code. * diff --git a/source/org/jivesoftware/smackx/ServiceDiscoveryManager.java b/source/org/jivesoftware/smackx/ServiceDiscoveryManager.java index f83588b45..5c1fefe22 100644 --- a/source/org/jivesoftware/smackx/ServiceDiscoveryManager.java +++ b/source/org/jivesoftware/smackx/ServiceDiscoveryManager.java @@ -150,8 +150,19 @@ public class ServiceDiscoveryManager { } public void connectionClosedOnError(Exception e) { - // Unregister this instance since the connection has been closed - instances.remove(connection); + // ignore + } + + public void reconnectionFailed(Exception e) { + // ignore + } + + public void reconnectingIn(int seconds) { + // ignore + } + + public void reconectionSuccessful() { + // ignore } }); diff --git a/source/org/jivesoftware/smackx/debugger/EnhancedDebugger.java b/source/org/jivesoftware/smackx/debugger/EnhancedDebugger.java index 3e1a078cd..3f02a6761 100644 --- a/source/org/jivesoftware/smackx/debugger/EnhancedDebugger.java +++ b/source/org/jivesoftware/smackx/debugger/EnhancedDebugger.java @@ -218,6 +218,30 @@ public class EnhancedDebugger implements SmackDebugger { }); } + public void reconnectingIn(final int seconds){ + SwingUtilities.invokeLater(new Runnable() { + public void run() { + statusField.setValue("Attempt to reconnect in " + seconds + " seconds"); + } + }); + } + + public void reconectionSuccessful() { + SwingUtilities.invokeLater(new Runnable() { + public void run() { + statusField.setValue("Reconnection stablished"); + EnhancedDebuggerWindow.connectionEstablished(EnhancedDebugger.this); + } + }); + } + + public void reconnectionFailed(Exception e) { + SwingUtilities.invokeLater(new Runnable() { + public void run() { + statusField.setValue("Reconnection failed"); + } + }); + } }; } @@ -551,7 +575,7 @@ public class EnhancedDebugger implements SmackDebugger { connPanel.add( label, new GridBagConstraints(0, 1, 1, 1, 0.0, 0.0, 21, 0, new Insets(0, 0, 0, 0), 0, 0)); - field = new JFormattedTextField(new Integer(connection.getPort())); + field = new JFormattedTextField(connection.getPort()); field.setMinimumSize(new java.awt.Dimension(150, 20)); field.setMaximumSize(new java.awt.Dimension(150, 20)); field.setEditable(false); @@ -618,16 +642,9 @@ public class EnhancedDebugger implements SmackDebugger { packetsPanel.setBorder(BorderFactory.createTitledBorder("Transmitted Packets")); statisticsTable = - new DefaultTableModel(new Object[][]{{"IQ", new Integer(0), new Integer(0)}, { - "Message", new Integer(0), new Integer(0) - }, { - "Presence", new Integer(0), new Integer(0) - }, { - "Other", new Integer(0), new Integer(0) - }, { - "Total", new Integer(0), new Integer(0) - } - }, new Object[]{"Type", "Received", "Sent"}) { + new DefaultTableModel(new Object[][]{{"IQ", 0, 0}, {"Message", 0, 0}, + {"Presence", 0, 0}, {"Other", 0, 0}, {"Total", 0, 0}}, + new Object[]{"Type", "Received", "Sent"}) { public boolean isCellEditable(int rowIndex, int mColIndex) { return false; } @@ -719,7 +736,7 @@ public class EnhancedDebugger implements SmackDebugger { private void addReadPacketToTable(final SimpleDateFormat dateFormatter, final Packet packet) { SwingUtilities.invokeLater(new Runnable() { public void run() { - String messageType = null; + String messageType; String from = packet.getFrom(); String type = ""; Icon packetTypeIcon; @@ -780,7 +797,7 @@ public class EnhancedDebugger implements SmackDebugger { private void addSentPacketToTable(final SimpleDateFormat dateFormatter, final Packet packet) { SwingUtilities.invokeLater(new Runnable() { public void run() { - String messageType = null; + String messageType; String to = packet.getTo(); String type = ""; Icon packetTypeIcon; @@ -840,9 +857,10 @@ public class EnhancedDebugger implements SmackDebugger { // Surround this setting in a try/catch for compatibility with Java 1.4. This setting is required // for Java 1.5 try { - tFactory.setAttribute("indent-number", new Integer(2)); + tFactory.setAttribute("indent-number", 2); } catch (IllegalArgumentException e) { + // Ignore } Transformer transformer = tFactory.newTransformer(); transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); diff --git a/source/org/jivesoftware/smackx/debugger/EnhancedDebuggerWindow.java b/source/org/jivesoftware/smackx/debugger/EnhancedDebuggerWindow.java index 741783ab5..75eb952cb 100644 --- a/source/org/jivesoftware/smackx/debugger/EnhancedDebuggerWindow.java +++ b/source/org/jivesoftware/smackx/debugger/EnhancedDebuggerWindow.java @@ -29,7 +29,6 @@ import java.awt.event.*; import java.net.URL; import java.util.ArrayList; import java.util.Collections; -import java.util.Iterator; import java.util.Vector; /** @@ -90,7 +89,7 @@ public class EnhancedDebuggerWindow { private JFrame frame = null; private JTabbedPane tabbedPane = null; - private java.util.List debuggers = new ArrayList(); + private java.util.List debuggers = new ArrayList(); private EnhancedDebuggerWindow() { } @@ -178,6 +177,12 @@ public class EnhancedDebuggerWindow { connectionClosedOnErrorIcon); } + synchronized static void connectionEstablished(EnhancedDebugger debugger) { + getInstance().tabbedPane.setIconAt( + getInstance().tabbedPane.indexOfComponent(debugger.tabbedPane), + connectionActiveIcon); + } + /** * Creates the main debug window that provides information about Smack and also shows * a tab panel for each connection that is being debugged. @@ -264,7 +269,7 @@ public class EnhancedDebuggerWindow { if (tabbedPane.getSelectedIndex() < tabbedPane.getComponentCount() - 1) { int index = tabbedPane.getSelectedIndex(); // Notify to the debugger to stop debugging - EnhancedDebugger debugger = (EnhancedDebugger) debuggers.get(index); + EnhancedDebugger debugger = debuggers.get(index); debugger.cancel(); // Remove the debugger from the root window tabbedPane.remove(debugger.tabbedPane); @@ -281,18 +286,17 @@ public class EnhancedDebuggerWindow { menuItem = new JMenuItem("Close All Not Active"); menuItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { - ArrayList debuggersToRemove = new ArrayList(); + ArrayList debuggersToRemove = new ArrayList(); // Remove all the debuggers of which their connections are no longer valid for (int index = 0; index < tabbedPane.getComponentCount() - 1; index++) { - EnhancedDebugger debugger = (EnhancedDebugger) debuggers.get(index); + EnhancedDebugger debugger = debuggers.get(index); if (!debugger.isConnectionActive()) { // Notify to the debugger to stop debugging debugger.cancel(); debuggersToRemove.add(debugger); } } - for (Iterator it = debuggersToRemove.iterator(); it.hasNext();) { - EnhancedDebugger debugger = (EnhancedDebugger) it.next(); + for (EnhancedDebugger debugger : debuggersToRemove) { // Remove the debugger from the root window tabbedPane.remove(debugger.tabbedPane); debuggers.remove(debugger); @@ -324,8 +328,7 @@ public class EnhancedDebuggerWindow { */ public void rootWindowClosing(WindowEvent evt) { // Notify to all the debuggers to stop debugging - for (Iterator it = debuggers.iterator(); it.hasNext();) { - EnhancedDebugger debugger = (EnhancedDebugger) it.next(); + for (EnhancedDebugger debugger : debuggers) { debugger.cancel(); } // Release any reference to the debuggers diff --git a/source/org/jivesoftware/smackx/filetransfer/FileTransferNegotiator.java b/source/org/jivesoftware/smackx/filetransfer/FileTransferNegotiator.java index c6febc2de..de426b9d4 100644 --- a/source/org/jivesoftware/smackx/filetransfer/FileTransferNegotiator.java +++ b/source/org/jivesoftware/smackx/filetransfer/FileTransferNegotiator.java @@ -35,6 +35,7 @@ import org.jivesoftware.smackx.packet.StreamInitiation; import java.net.URLConnection; import java.util.*; +import java.util.concurrent.ConcurrentHashMap; /** * Manages the negotiation of file transfers according to JEP-0096. If a file is @@ -64,7 +65,8 @@ public class FileTransferNegotiator { private static final String[] PROTOCOLS = {BYTE_STREAM, INBAND_BYTE_STREAM}; - private static final Map transferObject = new HashMap(); + private static final Map transferObject = + new ConcurrentHashMap(); private static final String STREAM_INIT_PREFIX = "jsi_"; @@ -92,7 +94,7 @@ public class FileTransferNegotiator { } if (transferObject.containsKey(connection)) { - return (FileTransferNegotiator) transferObject.get(connection); + return transferObject.get(connection); } else { FileTransferNegotiator transfer = new FileTransferNegotiator( @@ -114,12 +116,12 @@ public class FileTransferNegotiator { final boolean isEnabled) { ServiceDiscoveryManager manager = ServiceDiscoveryManager .getInstanceFor(connection); - for (int i = 0; i < NAMESPACE.length; i++) { + for (String ns : NAMESPACE) { if (isEnabled) { - manager.addFeature(NAMESPACE[i]); + manager.addFeature(ns); } else { - manager.removeFeature(NAMESPACE[i]); + manager.removeFeature(ns); } } } @@ -132,9 +134,8 @@ public class FileTransferNegotiator { * @return True if all related services are enabled, false if they are not. */ public static boolean isServiceEnabled(final XMPPConnection connection) { - for (int i = 0; i < NAMESPACE.length; i++) { - if (!ServiceDiscoveryManager.getInstanceFor(connection) - .includesFeature(NAMESPACE[i])) + for (String ns : NAMESPACE) { + if (!ServiceDiscoveryManager.getInstanceFor(connection).includesFeature(ns)) return false; } return true; @@ -198,14 +199,26 @@ public class FileTransferNegotiator { public void connectionClosedOnError(Exception e) { cleanup(connection); } + + public void reconnectionFailed(Exception e) { + // ignore + } + + public void reconectionSuccessful() { + // ignore + } + + public void reconnectingIn(int seconds) { + // ignore + } }); } private void cleanup(final XMPPConnection connection) { - transferObject.remove(connection); - - byteStreamTransferManager.cleanup(); - inbandTransferManager.cleanup(); + if (transferObject.remove(connection) != null) { + byteStreamTransferManager.cleanup(); + inbandTransferManager.cleanup(); + } } /** diff --git a/source/org/jivesoftware/smackx/muc/MultiUserChat.java b/source/org/jivesoftware/smackx/muc/MultiUserChat.java index 1096ddb0e..36cb781e8 100644 --- a/source/org/jivesoftware/smackx/muc/MultiUserChat.java +++ b/source/org/jivesoftware/smackx/muc/MultiUserChat.java @@ -2614,6 +2614,18 @@ public class MultiUserChat { cancel(); } + public void reconnectingIn(int seconds) { + // ignore + } + + public void reconectionSuccessful() { + // ignore + } + + public void reconnectionFailed(Exception e) { + // ignore + } + /** * Initializes the listeners to detect received room invitations and to detect when the * connection gets closed. As soon as a room invitation is received the invitations diff --git a/source/org/jivesoftware/smackx/muc/RoomListenerMultiplexor.java b/source/org/jivesoftware/smackx/muc/RoomListenerMultiplexor.java index e756f92f5..bfe38d095 100644 --- a/source/org/jivesoftware/smackx/muc/RoomListenerMultiplexor.java +++ b/source/org/jivesoftware/smackx/muc/RoomListenerMultiplexor.java @@ -109,6 +109,18 @@ class RoomListenerMultiplexor implements ConnectionListener { cancel(); } + public void reconnectingIn(int seconds) { + // ignore + } + + public void reconectionSuccessful() { + // ignore + } + + public void reconnectionFailed(Exception e) { + // ignore + } + /** * Initializes the listeners to detect received room invitations and to detect when the * connection gets closed. As soon as a room invitation is received the invitations diff --git a/test/org/jivesoftware/smack/LoginTest.java b/test/org/jivesoftware/smack/LoginTest.java index 68e5c66fb..9b114c1d7 100644 --- a/test/org/jivesoftware/smack/LoginTest.java +++ b/test/org/jivesoftware/smack/LoginTest.java @@ -32,10 +32,11 @@ public class LoginTest extends SmackTestCase { public void testInvalidLogin() { try { XMPPConnection connection = new XMPPConnection(getHost(), getPort()); + connection.connect(); try { // Login with an invalid user connection.login("invaliduser" , "invalidpass"); - connection.close(); + connection.disconnect(); fail("Invalid user was able to log into the server"); } catch (XMPPException e) { @@ -60,6 +61,8 @@ public class LoginTest extends SmackTestCase { try { XMPPConnection conn1 = new XMPPConnection(getHost(), getPort()); XMPPConnection conn2 = new XMPPConnection(getHost(), getPort()); + conn1.connect(); + conn2.connect(); try { // Try to login anonymously conn1.loginAnonymously(); @@ -76,8 +79,8 @@ public class LoginTest extends SmackTestCase { fail(e.getMessage()); } // Close the connection - conn1.close(); - conn2.close(); + conn1.disconnect(); + conn2.disconnect(); } catch (Exception e) { e.printStackTrace(); @@ -93,10 +96,13 @@ public class LoginTest extends SmackTestCase { ConnectionConfiguration config = new ConnectionConfiguration(getHost(), getPort()); config.setSASLAuthenticationEnabled(false); XMPPConnection conn1 = new XMPPConnection(config); + conn1.connect(); config = new ConnectionConfiguration(getHost(), getPort()); config.setSASLAuthenticationEnabled(false); XMPPConnection conn2 = new XMPPConnection(config); + conn2.connect(); + try { // Try to login anonymously conn1.loginAnonymously(); @@ -113,8 +119,8 @@ public class LoginTest extends SmackTestCase { fail(e.getMessage()); } // Close the connection - conn1.close(); - conn2.close(); + conn1.disconnect(); + conn2.disconnect(); } catch (Exception e) { e.printStackTrace(); @@ -128,6 +134,7 @@ public class LoginTest extends SmackTestCase { public void testLoginWithNoResource() { try { XMPPConnection conn = new XMPPConnection(getHost(), getPort()); + conn.connect(); try { conn.getAccountManager().createAccount("user_1", "user_1"); } catch (XMPPException e) { @@ -142,7 +149,7 @@ public class LoginTest extends SmackTestCase { assertNotNull("JID assigned by server is missing", conn.getUser()); assertNotNull("JID assigned by server does not have a resource", StringUtils.parseResource(conn.getUser())); - conn.close(); + conn.disconnect(); } else { fail("User with no resource was able to log into the server"); diff --git a/test/org/jivesoftware/smack/MessengerLoginTest.java b/test/org/jivesoftware/smack/MessengerLoginTest.java index 884a97142..33648d428 100644 --- a/test/org/jivesoftware/smack/MessengerLoginTest.java +++ b/test/org/jivesoftware/smack/MessengerLoginTest.java @@ -86,6 +86,7 @@ public class MessengerLoginTest extends TestCase { try { XMPPConnection con = new XMPPConnection(host, port); + con.connect(); con.login(username, password, resource); } catch (XMPPException e) { diff --git a/test/org/jivesoftware/smack/PresenceTest.java b/test/org/jivesoftware/smack/PresenceTest.java index 3ebb36f63..48a8620b6 100644 --- a/test/org/jivesoftware/smack/PresenceTest.java +++ b/test/org/jivesoftware/smack/PresenceTest.java @@ -37,6 +37,7 @@ public class PresenceTest extends SmackTestCase { try { // User_1 will log in again using another resource conn = new XMPPConnection(getHost(), getPort()); + conn.connect(); conn.login(getUsername(1), getUsername(1), "OtherPlace"); // Change the presence priorities of User_1 getConnection(1).sendPacket(new Presence(Presence.Type.available, null, 1, @@ -71,8 +72,7 @@ public class PresenceTest extends SmackTestCase { chat2.nextMessage(1000)); // User_1 closes his connection - chat2 = null; - conn.close(); + conn.disconnect(); Thread.sleep(150); // Test delivery of message to the unique presence of the user_1 @@ -85,6 +85,7 @@ public class PresenceTest extends SmackTestCase { // User_1 will log in again using another resource conn = new XMPPConnection(getHost(), getPort()); + conn.connect(); conn.login(getUsername(1), getUsername(1), "OtherPlace"); conn.sendPacket(new Presence(Presence.Type.available, null, 1, Presence.Mode.available)); @@ -119,7 +120,7 @@ public class PresenceTest extends SmackTestCase { } finally { if (conn != null) { - conn.close(); + conn.disconnect(); } } } @@ -128,6 +129,7 @@ public class PresenceTest extends SmackTestCase { * User1 logs from 2 resources but only one is available. User0 sends a message * to the full JID of the unavailable resource. User1 in the not available resource * should receive the message. + * TODO Fix this in Wildfire but before check if XMPP spec requests this feature */ public void testNotAvailablePresence() throws XMPPException { // Change the presence to unavailable of User_1 @@ -135,6 +137,7 @@ public class PresenceTest extends SmackTestCase { // User_1 will log in again using another resource (that is going to be available) XMPPConnection conn = new XMPPConnection(getHost(), getPort()); + conn.connect(); conn.login(getUsername(1), getUsername(1), "OtherPlace"); // Create chats between participants @@ -158,6 +161,7 @@ public class PresenceTest extends SmackTestCase { public void testMultipleResources() throws Exception { // Create another connection for the same user of connection 1 XMPPConnection conn4 = new XMPPConnection(getServiceName()); + conn4.connect(); conn4.login(getUsername(1), getUsername(1), "Home"); // Add a new roster entry @@ -188,7 +192,7 @@ public class PresenceTest extends SmackTestCase { assertTrue("Only one presence was found for user1", presences.hasNext()); // User1 logs out from one resource - conn4.close(); + conn4.disconnect(); // Wait up to 1 second Thread.sleep(700); diff --git a/test/org/jivesoftware/smack/RosterTest.java b/test/org/jivesoftware/smack/RosterTest.java index 91cd34ffd..f14d1b77f 100644 --- a/test/org/jivesoftware/smack/RosterTest.java +++ b/test/org/jivesoftware/smack/RosterTest.java @@ -367,6 +367,7 @@ public class RosterTest extends SmackTestCase { // Log in from another resource so we can test the roster XMPPConnection con2 = new XMPPConnection(getHost(), getPort()); + con2.connect(); con2.login(getUsername(0), getUsername(0), "MyNewResource"); Roster roster2 = con2.getRoster(); @@ -386,7 +387,7 @@ public class RosterTest extends SmackTestCase { assertTrue("NewGroup group was not found", groupNames.contains("NewGroup")); // Close the new connection - con2.close(); + con2.disconnect(); Thread.sleep(500); cleanUpRoster(); @@ -480,6 +481,7 @@ public class RosterTest extends SmackTestCase { // Create another connection for the same user of connection 1 XMPPConnection conn4 = new XMPPConnection(getServiceName()); + conn4.connect(); conn4.login(getUsername(1), getUsername(1), "Home"); // Add a new roster entry @@ -506,6 +508,7 @@ public class RosterTest extends SmackTestCase { // Check that the right presence is returned for a user+resource presence = roster.getPresenceResource(getFullJID(1)); + assertNotNull("Presence not found for user " + getFullJID(1), presence); assertEquals( "Returned the wrong Presence", StringUtils.parseResource(presence.getFrom()), @@ -525,7 +528,7 @@ public class RosterTest extends SmackTestCase { assertEquals("Wrong number of returned presences", count, 2); // Close the connection so one presence must go - conn4.close(); + conn4.disconnect(); // Check that the returned presences are correct presences = roster.getPresences(getBareJID(1)); @@ -548,6 +551,7 @@ public class RosterTest extends SmackTestCase { public void testMultipleResources() throws Exception { // Create another connection for the same user of connection 1 XMPPConnection conn4 = new XMPPConnection(getServiceName()); + conn4.connect(); conn4.login(getUsername(1), getUsername(1), "Home"); // Add a new roster entry @@ -605,6 +609,7 @@ public class RosterTest extends SmackTestCase { // Create another connection for the same user of connection 0 XMPPConnection conn2 = new XMPPConnection(getServiceName()); + conn2.connect(); conn2.login(getUsername(0), getUsername(0), "Home"); // Retrieve roster and verify that new contact is there and nickname is correct @@ -677,6 +682,46 @@ public class RosterTest extends SmackTestCase { getConnection(2).getRoster().getGroupCount()); } + /** + * Tests the creation of a roster and then simulates abrupt termination. Cached presences + * must go offline. At reconnection, presences must go back to online. + *
    + *
  1. Create some entries + *
  2. Breack the connection + *
  3. Check offline presences + *
  4. Whait for automatic reconnection + *
  5. Check online presences + *
+ */ + public void testOfflinePresencesAfterDisconnection() throws Exception { + // Add a new roster entry + Roster roster = getConnection(0).getRoster(); + roster.createEntry(getBareJID(1), "gato11", null); + roster.createEntry(getBareJID(2), "gato12", null); + + // Wait up to 2 seconds to let the server process presence subscriptions + long initial = System.currentTimeMillis(); + while (System.currentTimeMillis() - initial < 2000 && ( + roster.getPresence(getBareJID(1)) == null || + roster.getPresence(getBareJID(2)) == null)) { + Thread.sleep(100); + } + + Thread.sleep(200); + + // Brakes the connection + getConnection(0).packetReader.notifyConnectionError(new Exception("Simulated Error")); + + Presence presence = roster.getPresence(getBareJID(1)); + assertNull("Presence should be offline after a connection termination", presence); + // Reconnection should occur in 10 seconds + Thread.sleep(12200); + presence = roster.getPresence(getBareJID(1)); + assertNotNull("Presence not found for user", presence); + assertEquals("Presence should be online after a connection reconnection", + Presence.Type.available, presence.getType()); + } + protected int getMaxConnections() { return 3; } diff --git a/test/org/jivesoftware/smack/test/SmackTestCase.java b/test/org/jivesoftware/smack/test/SmackTestCase.java index c560e5de0..22040b22b 100644 --- a/test/org/jivesoftware/smack/test/SmackTestCase.java +++ b/test/org/jivesoftware/smack/test/SmackTestCase.java @@ -1,7 +1,7 @@ /** * $RCSfile$ * $Revision$ - * $Date: $ + * $Date$ * * Copyright 2003-2005 Jive Software. * @@ -19,20 +19,18 @@ */ package org.jivesoftware.smack.test; +import junit.framework.TestCase; +import org.jivesoftware.smack.ConnectionConfiguration; +import org.jivesoftware.smack.XMPPConnection; +import org.jivesoftware.smack.XMPPException; +import org.xmlpull.mxp1.MXParser; +import org.xmlpull.v1.XmlPullParser; + +import javax.net.SocketFactory; import java.io.InputStream; import java.net.URL; import java.util.Enumeration; -import javax.net.SocketFactory; - -import org.jivesoftware.smack.XMPPConnection; -import org.jivesoftware.smack.XMPPException; -import org.jivesoftware.smack.ConnectionConfiguration; -import org.xmlpull.v1.*; -import org.xmlpull.mxp1.MXParser; - -import junit.framework.TestCase; - /** * Base class for all the test cases which provides a pre-configured execution context. This * means that any test case that subclassifies this base class will have access to a pool of @@ -196,6 +194,7 @@ public abstract class SmackTestCase extends TestCase { else { connections[i] = new XMPPConnection(config, getSocketFactory()); } + 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 @@ -232,11 +231,12 @@ public abstract class SmackTestCase extends TestCase { super.tearDown(); for (int i = 0; i < getMaxConnections(); i++) { + if (getConnection(i).isConnected()) { // Delete the created account for the test getConnection(i).getAccountManager().deleteAccount(); // Close the connection - getConnection(i).close(); - + getConnection(i).disconnect(); + } } } diff --git a/test/org/jivesoftware/smackx/CompressionTest.java b/test/org/jivesoftware/smackx/CompressionTest.java index 06ebc8a05..af1da8202 100644 --- a/test/org/jivesoftware/smackx/CompressionTest.java +++ b/test/org/jivesoftware/smackx/CompressionTest.java @@ -20,11 +20,10 @@ package org.jivesoftware.smackx; -import org.jivesoftware.smack.test.SmackTestCase; +import org.jivesoftware.smack.ConnectionConfiguration; import org.jivesoftware.smack.XMPPConnection; import org.jivesoftware.smack.XMPPException; -import org.jivesoftware.smack.ConnectionConfiguration; -import org.jivesoftware.smack.SmackConfiguration; +import org.jivesoftware.smack.test.SmackTestCase; /** * Ensure that stream compression (JEP-138) is correctly supported by Smack. @@ -50,6 +49,7 @@ public class CompressionTest extends SmackTestCase { config.setSASLAuthenticationEnabled(true); XMPPConnection connection = new XMPPConnection(config); + connection.connect(); // Login with the test account connection.login("user0", "user0"); @@ -57,7 +57,7 @@ public class CompressionTest extends SmackTestCase { assertTrue("Connection is not using stream compression", connection.isUsingCompression()); // Close connection - connection.close(); + connection.disconnect(); } protected int getMaxConnections() { @@ -70,6 +70,7 @@ public class CompressionTest extends SmackTestCase { protected void setUp() throws Exception { super.setUp(); XMPPConnection setupConnection = new XMPPConnection(getHost(), getPort()); + setupConnection.connect(); if (!setupConnection.getAccountManager().supportsAccountCreation()) fail("Server does not support account creation"); @@ -90,11 +91,12 @@ public class CompressionTest extends SmackTestCase { protected void tearDown() throws Exception { super.tearDown(); XMPPConnection setupConnection = new XMPPConnection(getHost(), getPort()); + setupConnection.connect(); setupConnection.login("user0", "user0"); // Delete the created account for the test setupConnection.getAccountManager().deleteAccount(); // Close the setupConnection - setupConnection.close(); + setupConnection.disconnect(); } } diff --git a/test/org/jivesoftware/smackx/muc/MultiUserChatTest.java b/test/org/jivesoftware/smackx/muc/MultiUserChatTest.java index 4377c5f42..4893127c3 100644 --- a/test/org/jivesoftware/smackx/muc/MultiUserChatTest.java +++ b/test/org/jivesoftware/smackx/muc/MultiUserChatTest.java @@ -250,6 +250,7 @@ public class MultiUserChatTest extends SmackTestCase { try { // Anonymous user joins the new room XMPPConnection anonConnection = new XMPPConnection(getHost(), getPort()); + anonConnection.connect(); anonConnection.loginAnonymously(); MultiUserChat muc2 = new MultiUserChat(anonConnection, room); muc2.join("testbot2"); @@ -265,7 +266,7 @@ public class MultiUserChatTest extends SmackTestCase { // Anonymous user leaves the room muc2.leave(); - anonConnection.close(); + anonConnection.disconnect(); Thread.sleep(250); // User1 checks the presence of Anonymous user in the room presence = muc.getOccupantPresence(room + "/testbot2"); @@ -1766,6 +1767,7 @@ public class MultiUserChatTest extends SmackTestCase { XMPPConnection[] conns = new XMPPConnection[20]; for (int i = 0; i < conns.length; i++) { conns[i] = new XMPPConnection(getServiceName()); + conns[i].connect(); conns[i].login(getUsername(1), getUsername(1), "resource-" + i); } @@ -1788,7 +1790,7 @@ public class MultiUserChatTest extends SmackTestCase { // Each connection leaves the room and closes the connection for (int i = 0; i < mucs.length; i++) { mucs[i].leave(); - conns[i].close(); + conns[i].disconnect(); } } catch (Exception e) {