/** * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jivesoftware.smack; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import org.jivesoftware.smack.test.SmackTestCase; import org.jivesoftware.smackx.ping.PingManager; /** * Tests the connection and reconnection mechanism * * @author Francisco Vives */ public class ReconnectionTest extends SmackTestCase { private static final long MIN_RECONNECT_WAIT = 17; // Seconds public ReconnectionTest(String arg0) { super(arg0); } /** * Tests an automatic reconnection. * Simulates a connection error and then waits until gets reconnected. */ public void testAutomaticReconnection() throws Exception { XMPPTCPConnection connection = getConnection(0); CountDownLatch latch = new CountDownLatch(1); XMPPTCPConnectionTestListener listener = new XMPPConnectionTestListener(latch); connection.addConnectionListener(listener); // Simulates an error in the connection connection.notifyConnectionError(new Exception("Simulated Error")); latch.await(MIN_RECONNECT_WAIT, TimeUnit.SECONDS); // After 10 seconds, the reconnection manager must reestablishes the connection assertEquals("The ConnectionListener.connectionStablished() notification was not fired", true, listener.reconnected); assertTrue("The ReconnectionManager algorithm has reconnected without waiting at least 5 seconds", listener.attemptsNotifications > 0); // Executes some server interaction testing the connection executeSomeServerInteraction(connection); } public void testAutomaticReconnectionWithCompression() throws Exception { // Create the configuration for this new connection ConnectionConfiguration config = new ConnectionConfiguration(getHost(), getPort()); config.setCompressionEnabled(true); config.setSASLAuthenticationEnabled(true); XMPPTCPConnection connection = new XMPPConnection(config); // Connect to the server connection.connect(); // Log into the server connection.login(getUsername(0), getPassword(0), "MyOtherResource"); assertTrue("Failed to use compression", connection.isUsingCompression()); // Executes some server interaction testing the connection executeSomeServerInteraction(connection); CountDownLatch latch = new CountDownLatch(1); XMPPTCPConnectionTestListener listener = new XMPPConnectionTestListener(latch); connection.addConnectionListener(listener); // Simulates an error in the connection connection.notifyConnectionError(new Exception("Simulated Error")); latch.await(MIN_RECONNECT_WAIT, TimeUnit.SECONDS); // After 10 seconds, the reconnection manager must reestablishes the connection assertEquals("The ConnectionListener.connectionEstablished() notification was not fired", true, listener.reconnected); assertTrue("The ReconnectionManager algorithm has reconnected without waiting at least 5 seconds", listener.attemptsNotifications > 0); // Executes some server interaction testing the connection executeSomeServerInteraction(connection); } /** * Tests a manual reconnection. * Simulates a connection error, disables the reconnection mechanism and then reconnects. */ public void testManualReconnectionWithCancelation() throws Exception { XMPPTCPConnection connection = getConnection(0); CountDownLatch latch = new CountDownLatch(1); XMPPTCPConnectionTestListener listener = new XMPPConnectionTestListener(latch); connection.addConnectionListener(listener); // Produces a connection error connection.notifyConnectionError(new Exception("Simulated Error")); assertEquals( "An error occurs but the ConnectionListener.connectionClosedOnError(e) was not notified", true, listener.connectionClosedOnError); // Thread.sleep(1000); // Cancels the automatic reconnection connection.getConfiguration().setReconnectionAllowed(false); // Waits for a reconnection that must not happened. Thread.sleep(MIN_RECONNECT_WAIT * 1000); // Cancels the automatic reconnection assertEquals(false, listener.reconnected); // Makes a manual reconnection from an error terminated connection without reconnection connection.connect(); // Executes some server interaction testing the connection executeSomeServerInteraction(connection); } /** * Tests a manual reconnection after a login. * Closes the connection and then reconnects. */ public void testCloseAndManualReconnection() throws Exception { XMPPTCPConnection connection = getConnection(0); String username = connection.getConfiguration().getUsername(); String password = connection.getConfiguration().getPassword(); XMPPTCPConnectionTestListener listener = new XMPPConnectionTestListener(); connection.addConnectionListener(listener); // Produces a normal disconnection connection.disconnect(); assertEquals("ConnectionListener.connectionClosed() was not notified", true, listener.connectionClosed); // Waits 10 seconds waiting for a reconnection that must not happened. Thread.sleep(MIN_RECONNECT_WAIT * 1000); assertEquals("The connection was stablished but it was not allowed to", false, listener.reconnected); // Makes a manual reconnection connection.connect(); connection.login(username, password); // Executes some server interaction testing the connection executeSomeServerInteraction(connection); } /** * Tests a reconnection in a anonymously logged connection. * Closes the connection and then reconnects. */ public void testAnonymousReconnection() throws Exception { XMPPTCPConnection connection = createConnection(); connection.connect(); XMPPTCPConnectionTestListener listener = new XMPPConnectionTestListener(); connection.addConnectionListener(listener); // Makes the anounymous login connection.loginAnonymously(); // Produces a normal disconnection connection.disconnect(); assertEquals("ConnectionListener.connectionClosed() was not notified", true, listener.connectionClosed); // Makes a manual reconnection connection.connect(); connection.loginAnonymously(); assertEquals("Failed the manual connection", true, connection.isAnonymous()); } private XMPPTCPConnection createXMPPConnection() throws Exception { XMPPTCPConnection connection; // Create the configuration ConnectionConfiguration config = new ConnectionConfiguration(getHost(), getPort()); config.setCompressionEnabled(Boolean.getBoolean("test.compressionEnabled")); config.setSASLAuthenticationEnabled(true); connection = new XMPPTCPConnection(config); return connection; } /** * Execute some server interaction in order to test that the regenerated connection works fine. */ private void executeSomeServerInteraction(TCPConnection connection) throws XMPPException { PingManager pingManager = PingManager.getInstanceFor(connection); pingManager.pingMyServer(); } protected int getMaxConnections() { return 1; } private class XMPPTCPConnectionTestListener implements ConnectionListener { // Variables to support listener notifications verification private volatile boolean connectionClosed = false; private volatile boolean connectionClosedOnError = false; private volatile boolean reconnected = false; private volatile boolean reconnectionFailed = false; private volatile int remainingSeconds = 0; private volatile int attemptsNotifications = 0; private volatile boolean reconnectionCanceled = false; private CountDownLatch countDownLatch; private XMPPTCPConnectionTestListener(CountDownLatch latch) { countDownLatch = latch; } private XMPPTCPConnectionTestListener() { } /** * Methods to test the listener. */ public void connectionClosed() { connectionClosed = true; if (countDownLatch != null) countDownLatch.countDown(); } public void connectionClosedOnError(Exception e) { connectionClosedOnError = true; } public void reconnectionCanceled() { reconnectionCanceled = true; if (countDownLatch != null) countDownLatch.countDown(); } public void reconnectingIn(int seconds) { attemptsNotifications = attemptsNotifications + 1; remainingSeconds = seconds; } public void reconnectionSuccessful() { reconnected = true; if (countDownLatch != null) countDownLatch.countDown(); } public void reconnectionFailed(Exception error) { reconnectionFailed = true; if (countDownLatch != null) countDownLatch.countDown(); } } }