diff --git a/source/org/jivesoftware/smack/XMPPConnection.java b/source/org/jivesoftware/smack/XMPPConnection.java index fdbd705f1..f5dbfe2fc 100644 --- a/source/org/jivesoftware/smack/XMPPConnection.java +++ b/source/org/jivesoftware/smack/XMPPConnection.java @@ -51,13 +51,25 @@ import java.util.concurrent.ConcurrentHashMap; *
* // Create a connection to the jivesoftware.com XMPP server. * XMPPConnection con = new XMPPConnection("jivesoftware.com"); + * // Connect to the server + * con.connect(); * // Most servers require you to login before performing other tasks. * con.login("jsmith", "mypass"); * // Start a new conversation with John Doe and send him a message. * Chat chat = con.createChat("jdoe@jabber.org"); * chat.sendMessage("Hey, how's it going?"); + * // Disconnect from the server + * con.disconnect(); ** + * XMPPConnections can be reused between connections. This means that an XMPPConnection + * may be connected, disconnected and then connected again. Listeners of the XMPPConnection + * will remain accross connections.
+ * + * If a connected XMPPConnection gets disconnected abruptly then it will try to reconnect + * again. To stop the reconnection process just use {@link #disconnect()}. Once stopped + * you can use {@link #connect()} to manually connect to the server. + * * @author Matt Tucker */ public class XMPPConnection { @@ -113,7 +125,15 @@ public class XMPPConnection { String connectionID; private String user = null; private boolean connected = false; + /** + * Flag that indicates if the user is currently authenticated with the server. + */ private boolean authenticated = false; + /** + * Flag that indicates if the user was authenticated with the server when the connection + * to the server was closed (abruptly or not). + */ + private boolean wasAuthenticated = false; private boolean anonymous = false; private boolean usingTLS = false; @@ -122,7 +142,7 @@ public class XMPPConnection { Roster roster = null; private AccountManager accountManager = null; - private SASLAuthentication saslAuthentication = new SASLAuthentication(this); + protected SASLAuthentication saslAuthentication = new SASLAuthentication(this); Writer writer; Reader reader; @@ -147,22 +167,20 @@ public class XMPPConnection { * Holds the initial configuration used while creating the connection. */ private ConnectionConfiguration configuration; - + /** * Creates a new connection to the specified XMPP server. A DNS SRV lookup will be * performed to try to determine the IP address and port corresponding to the * serviceName; if that lookup fails, it's assumed that server resides at serviceName * with the default port of 5222. This is the preferred constructor for connecting - * to an XMPP server. + * to an XMPP server.
+ * + * Note that XMPPConnection constructors do not establish the connection to the server, + * to make it effective use the connect method. {@link #connect()}. * * @param serviceName the name of the XMPP server to connect to; e.g. jivesoftware.com. - * @throws XMPPException if an error occurs while trying to establish the connection. - * Two possible errors can occur which will be wrapped by an XMPPException -- - * UnknownHostException (XMPP error code 504), and IOException (XMPP error code - * 502). The error codes and wrapped exceptions can be used to present more - * appropiate error messages to end-users. */ - public XMPPConnection(String serviceName) throws XMPPException { + public XMPPConnection(String serviceName) { // Perform DNS lookup to get host and port to use DNSUtil.HostAddress address = DNSUtil.resolveXMPPDomain(serviceName); // Create the configuration for this new connection @@ -172,53 +190,46 @@ public class XMPPConnection { config.setCompressionEnabled(false); config.setSASLAuthenticationEnabled(true); config.setDebuggerEnabled(DEBUG_ENABLED); - // Set the new connection configuration - connectUsingConfiguration(config, null); + init(config, null); } /** - * Creates a new connection to the XMPP server at the specifiec host and port. + * Creates a new connection to the XMPP server at the specifiec host and port.
+ * + * Note that XMPPConnection constructors do not establish the connection to the server, + * to make it effective use the connect method. {@link #connect()}. * * @param host the name of the XMPP server to connect to; e.g. jivesoftware.com. * @param port the port on the server that should be used; e.g. 5222. - * @throws XMPPException if an error occurs while trying to establish the connection. - * Two possible errors can occur which will be wrapped by an XMPPException -- - * UnknownHostException (XMPP error code 504), and IOException (XMPP error code - * 502). The error codes and wrapped exceptions can be used to present more - * appropiate error messages to end-users. */ - public XMPPConnection(String host, int port) throws XMPPException { + public XMPPConnection(String host, int port) { // Create the configuration for this new connection ConnectionConfiguration config = new ConnectionConfiguration(host, port); config.setTLSEnabled(true); config.setCompressionEnabled(false); config.setSASLAuthenticationEnabled(true); config.setDebuggerEnabled(DEBUG_ENABLED); - // Set the new connection configuration - connectUsingConfiguration(config, null); + init(config, null); } /** - * Creates a new connection to the specified XMPP server on the given host and port. + * Creates a new connection to the specified XMPP server on the given host and port.
+ * + * Note that XMPPConnection constructors do not establish the connection to the server, + * to make it effective use the connect method. {@link #connect()}. * * @param host the host name, or null for the loopback address. * @param port the port on the server that should be used; e.g. 5222. * @param serviceName the name of the XMPP server to connect to; e.g. jivesoftware.com. - * @throws XMPPException if an error occurs while trying to establish the connection. - * Two possible errors can occur which will be wrapped by an XMPPException -- - * UnknownHostException (XMPP error code 504), and IOException (XMPP error code - * 502). The error codes and wrapped exceptions can be used to present more - * appropiate error messages to end-users. */ - public XMPPConnection(String host, int port, String serviceName) throws XMPPException { + public XMPPConnection(String host, int port, String serviceName) { // Create the configuration for this new connection ConnectionConfiguration config = new ConnectionConfiguration(host, port, serviceName); config.setTLSEnabled(true); config.setCompressionEnabled(false); config.setSASLAuthenticationEnabled(true); config.setDebuggerEnabled(DEBUG_ENABLED); - // Set the new connection configuration - connectUsingConfiguration(config, null); + init(config, null); } /** @@ -227,53 +238,44 @@ public class XMPPConnection { * * A custom SocketFactory allows fine-grained control of the actual connection to the * XMPP server. A typical use for a custom SocketFactory is when connecting through a - * SOCKS proxy. + * SOCKS proxy.
+ * + * Note that XMPPConnection constructors do not establish the connection to the server, + * to make it effective use the connect method. {@link #connect()}. * * @param host the host name, or null for the loopback address. * @param port the port on the server that should be used; e.g. 5222. * @param serviceName the name of the XMPP server to connect to; e.g. jivesoftware.com. * @param socketFactory a SocketFactory that will be used to create the socket to the XMPP * server. - * @throws XMPPException if an error occurs while trying to establish the connection. - * Two possible errors can occur which will be wrapped by an XMPPException -- - * UnknownHostException (XMPP error code 504), and IOException (XMPP error code - * 502). The error codes and wrapped exceptions can be used to present more - * appropiate error messages to end-users. */ - public XMPPConnection(String host, int port, String serviceName, SocketFactory socketFactory) - throws XMPPException - { + public XMPPConnection(String host, int port, String serviceName, SocketFactory socketFactory) { // Create the configuration for this new connection ConnectionConfiguration config = new ConnectionConfiguration(host, port, serviceName); config.setTLSEnabled(true); config.setCompressionEnabled(false); config.setSASLAuthenticationEnabled(true); config.setDebuggerEnabled(DEBUG_ENABLED); - // Set the new connection configuration - connectUsingConfiguration(config, socketFactory); + init(config, socketFactory); } - public XMPPConnection(ConnectionConfiguration config) throws XMPPException { - // Set the new connection configuration - connectUsingConfiguration(config, null); + public XMPPConnection(ConnectionConfiguration config) { + init(config, null); } - public XMPPConnection(ConnectionConfiguration config, SocketFactory socketFactory) - throws XMPPException { - // Set the new connection configuration - connectUsingConfiguration(config, socketFactory); + public XMPPConnection(ConnectionConfiguration config, SocketFactory socketFactory) { + init(config, socketFactory); } - private void connectUsingConfiguration(ConnectionConfiguration config, - SocketFactory socketFactory) throws XMPPException { + private void connectUsingConfiguration(ConnectionConfiguration config) throws XMPPException { this.host = config.getHost(); this.port = config.getPort(); try { - if (socketFactory == null) { + if (config.getSocketFactory() == null) { this.socket = new Socket(host, port); } else { - this.socket = socketFactory.createSocket(host, port); + this.socket = config.getSocketFactory().createSocket(host, port); } } catch (UnknownHostException uhe) { @@ -289,15 +291,7 @@ public class XMPPConnection { XMPPError.Condition.remote_server_error, errorMessage), ioe); } this.serviceName = config.getServiceName(); - try { - // Keep a copy to be sure that once the configuration has been passed to the - // constructor it cannot be modified - this.configuration = (ConnectionConfiguration) config.clone(); - } - catch (CloneNotSupportedException e) { - // Do nothing - } - init(); + initConnection(); } /** @@ -403,7 +397,11 @@ public class XMPPConnection { * presence may optionally be sent. If sendPresence * is false, a presence packet must be sent manually later. If more than five seconds * (default timeout) elapses in each step of the authentication process without a - * response from the server, or if an error occurs, a XMPPException will be thrown. + * response from the server, or if an error occurs, a XMPPException will be thrown.
+ *
+ * Before logging in (i.e. authenticate) to the server the connection must be connected.
+ * For compatibility and easiness of use the connection will automatically connect to the
+ * server if not already connected.
*
* @param username the username.
* @param password the password.
@@ -456,19 +454,24 @@ public class XMPPConnection {
useCompression();
}
- // Create the roster.
- this.roster = new Roster(this);
+ // Create the roster if it is not a reconnection.
+ if (this.roster == null) {
+ this.roster = new Roster(this);
+ }
roster.reload();
// Set presence to online.
if (sendPresence) {
packetWriter.sendPacket(new Presence(Presence.Type.available));
}
-
+
// Indicate that we're now authenticated.
authenticated = true;
anonymous = false;
+ // Stores the autentication for future reconnection
+ this.getConfiguration().setUsernameAndPassword(username, password);
+
// If debugging is enabled, change the the debug window title to include the
// name we are now logged-in as.
// If DEBUG_ENABLED was set to true AFTER the connection was created the debugger
@@ -642,9 +645,12 @@ public class XMPPConnection {
/**
* Closes the connection by setting presence to unavailable then closing the stream to
- * the XMPP server. Once a connection has been closed, it cannot be re-opened.
+ * the XMPP server. The shutdown logic will be used during a planned disconnection or when
+ * dealing with an unexpected disconnection. Unlike {@link #disconnect()} the connection's
+ * {@link PacketReader}, {@link PacketWriter} and {@link Roster} will not be removed thus
+ * connection's state is kept.
*/
- public void close() {
+ protected void shutdown() {
// Set presence to offline.
packetWriter.sendPacket(new Presence(Presence.Type.unavailable));
packetReader.shutdown();
@@ -675,8 +681,32 @@ public class XMPPConnection {
catch (Exception e) {
// Ignore.
}
+
+ this.setWasAuthenticated(authenticated);
authenticated = false;
connected = false;
+
+ saslAuthentication.init();
+ }
+
+ /**
+ * Closes the connection by setting presence to unavailable then closing the stream to
+ * the XMPP server. The XMPPConnection can still be used for connecting to the server
+ * again.
+ *
+ * The difference between disconnect and shutdown is that disconnect makes a complete reset
+ * of the connection state whereas shutdown only cleans the connection and keeps alive
+ * packet reader listeners, previous login and roster presences.
+ */
+ public void disconnect() {
+ this.shutdown();
+
+ this.roster = null;
+
+ this.wasAuthenticated = false;
+
+ packetWriter = null;
+ packetReader = null;
}
/**
@@ -834,29 +864,53 @@ public class XMPPConnection {
}
}
+ /**
+ * Initializes the connection configuration. This method is only executed
+ * when the XMPPConnection is created.
+ */
+ private void init(ConnectionConfiguration config, SocketFactory socketFactory) {
+ try {
+ // Keep a copy to be sure that once the configuration has been passed to the
+ // constructor it cannot be modified
+ this.configuration = (ConnectionConfiguration) config.clone();
+ this.configuration.setSocketFactory(socketFactory);
+ }
+ catch (CloneNotSupportedException e) {
+ // Do nothing
+ }
+ }
+
/**
* Initializes the connection by creating a packet reader and writer and opening a
* XMPP stream to the server.
*
* @throws XMPPException if establishing a connection to the server fails.
*/
- private void init() throws XMPPException {
+ private void initConnection() throws XMPPException {
// Set the reader and writer instance variables
initReaderAndWriter();
- try
- {
- packetWriter = new PacketWriter(this);
- packetReader = new PacketReader(this);
+ try {
+ boolean isFirstInitialization = packetReader == null || packetWriter == null;
- // If debugging is enabled, we should start the thread that will listen for
- // all packets and then log them.
- if (configuration.isDebuggerEnabled()) {
- packetReader.addPacketListener(debugger.getReaderListener(), null);
- if (debugger.getWriterListener() != null) {
- packetWriter.addPacketListener(debugger.getWriterListener(), null);
+ if (isFirstInitialization) {
+ packetWriter = new PacketWriter(this);
+ packetReader = new PacketReader(this);
+
+ // If debugging is enabled, we should start the thread that will listen for
+ // all packets and then log them.
+ if (configuration.isDebuggerEnabled()) {
+ packetReader.addPacketListener(debugger.getReaderListener(), null);
+ if (debugger.getWriterListener() != null) {
+ packetWriter.addPacketListener(debugger.getWriterListener(), null);
+ }
}
}
+ else {
+ packetWriter.init();
+ packetReader.init();
+ }
+
// Start the packet writer. This will open a XMPP stream to the server
packetWriter.startup();
// Start the packet reader. The startup() method will block until we
@@ -869,99 +923,123 @@ public class XMPPConnection {
// Start keep alive process (after TLS was negotiated - if available)
packetWriter.startKeepAliveProcess();
- // Notify that a new connection has been established
- connectionEstablished(this);
- // Add a listener for all message packets so that we can deliver errant
- // messages to the best Chat instance available.
- addPacketListener(new PacketListener() {
- public void processPacket(Packet packet) {
- Message message = (Message)packet;
- // Ignore any messages with a thread ID, as they will likely
- // already be associated with a Chat. This will miss messages
- // with new thread ID values, but we can only assume that a
- // listener is registered to deal with this case.
- if (message.getThread() == null &&
- message.getType() != Message.Type.GROUP_CHAT &&
- message.getType() != Message.Type.HEADLINE) {
- WeakReference
+ *
+ * Listeners will be preserved from a previous connection if the reconnection
+ * occurs after an abrupt termination.
+ *
+ * @throws XMPPException if an error occurs while trying to establish the connection.
+ * Two possible errors can occur which will be wrapped by an XMPPException --
+ * UnknownHostException (XMPP error code 504), and IOException (XMPP error code
+ * 502). The error codes and wrapped exceptions can be used to present more
+ * appropiate error messages to end-users.
+ */
+ public void connect() throws XMPPException {
+ // Stablishes the connection, readers and writers
+ connectUsingConfiguration(configuration);
+ // Automatically makes the login if the user was previouslly connected successfully
+ // to the server and the connection was terminated abruptly
+ if (connected && wasAuthenticated) {
+ // Make the login
+ try {
+ if (isAnonymous()) {
+ // Make the anonymous login
+ loginAnonymously();
+ } else {
+ login(getConfiguration().getUsername(), getConfiguration().getPassword());
+ }
+ } catch (XMPPException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ /**
+ * Sets if the connection has already logged in the server.
+ *
+ * @param wasAuthenticated
+ */
+ private void setWasAuthenticated(boolean wasAuthenticated) {
+ if (!this.wasAuthenticated) {
+ this.wasAuthenticated = wasAuthenticated;
+ }
+ }
}
\ No newline at end of file