diff --git a/source/org/jivesoftware/smack/PacketReader.java b/source/org/jivesoftware/smack/PacketReader.java index 590dfd951..af1c7682f 100644 --- a/source/org/jivesoftware/smack/PacketReader.java +++ b/source/org/jivesoftware/smack/PacketReader.java @@ -48,7 +48,7 @@ class PacketReader { private XMPPConnection connection; private XmlPullParser parser; - private boolean done; + volatile boolean done; private String connectionID = null; private Semaphore connectionSemaphore; @@ -155,48 +155,6 @@ class PacketReader { connection.collectors.clear(); } - /** - * Sends out a notification that there was an error with the connection - * and closes the connection. - * - * @param e the exception that causes the connection close event. - */ - void notifyConnectionError(Exception e) { - done = true; - // Closes the connection temporary. A reconnection is possible - connection.shutdown(new Presence(Presence.Type.unavailable)); - // Print the stack trace to help catch the problem - e.printStackTrace(); - // Notify connection listeners of the error. - for (ConnectionListener listener : connection.getConnectionListeners()) { - try { - listener.connectionClosedOnError(e); - } - catch (Exception e2) { - // Catch and print any exception so we can recover - // from a faulty listener - e2.printStackTrace(); - } - } - } - - /** - * Sends a notification indicating that the connection was reconnected successfully. - */ - protected void notifyReconnection() { - // Notify connection listeners of the reconnection. - for (ConnectionListener listener : connection.getConnectionListeners()) { - try { - listener.reconnectionSuccessful(); - } - catch (Exception e) { - // Catch and print any exception so we can recover - // from a faulty listener - e.printStackTrace(); - } - } - } - /** * 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 @@ -332,7 +290,7 @@ class PacketReader { if (!(done || connection.isSocketClosed())) { // Close the connection and notify connection listeners of the // error. - notifyConnectionError(e); + connection.notifyConnectionError(e); } } } diff --git a/source/org/jivesoftware/smack/PacketWriter.java b/source/org/jivesoftware/smack/PacketWriter.java index 155110cfb..f8b5c359f 100644 --- a/source/org/jivesoftware/smack/PacketWriter.java +++ b/source/org/jivesoftware/smack/PacketWriter.java @@ -44,7 +44,7 @@ class PacketWriter { private Writer writer; private XMPPConnection connection; private final BlockingQueue queue; - private boolean done; + volatile boolean done; /** * Timestamp when the last stanza was sent to the server. This information is used @@ -245,7 +245,7 @@ class PacketWriter { // packetReader could be set to null by an concurrent disconnect() call. // Therefore Prevent NPE exceptions by checking packetReader. if (connection.packetReader != null) { - connection.packetReader.notifyConnectionError(ioe); + connection.notifyConnectionError(ioe); } } } diff --git a/source/org/jivesoftware/smack/XMPPConnection.java b/source/org/jivesoftware/smack/XMPPConnection.java index af7b66750..6512aa981 100644 --- a/source/org/jivesoftware/smack/XMPPConnection.java +++ b/source/org/jivesoftware/smack/XMPPConnection.java @@ -473,6 +473,10 @@ public class XMPPConnection extends Connection { return; } + if (!isConnected()) { + return; + } + shutdown(unavailablePresence); if (roster != null) { @@ -483,9 +487,7 @@ public class XMPPConnection extends Connection { wasAuthenticated = false; packetWriter.cleanup(); - packetWriter = null; packetReader.cleanup(); - packetReader = null; } public void sendPacket(Packet packet) { @@ -614,10 +616,8 @@ public class XMPPConnection extends Connection { */ private void initConnection() throws XMPPException { boolean isFirstInitialization = packetReader == null || packetWriter == null; - if (!isFirstInitialization) { - compressionHandler = null; - serverAckdCompression = false; - } + compressionHandler = null; + serverAckdCompression = false; // Set the reader and writer instance variables initReaderAndWriter(); @@ -661,7 +661,7 @@ public class XMPPConnection extends Connection { } } else if (!wasAuthenticated) { - packetReader.notifyReconnection(); + notifyReconnection(); } } @@ -773,7 +773,7 @@ public class XMPPConnection extends Connection { void startTLSReceived(boolean required) { if (required && config.getSecurityMode() == ConnectionConfiguration.SecurityMode.disabled) { - packetReader.notifyConnectionError(new IllegalStateException( + notifyConnectionError(new IllegalStateException( "TLS required by server but not allowed by connection configuration")); return; } @@ -787,7 +787,7 @@ public class XMPPConnection extends Connection { writer.flush(); } catch (IOException e) { - packetReader.notifyConnectionError(e); + notifyConnectionError(e); } } @@ -977,7 +977,7 @@ public class XMPPConnection extends Connection { writer.flush(); } catch (IOException e) { - packetReader.notifyConnectionError(e); + notifyConnectionError(e); } } @@ -1041,7 +1041,7 @@ public class XMPPConnection extends Connection { login(config.getUsername(), config.getPassword(), config.getResource()); } - packetReader.notifyReconnection(); + notifyReconnection(); } catch (XMPPException e) { e.printStackTrace(); @@ -1059,4 +1059,50 @@ public class XMPPConnection extends Connection { this.wasAuthenticated = wasAuthenticated; } } + + /** + * Sends out a notification that there was an error with the connection + * and closes the connection. Also prints the stack trace of the given exception + * + * @param e the exception that causes the connection close event. + */ + synchronized void notifyConnectionError(Exception e) { + // Listeners were already notified of the exception, return right here. + if (packetReader.done && packetWriter.done) return; + + packetReader.done = true; + packetWriter.done = true; + // Closes the connection temporary. A reconnection is possible + shutdown(new Presence(Presence.Type.unavailable)); + // Print the stack trace to help catch the problem + e.printStackTrace(); + // Notify connection listeners of the error. + for (ConnectionListener listener : getConnectionListeners()) { + try { + listener.connectionClosedOnError(e); + } + catch (Exception e2) { + // Catch and print any exception so we can recover + // from a faulty listener + e2.printStackTrace(); + } + } + } + + /** + * Sends a notification indicating that the connection was reconnected successfully. + */ + protected void notifyReconnection() { + // Notify connection listeners of the reconnection. + for (ConnectionListener listener : getConnectionListeners()) { + try { + listener.reconnectionSuccessful(); + } + catch (Exception e) { + // Catch and print any exception so we can recover + // from a faulty listener + e.printStackTrace(); + } + } + } } diff --git a/test/org/jivesoftware/smack/ReconnectionTest.java b/test/org/jivesoftware/smack/ReconnectionTest.java index f4cbd0c32..35c085813 100644 --- a/test/org/jivesoftware/smack/ReconnectionTest.java +++ b/test/org/jivesoftware/smack/ReconnectionTest.java @@ -44,7 +44,7 @@ public class ReconnectionTest extends SmackTestCase { connection.addConnectionListener(listener); // Simulates an error in the connection - connection.packetReader.notifyConnectionError(new Exception("Simulated Error")); + connection.notifyConnectionError(new Exception("Simulated Error")); Thread.sleep(12000); // After 10 seconds, the reconnection manager must reestablishes the connection assertEquals("The ConnectionListener.connectionStablished() notification was not fired", @@ -79,7 +79,7 @@ public class ReconnectionTest extends SmackTestCase { connection.addConnectionListener(listener); // Simulates an error in the connection - connection.packetReader.notifyConnectionError(new Exception("Simulated Error")); + connection.notifyConnectionError(new Exception("Simulated Error")); Thread.sleep(12000); // After 10 seconds, the reconnection manager must reestablishes the connection assertEquals("The ConnectionListener.connectionStablished() notification was not fired", @@ -103,7 +103,7 @@ public class ReconnectionTest extends SmackTestCase { connection.addConnectionListener(listener); // Produces a connection error - connection.packetReader.notifyConnectionError(new Exception("Simulated Error")); + connection.notifyConnectionError(new Exception("Simulated Error")); assertEquals( "An error occurs but the ConnectionListener.connectionClosedOnError(e) was not notified", true, listener.connectionClosedOnError); diff --git a/test/org/jivesoftware/smack/RosterSmackTest.java b/test/org/jivesoftware/smack/RosterSmackTest.java index 6436f69f4..8bee8124f 100644 --- a/test/org/jivesoftware/smack/RosterSmackTest.java +++ b/test/org/jivesoftware/smack/RosterSmackTest.java @@ -689,7 +689,7 @@ public class RosterSmackTest extends SmackTestCase { Thread.sleep(200); // Break the connection - getConnection(0).packetReader.notifyConnectionError(new Exception("Simulated Error")); + getConnection(0).notifyConnectionError(new Exception("Simulated Error")); Presence presence = roster.getPresence(getBareJID(1)); assertFalse("Unavailable presence not found for offline user", presence.isAvailable());